Closed10

Behavior of aws_secretsmanager_secret_version between terraform refresh/import

ishii1648ishii1648

以下の記事で例付きで説明があるが、AWS Secrets Manager を terraform 外から更新した場合、aws_secretsmanager_secret_version を terraform refresh しても secret_string の更新が無視される(version_stages は更新される)。
https://dev.classmethod.jp/articles/note-about-terraform-ignore-changes/#%25E2%2596%25B3%253A%2520%25E6%25A9%259F%25E5%25AF%2586%25E6%2583%2585%25E5%25A0%25B1%25E3%2582%2592state%25E3%2583%2595%25E3%2582%25A1%25E3%2582%25A4%25E3%2583%25AB%25E3%2581%25AB%25E4%25BF%259D%25E6%258C%2581%25E3%2581%2597%25E3%2581%259F%25E3%2581%258F%25E3%2581%25AA%25E3%2581%2584

ishii1648ishii1648

しかし terraform import だと secret_string 含めたすべての値が tfstate に書き込まれる。

terraform refresh/import はいずれも実リソースを読み取り、tfstate に反映するという点で同じ役割を持ったサブコマンドであるにも関わらずこの挙動の違いはどうして起きるのか?

ishii1648ishii1648

実際に作って動き見る

terraform {
  required_version = "~> 1.11"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5"
    }
  }

resource "aws_secretsmanager_secret" "test" {
  name = "test"
}

resource "aws_secretsmanager_secret_version" "test" {
  secret_id     = aws_secretsmanager_secret.test.id
  secret_string = "test"
}
ishii1648ishii1648

terraform 外から更新

❯ a secretsmanager put-secret-value --secret-id test --secret-string test-3
{
    "ARN": "arn:aws:secretsmanager:ap-northeast-1:787080764332:secret:test-GdJpjt",
    "Name": "test",
    "VersionId": "0d974517-cdac-4bc4-8b4e-26c230fe87b0",
    "VersionStages": [
        "AWSCURRENT"
    ]
}

レスポンス的には更新されてないようだがコンソールからは確認できた

ishii1648ishii1648

一応 CLI からでも確認できた

❯ a secretsmanager get-secret-value --secret-id test
{
    "ARN": "arn:aws:secretsmanager:ap-northeast-1:787080764332:secret:test-GdJpjt",
    "Name": "test",
    "VersionId": "0d974517-cdac-4bc4-8b4e-26c230fe87b0",
    "SecretString": "test-3",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2025-05-17T20:46:39.458000+09:00"
}
ishii1648ishii1648

terraform refresh してから state の状態確認

関係するとこだけ抜粋

