IAMロールには、パスワードも永続的なアクセスキーもありません。それでも「ロールとしてS3にアクセスする」「別アカウントのロールでデプロイする」が成立するのは、間にAWS STSのAssumeRoleというAPIが挟まっているからです。前々回で「ロールは誰にも属さない器」、前回で「その器に入った権限がどう評価されるか」を整理したので、今回はその器を実際に「かぶる」瞬間、つまりAssumeRoleの動きを追います。信頼ポリシーと許可ポリシーの役割分担、CLIでの設定手順、ExternalId、PassRoleやロールチェーンとの違いまで、公式ドキュメントに沿って押さえます。
AssumeRoleは「ロールをかぶって一時キーをもらうAPI」
読み方と正体—sts:AssumeRoleというアクション
読み方は「アシュームロール」で、assumeは「引き受ける」の意味です。IAMポリシー上はsts:AssumeRoleというアクション名で登場し、呼び出す先はIAMではなくAWS STS(Security Token Service)です。STSは一時的なセキュリティ認証情報を発行するだけのサービスで、リクエストは無料です。
公式のAPIリファレンス「AssumeRole」の冒頭にはこうあります。
Returns a set of temporary security credentials that you can use to access AWS resources. These temporary credentials consist of an access key ID, a secret access key, and a security token.
つまりAssumeRoleは、ロールのARNを渡すと一時的なアクセスキー一式が返ってくるAPIです。返ってきたキーで署名したリクエストは、呼び出し元ではなくロールに付いた許可ポリシーの範囲で評価されます。「権限を委譲する」とは、この「一時キーで別人格になる」動きのことです。
返ってくるのは3点セットと有効期限
レスポンスのCredentialsにはAccessKeyId・SecretAccessKey・SessionTokenとExpirationが入ります。永続キー(AKIA始まり)と違って一時キーはASIAで始まり、必ずSessionTokenとセットで使います。有効期限はDurationSecondsで指定でき、公式仕様では次のとおりです。
- 最小900秒(15分)、最大43,200秒(12時間)
- 省略時のデフォルトは3,600秒(1時間)
- ロール側の「最大セッション期間」設定(1〜12時間)を超える値を指定するとエラー
- ロールチェーン(ロールから別のロールを引き受ける)の場合は最大1時間で固定
もう1つ、レスポンスのAssumedRoleUser.Arnはarn:aws:sts::123456789012:assumed-role/ロール名/セッション名という形で、arn:aws:iam::ではなくarn:aws:sts::で始まります。CloudTrailに残るのはこのARNです。
図で追う—呼び出しから一時認証情報の利用まで
全体の流れを1枚にすると次のようになります。呼び出し元がIAMユーザーでも、EC2やLambdaでも、別アカウントでも経路は同じです。
②で見ているのが信頼ポリシー、④で効いているのが許可ポリシーです。この2枚が別々のタイミングで評価される、というのがAssumeRoleの骨格です。
引き受けを成立させる2枚のポリシー
信頼ポリシー(AssumeRolePolicyDocument)—誰が引き受けられるか
ロールを作るとき、CLIでは--assume-role-policy-document、APIやCloudFormationではAssumeRolePolicyDocumentという名前で渡すJSONが信頼ポリシーです。中身は通常のポリシーと同じ文法ですが、Principal要素があるのが特徴です。まず、やってはいけない例から。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "*" },
"Action": "sts:AssumeRole"
}
]
}
$ aws iam create-role --role-name DeployRole \
--assume-role-policy-document file://trust-policy.json
# エラーにはならず作成できてしまう。Conditionが無ければ
# 任意のAWSアカウントのプリンシパルがこのロールを引き受けられる
"Principal": {"AWS": "*"}は「すべてのAWSプリンシパル」なので、Condition無しなら世界中の誰でも引き受けられます。信頼する相手はアカウントIDかARNで絞ります。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:root" },
"Action": "sts:AssumeRole",
"Condition": {
"Bool": { "aws:MultiFactorAuthPresent": "true" }
}
}
]
}
$ aws iam get-role --role-name DeployRole \
--query 'Role.AssumeRolePolicyDocument.Statement[0].Principal'
{
"AWS": "arn:aws:iam::111122223333:root"
}
:rootは「ルートユーザーだけ」ではなく、「アカウント111122223333のプリンシパルのうち、そのアカウント側でsts:AssumeRoleを許可された人」を指します。調べてみたら公式の「Roles terms and concepts」に、信頼ポリシーのPrincipalではARNにワイルドカードを使えないという制約もありました。user/dev-*のような書き方は通りません。
呼び出し側の許可ポリシー—sts:AssumeRoleをAllowする
信頼ポリシーは「受け入れる側の名簿」で、それだけでは権限を与えません。公式ドキュメントも「The trust policy doesn’t actually grant permissions.」と明言しています。引き受ける側には次のアイデンティティベースポリシーでsts:AssumeRoleを許可します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::123456789012:role/DeployRole"
}
]
}
$ aws iam simulate-custom-policy \
--policy-input-list file://caller-policy.json \
--action-names sts:AssumeRole \
--resource-arns arn:aws:iam::123456789012:role/DeployRole \
--query 'EvaluationResults[0].[EvalActionName,EvalDecision]'
[
"sts:AssumeRole",
"allowed"
]
Resourceを"*"にすると、信頼ポリシーで自分を受け入れてくれるロール全部を引き受けられる状態になるので、必要なロールに絞ります。simulate-custom-policyはSTSを呼ばずに机上で判定でき、無料です。
同一アカウントなら片側で足りる—信頼ポリシーはリソースベースポリシー
ここは触ってみないと気づきにくいところで、APIリファレンスには2択が書かれています。
- ユーザーに
sts:AssumeRoleを許可するポリシーを付ける(信頼ポリシーがアカウントを信頼していれば足りる) - 信頼ポリシーの
PrincipalにユーザーのARNを直接書く
後者だけで通るのは、信頼ポリシーがIAMのリソースベースポリシーとして扱われるからです。同一アカウント内では、リソースベースポリシーが特定のプリンシパルを明示的に許可していれば、アイデンティティベースポリシー側は不要です。:rootで書いた場合はアカウント全体への信頼なのでこの例外は使えず、クロスアカウントなら例外なく両方が要ります。
| 信頼ポリシーのPrincipal | 同一アカウント | 別アカウント |
|---|---|---|
| ユーザー/ロールのARNを直接指定 | 信頼ポリシーのみで可 | 両方必要 |
arn:aws:iam::アカウントID:root | 両方必要 | 両方必要 |
サービスプリンシパル(ec2.amazonaws.com等) | 信頼ポリシー+iam:PassRole | — |
手順—ロールを作ってCLIから引き受けるまで
アカウント123456789012にDeployRoleを作り、アカウント111122223333のIAMユーザーdev-tanakaがAWS CLI(v2.35系)から引き受けるまでを追います。
trust-policy.json として保存し、Principal に引き受ける側のアカウント arn:aws:iam::111122223333:root を指定するaws iam create-role --role-name DeployRole --assume-role-policy-document file://trust-policy.json を実行。返ってくる Role.Arn を控えるaws iam attach-role-policy --role-name DeployRole --policy-arn arn:aws:iam::123456789012:policy/DeployArtifactsWrite。ロールが「何をできるか」はここで決まるaws iam put-user-policy --user-name dev-tanaka --policy-name AllowAssumeDeployRole --policy-document file://caller-policy.json。前節の Resource をロールARNに絞ったJSONを使うaws sts assume-role --role-arn arn:aws:iam::123456789012:role/DeployRole --role-session-name deploy-tanaka で一時認証情報を取得し、aws sts get-caller-identity でロールセッションになっていることを確かめるaws sts assume-roleの出力を読む
ステップ5のコマンドを叩くと、次のJSONが返ります。
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/DeployRole \
--role-session-name deploy-tanaka \
--duration-seconds 3600
{
"Credentials": {
"AccessKeyId": "ASIAIOSFODNN7EXAMPLE",
"SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY",
"SessionToken": "IQoJb3JpZ2luX2VjEJz//////////wEaCXVzLWVhc3QtMSJHMEUCIQ...(約900文字、省略)",
"Expiration": "2026-08-15T13:12:41+00:00"
},
"AssumedRoleUser": {
"AssumedRoleId": "AROA3XFRBF535PLBIFPI4:deploy-tanaka",
"Arn": "arn:aws:sts::123456789012:assumed-role/DeployRole/deploy-tanaka"
}
}
--role-session-nameは必須で、2〜64文字の英数字と+=,.@-のみ、スペース不可です。省略するとAn error occurred (ParamValidation): the following arguments are required: --role-session-nameと返り、STSに問い合わせる前に止まります。SessionTokenの長さは固定ではなく、公式にも「make no assumptions about the maximum size」とあります。
一時キーを環境変数に入れてget-caller-identityを叩くと、ロールセッションになっていることが確認できます。
export AWS_ACCESS_KEY_ID=ASIAIOSFODNN7EXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYzEXAMPLEKEY
export AWS_SESSION_TOKEN=IQoJb3JpZ2luX2VjEJz...
aws sts get-caller-identity
{
"UserId": "AROA3XFRBF535PLBIFPI4:deploy-tanaka",
"Account": "123456789012",
"Arn": "arn:aws:sts::123456789012:assumed-role/DeployRole/deploy-tanaka"
}
Accountが呼び出し元の111122223333ではなく、ロールを所有する123456789012になっています。get-caller-identityは権限が不要で、明示的なDenyが付いていても呼べる特殊なAPIなので、「今どの人格か」の確認に安心して使えます。
~/.aws/configにrole_arnとsource_profileを書く
上の環境変数コピペは動作確認向きで、日常運用には向きません。1時間で切れるたびに貼り直すことになり、古いキーが.bash_historyに残ります。最初のうちはシェルスクリプトでexportを自動化していたのですが、CLI自体にロール用プロファイルの仕組みがありました。~/.aws/configに次のように書きます。
[profile dev-tanaka]
region = ap-northeast-1
output = json
[profile deploy]
role_arn = arn:aws:iam::123456789012:role/DeployRole
source_profile = dev-tanaka
role_session_name = deploy-tanaka
duration_seconds = 3600
$ aws s3 ls s3://deploy-artifacts-prod/ --profile deploy
2026-08-14 22:41:07 183422 app-v1.4.2.zip
2026-08-15 09:12:55 184016 app-v1.4.3.zip
--profile deployを指定すると、CLIが裏でsource_profileの認証情報でsts:AssumeRoleを呼び、一時キーでコマンドを実行します。EC2やECS上で「インスタンスのロールから別のロールへ」引き受けるなら、source_profileの代わりにcredential_source = Ec2InstanceMetadata(またはEcsContainer・Environment)を書きます。MFAならmfa_serial、外部IDならexternal_idを同じプロファイルに足すだけです。取得した一時キーは~/.aws/cli/cache/にJSONで保存され、期限が切れると自動で取り直されます。管理者がセッションを失効させた場合だけは自動更新されないので、rm -r ~/.aws/cli/cacheで消して取り直します。
クロスアカウントとExternalId—第三者に渡すときの決まりごと
信頼する側と信頼される側—Delegationの定義
公式の「Roles terms and concepts」にあるDelegationの定義では、リソースを持つ側がtrusting account(信頼する側)、アクセスしたいユーザーがいる側がtrusted account(信頼される側)です。ロールは常にリソースを持つtrusting側に作ります。先ほどの例なら123456789012がtrusting、111122223333がtrustedです。
ポリシーは合計3枚ですが、ロール側の2枚が本体で、呼び出し側の1枚は「このロールを引き受けてよい」という入場券です。
ExternalIdは秘密ではない—混乱した代理問題
外部ID(ExternalId)の位置づけは迷いやすいところです。公式の「Access to AWS accounts owned by third parties」には、目的は混乱した代理(confused deputy)問題の防止だとあります。監視SaaSのように1つのアカウントが多数の顧客ロールを引き受ける構成では、攻撃者が「あなたのロールARN」をSaaSに登録すると、SaaSは善意で他人のロールを引き受けてしまいます。それを防ぐため、SaaS側が顧客ごとにユニークなIDを発行し、顧客はそれを信頼ポリシーのConditionで要求します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::444455556666:root" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "sts:ExternalId": "cust-8f3a2c19" }
}
}
]
}
$ aws sts assume-role --role-arn arn:aws:iam::123456789012:role/MonitorRole \
--role-session-name monitor-agent
An error occurred (AccessDenied) when calling the AssumeRole operation: User: arn:aws:iam::444455556666:user/agent is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::123456789012:role/MonitorRole
$ aws sts assume-role --role-arn arn:aws:iam::123456789012:role/MonitorRole \
--role-session-name monitor-agent --external-id cust-8f3a2c19
{
"Credentials": { ... },
"AssumedRoleUser": {
"Arn": "arn:aws:sts::123456789012:assumed-role/MonitorRole/monitor-agent"
}
}
公式に「AWS does not treat the external ID as a secret.」とあるとおり、外部IDはパスワードではありません。ロールを閲覧できる人には見えますし、役割は「誰が」ではなく「どの文脈で」引き受けているかの表明です。同じ組織内では通常不要で、第三者に渡すときの仕組みと理解しておけば十分です。長さは2〜1,224文字、記号は+=,.@:/-のみです。
RoleSessionNameとSourceIdentityで「誰が」を残す
複数人が同じロールを共有すると、CloudTrailにはassumed-role/DeployRole/xxxとしか残らず、xxxのRoleSessionNameだけが個人の手がかりになります。sessionのような固定文字列にすると誰の操作か追えません。信頼ポリシーのsts:RoleSessionName条件キーでセッション名にユーザー名を強制する書き方が公式で紹介されています。ロールチェーンをまたいで引き継がれるSourceIdentityもあり、sts:SourceIdentity条件で必須にできます。
AssumeRoleと混同しやすい3つ
iam:PassRoleはAPIではなく「権限」
サジェストに「aws passrole assumerole」が並ぶくらい混同されがちな2つです。EC2にロールを付けて起動する場面では、実際にロールを引き受けるのはEC2サービスであってあなたではありません。あなたに必要なのは「このロールをEC2に渡してよい」という許可で、それがiam:PassRoleです。公式の「Grant a user permissions to pass a role to an AWS service」にはっきり書かれています。
PassRole is not an API call. PassRole is a permission, meaning no CloudTrail logs are generated for IAM PassRole.
CloudTrailでPassRoleというイベントを探しても出てきません。誰がどのロールをLambdaに渡したかは、CreateFunctionやRunInstancesのログに含まれるロールARNで追います。
| sts:AssumeRole | iam:PassRole | |
|---|---|---|
| 種類 | STSのAPIアクション | IAMの権限(APIではない) |
| 誰がロールを引き受けるか | 呼び出し元自身 | 渡された先のサービス(EC2・Lambda等) |
| いつ必要か | 一時認証情報が欲しいとき毎回 | サービスにロールを設定するとき(通常は初回) |
| CloudTrailの記録 | AssumeRoleイベントとして残る | 残らない(設定した側のイベントを見る) |
| アカウントをまたげるか | 可(信頼ポリシー次第) | 不可(同一アカウントのみ) |
PassRoleを絞るときはResourceでロールARNを限定するのに加え、iam:PassedToService条件キーで渡してよいサービスをec2.amazonaws.comなどに固定できます。ロールのタグをResourceTagで見る方法は、公式が「reliable results」にならないと明言しているので避けます。
STSの5つのAPIを比べる—AssumeRoleWithWebIdentityとGetSessionToken
STSには一時認証情報を返すAPIが5つあり、公式の「Compare AWS STS credentials」に比較表があります。GitHub ActionsからOIDCで入るAssumeRoleWithWebIdentityや、IAMユーザーにMFAを掛けるGetSessionTokenもここに含まれます。
| API | 呼べるのは | 有効期間(最小/最大/デフォルト) | MFA | セッションポリシー |
|---|---|---|---|---|
| AssumeRole | IAMユーザー、または一時認証情報を持つロール | 15分 / ロールの最大セッション期間 / 1時間 | 可 | 可 |
| AssumeRoleWithSAML | SAML IdPで認証済みの任意のユーザー | 15分 / ロールの最大セッション期間 / 1時間 | 不可 | 可 |
| AssumeRoleWithWebIdentity | OIDC IdPのJWTを持つ任意のユーザー | 15分 / ロールの最大セッション期間 / 1時間 | 不可 | 可 |
| GetFederationToken | IAMユーザー、ルートユーザー | IAMユーザー: 15分 / 36時間 / 12時間 | 不可 | 可 |
| GetSessionToken | IAMユーザー、ルートユーザー | IAMユーザー: 15分 / 36時間 / 12時間 | 可 | 不可 |
押さえたいのは2点です。AssumeRoleで得た認証情報からはGetFederationTokenとGetSessionTokenを呼べないこと、逆にGetSessionTokenの認証情報からSTSで呼べるのはAssumeRoleとGetCallerIdentityだけということです。またAssumeRoleWithWebIdentityとAssumeRoleWithSAMLはAWSの認証情報なしで呼べる(IdPのトークンが認証の代わり)ので、CIからアクセスキーを撤去したいときの本命です。
ロールチェーンは最大1時間
ロールAの一時認証情報でロールBを引き受けることをロールチェーンと呼びます。踏み台ロール経由で本番ロールに入る構成で登場しますが、公式の定義に「It applies regardless of the maximum session duration configured for individual roles.」とあるとおり、ロールBの最大セッション期間を12時間にしていてもチェーン経由なら1時間しか持ちません。DurationSecondsに3,600より大きい値を渡すと弾かれます。
$ aws sts assume-role --role-arn arn:aws:iam::123456789012:role/ProdRole \
--role-session-name deploy-tanaka --duration-seconds 7200 --profile bastion
An error occurred (ValidationError) when calling the AssumeRole operation: The requested DurationSeconds exceeds the 1 hour session limit for roles assumed by role chaining.
長時間バッチをチェーン先のロールで動かしたいなら、チェーンをやめて実行環境(EC2やECS)に直接ロールを付けるのが素直です。なお公式の「Methods to assume a role」によると、この1時間制限は「ユーザー認証情報からの最初の引き受け」と「インスタンスプロファイルを使うEC2上のアプリケーション」には適用されず、AWSサービス自身が引き受けるセッションには最大セッション期間の設定そのものが効きません。
まとめ
- AssumeRoleはロールARNを渡すと一時認証情報が返るSTSのAPI。そのキーで署名した操作はロールの許可ポリシーで評価される
- 必要なのはロール側の信頼ポリシー(誰が)と許可ポリシー(何を)、そして呼び出し側の
sts:AssumeRole許可。同一アカウントで信頼ポリシーにARNを直接書いた場合だけ、呼び出し側の許可を省略できる - 信頼ポリシーの
PrincipalにARNのワイルドカードは使えず、{"AWS": "*"}をCondition無しで書くと誰でも引き受けられる - CLIでは
~/.aws/configにrole_arnとsource_profile(サービス上ならcredential_source)を書けば、一時キーの取得とキャッシュ(~/.aws/cli/cache)をCLIが引き受ける - ExternalIdは秘密ではなく、混乱した代理問題を防ぐための「文脈の表明」。第三者にロールを渡すときに使う
iam:PassRoleはAPIではなく権限で、CloudTrailに単独のイベントは残らない。アカウントもまたげない- ロールチェーンは最大1時間。ロールの最大セッション期間より優先される
次は、ロールで得た権限が「どのネットワークから」使われるかという話で、VPCの構成要素(サブネット・ルートテーブル・IGW・NAT)に進みます。