cdktfでmovedブロックをやりたい
結論:
-
cdktf.out/stacks/xxx/cdk.tf.json
の横にmovedブロックだけを書いたmoves.tf
みたいなのを置くことで簡易に実現できる- git管理上は、moves.tfだけ
git add -f
で無理やり突っ込むのが楽
- git管理上は、moves.tfだけ
- (cdktf関係ないけど)tfautomvが便利そう
cdktfで、terraform (HCL) のmovedブロック相当をやりたい。
さしあたっての想定ユースケースは、HCLベースのプロジェクトを徐々に(notビックバンで)cdktfに移行するときの補助。
本家の関連issueはこのあたり。
最初の提起が2021/9/30で、2023/4/16現在で計画にもなっていないあたりから、近いうちでの公式実装はかなり望みが薄い。
(tf.jsonへのトランスパイラでしかないcdktfにて、ただのブロックの実現にこれだけ時間がかかるあたり、cdktfの性質による根本的な厄介事やエッジケースが存在するのだと思われる)
movedだけHCLで書いてmoduleとして読み込むという手が考えられる。
しかしmoduleで読み込んだ場合、そのmovedブロックはそのmodule以下にしか影響を与えることができない。想定ユースケースでは親cdktfのスタックにある子module(オリジナルのHCLコードベース)から親スタックにmovedする必要があるので、この手は採れない
残る手は
- movedブロックを生成するcdktfのリソースクラス(なりなんなり、cdktfのコードを書いてmovedを吐くやつ)を作る
- cdktfのsynthで生成されるtf.jsonの横に、movedを書いたHCL or tf.jsonを置く
あたりか?
それはそれとして、issueに貼ってあった下記ツールがなんか便利そう。
ひとまずお砂場を作った
コードはこれ。
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";
import { File } from "@cdktf/provider-local/lib/file";
import { LocalProvider } from "@cdktf/provider-local/lib/provider";
class MyStack extends TerraformStack {
constructor(scope: Construct, id: string) {
super(scope, id);
new LocalProvider(this, "local", {})
new File(this, "file1", {
filename: "file1.txt",
content: "hello world",
})
new File(this, "file2", {
filename: "file2.txt",
content: "hello world!!",
})
}
}
const app = new App();
new MyStack(app, "moved");
app.synth();
local providerでファイルを作るだけ。
適当にinitして適当に掃除しただけ。
なお、適当にinitするとTypeScript 5系が入ったのだが、5系ではsynthが通らなかったので4系に下げている。細かい事情は追ってない。
一応動作を見ておく。
この内容でapplyすると、cdktf.out/stacks/moved/
以下(要するにterraformコマンドが実行されるディレクトリ)にfile1.txt
とfile2.txt
が生成される。
更に下記変更を入れてplanすると
@@ -9,7 +9,7 @@ class MyStack extends TerraformStack {
new LocalProvider(this, "local", {})
- new File(this, "file1", {
+ new File(this, "file11", {
filename: "file1.txt",
content: "hello world",
})
こうなる。
moved Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
+ create
- destroy
Terraform will perform the following actions:
moved # local_file.file1 will be destroyed
# (because local_file.file1 is not in configuration)
- resource "local_file" "file1" {
- content = "hello world" -> null
- content_base64sha256 = "uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=" -> null
- content_base64sha512 = "MJ7MSJwS1utMxA9QyQLytNDtd+5RGnx6m808qG1M2G+YndNbxf9JlnDaNCVbRbDP2DDoH2Bdz33FVC6TrpzXbw==" -> null
- content_md5 = "5eb63bbbe01eeed093cb22bb8f5acdc3" -> null
- content_sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed" -> null
- content_sha256 = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" -> null
- content_sha512 = "309ecc489c12d6eb4cc40f50c902f2b4d0ed77ee511a7c7a9bcd3ca86d4cd86f989dd35bc5ff499670da34255b45b0cfd830e81f605dcf7dc5542e93ae9cd76f" -> null
- directory_permission = "0777" -> null
- file_permission = "0777" -> null
- filename = "file1.txt" -> null
- id = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed" -> null
}
# local_file.file11 (file11) will be created
+ resource "local_file" "file11" {
+ content = "hello world"
+ content_base64sha256 = (known after apply)
+ content_base64sha512 = (known after apply)
+ content_md5 = (known after apply)
+ content_sha1 = (known after apply)
+ content_sha256 = (known after apply)
+ content_sha512 = (known after apply)
+ directory_permission = "0777"
+ file_permission = "0777"
+ filename = "file1.txt"
+ id = (known after apply)
}
Plan: 1 to add, 0 to change, 1 to destroy.
当然だが、cdktf.out/stacks/moved
にcdしてからterraform plan
を直接実行しても同じ。
まずは簡単そうな
cdktfのsynthで生成されるtf.jsonの横に、movedを書いたHCL or tf.jsonを置く
を試してみる。
cdktfとの兼ね合いをゴニョゴニョするのは一旦後回しにして、cdktf.out/stacks/moved
ディレクトリ配下に直接下記ファイルを置く。
moved {
from = local_file.file1
to = local_file.file11
}
で、そのままterraform plan
local_file.file11: Refreshing state... [id=2aae6c35c94fcfb415dbe95f408b9ce91ee846ed]
local_file.file2: Refreshing state... [id=13cccf0a41de644625faad47eb59d388bc50e6c0]
Terraform will perform the following actions:
# local_file.file1 has moved to local_file.file11
resource "local_file" "file11" {
id = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"
# (10 unchanged attributes hidden)
}
Plan: 0 to add, 0 to change, 0 to destroy.
...いけとるがな。
オレオレcdktfリソース作らないといけないかと思って気合を入れてスクラップを作ったのに、書いてる途中で思いついた別方法で一瞬で解決してしまった。
これで終わってはどうしようもないので、もうちょっと実用を想定して下記を試してみたい。
-
cdktf.out
配下ではないところにmoved.tfを置き、cdktfコマンドを実行するときにいい感じに読ませるようにする-
cdktf.out
は生成ファイルなのでgitに乗らないし、なんなら時々削除するし。そこらへんをいい感じにする
-
- tfautomvも試してみる
- HCLのプロジェクトをcdktfにmoduleとして取り込み、それをcdktfに移していくシナリオを試してみる
なお、この方法では(cdktfリソースを作る方法と比べると)cdktfのコードからリソースIDを参照することができないというデメリットがある。
そのため、cdktfのプロジェクト/アプリケーションで一時的なmigrationファイルとして使うならともかく、ライブラリを提供するようなユースケースでは使えないかもしれない。
を試す。
今後真面目にモリモリ使いたいというステータスではないので、インストールは一時的な感じにする。
具体的にはリポジトリのreleasesページからバイナリを取ってきてそのまま置く。いやーいつものことながらシングルバイナリのCLIツールは良い。
で、先に作ったmoved.tf
を消し、planするとdestroyとcreateが出るような状態で
$ ./tfautomv -dry-run
Running "terraform init"...
Running "terraform plan"...
╷
│ Moves
│ ╷
│ │ From: local_file.file1
│ │ To: local_file.file11
│ ╵
╵
ちなみに-dry-run
なしだと moves.tf
が生える。
ところで、terraformコマンドの出力を食わせるわけでもplanファイルを読ませるわけでもなく、このコマンド単体でplanを実行したところが気になる。ローカルで使っているterraformコマンドとのバージョン互換の意味で。
ちょっと試してみると、ちゃんとPATHのterraformコマンドを実行するようだった。PATHにあったやつを雑にrenameすると動かなくなった。
また、改めてドキュメントを読むと(今更?)、terraformとして使うコマンドを明示的に指定するオプションがあるようだった。ヨシ!
次にgit管理。
cdktf.out
配下ではないところにmoved.tfを置き、cdktfコマンドを実行するときにいい感じに読ませるようにする
これはちょっと考えたが、ゴチャゴチャやらずにcdktf.out/stacks/moved/moves.tf
をgit add -f
で突っむのが一番シンプルで楽そう。
※当初はmoved.tf
の想定だったがtfautomvコマンドの出力がmoves.tf
だったので、そっちに合わせた
main.tsでsynthを実行する前後あたりでtsでファイルコピーしてもいいけど、gitにそのまま入れるほうが追加コードもなくてシンプルだと思う。tfautomvコマンドの実行 & 出力を考えてもそれが良さげ。
ちなみに、gitignoreをうまいことやってmoves.tfをignoreしないようにできないかと試行錯誤したが、ダメそうだった。
多階層にわたるディレクトリのファイルをignoreしつつ、深い階層の特定ファイルだけnotするのは無理っぽい?