こんにちは。X(クロス)イノベーション本部クラウドイノベーションセンターの柴田です。
本記事ではTerraformでコードを変更していないリソースが known after apply となってしまう場合の回避策をご紹介します。
前提
この記事は以下のTerraformのバージョンを前提とします。
新しいバージョンのTerraformでは本記事と異なる挙動をする可能性があります。
$ terraform version Terraform v1.5.3 on linux_amd64 + provider registry.terraform.io/hashicorp/aws v5.7.0
問題となるコードの例
以下のTerraformコードを例に考えてみましょう。
resource "aws_s3_bucket" "sample" { bucket_prefix = "sample-" tags = { Project = "My Project A" } } data "aws_iam_policy_document" "sample" { statement { effect = "Allow" actions = ["s3:*"] resources = [aws_s3_bucket.sample.arn] } } resource "aws_iam_policy" "sample" { name = "SampleBucketFullAccess" path = "/" policy = data.aws_iam_policy_document.sample.json }
このTerraformコードを terraform apply します。
$ terraform apply
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
<= read (data resources)
Terraform will perform the following actions:
# data.aws_iam_policy_document.sample will be read during apply
# (config refers to values not yet known)
<= data "aws_iam_policy_document" "sample" {
+ id = (known after apply)
+ json = (known after apply)
+ statement {
+ actions = [
+ "s3:*",
]
+ effect = "Allow"
+ resources = [
+ (known after apply),
]
}
}
# aws_iam_policy.sample will be created
+ resource "aws_iam_policy" "sample" {
+ arn = (known after apply)
+ id = (known after apply)
+ name = "SampleBucketFullAccess"
+ name_prefix = (known after apply)
+ path = "/"
+ policy = (known after apply)
+ policy_id = (known after apply)
+ tags_all = (known after apply)
}
# aws_s3_bucket.sample will be created
+ resource "aws_s3_bucket" "sample" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = (known after apply)
+ bucket_domain_name = (known after apply)
+ bucket_prefix = "sample-"
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ object_lock_enabled = (known after apply)
+ policy = (known after apply)
+ region = (known after apply)
+ request_payer = (known after apply)
+ tags = {
+ "Project" = "My Project A"
}
+ tags_all = {
+ "Project" = "My Project A"
}
+ website_domain = (known after apply)
+ website_endpoint = (known after apply)
}
Plan: 2 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_s3_bucket.sample: Creating...
aws_s3_bucket.sample: Creation complete after 1s [id=sample-20230713121614963200000001]
data.aws_iam_policy_document.sample: Reading...
data.aws_iam_policy_document.sample: Read complete after 0s [id=55239551]
aws_iam_policy.sample: Creating...
aws_iam_policy.sample: Creation complete after 1s [id=arn:aws:iam::************:policy/SampleBucketFullAccess]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
次にS3バケット aws_s3_bucket.sample の Project タグの値を変更します。
resource "aws_s3_bucket" "sample" {
bucket_prefix = "sample-"
tags = {
- Project = "My Project A"
+ Project = "My Project B"
}
}
変更後のTerraformコードに対して terraform plan を実行します。
$ terraform plan
aws_s3_bucket.sample: Refreshing state... [id=sample-20230713121614963200000001]
aws_iam_policy.sample: Refreshing state... [id=arn:aws:iam::************:policy/SampleBucketFullAccess]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
<= read (data resources)
Terraform will perform the following actions:
# data.aws_iam_policy_document.sample will be read during apply
# (depends on a resource or a module with changes pending)
<= data "aws_iam_policy_document" "sample" {
+ id = (known after apply)
+ json = (known after apply)
+ statement {
+ actions = [
+ "s3:*",
]
+ effect = "Allow"
+ resources = [
+ "arn:aws:s3:::sample-20230713121614963200000001",
]
}
}
# aws_iam_policy.sample will be updated in-place
~ resource "aws_iam_policy" "sample" {
id = "arn:aws:iam::************:policy/SampleBucketFullAccess"
name = "SampleBucketFullAccess"
~ policy = jsonencode(
{
- Statement = [
- {
- Action = "s3:*"
- Effect = "Allow"
- Resource = "arn:aws:s3:::sample-20230713121614963200000001"
},
]
- Version = "2012-10-17"
}
) -> (known after apply)
tags = {}
# (4 unchanged attributes hidden)
}
# aws_s3_bucket.sample will be updated in-place
~ resource "aws_s3_bucket" "sample" {
id = "sample-20230713121614963200000001"
~ tags = {
~ "Project" = "My Project A" -> "My Project B"
}
~ tags_all = {
~ "Project" = "My Project A" -> "My Project B"
}
# (10 unchanged attributes hidden)
# (3 unchanged blocks hidden)
}
Plan: 0 to add, 2 to change, 0 to destroy.
すると、変更がないはずのIAM policy aws_iam_policy.sample の policy が known after apply となってしまいました。
原因
これはData Sourceの仕様によるものです。
Data Resource Dependencies には以下のように記述されています。
Data resources have the same dependency resolution behavior as defined for managed resources. Setting the
depends_onmeta-argument withindatablocks defers reading of the data source until after all changes to the dependencies have been applied.In order to ensure that data sources are accessing the most up to date information possible in a wide variety of use cases, arguments directly referencing managed resources are treated the same as if the resource was listed in
depends_on.
要約すると以下のとおりです。
- Data Sourceでは
depends_onに記載されたリソースのすべての変更が適用されるまで読み取りは延期される。 - Data Sourceの引数が他のリソースを直接参照している場合、参照先のリソースがData Sourceの
depends_onに含まれている場合と同じように扱われる。
つまり Data Sourceが直接参照しているリソースに変更がある場合、それらの変更が適用されたあと、Data Sourceの読み取りが再実行される ということです。
改めて先ほどの例を考えてみましょう。Terraformが以下のように判断していたことがわかります。
aws_s3_bucket.sampleのコードの変更が検出される。data.aws_iam_policy_document.sampleはaws_s3_bucket.sampleを直接参照しているため、depends_onにaws_s3_bucket.sampleが含まれている場合と同じように扱われる。depends_onに含まれるaws_s3_bucket.sampleの変更が検出されたため、その変更が適用されたあとでdata.aws_iam_policy_document.sampleの再読み取りを行う必要があると判断される。- 3をうけて
aws_iam_policy.sampleのpolicyが更新されると判断される。
回避策
Data Resource Dependencies には以下のように記述されています。
This behavior can be avoided when desired by indirectly referencing the managed resource values through a
localvalue, unless the data resource itself has custom conditions.
どうやら Local Value を間に挟むことで先ほどの事象を回避できるようです。
試してみましょう。先ほどのTerraformコードを以下のように変更します。
provider "aws" { region = "ap-northeast-1" } resource "aws_s3_bucket" "sample" { bucket_prefix = "sample-" tags = { Project = "My Project B" } } locals { s3_bucket_arn = aws_s3_bucket.sample.arn } data "aws_iam_policy_document" "sample" { statement { effect = "Allow" actions = ["s3:*"] resources = [local.s3_bucket_arn] } } resource "aws_iam_policy" "sample" { name = "SampleBucketFullAccess" path = "/" policy = data.aws_iam_policy_document.sample.json }
変更後のTerraformコードに対して terraform plan を実行します。
$ terraform plan
aws_s3_bucket.sample: Refreshing state... [id=sample-20230713121614963200000001]
data.aws_iam_policy_document.sample: Reading...
data.aws_iam_policy_document.sample: Read complete after 0s [id=55239551]
aws_iam_policy.sample: Refreshing state... [id=arn:aws:iam::************:policy/SampleBucketFullAccess]
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_s3_bucket.sample will be updated in-place
~ resource "aws_s3_bucket" "sample" {
id = "sample-20230713121614963200000001"
~ tags = {
~ "Project" = "My Project A" -> "My Project B"
}
~ tags_all = {
~ "Project" = "My Project A" -> "My Project B"
}
# (10 unchanged attributes hidden)
# (3 unchanged blocks hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
S3バケット aws_s3_bucket.sample 以外の変更が表示されないことを確認できました。
おわりに
本記事ではTerraformでコードを変更していないリソースが known after apply となってしまう場合の回避策をご紹介しました。
この記事がこの問題に悩んでいる方のお役に立てば幸いです。
最後までお読みいただき、ありがとうございました。
参考
- Data Sources - Configuration Language | Terraform | HashiCorp Developer
- date source referencing managed resource proposes unnecessary changes under 0.14 · Issue #27171 · hashicorp/terraform
私たちは一緒に働いてくれる仲間を募集しています!
クラウドアーキテクト執筆:@shibata.takao、レビュー:寺山 輝 (@terayama.akira)
(Shodoで執筆されました)



