CloudFormation の Fn::GetStackOutput でクロスアカウント参照を試してみた

CloudFormation の Fn::GetStackOutput でクロスアカウント参照を試してみた

CloudFormation の新組み込み関数 Fn::GetStackOutput を使ったクロスアカウントのスタック間参照を検証しました。集約アカウント側の IAM ロール設計、送信アカウント側の CFn 実行ロール指定など、実際にデプロイして動作確認した手順を紹介します。
2026.05.04

先行記事では Fn::GetStackOutput の基本動作とクロスリージョン参照を検証しました。クロスアカウント参照は未検証として残していましたが、今回その検証結果をお届けします。

https://dev.classmethod.jp/articles/cloudformation-fn-getstackoutput-cross-region-reference/

検証シナリオ

クロスアカウント参照の検証シナリオとして、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.yamleb-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.yamlResource を対象スタックに限定することを推奨します(機密情報は Parameter Store や Secrets Manager に格納するのが CloudFormation のベストプラクティスです)
  • 依存関係の可視化:クロスアカウントになると「どのアカウントの Consumer が自分の Output を参照しているか」が集約アカウント側から見えません。Fn::ImportValue の削除ガードを持たない機能なので、長期運用では依存関係の管理に留意してください

Confused Deputy 対策

EventBridgeForwardRoleevents.amazonaws.com を信頼するサービスロールです。条件なしで設定すると、同じ AWS アカウント内の別のイベントバスやルールからも AssumeRole される可能性があります(Confused Deputy 問題)。aws:SourceAccountaws:SourceArn 条件を追加することで、意図したリソースからの呼び出しのみに限定できます。

Condition:
  StringEquals:
    aws:SourceAccount: !Ref AWS::AccountId
  ArnLike:
    aws:SourceArn: !Sub 'arn:aws:events:${AWS::Region}:${AWS::AccountId}:rule/*'

参考リンク

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事