Terraform 1.11がGAになりました

Terraform 1.11がGAになりました

Clock Icon2025.03.03

Terraformのversion 1.11が2025/2/27にGA(一般提供開始)になりました。新機能3つを紹介します。

write-only attributes が追加された

write-only attributesは、1つ前のバージョンであるv1.10で追加されたEphemeral Valuesの一機能です。v1.10の段階で「v1.11でwrite-only attributesを追加する」と告知されており、今回リリースされたv1.11で追加されました。
Ephemeral Valuesについてご存じない方は一度以下をご確認ください。

Ephemeral Valuesの中のEphemeral resourcesは、いわばData SourceのEphemeral版でした。今回のwrite-only attributeは、resourceのattributeのEphemeral版です。つまりリソース作成時に値を指定できますが、stateファイルには残りません。

コード例

AWSのParameter Storeを使います。 value_woがwrite-only attributeです。使用する際はvalue_wo_versionも併せて指定する必要があります。

resource "aws_ssm_parameter" "foo" {
  name  = "foo"
  type  = "SecureString"

  value_wo         = "bar"
  value_wo_version = 1
}

こちらのコードでapplyした後に、
State ファイルの中身を確認する terraform state pullコマンドを実行してみます。
value_woattribute値はnullになっていますね。

{
  "version": 4,
  "terraform_version": "1.11.0",
  "serial": 3,
  "lineage": "5d9ed96c-b8bf-a028-24d6-5aaaea655cf0",
  "outputs": {},
  "resources": [
    {
      "mode": "managed",
      "type": "aws_ssm_parameter",
      "name": "foo",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "allowed_pattern": "",
            "arn": "arn:aws:ssm:ap-northeast-1:123456789012:parameter/foo",
            "data_type": "text",
            "description": "",
            "has_value_wo": true,
            "id": "foo",
            "insecure_value": null,
            "key_id": "alias/aws/ssm",
            "name": "foo",
            "overwrite": null,
            "tags": {},
            "tags_all": {},
            "tier": "Standard",
            "type": "SecureString",
            "value": "",
            "value_wo": null,
            "value_wo_version": 1,
            "version": 1
          },
          "sensitive_attributes": [
            [
              {
                "type": "get_attr",
                "value": "value_wo"
              }
            ],
            [
              {
                "type": "get_attr",
                "value": "value"
              }
            ]
          ],
          "private": "bnVsbA=="
        }
      ]
    }
  ],
  "check_results": null
}

apply後に、以下のようにvalue指定値を変えたとします。

 resource "aws_ssm_parameter" "foo" {
   name  = "foo"
   type  = "SecureString"

-  value_wo         = "bar"
+  value_wo         = "modified"
   value_wo_version = 1
 }

この状態で再度applyしても変更は発生しません。

 % terraform apply
aws_ssm_parameter.foo: Refreshing state... [id=foo]

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

変更を適用するには value_wo_versionを変更する必要があります。value_wo_version値はStateファイル内にあるので、Stateファイル内の値とコード内の値を比べて、異なっている場合のみ現在のコードで指定されている value_wo値でvalue_woを更新する、という風になっています。

 resource "aws_ssm_parameter" "foo" {
   name  = "foo"
   type  = "SecureString"

   value_wo         = "modified"
-  value_wo_version = 1
+  value_wo_version = 2
 }

value_wo_versionは今回の様に 1→2と順にインクリメントする必要は無いです。例えば 1000を指定しても更新は走ります。とにかく現在の値と違う値であれば良いようです。とはいえ1ずつインクリメントさせる、更新日をYYYYMMDD形式(例:20250302)にするなどの方がわかりやすいかと思います。

以下のように plan結果に value_woが更新されるということは記載されません。個人的にはちょっと気持ち悪いので記載して欲しいですね。

  # aws_ssm_parameter.foo will be updated in-place
  ~ resource "aws_ssm_parameter" "foo" {
      ~ has_value_wo     = true -> (known after apply)
        id               = "foo"
        name             = "foo"
        tags             = {}
      ~ value_wo_version = 1 -> 2
        # (11 unchanged attributes hidden)
    }

コード改良

resource "aws_ssm_parameter" "foo" {
  name  = "foo"
  type  = "SecureString"

  value_wo         = "bar"
  value_wo_version = 1
}

ここまで例として使ってきた上記コードですが、write-only attributeを使うことによってStateファイルにはパラメーター値は残りませんが、コード内には記載されているのでGitに記録されてしまうのがイマイチですよね。実際に利用する際には以下のように ephemeral inputを使ってコマンド実行時に外部から値を指定するなどするのが良いと思います。

variable "foo_value" {
  type      = string
  ephemeral = true
  default   = ""
}

