Terraform v1.11 の Write-Only Attributes を試してみる
Terraform v1.11 の beta 版がリリースされています。
ノートを見てみると write-only attributes なるものが書いてあります。
どうやら「state に保存されない書き込み専用の attribute」が使えるようになるらしいです。
そして AWS Provider のリポジトリを眺めてみると、 aws_ssm_parameter resource に value_wo という Write-Only Attribute を追加する PR が出ています。
というわけで試してみました。
「せめて PR がマージされるまで待ちなよ」
うるさい!!!!
(2025/02/12 追記) マージされた!!!!
(2025/02/14 追記) リリースされた!!!! ( AWS Provider v5.87.0 )
検証環境
- Terraform v1.11.0-beta2
 - AWS Provider 
91b891ea9421b5b03c4ee63cebf9d51b3692c92a 
Write-Only Attributes を使わない場合
まずは Write-Only Attributes を使わない場合を見ていきましょう。
aws_ssm_parameter resource を使って password という名前の SSM パラメータを作成してみます。
resource "aws_ssm_parameter" "password" {
  name = "password"
  type = "SecureString"
  value = "AWESOME_PASSWORD" # これは機密情報
}
plan を実行してみると value は (sensitive value) としてマスクされています。
$ terraform plan
Terraform will perform the following actions:
  # aws_ssm_parameter.password will be created
  + resource "aws_ssm_parameter" "password" {
      + arn            = (known after apply)
      + data_type      = (known after apply)
      + id             = (known after apply)
      + insecure_value = (known after apply)
      + key_id         = (known after apply)
      + name           = "password"
      + tags_all       = (known after apply)
      + tier           = (known after apply)
      + type           = "SecureString"
      + value          = (sensitive value) # <===== ちゃんとマスクされている
      + version        = (known after apply)
    }
このように、 Terraform では sensitive としてマークされた値は通常 plan などの出力では表示されないため、一見すると安全なように見えます。
さて、 apply を実行したあとの tfstate の中身を見てみましょう。
$ terraform apply
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
{
  // ...
  "resources": [
    {
      // ...
      "type": "aws_ssm_parameter",
      "name": "password",
      // ...
      "instances": [
        {
          // ...
          "attributes": {
            // ...
            "value": "AWESOME_PASSWORD", // <===== !!!!!
            // ...
          },
          // ...
        }
      ]
    }
  ],
  // ...
}
value の値が平文で保存されていますね。
このように Terraform では sensitive としてマークされた値だろうと tfstate には平文で保存されてしまいます。
つまり、 tfstate にアクセスできる権限さえあれば Terraform 経由で設定された機密情報を誰でも閲覧できてしまうということになります。
これは Terraform 運用者を長年悩ませ続けてきた問題であり、今までも様々な運用方法が検討されてきました。
Write-Only Attributes を使う場合
それでは Write-Only Attributes を使ってみます。
aws_ssm_parameter resource の場合は value の代わりに value_wo を使います。
value_wo_version については後述します。
 resource "aws_ssm_parameter" "password" {
   name = "password"
   type = "SecureString"
-  value = "AWESOME_PASSWORD"
+  value_wo         = "AWESOME_PASSWORD"
+  value_wo_version = 1 # これも忘れずに!
 }
plan を実行してみると、 value_wo が (write-only attribute) として値がマスクされていることが確認できます。
$ terraform plan
Terraform will perform the following actions:
  # aws_ssm_parameter.password will be created
  + resource "aws_ssm_parameter" "password" {
      + arn              = (known after apply)
      + data_type        = (known after apply)
      + has_value_wo     = (known after apply)
      + id               = (known after apply)
      + insecure_value   = (known after apply)
      + key_id           = (known after apply)
      + name             = "password"
      + tags_all         = (known after apply)
      + tier             = (known after apply)
      + type             = "SecureString"
      + value            = (sensitive value)
      + value_wo         = (write-only attribute) # <===== これ
      + value_wo_version = 1
      + version          = (known after apply)
    }
このまま apply してみます。
$ terraform apply
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
SSM パラメータが作成されました。
# 作成された SSM パラメータを確認してみる
$ aws ssm get-parameter \
  --name password \
  --with-decryption \
  --output text \
  --query Parameter.Value
AWESOME_PASSWORD
そして tfstate を見てみましょう。
{
  // ...
  "resources": [
    {
      // ...
      "type": "aws_ssm_parameter",
      "name": "password",
      // ...
      "instances": [
        {
          // ...
          "attributes": {
            // ...
            "value": "", // <===== !!!!!
            "value_wo": null,
            "value_wo_version": 1,
            // ...
          },
          // ...
        }
      ]
    }
  ],
  // ...
}
value が tfstate に保存されていないことが確認できます ( value_wo も同様 ) 。
このように、 Write-Only Attributes は文字通り書き込み専用の Attribute であり、 tfstate に保存されることはありません。
つまり Write-Only Attributes を使えば Terraform 経由で値を設定しつつ、且つ機密情報が tfstate に平文で保存されることを防ぐことができます。アツい。
Write-Only Attributes を更新する
Write-Only Attributes を更新してみます。
とりあえず value_wo の値を書き換えてみます。
 resource "aws_ssm_parameter" "password" {
   name = "password"
   type = "SecureString"
-  value_wo         = "AWESOME_PASSWORD"
+  value_wo         = "AWESOME_PASSWORD_UPDATED"
   value_wo_version = 1
 }
この状態で plan を実行してみると No changes. と言われてしまいました。
$ terraform plan
No changes. Your infrastructure matches the configuration.
value_wo の値を更新するためには value_wo_version も一緒に更新してあげる必要があります。
 resource "aws_ssm_parameter" "password" {
   name = "password"
   type = "SecureString"
   value_wo         = "AWESOME_PASSWORD_UPDATED"
-  value_wo_version = 1
+  value_wo_version = 2
 }
この状態で再度 plan を実行してみると、 value_wo_version の変更が検知されていることが確認できます。
$ terraform plan
Terraform will perform the following actions:
  # aws_ssm_parameter.password will be updated in-place
  ~ resource "aws_ssm_parameter" "password" {
      ~ has_value_wo     = true -> (known after apply)
        id               = "password"
        name             = "password"
        tags             = {}
      ~ value_wo_version = 1 -> 2
        # (11 unchanged attributes hidden)
    }
apply を実行すると、 value_wo で設定した値が SSM パラメータに反映されました。
$ terraform apply
Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
# SSM パラメータが更新されていることを確認してみる
$ aws ssm get-parameter \
  --name password \
  --with-decryption \
  --output text \
  --query Parameter.Value
AWESOME_PASSWORD_UPDATED
このように、 value_wo_version は value_wo の変更を検知するために利用される Attribute です。
設定する値は数値であればなんでもいいようです。今回は 1 -> 2 にお行儀よくインクリメントしましたが、 0 でも -100 でも普通に動きました。
補足
あくまで今回の例は aws_ssm_parameter resource のケースでしかなく、必ずしも他のリソースでも同じ使い方 ( <属性>_wo / <属性>_wo_version の形式 ) になるとは限らないことに注意してください。
この辺りは Terraform Provider 側の実装次第なので、今後どのように導入が進められていくのか要注目です。
まとめ
Terraform v1.10 で導入された Ephemeral resources/values などもそうですが、 state に特定の値を保存させないための機能が揃ってきててとてもいいですね。
Discussion