Terraform CDK + Java + コミュニティプロバイダーを使ってみる

Terraform CDK + Java + コミュニティプロバイダー(さくらのクラウド)という組み合わせを試してみます。

概要

Terraform CDKとは

Terraform CDKとは汎用のプログラミング言語を用い、Terraform(+エコシステム)を通じてクラウドなどの各種プラットフォームを操作するためのツールです。

www.hashicorp.com

サポートされているプログラミング言語としては以下のようなものがあります。

C#Javaについてはv0.1からサポートされました。

www.hashicorp.com

Terraform CDKの位置付け/立ち位置

AWS CDKが汎用のプログラミング言語での定義からCloudFormationのコンフィグレーションを生成するのと同じく、 Terraform CDKは汎用のプログラミング言語での定義からTerraformのコンフィグレーションを生成します。

こちらの図にある通り、Terraformへの入力の一つという位置付けです。

f:id:febc_yamamoto:20210416141109p:plain (出典: https://www.hashicorp.com/blog/cdk-for-terraform-enabling-python-and-typescript-support)

  • Terraform CLIを通じてJSON/HCLを入力とする
  • Terraform Operatorを通じてCRDsを入力する
  • CDKを通じて汎用プログラミング言語での定義を入力とする

Terraformへの入力という位置付けなため、定義(dev)->プラン(plan)->適用(apply)というTerraformを用いたワークフローはそのまま利用する形です。
先ほどの記事にもThe goal of Terraform is to provide a consistent workflow for provisioning infrastructure(著者訳: Terraformのゴールはインフラのプロビジョニングに対し一貫したワークフローを提供することです)と章のはじめに書かれており、The Tao of HashiCorpにも書かれているワークフローへのこだわり(テクノロジーじゃない、ワークフローだ)を感じさせられます。

利用者は次に紹介するCLI cdktf を介してTerraformの各種コマンドを利用したり、Terraform向けのコードを(cdktf CLIで)生成して手動でTerraform CLIを実行するということも可能です。

Terraform CDKのツール

主に2つのパッケージが提供されています。

  • cdktf-cli: Terraform CDKを利用するためのCLI
  • cdktf: Terraformリソースを汎用プログラミング言語で定義するためのライブラリ

いずれもTypeScriptで実装されています。

CLIのヘルプはこんな感じです。

$ cdktf --help
cdktf [command]

Commands:
  cdktf deploy [OPTIONS]   Deploy the given stack
  cdktf destroy [OPTIONS]  Destroy the given stack
  cdktf diff [OPTIONS]     Perform a diff (terraform plan) for the given stack
  cdktf get [OPTIONS]      Generate CDK Constructs for Terraform providers and modules.
  cdktf init [OPTIONS]     Create a new cdktf project from a template.
  cdktf login              Retrieves an API token to connect to Terraform Cloud.
  cdktf synth [OPTIONS]    Synthesizes Terraform code for the given app in a directory.                                                                                                                                [aliases: synthesize]

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]
  -h, --help                  Show help                                                                                                                                                                                            [boolean]

Options can be specified via environment variables with the "CDKTF_" prefix (e.g. "CDKTF_OUTPUT")

CLIを用いた開発〜デプロイ(リソース作成)の基本的な流れは以下のようになります。

  • cdktf initで言語別のテンプレートからTerraform CDKプロジェクトを生成
  • 構成ファイルcdktf.jsonに必要なTerraformプロバイダー/モジュールなどを定義
  • cdktf getでプロバイダー/モジュールの取得〜TypeScriptのコード生成(jsii向け)
    (Note: Terraform CDKは多言語サポートのためにjsiiを利用しているため、一旦TypeScriptのコードが生成される)
  • 生成されたコードとライブラリの方のcdktfを用いてインフラのリソース定義
  • cdktf deployして適用(またはcdktf synthJSONを出力してterraform apply)

主要プロバイダー向けにはあらかじめコード生成済みのPrebuilt Providersも提供されています。
terraform-cdk/using-providers-and-modules.md at main · hashicorp/terraform-cdk · GitHub

