AuditアカウントからAssumeできる組織横断のReadロールを各アカウントに配置する

AuditアカウントからAssumeできる組織横断のReadロールを各アカウントに配置する

AWS Organizationsのマルチアカウント環境で、組織横断の棚卸しを効率化するためのReadロール配置とその活用法を紹介します。StackSetsを使った一括デプロイから実践的な運用例まで、検索ツールでは対応できないケースをカバーする方法をまとめました。
2026.07.31

はじめに

AWS Organizationsを活用してマルチアカウント戦略を取ることで、アジリティとガバナンスを両立させることが可能です。メリットが大きいため、最近ではマルチアカウント活用がかなり一般的になってきたかと思います。

一方で棚卸しなどを行うケースにおいては、組織横断で対応する必要があるため、シングルアカウントと比較して一工夫必要になってきます。こちらもAWS ConfigアグリゲータやAWS Resource Explorerなどを活用することで、大半はカバーすることが可能です。

https://dev.classmethod.jp/articles/advanced-queries-in-aws-config-to-find-resources-in-organizations/

https://dev.classmethod.jp/articles/aws-resource-explorer-multi-account-view/

但しこれらはどんなリソースがあるかを検索する用途に特化しているため、対応しきれないケースもあります。

まず検索対象外のリソースがあります。Configアグリゲータは記録対象のリソースタイプしか扱えず、Resource Explorerもインデックス対象のリソースしか検索できないため、これらがサポートしていないリソースタイプはヒットしません。実際、本記事の後半で扱うCloudFront VPC Originの単体リソースはConfigの記録対象外です。(2026/7時点)

また、任意のAPIを叩いてその結果を独自に加工・集計するような処理では別の手段を用意する必要があります。
以下のようなEBSスナップショットに対してEBS Direct APIを呼び出し、実データ量を算出して削除優先度をスコアリングするといった処理をマルチアカウント環境で実施する例が挙げられます。

https://dev.classmethod.jp/articles/ebs-snapshot-scorer-ebs-direct-api/

上記のようなケースでは、組織内にAssumeできるReadロールを配置しておくことで対応が可能です。

そこで本記事では、Auditアカウントから組織内の全アカウントへAssumeできる共通のReadロールを配置し、棚卸しや情報収集を実施できるようにします。今後執筆したい記事でも、この組織横断のReadロールを前提とする想定なので、展開方法と活用例を本記事でまとめておきます。

構成の全体像

全体像は次の通りです。

各メンバーアカウントに、AuditアカウントからのみAssumeできるReadロールを配置し、Auditアカウントから各アカウントのロールをAssumeし、情報を取得します。

Untitled_99__New_Diagram_-_Cacoo

各アカウントへのロール配置はCloudFormation StackSetsで実施します。
棚卸しの際に全てのアカウントに対して漏れがないことが重要になってきますので、AutoDeploymentを有効にして新規に作成されたアカウントにも自動でロールが展開されるようにします。

配置するReadロールについて

各メンバーアカウントに配置するロールは以下内容で設定します。

信頼ポリシー

棚卸しの処理などから広く使う想定をしているので、Auditアカウント全体からAssumeできるようにします。
以下のように信頼ポリシーを設定します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::XXXXXXXXXXXX:root"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

XXXXXXXXXXXX はAuditアカウントのIDに置き換わります。

権限

棚卸し用途なので、AWSマネージドポリシーの ReadOnlyAccess をアタッチします。
用途を定め細かく絞ることもできますが、今回は棚卸し用に広く利用できるようにしておきます。

StackSetsで全アカウントに展開する

StackSetsを使って組織全体に一括展開します。StackSetsのSERVICE_MANAGEDを利用して、組織へのデプロイを指定することで組織内の全てのアカウント及び今後作成される全てのアカウントに対してロールを展開します。

CloudFormationテンプレート

以下テンプレートを利用して展開します。
ロール名はorganization-readonly-roleとしています。(変更が必要な場合は修正してください。)

AWSTemplateFormatVersion: '2010-09-09'
Description: Organization-wide read-only role assumable from the Audit account

Parameters:
  AuditAccountId:
    Type: String
    Description: Audit account ID that is allowed to assume this role

Resources:
  OrganizationReadOnlyRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: organization-readonly-role
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              AWS: !Sub 'arn:aws:iam::${AuditAccountId}:root'
            Action: 'sts:AssumeRole'
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/ReadOnlyAccess

StackSetを作成する

ここからはマネジメントコンソールで進めます。

管理アカウント(もしくは委任管理者アカウント)でCloudFormationのコンソールを開き、左メニューの「StackSets」から「StackSetの作成」をクリックします。

StackSets___CloudFormation___ap-northeast-1

StackSets___CloudFormation___ap-northeast-1

  • テンプレートの選択

「サービスマネージドアクセス許可」を選択します。

