【Terraform】AWS Provider v4.9.0のS3リファクタリングの挙動を試してみた

v4以前のS3の書き方もオプションとして記述できるようになりました。
2022.04.11

こんにちは!AWS事業本部コンサルティング部のたかくにです。

今回は、タイトルの通りAWS Provider v4.9.0のS3リファクタリングについて挙動を確認してみようと思います。

aws_s3_bucketのリファクタリングについて

先日、v4のリリースで大きな反響があったaws_s3_bucketのリファクタリングについて変更がありました。

v4.9.0では、aws_s3_bucketブロック内に全て書く方法、aws_s3_bucket_policyなど複数ブロックに分けて書く方法のどちらでも書けるみたいです。

注意点として、両方で記述した場合、ドリフト検出の不具合や競合が生じるみたいです。

今回はその点も検証してみようと思います。

resource/aws_s3_bucket: The acceleration_status, acl, cors_rule, grant, lifecycle_rule, logging, object_lock_configuration.rule, policy, replication_configuration, request_payer, server_side_encryption_configuration, versioning, and website parameters are now Optional. Please refer to the documentation for details on drift detection and potential conflicts when configuring these parameters with the standalone aws_s3_bucket_* resources. (#23985)

v4.9.0

リファクタリングは不要になったわけでは無い

今回、v4.9.0でaws_s3_bucket内にプロパティを全て書き込めるようになりましたが、「リファクタリングが不要になった」と言う意味では無いようです。

aws_s3_bucketのリファレンスを確認すると、「非推奨の書き方」として取り上げられています。

S3 Bucket Accelerate can be configured in either the standalone resource aws_s3_bucket_accelerate_configuration or with the deprecated parameter acceleration_status in the resource aws_s3_bucket. Configuring with both will cause inconsistencies and may overwrite configuration.

Resource: aws_s3_bucket

今後のマイルストーンでも、2022年4月時点では特に言及がありませんでしたが、いつ変更になるかわからないため注意が必要です。

検証してみた

今回は、以下のコードでバージョンアップデート前後の挙動を確認してみようと思います。

適宜バージョンで、コメントアウト部分を変更していきます。

main.tf

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "4.8.0"
      # version = "4.9.0"
    }
  }
}

data "aws_caller_identity" "example" {}

resource "aws_s3_bucket" "example" {
  bucket  = "example-${data.aws_caller_identity.example.account_id}"

  # S3バケットのデフォルト暗号化
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm     = "AES256"
      }
    }
  }
}

# resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
#   bucket = aws_s3_bucket.example.bucket

#   rule {
#     apply_server_side_encryption_by_default {
#       sse_algorithm = "AES256"
#     }
#   }
# }

v4.8.0

terraform planを実行すると、v4以降のリファクタリングを行なっていないため怒られました。

takakuni % terraform plan
╷
│ Error: Value for unconfigurable attribute
│ 
│   with aws_s3_bucket.example,
│   on main.tf line 19, in resource "aws_s3_bucket" "example":
│   19: resource "aws_s3_bucket" "example" {
│ 
│ Can't configure a value for "server_side_encryption_configuration": its value will be decided automatically based on the result of applying this configuration.
╵

コードの一部を以下のように変更すると作成できました!

main.tf

resource "aws_s3_bucket" "example" {
  bucket  = "example-${data.aws_caller_identity.example.account_id}"

  # # S3バケットのデフォルト暗号化
  # server_side_encryption_configuration {
  #   rule {
  #     apply_server_side_encryption_by_default {
  #       sse_algorithm     = "AES256"
  #     }
  #   }
  # }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
  bucket = aws_s3_bucket.example.bucket

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

v4.9.0

ここからが、今回の本題です。

まずは、main.tfを一部修正して、terraform init -upgradeを実行します。

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      # version = "4.8.0"
      version = "4.9.0"
    }
  }
}

リファクタリング前のコード

terraform planを実行すると、無事planが通りますが、Warningが表示されました。

