AuditアカウントからAssumeできる組織横断のReadロールを各アカウントに配置する
はじめに
AWS Organizationsを活用してマルチアカウント戦略を取ることで、アジリティとガバナンスを両立させることが可能です。メリットが大きいため、最近ではマルチアカウント活用がかなり一般的になってきたかと思います。
一方で棚卸しなどを行うケースにおいては、組織横断で対応する必要があるため、シングルアカウントと比較して一工夫必要になってきます。こちらもAWS ConfigアグリゲータやAWS Resource Explorerなどを活用することで、大半はカバーすることが可能です。
但しこれらはどんなリソースがあるかを検索する用途に特化しているため、対応しきれないケースもあります。
まず検索対象外のリソースがあります。Configアグリゲータは記録対象のリソースタイプしか扱えず、Resource Explorerもインデックス対象のリソースしか検索できないため、これらがサポートしていないリソースタイプはヒットしません。実際、本記事の後半で扱うCloudFront VPC Originの単体リソースはConfigの記録対象外です。(2026/7時点)
また、任意のAPIを叩いてその結果を独自に加工・集計するような処理では別の手段を用意する必要があります。
以下のようなEBSスナップショットに対してEBS Direct APIを呼び出し、実データ量を算出して削除優先度をスコアリングするといった処理をマルチアカウント環境で実施する例が挙げられます。
上記のようなケースでは、組織内にAssumeできるReadロールを配置しておくことで対応が可能です。
そこで本記事では、Auditアカウントから組織内の全アカウントへAssumeできる共通のReadロールを配置し、棚卸しや情報収集を実施できるようにします。今後執筆したい記事でも、この組織横断のReadロールを前提とする想定なので、展開方法と活用例を本記事でまとめておきます。
構成の全体像
全体像は次の通りです。
各メンバーアカウントに、AuditアカウントからのみAssumeできるReadロールを配置し、Auditアカウントから各アカウントのロールをAssumeし、情報を取得します。

各アカウントへのロール配置は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の作成」をクリックします。


- テンプレートの選択
「サービスマネージドアクセス許可」を選択します。

先ほどのテンプレートを設定して、次に進みます。
- パラメータの入力
任意のスタックセット名とパラメータとしてAssumeRoleを許可するアカウントID(Auditアカウントを想定)を入力してください。
- 実行設定
単一のロールを展開するだけになり競合等も想定されないため並列で実行していきます。
またロール名を設定しているため、チェックボックスにチェックが必要になります。

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

IAMはグローバルサービスなので、どこかのリージョン1つを指定していただければと思います。
(例ではus-east-1を指定)
デプロイオプションについては任意の値を設定ください。

内容を確認して作成するとロールが各アカウントに展開されます。ステータスが SUCCEEDED になれば完了です。
ロールを使って組織の棚卸しをしてみる
配置したロールの実用例として、VPC Originを利用しているディストリビューションを組織横断で洗い出す例を紹介いたします。
先日発生したCloudFront VPC Originsの障害では、VPC Originを利用しているディストリビューションだけが影響を受けました。影響を受けているものが組織内にないか迅速に洗い出したく、今回展開したものと同じ仕組みのロールを活用してClaude Codeで確認スクリプトを作成しました。
処理の流れは次の通りです。
aws organizations list-accountsでアクティブなアカウントを列挙する- 各アカウントの
organization-readonly-roleをAssumeRoleする list-distributionsのVpcOriginConfig参照有無で、実際にVPC Originを使っているディストリビューションを特定する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で展開する仕組みと使用例を紹介させていただきました。
今後はこの仕組みを活用して、棚卸しや情報を収集する仕組みをどんどん紹介できればと思っています。
この記事が何かの参考になれば幸いです。
以上、クラウド事業本部の木村がお届けしました。






