どうも、型にハマらない人生を歩みたいけど、コードを書く時は型が欲しい id:nabeop です。
1年くらい前に cdktf に入門したあとは changelog は追っていたけど、使う機会がなかった。 ちょうど AWS アカウント単位で検証環境を複数作ることになったので、cdktf に再入門してみた。
最初に触ったときは v0.0.18 で現時点では v0.9.0 だった。
変わったところ
cdktf init 直後のディレクトリ構造がシンプルになった
cdktf init
によって作られる初期状態のディレクトリ構造にちょっとした変化があった。
以下は過去のエントリに書いていた v0.0.18 時点のディレクトリ構造
$ ls -a ./ .gitignore main.d.ts node_modules/ tsconfig.json ../ cdktf.json main.js package-lock.json .gen/ help main.ts package.json $
で、v0.9.0 では以下のようになっていた
$ ls -a ./ .npmrc help node_modules/ setup.js ../ __tests__/ jest.config.js package-lock.json tsconfig.json .gitignore cdktf.json main.ts package.json $
パッと目につくところだと、以下の3つくらい。
- テスト用グッズが揃っている
.gen
ディレクトリがなくなったmain.ts
向けの.d.ts
と.js
がなくなった
テストの用のグッズが揃っているのはありがたい。__tests__/main.test.ts
にテストの雛形があるので、ひとまずはこのファイルのコメントを読みながら必要なテストを書いて npm run test
をすればテストが実行できるようになっている。v0.0.18 時点でもテストのための機能はあったけど、テスト環境は自前で構築する必要があった。
.gen
ディレクトリがなくなったのは後述の provider のネイティブ化によって cdktf.json
の terraformProviders
から AWS 向けのプロバイダーの指定がなくなっているからぽい。ただし、以前のエントリではあった main.ts
をコンパイルした結果の main.d.ts
と main.js
がなくなっている関係から、cdktf init
したときに cdktf compile
相当の処理がなくなったからかもしれない。ただし、terraformProviders
や `terraformModules
が空の状態で cdktf get
しても .gen
ディレクトリは生成されないので初期状態で不要なディレクトリが生成されないのは嬉しい。
シェルでの補完関数が提供されるようになった
cdktf completion
によってシェルの補完関数が使えるようになっていた。ただし、zsh でつかってみたら cdktf completion
に含まれる cdktf
へのパスが絶対パスになっていて、nodenv と相性が悪かったので以下のように変更している。
_cdktf_yargs_completions() { local reply local si=$IFS IFS=$' ' reply=($(COMP_CWORD="$((CURRENT-1))" COMP_LINE="$BUFFER" COMP_POINT="$CURSOR" "$( nodenv root )/shims/cdktf" --get-yargs-completions "${words[@]}")) IFS=$si _describe 'values' reply } compdef _cdktf_yargs_completions cdktf
必要なコマンドは npm script に登録しているのであんまり使う機会はないけど、補完されるのは嬉しい。
よく使う (と思われる) provider がネイティブ化していた
v0.7.0 からの機能で AWS 向けの provider がネイティブ化されていた。どうやら cdktf 的には Namespaced AWS Provider というらしい。
以前は cdktf.json
の terraformProviders
に使用するプロバイダーの設定を書いて、.gen
ディレクトリ以下に typescript 向けのファイルを生成してから import { Ec2 } from "./.gen/provider/aws"
と呼び出していた。
ネイティブ化されたので provider は npm install @cdktf/provider
としてインストールして、import { Ec2 } from "@cdktf/provider-aws"
と呼び出すことができる。
このおかげで pakcage.json
や package-lock.json
に使用するプロバイダーが記述されるので、renovate などによる更新の対象になってくれた。
v0.9.0 の時点では AWS 向けプロバイダーの他に以下のプロバイダーも同様にネイティブ化されているらしい。
- Google Cloud 向けプロバイダー (
@cdktf/provider-google
) - Azure 向けプロバイダー (
@cdktf/provider-azurerm
) - Docker 向けプロバイダー (
@cdktf/provider-docker
) - Github 向けプロバイダー (
@cdktf/provider-github
) - Null プロバイダー (
@cdktf/provider-null
)
今回の再入門で確認したこと
state ファイルの単位としての Stack
前回は cdktf における Stack について余り深く考えないで使っていたけど、冷静に考えたら Stack は aws-cdk というか CloudFormation 由来の用語なので、Terraform (cdktf) における Stack とは??となったので、ちゃんとみてみた。
結論をから書くと cdktf における Stack とは state の単位になる。つまり、Stack ごとに state は生成されるし、cdktf によって生成される Terraform 向けのファイル群は cdktf.out/stacks/<stack name>
というディレクトリ以下に生成される。v0.0.18 時点では cdktf.out
直下に Terraform の作業ディレクトリが生成されていたので、このあたりも扱いが変わったのかもしれない。
v0.9.0 時点での sample Stack の Terraform の作業ディレクトリは以下のようになっていた。
$ ls -a cdktf.out/stacks/sample ./ .terraform/ cdk.tf.json ../ .terraform.lock.hcl plan $
つまり、cdktf-cli 経由ではなく、terraform コマンドを直接実行したいときは -chdir
オプションを使えば良い。例えば、 terraform state list
など cdktf-cli では実装されていない操作をしたい時は以下のようにする
terraform -chdir=cdktf.out/stacks/sample state list
cdktf における Stack については Stacks | Terraform by HashiCorp としてドキュメントがまとまっている。
Stack の持つ意味がわかったら cdktf における cross stack 参照が remote state の参照に相当するということもすんなり腹落ちする。また、v0.9.0 によって cross stack 参照がより aws-cdk ぽい感じになっていて記述しやすくなっていた。
Typescript で記述したリソースと state ファイル内のリソースの対応関係
Terraform を運用していると terraform state mv
や terraform import
などで直接 state ファイルを書き換える必要がでくる。直接 state ファイルを書き換える場合はコード側も追従させる必要がある。当然 cdktf でも同様の作業は発生するはずなので、Typescript のコードで state 内のリソースがどのように表現されるかは知っておく必要がある。AWS-CDK だと AWS-CDK 側が CloudFormation における論理 ID や物理 ID をよしなに管理されてしまうが、同じことが cdktf でもおきると困るなーという感じです。
たとえば、EIP を確保するために以下のようなコードを書いたとする。
import { Eip } from '@cdktf/provider-aws/lib/ec2'; ... new Eip(this, 'ReservedEip', { vpc: true, });
同様の定義を hcl で書くと以下のようになる。
resource "aws_eip" "ReservedEip" { vpc = true }
つまり、id
の部分がそのまま id として使われる。したがって、state ファイルでは以下のようになっているはず。
{ "mode": "managed", "type": "aws_eip", "name": "ReservedEip", "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]", "instances": [ { "schema_version": 0, "attributes": { ... }, "sensitive_attributes": [], "private": "********************" } ] }
前述の通り、state ファイルを直接変更することは稀によくあることなので、このことを頭の片隅に入れてコードを書いておいた方が良い。
Terraform Cloud が使えるようになった
これは v0.0.18 の時点から使えていたぽいけど、このときは Terraform Cloud を使っていなかったのでスルーしていた。今回は Terraform Cloud が使える環境にあったので、試してみた。
v0.1.0 の時点で Terraform Cloud Support by skorfmann · Pull Request #449 · hashicorp/terraform-cdk としてリモート実行モードでも使えるようになっていたけど、この変更を見逃していてローカル実行モードで試していた。
実際に cdktf init --template=typescript
としてプロジェクトを作ると、バックエンドは remote
(Terraform Cloud) を使うように生成されるが、Terraform Cloud 側はリモート実行モードとして構築されていた。
何も設定していない限り cdktf synth
によって生成される terraform 向けのファイルは cdktf.out/stacks/<stack name>/
以下に生成されるので、Terraform Cloud をリモート実行モードとするときはこのディレクトリ以下が転送されて、Terraform Cloud 側で terraform plan
や terraform apply
が実行されるんじゃないかと予想している。
hcl で書かれている terrafrom module を cdktf (typescript) で使うときに注意すること
v0.7.0 で Technical preview として導入された AWS Adapter を使えば、間接的に aws-cdk を呼び出せるので hcl で書かれている terraform module をわざわざ cdktf で使う必要はないかもしれない。
ただ、今回は AWS Adapter のことを忘れていて、hcl 向けの terraform module を使っていた。aws-cdk が提供している L2 Construct や L3 Construct による開発体験を知ってしまっているので機会があれば使ってみたい。あと、cdktf 自体が beta であると宣言しているうえで AWS Adapter だけ Technical preview として名言されているので、何かしらの罠があるんだろうと思っている。(楽しみ)
hcl 向けに提供されているモジュールを使うので .gen
以下に cdktf で使えるように変換する必要がある。hcl 製の terraform module を使う時は cdktf.json
の terraformModules
に使用する terraform moudle を追記して、cdktf get
を実行する必要がある。例えば、terraform-aws-modules/terraform-aws-vpc: Terraform module which creates VPC resources on AWS を使いたい場合は、cdktf.json
の terraformModules
で以下のように書く。
"terraformModules": [{ "name": "vpc", "source": "terraform-aws-modules/vpc/aws", "version": "=3.11.5" }],
ts 側では import { Vpc } from "./.gen/moduels/vpc"
として呼び出す。
使っている時に気づいた注意点としては本来は terraform module ごとに provider を指定できるはずだけど、cdktf gen
によって生成された TerraformModule は provider を渡せない実装になっていた。
先ほどの terraform-aws-modules/vpc/aws
によって生成された Vpc
クラスを例にとると、Vpc
は provider を受け付けることができる TerraformModule
クラスを extends
しているが、Vpc
クラスの引数である VpcOptions
は TerraformModule
クラスの引数である TerraformModuleOptions
を extends
していないので provider を渡すことができないように見えた。.gen/moduels/vpc.ts
を書き換えれば動きそうな気がしたけど、そこまではやっていない。
つまり、同一 Stack の中で provider を切り替えて使用することができないので、Stack 設計時に気をつける必要がある。
また、元になった terraform module で必須となっている input が cdktf gen
によって生成されたクラスでは strings | undefined
となっていたり、配列で生成される output が壊れているなど気になるところが多かった。
hcl で書かれた既存の Terraform を cdktf のプロジェクトとしてインポートできるようになった
cdktf init
のオプションとして --from-terraform-project
が生えていた。これは cdktf-cli の v0.5.0 で導入された機能だった。
ドキュメントを読む限り、hcl で書かれた既存の Terraform を cdktf のプロジェクトとしてインポートできるらしい。
試しに試しに手近な Terraform プロジェクトをインポートしてみたところ、いくつかのプロジェクトでは解析に失敗したが、シンプルな構造のプロジェクトでインポートに成功した。
インポート前の Terraform プロジェクトのディレクトリ構成:
$ ls -a ./ .gitignore .terraform.lock.hcl main.tf terraform.tf ../ .terraform/ README.md modules/ $
インポート後の cdktf プロジェクトのディレクトリ構成:
./ .npmrc jest.config.js package-lock.json ../ __tests__/ main.ts package.json .gen/ cdktf.json modules/ setup.js .gitignore help node_modules/ tsconfig.json
インポート後の構成をみてみると、オリジナルでは複数の tf ファイルで分割していた内容が main.ts
に統合されている。また、main.ts
を見る限り、インポート前後で state におけるアドレスは維持されているように見えたので、そのまま使えそうな気配を感じました。
触ってみた感想
以前のエントリでは以下のように感想をまとめていた。
未整備なところもあるけど、Terraform のお作法や aws-cdk での知見を活かすための機能追加はあった。ただし、今の状態で cdktf を本番環境に導入するか?と聞かれたら、まだまだ厳しいと答えると思う。理由は
- hcl のテンプレートエンジンとして cdktf を使うには
cdktf gen
によって生成されるクラスに難がある - aws-cdk の資産が使えそうな AWS Adapter は Technical preview になっていて本番環境で使うには不安がある
というところが大きい。
ただし、大きなプロジェクトや重要なプロジェクトではなく、小規模であったり重要度の低いプロジェクトであればパイロットプロジェクト的な立ち位置で導入してみることはできるかもしれない。例えば cdktf から Terraform への変更は state ファイルの構造を維持するように変更することは可能だろうし、いざとなたったら IaC による管理をやめてしまうということも可能だろうという見込みもある。
前回の感想でかいていた cdktf diff
の出力では変更されるリソースの詳細がわからないから厳しい、というのはそのうち改善されそうだし、cdktf.out
以下に生成される terraform 向けの作業ディレクトリで terraform plan
をすれば良いかという気分になっている。
いずれにせよ、これからも cdktf は追いかけていきたい。
ところで、cdktf の正式な名称はなんだろう? 開発元の HashiCorp のサイトでも「CDK for Terraform」とか「CDKTF」とか書かれていて、よくわからない。Google で "CDK for Terraform" というキーワードで検索した時と "cdktf" で検索した時を比較すると、Google 的には「CDK for Terraform」が正式名称と認識していそうな雰囲気を感じた。このあたりも正式リリースまでにはっきりしてほしいところではある。