takakuni % terraform plan

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_s3_bucket.example will be created
  + resource "aws_s3_bucket" "example" {
      + acl                         = "private"
      + bucket                      = "example-XXXXXXXXXXXX"

      + server_side_encryption_configuration {
          + rule {
              + apply_server_side_encryption_by_default {
                  + sse_algorithm = "AES256"
                }
            }
        }

Plan: 1 to add, 0 to change, 0 to destroy.
╷
│ Warning: Argument is deprecated
│ 
│   with aws_s3_bucket.example,
│   on main.tf line 13, in resource "aws_s3_bucket" "example":
│   13: resource "aws_s3_bucket" "example" {
│ 
│ Use the aws_s3_bucket_server_side_encryption_configuration resource instead
│ 
│ (and one more similar warning elsewhere)

terraform applyでもリソース作成されましたが、作成後にもWarningが表示される仕様でした。

aws_s3_bucket.example: Creation complete after 2s [id=example-XXXXXXXXXXXX]
╷
│ Warning: Argument is deprecated
│ 
│   with aws_s3_bucket.example,
│   on main.tf line 19, in resource "aws_s3_bucket" "example":
│   19: resource "aws_s3_bucket" "example" {
│ 
│ Use the aws_s3_bucket_server_side_encryption_configuration resource instead
│ 
│ (and one more similar warning elsewhere)
╵

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

リファクタリング後のコード

以下のようにコードを変更して、リソースを作成してみました。

main.tf

resource "aws_s3_bucket" "example" {
  bucket  = "example-${data.aws_caller_identity.example.account_id}"

  # # S3バケットのデフォルト暗号化
  # server_side_encryption_configuration {
  #   rule {
  #     apply_server_side_encryption_by_default {
  #       sse_algorithm = "AES256"
  #     }
  #   }
  # }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
  bucket = aws_s3_bucket.example.bucket

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm = "AES256"
    }
  }
}

以下、terraform apply結果の抜粋ですが、Warningも表示されることなくリソースが作成されました。

takakuni % terraform apply

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_s3_bucket.example will be created
  + resource "aws_s3_bucket" "example" {
    }

  # aws_s3_bucket_server_side_encryption_configuration.example will be created
  + resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
    }

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.example: Creating...
aws_s3_bucket.example: Creation complete after 1s [id=example-XXXXXXXXXXXX]
aws_s3_bucket_server_side_encryption_configuration.example: Creating...
aws_s3_bucket_server_side_encryption_configuration.example: Creation complete after 1s [id=example-XXXXXXXXXXXX]

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

競合時のドリフト検出の挙動について

今回のアップデートで、両方のパターンで書ける状態になりました。

しかし、設定値が競合する場合、ドリフト検出がループします。

そのため、競合を避ける必要があります

どのような挙動になるか、以下のコードで検証してみました。(S3バケット部分以外は抜粋しています。)

暗号化方式の競合が起こるコードになります。

resource "aws_s3_bucket" "example" {
  bucket  = "example-${data.aws_caller_identity.example.account_id}"

  # S3バケットのデフォルト暗号化
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm     = "AES256"
      }
    }
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
  bucket = aws_s3_bucket.example.bucket

  rule {
    apply_server_side_encryption_by_default {
      kms_master_key_id = "aws/s3"
      sse_algorithm     = "aws:kms"
    }
  }
}

初回デプロイ時

初回デプロイ時は、「aws:kms」アルゴリズムでS3バケットは作成されます。

厳密には、「AES256からaws:kmsに上書きする挙動」となります。

% terraform apply

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_s3_bucket.example will be created
  + resource "aws_s3_bucket" "example" {

      + bucket                      = "example-XXXXXXXXXXXX"

      + server_side_encryption_configuration {
          + rule {
              + apply_server_side_encryption_by_default {
                  + sse_algorithm = "AES256"
                }
            }
        }
    }

  # aws_s3_bucket_server_side_encryption_configuration.example will be created
  + resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
      + bucket = "example-XXXXXXXXXXXX"

      + rule {
          + apply_server_side_encryption_by_default {
              + kms_master_key_id = "aws/s3"
              + sse_algorithm     = "aws:kms"
            }
        }
    }

