「provider aliasは名前だけ知っているが、assume_roleと組み合わせてクロスアカウント操作をどう書けばいいのかわからない」
複数のAWSアカウントを扱う場合、Terraformのprovider aliasとassume_roleを組み合わせると、1つのコードベースで開発・ステージング・本番の各アカウントにまたがるリソースを同時管理できます。アカウントをまたぐ認証切り替えはTerraformが自動的に行ってくれます。
この記事では、provider aliasの基本設定からassume_roleによるクロスアカウント認証の仕組み、IAMクロスアカウントロールの準備手順、実際のリソース実装例、さらに「provider aliasで統合管理」と「ディレクトリ分割で分離管理」のどちらを選ぶべきかの判断基準まで解説します。
実行環境:Terraform 1.8.x(RHEL 9.4 / Ubuntu 24.04 LTSで動作確認済み)
この記事のポイント
・provider aliasで複数AWSアカウントへのproviderを1つのコードで定義できる
・assume_roleブロックにrole_arnを指定するだけでクロスアカウント認証が切り替わる
・リソース定義でprovider = aws.devのように明示すると作成先アカウントを固定できる
・本番/開発で構成差が大きい場合はディレクトリ分割のほうが安全
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜAWSをマルチアカウントで分離するのか
AWSのベストプラクティスでは、用途ごとにAWSアカウントを分離することが強く推奨されています。1つのアカウントに開発・ステージング・本番を同居させると、次のリスクが生まれます。・IAMロールのミスで開発者が本番のリソースを誤操作するリスク
・本番のコスト増加に開発のトラフィックが影響し、原因特定が難しくなる
・セキュリティインシデントが発生したとき、被害が全環境に広がる
マルチアカウント構成では、アカウントそのものがセキュリティ境界になります。開発アカウントで何が起きても、IAMポリシーの設定ミスが起きても、本番アカウントとは別のアカウントのため影響が遮断されます。
AWS Organizationsを使う組織では、管理アカウント(マスターアカウント)の下に開発・ステージング・本番の各メンバーアカウントを配置する構成が標準です。Terraformでこの構成を管理する場合、大きく2つのアプローチがあります。
・provider aliasで統合管理:1つのTerraformコードで複数アカウントのリソースを同時定義する
・ディレクトリ分割で分離管理:アカウントごとにTerraformのルートモジュールを分けてそれぞれ管理する
この2つの判断基準については後述します。まずprovider aliasの仕組みを理解しましょう。
provider aliasの仕組みと基本的な書き方
Terraformでは通常、1つのprovider(例:aws)を1つだけ定義します。しかしprovider aliasを使うと、同じproviderに複数の設定を持たせることができます。同一アカウントの異なるリージョンにリソースを配置する場合も、provider aliasを使います。マルチアカウントではこれに加えてassume_roleを組み合わせます。
1. 基本的なprovider alias設定(同一アカウント・リージョン違い)
まず理解しやすいリージョン違いの例で仕組みを確認します。# providers.tf - 同一アカウントの東京/大阪リージョンにprovider aliasを定義 terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } # デフォルトprovider(東京リージョン) provider "aws" { region = "ap-northeast-1" } # aliasを持つprovider(大阪リージョン) provider "aws" { alias = "osaka" region = "ap-northeast-3" }
provider = aws.osakaのように参照します。# main.tf - リソースにprovider aliasを指定 resource "aws_s3_bucket" "dr_backup" { provider = aws.osaka # 大阪リージョンのproviderを明示 bucket = "my-app-dr-backup-osaka" tags = { Name = "DR用バックアップバケット" } }
2. assume_roleを使ったクロスアカウントprovider alias
別AWSアカウントへのアクセスには、IAMのSTS(Security Token Service)経由でロールを引き受けるassume_roleを使います。assume_roleブロックに切り替え先アカウントのIAMロールARNを指定するだけで、TerraformはAPI呼び出し時に自動でSTS:AssumeRoleを実行して認証を切り替えます。# providers.tf - マルチアカウント設定 # 管理アカウント(実行元): 111122223333 # 開発アカウント: 123456789012 # 本番アカウント: 987654321098 # 実行元(管理アカウント)のデフォルトprovider provider "aws" { region = "ap-northeast-1" } # 開発アカウントへのprovider alias provider "aws" { alias = "dev" region = "ap-northeast-1" assume_role { role_arn = "arn:aws:iam::123456789012:role/TerraformCrossAccountRole" } } # 本番アカウントへのprovider alias provider "aws" { alias = "prod" region = "ap-northeast-1" assume_role { role_arn = "arn:aws:iam::987654321098:role/TerraformCrossAccountRole" } }
IAMクロスアカウントロールの準備(Terraform実行前の設定)
provider aliasのassume_roleが機能するためには、切り替え先アカウントにクロスアカウントロールを作成し、実行元アカウントからのassumeを許可する必要があります。3. 切り替え先アカウントにIAMロールを作成する
開発・本番の各アカウントに「TerraformCrossAccountRole」を作成します。信頼ポリシー(Trust Policy)で、実行元アカウント(管理アカウント)からのSTS:AssumeRoleを許可します。# AWSマネジメントコンソールまたはCLIで開発アカウント側に作成する信頼ポリシー # (管理アカウントのアカウントID: 111122223333 を許可) { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::111122223333:root" }, "Action": "sts:AssumeRole" } ] }
Principalに管理アカウントのroot ARNを指定すると、管理アカウントのすべてのIAMエンティティがこのロールを引き受けられる候補になります。実際にAssumeRoleできるのは、次のステップで「sts:AssumeRole権限」を付与したエンティティだけです。このロールにはTerraformが操作するリソースに応じた権限ポリシーもアタッチします。開発環境でEC2・VPC・S3を管理する場合は、対象サービスへのフルアクセスか、PowerUserAccessポリシーを付与するのが一般的です。
4. 実行元アカウント側にsts:AssumeRoleを許可する
管理アカウント側では、Terraformを実行するIAMユーザー・ロールに、切り替え先アカウントのロールをAssumeRoleできる権限を付与します。# 管理アカウントのIAMポリシー(TerraformExecutionRole または実行ユーザーにアタッチ) { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": [ "arn:aws:iam::123456789012:role/TerraformCrossAccountRole", "arn:aws:iam::987654321098:role/TerraformCrossAccountRole" ] } ] }
実践|複数アカウントにVPCとサブネットを構築する
IAMの準備が完了したら、実際にリソースを定義します。5. provider設定とリソース定義
# main.tf - 開発・本番アカウントにVPCを作成する例 # 開発アカウントにVPCを作成(provider = aws.dev を明示) resource "aws_vpc" "dev" { provider = aws.dev cidr_block = "10.1.0.0/16" tags = { Name = "dev-vpc" Environment = "dev" } } resource "aws_subnet" "dev_public" { provider = aws.dev vpc_id = aws_vpc.dev.id cidr_block = "10.1.1.0/24" availability_zone = "ap-northeast-1a" tags = { Name = "dev-public-subnet" } } # 本番アカウントにVPCを作成(provider = aws.prod を明示) resource "aws_vpc" "prod" { provider = aws.prod cidr_block = "10.0.0.0/16" tags = { Name = "prod-vpc" Environment = "prod" } } resource "aws_subnet" "prod_public" { provider = aws.prod vpc_id = aws_vpc.prod.id cidr_block = "10.0.1.0/24" availability_zone = "ap-northeast-1a" tags = { Name = "prod-public-subnet" } }
6. terraform planで差分を確認する
# terraform init 後に terraform plan を実行 $ terraform plan Terraform will perform the following actions: # aws_subnet.dev_public will be created + resource "aws_subnet" "dev_public" { + arn = (known after apply) + availability_zone = "ap-northeast-1a" + cidr_block = "10.1.1.0/24" + id = (known after apply) + tags = { + "Name" = "dev-public-subnet" } + vpc_id = (known after apply) } # aws_vpc.dev will be created + resource "aws_vpc" "dev" { + arn = (known after apply) + cidr_block = "10.1.0.0/16" + id = (known after apply) + tags = { + "Environment" = "dev" + "Name" = "dev-vpc" } } # aws_subnet.prod_public will be created + resource "aws_subnet" "prod_public" { + arn = (known after apply) + availability_zone = "ap-northeast-1a" + cidr_block = "10.0.1.0/24" + id = (known after apply) + tags = { + "Name" = "prod-public-subnet" } + vpc_id = (known after apply) } # aws_vpc.prod will be created + resource "aws_vpc" "prod" { + arn = (known after apply) + cidr_block = "10.0.0.0/16" + id = (known after apply) + tags = { + "Environment" = "prod" + "Name" = "prod-vpc" } } Plan: 4 to add, 0 to change, 0 to destroy.
>> Terraform実践セミナーの詳細はこちら
provider alias vs ディレクトリ分割|どちらを選ぶか
マルチアカウント管理の手法は「provider aliasで1つのコードに統合する」か「アカウントごとにディレクトリを分割して独立管理する」かの2択です。それぞれの特徴を比較します。| 比較軸 | provider alias統合管理 | ディレクトリ分割管理 |
|---|---|---|
| コードの一元化 | 1か所で全アカウントを定義 | アカウントごとに別ディレクトリ |
| terraform apply範囲 | 全アカウントが一度に変更対象 | ディレクトリごとに独立実行 |
| 誤操作リスク | 本番provider aliasのミスで本番に意図しない変更 | ディレクトリを間違えない限り影響範囲が限定 |
| 共通リソースの管理 | 1つのコードで参照でき整合性が取りやすい | remote_stateで参照する設計が必要 |
| 向いているケース | アカウント間で構成差が小さく共有リソースが多い | 本番だけ構成が大きく異なり独立性を重視したい |
dev・stg・prodでVPCやサブネットのCIDR設計は同じで、インスタンスサイズだけ違う場合はprovider alias + variableによる統合管理が効率的です。一方、本番環境だけWAFやShieldを導入していて構成が根本的に違う場合、本番リソースを別ディレクトリで分離したほうがterraform applyの影響範囲が明確になります。
「本番への誤操作リスクを最小化したい」という方針の現場では、ディレクトリ分割が選ばれることが多いです。provider aliasは利便性が高い反面、
provider = aws.prodの書き忘れが本番意図しない変更につながるリスクがあります。トラブルシュート|よくあるエラーと対処
「No valid credential sources found」が出た時の対処
Error: No valid credential sources found with provider["registry.terraform.io/hashicorp/aws"].dev, on providers.tf line 14, in provider "aws": 14: assume_role {
・ローカル実行の場合:
aws configureで管理アカウントの認証情報を設定するか、AWS_PROFILE環境変数でプロファイルを指定する・CI/CD実行の場合(GitHub Actions等):実行ランナーにIAMロールをOIDCで割り当てる(アクセスキー不要)か、Secretsにアクセスキーを設定する
「AccessDenied: is not authorized to assume role」が出た時の対処
Error: AuthFailure: AWS was not able to validate the provided access credentials Error: user: arn:aws:iam::111122223333:user/terraform-ci is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::123456789012:role/TerraformCrossAccountRole
・実行元アカウント側:実行IAMユーザーまたはロールに
sts:AssumeRoleの権限が付与されているか確認する・切り替え先アカウント側:TerraformCrossAccountRoleの信頼ポリシーに、実行元アカウントのプリンシパルが正しく記載されているか確認する
信頼ポリシーのPrincipalに
arn:aws:iam::111122223333:rootを指定した場合でも、実行元IAMエンティティにsts:AssumeRole権限がなければ失敗します。「許可する側(信頼ポリシー)」と「実行できる側(IAMポリシー)」の両方が必要です。provider alias指定漏れによる意図しないアカウントへの作成
【要注意】リソース定義でprovider引数を省略すると、デフォルトprovider(実行元アカウント)に作成されます。開発アカウントに作るつもりでprovider = aws.devを書き忘れると、管理アカウントにリソースが作成されてしまいます。この誤操作は気づかないまま進みやすいため注意が必要です。これを防ぐには次の手順が有効です。
・デフォルトproviderに実運用リソースを置かない:管理アカウントにはTerraformのstateバックエンド(S3+DynamoDB)だけを置き、実リソースは全てaliasを持つproviderで管理する設計にする
・terraform planの結果を必ず確認する:planの出力にある「provider」の行を確認し、意図したアカウントへの変更になっているか確認してからapplyを実行する
本記事のまとめ
| やりたいこと | 設定・操作 |
|---|---|
| 別アカウント用のproviderを定義する | provider "aws" { alias = "dev" } |
| クロスアカウント認証を設定する | assume_role { role_arn = "arn:aws:iam::..." } |
| リソースを特定アカウントに作成する | resource "aws_vpc" "dev" { provider = aws.dev } |
| 実行元IAMに権限を付与する | sts:AssumeRoleを含むIAMポリシーをアタッチ |
| 切り替え先に信頼ポリシーを設定する | 実行元アカウントIDをPrincipalに記載 |
| provider aliasかディレクトリ分割か判断する | 構成差が小さい→alias統合、本番を独立させたい→ディレクトリ分割 |
>> Terraform実践セミナーの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:Terraformのnull_resourceとterraform_dataの使いどころ|local-execを乱用しないための判断基準
- この記事の属するカテゴリ:Terraformへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます