terraform-cdkとは
CDK for Terraform (CDKTF)を使えば、TypeScript、Python、Java、C#、Goといった馴染みのあるプログラミング言語でクラウドインフラを定義し、Terraformを通じてプロビジョニングできます。これにより、HCLを学ぶ必要がなく、Terraformエコシステムを活用しながら既存のツールチェーン(テスト、依存管理など)の強みを生かせます。
対応しているProviders
AWSやGoogle Cloudはもちろんその他にも結構対応している。
Usage
% cdktf --help cdktf コマンド: cdktf init Create a new cdktf project from a template. cdktf get Generate CDK Constructs for Terraform providers and modules. cdktf convert Converts a single file of HCL configuration to CDK for Terraform. Takes the file to be converted on stdin. cdktf deploy [stacks...] Deploy the given stacks [エイリアス: apply] cdktf destroy [stacks..] Destroy the given stacks cdktf diff [stack] Perform a diff (terraform plan) for the given stack [エイリアス: plan] cdktf list List stacks in app. cdktf login Retrieves an API token to connect to Terraform Cloud or Terraform Enterprise. cdktf synth Synthesizes Terraform code for the given app in a directory. [エイリアス: synthesize] cdktf watch [stacks..] [experimental] Watch for file changes and automatically trigger a deploy cdktf output [stacks..] Prints the output of stacks [エイリアス: outputs] cdktf debug Get debug information about the current project and environment cdktf provider A set of subcommands that facilitates provider management cdktf completion generate completion script
initしてdeployすれば動く。個人的に便利だなと思ったのはconvert。以下のようなHCLがあるときに
provider "aws" { region = "us-east-1" } variable "read_replicas" { description = "List of read replica configurations" type = map(object({ instance_class = string engine = string })) default = { replica1 = { instance_class = "db.t3.micro" engine = "mysql" } replica2 = { instance_class = "db.t3.micro" engine = "mysql" } } } resource "aws_db_instance" "primary" { identifier = "primary-db" allocated_storage = 20 engine = "mysql" engine_version = "8.0" instance_class = "db.t3.micro" username = "admin" password = "password" parameter_group_name = "default.mysql8.0" skip_final_snapshot = true } resource "aws_db_instance" "read_replica" { for_each = var.read_replicas identifier = "read-replica-${each.key}" instance_class = each.value.instance_class engine = each.value.engine source_db_instance_identifier = aws_db_instance.primary.id }
convertを打つと以下のようなTypeScriptが生成される。
import { Construct } from "constructs"; import { VariableType, TerraformVariable, Token, TerraformIterator, Fn, } from "cdktf"; /* * Provider bindings are generated by running `cdktf get`. * See https://cdk.tf/provider-generation for more details. */ import { DbInstance } from "./.gen/providers/aws/db-instance"; import { AwsProvider } from "./.gen/providers/aws/provider"; class MyConvertedCode extends Construct { constructor(scope: Construct, name: string) { super(scope, name); /*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.*/ /*Terraform Variables are not always the best fit for getting inputs in the context of Terraform CDK. You can read more about this at https://cdk.tf/variables*/ new AwsProvider(this, "aws", { region: "us-east-1", }); const readReplicas = new TerraformVariable(this, "read_replicas", { default: [ { replica1: [ { engine: "mysql", instance_class: "db.t3.micro", }, ], replica2: [ { engine: "mysql", instance_class: "db.t3.micro", }, ], }, ], description: "List of read replica configurations", type: VariableType.map( VariableType.object({ engine: VariableType.STRING, instance_class: VariableType.STRING, }) ), }); const primary = new DbInstance(this, "primary", { allocated_storage: 20, engine: "mysql", engine_version: "8.0", identifier: "primary-db", instance_class: "db.t3.micro", parameter_group_name: "default.mysql8.0", password: "password", skip_final_snapshot: true, username: "admin", }); /*In most cases loops should be handled in the programming language context and not inside of the Terraform context. If you are looping over something external, e.g. a variable or a file input you should consider using a for loop. If you are looping over something only known to Terraform, e.g. a result of a data source you need to keep this like it is.*/ const readReplicaForEachIterator = TerraformIterator.fromList( Token.asAny(readReplicas.value) ); new DbInstance(this, "read_replica", { engine: Fn.lookupNested(readReplicaForEachIterator.value, ["engine"]), identifier: "read-replica-${" + readReplicaForEachIterator.key + "}", instance_class: Fn.lookupNested(readReplicaForEachIterator.value, [ "instance_class", ]), source_db_instance_identifier: primary.id, forEach: readReplicaForEachIterator, }); } }
感想
Terraformだけで済むならTerraformだけで済ませたいが動的にインフラをプロビジョニングしたいケースとかが出てくると確かに便利そうだなと思った。例えば開発者環境だったりホスティングサービスの会社とかだと結構便利に使えそう。そうじゃない場合はおそらく使うケースってあまりないかなぁと思った。IDEによる補完みたいなメリットはありそうだけど保守する際に開発者ごとに書き味が違うコードが大量生産されると再利用性とかが低くなって大変とかそういうイメージ。