Hello! In our previous lessons, we've built a solid foundation in AWS IAM, covering everything from identity-based policies and roles for services to the nuances of iam:PassRole and permissions boundaries for safe delegation within an account. You've learned how to control what your users and services can do.
This lesson addresses a equally critical question for any enterprise system: how do you grant access to your resources to principals that exist outside of your AWS account? We will move from managing internal access to securely enabling cross-account collaboration. You'll learn how to configure a resource-based policy, a distinct type of IAM policy, to grant specific permissions on resources like S3 buckets to users or roles in other AWS accounts. This is a core pattern in building integrated, multi-account AWS architectures and a frequent topic on the DVA-C02 exam.
By the end of this lesson, you will be able to differentiate resource-based from identity-based policies and configure an S3 bucket policy to allow secure, fine-grained cross-account access.
Identity-Based vs. Resource-Based Policies
So far, we have focused exclusively on identity-based policies. These are policies that you attach to an IAM principal (a user, group, or role). They answer the question, "What can this principal do?"
Now, we introduce resource-based policies. These are attached directly to a resource, such as an S3 bucket, an SQS queue, or a KMS key. They answer the question, "Who can access this resource?" The most significant difference in their JSON structure is the inclusion of a Principal element, which specifies the user, account, or service that is allowed or denied access.
The following video from Digital Cloud Training provides a great overview of the different access control mechanisms in S3, setting the stage for our deep dive.
Amazon S3 Access Control - IAM Policies, Bucket Policies and ACLs
This video introduces and compares the main ways to control access to S3: IAM policies, bucket policies, and ACLs.
Watch the first part of the video to understand the conceptual differences: IAM Policies: A quick review of the identity-based policies you're already familiar with. S3 Bucket Policies: An introduction to resource-based policies and the Principal element. When to use each: This section discusses the use cases, highlighting cross-account access as a key driver for bucket policies. Authorization Logic: A crucial review of how AWS evaluates permissions, emphasizing that an explicit Deny always wins.
The "Double Handshake": A Cross-Account Access Pattern
Let's imagine a common scenario. Your application, running in Account A, owns an S3 bucket. A partner application, running in Account B, needs to read data from that bucket. The most direct way to enable this is with a resource-based policy on the S3 bucket.
You might think you could simply add a bucket policy to the bucket in Account A that says "Allow s3:GetObject for Account B." However, it's not that simple. For cross-account access to work (when not using roles), permissions must be granted on both sides.
- Resource Side (Account A): The S3 bucket policy must specify the principal in Account B that is allowed access.
- Identity Side (Account B): The IAM principal (user or role) in Account B must have an identity-based policy allowing it to perform the action on the resource in Account A.
This "double handshake" is a fundamental concept. Let's walk through this in detail.
S3 bucket access from the same and another AWS account | Tom Gregory
This article by Tom Gregory provides a clear, practical explanation of cross-account S3 access.
Please read the following sections: Start with the introduction for a refresher on the two policy types and S3 bucket policies. Then, jump to the main topic in the section titled Apply a cross-account bucket policy. Pay close attention to the part where the initial attempt fails because the policy is only on one side. This demonstrates the "double handshake" requirement perfectly.
To solidify this pattern, the official AWS documentation provides a step-by-step walkthrough that mirrors this exact scenario.
Example 2: Bucket owner granting cross-account bucket permissions - Amazon Simple Storage Service
This walkthrough details the precise steps required from both the bucket owner (Account A) and the accessing account (Account B).
Read through the following steps of the walkthrough: The summary provides a great visual and textual overview of the process. Skim the preparation to see how the two accounts and users are set up. Read Step 1, where the bucket owner creates the bucket and applies the resource-based policy granting access to Account B. Read Step 2, where the administrator in Account B creates a user and attaches an identity-based policy, completing the "handshake."
Hands-On Demo: Putting It All Together
Theory is good, but seeing it in action is better. The following video demonstrates this entire cross-account workflow using the AWS CLI, showing the exact commands and the resulting "Access Denied" errors that occur until both policies are correctly in place.
AWS S3 Cross Account Access with Bucket Policy, Public Access (Demo)
This video from Cloud With Raj provides a clear, hands-on demonstration of configuring cross-account access using an S3 bucket policy.
Watch the following segments: The introduction sets up the scenario perfectly: how do you give an external user access to your bucket without creating an IAM user for them in your account? The hands-on demo is the most important part. Watch as the presenter first tries to access the bucket and gets "Access Denied." Then, see how they add a ListBucket permission via a bucket policy, which partially works, and finally add GetObject to complete the access. This is a practical walkthrough of the "double handshake."
Complication: Cross-Account Writes and Object Ownership
Reading data is one thing, but what happens when the user in Account B needs to write an object to the bucket in Account A? This introduces a new challenge: object ownership.
By default, when a principal from Account B uploads an object to a bucket in Account A, Account B still owns the object. This can lead to a confusing situation where the bucket owner (Account A) cannot read or manage an object inside its own bucket.
Historically, the solution to this problem involves S3 Access Control Lists (ACLs), a legacy access control mechanism. When uploading, the user in Account B can specify the bucket-owner-full-control canned ACL, which grants the bucket owner (Account A) full control over the newly uploaded object.
S3 bucket access from the same and another AWS account | Tom Gregory
Let's return to the Tom Gregory article to see how this problem is solved.
Read the section Writing to S3 buckets cross-account. This section walks you through the write permission policies, demonstrates the "Access Denied" error for the bucket owner, and shows how using the --acl bucket-owner-full-control flag resolves the issue.
Note for modern practice: While the ACL method is important to know for the exam, AWS now recommends a simpler approach using S3 Object Ownership. By setting the bucket's Object Ownership to "Bucket owner preferred," all new objects written by other accounts are automatically owned by the bucket owner, eliminating the need for ACLs.
Alternative: Cross-Account Access via IAM Roles
Using a resource-based policy is not the only way to handle cross-account access. The other primary method, which you are somewhat familiar with from earlier lessons, is using an IAM Role.
In this pattern:
- Account A (the resource owner) creates an IAM role. This role has an identity-based policy attached that grants it access to the S3 bucket.
- The role's trust policy is configured to allow principals from Account B to assume it.
- A user in Account B calls the
sts:AssumeRoleAPI to "swap hats" and receive temporary credentials for the role in Account A. - Using these temporary credentials, the user can now access the S3 bucket as if they were operating from within Account A.
The image below provides a great visual comparison of these two patterns. The resource-based policy approach involves a "Direct API Call," while the IAM role approach involves a "Hat Swap" via the AssumeRole API.