❯ t refresh && t state pull
    {
      "mode": "managed",
      "type": "aws_secretsmanager_secret_version",
      "name": "test",
      "provider": "provider[\"registry.terraform.io/hashicorp/aws\"]",
      "instances": [
        {
          "schema_version": 0,
          "attributes": {
            "has_secret_string_wo": null,
            "secret_binary": "",
            "secret_id": "arn:aws:secretsmanager:ap-northeast-1:787080764332:secret:test-GdJpjt",
            "secret_string": "test",
            "secret_string_wo": null,
            "secret_string_wo_version": null,
            "version_id": "terraform-20250517112732680300000002",
            "version_stages": []
          },
          
ishii1648ishii1648

DEBUG モードで terraform refresh してみる

❯ TF_LOG=DEBUG t refresh
2025-05-17T20:49:18.832+0900 [DEBUG] provider.terraform-provider-aws_v5.98.0_x5: HTTP Request Sent: net.peer.name=secretsmanager.ap-northeast-1.amazonaws.com tf_rpc=ReadResource http.request.header.content_type=application/x-amz-json-1.1 rpc.method=GetSecretValue
  http.request.body=
  | {"SecretId":"arn:aws:secretsmanager:ap-northeast-1:111111111:secret:test-GdJpjt","VersionId":"terraform-20250517112732680300000002"}
   rpc.system=aws-api tf_provider_addr=registry.terraform.io/hashicorp/aws tf_req_id=f04ff7cb-f5ed-8471-e22f-697537dbd240 @module=aws aws.region=ap-northeast-1 http.method=POST tf_mux_provider="*schema.GRPCProviderServer" tf_resource_type=aws_secretsmanager_secret_version http.request.header.amz_sdk_invocation_id=0500c5d4-21ad-4368-9695-61b840fdc85c @caller=github.com/hashicorp/aws-sdk-go-base/v2@v2.0.0-beta.64/logging/tf_logger.go:45 http.request_content_length=135 rpc.service="Secrets Manager" http.request.header.x_amz_target=secretsmanager.GetSecretValue http.request.header.x_amz_security_token="*****" http.url=https://secretsmanager.ap-northeast-1.amazonaws.com/ tf_aws.signing_region="" http.request.header.authorization="AWS4-HMAC-SHA256 Credential=ASIA************F76C/20250517/ap-northeast-1/secretsmanager/aws4_request, SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;content-length;content-type;host;x-amz-date;x-amz-security-token;x-amz-target, Signature=*****" http.request.header.x_amz_date=20250517T114918Z http.user_agent="APN/1.0 HashiCorp/1.0 Terraform/1.11.3 (+https://www.terraform.io) terraform-provider-aws/5.98.0 (+https://registry.terraform.io/providers/hashicorp/aws) aws-sdk-go-v2/1.36.3 ua/2.1 os/macos lang/go#1.23.8 md/GOOS#darwin md/GOARCH#amd64 api/secretsmanager#1.35.4 m/i" tf_aws.sdk=aws-sdk-go-v2 http.request.header.amz_sdk_request="attempt=1; max=25" timestamp="2025-05-17T20:49:18.832+0900"
2025-05-17T20:49:18.865+0900 [DEBUG] provider.terraform-provider-aws_v5.98.0_x5: HTTP Response Received: http.duration=32 http.response.header.content_type=application/x-amz-json-1.1 http.status_code=200 tf_aws.sdk=aws-sdk-go-v2 tf_provider_addr=registry.terraform.io/hashicorp/aws @caller=github.com/hashicorp/aws-sdk-go-base/v2@v2.0.0-beta.64/logging/tf_logger.go:45
  http.response.body=
  | {"ARN":"arn:aws:secretsmanager:ap-northeast-1:111111111:secret:test-GdJpjt","CreatedDate":1.747481252721E9,"Name":"test","SecretString":"test","VersionId":"terraform-20250517112732680300000002"}
   http.response.header.date="Sat, 17 May 2025 11:49:18 GMT" http.response_content_length=197 tf_req_id=f04ff7cb-f5ed-8471-e22f-697537dbd240 tf_aws.signing_region="" tf_mux_provider="*schema.GRPCProviderServer" tf_resource_type=aws_secretsmanager_secret_version aws.region=ap-northeast-1 @module=aws http.response.header.x_amzn_requestid=b1267fb3-d0d1-43cc-a1cd-0e82ebe227cf rpc.method=GetSecretValue rpc.service="Secrets Manager" rpc.system=aws-api tf_rpc=ReadResource timestamp="2025-05-17T20:49:18.865+0900"

リクエスト/レスポンス

{
  "SecretId": "arn:aws:secretsmanager:ap-northeast-1:111111111:secret:test-GdJpjt",
  "VersionId": "terraform-20250517112732680300000002"
}
{
  "ARN": "arn:aws:secretsmanager:ap-northeast-1:111111111:secret:test-GdJpjt",
  "CreatedDate": 1747481252.721,
  "Name": "test",
  "SecretString": "test",
  "VersionId": "terraform-20250517112732680300000002"
}
ishii1648ishii1648

terraform import

❯ t state rm aws_secretsmanager_secret_version.test
❯ TF_LOG=DEBUG terraform import aws_secretsmanager_secret_version.test 'arn:aws:secretsmanager:ap-northeast-1:111111111:secret:test-GdJpjt|0d974517-cdac-4bc4-8b4e-26c230fe87b0'
2025-05-17T21:14:04.244+0900 [DEBUG] provider.terraform-provider-aws_v5.98.0_x5: HTTP Request Sent: @module=aws aws.region=ap-northeast-1 tf_resource_type=aws_secretsmanager_secret_version http.request.header.amz_sdk_invocation_id=950b4345-62c3-4605-82c1-b61f0f363080 http.request.header.amz_sdk_request="attempt=1; max=25" http.url=https://secretsmanager.ap-northeast-1.amazonaws.com/ http.user_agent="APN/1.0 HashiCorp/1.0 Terraform/1.11.3 (+https://www.terraform.io) terraform-provider-aws/5.98.0 (+https://registry.terraform.io/providers/hashicorp/aws) aws-sdk-go-v2/1.36.3 ua/2.1 os/macos lang/go#1.23.8 md/GOOS#darwin md/GOARCH#amd64 api/secretsmanager#1.35.4 m/i" net.peer.name=secretsmanager.ap-northeast-1.amazonaws.com rpc.service="Secrets Manager"
  http.request.body=
  | {"SecretId":"arn:aws:secretsmanager:ap-northeast-1:1111111:secret:test-GdJpjt","VersionId":"0d974517-cdac-4bc4-8b4e-26c230fe87b0"}
   http.request_content_length=135 @caller=github.com/hashicorp/aws-sdk-go-base/v2@v2.0.0-beta.64/logging/tf_logger.go:45 http.request.header.x_amz_date=20250517T121404Z http.request.header.x_amz_target=secretsmanager.GetSecretValue http.method=POST http.request.header.authorization="AWS4-HMAC-SHA256 Credential=ASIA************QJMS/20250517/ap-northeast-1/secretsmanager/aws4_request, SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;content-length;content-type;host;x-amz-date;x-amz-security-token;x-amz-target, Signature=*****" tf_provider_addr=registry.terraform.io/hashicorp/aws tf_rpc=ReadResource rpc.system=aws-api tf_aws.signing_region="" http.request.header.content_type=application/x-amz-json-1.1 http.request.header.x_amz_security_token="*****" rpc.method=GetSecretValue tf_aws.sdk=aws-sdk-go-v2 tf_mux_provider="*schema.GRPCProviderServer" tf_req_id=8595e74e-38b4-6201-d3ca-33106ba59123 timestamp="2025-05-17T21:14:04.243+0900"
2025-05-17T21:14:04.341+0900 [DEBUG] provider.terraform-provider-aws_v5.98.0_x5: HTTP Response Received: @module=aws http.response_content_length=230 tf_mux_provider="*schema.GRPCProviderServer" tf_provider_addr=registry.terraform.io/hashicorp/aws tf_resource_type=aws_secretsmanager_secret_version http.response.header.date="Sat, 17 May 2025 12:14:04 GMT" http.response.header.x_amzn_requestid=a08fe1dc-3370-421c-808a-3589ce75902d rpc.method=GetSecretValue tf_aws.sdk=aws-sdk-go-v2 @caller=github.com/hashicorp/aws-sdk-go-base/v2@v2.0.0-beta.64/logging/tf_logger.go:45 http.duration=97 tf_aws.signing_region="" tf_rpc=ReadResource aws.region=ap-northeast-1
  http.response.body=
  | {"ARN":"arn:aws:secretsmanager:ap-northeast-1:1111111:secret:test-GdJpjt","CreatedDate":1.747482399458E9,"Name":"test","SecretString":"test-3","VersionId":"0d974517-cdac-4bc4-8b4e-26c230fe87b0","VersionStages":["AWSCURRENT"]}
   http.response.header.content_type=application/x-amz-json-1.1 http.status_code=200 rpc.service="Secrets Manager" rpc.system=aws-api tf_req_id=8595e74e-38b4-6201-d3ca-33106ba59123 timestamp="2025-05-17T21:14:04.341+0900"

リクエスト/レスポンス

❯ echo '{"SecretId":"arn:aws:secretsmanager:ap-northeast-1:1111111:secret:test-GdJpjt","VersionId":"0d974517-cdac-4bc4-8b4e-26c230fe87b0"}' | jq
{
  "SecretId": "arn:aws:secretsmanager:ap-northeast-1:1111111:secret:test-GdJpjt",
  "VersionId": "0d974517-cdac-4bc4-8b4e-26c230fe87b0"
}
{
  "ARN": "arn:aws:secretsmanager:ap-northeast-1:1111111:secret:test-GdJpjt",
  "CreatedDate": 1747482399.458,
  "Name": "test",
  "SecretString": "test-3",
  "VersionId": "0d974517-cdac-4bc4-8b4e-26c230fe87b0",
  "VersionStages": [
    "AWSCURRENT"
  ]
}
ishii1648ishii1648

結論

terraform import の時は VersionId を渡してるので、それに紐づくシークレット値が返ってくる。

terraform で aws_secretsmanager_secret_version を作成すると VersionId は AWS が自動的に生成した ID がセットされて tfstate に保存される(上の例だと terraform-20250517112732680300000002)。

手動で更新した場合は当然その VersionId は tfstate に保存されないので terraform refresh したとしてもシークレット値は更新されない。一方 terraform refresh では(厳密には provider の READ 処理)、secretsmanager.DescribeSecret API も実行しており、ここで VersionIdsToStages というレスポンスを受け取っている。その結果、tfstate の secret_string は書き換わらず version_stages だけが更新されるという挙動になる。

このスクラップは5ヶ月前にクローズされました