はじめに
基本的な使い方をまとめてみました。
CDK for Terraform Is Now Generally Available
今回は TypeScript を使っている前提で記述するため、他の言語を利用する場合は適宜読み替えてください。
CDK for Terraform とは
CDK for Terraform | Terraform by HashiCorp
CDK for Terraform を使うと使い慣れたプログラミング言語を使ってインフラの定義やプロビジョニングを行うことができます。
これにより HCL を学ぶことなく Terraform エコシステム全体にアクセスでき、またテストや依存関係管理などの既存のツールチェインの力を活用することができます。
CDK for Terraform では 2022年08月29日現在、次の言語をサポートしています。
- TypeScript
- Python
- Java
- C#
- Go
言語によっては実験的な新機能が利用できない場合もあります。
( CDK for Terraform 自身が TypeScript で作られているからか、 TypeScript のサポートが一番手厚い印象を受けました。 )
CDK for Terraform では Terraform Registry で利用可能な全ての Terraform プロバイダとモジュールを利用できます。また、 Terraform Cloud, Terraform Enterprise, Sentinel なども利用可能です。
準備
前提条件
- Terraform CLI ( v1.1 以上 )
- Node.js ( v16 以上 )
cdktf cli をインストール
CDK for Terraform は cdktf cli を使ってさまざまなオペレーションを実行します。
cdktf cli は npm でインストールすることができます。
# 安定版リリース
$ npm install --global cdktf-cli@latest
# 開発版リリース
$ npm install --global cdktf-cli@next
使い方
cdktf init – プロジェクトを作成
cdktf init
でプロジェクトを作成することができます。
$ cdktf init --template=typescript --local
--template
でテンプレートを指定します。
用意されているテンプレートは下記ページから参照できます。
terraform-cdk/packages/cdktf-cli/templates at main · hashicorp/terraform-cdk--local
で tfstate をローカルで管理することを指定します。
・Terraform Cloud を利用する場合はこのオプションは不要です。
・AWS S3 などによるリモートステートを利用する場合もここでは--local
を指定します。
実行すると対話形式でさまざまな設定をしていきます。
- プロジェクト名
- プロジェクトの説明
- 既存の Terraform プロジェクトを利用するかどうか ( 2022年08月29日現在 TypeScript のみ )
利用する場合、 Terraform プロジェクトへのパス - クラッシュレポートを送信するかどうか
? Project Name
? Project Description
? Do you want to start from an existing Terraform project?
? Please enter the path to the Terraform project
? 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
プロジェクトの作成が完了するとカレントディレクトリに様々なファイルが作成されます。
$ tree -aFL 1
./
├── .gitignore
├── .npmrc
├── __tests__/
├── cdktf.json
├── help
├── jest.config.js
├── main.ts
├── node_modules/
├── package-lock.json
├── package.json
├── setup.js
└── tsconfig.json
2 directories, 10 files
インフラリソースの定義は主に main.ts
に記述していきます。
CDK for Terraform の設定は cdktf.json
で行います。
cdktf.json の例
{
"language": "typescript",
"app": "npx ts-node main.ts",
"projectId": "00000000-0000-0000-0000-000000000000",
"sendCrashReports": "false",
"terraformProviders": [],
"terraformModules": [],
"context": {
"excludeStackIdFromLogicalIds": "true",
"allowSepCharsInLogicalIds": "true"
}
}
cdktf.json
の詳細につきましては下記ドキュメントをご参照ください。
Configuration | Terraform by HashiCorp
cdktf provider add – プロバイダを追加する
cdktf provider add
でプロバイダを追加することができます。
例えば aws プロバイダを追加する場合は次のように実行します。
$ cdktf provider add "aws@~>4.0"
cdktf provider add
を実行すると、まずビルド済みのパッケージ ( 後述 ) が存在するかどうかチェックされます。
ビルド済みのパッケージが見つからなかった場合は cdktf.json
の terraformProviders
にプロバイダ情報が追記され、 .gen/providers/{プロバイダ名}/
ディレクトリ以下にコードが生成されます。
次の例では sops プロバイダ を追加しています。
$ cdktf provider add "carlpett/sops@~>0.7"
Checking whether pre-built provider exists for the following constraints:
provider: carlpett/sops
version : ~>0.7
language: typescript
Pre-built provider does not exist for the given constraints.
Adding local provider registry.terraform.io/carlpett/sops with version constraint ~>0.7 to cdktf.json
Generated typescript constructs in the output directory: .gen
cdktf.json
の terraformProviders
に sops プロバイダの情報が追記されます。
// cdktf.json
{
// ...省略
"terraformProviders": [
"carlpett/sops@~>0.7"
],
// ...省略
}
.gen/providers/sops/
ディレクトリ以下にコードが生成されています。
$ tree -aF .gen/providers
.gen/providers/
└── sops/
├── data-sops-external.ts
├── data-sops-file.ts
├── index.ts
└── sops-provider.ts
1 directory, 4 files
生成されたコードは import して使用できます。
// main.ts
import { DataSopsFile } from "./.gen/providers/sops";
// ...省略
// data "sops_file" "some_file" {
// source_file = "path/to/SOURCE_FILE"
// }
new DataSopsFile(stack, "some_file", {
sourceFile: "path/to/SOURCE_FILE"
});
// ...省略
大きなスキーマを持つプロバイダの場合はコードを生成するために数分かかることがあります。
そのため、よく使われるプロバイダはビルド済みのパッケージとして公開されています。
全てのビルド済みパッケージは下記ページから参照できます。
HashiCorp
ビルド済みのパッケージが見つかった場合はそのパッケージがインストールされます。
次の例では aws プロバイダを追加しています。
aws プロバイダの場合はビルド済みの @cdktf/provider-aws パッケージが用意されているので、こちらがインストールされます。
$ cdktf provider add "aws@~>4.0"
Checking whether pre-built provider exists for the following constraints:
provider: aws
version : ~>4.0
language: typescript
cdktf : 0.12.1
Found pre-built provider.
Adding package @cdktf/provider-aws @ 9.0.15
Installing package @cdktf/provider-aws @ 9.0.15 using npm.
Package installed.
この場合はパッケージがインストールされるだけで、 cdktf.json
は更新されず、 .gen/providers/
ディレクトリ以下にコードが生成されることもありません。
また、ビルド済みのパッケージは cdktf provider add
を使わずに npm install
でインストールすることもできます。
$ npm install @cdktf/provider-aws
インフラリソースを定義する
main.ts
にコードを記述してインフラリソースを定義していきます。
CDK for Terraform ではスタックという単位でインフラリソースを定義していきます。
プロビジョニングはスタックごとに実行することができ、 tfstate ファイルもスタックごとに分けて管理されます。
スタックを定義するには TerraformStack
クラスを継承したクラスを用意します。
次の例では S3 バケットを作成するスタックをひとつだけ作成しています。
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";
import { AwsProvider, s3 } from "@cdktf/provider-aws";
// スタックを定義
class MyStack extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);
// provider "aws" {
// region = "us-east-1"
// }
new AwsProvider(this, "aws", {
region: "us-east-1",
});
// resource "aws_s3_bucket" "example_bucket" {
// bucket_prefix = "example-"
// }
new s3.S3Bucket(this, "example_bucket", {
bucketPrefix: "example-",
});
}
}
const app = new App();
// スタックを作成
new MyStack(app, "example-stack");
app.synth();
スタックは複数作成することもできます。
次の例では環境ごとにスタックを作成しています。
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";
import { AwsProvider, s3 } from "@cdktf/provider-aws";
// スタックを定義
class MyStack extends TerraformStack {
// スタックのコンストラクタで環境を指定できるようにする
constructor(scope: Construct, id: string, env: "dev" | "stg" | "prd") {
super(scope, id);
new AwsProvider(this, "aws", {
region: "us-east-1",
});
new s3.S3Bucket(this, "example_bucket", {
bucketPrefix: `example-${env}-`,
tags: {
environment: env,
},
});
}
}
const app = new App();
// 環境ごとにスタックを作成
new MyStack(app, "example-stack-dev", "dev");
new MyStack(app, "example-stack-stg", "stg");
new MyStack(app, "example-stack-prd", "prd");
app.synth();
他にも用途ごとにスタックを作成したり、スタック間の依存関係を管理したりすることもできます。
詳しくは下記ドキュメントをご参照ください。
Stacks | Terraform by HashiCorp
インフラリソースをプロビジョニングする
cdktf cli を使用して定義したインフラリソースをプロビジョニングすることができます。
cdktf diff – 差分を表示
cdktf diff
( もしくは cdktf plan
) で差分を表示することができます ( = terraform plan
) 。
- 出力例
$ cdktf diff
example-stack Initializing the backend...
example-stack
Successfully configured the backend "local"! Terraform will automatically
use this backend unless the backend configuration changes.
example-stack Initializing provider plugins...
- Finding hashicorp/aws versions matching "4.27.0"...
example-stack - Using hashicorp/aws v4.27.0 from the shared cache directory
example-stack Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
example-stack Terraform has been successfully initialized!
example-stack
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.
example-stack 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:
example-stack # aws_s3_bucket.example_bucket (example_bucket) will be created
+ resource "aws_s3_bucket" "example_bucket" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = (known after apply)
+ bucket_domain_name = (known after apply)
+ bucket_prefix = "example-"
+ 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.
─────────────────────────────────────────────────────────────────────────────
example-stack Saved the plan to: plan
To perform exactly these actions, run the following command to apply:
terraform apply "plan"
cdktf deploy – プロビジョニングを実行
cdktf deploy
( もしくは cdktf apply
) で定義に合わせてインフラリソースをプロビジョニングします ( = terraform apply
) 。
- 出力例
$ cdktf deploy
example-stack Initializing the backend...
example-stack Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
example-stack - Using previously-installed hashicorp/aws v4.27.0
Terraform has been successfully initialized!
example-stack
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.
example-stack 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:
example-stack # aws_s3_bucket.example_bucket (example_bucket) will be created
+ resource "aws_s3_bucket" "example_bucket" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = (known after apply)
+ bucket_domain_name = (known after apply)
+ bucket_prefix = "example-"
+ 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.
example-stack
─────────────────────────────────────────────────────────────────────────────
Saved the plan to: plan
To perform exactly these actions, run the following command to apply:
terraform apply "plan"
example-stack aws_s3_bucket.example_bucket (example_bucket): Creating...
example-stack aws_s3_bucket.example_bucket (example_bucket): Creation complete after 5s [id=example-20220828121939889900000001]
example-stack
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
No outputs found.
実行すると Approve
、 Dismiss
、 Stop
の選択肢が表示されます。
方向キーで選択し、 Enter キーで決定します。
Please review the diff output above for {スタック名}
❯ Approve
Dismiss
Stop
選択肢 | 説明 |
---|---|
Approve | 現在のスタックのプロビジョニングを実行する。 |
Dismiss | 現在のスタックのプロビジョニングを中止し、次のスタックの確認に進む。また、中止したスタックに依存しているスタックのプロビジョニングも中止される。 |
Stop | 以降の全てのスタックのプロビジョニングを中止する。 |
--auto-approve
フラグを指定すると確認をスキップして全てのスタックのプロビジョニングが実行されます。
cdktf destroy – リソースを削除
cdktf destroy
でインフラリソースを削除します ( = terraform destroy
) 。
- 出力例
$ cdktf destroy --auto-approve
example-stack Initializing the backend...
example-stack Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
example-stack - Using previously-installed hashicorp/aws v4.27.0
example-stack 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.
example-stack aws_s3_bucket.example_bucket (example_bucket): Refreshing state... [id=example-20220828121939889900000001]
example-stack Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
example-stack # aws_s3_bucket.example_bucket (example_bucket) will be destroyed
- resource "aws_s3_bucket" "example_bucket" {
- arn = "arn:aws:s3:::example-20220828121939889900000001" -> null
- bucket = "example-20220828121939889900000001" -> null
- bucket_domain_name = "example-20220828121939889900000001.s3.amazonaws.com" -> null
- bucket_prefix = "example-" -> null
- bucket_regional_domain_name = "example-20220828121939889900000001.s3.amazonaws.com" -> null
- force_destroy = false -> null
- hosted_zone_id = "**************" -> null
- id = "example-20220828121939889900000001" -> null
- object_lock_enabled = false -> null
- region = "us-east-1" -> null
- request_payer = "BucketOwner" -> null
- tags = {} -> null
- tags_all = {} -> null
- grant {
- id = "****************************************************************" -> null
- permissions = [
- "FULL_CONTROL",
] -> null
- type = "CanonicalUser" -> null
}
- versioning {
- enabled = false -> null
- mfa_delete = false -> null
}
}
Plan: 0 to add, 0 to change, 1 to destroy.
─────────────────────────────────────────────────────────────────────────────
Saved the plan to: plan
To perform exactly these actions, run the following command to apply:
terraform apply "plan"
example-stack aws_s3_bucket.example_bucket (example_bucket): Refreshing state... [id=example-20220828121939889900000001]
example-stack Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
example-stack # aws_s3_bucket.example_bucket (example_bucket) will be destroyed
- resource "aws_s3_bucket" "example_bucket" {
- arn = "arn:aws:s3:::example-20220828121939889900000001" -> null
- bucket = "example-20220828121939889900000001" -> null
- bucket_domain_name = "example-20220828121939889900000001.s3.amazonaws.com" -> null
- bucket_prefix = "example-" -> null
- bucket_regional_domain_name = "example-20220828121939889900000001.s3.amazonaws.com" -> null
- force_destroy = false -> null
- hosted_zone_id = "**************" -> null
- id = "example-20220828121939889900000001" -> null
- object_lock_enabled = false -> null
- region = "us-east-1" -> null
- request_payer = "BucketOwner" -> null
- tags = {} -> null
- tags_all = {} -> null
- grant {
- id = "****************************************************************" -> null
- permissions = [
- "FULL_CONTROL",
] -> null
- type = "CanonicalUser" -> null
}
- versioning {
- enabled = false -> null
- mfa_delete = false -> null
}
}
Plan: 0 to add, 0 to change, 1 to destroy.
example-stack aws_s3_bucket.example_bucket (example_bucket): Destroying... [id=example-20220828121939889900000001]
example-stack aws_s3_bucket.example_bucket (example_bucket): Destruction complete after 1s
example-stack
Destroy complete! Resources: 1 destroyed.
こちらも cdktf deploy
と同様に Approve
、 Dismiss
、 Stop
の選択肢が表示され、 --auto-approve
フラグでそれらをスキップすることができます。
Terraform Module を利用する
CDK for Terraform では、 Terraform Registry や GitHub にある Terraform Module を使うこともできます。
まず cdktf.json
の terraformModules
に利用するモジュール情報を追記します。
次の例では、 aws の vpc モジュールとローカルに存在する local-module
モジュールを追記しています。
// cdktf.json
{
// ...省略
"terraformModules": [
// https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws
{
"name": "vpc",
"source": "terraform-aws-modules/vpc/aws",
"version": "~> 3.0"
},
// ローカルのモジュールを指定することも可能
{
"name": "local-module",
"source": "./path/to/local-module"
}
],
// ...省略
}
この状態で cdktf get
コマンドを実行すると .gen/modules/
ディレクトリ以下にコードが生成されます。
$ cdktf get
Generated typescript constructs in the output directory: .gen
$ tree -F .gen/modules
.gen/modules/
├── local-module.ts
└── vpc.ts
0 directories, 2 files
生成されたコードは import して利用することができます。
import { Vpc } from "./.gen/modules/vpc";
// ...省略
new Vpc(stack, "example_vpc", {
name: "my-vpc",
cidr: "10.0.0.0/16",
azs: ["us-west-2a", "us-west-2b", "us-west-2c"],
privateSubnets: ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"],
publicSubnets: ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"],
enableNatGateway: true,
});
// ...省略
cdktf get
によるコード生成を利用せずに TerraformHclModule
を使用してモジュールを参照することもできます。
ただし、この場合は引数の型ヒントが利用できないことに注意してください。
import { TerraformHclModule } from "cdktf";
import { AwsProvider } from "@cdktf/provider-aws";
// ...省略
const provider = new AwsProvider(stack, "provider", {
region: "us-east-1",
});
const module = new TerraformHclModule(stack, "example_vpc", {
source: "terraform-aws-modules/vpc/aws",
variables: {
name: "my-vpc",
cidr: "10.0.0.0/16",
azs: ["us-west-2a", "us-west-2b", "us-west-2c"],
privateSubnets: ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"],
publicSubnets: ["10.0.101.0/24", "10.0.102.0/24", "10.0.103.0/24"],
enableNatGateway: true,
},
providers: [provider],
});
// ...省略
ユニットテスト
CDK for Terraform ではユニットテスト用のライブラリが提供されています。
// 引用: <https://www.terraform.io/cdktf/test/unit-tests#write-assertions>
import { Testing } from "cdktf";
import { Image, Container } from "../.gen/providers/docker";
import MyApplicationsAbstraction from "../app"; // Could be a class extending from Construct
describe("Unit testing using assertions", () => {
it("should contain a container", () => {
expect(
Testing.synthScope((scope) => {
new MyApplicationsAbstraction(scope, "my-app", {});
})
).toHaveResource(Container);
});
it("should use an ubuntu image", () => {
expect(
Testing.synthScope((scope) => {
new MyApplicationsAbstraction(scope, "my-app", {});
})
).toHaveResourceWithProperties(Image, { name: "ubuntu:latest" });
});
});
詳しくは下記ドキュメントをご参照ください。
Unit Tests | Terraform by HashiCorp
cdktf synth – Terraform CLI から利用できる設定ファイルを生成する
cdktf synth
は Terraform CLI から利用できる JSON 形式の設定ファイルを生成します。
実行すると ./cdktf.out/stacks/{スタック名}/cdk.tf.json
という名前で JSON ファイルが生成されます。
$ cdktf synth
Generated Terraform code for the stacks: cdktf-aws-example
$ ls cdktf.out/stacks/{スタック名}/
cdk.tf.json
cdk.tf.json
の例
{
"//": {
"metadata": {
"backend": "local",
"stackName": "example-stack",
"version": "0.12.2"
},
"outputs": {
}
},
"provider": {
"aws": [
{
"region": "us-east-1"
}
]
},
"resource": {
"aws_s3_bucket": {
"example_bucket": {
"//": {
"metadata": {
"path": "example-stack/example_bucket",
"uniqueId": "example_bucket"
}
},
"bucket_prefix": "example-"
}
}
},
"terraform": {
"backend": {
"local": {
"path": "/path/to/terraform.example-stack.tfstate"
}
},
"required_providers": {
"aws": {
"source": "aws",
"version": "4.27.0"
}
}
}
}
cdk.tf.json
があるディレクトリ内で Terraform CLI を実行することができます。
$ cd ./cdktf.out/stacks/{スタック名}/
$ terraform init
$ terraform apply
cdktf convert – HCL を CDK for Terraform のコードに変換する
cdktf convert
で HCL で書かれた tf コードを CDK for Terraform のコードに変換することができます。
( 2022年08月29日現在 Go では使えません )
例えば次のような s3.tf
ファイルが存在する場合
# s3.tf
resource "aws_s3_bucket" "example_bucket" {
bucket_prefix = "example"
}
次のように実行することで標準出力に変換後のコードが出力されます。
# `--language` で言語を指定することができる
$ cat s3.tf | cdktf convert --language typescript
出力例:
/*Provider bindings are generated by running cdktf get.
See <https://cdk.tf/provider-generation> for more details.*/
import * as aws from "./.gen/providers/aws";
/*The following providers are missing schema information and might need manual adjustments to synthesize correctly: aws.
For a more precise conversion please use the --provider flag in convert.*/
new aws.s3.S3Bucket(this, "example_bucket", {
bucket_prefix: "example",
});
公式サンプル
下記ページで言語ごとに様々なサンプルが用意されています。
CDKTF Examples and Guides | Terraform by HashiCorp
個人的な感想
- Terraform に慣れていればそれほど苦労せずにキャッチアップできると思います。
- 特に使いづらかったりすることもなく結構いい感じでした。
ただ、plan のインデントが潰されるのはどうにかしてほしい。 - HCL で満足している人が多いと思うので、あまり流行るイメージはない印象を受けました。
「HCL を学ばなくていい」とはいうものの、 HCL 自体そんなに学習コストがかかる言語でもないはずなので、
HCL の良さの一つとしてとにかくシンプル ( 読めばわかる、誰が書いても大体同じようなコードになる ) というのがあるが、 こういった HCL の恩恵を放棄してまで CDK for Terraform を使うことにどれほどのメリットがあるのか、みたいなところが気になりました。 - 類似のツールとしては Pulumi があります。こちらも機会があれば触ってみたいです。
Pulumi – Universal Infrastructure as Code