Behavior of aws_secretsmanager_secret_version between terraform refresh/import

以下の記事で例付きで説明があるが、AWS Secrets Manager を terraform 外から更新した場合、aws_secretsmanager_secret_version を terraform refresh しても secret_string の更新が無視される(version_stages は更新される)。

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

terraform コアの話として、terraform import は ImportState -> Read の順で実行して tfsate を作る(terraform refresh は Read だけ)

実際に作って動き見る
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"
}

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"
]
}
レスポンス的には更新されてないようだがコンソールからは確認できた

一応 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"
}

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": []
},

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"
}

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"
]
}

結論
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 だけが更新されるという挙動になる。