Study guide
Technical reference and lesson notes
AWS Cross-Account S3 Access with IAM Roles and AWS STS
Purpose of This Lesson
This lesson demonstrates how an identity in one AWS account can access resources in another AWS account by assuming an IAM role through AWS Security Token Service (AWS STS).
The example uses:
- Account A: The management or identity account containing the user
Jack. - Account B: The production or resource account containing the IAM role and Amazon S3 resources.
- IAM role in Account B: A role that trusts principals from Account A and grants access to S3.
The central pattern is: Jack does not receive direct permissions in Account B. Instead, Jack assumes a role in Account B and uses the role’s temporary credentials.
Key Concepts
Cross-account role assumption
Cross-account access requires configuration in both accounts:
- Account B contains the role and its trust policy.
- Account B also contains the role’s permissions policy, which controls the resources and actions available after assumption.
- Account A contains Jack and an identity-based policy allowing him to call
sts:AssumeRoleon the specific role.
Permission must be granted on both sides. A trust policy alone does not grant access to S3, and an identity policy allowing role assumption does not grant S3 permissions by itself.
Trust policy
The role’s trust policy determines who may assume it. The important elements in this example are:
Effect: AllowAction: sts:AssumeRole- A principal representing Account A
- A condition requiring a specific AWS STS external ID
A principal such as arn:aws:iam::<ACCOUNT_A_ID>:root in this trust-policy context does not mean only the root user can assume the role. It represents principals from that AWS account, subject to the applicable identity permissions and conditions.
Example structure:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<ACCOUNT_A_ID>:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "<EXTERNAL_ID>"
}
}
}
]
}
Role permissions policy
The role’s permissions policy controls what the assumed session can do in Account B. In the demonstration, the role receives the AWS managed AmazonS3FullAccess policy, allowing broad S3 access.
That policy is suitable for demonstrating the mechanics, but production roles should grant only the required actions and resources whenever possible.
Identity-based policy for Jack
Jack’s policy in Account A allows him to:
- Call
iam:ListRoles. - Call
sts:AssumeRole.
The sts:AssumeRole permission can be restricted to the ARN of the intended role in Account B rather than using a wildcard resource. This limits Jack to the designated cross-account access path.
External ID
The external ID is supplied in the AssumeRole request and checked by the role’s trust policy. If the requested external ID does not match the value in the trust policy, role assumption fails.
External IDs are particularly relevant when an external party or service assumes roles across customer accounts. The value should be treated as a security control and not casually exposed or reused.
Temporary role credentials
A successful sts:AssumeRole call returns temporary credentials:
- Access key ID
- Secret access key
- Session token
The session token is required when using the temporary credentials. These credentials represent the assumed-role session, not Jack’s original IAM user session.
Cross-Account Access Workflow
1. Create the role in Account B
Switch to Account B and create a trust-policy file, such as trust-policy.json, containing Account A’s ID and the required external ID.
Create the role with the AWS CLI:
aws iam create-role \
--role-name S3-access-role-for-external-account \
--assume-role-policy-document file://trust-policy.json
Attach the S3 permissions policy to the role:
aws iam attach-role-policy \
--role-name S3-access-role-for-external-account \
--policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess
Verify that the trust relationship references Account A, not Account B.
2. Create Jack in Account A
Switch to Account A and create the user:
aws iam create-user --user-name Jack
Create and attach an identity policy that permits role assumption. The policy can also include iam:ListRoles for role discovery:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:ListRoles",
"sts:AssumeRole"
],
"Resource": "*"
}
]
}
For tighter access, restrict the sts:AssumeRole statement’s Resource to the ARN of the intended role in Account B.
3. Configure Jack’s CLI profile
Create access keys for the test user and configure a named AWS CLI profile:
aws iam create-access-key --user-name Jack
aws configure --profile jack
The profile should contain Jack’s access key ID, secret access key, and a default region such as us-east-1.
4. Confirm the active identity
Use sts get-caller-identity to verify which credentials are being used:
aws sts get-caller-identity --profile jack
The --profile jack option is important. Without it, the CLI may continue using the default CloudShell or administrator identity.
5. Assume the role in Account B
From Account A, have Jack assume the role by supplying:
- Jack’s CLI profile
- The role ARN in Account B
- A session name
- The exact external ID required by the trust policy
Example:
aws sts assume-role \
--profile jack \
--role-arn arn:aws:iam::<ACCOUNT_B_ID>:role/S3-access-role-for-external-account \
--role-session-name jack-s3-session \
--external-id <EXTERNAL_ID>
The command returns the temporary access key ID, secret access key, and session token.
6. Use the temporary credentials
Export or configure the three returned values in the shell:
export AWS_ACCESS_KEY_ID=<TEMPORARY_ACCESS_KEY_ID>
export AWS_SECRET_ACCESS_KEY=<TEMPORARY_SECRET_ACCESS_KEY>
export AWS_SESSION_TOKEN=<TEMPORARY_SESSION_TOKEN>
Confirm the resulting identity:
aws sts get-caller-identity
The returned identity should show an assumed-role session. S3 commands now run with the role’s Account B permissions rather than Jack’s original Account A permissions.
Exam- or Assessment-Relevant Takeaways
- Cross-account access commonly uses an IAM role in the resource account and AWS STS to issue temporary credentials.
- The trust policy answers: “Who may assume this role?”
- The role permissions policy answers: “What may the assumed session do?”
- The identity-based policy in the source account must allow the user to call
sts:AssumeRoleon the target role. - The external ID must be present in the
AssumeRolerequest and must match the trust-policy condition. - A successful role assumption returns an access key ID, secret access key, and session token.
- Always validate the active identity with
aws sts get-caller-identitywhen changing profiles or credentials. - Restrict the
sts:AssumeRoleresource to a specific role ARN when the user should access only one cross-account role. - Do not confuse
arn:aws:iam::<ACCOUNT_ID>:rootin a role trust policy with permission granted only to the account’s root user.
Tool / Feature Decision Guide
| Need | Appropriate mechanism | Reason |
|---|---|---|
| Allow an identity from another account to use a role | Role trust policy | Establishes which external account or principal may call sts:AssumeRole. |
| Define access after the role is assumed | Role permissions policy | Controls actions and resources available to the temporary session. |
| Permit Jack to initiate the cross-account operation | Identity-based policy in Account A | Grants Jack permission to call sts:AssumeRole. |
| Add a matching value to the role-assumption request | STS external ID | Satisfies the trust-policy condition and adds a required security value. |
| Determine which principal is executing a CLI command | aws sts get-caller-identity | Confirms whether the CLI is using the default identity, Jack, or an assumed role. |
| Run commands with Jack’s long-term test credentials | --profile jack | Selects the named profile instead of the default CLI credentials. |
| Run commands as the assumed role | Environment variables containing temporary credentials | Sets the CLI context to the returned role session, including the required session token. |
Common Traps / Misconceptions
- Creating only the role is insufficient. The source identity must also be allowed to assume it.
- A trust policy does not grant S3 access. S3 permissions must be attached to or granted by the role’s permissions policy.
- The account ID in the trust policy matters. It must identify the trusted source account, Account A, rather than the account containing the role.
- The external ID is not optional when the trust policy requires it. Omitting it or supplying the wrong value causes role assumption to fail.
- The session token must be included. Temporary credentials consist of all three returned values, not just the access key ID and secret access key.
- The CLI profile does not automatically change globally. Use
--profile jackwhen testing Jack’s permissions; otherwise commands may run as the default administrator. get-caller-identityis a diagnostic, not an authorization grant. It tells you who is active but does not change permissions.- Broad managed policies are not automatically least privilege. The demonstration uses S3 full access to validate the workflow, but production permissions should be narrowed.
- Long-term access keys for test users require cleanup. Delete test users, policies, keys, roles, and other resources after the exercise.
Real-World Engineer / Analyst Notes
- Model cross-account access as two separate authorization decisions: the caller must be allowed to assume the role, and the role must trust the caller’s account or principal.
- Keep account IDs and role ARNs explicit in automation to reduce accidental use of the wrong account.
- Use named CLI profiles during testing and verify each profile before performing changes.
- Treat external IDs as controlled configuration values. A mismatch is often the first clue when a role-assumption operation returns an authorization failure.
- Use temporary credentials from STS instead of distributing permanent credentials for cross-account operations.
- When troubleshooting, check the caller identity, the target role ARN, the trust policy’s principal, the external ID condition, and the role permissions policy in that order.
- Clean up temporary IAM users and access keys after a lab. The lesson notes that the demonstrated resources do not incur charges, but unused credentials still create security risk.
Quick Reference Summary
Account A: Jack
└─ Identity policy: allows sts:AssumeRole on the target role
Account B: S3 resource account
└─ IAM role
├─ Trust policy: trusts Account A and requires external ID
└─ Permissions policy: grants S3 access
Execution flow:
1. Jack uses his Account A credentials.
2. Jack calls sts:AssumeRole with the role ARN and external ID.
3. AWS STS validates the identity and trust policy.
4. STS returns temporary credentials.
5. The CLI uses those credentials to access Account B resources.
Core commands:
aws sts get-caller-identity --profile jack
aws sts assume-role --profile jack --role-arn <ACCOUNT_B_ROLE_ARN> --role-session-name <SESSION_NAME> --external-id <EXTERNAL_ID>
aws sts get-caller-identity
Flashcards
Q: An IAM user in Account A needs access to an S3 resource in Account B. Which account should contain the access role, and why?
A: The role should be created in Account B, the resource account. Its permissions define what the assumed session can do there, while its trust policy permits the Account A identity to assume it.
Q: What are the two separate policy requirements for cross-account role assumption?
A: The caller’s identity policy must allow sts:AssumeRole, and the target role’s trust policy must trust the caller’s account or principal. The role’s permissions policy separately controls actions after assumption.
Q: In a trust policy, what does arn:aws:iam::<ACCOUNT_A_ID>:root generally represent when used as the principal?
A: In this cross-account trust context, it represents principals from Account A, not exclusively the root user. Those principals still need appropriate identity permissions and must satisfy any conditions.
Q: Jack’s role-assumption request fails even though the role trusts Account A. The request omitted the external ID. What is the likely cause?
A: The trust policy requires sts:ExternalId to equal a specific value. The assume-role request must include the matching --external-id value.
Q: Which policy determines what Jack can do after he successfully assumes the role?
A: The permissions policy attached to the role in Account B. Jack’s Account A policy only authorizes the role-assumption operation and does not directly grant the role’s S3 permissions.
Q: Why should aws sts get-caller-identity be run before and after assuming a role?
A: It confirms which credentials are active. The first check can verify Jack’s profile, and the second confirms that the CLI is operating as the assumed role session.
Q: What three credential values does sts:AssumeRole return?
A: A temporary access key ID, secret access key, and session token. All three are needed to use the temporary role credentials.
Q: When should --profile jack be included on an AWS CLI command?
A: Include it whenever the command must run with Jack’s configured credentials rather than the default CloudShell or CLI identity. Without it, the command may run as a different principal.
Q: How can Jack’s policy be made more restrictive than allowing role assumption against every resource?
A: Set the sts:AssumeRole statement’s Resource to the exact role ARN in Account B. This limits Jack to the intended cross-account role.
Q: What is the difference between the role trust policy and the role permissions policy?
A: The trust policy specifies who can assume the role and under what conditions. The permissions policy specifies what the role session can access and which actions it can perform.
Q: A command using Jack’s profile lists no S3 buckets, but the assumed role should allow S3 access. What should be checked first?
A: Run aws sts get-caller-identity and verify whether the command is still using Jack’s original identity or the assumed-role credentials. Then check the target account, role permissions, and S3 resource state.
Q: Why is the session token important when configuring credentials returned by STS?
A: It is part of the temporary credential set and is required for authenticated requests. Configuring only the access key ID and secret access key is incomplete.
Q: Why is an external ID useful in a cross-account trust relationship?
A: It adds a required value to the role-assumption request, allowing the trust policy to distinguish requests that have the expected shared value. A request with a missing or incorrect value is rejected.
Practice Questions
Question 1
A user in Account A must access an S3 bucket in Account B. The user can call sts:AssumeRole, but the call returns an authorization error because the role’s trust policy references Account B as the principal. What change is required?
A. Add s3:* permissions directly to the user in Account A
B. Change the trust-policy principal to Account A
C. Remove the role’s permissions policy
D. Create the S3 bucket in Account A
Correct answer: B
The role is in Account B, but it must trust the source account or principal from Account A. Direct S3 permissions on the user do not correct the trust relationship.
Question 2
Jack’s assume-role command uses the correct role ARN and his profile, but the trust policy requires an external ID. Which command element is missing?
A. --role-session-name
B. --profile
C. --external-id
D. --policy-arn
Correct answer: C
The request must include the external ID that matches the trust policy’s sts:ExternalId condition. A policy ARN is not an argument to the assume-role operation.
Question 3
An engineer believes the CLI is using the assumed role, but an S3 operation behaves as though it is still running under the default administrator identity. What is the best first diagnostic step?
A. Recreate the S3 bucket
B. Run aws sts get-caller-identity
C. Delete the trust policy
D. Attach AmazonS3FullAccess to Jack
Correct answer: B
aws sts get-caller-identity reveals the active principal. This identifies whether the CLI is using the default identity, Jack’s profile, or the assumed-role session.
Question 4
A security review finds that Jack can assume any role because his identity policy permits sts:AssumeRole on Resource: "*". The requirement is for Jack to use only the S3 access role in Account B. What is the best improvement supported by the lesson?
A. Replace the role with an IAM user in Account B
B. Restrict the resource to the specific role ARN
C. Remove the role trust policy
D. Put Jack’s access keys into the S3 bucket
Correct answer: B
Restricting the sts:AssumeRole resource to the intended role ARN applies least privilege to the role-assumption permission.
Question 5
After a successful assume-role call, the engineer exports the access key ID and secret access key but omits the session token. Requests using the credentials fail. What explains the failure?
A. Assumed roles cannot access S3
B. The role session name must be the IAM user name
C. Temporary STS credentials require the session token as well
D. The external ID must be exported as an environment variable
Correct answer: C
STS returns a three-part temporary credential set. The session token must be supplied along with the access key ID and secret access key.
WordPress Metadata
Suggested Slug:
aws-cross-account-s3-access-iam-role-sts
Meta Description:
Learn how to configure secure cross-account Amazon S3 access using IAM trust policies, identity policies, external IDs, AWS STS, and temporary credentials.
Tags:
AWS IAM, Amazon S3, AWS STS, cross-account access, IAM roles, trust policies, identity-based policies, external ID, AWS CLI, CloudShell, temporary credentials, SOA-C03