例えばTypeScriptでリソース定義を行う場合以下のようなコードになります。

import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";
import { AwsProvider, Instance } from "./.gen/providers/aws";

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

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

    new Instance(this, "Hello", {
      ami: "ami-2757f631",
      instanceType: "t2.micro",
    });
  }
}

const app = new App();
new MyStack(app, "hello-terraform");
app.synth();

(出典: terraform-cdk/typescript.md at 342b4cc71568cc73be93954901265f753e8709b4 · hashicorp/terraform-cdk · GitHub)

細かな解説は省きますが、ポイントは以下のとおりです。

  • aws/constructsを利用している
  • ライブラリの方のcdktfを利用している
  • 生成されたコードは./.gen配下に格納される

利用するプロバイダーの定義とリソースの定義だけのシンプルな例となっています。
コードを追加すればステートの保存先としてリモート(Terraform CloudやS3など)を利用することも可能です。

Terraform Cloud利用時の注意点

現時点ではTerraform Cloud上でterraformコマンドを実行するExecution Mode=Remoteは利用できず、ステートだけをTerraform Cloudに保存するExecution Mode=Localのみ利 用可能です。 参考: terraform-cdk/remote-backend.md at 342b4cc71568cc73be93954901265f753e8709b4 · hashicorp/terraform-cdk · GitHub

利用例: Terraform CDK + さくらのクラウド + Java

ここでは例としてJavaを用いてさくらのクラウド上にリソースを作成してみます。

余談ですが類似プロダクトのPulumiではJavaまだサポートされていません

事前準備(Javaのみ、今だけの暫定処置)

