HashiCorp公式ブログによると、8月からCDK for TerraformがGAリリースされ、さらにGoにも完全にサポートするようになったので、早速使ってみました。

CDK for Terraform(CDKTF)とは

CDK for Terraform(CDKTF)は、使い慣れたプログラミング言語を使用してインフラストラクチャを定義およびプロビジョニングできるツールです。CDKTFを利用することで、HashiCorp構成言語(HCL)を習得することなくTerraformエコシステム全体にアクセスでき、既存のツールチェーンのパワーをテスト、依存関係管理などに活用できるようになリます。

1. CDK for Terraform実装の前提条件

CDKTFを使う前に、下記の前提条件を満たす必要があります:

  • Terraform v1.0以上インストールされていること
  • AWSアカウントを持ち、かつCredentials情報が設定されていること
  • Go v1.16以上インストールされていること

2. CDK for Terraformのインストール

CDKTFは、ほぼ全OSでnpmを使ってインストールすることができます。
また、MacOSではHomebrewでCDKTFをインストールできます。

npmの場合:

npm install --global cdktf-cli@latest

Homebrewの場合:

brew install cdktf

3. CDK for Terraformプロジェクトの作成と初期化

CDKTFプロジェクトを作成するには、空きディレクトリが必要のため、
まずプロジェクト用にcdktf-demoというディレクトリを作成します。

mkdir cdktf-demo

続いて、cdktf-demoディレクトリに移動します。

cd cdktf-demo

cdktf-demoディレクトリ内で、cdktf initコマンドを使って好きな言語でプロジェクトを初期化します。
本記事ではGoを使用するので、--templateにgoを記入します。

cdktf init --template="go"

? Do you want to continue with Terraform Cloud remote state management? No
? Project Name cdktf-demo
? Project Description A simple getting started project for cdktf.
? 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
go: downloading github.com/hashicorp/terraform-cdk-go/cdktf v0.12.0
go: downloading github.com/aws/constructs-go/constructs/v10 v10.1.56
go: downloading github.com/aws/jsii-runtime-go v1.62.0
go: downloading github.com/Masterminds/semver/v3 v3.1.1
go: upgraded github.com/aws/constructs-go/constructs/v10 v10.0.25 => v10.1.56
========================================================================================================

  Your cdktf go project is ready!

  cat help                Prints this message

  Compile:
    go build              Builds your go project

  Synthesize:
    cdktf synth [stack]   Synthesize Terraform resources to cdktf.out/

  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 given stack

  Learn more about using modules and providers https://cdk.tf/modules-and-providers

Use Providers:

  Use the add command to add providers:

  cdktf provider add "aws@~>3.0" null kreuzwerker/docker

  Learn more: https://cdk.tf/modules-and-providers

========================================================================================================

上記のようなアウトプットを確認ができましたら、プロジェクトの初期化が完了となります。

4. AWSプロバイダーの追加

続いて、AWSプロバイダーを追加します。

cdktf provider add "aws@~>4.0"

Checking whether pre-built provider exists for the following constraints:
  provider: aws
  version : ~>4.0
  language: go
  cdktf   : v0.12.0

Found pre-built provider.
Adding package github.com/hashicorp/cdktf-provider-aws-go/aws @ 9.0.10
[2022-08-08T11:56:34.251] [ERROR] default - go: downloading github.com/hashicorp/cdktf-provider-aws-go v0.0.0-20220804010506-f53ab9125fed

go: downloading github.com/hashicorp/cdktf-provider-aws-go v0.0.0-20220804010506-f53ab9125fed
[2022-08-08T11:57:15.499] [ERROR] default - go: module github.com/hashicorp/cdktf-provider-aws-go@upgrade found (v0.0.0-20220804010506-f53ab9125fed), but does not contain package github.com/hashicorp/cdktf-provider-aws-go/aws

go: module github.com/hashicorp/cdktf-provider-aws-go@upgrade found (v0.0.0-20220804010506-f53ab9125fed), but does not contain package github.com/hashicorp/cdktf-provider-aws-go/aws
Error: non-zero exit code 1

なぜか公式のドキュメント通りに行かず、パッケージが見つからないようですが、
https://pkg.go.dev/ でcdktf-provider-aws-goライブラリーを調べたところ、パッケージのURLはgithub.com/hashicorp/cdktf-provider-aws-go/awsではなく
github.com/hashicorp/cdktf-provider-aws-go/aws/v9になったため、go getで手動で追加します。

go get github.com/hashicorp/cdktf-provider-aws-go/aws/v9

go: downloading github.com/hashicorp/cdktf-provider-aws-go/aws/v9 v9.0.10
go: downloading github.com/aws/jsii-runtime-go v1.63.2
go: downloading github.com/aws/constructs-go/constructs/v10 v10.1.66
go: upgraded github.com/aws/constructs-go/constructs/v10 v10.1.56 => v10.1.66
go: added github.com/hashicorp/cdktf-provider-aws-go/aws/v9 v9.0.10

5. AWSリソースの作成

S3バケットを作成したいので、まずS3のライブラリーを追加します:

go get github.com/hashicorp/cdktf-provider-aws-go/aws/v9/s3

続いて、cdktf-demoディレクトリ内のmain.goを編集して、新しいS3バケットを作ってみましょう。

