AWS SSO環境でクロスアカウントS3 syncがAccessDeniedになる原因と解決策

想定読者
- AWS IAM Identity Center(旧AWS SSO)でマルチアカウント運用している
aws s3 syncをクロスアカウントで実行しようとして AccessDenied に遭遇した
先に結論
宛先バケット側にバケットポリシーで明示的な許可が必要
aws s3 syncは宛先バケットに対してs3:ListBucketとs3:PutObjectが必須s3:ListBucket: 差分判定のために宛先のオブジェクト一覧を取得するためs3:PutObject: オブジェクトを宛先にコピーするため
- SSO環境でのPrincipal指定はパス付きIAMロールARNを使う
- ❌ 「一時的なセッション」を指すARN:
arn:aws:sts::...:assumed-role/... - ✅ 「IAMロール自体」を指すARN
arn:aws:iam::...:role/aws-reserved/sso.amazonaws.com/<region>/AWSReservedSSO_...
- ❌ 「一時的なセッション」を指すARN:
やりたいこと
ソース側のSSO認証情報(AdministratorAccess)を使って、別アカウントのS3バケットへ同期する。
| 項目 | 値 |
|---|---|
| ソース(アカウントA) | s3://dev-cm-aaaaaaaaaaaa |
| 宛先(アカウントB) | s3://dev-cm-bbbbbbbbbbbb |
実行コマンド
aws s3 sync s3://dev-cm-aaaaaaaaaaaa s3://dev-cm-bbbbbbbbbbbb --only-show-errors
遭遇したエラー
fatal error: An error occurred (AccessDenied) when calling the ListObjectsV2 operation:
User: arn:aws:sts::aaaaaaaaaaaa:assumed-role/AWSReservedSSO_AdministratorAccess_.../...
is not authorized to perform: s3:ListBucket on resource: arn:aws:s3:::dev-cm-bbbbbbbbbbbb
because no resource-based policy allows the s3:ListBucket action
ハマりポイントと解決策
ハマりポイント1: 宛先側にバケットポリシーが必要だと気づかない
誤解
SSOでAdministratorAccessを取得している = どのバケットも操作できるはず
実際
AdministratorAccessはあくまでそのアカウント内の権限です。クロスアカウントでは、宛先側のリソースポリシー(バケットポリシー)で許可しない限りアクセスできません。
ハマりポイント2: PrincipalにSTSセッションARNを指定してしまう
エラーメッセージに表示されるARNをそのままコピペしたくなりますが、これでは動きません。
❌ 動かない例(STSセッションARN)
{
"Principal": {
"AWS": "arn:aws:sts::111111111111:assumed-role/AWSReservedSSO_AdministratorAccess_.../username"
}
}
❌ 動かない例(パスなしIAMロールARN)
{
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/AWSReservedSSO_AdministratorAccess_..."
}
}
✅ 正しい例(パス付きIAMロールARN)
{
"Principal": {
"AWS": "arn:aws:iam::111111111111:role/aws-reserved/sso.amazonaws.com/ap-northeast-1/AWSReservedSSO_AdministratorAccess_..."
}
}
SSO経由で作成されるIAMロールは /aws-reserved/sso.amazonaws.com/<region>/ というパスを持っています。このパスを含めた完全なARNを指定する必要があります。
正しいロールARNの取得方法
ソース側のSSO認証情報を環境変数に設定した状態で、以下を実行します。
ロール名を取得
ROLE_NAME=$(
aws sts get-caller-identity --query Arn --output text \
| sed -n 's#.*:assumed-role/\([^/]*\)/.*#\1#p'
)
echo "Role Name: $ROLE_NAME"
# パス付きロールARNを取得
ROLE_ARN=$(
aws iam get-role --role-name "$ROLE_NAME" --query 'Role.Arn' --output text
)
echo "Role ARN: $ROLE_ARN"
出力例:
Role Name: AWSReservedSSO_AdministratorAccess_1234567890abcdef
Role ARN: arn:aws:iam::111111111111:role/aws-reserved/sso.amazonaws.com/ap-northeast-1/AWSReservedSSO_AdministratorAccess_1234567890abcdef
最終的なバケットポリシー
宛先バケット(アカウントB)に以下のポリシーを設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCrossAccountSync",
"Effect": "Allow",
"Principal": {
"AWS": "<パス付きロールARN>"
},
"Action": [
"s3:ListBucket",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::<宛先側のバケット名>",
"arn:aws:s3:::<宛先側のバケット名>/*"
]
}
]
}
まとめ
| 項目 | 内容 |
|---|---|
| エラー原因 | s3 sync は宛先に s3:ListBucket が必要 |
| 解決策 | 宛先バケットにバケットポリシーを追加 |
| Principal指定 | パス付きIAMロールARNを使用 |






