NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

注目のタグ

    【Terraform】AWSリージョンをリソース単位で設定する

    本記事は  AWSアワード受賞者祭り  10日目の記事です。
    ✨🏆  9日目  ▶▶ 本記事 ▶▶  11日目  🏆✨

    はじめに

    こんにちは。加藤です。

    この度は、2025 Japan All AWS Certifications Engineers に選出いただきました。

    2021年にAWS Certified Solutions Architect - Associateを取得してから約4年かけて全ての資格を取得することができました。地道にコツコツ頑張ってきた甲斐がありました。自分で自分を褒めてあげたいです。

    さて、本ブログ記事ではTerraformでAWSリージョンをリソース単位で設定する方法について解説します。AWSアワード受賞者祭りっぽくない、というツッコミはひとまずおいておきます。

    2025年6月にリリースされたTerraform AWS Provider v6で実装されたregion属性により、従来とは異なる方法でAWSリージョンを設定することが可能になりました。マルチリージョン構成をTerraformで管理運用している場合に有効な方法かと思いますので、心当たりがある方はぜひ参考にしていただければと思います。

    region属性について

    region属性は、AWSリージョンをリソース単位で設定できる属性です。Terraform AWS Provider v6で実装されました。

    公式からはregion属性についての解説記事が作られており、重要なエンハンス対応であることが分かります。

    registry.terraform.io

    従来のAWSリージョン設定方法

    ここからは公式記事を基にregion属性の詳細について見ていきます。

    v6以前のAWS Providerでは、 プロバイダー設定(もしくはAWS Profileや環境変数など)でAWSリージョンを指定していました。

    provider "aws" {
      region = "us-east-1"
    }

    別リージョンのリソースを定義する場合、別リージョン用のプロバイダー設定を追加し、対象リソースに指定する必要がありました。

    provider "aws" {
      region = "us-east-1"
    }
    
    provider "aws" { ← 別リージョン用のプロバイダー設定
      alias  = "peer"
      region = "us-west-2"
    }
    
    resource "aws_vpc" "main" {
      cidr_block = "10.0.0.0/16"
    }
    
    resource "aws_vpc" "peer" {
      provider   = aws.peer ← 別リージョン用のプロバイダー設定を指定
      cidr_block = "10.1.0.0/16"
    }

    リージョンが増えるたびにプロバイダー設定や同様のコードを繰り返し記述する必要があり、何かと不便でした。

    v6で実装されたregion属性により、単一のプロバイダー設定でリソース単位のリージョン指定が可能になりました。

    provider "aws" {
      region = "us-east-1"
    }
    
    resource "aws_vpc" "main" {
      cidr_block = "10.0.0.0/16"
    }
    
    resource "aws_vpc" "peer" {
      region     = "us-west-2" ← リソース単位でリージョンを指定
      cidr_block = "10.1.0.0/16"
    }

    プロバイダー設定を追加しなくて良いので、柔軟でDRYなコード管理が実現できそうですね。

    region属性のポイント

    region属性に関する主なポイントをまとめます。

    • 従来のプロバイダー設定によるリージョン分割方法は非推奨ではありません。v6アップグレード時もregion属性への変更対応は任意となります。
      • region属性は必須属性ではなく任意属性です。デフォルトではプロバイダー設定のリージョンが適用されます。
      • AWS Profileや環境変数によるリージョン指定も引き続き有効です。
      • 公式よりv6へのアップグレードガイドが公開されています。リリースノートを見る限りBREAKING CHANGESも含まれているので、確認/検証の上アップグレードを計画するべきでしょう。
    • 全てのAWSリソースにregion属性が付与されているわけではありません。
      • 付与されないリソースは、主にグローバルサービス(AWS IAM、Amazon CloudFront、Amazon Route53など)です。

    実際に試してみた

    それでは、region属性を試してみようと思います。

    なるべく実運用に即したコード体系としたかったので、以下の構成に基づきコードを作成してみました。region属性を試すのであれば、Amazon S3バケットだけでも良かったのですが、応用例としてレプリケーションの設定も入れています。

    • シンプルな構成とするためAmazon S3のみを実装
    • 環境はdevprodの2つ
    • コードを再利用するため各環境で同じモジュールを利用

    ディレクトリ構成

    .
    ├── env
    │   ├── dev
    │   │   ├── main.tf
    │   │   ├── providers.tf
    │   │   └── versions.tf
    │   └── prod
    │       ├── main.tf
    │       ├── providers.tf
    │       └── versions.tf
    └── modules
        └── s3_bucket
            ├── main.tf
            └── variables.tf

    modulesディレクトリ

    実リソースを管理するためのTerraformコードを配置します。

    s3_bucket/main.tfではAmazon S3関連リソース(バケット、バージョニング設定、レプリケーション設定)とAWS IAM関連リソース(レプリケーション用IAMロール、IAMポリシー、アタッチメント)を定義します。

    Amazon S3関連リソースにはregion属性を付与して、リソース単位でのリージョン指定を実現しています。具体的には変数(regions)の値が設定されるようにしています。また、regionsに入力したリージョン分Amazon S3関連リソースが作成されるように実装しています。

    AWS IAMはグローバルサービスであるため、region属性は付与できません。

    s3_bucket/main.tf

    # 複数リージョンに対応したS3バケット作成
    resource "aws_s3_bucket" "buckets" {
      for_each = toset(var.regions)
    
      region = each.value ← ★リージョンをリソース単位で指定★
    
      bucket = "${var.bucket_name}-${each.key}"
      tags = merge(var.tags, {
        Region = each.key
      })
    }
    
    # バケットバージョニング設定
    resource "aws_s3_bucket_versioning" "versioning" {
      for_each = aws_s3_bucket.buckets
    
      region = each.value.region ← ★リージョンをリソース単位で指定★
    
      bucket = each.value.id
      versioning_configuration {
        status = "Enabled"
      }
    }
    
    # レプリケーション設定(複数リージョンの場合のみ)
    resource "aws_s3_bucket_replication_configuration" "replication" {
      for_each = length(var.regions) > 1 ? aws_s3_bucket.buckets : {}
    
      region = each.value.region ← ★リージョンをリソース単位で指定★
    
      bucket = each.value.id
      role   = aws_iam_role.replication[each.key].arn
    
      dynamic "rule" {
        for_each = [for region in var.regions : region if region != each.value.region]
        content {
          id     = "replication-to-${rule.value}"
          status = "Enabled"
    
          destination {
            bucket        = aws_s3_bucket.buckets[rule.value].arn
            storage_class = "STANDARD"
          }
        }
      }
    
      depends_on = [aws_s3_bucket_versioning.versioning]
    }
    
    # レプリケーション用IAMロール
    resource "aws_iam_role" "replication" {
      for_each = length(var.regions) > 1 ? aws_s3_bucket.buckets : {}
    
      name = "${var.bucket_name}-${each.key}-replication-role"
      assume_role_policy = jsonencode({
        Version = "2012-10-17"
        Statement = [
          {
            Action = "sts:AssumeRole"
            Effect = "Allow"
            Principal = {
              Service = "s3.amazonaws.com"
            }
          }
        ]
      })
    }
    
    # レプリケーション用IAMポリシー
    resource "aws_iam_policy" "replication" {
      for_each = length(var.regions) > 1 ? aws_s3_bucket.buckets : {}
    
      name = "${var.bucket_name}-${each.key}-replication-policy"
      policy = jsonencode({
        Version = "2012-10-17"
        Statement = [
          {
            Effect = "Allow"
            Action = [
              "s3:GetReplicationConfiguration",
              "s3:ListBucket"
            ]
            Resource = each.value.arn
          },
          {
            Effect = "Allow"
            Action = [
              "s3:GetObjectVersionForReplication",
              "s3:GetObjectVersionAcl",
              "s3:GetObjectVersionTagging"
            ]
            Resource = "${each.value.arn}/*"
          },
          {
            Effect = "Allow"
            Action = [
              "s3:ReplicateObject",
              "s3:ReplicateDelete",
              "s3:ReplicateTags"
            ]
            Resource = [
              for bucket in aws_s3_bucket.buckets : "${bucket.arn}/*"
              if bucket.region != each.value.region
            ]
          }
        ]
      })
    }
    
    # IAMロールとポリシーの関連付け
    resource "aws_iam_role_policy_attachment" "replication" {
      for_each = length(var.regions) > 1 ? aws_iam_role.replication : {}
    
      role       = each.value.name
      policy_arn = aws_iam_policy.replication[each.key].arn
    }

    s3_bucket/variables.tf

    variable "bucket_name" {
      description = "The base name for the S3 buckets."
      type        = string
    }
    
    variable "regions" {
      description = "List of AWS regions where buckets should be created."
      type        = list(string)
      validation {
        condition     = length(var.regions) > 0
        error_message = "At least one region must be specified."
      }
    }
    
    variable "tags" {
      description = "A map of tags to assign to the buckets."
      type        = map(string)
      default     = {}
    }

    envディレクトリ

    各環境用のTerraformコードを配置します。

    main.tfではmoduleブロックでs3_bucketモジュールを呼び出します。regions変数にはdevでは単一リージョンを、prodでは二つのリージョンを入力しています。bucket_nametagsには環境識別子等を入力します。

    dev/main.tf

    module "s3_bucket" {
      source = "../../modules/s3_bucket"
    
      regions = ["ap-northeast-1"] ← 単一リージョン
    
      bucket_name = "t3-kato-dev"
      tags = {
        Environment = "dev"
      }
    } 

    live/main.tf

    module "s3_bucket" {
      source = "../../modules/s3_bucket"
    
      regions = ["ap-northeast-1", "us-west-2"] ← 二つのリージョン
    
      bucket_name = "t3-kato-prod"
      tags = {
        Environment = "prod"
      }
    } 

    provider.tfではproviderブロックを定義します。

    今回はリソース側のregion属性が効いているか確認するため、プロバイダー設定ではリージョンを指定しないようにしました。

    provider.tf

    provider "aws" { ← リージョンを指定しない
      allowed_account_ids = [
        "XXXXXXXXXXXX"
      ]
    }

    versions.tfではterraformブロックを定義します。provider.tfversions.tfdevliveで同じものとしました。

    versions.tf

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

    コードの説明は以上です。

    リソース単位でリージョンを指定することにより、プロバイダー定義を書く手間を減らしつつ、コードの再利用性が上がっていることが分かります。

    さいごに

    今回は、Terraformのregion属性について紹介しました。

    なかなかニッチな内容かもしれないですが、個人的には痒い所に手が届く良いアップデートだと感じています。

    また、今回はAWS Terraform MCP Serverを利用してベースのコードを作成しました。私は、AIツールをあまり使いこなせてないのですが、設定も簡単で非常に使いやすい印象を受けました。region属性のような比較的新しい情報についてもキャッチアップしてくれるのはありがたいですね。

    なお、TerraformのMCP ServerはHashiCorpからも提供されています。

    github.com

    こちらもいつか触ってみたいです。

    本ブログ記事がどなたかの参考になれば幸いです。

    執筆者: 加藤 俊稀

    🧑‍💻クラウドエンジニア / インフラエンジニア
    🎖️ 2025 Japan All AWS Certifications Engineers
    📝https://tech.nri-net.com/archive/author/t3-kato