package main

import (
    "github.com/aws/constructs-go/constructs/v10"
    "github.com/aws/jsii-runtime-go"
    "github.com/hashicorp/cdktf-provider-aws-go/aws/v9"
    "github.com/hashicorp/cdktf-provider-aws-go/aws/v9/s3"
    "github.com/hashicorp/terraform-cdk-go/cdktf"
)

func NewMyStack(scope constructs.Construct, id string) cdktf.TerraformStack {
    stack := cdktf.NewTerraformStack(scope, &id)

    s3Bucket := s3.NewS3Bucket(stack, jsii.String("cdktf-demo-s3-bucket"), &s3.S3BucketConfig{
        Bucket: jsii.String("cdktf-demo-s3-bucket"),
    })

    // アウトプット
    cdktf.NewTerraformOutput(stack, jsii.String("bucket_arn"), &cdktf.TerraformOutputConfig{
        Value: s3Bucket.Arn(),
    })

    return stack
}

func main() {
    app := cdktf.NewApp(nil)

    stack := NewMyStack(app, "cdktf-demo")

    const region = "ap-northeast-1"

    // プロバイダー設定
    aws.NewAwsProvider(stack, jsii.String("aws"), &aws.AwsProviderConfig{
        Region: jsii.String(region),
    })

    // ステージファイル設定
    cdktf.NewS3Backend(stack, &cdktf.S3BackendProps{
        Bucket: jsii.String("cdktf-demo-bucket"),  // 事前にステージ管理のS3バケットの作成が必要です。
        Key:    jsii.String("s3/demo-s3-bucket.tfstate"),
        Region: jsii.String(region),
    })

    app.Synth()
}

go mod tidyを実行します。

❯ go mod tidy

go: downloading github.com/stretchr/testify v1.8.0
go: downloading gopkg.in/yaml.v3 v3.0.1
go: downloading github.com/pmezard/go-difflib v1.0.0
go: downloading github.com/davecgh/go-spew v1.1.1

準備が終わりましたので、cdktf planを実行して差分を見てみましょう。

❯ cdktf plan

cdktf-demo  Initializing the backend...
cdktf-demo
            Successfully configured the backend "s3"! Terraform will automatically
            use this backend unless the backend configuration changes.
cdktf-demo  Initializing provider plugins...
            - Finding hashicorp/aws versions matching "4.24.0"...
cdktf-demo  - Installing hashicorp/aws v4.24.0...
cdktf-demo  - Installed hashicorp/aws v4.24.0 (signed by HashiCorp)
cdktf-demo  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.

            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-demo  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-demo    # aws_s3_bucket.cdktf-demo-s3-bucket (cdktf-demo-s3-bucket) will be created
              + resource "aws_s3_bucket" "cdktf-demo-s3-bucket" {
            + acceleration_status         = (known after apply)
            + acl                         = (known after apply)
            + arn                         = (known after apply)
            + bucket                      = "cdktf-demo-s3-bucket"
            + 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)
            }
            }
cdktf-demo  Plan: 1 to add, 0 to change, 0 to destroy.

            Changes to Outputs:
            + bucket_arn = (known after apply)

            ─────────────────────────────────────────────────────────────────────────────

            Saved the plan to: plan

            To perform exactly these actions, run the following command to apply:
            terraform apply "plan"

特に問題なさそうなので続いて、cdktf applyでS3バケットを作成します。

❯ cdktf apply

cdktf-demo  Initializing the backend...
cdktf-demo  Initializing provider plugins...
cdktf-demo  - Reusing previous version of hashicorp/aws from the dependency lock file
cdktf-demo  - Using previously-installed hashicorp/aws v4.24.0
cdktf-demo  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-demo  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-demo    # aws_s3_bucket.cdktf-demo-s3-bucket (cdktf-demo-s3-bucket) will be created
              + resource "aws_s3_bucket" "cdktf-demo-s3-bucket" {
            + acceleration_status         = (known after apply)
            + acl                         = (known after apply)
            + arn                         = (known after apply)
            + bucket                      = "cdktf-demo-s3-bucket"
            + 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.

cdktf-demo
            Changes to Outputs:
            + bucket_arn = (known after apply)

            ─────────────────────────────────────────────────────────────────────────────

            Saved the plan to: plan

            To perform exactly these actions, run the following command to apply:
            terraform apply "plan"
cdktf-demo  aws_s3_bucket.cdktf-demo-s3-bucket (cdktf-demo-s3-bucket): Creating...
cdktf-demo  aws_s3_bucket.cdktf-demo-s3-bucket (cdktf-demo-s3-bucket): Creation complete after 3s [id=cdktf-demo-s3-bucket]
cdktf-demo
            Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

            Outputs:

            bucket_arn = "arn:aws:s3:::cdktf-demo-s3-bucket"

  cdktf-demo
  bucket_arn = arn:aws:s3:::cdktf-demo-s3-bucket

以上でCDKTFによりAWSリソースの作成ができました。
リソースの削除もterraformコマンドと同様にcdktf destroyで削除可能です。

プロフィール

祁鋭
祁鋭
Web広告事業→AWSクラウドエンジニア→フルスタックエンジニア
最近SaaSの開発、Rustとtauri絶賛勉強中です。