Closed17

cdktfでmovedブロックをやりたい

ピン留めされたアイテム
inomotoinomoto

結論:

  • cdktf.out/stacks/xxx/cdk.tf.jsonの横にmovedブロックだけを書いたmoves.tfみたいなのを置くことで簡易に実現できる
    • git管理上は、moves.tfだけgit add -fで無理やり突っ込むのが楽
  • (cdktf関係ないけど)tfautomvが便利そう
inomotoinomoto

cdktfで、terraform (HCL) のmovedブロック相当をやりたい。
さしあたっての想定ユースケースは、HCLベースのプロジェクトを徐々に(notビックバンで)cdktfに移行するときの補助。

inomotoinomoto

本家の関連issueはこのあたり。
https://github.com/hashicorp/terraform-cdk/issues/1102
https://github.com/hashicorp/terraform-cdk/issues/1292

最初の提起が2021/9/30で、2023/4/16現在で計画にもなっていないあたりから、近いうちでの公式実装はかなり望みが薄い。
(tf.jsonへのトランスパイラでしかないcdktfにて、ただのブロックの実現にこれだけ時間がかかるあたり、cdktfの性質による根本的な厄介事やエッジケースが存在するのだと思われる)

inomotoinomoto

movedだけHCLで書いてmoduleとして読み込むという手が考えられる。

しかしmoduleで読み込んだ場合、そのmovedブロックはそのmodule以下にしか影響を与えることができない。想定ユースケースでは親cdktfのスタックにある子module(オリジナルのHCLコードベース)から親スタックにmovedする必要があるので、この手は採れない

inomotoinomoto

残る手は

  • movedブロックを生成するcdktfのリソースクラス(なりなんなり、cdktfのコードを書いてmovedを吐くやつ)を作る
  • cdktfのsynthで生成されるtf.jsonの横に、movedを書いたHCL or tf.jsonを置く

あたりか?

inomotoinomoto

ひとまずお砂場を作った
https://github.com/cumet04/sbox_cdktf/tree/6948f2a34eea67b1318ebfd642a7b1c37bd17293/moved

コードはこれ。

main.ts
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でファイルを作るだけ。

inomotoinomoto

適当にinitして適当に掃除しただけ。

なお、適当にinitするとTypeScript 5系が入ったのだが、5系ではsynthが通らなかったので4系に下げている。細かい事情は追ってない。

inomotoinomoto

一応動作を見ておく。

この内容でapplyすると、cdktf.out/stacks/moved/以下(要するにterraformコマンドが実行されるディレクトリ)にfile1.txtfile2.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を直接実行しても同じ。

inomotoinomoto

まずは簡単そうな

cdktfのsynthで生成されるtf.jsonの横に、movedを書いたHCL or tf.jsonを置く

を試してみる。

inomotoinomoto

cdktfとの兼ね合いをゴニョゴニョするのは一旦後回しにして、cdktf.out/stacks/movedディレクトリ配下に直接下記ファイルを置く。

cdktf.out/stacks/moved/moved.tf
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.

...いけとるがな。

inomotoinomoto

オレオレcdktfリソース作らないといけないかと思って気合を入れてスクラップを作ったのに、書いてる途中で思いついた別方法で一瞬で解決してしまった。

これで終わってはどうしようもないので、もうちょっと実用を想定して下記を試してみたい。

  • cdktf.out配下ではないところにmoved.tfを置き、cdktfコマンドを実行するときにいい感じに読ませるようにする
    • cdktf.outは生成ファイルなのでgitに乗らないし、なんなら時々削除するし。そこらへんをいい感じにする
  • tfautomvも試してみる
  • HCLのプロジェクトをcdktfにmoduleとして取り込み、それをcdktfに移していくシナリオを試してみる
inomotoinomoto

なお、この方法では(cdktfリソースを作る方法と比べると)cdktfのコードからリソースIDを参照することができないというデメリットがある。

そのため、cdktfのプロジェクト/アプリケーションで一時的なmigrationファイルとして使うならともかく、ライブラリを提供するようなユースケースでは使えないかもしれない。

inomotoinomoto
inomotoinomoto

今後真面目にモリモリ使いたいというステータスではないので、インストールは一時的な感じにする。
具体的にはリポジトリの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が生える。

inomotoinomoto

ところで、terraformコマンドの出力を食わせるわけでもplanファイルを読ませるわけでもなく、このコマンド単体でplanを実行したところが気になる。ローカルで使っているterraformコマンドとのバージョン互換の意味で。

ちょっと試してみると、ちゃんとPATHのterraformコマンドを実行するようだった。PATHにあったやつを雑にrenameすると動かなくなった。
また、改めてドキュメントを読むと(今更?)、terraformとして使うコマンドを明示的に指定するオプションがあるようだった。ヨシ!

inomotoinomoto

次にgit管理。

cdktf.out配下ではないところにmoved.tfを置き、cdktfコマンドを実行するときにいい感じに読ませるようにする

これはちょっと考えたが、ゴチャゴチャやらずにcdktf.out/stacks/moved/moves.tfgit add -fで突っむのが一番シンプルで楽そう。
※当初はmoved.tfの想定だったがtfautomvコマンドの出力がmoves.tfだったので、そっちに合わせた

main.tsでsynthを実行する前後あたりでtsでファイルコピーしてもいいけど、gitにそのまま入れるほうが追加コードもなくてシンプルだと思う。tfautomvコマンドの実行 & 出力を考えてもそれが良さげ。

ちなみに、gitignoreをうまいことやってmoves.tfをignoreしないようにできないかと試行錯誤したが、ダメそうだった。
多階層にわたるディレクトリのファイルをignoreしつつ、深い階層の特定ファイルだけnotするのは無理っぽい?

このスクラップは2023/07/30にクローズされました