
Terraform AWS Provider version 6がリリースされ、複数リージョンへの展開がかなり簡単になりました
2025年6月19日にTerraformのAWS Providerの version 6がリリースされました。6月27日には 6.1.0、7月3日に6.2.0もリリースされています。
本エントリでは version6の変更点についてご紹介します。
複数リージョンへの展開がかなり簡単になりました
これまで
AWS provider version 5までは、複数のリージョンにリソースをプロビジョニングしたい場合は、各リージョンごとに provider
ブロックを定義する必要がありました。
# デフォルトプロバイダー(us-east-1)
provider "aws" {
region = "us-east-1"
}
# us-west-2用のエイリアス付きプロバイダー
provider "aws" {
alias = "west"
region = "us-west-2"
}
そして リソース・データソースブロック内のprovider
引数にてエイリアスを指定することで、リソースであればそのリージョンにリソースをプロビジョニング、データソースであればそのリージョンに存在するリソースを参照することができました。
# us-east-1にS3バケットを作成
# ※ エイリアス未指定の場合、デフォルトプロバイダーが使用される
resource "aws_s3_bucket" "main" {
bucket = "my-main-bucket"
}
# us-west-2にS3バケットを作成(aliasを使用)
resource "aws_s3_bucket" "west" {
provider = aws.west
bucket = "my-west-bucket"
}
# us-east-1のデフォルトVPCを参照
# ※ エイリアス未指定の場合、デフォルトプロバイダーが使用される
data "aws_vpc" "main" {
default = true
}
# us-west-2のデフォルトVPCを参照(aliasを使用)
data "aws_vpc" "west" {
provider = aws.west
default = true
}
上記のように使うリージョンが2つくらいだったらさほど問題ではないです。ですがもっとたくさんのリージョンを使いたい場合、コードが冗長になり可読性が下がるという問題がありました。
具体的な例としては、GuardDutyのような全リージョンで有効化することが推奨されているサービスのIaCをTerraformでやりたい場合が考えられます。以下サンプルコードです。
provider "aws" {
region = "us-east-1"
}
provider "aws" {
alias = "us-east-2"
region = "us-east-2"
}
provider "aws" {
alias = "us-west-1"
region = "us-west-1"
}
provider "aws" {
alias = "us-west-2"
region = "us-west-2"
}
provider "aws" {
alias = "ap-northeast-1"
region = "ap-northeast-1"
}
resource "aws_guardduty_detector" "us_east_1" {
enable = true
datasources {
s3_logs {
enable = true
}
kubernetes {
audit_logs {
enable = false
}
}
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes {
enable = true
}
}
}
}
}
resource "aws_guardduty_detector" "us_east_2" {
provider = aws.us-east-2
enable = true
datasources {
s3_logs {
enable = true
}
kubernetes {
audit_logs {
enable = false
}
}
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes {
enable = true
}
}
}
}
}
resource "aws_guardduty_detector" "us_west_1" {
provider = aws.us-west-1
enable = true
datasources {
s3_logs {
enable = true
}
kubernetes {
audit_logs {
enable = false
}
}
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes {
enable = true
}
}
}
}
}
resource "aws_guardduty_detector" "us_west_2" {
provider = aws.us-west-2
enable = true
datasources {
s3_logs {
enable = true
}
kubernetes {
audit_logs {
enable = false
}
}
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes {
enable = true
}
}
}
}
}
resource "aws_guardduty_detector" "ap_northeast_1" {
provider = aws.ap-northeast-1
enable = true
datasources {
s3_logs {
enable = true
}
kubernetes {
audit_logs {
enable = false
}
}
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes {
enable = true
}
}
}
}
}
長いですね。しかもほぼ同じコードが続くという…
「for_eachを使えばもっとコンパクトにできるのでは?」とお考えの方、鋭いです。が、providerブロックではfor_eachを使う事ができません。
locals {
target_regions = [
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
"ap-northeast-1",
]
}
provider "aws" {
for_each = toset(local.target_regions)
alias = (each.value == "us-east-1") ? null : each.value
region = each.value
}
# エラーになります
% terraform apply
╷
│ Error: Invalid provider configuration alias
│
│ An alias must be a valid name. A name must start with a letter or underscore and may contain only letters, digits, underscores, and
│ dashes.
╵
╷
│ Error: Reserved argument name in provider block
│
│ on main.tf line 24, in provider "aws":
│ 24: for_each = toset(local.target_regions)
│
│ The provider argument name "for_each" is reserved for use by Terraform in a future version.
╵
╷
│ Error: Variables not allowed
│
│ on main.tf line 26, in provider "aws":
│ 26: alias = (each.value == "us-east-1") ? null : each.value
│
│ Variables may not be used here.
╵
╷
│ Error: Unsuitable value type
│
│ on main.tf line 26, in provider "aws":
│ 26: alias = (each.value == "us-east-1") ? null : each.value
│
│ Unsuitable value: value must be known
また、 aws_guardduty_detector
リソースブロックでも、for_eachが使えません。 provider
メタ引数に対して for_eachが使えないのです。
resource "aws_guardduty_detector" "main" {
for_each = local.target_regions
provider = (each.value == "us-east-1") ? null : each.value
enable = true
datasources {
s3_logs {
enable = true
}
kubernetes {
audit_logs {
enable = false
}
}
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes {
enable = true
}
}
}
}
}
# エラーになります
% terraform apply
╷
│ Error: Invalid provider configuration reference
│
│ on main.tf line 58, in resource "aws_guardduty_detector" "main":
│ 58: provider = (each.value == "us-east-1") ? null : "aws.${each.value}"
│
│ The provider argument requires a provider type name, optionally followed by a period and then a configuration alias.
これから
version 6より、各リソース・データソースブロック内でリージョン指定ができるようになりました。
まず、providerブロックは一つだけで良くなりました。デフォルトリージョンを指定します。
provider "aws" {
region = "us-east-1"
}
デフォルトリージョン以外でリソースもしくはデータソースを使いたい場合は、
各リソースブロック・データソースブロック に region
引数が追加されているのでこれを使います。
resource "aws_guardduty_detector" "us_east_2" {
region = "us-east-2"
enable = true
datasources {
s3_logs {
enable = true
}
kubernetes {
audit_logs {
enable = false
}
}
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes {
enable = true
}
}
}
}
}
以下 plan結果です。 region
はド頭に記載して欲しいですが、仕方ないですかね…
% 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_guardduty_detector.us_east_2 will be created
+ resource "aws_guardduty_detector" "us_east_2" {
+ account_id = (known after apply)
+ arn = (known after apply)
+ enable = true
+ finding_publishing_frequency = (known after apply)
+ id = (known after apply)
+ region = "us-east-2"
+ tags_all = (known after apply)
+ datasources {
+ kubernetes {
+ audit_logs {
+ enable = false
}
}
+ malware_protection {
+ scan_ec2_instance_with_findings {
+ ebs_volumes {
+ enable = true
}
}
}
+ s3_logs {
+ enable = true
}
}
}
Plan: 1 to add, 0 to change, 0 to destroy.
そして、(これまでの provider
meta argumentとは違って、) for_eachが使えます!
locals {
target_regions = [
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
"ap-northeast-1",
]
}
resource "aws_guardduty_detector" "main" {
for_each = toset(local.target_regions)
region = each.value
enable = true
datasources {
s3_logs {
enable = true
}
kubernetes {
audit_logs {
enable = false
}
}
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes {
enable = true
}
}
}
}
}
デフォルトリージョンに対しては region
引数要らないので以下でも良いですが、plan結果でdiffは出なかったのでどちらでも良いみたいです。
resource "aws_guardduty_detector" "main" {
for_each = toset(local.target_regions)
- region = each.value
+ region = (each.value == "us-east-1") ? null : each.value
enable = true
(以下略)
また、providerブロックの定義をひとつだけにできたことで、Terraformのメモリ使用料量も減らすことができたそうです。
移行方法
version 6でも 5までの書き方がそのまま使えます。具体的には
- alias引数を使って複数のprovider ブロックを書く
- 各リソースブロック・データソースブロックで provider引数を指定する
がそのまま使えるので、単に AWS providerのversion指定をあげて terraform init -upgrade
を実行すればよいです。
terraform {
required_version = "= 1.12.2"
required_providers {
aws = {
source = "hashicorp/aws"
- version = "~> 5.100"
+ version = "~> 6.2"
}
}
}
その後、 movedブロックを使ってリファクタリングしていきましょう。
※ ただ、公式ブログには、リファクタリングの前に terraform plan -refresh-only
と terraform apply -refresh-only
が必要と書かれています。ですので慎重にやりたい場合はこれらのコマンドも実行するほうが無難でしょう。今回の私のサンプルコードの場合は特に必要ありませんでした。
+ locals {
+ target_regions = [
+ "us-east-1",
+ "us-east-2",
+ "us-west-1",
+ "us-west-2",
+ "ap-northeast-1",
+ ]
+ }
provider "aws" {
region = "us-east-1"
}
- provider "aws" {
- alias = "us-east-2"
- region = "us-east-2"
- }
-
- provider "aws" {
- alias = "us-west-1"
- region = "us-west-1"
- }
-
- provider "aws" {
- alias = "us-west-2"
- region = "us-west-2"
- }
-
- provider "aws" {
- alias = "ap-northeast-1"
- region = "ap-northeast-1"
- }
+ resource "aws_guardduty_detector" "main" {
+ for_each = toset(local.target_regions)
+
+ region = each.value
+
+ enable = true
+
+ datasources {
+ s3_logs {
+ enable = true
+ }
+ kubernetes {
+ audit_logs {
+ enable = false
+ }
+ }
+ malware_protection {
+ scan_ec2_instance_with_findings {
+ ebs_volumes {
+ enable = true
+ }
+ }
+ }
+ }
+ }
+
+ moved {
+ from = aws_guardduty_detector.us_east_1
+ to = aws_guardduty_detector.main["us-east-1"]
+ }
+
+ moved {
+ from = aws_guardduty_detector.us_east_2
+ to = aws_guardduty_detector.main["us-east-2"]
+ }
+
+ moved {
+ from = aws_guardduty_detector.us_west_1
+ to = aws_guardduty_detector.main["us-west-1"]
+ }
+
+ moved {
+ from = aws_guardduty_detector.us_west_2
+ to = aws_guardduty_detector.main["us-west-2"]
+ }
+
+ moved {
+ from = aws_guardduty_detector.ap_northeast_1
+ to = aws_guardduty_detector.main["ap-northeast-1"]
+ }
- resource "aws_guardduty_detector" "us_east_1" {
- enable = true
-
- datasources {
- s3_logs {
- enable = true
- }
- kubernetes {
- audit_logs {
- enable = false
- }
- }
- malware_protection {
- scan_ec2_instance_with_findings {
- ebs_volumes {
- enable = true
- }
- }
- }
- }
- }
-
- resource "aws_guardduty_detector" "us_east_2" {
- provider = aws.us-east-2
-
- enable = true
-
- datasources {
- s3_logs {
- enable = true
- }
- kubernetes {
- audit_logs {
- enable = false
- }
- }
- malware_protection {
- scan_ec2_instance_with_findings {
- ebs_volumes {
- enable = true
- }
- }
- }
- }
- }
-
- resource "aws_guardduty_detector" "us_west_1" {
- provider = aws.us-west-1
-
- enable = true
-
- datasources {
- s3_logs {
- enable = true
- }
- kubernetes {
- audit_logs {
- enable = false
- }
- }
- malware_protection {
- scan_ec2_instance_with_findings {
- ebs_volumes {
- enable = true
- }
- }
- }
- }
- }
-
- resource "aws_guardduty_detector" "us_west_2" {
- provider = aws.us-west-2
-
- enable = true
-
- datasources {
- s3_logs {
- enable = true
- }
- kubernetes {
- audit_logs {
- enable = false
- }
- }
- malware_protection {
- scan_ec2_instance_with_findings {
- ebs_volumes {
- enable = true
- }
- }
- }
- }
- }
-
- resource "aws_guardduty_detector" "ap_northeast_1" {
- provider = aws.ap-northeast-1
-
- enable = true
-
- datasources {
- s3_logs {
- enable = true
- }
- kubernetes {
- audit_logs {
- enable = false
- }
- }
- malware_protection {
- scan_ec2_instance_with_findings {
- ebs_volumes {
- enable = true
- }
- }
- }
- }
- }
% terraform apply
aws_guardduty_detector.main["ap-northeast-1"]: Refreshing state... [id=cccbf250f5435e518605fc52ce8f1898]
aws_guardduty_detector.main["us-east-2"]: Refreshing state... [id=d8cbf250f860b978e47fba274e59bbc3]
aws_guardduty_detector.main["us-west-2"]: Refreshing state... [id=d6cbf250f77a92ede9ed4af886326f03]
aws_guardduty_detector.main["us-east-1"]: Refreshing state... [id=a6cbf250f810a6cb51932b1f760bf6d4]
aws_guardduty_detector.main["us-west-1"]: Refreshing state... [id=a6cbf250f6b51407ce25053fcfa50ff1]
Terraform will perform the following actions:
# aws_guardduty_detector.ap_northeast_1 has moved to aws_guardduty_detector.main["ap-northeast-1"]
resource "aws_guardduty_detector" "main" {
id = "cccbf250f5435e518605fc52ce8f1898"
tags = {}
# (6 unchanged attributes hidden)
# (1 unchanged block hidden)
}
# aws_guardduty_detector.us_east_1 has moved to aws_guardduty_detector.main["us-east-1"]
resource "aws_guardduty_detector" "main" {
id = "a6cbf250f810a6cb51932b1f760bf6d4"
tags = {}
# (6 unchanged attributes hidden)
# (1 unchanged block hidden)
}
# aws_guardduty_detector.us_east_2 has moved to aws_guardduty_detector.main["us-east-2"]
resource "aws_guardduty_detector" "main" {
id = "d8cbf250f860b978e47fba274e59bbc3"
tags = {}
# (6 unchanged attributes hidden)
# (1 unchanged block hidden)
}
# aws_guardduty_detector.us_west_1 has moved to aws_guardduty_detector.main["us-west-1"]
resource "aws_guardduty_detector" "main" {
id = "a6cbf250f6b51407ce25053fcfa50ff1"
tags = {}
# (6 unchanged attributes hidden)
# (1 unchanged block hidden)
}
# aws_guardduty_detector.us_west_2 has moved to aws_guardduty_detector.main["us-west-2"]
resource "aws_guardduty_detector" "main" {
id = "d6cbf250f77a92ede9ed4af886326f03"
tags = {}
# (6 unchanged attributes hidden)
# (1 unchanged block hidden)
}
Plan: 0 to add, 0 to change, 0 to destroy.
movedブロックでは(Terraform version 1.12の現時点では) for_eachを使えないので、地道に1リージョン分ずつ書いていくしか無いですね…
破壊的変更がたくさん
- OpsWorks, SimpleDBなどライフサイクル終了(EOL)を迎えたサービスに関連する多くの属性やリソースが削除されました。
- 前項で紹介した通り、全リソース・データソースで
region
引数が利用可能になったことから波及した変更が複数のリソース・データソースで発生しています。例えばData Source: aws_region
はname
引数が Deprecatedになり、代わりにregion
を使うことが推奨になりました。
などなど。詳しくは以下のUpgrade Guideをご確認ください!
S3バケットの破壊的変更はどうなったのか
結論から言うと変更なしのようです。これまでの経緯をご紹介します。
- Version 4のGA時に、S3バケット(aws_s3_bucketリソース)にbreaking change(破壊的変更)がありました。つまりVersion 3と同じコードだとエラーになるようになりました。
- その後Version 4.9.0にて、breaking changeは撤回つまりversion 3と同じコードでもエラーにならなくなりました。ただしDeprecatedとしてWarningが表示されます。
- Version 5にて再び旧来の(Version 3までの)コードだとエラーになる予定でした。
- が、Version5リリース時には上記対応は見送られました。つまり旧来のコードではエラーにならず、deprecatedのwarningが表示されるだけです。
- 改めてVersion6で対応される予定となっていました。
で、今回 Version 6がリリースされたわけですが、このS3バケットの破壊的変更については変更なし、つまり 旧来の(Version 3までの)コードでもエラーにならずdeprecatedのwarningが表示されるだけのままです。
Upgrade Guideのaws_s3_bucket
項に特に記載がありませんし、V6のaws_s3_bucket
リソースのドキュメントにも Deprecatedの引数が多数記載されています。以下はドキュメントのキャプチャです。
参考情報
- Terraform AWS provider 6.0 now generally available
- Release v6.0.0 · hashicorp/terraform-provider-aws
- Release v6.1.0 · hashicorp/terraform-provider-aws
- Release v6.2.0 · hashicorp/terraform-provider-aws
- Terraform AWS Provider Version 6 Upgrade Guide | Guides | hashicorp/aws | Terraform | Terraform Registry