スタックセットを作成___CloudFormation___ap-northeast-1

先ほどのテンプレートを設定して、次に進みます。

  • パラメータの入力

任意のスタックセット名とパラメータとしてAssumeRoleを許可するアカウントID(Auditアカウントを想定)を入力してください。

  • 実行設定

単一のロールを展開するだけになり競合等も想定されないため並列で実行していきます。
またロール名を設定しているため、チェックボックスにチェックが必要になります。

スタックセットを作成___CloudFormation___ap-northeast-1

  • デプロイオプションの設定

今回は組織全体へ展開を行いたいので「組織へのデプロイ」を選択します。

また「自動デプロイ」を有効にしておくと、以降に組織へ追加された新規アカウントにも自動でロールが配布されます。棚卸しではもれなく組織の情報を収集することが重要なので、新規アカウントにも自動で展開されるように有効化しておきましょう。

スタックセットを作成___CloudFormation___ap-northeast-1

IAMはグローバルサービスなので、どこかのリージョン1つを指定していただければと思います。
(例ではus-east-1を指定)

デプロイオプションについては任意の値を設定ください。

スタックセットを作成___CloudFormation___ap-northeast-1

内容を確認して作成するとロールが各アカウントに展開されます。ステータスが SUCCEEDED になれば完了です。

ロールを使って組織の棚卸しをしてみる

配置したロールの実用例として、VPC Originを利用しているディストリビューションを組織横断で洗い出す例を紹介いたします。

先日発生したCloudFront VPC Originsの障害では、VPC Originを利用しているディストリビューションだけが影響を受けました。影響を受けているものが組織内にないか迅速に洗い出したく、今回展開したものと同じ仕組みのロールを活用してClaude Codeで確認スクリプトを作成しました。

処理の流れは次の通りです。

  1. aws organizations list-accounts でアクティブなアカウントを列挙する
  2. 各アカウントの organization-readonly-role をAssumeRoleする
  3. list-distributionsVpcOriginConfig 参照有無で、実際にVPC Originを使っているディストリビューションを特定する
  4. list-vpc-origins と突き合わせ、関連付けされているか否かを分類する

CloudFrontはグローバルサービスなので、リージョンは us-east-1 を使います。

#!/usr/bin/env bash
#
# CloudFront VPC Origin 横断調査スクリプト
#   Audit アカウントの認証情報で実行し、Organizations 内の全アクティブアカウントに
#   organization-readonly-role を AssumeRole して VPC Origin の利用有無を確認する。
#
# 前提: Audit アカウントの認証情報が環境にセットされていること
#
set -uo pipefail

ROLE_NAME="organization-readonly-role"
# CloudFront はグローバルサービスのため us-east-1 を使用
CF_REGION="us-east-1"
OUT="cloudfront-vpc-origin-result-$(date +%Y%m%d-%H%M%S).tsv"

echo -e "AccountId\tAccountName\tType\tId\tName_or_Domain\tDetail" > "$OUT"

echo "実行アカウント: $(aws sts get-caller-identity --query Account --output text)"
echo "結果ファイル: $OUT"
echo

# アクティブなアカウント一覧を取得
accounts=$(aws organizations list-accounts \
  --query "Accounts[?Status=='ACTIVE'].[Id,Name]" --output text)

if [[ -z "$accounts" ]]; then
  echo "アカウント一覧が取得できませんでした。認証情報を確認してください。" >&2
  exit 1
fi

