はじめに
S3バケットに対して別サービスのロールからアクセスを許可する設計をした際、IAMの理解が曖昧なまま進めてしまい時間がかかった。この記事ではIAMの全体像を整理し、「誰が」「何に」「何をできるか」を制御する仕組みを体系的にまとめる。
IAMの登場人物
IAMには大きく4つの概念がある。
| 概念 | 何か | 例 |
|---|---|---|
| ユーザー | 人間がAWSを操作するためのID | 開発者のAWSアカウント |
| ロール | サービスやアプリが一時的に引き受ける権限セット | Lambda関数、ECSタスク |
| ポリシー | 「何を許可/拒否するか」のJSON定義 | S3読み取り許可 |
| 信頼ポリシー | 「誰がこのロールを引き受けられるか」の定義 | ECSタスクがこのロールを使える |
ポリシーの種類
アイデンティティベースポリシー(IAMポリシー)
ロールやユーザーに「あなたは何ができるか」をアタッチする。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/output/*"
}
]
}
これをロールにアタッチすると、そのロールは my-bucket/output/* を読める。
リソースベースポリシー(バケットポリシー等)
リソース側に「誰がアクセスしてよいか」を定義する。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/my-app-role"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/output/*"
}
]
}
S3バケット自身が「このロールからのGetObjectを許可する」と宣言している。
両方必要なケース
同一アカウント内: どちらか一方で許可されていればアクセス可能。
クロスアカウント: 両方で許可が必要。
- 呼び出し側ロールのIAMポリシーで
Allow - リソース側のバケットポリシーで
Principalに呼び出し側を指定
信頼ポリシー(AssumeRole)
ロールには「誰がこのロールになれるか」を定義する信頼ポリシーがある。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
これにより、ECSタスクがこのロールを引き受けて動作できる。
評価ロジック
AWSがアクセスを判定する順序:
1. 明示的Deny → 即拒否(最優先)
2. Organizations SCP → 組織レベルの制限
3. リソースベースポリシー → 許可があればOK(同一アカウント)
4. アイデンティティベースポリシー → 許可があればOK
5. Permissions boundary → 上限チェック
6. セッションポリシー → AssumeRole時の追加制限
7. デフォルト → 暗黙的Deny
重要なのは「明示的Denyは何よりも優先される」という点である。
実務でよくあるパターン
パターン1: サービスAからS3バケットへのアクセス
[サービスA (ECSタスク)]
↓ AssumeRole
[タスクロール]
↓ IAMポリシーで s3:GetObject 許可
[S3バケット]
↓ バケットポリシーでタスクロールを Principal に指定
[オブジェクト取得成功]
同一アカウントならバケットポリシー OR IAMポリシーどちらか一方でよい。
パターン2: Presigned URL生成
Presigned URLは「URLを生成したロールの権限」でアクセスする。つまり:
- URL生成側のロールに
s3:GetObjectが必要 - バケットポリシーでそのロールを許可する(クロスアカウントの場合)
URLにアクセスするユーザーはAWS認証不要だが、URL生成側の権限が足りなければ403になる。
パターン3: 最小権限の設計
{
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/output/*"
}
- Action: 必要な操作のみ(
s3:*は使わない) - Resource: 必要なパスのみ(バケット全体は避ける)
- Condition: 必要に応じてIPやVPCエンドポイントで制限
デバッグ手順
アクセスが拒否された場合のチェックリスト:
- IAMポリシー確認: ロールにアタッチされたポリシーでActionとResourceが許可されているか
- リソースポリシー確認: バケットポリシーのPrincipalにロールARNが含まれているか
- 明示的Deny確認: どこかにDenyステートメントがないか
- 信頼ポリシー確認: サービスがそのロールをAssumeRoleできるか
- ARN確認: アカウントID、リージョン、リソース名にtypoがないか
- IAM Policy Simulator: AWS ConsoleのPolicy Simulatorで検証
よくある罠
| 罠 | 原因 | 対策 |
|---|---|---|
| 同一アカウントなのに403 | リソースARNのtypo | コピペで正確に |
| クロスアカウントで403 | 片方しか設定してない | 両側で許可必要 |
| ワイルドカードが効かない | Resource: "*" だが s3:ListBucket は バケットARN指定が必要 | Action別にResourceを分ける |
| Presigned URLが403 | URL生成側ロールの権限不足 | 生成側に GetObject 付与 |
まとめ
| 質問 | 答え |
|---|---|
| 誰が操作するか | → ロール/ユーザーを特定 |
| 何に対して操作するか | → リソースARNを特定 |
| 何の操作か | → Action(GetObject, PutObject等)を特定 |
| どこに書くか | → IAMポリシー or リソースポリシー or 両方 |
IAMの設計は「主語(誰が)」「動詞(何をする)」「目的語(何に対して)」を明確にすれば迷わない。クロスアカウントの場合だけ「両側で許可」を忘れなければ大丈夫である。