Cross-Account Access
Complete the full lesson to earn 25 points
Work through each section, then tap “Mark as Complete” on the last one.
Identity and Access Management: Mastering Cross-Account Access
Introduction: The Necessity of Cross-Account Access
In modern cloud computing, organizations rarely operate within a single, isolated environment. As systems grow in complexity, teams often find themselves managing multiple cloud accounts—perhaps one for production, another for staging, a separate one for sandbox experimentation, and perhaps even dedicated accounts for security logging or shared services. While this isolation is a core principle of security and billing management, it creates a significant operational challenge: how do you allow a user or an application in one account to perform tasks in another without compromising security?
This is where Cross-Account Access comes into play. Cross-account access is the practice of granting an identity (a user, a role, or an application) in one cloud account the permission to access resources located in a different cloud account. Without a structured approach to this, engineers are often tempted to create "hard-coded" credentials—long-lived access keys stored in configuration files or environment variables—which are a primary vector for security breaches.
Understanding cross-account access is not just a technical requirement; it is a fundamental pillar of the "Principle of Least Privilege." By properly configuring trust relationships and temporary security tokens, you can ensure that your systems remain agile while maintaining a rigorous security posture. This lesson will guide you through the mechanics of cross-account access, the underlying trust models, and the best practices required to implement it safely in a production environment.
The Mechanics of Trust: How It Works
At its core, cross-account access relies on a "Trust Relationship." Think of this as a formal agreement between two entities: the "Trusting Account" (the account that owns the resources) and the "Trusted Account" (the account where the user or service originates).
When you set up cross-account access, you are essentially telling the Trusting Account: "I trust any entity from the Trusted Account that assumes this specific role." This process is almost always facilitated through a mechanism called "Role Assumption." Instead of creating a permanent user in every account, you create a role in the destination account with a specific set of permissions and a "Trust Policy" that defines who is allowed to take on that role.
The Role Assumption Flow
- The Request: An identity in the Trusted Account requests a temporary security token by calling an "AssumeRole" operation.
- The Validation: The cloud provider checks the Trust Policy attached to the role in the Trusting Account. It verifies if the requester has the right to assume the role.
- The Issuance: If valid, the provider issues a set of temporary, short-lived security credentials (an access key ID, a secret access key, and a session token).
- The Action: The identity uses these temporary credentials to perform authorized actions in the Trusting Account.
Callout: Identity vs. Resource It is helpful to distinguish between Identity-based policies and Resource-based policies. Identity-based policies are attached to the person or service (like a user or a role) and define what they can do. Resource-based policies are attached to the resource itself (like an S3 bucket or a KMS key) and define who can access that resource. Cross-account access often involves a combination of both: an identity assumes a role, and that role might interact with a resource that has its own cross-account policy.
Practical Implementation: A Step-by-Step Guide
To implement cross-account access, you must perform actions in both the destination (Trusting) account and the source (Trusted) account. For this example, let’s assume you are using an Identity account (Account A) that needs to access an S3 bucket in a Production account (Account B).
Step 1: Configure the Trusting Account (Account B)
In the account where the resources reside, you must create an IAM Role that defines the permissions required.
- Create a new IAM Role.
- Select "Another AWS Account" as the trusted entity type.
- Enter the Account ID of the Trusted Account (Account A).
- Attach the necessary permissions policy (e.g.,
s3:ListBucketands3:GetObjecton the specific bucket). - Name the role, for example,
CrossAccountS3AccessRole.
The resulting Trust Policy (JSON) will look something like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "sts:AssumeRole",
"Condition": {}
}
]
}
Step 2: Configure the Trusted Account (Account A)
Now, you must grant the user or service in Account A the permission to perform the sts:AssumeRole action. Without this permission, the user will be denied, even if Account B trusts them.
Attach this policy to the user or role in Account A:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::987654321098:role/CrossAccountS3AccessRole"
}
]
}
Step 3: Assuming the Role
Once configured, the user in Account A can now switch roles. If using the Command Line Interface (CLI), they would typically update their ~/.aws/config file to include a profile that automatically assumes the role:
[profile prod-access]
role_arn = arn:aws:iam::987654321098:role/CrossAccountS3AccessRole
source_profile = default
When the user runs aws s3 ls --profile prod-access, the CLI automatically calls AssumeRole, receives the temporary credentials, and executes the command in Account B.
Note: Temporary credentials issued by
AssumeRoleusually expire after one hour by default. You can adjust the session duration when calling the API, up to the maximum duration configured for the role.
Securing Cross-Account Access: Best Practices
Implementing cross-account access is a powerful tool, but it also opens a "door" between your accounts. If that door is left unlocked or poorly monitored, it becomes a security risk.
Use External IDs
When you grant access to a third-party service or a partner, they might ask you to trust their account. To prevent the "Confused Deputy" problem, always use an ExternalID. An ExternalID is a unique string that acts as a password for the trust relationship. It ensures that the third party cannot assume your role unless they provide the secret string you shared with them.
Restrict by Condition
Don't just trust the entire account. Use conditions in your Trust Policy to restrict access to specific users, specific IP addresses, or even specific Multi-Factor Authentication (MFA) requirements.
Example of an MFA-restricted trust policy:
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Action": "sts:AssumeRole",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
Regularly Audit Trust Relationships
IAM policies are easy to write but hard to track as an organization scales. Use tools like CloudTrail or native cloud security audit services to monitor who is assuming roles and when. If a role hasn't been used in 90 days, delete it.
Avoid Wildcards in Permissions
When defining the permissions policy for the cross-account role, avoid using Action: "*" or Resource: "*". Be explicit. If the role only needs to read from one specific bucket, grant access only to that bucket. This limits the "blast radius" if the role is ever compromised.
Callout: The Confused Deputy Problem The "Confused Deputy" is a classic security vulnerability where a more privileged entity (the deputy) is tricked by a less privileged entity into misusing its authority. In cross-account access, this happens if you trust an entire account without verifying that the specific user in that account has the authority to make the request. Always use ExternalIDs and specific conditions to ensure only authorized entities can perform the assumption.
Comparison: IAM Users vs. IAM Roles for Cross-Account
Many beginners struggle with the choice between creating a user in the destination account versus using a role for cross-account access. The following table highlights why roles are the industry standard.
| Feature | IAM User | IAM Role |
|---|---|---|
| Credential Type | Long-lived (Access Key/Secret) | Temporary (Token) |
| Management Overhead | High (Rotation required) | Low (Managed by provider) |
| Security Risk | High (Key leakage risk) | Low (Automatically expires) |
| Flexibility | Static | Dynamic/On-demand |
| Best Practice | Avoid for cross-account | Recommended approach |
Common Pitfalls and How to Avoid Them
Even with the best intentions, cross-account configurations can fail. Here are the most common issues you will encounter in the field.
1. The "Access Denied" Loop
The most frequent issue is a mismatch in permissions. Remember: the identity must have sts:AssumeRole permissions, and the role must have the permissions to perform the actual resource task. If you get an "Access Denied" error, check:
- Does the identity have permission to call
sts:AssumeRoleon the target ARN? - Does the role have the required resource-level permissions (e.g.,
s3:GetObject)? - Is there an explicit "Deny" statement in any policy that might be overriding the "Allow"?
2. Missing Trust Relationship
Sometimes engineers create the role but forget to update the Trust Policy. If the Trust Policy doesn't explicitly list the account ID of the requester as a principal, the request will be rejected before it even reaches the permission-check phase. Always double-check the Principal block in your Trust Policy.
3. Ignoring Session Duration
If your automated scripts or CI/CD pipelines take longer than one hour to run, they will fail because the default session duration for a role is one hour. You must explicitly request a longer duration if your workload requires it.
4. Over-Privileged Roles
Creating one "God Mode" role that has access to everything in the destination account is a common shortcut that leads to disaster. Always create granular roles. For example, create an S3ReadOnlyRole and an S3FullAccessRole rather than one AdminRole.
Troubleshooting Checklist
If you find yourself stuck, go through this checklist in order:
- Verify Account IDs: Are you using the correct 12-digit account IDs? A single digit typo will cause immediate failure.
- Check the ARN: Does the role ARN in the
AssumeRolecall exactly match the role ARN in the destination account? - Review CloudTrail: Search for
AssumeRoleevents in the destination account. The event logs will show you exactly who made the request and why it might have been denied. - Test via CLI: Always test manually using the CLI before integrating into automated code. The CLI error messages are often more descriptive than those found in web consoles.
Advanced Scenario: Cross-Account KMS Access
A particularly tricky area of cross-account access involves encrypted data. If your S3 bucket in Account B is encrypted with a Customer Master Key (CMK) in the Key Management Service (KMS), simply granting s3:GetObject is not enough. You must also grant the role permission to use the KMS key.
This requires two parts:
- The IAM role in Account A must have
kms:Decryptpermission for the key in Account B. - The KMS Key Policy in Account B must explicitly trust the IAM role in Account A.
If you omit the KMS key policy update, the user will be able to list the bucket but will receive an "Access Denied" error when trying to download or view the content because they lack the "key" to unlock the data.
Best Practices for Scaling Across Many Accounts
As your organization grows to dozens or hundreds of accounts, managing individual trust relationships becomes impossible. This is where "Identity Federation" and "Organization-level controls" come in.
- Use an Identity Provider (IdP): Instead of local IAM users, use a central identity provider (like Okta, Azure AD, or AWS IAM Identity Center). This allows you to manage users in one place and grant them access to roles across all accounts.
- Standardize Role Names: Adopt a naming convention for your cross-account roles (e.g.,
ReadOnlyAccess,SupportAccess). This makes it significantly easier to automate deployments using Infrastructure as Code (IaC) tools like Terraform or CloudFormation. - Automate with IaC: Never create cross-account roles manually in the console. Use Terraform to define the role and the trust policy. This ensures that the configuration is version-controlled, peer-reviewed, and repeatable.
Summary of Key Takeaways
To master cross-account access, keep these fundamental principles in mind:
- Temporary is Secure: Always favor temporary, short-lived security tokens via
AssumeRoleover permanent, long-lived access keys. - Two-Way Trust: Remember that cross-account access requires a handshake. The source account must be allowed to assume, and the destination account must trust the source to perform that assumption.
- Principle of Least Privilege: Start with no permissions and add only what is strictly necessary. Use specific resource ARNs rather than wildcards.
- Use External IDs: When dealing with third-party partners, always use an ExternalID to prevent the Confused Deputy vulnerability.
- Monitor and Audit: Use logs to understand who is accessing what. Regularly prune unused roles to reduce your attack surface.
- Mind the KMS: When dealing with encrypted resources, remember that access control is required for both the resource (like an S3 bucket) and the encryption key (the KMS key).
- Automate Everything: Use Infrastructure as Code to manage your trust relationships to ensure consistency and prevent configuration drift across your multi-account environment.
By following these guidelines, you transform cross-account access from a high-risk security hurdle into a reliable, scalable foundation for your cloud architecture. Whether you are building a data lake shared across departments or a multi-account CI/CD pipeline, these practices will ensure your systems remain secure, compliant, and easy to manage.
Common Questions (FAQ)
Q: Can I use cross-account access to access resources in another region? A: Yes. Cross-account access works across regions. The role itself is a global entity, though the resources it accesses are regional.
Q: What happens if the source account is deleted? A: If the source account is deleted, the trust relationship becomes invalid. Any existing sessions will expire, and no new sessions can be created.
Q: Is there a limit to how many accounts can assume a single role? A: While there is no hard limit on the number of accounts in a trust policy, there is a character limit on the policy size. If you need to trust hundreds of accounts, consider using an organization-wide policy or a central identity hub.
Q: How do I handle MFA for cross-account access?
A: You can force MFA in the trust policy using the aws:MultiFactorAuthPresent condition. This requires the user to have authenticated with MFA in their home account before they can successfully assume the role in the target account.
Q: Can I assume a role from a role? A: Yes, this is known as "Role Chaining." However, keep in mind that the session duration for chained roles is limited to one hour, and you cannot pass session tags through chains as easily as in a single step. It is generally better to assume the target role directly if possible.
Enjoying the courses?
Everything stays free. Pro shows fewer ads, doubles your daily points limit so you progress twice as fast, and lets you read each lesson on one page.
- ✓ Fewer advertisements
- ✓ 2× daily points limit
- ✓ Distraction-free lessons