resource "aws_ssm_parameter" "foo" {
  name  = "foo"
  type  = "SecureString"

  value_wo         = var.foo_value
  value_wo_version = 1
}

初回は -varオプションでパラメーター値を指定します。

% terraform apply -var 'foo_value=bar'

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_ssm_parameter.foo will be created
  + resource "aws_ssm_parameter" "foo" {
      + 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             = "foo"
      + 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)
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_ssm_parameter.foo: Creating...
aws_ssm_parameter.foo: Creation complete after 0s [id=foo]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

以降は、パラメーター値を更新したいときは value_wo_versionを更新のうえ再度applyコマンドに-varオプションを追加してパラメーター値を指定、
更新したくないときは単純に applyすれば良いです。

AWS providerのwrite-only attributes 一覧

write-only attributeの名前は接尾辞として _woが付きます。

※ 漏れがあったらゴメンナサイ

気になった点: aws_db_instanceのドキュメントに 「it will be stored in the state file」の記述

前述のaws_db_instancepassword_woのドキュメントのpassword_wo欄に、「it will be stored in the state file」との記載があります。

aws_db_instance-password_wo

気になったので実際にインスタンス作成して terraform state pullでStateファイルの中身を確認してみましたが、password_woは記録されていませんでした。ざっとこの「it will be stored in the state file」記述について調べてみたのですが特に情報が見つかりませんでした。何かご存知の方は教えていただけないでしょうか…

{
    "mode": "managed",
    "type": "aws_db_instance",
    "name": "default",
    "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
    "instances": [
    {
        (略)
        "password": null,
        "password_wo": null,
        "password_wo_version": 1,
        (略)
        },
        "sensitive_attributes": [
        [
            {
            "type": "get_attr",
            "value": "password_wo"
            }
        ],
        [
            {
            "type": "get_attr",
            "value": "password"
            }
        ]
        ],
        (略)
    }
    ]
}

S3 Native State Locking がGAに。 DynamoDBでのロックは deprecatedに。

1つ前の v1.10より S3 Native State Locking機能が experimental featureとして利用可能でした。 こちらが今回GAになっています。

この機能は、S3バックエンド上のStateファイルが破損しないように保護するものです。複数人が同時に terraform applyコマンドを実行するなどしてStateファイルを同時更新しようとする際にファイル破損が起こり得ます。そこで、Stateファイルを更新中はファイルにロックをかけて、他のユーザーが更新を行えないようにする仕組みを提供します。(昔使っていた VSS(Visual SourceSafe)というバージョン管理ツールにこういう機能あったな…) このロック機能をS3だけで実現できるようになったのがS3 Native State Lockingです。

従来は同様のロック機能をDynamoDBを用いて実現していました。が、こちらはv1.10時点で将来deprecated化されることが告知されており、今回v1.11にて正式にdeprecatedとなりました。具体的には S3 backendブロックでのDynamoDBロック関連のattributesがdeprecatedとなっています。将来的には利用不可になりますのでS3 Native State Lockingへ移行しましょう。

コード例

S3 Native State Lockingのコード例です。最後のuse_lockfile = true のみが本機能利用に必要なattributeです。とても簡単ですね。

terraform {
  required_version = "= 1.11.0"

  backend "s3" {
    bucket = "(バケット名)"
    key    = "s3-native-state-locking-test.tfstate"
    region = "ap-northeast-1"

    use_lockfile = true
  }
}

ロック例

terraform apply中に${stateファイルオブジェクトキー名}.tflockというファイル名のロックファイルが作成され、コマンド終了後に削除されました。
lockfile.png

S3 Native State Lockingを使う場合、各種terraformコマンドを実施するIAMエンティティにこのロックファイルオブジェクトの更新権限を付与する必要があることに注意です。以下はIAM Policy例です。最後のブロックでロックファイルに関する権限を付与しています。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::mybucket",
      "Condition": {
        "StringEquals": {
          "s3:prefix": "mybucket/path/to/my/key"
        }
      }
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": [
        "arn:aws:s3:::mybucket/path/to/my/key"
      ]
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
      "Resource": [
        "arn:aws:s3:::mybucket/path/to/my/key.tflock"
      ]
    }
  ]
}

S3 Bucket Permissions | Backend Type: s3 | Terraform | HashiCorp Developer より引用

ちなみに terraform planでもロックがかかります。plan時に使うIAMエンティティにReadOnly権限のみを付与していた場合、ロックファイル作成に失敗してエラーになります。 terraform plan -lock=false で回避できます。

terraform testコマンドで JUnit XML フォーマットでテストレポートを作成できるようになった

以下のチュートリアルのテストコードのTerraformバージョンを 1.11.0に上げ、-junit-xmlオプションを使ってJUnit XML フォーマットのテストレポートを作成してみました。

テストコードは以下のようになっています。

