CDK for TerraformがGAになってたので触ってみた
本記事は Sansan Advent Calendar 2022 13日目の記事です。
TerraformをAWS CDKテイストで書ける CDK for Terraform(CDKTF) が2022/08/01にGAとなったため触ってみた内容と所感をまとめたいと思います。
CDK for Terraformとは
以下、ドキュメントからの引用です。
The Cloud Development Kit for Terraform (CDKTF) generates JSON Terraform configuration from code in C#, Python, TypeScript, Java, or Go, and creates infrastructure using Terraform. With CDKTF, you can use hundreds of providers and thousands of module definitions provided by HashiCorp and the Terraform community. By using your programming language of choice, you can take advantage of the features and development workflows you are familiar with.
CDKTF uses the Cloud Development Kit from AWS, which provides a set of language-native frameworks for defining infrastructure, and adapters that let underlying provisioning tools use those definitions. CDK for Terraform generates Terraform configuration to provision infrastructure with Terraform. The adapter works with existing Terraform providers and modules, and integrates with Terraform Cloud and Terraform Enterprise. CDKTF uses the core Terraform workflow, including planning and applying your infrastructure changes.
Install CDK for Terraform and Run a Quick Start Demo
要は
- プログラミング言語を使ってTerraformを書くことができるよ
- AWS CDK(Cloud Development Kit)が言語ネイティブなフレームワークと、Terraformでそれらの定義を使用するためのアダプターを提供するよ
ということです。
個人的には以下の点からTerraformとCDKの良いとこ取りをしたようなツールだと考えています。
- 使い慣れたプログラミング言語で書ける(C#, Python, TypeScript, Java, Goをサポート)
- Terraform独自のHCLを習得しなくてもOK
- マルチクラウドに対応している
- AWS CDKはCloudFormationテンプレートを生成するためAWSでしか使えなかった
CDKTFを使うことで、プログラミング言語の型や抽象化のメリットを生かしつつ、HashiCorpやTerraformコミュニティが提供する数百のプロバイダーと数千のモジュール定義を利用することができちゃいます。
GitHub ActionsでCDKTFの diff や deploy を実行するためのActionsも公開されているようです。
ということで、さっそく試してみたいと思います。
実行環境
CDKTFを使用するには以下が必要です。
- Terraform CLI 1.1以上
- Node.js 16以上
実際に使用した環境はこちらです。
- Node.js 18.12.1
- npm 8.19.2
- Terraform 1.1.4
- CDKTF 0.14.1
- TypeScript 4.9.3
セットアップ
cdktf-cli
をインストール
npm i --global cdktf-cli@latest
ディレクトリ作成
mkdir cdktf-sample && cd cdktf-sample
CDKTFプロジェクトの初期生成
❯ cdktf init --template="typescript"
Welcome to CDK for Terraform!
By default, cdktf allows you to manage the state of your stacks using Terraform Cloud for free.
cdktf will request an API token for app.terraform.io using your browser.
If login is successful, cdktf will store the token in plain text in
the following file for use by subsequent Terraform commands:
/home/tsujita/.terraform.d/credentials.tfrc.json
Note: The local storage mode isn't recommended for storing the state of your stacks.
? Do you want to continue with Terraform Cloud remote state management? No
? Project Name cdktf-sample
? Project Description A simple getting started project for cdktf.
? Do you want to start from an existing Terraform project? No
? Do you want to send crash reports to the CDKTF team? See https://www.terraform.io/cdktf/create-and-deploy/configuration-file#enable-crash-reporting-for-the-cli for more information Yes
added 2 packages, and audited 57 packages in 2s
5 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
added 296 packages, and audited 353 packages in 24s
33 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
========================================================================================================
Your cdktf typescript project is ready!
cat help Print this message
Compile:
npm run get Import/update Terraform providers and modules (you should check-in this directory)
npm run compile Compile typescript code to javascript (or "npm run watch")
npm run watch Watch for changes and compile typescript in the background
npm run build Compile typescript
Synthesize:
cdktf synth [stack] Synthesize Terraform resources from stacks to cdktf.out/ (ready for 'terraform apply')
Diff:
cdktf diff [stack] Perform a diff (terraform plan) for the given stack
Deploy:
cdktf deploy [stack] Deploy the given stack
Destroy:
cdktf destroy [stack] Destroy the stack
Test:
npm run test Runs unit tests (edit __tests__/main-test.ts to add your own tests)
npm run test:watch Watches the tests and reruns them on change
Upgrades:
npm run upgrade Upgrade cdktf modules to latest version
npm run upgrade:next Upgrade cdktf modules to latest "@next" version (last commit)
Use Providers:
You can add prebuilt providers (if available) or locally generated ones using the add command:
cdktf provider add "aws@~>3.0" null kreuzwerker/docker
You can find all prebuilt providers on npm: https://www.npmjs.com/search?q=keywords:cdktf
You can also install these providers directly through npm:
npm install @cdktf/provider-aws
npm install @cdktf/provider-google
npm install @cdktf/provider-azurerm
npm install @cdktf/provider-docker
npm install @cdktf/provider-github
npm install @cdktf/provider-null
You can also build any module or provider locally. Learn more https://cdk.tf/modules-and-providers
========================================================================================================
Note: You can always add providers using 'cdktf provider add' later on
? What providers do you want to use? aws
Checking whether pre-built provider exists for the following constraints:
provider: aws
version : latest
language: typescript
cdktf : 0.14.1
Found pre-built provider.
Adding package @cdktf/provider-aws @ 11.0.5
Installing package @cdktf/provider-aws @ 11.0.5 using npm.
Package installed.
初期構築時の設定内容は以下です。
Do you want to continue with Terraform Cloud remote state management?
StateをTerraform Cloudに保存するかどうか
今回はお試しなのでNo
にしました。ローカルに保存されます。
Project Name
プロジェクト名
デフォルトではcdktf initを実行したディレクトリ名が設定されます。
Project Description
プロジェクトの説明
デフォルトではA simple getting started project for cdktf.
が設定されます。
Do you want to start from an existing Terraform project?
既存のTerraformプロジェクトから開始するか
今回は新規作成から試したいためNo
しました。既存から読み込むパターンは後ほど紹介します。
Do you want to send crash reports to the CDKTF team?
CDKTFが異常終了した際にレポートを送るか
Yes
にしました。
What providers do you want to use?
どのプロバイダーを使用するか
CDKTFでは人気のあるプロバイダーはビルド済みのパッケージとして提供されているため、使用したいプロバイダーを選択します。全プロバイダー一覧はこちら
今回はaws
を選択しました。
プロバイダーはcdktf provider add
コマンドで後から追加することもできます。
ディレクトリ
生成されたファイルは以下です。main.tsにリソースを追加していていきます。
.
|-- __tests__
|-- cdktf.json
|-- help
|-- jest.config.js
|-- main.ts
|-- node_modules
|-- package-lock.json
|-- package.json
|-- setup.js
`-- tsconfig.json
S3を構築してみる
S3のリソースを追加して適用してみます。
S3のリソースを追加
main.tsにコードを追加します。
import { Construct } from "constructs";
import { AwsProvider } from "@cdktf/provider-aws/lib/provider";
import { S3Bucket } from "@cdktf/provider-aws/lib/s3-bucket";
import { App, TerraformStack } from "cdktf";
class MyStack extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);
// AWSプロバイダーを東京リージョンで使用
new AwsProvider(this, "AWS", {
region: "ap-northeast-1",
});
// S3バケットを作成
new S3Bucket(this, "sample_bucket", {
bucket: "cdktf-sample-bucket",
});
}
}
const app = new App();
new MyStack(app, "cdktf-sample");
app.synth();
CDKでの書きっぷりとほぼ同じですが、スタックを作成する際にextendするクラスがTerraformStack
になってます。
class MyStack extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);
次にプロバイダーを作成する部分です。CDKではAWS前提だったのでプロバイダーに関するクラスは存在しませんでしたが、CDKTFでは使用したいプロバイダーをnewして使用することとなります。
new AwsProvider(this, "AWS", {
region: "ap-northeast-1",
});
terraformのプロバイダー指定部分と一致します。
provider "aws" {
region = "ap-northeast-1"
}
デプロイ
デプロイしてみます。
❯ cdktf deploy
cdktf-sample Initializing the backend...
cdktf-sample Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
cdktf-sample - Using previously-installed hashicorp/aws v4.45.0
cdktf-sample Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
cdktf-sample Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
cdktf-sample # aws_s3_bucket.sample_bucket (sample_bucket) will be created
+ resource "aws_s3_bucket" "sample_bucket" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = "cdktf-sample-bucket-misaosyushi"
+ bucket_domain_name = (known after apply)
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ object_lock_enabled = (known after apply)
+ policy = (known after apply)
+ region = (known after apply)
+ request_payer = (known after apply)
+ tags_all = (known after apply)
+ website_domain = (known after apply)
+ website_endpoint = (known after apply)
+ cors_rule {
+ allowed_headers = (known after apply)
+ allowed_methods = (known after apply)
+ allowed_origins = (known after apply)
+ expose_headers = (known after apply)
+ max_age_seconds = (known after apply)
}
+ grant {
+ id = (known after apply)
+ permissions = (known after apply)
+ type = (known after apply)
+ uri = (known after apply)
}
+ lifecycle_rule {
+ abort_incomplete_multipart_upload_days = (known after apply)
+ enabled = (known after apply)
+ id = (known after apply)
+ prefix = (known after apply)
+ tags = (known after apply)
+ expiration {
+ date = (known after apply)
+ days = (known after apply)
+ expired_object_delete_marker = (known after apply)
}
+ noncurrent_version_expiration {
+ days = (known after apply)
}
+ noncurrent_version_transition {
+ days = (known after apply)
+ storage_class = (known after apply)
}
+ transition {
+ date = (known after apply)
+ days = (known after apply)
+ storage_class = (known after apply)
}
}
+ logging {
+ target_bucket = (known after apply)
+ target_prefix = (known after apply)
}
+ object_lock_configuration {
+ object_lock_enabled = (known after apply)
+ rule {
+ default_retention {
+ days = (known after apply)
+ mode = (known after apply)
+ years = (known after apply)
}
}
}
+ replication_configuration {
+ role = (known after apply)
+ rules {
+ delete_marker_replication_status = (known after apply)
+ id = (known after apply)
+ prefix = (known after apply)
+ priority = (known after apply)
+ status = (known after apply)
+ destination {
+ account_id = (known after apply)
+ bucket = (known after apply)
+ replica_kms_key_id = (known after apply)
+ storage_class = (known after apply)
+ access_control_translation {
+ owner = (known after apply)
}
+ metrics {
+ minutes = (known after apply)
+ status = (known after apply)
}
+ replication_time {
+ minutes = (known after apply)
+ status = (known after apply)
}
}
+ filter {
+ prefix = (known after apply)
+ tags = (known after apply)
}
+ source_selection_criteria {
+ sse_kms_encrypted_objects {
+ enabled = (known after apply)
}
}
}
}
+ server_side_encryption_configuration {
+ rule {
+ bucket_key_enabled = (known after apply)
+ apply_server_side_encryption_by_default {
+ kms_master_key_id = (known after apply)
+ sse_algorithm = (known after apply)
}
}
}
+ versioning {
+ enabled = (known after apply)
+ mfa_delete = (known after apply)
}
+ website {
+ error_document = (known after apply)
+ index_document = (known after apply)
+ redirect_all_requests_to = (known after apply)
+ routing_rules = (known after apply)
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
─────────────────────────────────────────────────────────────────────────────
Saved the plan to: plan
To perform exactly these actions, run the following command to apply:
terraform apply "plan"
Please review the diff output above for cdktf-sample
❯ Approve Applies the changes outlined in the plan.
Dismiss
Stop
見慣れたterraformの差分がでてきます。cdktf deploy
コマンドは裏ではterraform apply
が実行されます。
Approveすると、無事にバケットが作成できました!
cdktf-sample aws_s3_bucket.sample_bucket (sample_bucket): Creating...
cdktf-sample aws_s3_bucket.sample_bucket (sample_bucket): Creation complete after 1s [id=cdktf-sample-bucket-misaosyushi]
cdktf-sample
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
No outputs found.
既存のTerraformをCDKTFに変換してみる
既存のTerraformプロジェクトから生成することもできるようなので試してみます。
cdktf initコマンドの以下の質問でYes
にし、terraformプロジェクトのパスを指定します。
? Do you want to start from an existing Terraform project? Yes
? Please enter the path to the Terraform project /path/to/existing-terraform-project
今回はこのようなmain.tfを用意し読み込ませてみました。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
resource "aws_s3_bucket" "sample_bucket" {
bucket = "cdktf-sample-bucket-misaosyushi2"
}
生成されるファイルは空のプロジェクトを構築するときと同じでした。
main.tsの中身を確認してみると
/*Provider bindings are generated by running cdktf get.
See https://cdk.tf/provider-generation for more details.*/
import * as aws from "./.gen/providers/aws";
// Copyright (c) HashiCorp, Inc
// SPDX-License-Identifier: MPL-2.0
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";
class MyStack extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);
new aws.provider.AwsProvider(this, "aws", {
region: "ap-northeast-1",
});
new aws.s3Bucket.S3Bucket(this, "sample_bucket", {
bucket: "cdktf-sample-bucket-misaosyushi2",
});
}
}
const app = new App();
new MyStack(app, "cdktf-sample2");
app.synth();
ちゃんとterraformと同じS3バケットがCDKTFのコードとして生成されました。素晴らしい!
まとめ
2年ほど前から楽しみにしていたプロジェクトなのでついにGAになって嬉しいですし、とっても便利そうでワクワクしました。Terraformは便利ですが書いていてあまり楽しくないなあと個人的には感じていました。型の恩恵を生かしてTerraoformが書けるのは良い開発体験になりそうです。
Terraformのmoduleで抽象化しているリソースはCDKTFでどうやって書くのかな?と気になったんですが、jsonを読み込ませることでコードバインディングを生成してくれる的なことが書いてありました。
実プロジェクトに取り入れるにはmodule部分のコード化は必須かなと思っているのでこのあたりも今度試してみたいと思います。
以上、CDKTFを触ってみたまとめと所感でした!
Discussion