CDK for Terraform で GCP スタックを作る

thumbnail

CDK for Terraform (Cloud Development Kit for Terraform (CDKTF)) とは TS や Python や Go などのプログラミング言語で IaC が行えるツールです。

FYI: https://github.com/hashicorp/terraform-cdk

つまり HCL を書かずに Terraform を使えるものです。 HCL を書いているときは「型が欲しい」「補完されたい」と思ったりもするものですが、TS で書くことでそれが解消できるという素晴らしいツールです。

さて、この CDKTF にはドキュメントがありますが、実は TS + GCP を実現する方法が書かれていません。

https://www.terraform.io/cdktf

そこでこの記事では TS + CDKTF で GCP に Hello World するところまでを書きます。

init

まず cdktf を入れます。

npm install --global cdktf-cli@latest

そして cdk プロジェクトを作ります。

cdktf init

質問に答えるとプロジェクトができます。

GCP にデプロイする

さて GCP にデプロイしましょう。

ここで作られたファイルを見ると

import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name);
  }
}

const app = new App();
new MyStack(app, "cdktf");
app.synth();

とあり、GCP へのリソース指定はどこにもありません。

もしかして精製コマンドに何か必要だったのでしょうか。 オプションを見てみます。

cdktf init -h
cdktf init [OPTIONS]

Create a new cdktf project from a template.

Options:
      --version                   Show version number                  [boolean]
      --disable-logging           Dont write log files. Supported using the env
                                  CDKTF_DISABLE_LOGGING.
                                                       [boolean] [default: true]
      --disable-plugin-cache-env  Dont set TF_PLUGIN_CACHE_DIR automatically.
                                  This is useful when the plugin cache is
                                  configured differently. Supported using the
                                  env CDKTF_DISABLE_PLUGIN_CACHE_ENV.
                                                      [boolean] [default: false]
      --log-level                 Which log level should be written. Only
                                  supported via setting the env CDKTF_LOG_LEVEL
                                                                        [string]
      --template                  The template to be used to create a new
                                  project. Either URL to zip file or one of the
                                  built-in templates: ["csharp", "go", "java",
                                  "python", "python-pip", "typescript"] [string]
      --project-name              The name of the project.              [string]
      --project-description       The description of the project.       [string]
      --dist                      Install dependencies from a "dist" directory
                                  (for development)                     [string]
      --local                     Use local state storage for generated
                                  Terraform.          [boolean] [default: false]
      --cdktf-version             The cdktf version to use while creating a new
                                  project.           [string] [default: "0.9.0"]
      --from-terraform-project    Use a terraform project as the basis, CDK
                                  constructs will be generated based on the .tf
                                  files in the path                     [string]
  -h, --help                      Show help                            [boolean]

特に見つかるわけでもありません。

次にドキュメントを見てみましょう。

ドキュメント

AWS についてしかありません。

AWS のドキュメントを見てみると

import { Construct } from "constructs";
import { App, TerraformStack, TerraformOutput } from "cdktf";
import { AwsProvider, ec2 } from "@cdktf/provider-aws";

class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    new AwsProvider(this, "aws", {
      region: "us-west-1",
    });

    const instance = new ec2.Instance(this, "compute", {
      ami: "ami-01456a894f71116f2",
      instanceType: "t2.micro",
    });

    new TerraformOutput(this, "public_ip", {
      value: instance.publicIp,
    });
  }
}

const app = new App();
new MyStack(app, "typescript-aws");
app.synth();

とあります。

https://learn.hashicorp.com/tutorials/terraform/cdktf-build?in=terraform/cdktf

つまり import { AwsProvider, ec2 } from "@cdktf/provider-aws"; の GCP 版があればいけそうです。

そして cdktf 配下の npm レジストリを探すと、 @cdktf/provider-google が見つかります。

なので、npm i @cdktf/provider-google とすれば GCP リソースを持ってこれるようになります。

import { App, TerraformStack } from "cdktf";
import { Construct } from "constructs";
import * as fs from "fs";
import * as path from "path";

import {
  AppEngineApplication,
  CloudbuildTrigger,
  CloudRunService,
  CloudRunServiceIamPolicy,
  ContainerRegistry,
  DataGoogleIamPolicy,
  GoogleProvider,
} from "@cdktf/provider-google";

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name);
    const credentialsPath = path.join(process.cwd(), "google.json");
    const credentials = fs.existsSync(credentialsPath)
      ? fs.readFileSync(credentialsPath).toString()
      : "{}";
    const projectId = "xxx";

    new GoogleProvider(this, "Google", {
      region: "asia-northeast1",
      zone: "asia-northeast1-a",
      project: projectId,
      credentials,
    });

    new AppEngineApplication(this, "gae", {
      project: projectId,
      locationId: "asia-northeast1",
      databaseType: "CLOUD_FIRESTORE",
    });
  }
}

const app = new App();
new MyStack(app, "iac");
app.synth();

あとは apply すればリソースができます。

./.gen/providers/google というやり方がある

一方で、公式の example レポジトリを調べていると ./.gen/providers/google というパスからリソースを持ってくる例を見つかります。

しかし生成したファイルには ./.gen/providers/google なんてものはありません。どこから作るのでしょうか。

実はこれは npm run get つまり cdktf get する必要があります。 というわけで実行すると

> cdktf@1.0.0 get
> cdktf get

ERROR: Please specify providers or modules in "cdktf.json" config file

と言われます。

どうやら cdktf.json に設定がいるようです。

その設定とはこうです。

{
  ...
  "terraformProviders": ["google@~> 4.7.0"],
  ...
}

この状態でコマンドを叩くと

npm run get

> cdktf@1.0.0 get
> cdktf get

Generated typescript constructs in the output directory: .gen

と成功し、.gen が生成されます。

あとはここから Terraform CDK のクラスを import できます。

インフラの構成が終わったら cdktf deploy でデプロイできます。 このとき cdk.json の設定によって自動でコンパイルされるのですが、コンパイル済みは ckd.out に出力されておりそのコードをみると Terraform のコードが出力されているので、それが apply されて構成が完了します。