CloudFormation の Fn::GetStackOutput でクロスアカウント参照を試してみた
先行記事では Fn::GetStackOutput の基本動作とクロスリージョン参照を検証しました。クロスアカウント参照は未検証として残していましたが、今回その検証結果をお届けします。
検証シナリオ
クロスアカウント参照の検証シナリオとして、EventBridge クロスアカウント転送を選びました。
- 集約アカウント(= Producer):カスタムイベントバスを持ち、イベントを受け取る側。
Fn::GetStackOutputで Output を提供する側 - 送信アカウント(= Consumer):
Fn::GetStackOutputで集約アカウントのイベントバス ARN を参照し、イベントを転送する側
Fn::GetStackOutput の使用箇所は2か所です。
eb-role.yaml:転送ロールのポリシーリソースにイベントバス ARN を指定eb-consumer.yaml:転送ルールのターゲット ARN にイベントバス ARN を指定
デプロイ時(Fn::GetStackOutput による ARN 解決)
実行時(EventBridge イベント転送)
CloudFormation テンプレート
eb-producer.yaml(集約アカウント)
カスタムイベントバスと CloudWatch Logs を作成します。EventBusPolicy で送信アカウントからの events:PutEvents を許可しています。EventBridge クロスアカウント転送には、受信バス側の EventBusPolicy と送信側の転送ロール(後述)の両方が必要です。この点については別記事でも触れる予定です。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
ConsumerAccountId:
Type: String
Resources:
EventBus:
Type: AWS::Events::EventBus
Properties:
Name: getstackoutput-cross-account-bus
EventBusPolicy:
Type: AWS::Events::EventBusPolicy
Properties:
EventBusName: !Ref EventBus
StatementId: AllowCrossAccountPutEvents
Action: events:PutEvents
Principal: !Ref ConsumerAccountId
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/events/getstackoutput-cross-account
RetentionInDays: 7
LogGroupPolicy:
Type: AWS::Logs::ResourcePolicy
Properties:
PolicyName: EventBridgeToCloudWatchLogs
PolicyDocument: !Sub |
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "events.amazonaws.com"},
"Action": ["logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/events/getstackoutput-cross-account:*"
}]
}
Rule:
Type: AWS::Events::Rule
Properties:
EventBusName: !Ref EventBus
EventPattern:
source:
- my.test
Targets:
- Id: CloudWatchLogs
Arn: !Sub 'arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/events/getstackoutput-cross-account'
Outputs:
EventBusArn:
Value: !GetAtt EventBus.Arn
cfn-role.yaml(集約アカウント)
Fn::GetStackOutput が使用する IAM ロールです。送信アカウントの cfn-getstackoutput- プレフィックスを持つロールを前方一致で信頼し、cloudformation:DescribeStacks 権限を付与します。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
ConsumerAccountId:
Type: String
Resources:
GetStackOutputRole:
Type: AWS::IAM::Role
Properties:
RoleName: cfn-getstackoutput-cross-account-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${ConsumerAccountId}:root'
Action: sts:AssumeRole
Condition:
ArnLike:
aws:PrincipalArn: !Sub 'arn:aws:iam::${ConsumerAccountId}:role/cfn-getstackoutput-*'
GetStackOutputPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: cfn-getstackoutput-describe-stacks-policy
Roles:
- !Ref GetStackOutputRole
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: cloudformation:DescribeStacks
Resource:
- !Sub 'arn:aws:cloudformation:${AWS::Region}:${AWS::AccountId}:stack/cfn-getstackoutput-eb-producer/*'
Outputs:
RoleArn:
Value: !GetAtt GetStackOutputRole.Arn
信頼ポリシーの設計について: root(アカウント全体)を Principal にしつつ、ArnLike 条件で cfn-getstackoutput- プレフィックスのロールに限定しています。特定ロール ARN を直接指定する方がより厳密ですが、デプロイ方法が変わるたびに集約アカウント側の更新が必要になるトレードオフがあります。
cfn-exec-role.yaml(送信アカウント)
送信アカウントで使用する CFn 実行ロールです。cfn-getstackoutput- プレフィックスで統一することで、集約アカウントの前方一致信頼ポリシーに合致させています。
cfn-exec-role.yaml(クリックで展開)
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
AggregatorAccountId:
Type: String
Resources:
CfnExecRole:
Type: AWS::IAM::Role
Properties:
RoleName: cfn-getstackoutput-cfn-exec-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: cloudformation.amazonaws.com
Action: sts:AssumeRole
CfnExecPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: cfn-getstackoutput-cfn-exec-policy
Roles:
- !Ref CfnExecRole
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- cloudformation:CreateStack
- cloudformation:UpdateStack
- cloudformation:DeleteStack
- cloudformation:DescribeStacks
- cloudformation:DescribeStackEvents
- cloudformation:CreateChangeSet
- cloudformation:ExecuteChangeSet
- cloudformation:DescribeChangeSet
- cloudformation:DeleteChangeSet
- cloudformation:GetTemplate
Resource: '*'
- Effect: Allow
Action:
- iam:CreateRole
- iam:DeleteRole
- iam:GetRole
- iam:PutRolePolicy
- iam:DeleteRolePolicy
- iam:GetRolePolicy
- iam:AttachRolePolicy
- iam:DetachRolePolicy
- iam:PassRole
Resource:
- !Sub 'arn:aws:iam::${AWS::AccountId}:role/cfn-getstackoutput-*'
- !Sub 'arn:aws:iam::${AWS::AccountId}:policy/cfn-getstackoutput-*'
- Effect: Allow
Action:
- events:CreateEventBus
- events:DeleteEventBus
- events:DescribeEventBus
- events:PutRule
- events:DeleteRule
- events:DescribeRule
- events:PutTargets
- events:RemoveTargets
- events:ListTargetsByRule
- events:TagResource
- events:UntagResource
Resource: '*'
- Effect: Allow
Action: sts:AssumeRole
Resource: !Sub 'arn:aws:iam::${AggregatorAccountId}:role/cfn-getstackoutput-*'
Outputs:
RoleArn:
Value: !GetAtt CfnExecRole.Arn
eb-role.yaml(送信アカウント)
ここが Fn::GetStackOutput の1か所目です。 EventBridge 転送ロールのポリシーリソースに、集約アカウントのイベントバス ARN を Fn::GetStackOutput で取得して指定しています。
eb-role.yaml と eb-consumer.yaml を分けているのは、Fn::GetStackOutput が IAM ポリシーの Resource と EventBridge ルールのターゲット ARN という異なる箇所で使用できることを示すためです。実運用では1スタックにまとめても動作します。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
GetStackOutputRoleArn:
Type: String
Resources:
EventBridgeForwardRole:
Type: AWS::IAM::Role
Properties:
RoleName: cfn-getstackoutput-eb-forward-role
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: sts:AssumeRole
EventBridgeForwardPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: cfn-getstackoutput-eb-forward-policy
Roles:
- !Ref EventBridgeForwardRole
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action: events:PutEvents
Resource:
Fn::GetStackOutput: # ← ここで集約アカウントの EventBusArn を取得
StackName: cfn-getstackoutput-eb-producer
OutputName: EventBusArn
Region: ap-northeast-1
RoleArn: !Ref GetStackOutputRoleArn
Outputs:
ForwardRoleArn:
Value: !GetAtt EventBridgeForwardRole.Arn
eb-consumer.yaml(送信アカウント)
ここが Fn::GetStackOutput の2か所目です。 EventBridge ルールのターゲット ARN に、集約アカウントのイベントバス ARN を Fn::GetStackOutput で取得して指定しています。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
GetStackOutputRoleArn:
Type: String
ForwardRoleArn:
Type: String
Resources:
EventBus:
Type: AWS::Events::EventBus
Properties:
Name: getstackoutput-cross-account-consumer-bus
ForwardRule:
Type: AWS::Events::Rule
Properties:
EventBusName: !Ref EventBus
EventPattern:
source:
- my.test
Targets:
- Id: CrossAccountBus
Arn:
Fn::GetStackOutput: # ← ここで集約アカウントの EventBusArn を取得
StackName: cfn-getstackoutput-eb-producer
OutputName: EventBusArn
Region: ap-northeast-1
RoleArn: !Ref GetStackOutputRoleArn
RoleArn: !Ref ForwardRoleArn
Outputs:
EventBusName:
Value: !Ref EventBus
デプロイ手順
以下のシェル変数を設定してから実行してください。
AGGREGATOR_ACCOUNT_ID=111111111111 # 集約アカウント ID
CONSUMER_ACCOUNT_ID=222222222222 # 送信アカウント ID
# 変数確認
echo "Aggregator: ${AGGREGATOR_ACCOUNT_ID:?未設定}"
echo "Consumer: ${CONSUMER_ACCOUNT_ID:?未設定}"
Step 1・2:集約アカウントにデプロイ
# 集約アカウントに切り替え後
aws cloudformation deploy \
--stack-name cfn-getstackoutput-eb-producer \
--template-file eb-producer.yaml \
--parameter-overrides ConsumerAccountId=${CONSUMER_ACCOUNT_ID} \
--region ap-northeast-1
aws cloudformation deploy \
--stack-name cfn-getstackoutput-cfn-role \
--template-file cfn-role.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides ConsumerAccountId=${CONSUMER_ACCOUNT_ID} \
--region ap-northeast-1
Step 3・4・5:送信アカウントにデプロイ
# 送信アカウントに切り替え後
aws cloudformation deploy \
--stack-name cfn-getstackoutput-exec-role \
--template-file cfn-exec-role.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides AggregatorAccountId=${AGGREGATOR_ACCOUNT_ID} \
--region ap-northeast-1
aws cloudformation deploy \
--stack-name cfn-getstackoutput-eb-role \
--template-file eb-role.yaml \
--capabilities CAPABILITY_NAMED_IAM \
--parameter-overrides \
GetStackOutputRoleArn=arn:aws:iam::${AGGREGATOR_ACCOUNT_ID}:role/cfn-getstackoutput-cross-account-role \
--role-arn arn:aws:iam::${CONSUMER_ACCOUNT_ID}:role/cfn-getstackoutput-cfn-exec-role \
--region ap-northeast-1
aws cloudformation deploy \
--stack-name cfn-getstackoutput-eb-consumer \
--template-file eb-consumer.yaml \
--parameter-overrides \
GetStackOutputRoleArn=arn:aws:iam::${AGGREGATOR_ACCOUNT_ID}:role/cfn-getstackoutput-cross-account-role \
ForwardRoleArn=arn:aws:iam::${CONSUMER_ACCOUNT_ID}:role/cfn-getstackoutput-eb-forward-role \
--role-arn arn:aws:iam::${CONSUMER_ACCOUNT_ID}:role/cfn-getstackoutput-cfn-exec-role \
--region ap-northeast-1
動作確認
イベント送信(送信アカウント)
aws events put-events \
--entries '[{
"Source": "my.test",
"DetailType": "TestEvent",
"Detail": "{\"message\": \"hello from cross-account\"}",
"EventBusName": "getstackoutput-cross-account-consumer-bus"
}]' \
--region ap-northeast-1
CloudWatch Logs で受信確認(集約アカウント)
aws logs filter-log-events \
--log-group-name /aws/events/getstackoutput-cross-account \
--region ap-northeast-1
結果
{
"version": "0",
"id": "9409e3bd-f762-32c4-e773-aa9c78451b39",
"detail-type": "TestEvent",
"source": "my.test",
"account": "222222222222",
"time": "2026-05-04T10:14:29Z",
"region": "ap-northeast-1",
"detail": {"message": "hello from cross-account"}
}
送信アカウント(222222222222)から送ったイベントが、集約アカウント(111111111111)の CloudWatch Logs に届いています。Fn::GetStackOutput でクロスアカウントの EventBusArn を取得し、EventBridge 転送が正常に動作することを確認できました。
まとめ
Fn::GetStackOutput のクロスアカウント参照が動作することを確認できました。
クロスアカウントで使用する際のポイントは以下の2点です。
集約アカウント側(Producer):
Fn::GetStackOutput用の IAM ロールを作成し、送信アカウントの CFn 実行ロールを信頼する- 権限は
cloudformation:DescribeStacks(対象スタックに限定)のみ
送信アカウント側(Consumer):
--role-arnで CFn 実行ロールを明示指定してデプロイする- CFn 実行ロールのロール名を集約アカウントの信頼ポリシーの条件(前方一致)に合わせる
先行記事で未検証のまま残していたクロスアカウント参照も、今回の検証で使えることがわかりました。Fn::GetStackOutput の正式リリースを待ちつつ、マルチアカウント構成での活用を検討したいと思います。
注意事項・制約
本記事ではクロスアカウント固有の注意事項を扱います。スタック削除の挙動(Producer 削除後に Consumer を更新するとどうなるか等)は先行記事を参照してください。
- CFn 実行ロールの指定:
--role-arnを省略するとデプロイ実行ユーザーのロールがsts:AssumeRoleを呼ぶため、集約アカウント側の信頼ポリシーと一致しない場合があります。専用の CFn 実行ロールを--role-arnで明示指定することを推奨します - 許可対象の絞り込み:Output に機密情報が含まれる可能性がある場合は、
cfn-role.yamlのResourceを対象スタックに限定することを推奨します(機密情報は Parameter Store や Secrets Manager に格納するのが CloudFormation のベストプラクティスです) - 依存関係の可視化:クロスアカウントになると「どのアカウントの Consumer が自分の Output を参照しているか」が集約アカウント側から見えません。
Fn::ImportValueの削除ガードを持たない機能なので、長期運用では依存関係の管理に留意してください
Confused Deputy 対策
EventBridgeForwardRole は events.amazonaws.com を信頼するサービスロールです。条件なしで設定すると、同じ AWS アカウント内の別のイベントバスやルールからも AssumeRole される可能性があります(Confused Deputy 問題)。aws:SourceAccount と aws:SourceArn 条件を追加することで、意図したリソースからの呼び出しのみに限定できます。
Condition:
StringEquals:
aws:SourceAccount: !Ref AWS::AccountId
ArnLike:
aws:SourceArn: !Sub 'arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/*'