現時点ではTerraform CDK周りのライブラリがMavenセントラルに登録されておらずGitHub Packages(https://maven.pkg.github.com/hashicorp/terraform-cdk)での提供となります。
このため、事前にMavenからGitHub Packagesを利用できるようにするための設定が必要になります。
(この問題は将来的に解消予定とのこと)

事前にGitHubのPersonal Access Tokenを取得した上で~/.m2/settings.xmlファイルを以下のようにしておいてください。(ファイルがなければ作成する)

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">

    <activeProfiles>
        <activeProfile>github</activeProfile>
    </activeProfiles>

    <profiles>
        <profile>
            <id>github</id>
            <repositories>
                <repository>
                    <id>central</id>
                    <url>https://repo1.maven.org/maven2</url>
                </repository>
                <repository>
                    <id>cdktf</id>
                    <url>https://<GitHubユーザー名>:<アクセストークン>@maven.pkg.github.com/hashicorp/terraform-cdk</url>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </repository>
            </repositories>
        </profile>
    </profiles>

</settings>

参考: Configuring Apache Maven for use with GitHub Packages - GitHub Docs

CLIのインストール

まずTerraform CDKのCLIをインストールしておきます。
通常はstable版をインストールするのですが、今回はこの記事執筆段階でTerraform CDK側の問題を見つけたため、その問題が解消されたv0.2.3以降が必要です。
本日(2021/4/16)時点ではv0.2.3は未リリースですのでstable版ではなく開発版をインストールします。

$ npm install --global cdktf-cli@next

開発用のディレクトリの作成 & 初期コードの生成

次にcdktfでプロジェクトの雛形を生成します。
適当なディレクトリを作成しcdktf initを実行してください。

# ステートの管理はローカル + 開発言語にはJavaを利用する
$ cdktf init --local --template java

うまくいくと以下のようなファイル構成になっているはずです。

$ tree .
.
├── cdktf.json
├── help
├── pom.xml
├── src
│   └── main
│       └── java
│           └── com
│               └── mycompany
│                   └── app
│                       └── Main.java
└── target
    ├── cdk-work-java2-0.1.jar
    ├── classes
    │   └── com
    │       └── mycompany
    │           └── app
    │               └── Main.class
    ├── generated-sources
    │   └── annotations
    ├── maven-archiver
    │   └── pom.properties
    └── maven-status
        └── maven-compiler-plugin
            └── compile
                └── default-compile
                    ├── createdFiles.lst
                    └── inputFiles.lst

cdktf.jsonに依存プロバイダーを記載

次にカレントディレクトリに生成されているcdktf.jsonを編集します。
今回はさくらのクラウド向けプロバイダーを利用するのでterraformProviderssacloud/sakuracloud@~> 2.8と追記してください。

{
  "language": "java",
  "app": "mvn -e -q compile exec:java",
  "terraformProviders": [
    "sacloud/sakuracloud@~> 2.8"
  ],
  "terraformModules": [],
  "context": {
    "excludeStackIdFromLogicalIds": "true",
    "allowSepCharsInLogicalIds": "true"
  }
}

cdktf getの実行

次にプロバイダーの取得~コード生成を行うためにcdktf getを実行します。

$ cdktf get

Generated java constructs in the output directory: .gen

通常は生成されたコードが.genディレクトリ配下にあるのですが、Javaの場合はsrc/main/java/imports.sakuracloud配下などに配置されています。

Javaでリソース定義

次にsrc/main/java/com/mycompany/app/Main.javaにコードを書いていきます。
今回はシンプルにスイッチを作るだけの例です。

package com.mycompany.app;

import imports.sakuracloud.SakuracloudProvider;
import imports.sakuracloud.Switch;
import software.constructs.Construct;

import com.hashicorp.cdktf.App;
import com.hashicorp.cdktf.TerraformStack;

public class Main extends TerraformStack
{
    public Main(final Construct scope, final String id) {
        super(scope, id);

        // プロバイダーの定義
        SakuracloudProvider.Builder.create(this, "default").build();

        // リソースの定義
        Switch.Builder.create(this, "testSwitch")
                .name("from-terraform-cdk-with-java")
                .description("this resource is created by terraform-cdk with java")
                .build();
    }

    public static void main(String[] args) {
        final App app = new App();
        new Main(app, "cdk-work-java");
        app.synth();
    }
}

Terraformのコンフィギュレーションの生成(省略可能)

次に先ほど書いたコードからTerraformのコンフィギュレーションを生成してみます。

$ cdktf synth
Generated Terraform code in the output directory: cdktf.out

うまくいくとcdktf.outというディレクトリにcdk.tf.jsonというJSONファイルが生成されます。

$ tree cdktf.out/ 
cdktf.out/
└── cdk.tf.json

0 directories, 1 file

このファイルはTerraform CLI直接渡せるものとなっています。

なお次のcdktf deployを実行するとsynthも実行されますのでこの工程は省略可能です。

cdktf deployでデプロイ

いよいよデプロイです。cdktf deployを実行します。
途中で実行して良いか聞かれますのでyesを入力します。

$ cdktf deploy
Stack: cdk-work-java
Resources
 + SAKURACLOUD_SWITCH   testSwitch          sakuracloud_switch.testSwitch

Diff: 1 to create, 0 to update, 0 to delete.

Do you want to perform these actions?
  CDK for Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value:   # yesと入力する

# しばらく待つ

Resources
 ✔ SAKURACLOUD_SWITCH   testSwitch          sakuracloud_switch.testSwitch

Summary: 1 created, 0 updated, 0 destroyed.

デプロイできましたね!
今回はステートをローカルに保存するようにしましたので、カレントディレクトリにterraform.tfstateファイルが作成されているはずです。

後片付け: cdktf destroy

最後に忘れずに作成したリソースの削除を行っておきます。
こちらも途中で実行して良いか聞かれますのでyesと入力します。

$ cdktf destroy

これで削除されるはずです。

終わりに

今回はTerraform CDKの紹介とTerraform CDK+さくらのクラウドプロバイダーを利用する方法について紹介しました。
またまだ開発段階ということでちらほら気になる挙動があったりしますが、Pulumiと比べると既存のTerraformプロバイダーが利用しやすいと感じました。
(Pulumiの場合はブリッジを書く必要がある)

Terraformへの入力が変わるだけで既存のワークフロー(CI/CDなど)はそのまま利用できるのも嬉しいですね。

今後もう少し使い込んでみようかと思います。以上です。