「provider aliasは名前だけ知っているが、assume_roleと組み合わせてクロスアカウント操作をどう書けばいいのかわからない」
複数のAWSアカウントを扱う場合、Terraformのprovider aliasとassume_roleを組み合わせると、1つのコードベースで開発・ステージング・本番の各アカウントにまたがるリソースを同時管理できます。アカウントをまたぐ認証の切り替えはTerraformが自動で行ってくれます。
この記事では、provider aliasの基本設定からassume_roleによるクロスアカウント認証の仕組み、IAMクロスアカウントロールの準備手順、data sourceへのprovider指定、実際のリソース実装例、moduleへのprovider引き渡し設計、さらに「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のように明示すると作成先アカウントを固定できる
・一度作成したaliasをproviders.tfから削除するとstateが壊れるので慎重に扱う
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜ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" } }
3. data sourceへのprovider参照
data sourceブロックにもproviderを指定できます。特定アカウントの既存リソース情報(AMI IDやVPC IDなど)を参照するときに使います。# 開発アカウントの既存VPC IDを取得する例 data "aws_vpc" "dev_existing" { provider = aws.dev # 開発アカウントのproviderを指定 tags = { Name = "existing-dev-vpc" } } # 本番アカウントのAMI IDを取得する例(本番アカウントが所有するゴールデンAMI) data "aws_ami" "prod_golden" { provider = aws.prod most_recent = true owners = ["self"] filter { name = "name" values = ["golden-ami-*"] } }
IAMクロスアカウントロールの準備(Terraform実行前の設定)
provider aliasのassume_roleが機能するためには、切り替え先アカウントにクロスアカウントロールを作成し、実行元アカウントからのassumeを許可する必要があります。4. 切り替え先アカウントに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ポリシーを付与するのが一般的です。
5. 実行元アカウント側に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の準備が完了したら、実際にリソースを定義します。6. 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" } }
7. 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.
moduleへのprovider引き渡し設計
Terraformコードをmodule化している場合、provider aliasをmodule内で使うには呼び出し元からproviders引数で明示的に渡す必要があります。8. providers引数の渡し方
アカウントごとに同じリソース構成を作る場合、moduleを複数回呼び出す設計が一般的です。呼び出し時にproviders引数でprovider aliasを渡します。# main.tf(呼び出し元) # 同じVPCモジュールを開発・本番の両アカウントに展開する例 module "vpc_dev" { source = "./modules/vpc" providers = { aws = aws.dev # 開発アカウントのproviderを渡す } vpc_cidr = "10.1.0.0/16" env_name = "dev" } module "vpc_prod" { source = "./modules/vpc" providers = { aws = aws.prod # 本番アカウントのproviderを渡す } vpc_cidr = "10.0.0.0/16" env_name = "prod" }
providers引数を省略すると、module内のリソースはすべてデフォルトprovider(管理アカウント)を使います。マルチアカウント設計では必ずこの引き渡しを明示してください。VPCピアリングのように2つのアカウントにまたがる操作をmodule内で完結させる場合は、複数のproviderを渡す設計も使えます。
# VPCピアリングmodule(管理アカウントと開発アカウントをまたぐ操作) module "vpc_peering" { source = "./modules/vpc-peering" providers = { aws.requester = aws # 管理アカウント(ピアリングをリクエストする側) aws.accepter = aws.dev # 開発アカウント(ピアリングを承認する側) } requester_vpc_id = "vpc-xxx" accepter_vpc_id = "vpc-yyy" }
9. module内でのconfiguration_aliases宣言
複数のprovider aliasを受け取るmoduleでは、受け取るproviderをrequired_providersのconfiguration_aliasesで宣言します。# modules/vpc-peering/versions.tf terraform { required_version = ">= 1.3" required_providers { aws = { source = "hashicorp/aws" version = ">= 5.0" configuration_aliases = [aws.requester, aws.accepter] } } }
provider = aws.requesterやprovider = aws.accepterで参照できます。注意点:
configuration_aliasesを宣言しないままmodule内でaliasを使うと、Terraform 1.6以降では警告が出るようになりました。Terraform 1.8以降ではエラーになるケースもあるため、moduleを作成するときは必ず宣言しておきましょう。また、
configuration_aliasesで宣言したaliasは後から気軽に削除できない点にも注意が必要です。stateにそのaliasで作成したリソースが残っている状態でaliasブロックを削除すると、terraform planの段階でエラーが発生します。aliasを削除したい場合は、先にterraform destroyでリソースを削除するか、terraform state rmでstateから除いてから設定を変更してください。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を実行する
「No configuration for provider with alias...」が出た時の対処
Error: No configuration for provider "aws" with alias "dev" on modules/vpc/main.tf line 3, in resource "aws_vpc" "main": 3: provider = aws.dev
providers引数を渡し忘れたか、alias名が一致していないケースです。呼び出し元のproviders = { ... }ブロックを確認し、module側のconfiguration_aliasesと名前が完全一致しているかを確認します。alias名のアンダースコアとハイフンは混同しやすい点にも注意が必要です。
aws.devとproviders = { aws.dev = aws.dev }の対応関係を確認し、定義と参照で名前を統一してください。aliasブロックを削除するとstateが壊れるケース
# エラー出力例(環境により異なります) Error: Provider configuration not present To work with aws_vpc.dev its original provider configuration at provider["registry.terraform.io/hashicorp/aws"].dev is required, but it has been removed. Add back its configuration.
対処法は2つあります。
・リソースを先に削除する:
terraform destroy -target=aws_vpc.dev等でそのaliasを使うリソースを先に削除してから、aliasブロックを消す・stateから除いてからブロックを削除する:リソースを残したままaliasだけ外したい場合は
terraform state rm aws_vpc.devでstateの管理から除いた後にaliasブロックを削除する(この場合、実際のAWSリソースはそのまま残る)「もう使わないaliasだから消してしまおう」と思ったときは、先にそのaliasで作成したリソースが残っていないかを
terraform state listで確認する習慣をつけてください。本記事のまとめ
| やりたいこと | 設定・操作 |
|---|---|
| 別アカウント用のproviderを定義する | provider "aws" { alias = "dev" } |
| クロスアカウント認証を設定する | assume_role { role_arn = "arn:aws:iam::..." } |
| リソースを特定アカウントに作成する | resource "aws_vpc" "dev" { provider = aws.dev } |
| 別アカウントのdata sourceを参照する | data "aws_vpc" "dev" { provider = aws.dev } |
| 実行元IAMに権限を付与する | sts:AssumeRoleを含むIAMポリシーをアタッチ |
| 切り替え先に信頼ポリシーを設定する | 実行元アカウントIDをPrincipalに記載 |
| moduleにprovider aliasを渡す | providers = { aws = aws.dev }で引き渡す |
| module内でaliasを受け取る | configuration_aliases = [aws.requester, aws.accepter]で宣言 |
| provider aliasかディレクトリ分割か判断する | 構成差が小さい→alias統合、本番を独立させたい→ディレクトリ分割 |
| aliasブロックを安全に削除する | terraform destroyまたはterraform state rmでstateから除いてから削除 |
>> Terraform実践セミナーの詳細はこちら
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

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