CDK for TerraformがGoをサポートしたので試してみた
記事一覧はこちら
背景・モチベーション
CDKには元々興味がありました!CloudFormationでのyaml記述はやはり辛いものがあるし、プログラミング言語を利用することでコード補完によるサポートを得られたり、抽象化やvalidateを入れたり出来るのではと期待があります。
CDK for Terraformのv0.0.4がリリースされ、Goのサポート(experimental)を開始したことをこの記事から知り、良い機会なのかなと思い試してみました。
参考文献
CDK For Terraform とは
ここにあるとおり、プログラミング言語でインフラストラクチャを記述したいというDeveloperの要望に応える形で、AWS CDKのチームと協力し、AWS CDKの2つの主要なコンポーネントを活用してTerraform用のCDKをサポートし始めたとのことです。
AWS CDKに関しては、こちらが分かりやすいです。
AWS CDKはCloudFormationテンプレートをjson形式で吐くのに対し、Terraform用CDKはTerraformの構成ファイルをjson形式で吐きます。元々、HCLだけでなくjsonもサポートしているのはあんまり意識したことなかったなあと思ったり。
CDKは多言語をサポートしてます。そこにはjsiiというライブラリとかが絡んできていて、掘るのが面白そうなのですがそれはまた別の記事にしようと思います。
Getting Started
リポジトリはこちら
※ Terraformはインストール済の前提
実装の初期段階まで
$ brew install cdktf $ mkdir cdk-for-terraform-sample # 雛形の作成 $ cdktf init --template="go" --local # moduleの宣言が自動生成されているので修正する $ vi go.mod
プロジェクトルートにある cdktf.json
に利用するTerraform ProviderとModuleを宣言します
例えば、AWSのProviderとEKSのModuleを利用するのであれば下記のように宣言する
{ "language": "go", "app": "go run main.go", "codeMakerOutput": "generated", "terraformProviders": [ "hashicorp/aws@~> 3.40.0" ], "terraformModules": [ { "name": "aws-eks-module", "source": "terraform-aws-modules/eks/aws", "version": "~> 16.0" } ] }
下記のコマンドを実行すると、上で設定した内容を元にコードを自動生成します
codeMakerOutput
で設定したディレクトリに生成したコードが配置されます
$ cdktf get
⠇ downloading and generating modules and providers... # めっちゃ時間かかりますし、メモリを6-7GB食います
Goのコードで実装
main.go
に宣言された、NewMyStack
関数にてリソースの定義を行っていきます。
基本的な関数の構造としては下記のような形で、第一引数と第二引数は各関数で共通で、第三引数は各リソースごとのinputのパラメータを持った構造体を渡してあげるイメージ。殆どが参照渡しになるので、適宜jsii
パッケージのヘルパー関数を利用する。
vpc := aws.NewCamelCaseResourceName(stack cdktf.TerraformStack, id *string, config *aws.CamelCaseResourceNameConfig)
例えば、簡単なAWSのスタックを作成するとなると下記のようになると思います。
func NewMyStack(scope constructs.Construct, id string) cdktf.TerraformStack { stack := cdktf.NewTerraformStack(scope, &id) aws.NewAwsProvider(stack, jsii.String("aws"), &aws.AwsProviderConfig{ Region: jsii.String(region), }) vpc := aws.NewVpc(stack, jsii.String("isucon_vpc"), &aws.VpcConfig{ CidrBlock: jsii.String("172.16.0.0/16"), Tags: &map[string]*string{ "Name": jsii.String("isucon-training"), }, }) igw := aws.NewInternetGateway(stack, jsii.String("isucon_vpc_igw"), &aws.InternetGatewayConfig{ VpcId: vpc.Id(), }) subnet := aws.NewSubnet(stack, jsii.String("isucon9_subnet"), &aws.SubnetConfig{ VpcId: vpc.Id(), CidrBlock: jsii.String("172.16.10.0/24"), AvailabilityZone: jsii.String("ap-northeast-1d"), Tags: &map[string]*string{ "Name": jsii.String("isucon-training"), }, DependsOn: &[]cdktf.ITerraformDependable{ igw, }, }) // 他にもリソースを作成(詳しくはリポジトリをご覧ください) return stack }
デプロイしてみる
下記のコマンド実行時に cdktf synth
も実行されて、cdktf.out
ディレクトリにjson形式でterraformの構成ファイルが生成されます。
注意点としては、cdktfのv0.4.0
の時点では cdktf destroy
時にエラーが発生します(Issue)。
# terrafrom planとほぼ同じような出力が得られる $ cdktf diff Stack: cdk-for-terraform-sample Resources + AWS_DEFAULT_ROUTE_TA isucon9_subnet_rout aws_default_route_table.isucon9_subnet_ BLE e_table route_table + AWS_EC2_MANAGED_PREF default_prefix aws_ec2_managed_prefix_list.default_pre IX_LIST fix + AWS_EIP isucon9_qualify_ins aws_eip.isucon9_qualify_instance_eip tance_eip + AWS_INSTANCE isucon9_qualify_ins aws_instance.isucon9_qualify_instance tance + AWS_KEY_PAIR developer_keypair aws_key_pair.developer_keypair + AWS_ROUTE_TABLE_ASSO isucon9_subnet_rout aws_route_table_association.isucon9_sub CIATION e_table_d net_route_table_d ~ AWS_SECURITY_GROUP isucon9_qualify_ins aws_security_group.isucon9_qualify_inst tance_sg ance_sg + AWS_SUBNET isucon9_subnet aws_subnet.isucon9_subnet Diff: 7 to create, 1 to update, 0 to delete. # デプロイ $ cdktf deploy # お片付け(エラーが発生します) $ cdktf destroy ypeError: Cannot read property 'startsWith' of undefined
使ってみての所感
文字列をポインタで渡さなくてはいけない関係上、jsii.String
関数を毎回使うのでスッキリ見えないのがあまり良くないかもというのはあります。
また、この段階だからだと思うのですが、Terraformのresourceやmoduleを元にコード生成する過程でかなり時間とリソースを食います。これはネックになりそうです。
規模感が大きくならないと、中々恩恵は受けづらいのかなと思う面もありました。
(でもyaml書くより楽しい…)
以上!