while IFS=$'\t' read -r acct_id acct_name; do
  [[ -z "$acct_id" ]] && continue
  printf "== %s (%s) ... " "$acct_id" "$acct_name"

  # AssumeRole
  creds=$(aws sts assume-role \
    --role-arn "arn:aws:iam::${acct_id}:role/${ROLE_NAME}" \
    --role-session-name "vpc-origin-scan" \
    --query "Credentials.[AccessKeyId,SecretAccessKey,SessionToken]" \
    --output text 2>/dev/null)

  if [[ -z "$creds" ]]; then
    echo "AssumeRole 失敗(スキップ)"
    echo -e "${acct_id}\t${acct_name}\tERROR\t-\t-\tassume-role-failed" >> "$OUT"
    continue
  fi

  read -r AK SK ST <<< "$creds"
  export AWS_ACCESS_KEY_ID="$AK"
  export AWS_SECRET_ACCESS_KEY="$SK"
  export AWS_SESSION_TOKEN="$ST"

  # 1) VpcOriginConfig を参照しているディストリビューションを抽出
  #    → CloudFront から「実際に使われている」VPC Origin はこれで特定できる
  dists=$(aws cloudfront list-distributions --region "$CF_REGION" \
    --query "DistributionList.Items[?Origins.Items[?VpcOriginConfig]].[Id,DomainName,Enabled,join(',',Origins.Items[?VpcOriginConfig].VpcOriginConfig.VpcOriginId)]" \
    --output text 2>/dev/null)

  # 2) アカウント内の VPC Origin リソース一覧(使用/未使用の区別に使う)
  vpc_origins=$(aws cloudfront list-vpc-origins --region "$CF_REGION" \
    --query "VpcOriginList.Items[].[Id,Name,Arn]" --output text 2>/dev/null)

  # クエリ結果が null(対象0件)のとき --output text は "None" を返すため空文字に正規化
  [[ "$dists" == "None" ]] && dists=""
  [[ "$vpc_origins" == "None" ]] && vpc_origins=""

  # ディストリビューションが参照している VpcOriginId の集合を作る
  used_ids=""
  found=0
  if [[ -n "$dists" ]]; then
    found=1
    while IFS=$'\t' read -r did ddom denabled dvpcids; do
      [[ -z "$did" ]] && continue
      echo -e "${acct_id}\t${acct_name}\tDISTRIBUTION(USED)\t${did}\t${ddom}\tenabled=${denabled};vpcOriginIds=${dvpcids}" >> "$OUT"
      used_ids="${used_ids} ${dvpcids//,/ }"
    done <<< "$dists"
  fi

  # VPC Origin リソースを「使用中(USED)」「未使用(ORPHANED)」に分類して出力
  if [[ -n "$vpc_origins" ]]; then
    while IFS=$'\t' read -r id name arn; do
      [[ -z "$id" ]] && continue
      if [[ " $used_ids " == *" $id "* ]]; then
        echo -e "${acct_id}\t${acct_name}\tVPC_ORIGIN(USED)\t${id}\t${name}\t${arn}" >> "$OUT"
      else
        echo -e "${acct_id}\t${acct_name}\tVPC_ORIGIN(ORPHANED)\t${id}\t${name}\t${arn}" >> "$OUT"
      fi
    done <<< "$vpc_origins"
  fi

  if [[ "$found" -eq 1 ]]; then
    echo "★ CloudFront が VPC Origin を使用中"
  elif [[ -n "$vpc_origins" ]]; then
    echo "使用なし(VPC Originリソースは存在するがディストリビューション未参照 = 孤立)"
  else
    echo "なし"
  fi

  # 認証情報をリセット(Audit アカウントに戻す)
  unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
done <<< "$accounts"

echo
echo "=== 完了 ==="
echo "【CloudFront が VPC Origin を実際に使用しているアカウント】"
grep "DISTRIBUTION(USED)" "$OUT" | cut -f1,2 | sort -u || echo "  (なし)"
echo
echo "【参考】VPC Originリソースは存在するが未参照(孤立)のアカウント:"
grep "VPC_ORIGIN(ORPHANED)" "$OUT" | cut -f1,2 | sort -u || echo "  (なし)"
echo
echo "詳細: $OUT"

出力例

実行すると、次のようなtsvが得られます。

AccountId       AccountName     Type                    Id              Name_or_Domain          Detail
XXXXXXXXXXXX    workload-prd    DISTRIBUTION(USED)      E1XXXXXXXXXXXX  dxxxxxxxxxxxxx.cloudfront.net   enabled=True;vpcOriginIds=vo-xxxxxxxxxxxx
XXXXXXXXXXXX    workload-prd    VPC_ORIGIN(USED)        vo-xxxxxxxxxxxx origin-alb              arn:aws:cloudfront::XXXXXXXXXXXX:vpcorigin/vo-xxxxxxxxxxxx
YYYYYYYYYYYY    workload-stg    VPC_ORIGIN(ORPHANED)    vo-yyyyyyyyyyyy old-origin              arn:aws:cloudfront::YYYYYYYYYYYY:vpcorigin/vo-yyyyyyyyyyyy

ディストリビューションから参照されているかどうかを区別し、収集しています。
障害の影響を実際に受けるのはディストリビューションから参照されているものなので使用されているものを対象に確認していき、該当がなければ影響なしといった形で確認を行いました。

ロールを事前に展開しておくと緊急で影響調査しないといけないこのようなケースで活用することもできます。

まとめ

Auditアカウントから組織内の全アカウントをRead権限で棚卸しするためのロールを、StackSetsで展開する仕組みと使用例を紹介させていただきました。

今後はこの仕組みを活用して、棚卸しや情報を収集する仕組みをどんどん紹介できればと思っています。

この記事が何かの参考になれば幸いです。
以上、クラウド事業本部の木村がお届けしました。


そのマルチアカウント運用、気合いで支えていませんか

Organizations や Control Tower で土台は作れても、アカウントもポリシーも増えるほど、運用は「詳しい一人」に寄りかかっていく。属人化が限界を迎える前に、組織として回す仕組み=CCoEへ。5,600社の支援から得た立ち上げの型を、無料資料にまとめました。

CCoE総合支援

組織で回す仕組みの資料をもらう

この記事をシェアする

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

関連記事