IAM Roles and Cross-Account Access
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
Identity and Access Management: IAM Roles and Cross-Account Access
Introduction: The Foundation of Secure Cloud Operations
In the modern landscape of cloud computing, security is no longer an optional overlay; it is the fundamental architecture upon which all services must be built. At the heart of this security infrastructure lies Identity and Access Management (IAM). IAM acts as the gatekeeper, verifying who is requesting access to a resource and determining exactly what they are permitted to do once they arrive. While managing users and permissions within a single account is straightforward, the complexity grows exponentially when you operate across multiple accounts.
Cross-account access is a common requirement for organizations that want to separate production environments from development, manage centralized logging, or provide third-party vendors with temporary, scoped access to specific resources. Understanding how to manage these relationships securely is the difference between a controlled, audit-ready environment and a system vulnerable to unauthorized lateral movement. This lesson explores the mechanics of IAM roles and the specific workflows required to enable secure cross-account communication.
Understanding IAM Roles: Beyond Static Credentials
When we talk about IAM, we often think of users—individuals with passwords or access keys. However, relying on long-term credentials for every interaction is a significant security risk. If a developer's access key is accidentally committed to a public repository, the entire environment is compromised. IAM roles solve this problem by providing temporary, short-lived security tokens instead of static credentials.
An IAM role is an identity that does not have a password or access key associated with it. Instead, it is meant to be assumed by anyone who needs it. When a principal (a user, a service, or an application) assumes a role, they receive temporary security credentials that expire after a set duration. This "just-in-time" access model significantly reduces the blast radius if credentials are leaked, as they will naturally time out and become useless.
The Anatomy of an IAM Role
Every IAM role consists of two primary policy components that work in tandem to determine if access is granted:
- Trust Policy (Resource-based): This defines who is allowed to assume the role. It essentially acts as the "front door" policy. It specifies the principals (users or accounts) that are trusted to take on this identity.
- Permissions Policy (Identity-based): This defines what the role is allowed to do once it is assumed. This is a standard IAM policy that lists the specific actions (like
s3:GetObjectorec2:DescribeInstances) that are permitted.
Callout: Trust Policies vs. Permissions Policies It is helpful to think of the Trust Policy as the "Who can enter?" agreement, while the Permissions Policy is the "What can they do inside?" contract. You must have both configured correctly; if you trust a user but don't give the role any permissions, the user will successfully assume the role but will be unable to perform any meaningful actions.
The Mechanics of Cross-Account Access
Cross-account access allows a user in one account (the "Trusting Account") to perform actions in a different account (the "Trusted Account") without needing to manage separate user identities in every single environment. This pattern is essential for centralized security management, where you might want to give a centralized security audit team access to all your production accounts without creating individual users for them in every account.
The Step-by-Step Workflow
To enable cross-account access, you must follow a specific sequence involving both the account that owns the resource and the account that owns the user.
- Define the Role in the Target Account (Trusting Account): Create an IAM role that includes a Trust Policy allowing the external account's root ID or a specific user to assume it.
- Assign Permissions: Attach the necessary permissions policies to that role, defining the scope of what the external user can do.
- Grant Permission in the Source Account (Trusted Account): The user in the source account must have an IAM policy attached to them that explicitly grants them permission to perform the
sts:AssumeRoleaction on the specific Role ARN created in step 1.
Note: The
sts:AssumeRoleaction is the bridge that allows the switch. Without this specific permission in the source account, the request to switch roles will be denied before it even reaches the destination account.
Practical Implementation: A Step-by-Step Guide
Let's walk through a scenario where an administrator in "Account A" needs to manage an S3 bucket in "Account B."
Step 1: Configure the Role in Account B (The Resource Owner)
In Account B, you create a role named CrossAccountS3AccessRole. The trust policy must explicitly whitelist Account A.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:root"
},
"Action": "sts:AssumeRole"
}
]
}
Explanation: The Principal block points to the AWS Account ID of Account A. By using the root ARN, we are saying that Account A is trusted to manage which of its users can assume this role.
Step 2: Set Permissions for the Role
Next, attach a policy to this role that grants the necessary S3 access.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::my-secure-data-bucket"
}
]
}
Step 3: Configure the User in Account A (The Requestor)
In Account A, the user (or group) needs permission to initiate the assumption of that role.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::444455556666:role/CrossAccountS3AccessRole"
}
]
}
Warning: Always use the specific ARN of the role in the Resource field. Avoid using wildcards (*) here, as it would allow the user to assume any role in any account, which is a massive security vulnerability.
Best Practices for Secure Cross-Account Operations
Security is rarely about a single configuration; it is about the posture you maintain over time. As you scale, managing hundreds of cross-account roles can become messy. Follow these industry-standard practices to maintain control.
1. Implement External IDs for Third Parties
When you grant access to a third-party vendor, always include an ExternalId in your Trust Policy. This prevents the "Confused Deputy" problem, where a malicious actor might try to trick a service into assuming a role that it shouldn't. The ExternalId acts as a secret password that only you and your trusted vendor know, ensuring that only the specific vendor can assume the role.
2. Enforce MFA for Role Assumption
For sensitive roles, you should require Multi-Factor Authentication (MFA) in the Trust Policy. This ensures that even if a user’s password is compromised, the attacker still cannot assume the role without the physical MFA device.
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
3. Use Least Privilege
Only grant the exact permissions required for the task. If a user only needs to read logs from a bucket, do not give them s3:* permissions. Regularly audit your roles to ensure that permissions haven't drifted into "over-privileged" territory.
4. Leverage Service Control Policies (SCPs)
If you are using an organization structure, use Service Control Policies to set guardrails. SCPs act as a ceiling for permissions; even if an IAM role has full administrator access, an SCP can deny it the ability to delete logs or modify security settings across all accounts in the organization.
Comparison Table: IAM Features for Cross-Account Access
| Feature | Role Assumption | Resource-Based Policy |
|---|---|---|
| Primary Use | Temporary access for users/services | Direct access to resources (S3, SQS) |
| Credential Type | Temporary tokens | Uses requester's existing identity |
| Complexity | Medium | Low |
| Best For | Administrative tasks, cross-account CLI | Public/Shared data access, S3 buckets |
| Security | High (temporary, audit-friendly) | Moderate (must manage access carefully) |
Common Pitfalls and How to Avoid Them
Even experienced engineers frequently stumble when configuring cross-account access. Below are the most common issues and the strategies to mitigate them.
The "Access Denied" Loop
The most common error is the dreaded AccessDenied message. This usually happens because one half of the equation is missing. Remember: the user needs the sts:AssumeRole permission in their home account, AND the role needs a Trust Policy that allows that user's account to assume it. If either is missing, access will fail.
Over-relying on Wildcards
Using Resource: "*" in an AssumeRole policy is a critical mistake. It effectively grants the user permission to become any identity in any account if they can guess the role name. Always restrict the Resource field to the specific Role ARN.
Forgetting to Audit Roles
Roles are often created for a specific project or vendor and then forgotten. Over time, these "orphan" roles become security holes. Implement a tagging strategy for all roles (e.g., Owner, Project, ExpirationDate). Use automated scripts to identify and remove roles that haven't been used in the last 90 days.
Ignoring the "Confused Deputy" Problem
If you are building a service that interacts with other accounts, always use the ExternalId parameter. Without it, you are vulnerable to attacks where a malicious actor provides your service with the ARN of a role they own, tricking your service into performing actions on their behalf.
Callout: The Confused Deputy Explained A "Confused Deputy" occurs when a privileged entity (like your application) is tricked into using its permissions to perform an action on behalf of an unauthorized user. By requiring an
ExternalId, you ensure that the entity assuming the role can prove they are authorized by the account owner, preventing unauthorized parties from hijacking the trust relationship.
Advanced Topic: Using IAM Roles with EC2 and Lambda
While much of our discussion has focused on users assuming roles, the most common use case in production is services assuming roles. When you run code on an EC2 instance or in a Lambda function, you should never hardcode credentials. Instead, you attach an IAM role to the resource itself.
Instance Profiles
For EC2, you attach an "Instance Profile." This is a container for an IAM role that the EC2 service can pass to the instance. The instance then automatically fetches temporary credentials from the metadata service. Your application code doesn't need to know how to handle credentials; the AWS SDKs automatically look for these temporary credentials in the environment.
Lambda Execution Roles
Similarly, every Lambda function must have an execution role. This role defines what the function can do—such as writing logs to CloudWatch or reading from a database. When you need a Lambda function to perform a cross-account action, you simply grant the Lambda's execution role permission to AssumeRole into the target account.
Automating Cross-Account Access with Infrastructure as Code
Manually configuring roles across multiple accounts is tedious and error-prone. In a mature environment, you should use Infrastructure as Code (IaC) tools like Terraform or CloudFormation to define your IAM relationships.
Terraform Example
When using Terraform, you can manage the role in the target account and the permission in the source account within the same repository, provided your Terraform provider has access to both accounts.
# In the Target Account (Account B)
resource "aws_iam_role" "cross_account_role" {
name = "CrossAccountRole"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { AWS = "arn:aws:iam::111122223333:root" }
}]
})
}
# In the Source Account (Account A)
resource "aws_iam_policy" "assume_role_policy" {
name = "AssumeRolePolicy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Resource = aws_iam_role.cross_account_role.arn
}]
})
}
Explanation: By using IaC, you create a version-controlled record of who has access to what. If you need to change a policy, you update the code, run a plan, and apply it. This removes the "human factor" from security configurations and ensures that your environment is reproducible.
Auditing and Monitoring IAM Activity
Once your cross-account access is set up, you must monitor it. You cannot secure what you cannot see.
CloudTrail Logs
AWS CloudTrail is your primary source of truth for IAM activity. Every time a role is assumed, an AssumeRole event is logged in the target account. You should configure alerts for these events, especially for sensitive roles. If you see a spike in AssumeRole activity from an unexpected IP address, you know immediately that you have a potential security incident.
IAM Access Analyzer
Use the IAM Access Analyzer tool to identify roles that have external access. It will automatically flag any role that allows access from outside your organization. This is an essential tool for catching misconfigurations before they are exploited.
Best Practices for Monitoring:
- Centralize Logs: Send CloudTrail logs from all accounts to a single, dedicated security account.
- Alert on Anomalies: Use services like Amazon GuardDuty to detect unusual API calls, such as an identity assuming a role from a known malicious IP.
- Regular Reviews: Conduct quarterly reviews of all cross-account roles. If a project has finished, delete the associated roles immediately.
Frequently Asked Questions (FAQ)
Q: Can I use a single IAM role for multiple accounts?
A: Yes, you can list multiple account IDs in the Principal block of your Trust Policy. However, it is often cleaner to create separate roles for separate accounts to maintain better audit trails and granular control.
Q: What happens if I delete an account that is trusted by a role? A: The Trust Policy will still contain the account ID, but the trust relationship will effectively become inert because the principal no longer exists. It is good practice to clean up these stale trust policies during your regular maintenance cycles.
Q: Is there a limit to how many roles a user can assume? A: There is no hard limit on the number of roles, but there are limits on the session duration and the size of the policy document. Most users will never hit these limits, but keep them in mind for highly complex, nested role-assumption chains.
Q: Can I assume a role from another region? A: Yes, IAM roles are global, and you can assume a role from any region. The temporary credentials you receive are also valid globally (unless you restrict them via policy).
Summary and Key Takeaways
Mastering IAM roles and cross-account access is a journey that moves from understanding basic identity concepts to managing complex, multi-account security architectures. By moving away from static user credentials and embracing the power of temporary, role-based security, you create a system that is both agile and resilient.
Key Takeaways for Your Security Strategy:
- Prioritize Temporary Access: Always favor IAM roles over long-term IAM user access keys. Roles provide temporary, short-lived security tokens that significantly reduce the risk of credential leakage.
- The Two-Way Trust Relationship: Remember that cross-account access requires configuration in both the source (Trusted) and target (Trusting) accounts. Both the
AssumeRolepermission and the Trust Policy must be correctly synchronized. - Enforce Least Privilege: Never use wildcards in your
AssumeRoleresource definitions. Explicitly name the ARNs of the roles to which you are granting access to prevent lateral movement. - Use External IDs for Vendors: When dealing with third-party access, always implement an
ExternalIdin your Trust Policy to prevent "Confused Deputy" security vulnerabilities. - Monitor and Audit: Use CloudTrail to log all
AssumeRoleevents and use tools like the IAM Access Analyzer to identify external access patterns. - Automate with IaC: Keep your IAM configurations in Infrastructure as Code. This creates an audit trail, prevents manual configuration drift, and allows for rapid, consistent deployments across multiple accounts.
- Review Regularly: Access requirements change as projects evolve. Establish a cadence for reviewing and pruning unused roles and permissions to keep your environment clean and secure.
By internalizing these principles, you are not just managing access; you are building a secure foundation that allows your organization to scale with confidence. Security is a continuous process of refinement, and the IAM framework provides the tools necessary to adapt to any requirement, provided you apply them with diligence and care.
Continue the course
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