CDKTFでCloudflareリソースのIaCを実現する
こんにちは。最近 Cloudflare にお熱なエンジニアです。元々は AWS メインで CDK をバリバリに使っていました。
Cloudflare で IaC をやるときに Terraform で HCL[1] かー。という気分になっていたところに CDKTF に出会い半年程使って体験が良かったので紹介します。
CDKTF 概要
CDKTF(Cloud Development Kit for Terraform)は CDK の概念を Terraform に持ち込んだフレームワークで、TypeScript や Python などを使って様々なクラウドのリソースを管理できます。
AWS のリソースを管理するなら基本的に CDK で事足りますが、CDKTF は CDK のベストプラクティスを活かしつつ AWS 以外のクラウドリソース管理ができるので CDK ユーザーには凄くおすすめです。
(CDKTF の開発には CDK の開発者チームも関わっているので、CDK との親和性がかなり高い)
より詳しい情報は公式ドキュメントからどうぞ
今回は R2Bucket を作成するサンプルで CDKTF の使い方を紹介します。
想定読者
- CDK には触れたことがあるが、Terraform, CDKTF に触れたことがない人
- Terraform 使いたいが HCL が苦手な人
事前準備
CDKTF の Init は公式ドキュメントに書かれているのでそちらを参照。
CDKTF も CDK と同じく TypeScript がおすすめなので、本記事では TypeScript での実装とします。
Cloudflare Provider のインストール
CDKTF では各 Provider の定義がパッケージとして提供されているので @cdktf/provider-cloudflare
をインストール
$ npm install @cdktf/provider-cloudflare
R2 サンプル
試しに R2Bucket を作成するサンプルを用意します。
import { App } from "cdktf";
import { MyStack } from "./stack/my-stack";
const app = new App();
new MyStack(app, "MyStack");
app.synth();
import { provider, r2Bucket } from "@cdktf/provider-cloudflare";
import { TerraformStack } from "cdktf";
import type { Construct } from "constructs";
const CLOUDFLARE_ACCOUNT_ID = "xxxxxxxxxxxxx";
export class MyStack extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);
new provider.CloudflareProvider(this, "cloudflare");
new r2Bucket.R2Bucket(this, "hoge-bucket", {
accountId: CLOUDFLARE_ACCOUNT_ID,
name: "hoge-bucket",
});
}
}
CDK ユーザーにはおなじみの記法でスタックを定義できます 🚄
Env の設定
アプリケーションを作成する場合基本的に環境分けしたくなりますね 🐇
環境変数の管理は色々方法がありますが、通常の環境変数であれば TypeScript のコード内で管理してしまうのが楽でしょう。
私はこんな感じで管理してます。
function firstUpperCase(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1);
}
const app = new App();
const envMap = {
dev: {},
stg: {},
prd: {},
};
[("dev", "stg", "prd")].forEach((env) => {
const envValues = envMap[env];
const firstUpperEnv = firstUpperCase(env);
new MyStack(app, `MyStack-${firstUpperEnv}`, envValues);
});
app.synth();
export class MyStack extends TerraformStack {
constructor(scope: Construct, id: string, envValues: EnvValues) {
super(scope, id);
new provider.CloudflareProvider(this, "cloudflare");
new r2Bucket.R2Bucket(this, "hoge-bucket", {
accountId: CLOUDFLARE_ACCOUNT_ID,
name: "hoge-bucket",
});
}
}
環境分けや環境変数の値がわかりやすいのも CDK 由来の良さですね 🌈
認証情報の設定
Cloudflare の認証情報を環境変数で設定する必要があります。Cloudflare では現在 API トークンが推奨されているため、以下の手順で API トークンを作成し、環境変数に設定します。
CLI 上で export
すると CDKTF 上から参照されるようになります。
$ export CLOUDFLARE_API_TOKEN=xxxxxxxxxxxxx
Diff, Deploy, Destroy
Diff, Deploy, Destroy は CDK と同じですね。
$ npx cdktf diff HogeStack
$ npx cdktf deploy HogeStack
$ npx cdktf destroy HogeStack
tfstate の管理
CDKTF と CDK との大きな違いの一つに tfstate の存在があります。
tfstate は Terraform が管理するリソースの状態を保存するファイルで、どのスタックで何のリソースが作成されているかなどが記録されています。
ローカルで管理もできますが、誤って消したりするとリソースの状態がわからなくなるので S3 などのリモートで保存することが推奨されます。(当然チーム開発時にはリモートで管理することが必須)
リモートで管理する場合、例えば S3 に保存するには次のように記載します。
export class MyStack extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);
new provider.CloudflareProvider(this, "cloudflare");
// tfstate を S3 に保存
new S3Backend(this, {
bucket: "tfstate-bucket",
key: "terraform.tfstate",
region: "us-east-1",
});
new r2Bucket.R2Bucket(this, "hoge-bucket", {
accountId: CLOUDFLARE_ACCOUNT_ID,
name: "hoge-bucket",
});
}
}
↑ では S3Backend
コンストラクトを用いて S3 に保存していますが、私は Cloudflare R2 で管理するようにしました。
Diff, Deploy 時に AWS への認証が無駄に挟まってしまうので、Cloudflare で一元管理したかったからですね。
R2 で tfstate を管理する方法は別途記事にしたいと思います。
(R2Backend は公式サポートされてないなど、色々事情がここでは割愛)
まとめ
Terraform を TypeScript で書ける CDKTF はかなり便利で、CDK ユーザーにとっては特におすすめのツールです。
CDKTF はいいぞ!🚀
Discussion