Plan: 2 to add, 0 to change, 0 to destroy.
╷
│ Warning: Argument is deprecated
│ 
│   with aws_s3_bucket.example,
│   on main.tf line 13, in resource "aws_s3_bucket" "example":
│   13: resource "aws_s3_bucket" "example" {
│ 
│ Use the aws_s3_bucket_server_side_encryption_configuration resource instead
│ 
│ (and one more similar warning elsewhere)
╵

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.example: Creating...
aws_s3_bucket.example: Creation complete after 2s [id=example-XXXXXXXXXXXX]
aws_s3_bucket_server_side_encryption_configuration.example: Creating...
aws_s3_bucket_server_side_encryption_configuration.example: Creation complete after 1s [id=example-XXXXXXXXXXXX]
╷
│ Warning: Argument is deprecated
│ 
│   with aws_s3_bucket.example,
│   on main.tf line 13, in resource "aws_s3_bucket" "example":
│   13: resource "aws_s3_bucket" "example" {
│ 
│ Use the aws_s3_bucket_server_side_encryption_configuration resource instead
╵

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

デプロイ(2回目)

aws_s3_bucketのserver_side_encryption_configurationプロパティのドリフトが検出されます。

そのため、2回目のterraform apply時に、「AES256」に更新されます。

% terraform apply
aws_s3_bucket.example: Refreshing state... [id=example-XXXXXXXXXXXX]
aws_s3_bucket_server_side_encryption_configuration.example: Refreshing state... [id=example-XXXXXXXXXXXX]

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

  # aws_s3_bucket.example has changed
  ~ resource "aws_s3_bucket" "example" {
        id                          = "example-XXXXXXXXXXXX"
      + tags                        = {}
        # (10 unchanged attributes hidden)


      ~ server_side_encryption_configuration {
          ~ rule {
                # (1 unchanged attribute hidden)

              ~ apply_server_side_encryption_by_default {
                  + kms_master_key_id = "aws/s3"
                  ~ sse_algorithm     = "AES256" -> "aws:kms"
                }
            }
        }

        # (2 unchanged blocks hidden)
    }

  # aws_s3_bucket_server_side_encryption_configuration.example has changed
  ~ resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
        id     = "example-XXXXXXXXXXXX"
        # (1 unchanged attribute hidden)

      + rule {
          + bucket_key_enabled = false

          + apply_server_side_encryption_by_default {
              + kms_master_key_id = "aws/s3"
              + sse_algorithm     = "aws:kms"
            }
        }
      - rule {
          - apply_server_side_encryption_by_default {
              - kms_master_key_id = "aws/s3" -> null
              - sse_algorithm     = "aws:kms" -> null
            }
        }
    }


Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include actions to undo or respond to these changes.

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

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.example will be updated in-place
  ~ resource "aws_s3_bucket" "example" {
        id                          = "example-XXXXXXXXXXXX"
        tags                        = {}
        # (10 unchanged attributes hidden)


      ~ server_side_encryption_configuration {
          ~ rule {
                # (1 unchanged attribute hidden)

              ~ apply_server_side_encryption_by_default {
                  - kms_master_key_id = "aws/s3" -> null
                  ~ sse_algorithm     = "aws:kms" -> "AES256"
                }
            }
        }

        # (2 unchanged blocks hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.
╷
│ Warning: Argument is deprecated
│ 
│   with aws_s3_bucket.example,
│   on main.tf line 13, in resource "aws_s3_bucket" "example":
│   13: resource "aws_s3_bucket" "example" {
│ 
│ Use the aws_s3_bucket_server_side_encryption_configuration resource instead
│ 
│ (and one more similar warning elsewhere)
╵

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.example: Modifying... [id=example-XXXXXXXXXXXX]
aws_s3_bucket.example: Modifications complete after 1s [id=example-XXXXXXXXXXXX]
╷
│ Warning: Argument is deprecated
│ 
│   with aws_s3_bucket.example,
│   on main.tf line 13, in resource "aws_s3_bucket" "example":
│   13: resource "aws_s3_bucket" "example" {
│ 
│ Use the aws_s3_bucket_server_side_encryption_configuration resource instead
╵

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

デプロイ(3回目)

aws_s3_bucket_server_side_encryption_configurationのruleプロパティでドリフトが検出されます。

そのため、3回目のterraform apply時に、再度「"aws:kms"」アルゴリズムに更新されます。

以降は、デプロイ(2回目)とデプロイ(3回目)をループするため、競合を避ける必要があります

% terraform apply
aws_s3_bucket.example: Refreshing state... [id=example-XXXXXXXXXXXX]
aws_s3_bucket_server_side_encryption_configuration.example: Refreshing state... [id=example-XXXXXXXXXXXX]

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

  # aws_s3_bucket_server_side_encryption_configuration.example has changed
  ~ resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
        id     = "example-XXXXXXXXXXXX"
        # (1 unchanged attribute hidden)

      + rule {
          + bucket_key_enabled = false

          + apply_server_side_encryption_by_default {
              + sse_algorithm = "AES256"
            }
        }
      - rule {
          - bucket_key_enabled = false -> null

          - apply_server_side_encryption_by_default {
              - kms_master_key_id = "aws/s3" -> null
              - sse_algorithm     = "aws:kms" -> null
            }
        }
    }


Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include actions to undo or respond to these changes.

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

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_server_side_encryption_configuration.example will be updated in-place
  ~ resource "aws_s3_bucket_server_side_encryption_configuration" "example" {
        id     = "example-XXXXXXXXXXXX"
        # (1 unchanged attribute hidden)

      - rule {
          - bucket_key_enabled = false -> null

          - apply_server_side_encryption_by_default {
              - sse_algorithm = "AES256" -> null
            }
        }
      + rule {
          + apply_server_side_encryption_by_default {
              + kms_master_key_id = "aws/s3"
              + sse_algorithm     = "aws:kms"
            }
        }
    }

Plan: 0 to add, 1 to change, 0 to destroy.
╷
│ Warning: Argument is deprecated
│ 
│   with aws_s3_bucket.example,
│   on main.tf line 13, in resource "aws_s3_bucket" "example":
│   13: resource "aws_s3_bucket" "example" {
│ 
│ Use the aws_s3_bucket_server_side_encryption_configuration resource instead
│ 
│ (and one more similar warning elsewhere)
╵

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_server_side_encryption_configuration.example: Modifying... [id=example-XXXXXXXXXXXX]
aws_s3_bucket_server_side_encryption_configuration.example: Modifications complete after 0s [id=example-XXXXXXXXXXXX]

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

まとめ

今回は、v4.9.0のS3リファクタリング挙動を試してみました。

オプション化で、以前の書き方ができるようになりましたが、引き続きリファクタリングが必要そうです。

この記事が、どなたかの参考になれば幸いです。以上、コンサルティング部のたかくにでした!