地方エンジニアの学習日記

興味ある技術の雑なメモだったりを書いてくブログ。たまに日記とガジェット紹介。

【CDKTF】触ってみる

github.com

terraform-cdkとは

CDK for Terraform (CDKTF)を使えば、TypeScript、Python、Java、C#、Goといった馴染みのあるプログラミング言語でクラウドインフラを定義し、Terraformを通じてプロビジョニングできます。これにより、HCLを学ぶ必要がなく、Terraformエコシステムを活用しながら既存のツールチェーン(テスト、依存管理など)の強みを生かせます。

対応しているProviders

github.com

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による補完みたいなメリットはありそうだけど保守する際に開発者ごとに書き味が違うコードが大量生産されると再利用性とかが低くなって大変とかそういうイメージ。