Cross-Account Access Management
Complete the full lesson to earn 25 points
Work through each section, then tap “Mark as Complete” on the last one.
✦ Skip the page breaks and see fewer ads — read each lesson on a single page with Pro
Security Controls: Mastering Cross-Account Access Management
Introduction: The Challenge of Distributed Environments
In the early days of cloud computing, organizations often operated within a single account or project. This simplified management but created massive "blast radius" risks, where a single misconfiguration or compromised credential could expose an entire company's infrastructure. Today, the industry standard has shifted toward multi-account architectures. By isolating workloads into separate accounts, organizations gain better billing transparency, clearer security boundaries, and more granular control over resource limits. However, this architectural shift introduces a significant challenge: how do you allow users and services in one account to safely and efficiently access resources in another?
Cross-account access management is the discipline of granting temporary, permission-limited access between isolated environments without resorting to long-lived credentials. It is the backbone of modern cloud governance. If you fail to implement this correctly, you end up with "credential sprawl," where developers store access keys in plaintext files, hardcode secrets into scripts, or bypass security controls entirely to get their work done. This lesson explores the mechanics of cross-account access, the principle of least privilege in distributed systems, and the technical implementation details required to build a secure, scalable access model.
Understanding the Trust Relationship
At its core, cross-account access is built on the concept of a "Trust Relationship." One account (the Resource Account) must explicitly decide to trust an identity from another account (the Identity Account). This trust is not implicit; it is a deliberate configuration that acts as a gatekeeper. When a user in the Identity Account wants to access the Resource Account, they are not logging in with a new password. Instead, they are assuming a role—a temporary set of permissions that exists only for a short duration.
This process relies heavily on an identity provider or a centralized Security Token Service (STS). The flow typically involves the Identity Account requesting a security token from the Resource Account, proving its identity, and then receiving temporary, limited-privilege credentials. This approach eliminates the need for managing user accounts in every single sub-account, which would be an operational nightmare. By centralizing identity management, you can enforce Multi-Factor Authentication (MFA), rotation policies, and audit logging in one place, while the Resource Accounts remain focused on enforcing granular access policies.
Callout: Identity Account vs. Resource Account It is helpful to distinguish between the two roles in this relationship. The Identity Account is the "Source," where the human or machine entity resides. This is typically your central IAM (Identity and Access Management) hub. The Resource Account is the "Destination," where the data, databases, or compute resources live. The Resource Account defines the policy, but the Identity Account provides the subject that requests access.
The Mechanics of Role Assumption
To understand how this functions, we need to look at the two distinct policies involved in any cross-account interaction. You cannot have a functional setup without both of these working in tandem.
1. The Trust Policy (The Resource Account)
This is an identity-based policy attached to a role in the Resource Account. It defines who is allowed to assume the role. It essentially says, "I trust the Identity Account, and specifically, I trust this specific user or group within that account." Without this, the Resource Account will reject any incoming request, regardless of what permissions the user has in their home account.
2. The Permissions Policy (The Resource Account)
This policy defines what the role is allowed to do once it has been assumed. This is where you apply the principle of least privilege. Even if a user has full administrative rights in their Identity Account, they cannot perform actions in the Resource Account unless those specific actions are explicitly listed in this policy.
3. The Identity Policy (The Identity Account)
This is a policy attached to the user or group in the Identity Account. It grants them permission to "assume" the role defined in the Resource Account. If this policy is missing, the user will receive an "Access Denied" error before they even reach the Resource Account, because their own account will block the attempt to initiate the cross-account call.
Implementation: A Step-by-Step Guide
Let’s walk through a practical scenario: A Data Science team in an "Analytics" account needs to read files from an S3 bucket located in a "Production" account.
Step 1: Configuring the Resource Account (Production)
In the Production account, you create an IAM Role (e.g., ReadOnlyAccessRole). You must attach a Trust Policy that explicitly permits the Analytics account to assume it.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "ProjectX-Analytics-Team"
}
}
}
]
}
Explanation of the code:
Principal: This defines the trusted account. Using the:rootidentifier means any user in the Analytics account could potentially assume the role, provided they have permission from their own account.Condition: This is a security best practice. TheExternalIdacts as a secret token that helps mitigate the "Confused Deputy" problem. It ensures that even if someone has broad permissions, they must know this specific identifier to successfully assume the role.
Step 2: Defining Permissions in the Resource Account
Next, attach a permissions policy to that same role. This policy limits the scope to only what is necessary.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:Get*",
"s3:List*"
],
"Resource": [
"arn:aws:s3:::production-data-bucket",
"arn:aws:s3:::production-data-bucket/*"
]
}
]
}
Step 3: Configuring the Identity Account (Analytics)
Finally, you must grant the specific user or group in the Analytics account the right to call the sts:AssumeRole action on the Production role.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::987654321098:role/ReadOnlyAccessRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "ProjectX-Analytics-Team"
}
}
}
]
}
Note: Always use the
Conditionblock withsts:ExternalIdwhen sharing resources with third-party vendors or between accounts you don't fully control. It prevents other accounts from accidentally or maliciously guessing the role name and attempting to assume it.
The "Confused Deputy" Problem
The "Confused Deputy" is a classic security vulnerability in cross-account access. It occurs when a privileged entity (the deputy) is tricked into using its authority to perform an action on behalf of an unauthorized party. In the cloud, this happens if you rely solely on the Account ID to grant trust. If an attacker manages to create a resource in their own account and tricks your system into interacting with it, they might be able to exploit the trust relationship.
To prevent this, you should always use specific conditions in your trust policies. Beyond ExternalId, you can also use aws:SourceArn or aws:SourceAccount. These conditions restrict the access request to only come from specific, known origins. By narrowing the scope of the trust relationship, you ensure that the "Deputy" (the role being assumed) cannot be misused by an unauthorized caller, even if they have the right account ID.
Best Practices for Scalable Access
Managing access for hundreds of users across dozens of accounts requires automation. Doing this manually in the console is a recipe for error. Here are the industry standards for managing this at scale:
- Use Infrastructure as Code (IaC): Never create roles manually. Use Terraform, CloudFormation, or Pulumi to define your roles and policies. This allows you to peer-review security changes before they are applied.
- Centralized Identity Management: Use a single source of truth for identities, such as an SSO (Single Sign-On) provider or a central IAM account. Connect your users to this central hub, and use the hub to assume roles in downstream accounts.
- Audit Everything: Enable CloudTrail in every account. Cross-account access events are logged in both the Identity Account (who requested the role) and the Resource Account (who assumed the role). Use these logs to create alerts for suspicious access patterns.
- Session Duration Management: When creating roles, set the maximum session duration to the minimum time required for the task. If a developer only needs one hour to perform a data migration, don't leave the role open for the default 12-hour window.
- Regular Access Reviews: Implement a process to review trust policies quarterly. Remove roles that are no longer in use. Orphaned roles are a prime target for attackers, as they often have broad permissions and are rarely monitored.
Comparing Access Models
The following table summarizes the different ways to handle access between accounts:
| Access Method | Security Level | Scalability | Use Case |
|---|---|---|---|
| Shared Credentials | Very Low | Poor | Never recommended (Hardcoded keys) |
| Cross-Account Roles | High | High | Standard for inter-account communication |
| Identity Federation | High | Very High | Large organizations with centralized SSO |
| Resource-Based Policies | Medium | Medium | Simple, direct access to specific resources (e.g., S3 buckets) |
Callout: Resource-Based vs. Identity-Based Identity-based policies (IAM Roles) are generally preferred because they are centralized and easier to manage. However, resource-based policies (like S3 Bucket Policies) are sometimes necessary when you need to grant access to a service that doesn't support assuming roles, or when you need to provide public-read access to a specific object. Use resource-based policies sparingly to avoid "permission sprawl" where access logic is scattered across thousands of individual resources.
Common Pitfalls and How to Avoid Them
1. Over-privileged Roles
A common mistake is creating "Admin" roles for cross-account access to save time. If a developer needs to read a log, give them s3:GetObject on that specific path, not s3:* on the whole bucket, and certainly not full account access.
- The Fix: Use policy simulators to test your permissions before deploying. Always start with a "Deny All" approach and add only the specific actions required.
2. Ignoring the "Deny" Logic
Remember that in cloud IAM, a "Deny" always overrides an "Allow." If you have a global policy that denies access to a sensitive resource, adding an "Allow" in a cross-account role will not work.
- The Fix: Audit your Service Control Policies (SCPs) at the organization level. If you are struggling to get a cross-account connection working, check if an SCP is blocking the action at the account boundary.
3. Missing the Trust Policy Update
Developers often create a role and define the permissions, but they forget to update the trust policy. The role exists, the user has permission to assume it, but the role itself refuses to be assumed.
- The Fix: Always verify the trust policy in the "Trust Relationships" tab of the IAM role. Ensure the
Principalmatches the exact account ID or ARN you intend to trust.
4. Hardcoding Account IDs
Hardcoding account IDs in your code or scripts makes your infrastructure brittle. If you move from a staging account to a production account, your code will break.
- The Fix: Use environment variables or configuration files to inject account IDs at runtime. Keep your code environment-agnostic so it can be deployed across multiple accounts without modification.
Advanced Scenario: Chained Access
Sometimes, you may need to access a resource in Account C, which requires going through Account B, starting from Account A. This is known as role chaining. While it is technically possible, it is generally discouraged because it obscures the audit trail and makes debugging difficult.
If you find yourself needing to chain roles, reconsider your architecture. Can you give Account A direct access to Account C? If not, can you use a service that acts as a gateway or proxy? If you must use chaining, remember that the session duration for the final role will be limited by the shortest duration of any role in the chain. Furthermore, the final role must trust the intermediary role, which creates a complex web of dependencies that is hard to secure.
Troubleshooting Cross-Account Access
When a cross-account access request fails, the error messages can be intentionally vague for security reasons (to prevent revealing information about existing resources to unauthorized users). Here is a checklist to debug these issues:
- Check the Identity Policy: Does the user/role in the source account have the
sts:AssumeRolepermission for the destination role ARN? - Check the Trust Policy: Does the destination role have a trust relationship that explicitly allows the source account/user?
- Check the Resource Policy: Is there a resource-based policy (like an S3 bucket policy) that explicitly denies the access?
- Check Service Control Policies (SCPs): Does the Organization level policy block the
stsaction? - Check MFA Requirements: If the destination role requires MFA, did the user provide an MFA token during the
assume-rolecall?
Practical Example: Using the CLI
If you are a developer, you will likely interact with cross-account roles using the Command Line Interface (CLI). To assume a role, you don't just log in again; you use the STS service to get temporary credentials.
# Get temporary credentials for the cross-account role
aws sts assume-role \
--role-arn arn:aws:iam::987654321098:role/ReadOnlyAccessRole \
--role-session-name MySessionName \
--external-id ProjectX-Analytics-Team
The output of this command will provide you with an AccessKeyId, SecretAccessKey, and SessionToken. You can then export these as environment variables in your terminal to perform actions in the destination account.
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
Tip: You can automate this further by adding a profile to your
~/.aws/configfile. This allows you to useaws s3 ls --profile production-datainstead of manually exporting environment variables. This is much safer and prevents the accidental leakage of credentials into your shell history.
The Role of Service Control Policies (SCPs)
In large organizations, individual account administrators might try to grant permissions that are too broad. Service Control Policies (SCPs) allow the organization's central security team to set "guardrails" that apply to all accounts, regardless of what the local account administrator does.
For example, you could create an SCP that denies the ability to delete backups in any account, or denies the ability to create resources in regions that are not compliant with local data laws. When managing cross-account access, ensure your SCPs do not inadvertently block the sts:AssumeRole action. If they do, no amount of identity-based policy configuration will fix the issue.
Security Controls and Compliance
Cross-account access management is not just about convenience; it is a critical component of compliance frameworks like SOC2, HIPAA, and PCI-DSS. Auditors will look for evidence that:
- Access is granted based on the principle of least privilege.
- Access is logged and monitored.
- Credentials are temporary and automatically expire.
- There is a clear process for revoking access when a user leaves the organization or changes teams.
By building your cross-account strategy around temporary roles rather than static credentials, you satisfy many of these compliance requirements by default. You remove the need to manage secret rotation, you get a built-in audit trail of every access event, and you ensure that access is tied to a verifiable identity.
Summary: Designing for the Future
As your organization grows, the number of accounts will likely increase. This is a healthy trend, as it promotes agility and reduces blast radii. However, the complexity of managing these accounts can quickly become a bottleneck. By mastering cross-account access management, you turn this complexity into a competitive advantage. You create a system where security is baked into the architecture, not bolted on as an afterthought.
Remember that security is a process, not a destination. As your cloud usage evolves, revisit your trust policies and roles. Use the tools available to you—like IaC, centralized SSO, and automated logging—to keep your environment lean and secure. Avoid the temptation to take shortcuts like shared keys, and always prioritize the principle of least privilege.
Key Takeaways
- Trust is Explicit: Cross-account access requires a bidirectional agreement. The Identity Account must grant permission to initiate the request, and the Resource Account must explicitly trust the Identity Account via a Trust Policy.
- Use Temporary Credentials: Never share static access keys across accounts. Always use
sts:AssumeRoleto generate short-lived, session-based credentials that automatically expire. - The Principle of Least Privilege: Apply granular permissions to every role. If an entity only needs to read a file, ensure the policy only allows
s3:GetObjectand nothing else. - Mitigate the Confused Deputy: Use
ExternalIdand other conditional constraints (likeaws:SourceArn) in your trust policies to ensure that roles are only assumed by authorized entities under expected conditions. - Centralize Identity: Use a central identity hub (SSO) to manage users and groups. This reduces the operational overhead of managing identities in every sub-account and makes auditing significantly easier.
- Automate with IaC: Treat your IAM roles and policies as code. Use version control to track changes, and perform peer reviews to catch misconfigurations before they reach production.
- Monitor and Audit: Enable logging (such as CloudTrail) in all accounts. Regularly review access patterns and prune unused roles to reduce your attack surface.
- Understand SCPs: Be aware of organizational-level guardrails. Ensure your access management strategy aligns with the broader security policies set by your organization's central governance team.
By following these principles, you will be well-equipped to design, implement, and maintain a secure cross-account access model that scales with your organization's needs. The key is to remain disciplined, leverage automation, and always assume that security boundaries should be verified rather than assumed.
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