# Call the setup module to create a random bucket prefix
run "setup_tests" {
  module {
    source = "./tests/setup"
  }
}

# Apply run block to create the bucket
run "create_bucket" {
  variables {
    bucket_name = "${run.setup_tests.bucket_prefix}-aws-s3-website-test"
  }

  # Check that the bucket name is correct
  assert {
    condition     = aws_s3_bucket.s3_bucket.bucket == "${run.setup_tests.bucket_prefix}-aws-s3-website-test"
    error_message = "Invalid bucket name"
  }

  # Check index.html hash matches
  assert {
    condition     = aws_s3_object.index.etag == filemd5("./www/index.html")
    error_message = "Invalid eTag for index.html"
  }

  # Check error.html hash matches
  assert {
    condition     = aws_s3_object.error.etag == filemd5("./www/error.html")
    error_message = "Invalid eTag for error.html"
  }
}

コマンド実行します。

% terraform test -junit-xml=testreport.xml
tests/website.tftest.hcl... in progress
  run "setup_tests"... pass
  run "create_bucket"... pass
tests/website.tftest.hcl... tearing down
tests/website.tftest.hcl... pass

Success! 2 passed, 0 failed.

以下のテストレポートが作成されました。

<?xml version="1.0" encoding="UTF-8"?><testsuites>
  <testsuite name="tests/website.tftest.hcl" tests="2" skipped="0" failures="0" errors="0">
    <testcase name="setup_tests" classname="tests/website.tftest.hcl" time="0.31946" timestamp="2025-03-03T02:30:38Z"></testcase>
    <testcase name="create_bucket" classname="tests/website.tftest.hcl" time="14.114209" timestamp="2025-03-03T02:30:39Z"></testcase>
  </testsuite>
</testsuites>

以下のようにしてテストをわざと失敗させてみます。

   # Check that the bucket name is correct
   assert {
+    condition     = aws_s3_bucket.s3_bucket.bucket == "error"
-    condition     = aws_s3_bucket.s3_bucket.bucket == "${run.setup_tests.bucket_prefix}-aws-s3-website-test"
     error_message = "Invalid bucket name"
   }

テスト実行結果です。

% terraform test -junit-xml=testreport.xml
tests/website.tftest.hcl... in progress
  run "setup_tests"... pass
  run "create_bucket"... fail
╷
│ Error: Test assertion failed
│ 
│   on tests/website.tftest.hcl line 16, in run "create_bucket":
│   16:     condition     = aws_s3_bucket.s3_bucket.bucket == "error"
│     ├────────────────
│     │ aws_s3_bucket.s3_bucket.bucket is "daily-manually-sharp-catfish-aws-s3-website-test"
│ 
│ Invalid bucket name
╵
tests/website.tftest.hcl... tearing down
tests/website.tftest.hcl... fail

Failure! 1 passed, 1 failed.

テストレポートにも詳細なエラー内容が記載されました。

<?xml version="1.0" encoding="UTF-8"?><testsuites>
  <testsuite name="tests/website.tftest.hcl" tests="2" skipped="0" failures="1" errors="0">
    <testcase name="setup_tests" classname="tests/website.tftest.hcl" time="0.233125" timestamp="2025-03-03T02:38:21Z"></testcase>
    <testcase name="create_bucket" classname="tests/website.tftest.hcl" time="10.936528" timestamp="2025-03-03T02:38:21Z">
      <failure message="1 of 3 assertions failed: Invalid bucket name"><![CDATA[
Error: Test assertion failed

  on tests/website.tftest.hcl line 16, in run "create_bucket":
  16:     condition     = aws_s3_bucket.s3_bucket.bucket == "error"
    ├────────────────
    │ aws_s3_bucket.s3_bucket.bucket is "daily-manually-sharp-catfish-aws-s3-website-test"

Invalid bucket name
]]></failure>
    </testcase>
  </testsuite>
</testsuites>

この機能により、CI/CDツールとのスムーズな連携、JUnit XML フォーマットに対応している他システムやフレームワークのテスト結果との統合・集約などが期待できます。

感想

write-only attributesは今後使えるところはどんどん使っていきたいです。機密情報がStateファイルに載っていて嬉しいことは特に無いので。

S3 Native State Lockingも良いですね!私はこれまでもあったDynamoDBによるロック機能はDynamoDBテーブルの用意が面倒であまり利用していませんでした。がこの機能だと追加で用意する必要のあるリソースが無いので、手間無しにより安全にTerraformを利用できるようになるので、積極的に使っていきたいと思います。

JUnit XML フォーマットでテストレポートを作成できる件については、現時点ではあまり使うつもりは無いです。現状 terraform test自体も活用できていないので。ですがすでにJUnit XML フォーマットを活用されている現場ではメリットが大きいのではないでしょうか。

参考情報

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.