This second image illustrates the AssumeRole flow in more detail, showing the trust policy and permissions policy involved.

This role-based approach is often preferred when a principal needs access to multiple resources across different services in the target account, as all permissions can be managed centrally in the role's policy.
Conclusion
In this lesson, we've explored the crucial topic of cross-account access, a cornerstone of building secure, multi-tenant applications on AWS. You've learned how resource-based policies differ from the identity-based policies we've covered previously and how to use them to grant access to your resources to principals in other accounts.
Here are the key takeaways:
- Resource-Based Policies: Attached directly to a resource (like an S3 bucket) and use a
Principalelement to specify who can access it. - The "Double Handshake": For cross-account access using a resource-based policy, permissions must be explicitly allowed by both the resource-based policy in the resource's account and an identity-based policy in the principal's account.
- Object Ownership: When writing objects cross-account, the uploading account owns the object by default. This can be resolved by having the uploader specify the
bucket-owner-full-controlACL or by using the modern S3 Object Ownership feature. - Two Patterns: The two primary patterns for cross-account access are using a resource-based policy for direct access or using an IAM Role that an external principal can assume.
With this lesson, we conclude our first module on AWS Developer Fundamentals & IAM. You now have a comprehensive understanding of how AWS manages identity and permissions.
Our next lesson will begin a new module, diving into the heart of serverless development with AWS Lambda. We'll start by exploring the different ways you can invoke a Lambda function, covering the synchronous and asynchronous invocation models.