This chapter covers cross-account S3 access patterns, a critical topic for the SAA-C03 exam under Secure Architectures (Objective 1.5). Understanding how to securely grant access to S3 buckets across AWS accounts is essential for designing multi-account architectures. Approximately 10-15% of exam questions touch on S3 security, with cross-account access being a key subtopic. You will learn the three primary methods—bucket policies with IAM roles, ACLs (legacy), and access points—and when to use each. The chapter also covers common pitfalls, exam traps, and step-by-step configuration.
Jump to a section
Imagine two companies, Acme Corp and Beta Inc, sharing a library building. Each company has its own locked room (S3 bucket) in the building. The building has a main entrance with a security guard (AWS Organizations). Acme Corp wants to allow Beta Inc's employees to access certain files in its room. Acme's security officer writes a policy (bucket policy) that says: 'Employees of Beta Inc with a valid badge (IAM role) can enter my room and read files labeled 'ProjectX'.' Acme's officer also gives the policy to the building's central security (AWS Organizations) to authorize cross-company access. Meanwhile, Beta Inc's security officer creates a special badge (IAM role) for its employees and attaches a permissions policy (IAM policy) that says: 'Holders of this badge may enter Acme's room and read 'ProjectX' files.' When a Beta employee wants to access a file, they first show their badge at Beta's internal security (IAM) to get the special badge, then walk to the building entrance. The building guard checks that both companies' policies allow the access (both allow the action), and then the employee can enter Acme's room and retrieve the file. The guard also logs the access (CloudTrail). If either policy denies the access, the guard blocks entry. This two-sided permission check is the core mechanism: the resource-based policy (bucket policy from Acme) and the identity-based policy (IAM role from Beta) must both grant the request. The building guard (AWS) evaluates both policies and only permits access if both allow.
What Is Cross-Account S3 Access?
Cross-account S3 access allows a user or service in one AWS account (the 'requesting account') to access an S3 bucket owned by a different AWS account (the 'owning account'). This is a fundamental capability for multi-account architectures, where data is often centralized in a logging or data lake account and consumed by multiple workload accounts.
By default, all S3 resources are private to the owning account. To grant access to another account, you must explicitly configure permissions. AWS evaluates access using a combination of resource-based policies (bucket policies, ACLs, access point policies) and identity-based policies (IAM user/role policies). For cross-account access to succeed, both the resource-based policy AND the identity-based policy must grant the request. This is a key exam point: there is no 'implicit allow' across accounts.
How It Works Internally
When a user in Account A makes a request to an S3 bucket in Account B, AWS performs an authorization decision using the following logic:
The request context includes the principal (user/role ARN from Account A), the action (e.g., s3:GetObject), the resource (bucket ARN in Account B), and other conditions.
AWS evaluates all applicable policies:
- Identity-based policies attached to the principal in Account A (IAM user/role policy) - Resource-based policies on the bucket in Account B (bucket policy, ACL, or access point policy) - Service control policies (SCPs) in AWS Organizations, if applicable - Session policies if using role assumption 3. The decision is based on the following rules:
By default, all requests are denied (implicit deny).
An explicit allow in either the identity-based policy or resource-based policy (or both) can override the implicit deny.
An explicit deny in any policy overrides any allow.
For cross-account access, BOTH the identity-based policy and the resource-based policy MUST allow the request. If either is missing an allow, the request is denied.
This two-way allow requirement is the most important concept for the exam. It differs from same-account access, where only one policy (identity or resource) needs to allow.
Key Components and Defaults
Bucket Policies: Resource-based policies attached directly to an S3 bucket. They can grant access to principals in other accounts by specifying the principal's AWS account ID or full ARN. Bucket policies are the recommended method for cross-account access because they are centralized, support conditions, and work with IAM roles.
IAM Roles: An IAM role in the requesting account with a trust policy that allows the bucket owner account to assume the role? No — actually, the role is assumed by users in the requesting account, but the bucket policy in the owning account must grant access to that role ARN. The trust policy of the role must allow the users in the requesting account to assume it. Then, when a user assumes the role, they get temporary credentials that are used to access the bucket. The bucket policy must explicitly allow the role ARN.
IAM User Policies: Alternatively, you can attach a policy to an IAM user in the requesting account (not recommended for production). The bucket policy then grants access to the user ARN or the entire account. This is less flexible because it requires managing individual users.
Access Control Lists (ACLs): Legacy method. ACLs are sub-resources of objects and buckets. They can grant access to other AWS accounts (by account ID) but not to specific IAM users/roles within that account. ACLs are less granular and harder to audit. AWS recommends using bucket policies instead. By default, ACLs are disabled on new buckets (S3 Object Ownership setting). The exam may test that ACLs are deprecated but still supported.
Access Points: A newer feature that simplifies managing access for large-scale data sharing. Each access point has its own policy and network controls. You can create an access point in the owning account and grant cross-account access via the access point policy. The requesting account then uses the access point ARN instead of the bucket ARN. Access points enforce a separate policy evaluation.
Service Control Policies (SCPs): If both accounts are in the same AWS Organization, SCPs can restrict cross-account access. An SCP that denies s3:PutObject to all principals would override any allow in bucket policies. The exam may include SCPs as a potential blocker.
Configuration and Verification
Method 1: Bucket Policy with IAM Role (Recommended)
In Account A (requesting account), create an IAM role with a trust policy that allows users in Account A to assume it. Attach an IAM policy that allows the desired S3 actions on the target bucket ARN.
In Account B (owning account), attach a bucket policy that grants the role ARN from Account A the necessary permissions.
Users in Account A assume the role, then access the bucket using the role's temporary credentials.
Example bucket policy in Account B:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::ACCOUNT_A:role/CrossAccountRole"
},
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::bucket-b/*",
"arn:aws:s3:::bucket-b"
]
}
]
}Example IAM policy attached to the role in Account A:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::bucket-b/*",
"arn:aws:s3:::bucket-b"
]
}
]
}Method 2: Bucket Policy Granting Access to Entire Account
In the bucket policy, specify the principal as the entire requesting account:
"Principal": {
"AWS": "arn:aws:iam::ACCOUNT_A:root"
}This allows any IAM user/role in Account A that also has an IAM policy allowing the action. This is simpler but less secure because it grants access to all principals in Account A.
Method 3: Using Access Points
In Account B, create an S3 access point for the bucket.
Attach a policy to the access point that grants access to the role in Account A.
In Account A, use the access point ARN (instead of the bucket ARN) when making requests.
Access points can also enforce a VPC origin, which is useful for restricting access to a specific VPC.
Verification Commands
You can use the AWS CLI to test cross-account access. First, assume the role in Account A:
aws sts assume-role --role-arn "arn:aws:iam::ACCOUNT_A:role/CrossAccountRole" --role-session-name testThen use the returned credentials to access the bucket:
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_SESSION_TOKEN=...
aws s3 ls s3://bucket-b/If access is denied, check CloudTrail logs for the reason. Common errors include missing bucket policy, incorrect principal ARN, or SCP restrictions.
Interaction with Related Technologies
AWS Organizations: If accounts are in the same organization, you can use SCPs to restrict cross-account access. For example, an SCP can deny s3:PutObject to any bucket in a specific account unless the request comes from a specific VPC.
VPC Endpoints: You can use gateway endpoints or interface endpoints to access S3 privately. For cross-account access, the bucket policy must allow the VPC endpoint or the principal's VPC. You can also use access points with VPC origin.
CloudTrail: All S3 API calls are logged. For cross-account access, the source IP will be from the requesting account's VPC or internet, and the user identity will be the assumed role ARN.
AWS KMS: If the bucket is encrypted with a customer-managed KMS key (CMK) in Account B, the requesting account's role must also have kms:Decrypt permission on that key. The key policy must grant access to the role ARN. This is a common exam trap: forgetting KMS permissions.
Common Exam Traps
Two-way allow requirement: Many candidates think that a bucket policy alone is sufficient. For cross-account, the requesting principal must also have an IAM policy that allows the action.
Principal is the account root: Using "Principal": { "AWS": "ACCOUNT_A" } is shorthand for the root user of that account, not all users. To grant access to all users/roles in the account, use "AWS": "arn:aws:iam::ACCOUNT_A:root".
ACLs are deprecated: The exam may test that ACLs are still supported but not recommended. New buckets have ACLs disabled by default.
Access points are not a separate service: They are a feature of S3. They have their own policies and can simplify cross-account access.
KMS key policy: If using SSE-KMS, the key policy must explicitly allow the cross-account principal. The bucket policy alone is insufficient.
Identify Owning and Requesting Accounts
Determine which AWS account owns the S3 bucket (the owning account) and which account needs access (the requesting account). This step is crucial because the configuration direction is from the owning account to the requesting account. In the exam, you may be given a scenario where Account A has data and Account B needs to read it. You must configure the bucket policy in Account A and the IAM role in Account B. Note that the bucket policy is attached to the bucket in the owning account, not the requesting account. This is a common mistake: candidates sometimes try to attach a bucket policy to a bucket they don't own.
Create IAM Role in Requesting Account
In the requesting account (Account B), create an IAM role that will be assumed by users/services in that account. Attach an IAM policy that grants the desired S3 actions (e.g., s3:GetObject, s3:ListBucket) on the target bucket ARN. The trust policy of the role must allow principals in the requesting account to assume it. For example, allow the IAM user 'bob' or the AWS service 'EC2' to assume the role. The role will receive temporary security credentials from STS. These credentials are used for the cross-account request.
Attach Bucket Policy in Owning Account
In the owning account (Account A), attach a bucket policy to the target S3 bucket. The policy must include a statement that grants the IAM role ARN from Account B the necessary permissions. The Principal element must specify the role ARN exactly (e.g., 'arn:aws:iam::ACCOUNT_B:role/CrossAccountRole'). The Action element lists the allowed S3 actions. The Resource element includes the bucket ARN and optionally the object ARN with a wildcard. If you forget to include the bucket ARN itself (for ListBucket), the request will fail. Also, ensure that the bucket policy does not have a Deny that overrides the Allow.
Configure KMS Key Policy (if encrypted with CMK)
If the bucket uses SSE-KMS with a customer-managed KMS key (CMK), you must also modify the key policy to grant the IAM role in Account B permission to use the key. The key policy must include a statement that allows the role to perform kms:Decrypt (and optionally kms:GenerateDataKey) on the key. Without this, the cross-account access will fail even if the bucket policy allows it. This is a frequent exam trap: candidates configure the bucket policy correctly but forget the key policy. The key policy is a resource-based policy attached to the KMS key, not the bucket.
Assume Role and Access Bucket
In the requesting account, a user or service assumes the IAM role using AWS STS. This can be done via the AWS CLI, SDK, or console. The user must have permission to call sts:AssumeRole on the role ARN (granted by the role's trust policy). After assuming the role, the user receives temporary credentials (access key, secret key, session token). These credentials are then used to make S3 API calls to the bucket in the owning account. The S3 service evaluates both the bucket policy (owning account) and the IAM policy attached to the role (requesting account). Both must allow the action. If either denies, the request fails.
Enterprise Scenario 1: Centralized Logging
A large enterprise has a central logging account that stores S3 access logs, CloudTrail logs, and VPC Flow Logs from hundreds of workload accounts. The workload accounts need read-only access to their own log prefixes. The solution uses a bucket policy in the logging account that grants access to each workload account's IAM role. For example, the bucket policy has multiple statements, each with a Principal pointing to a specific role ARN in a workload account. The role in each workload account has an IAM policy allowing s3:GetObject and s3:ListBucket on the logging bucket. The bucket policy also uses condition keys like 'aws:SourceAccount' to restrict access to the correct account, preventing a workload account from accessing another account's logs. In production, this setup handles millions of objects daily. A common misconfiguration is forgetting to update the bucket policy when a new workload account is added, causing access failures. To avoid this, the team uses Infrastructure as Code (e.g., Terraform) to manage the bucket policy dynamically.
Enterprise Scenario 2: Data Lake Sharing
A data analytics company has a data lake in Account A that is consumed by multiple business units in separate AWS accounts. Each business unit has its own IAM role. The bucket policy in Account A grants s3:GetObject and s3:ListBucket to each role. To simplify management, the company uses S3 access points. They create an access point per business unit, each with its own policy that grants access to that unit's role. The access points also enforce VPC origin, ensuring that traffic stays within the corporate network. This setup allows fine-grained access control and network isolation. In production, the access points handle thousands of requests per second. A performance consideration is that access points add a small latency overhead, but it is negligible for most workloads. The biggest issue is misconfigured access point policies that accidentally grant broader access than intended, leading to data leaks. Regular audits using IAM Access Analyzer help detect overly permissive policies.
Enterprise Scenario 3: Cross-Account Backup
A company uses a separate backup account to store backups from production accounts. The production account has an IAM role that allows s3:PutObject to the backup bucket. The backup bucket policy grants access to the production role. The production account's backup scripts assume the role and upload backup files. A common problem is that the backup role's trust policy must allow the backup service (e.g., AWS Backup) to assume it, which requires a specific service principal. Additionally, if the backup bucket uses S3 Object Lock for compliance, the role needs s3:PutObjectLegalHold permission. Misconfiguring the trust policy or forgetting the Object Lock permissions can cause backup failures. The team tests the configuration using the AWS CLI with the assume-role command before deploying to production.
What the SAA-C03 Tests
This topic falls under Domain 1: Secure Architectures, Objective 1.5: 'Determine how to implement a secure multi-account AWS environment.' The exam specifically tests your ability to choose the correct cross-account S3 access method given a scenario. Key objective codes: 1.5.1 (Design a multi-account structure), 1.5.2 (Implement cross-account access). Questions often present a scenario where a company needs to share data between accounts and ask which combination of policies is required.
Most Common Wrong Answers
'Use an IAM policy in the owning account to grant access to the requesting account.' This is wrong because IAM policies are identity-based and can only be attached to principals in the same account. You cannot attach an IAM policy to a user in another account. The correct approach is a bucket policy (resource-based) in the owning account.
'Grant access using S3 ACLs with the requesting account ID.' While ACLs can grant access to another account, they are legacy and less secure. The exam typically favors bucket policies or access points. Also, ACLs grant access to the entire account, not specific roles/users, which is often too permissive.
'The bucket policy alone is sufficient.' Many candidates forget that the requesting principal must also have an IAM policy allowing the action. The exam will present a scenario where the bucket policy is correctly configured but access still fails, and the answer is that the IAM role lacks permissions.
'Use a resource-based policy on the IAM role.' IAM roles do not have resource-based policies; they have trust policies. The trust policy controls who can assume the role, not what the role can do. The role's permissions are defined by its attached IAM policies.
Specific Numbers and Terms
The exam expects you to know that bucket policies support a maximum size of 20 KB.
Access points have a limit of 10,000 per account per region.
The default S3 Object Ownership setting disables ACLs; you must enable them if you want to use ACLs.
The condition key 'aws:SourceAccount' is used to restrict cross-account access to a specific account.
KMS key policies are separate from bucket policies; both must allow cross-account access.
Edge Cases
If the requesting account is in the same AWS Organization, an SCP can block the access even if both policies allow. The exam may ask you to identify why access is denied despite correct policies.
If the bucket is configured with 'Block Public Access' settings, they can override cross-account access if the bucket policy grants access to a public principal. However, cross-account access to a specific role is not blocked by these settings.
Cross-account access does not work if the bucket policy uses a principal of '*' unless the requesting principal also has an IAM policy allowing the action. The 'Block Public Access' settings may also block it.
How to Eliminate Wrong Answers
Ask yourself: 'Does the principal have an IAM policy allowing the action?' If not, eliminate answers that only mention bucket policies. 'Is the bucket policy in the correct account?' Eliminate answers that attach the bucket policy to the requesting account. 'Is the principal specified correctly?' Eliminate answers that use account ID without specifying root or role ARN. 'Is KMS involved?' If yes, check that the key policy includes the cross-account principal.
Cross-account S3 access requires both a resource-based policy (bucket policy) in the owning account and an identity-based policy (IAM policy) in the requesting account to allow the action.
The bucket policy must specify the principal as the IAM role ARN or the root user of the requesting account (using 'arn:aws:iam::ACCOUNT_ID:root').
If using SSE-KMS with a customer-managed key, the KMS key policy must also grant the cross-account principal decrypt permissions.
S3 ACLs are legacy and disabled by default; use bucket policies or access points instead.
Access points simplify cross-account access by allowing separate policies and VPC restrictions.
SCPs in AWS Organizations can override cross-account permissions even if bucket and IAM policies allow.
The condition key 'aws:SourceAccount' can restrict cross-account access to a specific account.
These come up on the exam all the time. Here's how to tell them apart.
Bucket Policy with IAM Role
Centralized policy on the bucket itself.
Requires the IAM role ARN to be specified in the bucket policy.
No additional cost beyond S3 storage.
Supports condition keys like aws:SourceAccount.
Bucket policy size limit of 20 KB.
S3 Access Points
Decentralized policies per access point.
Access point has its own policy; bucket policy can be simpler.
No additional cost for access points themselves, but may incur data transfer costs.
Supports VPC origin restriction natively.
Can create up to 10,000 access points per account per region.
Mistake
A bucket policy alone is enough for cross-account access.
Correct
Both the bucket policy (resource-based) in the owning account and an IAM policy (identity-based) in the requesting account must allow the action. Without the IAM policy, the request is denied because the principal has no explicit allow.
Mistake
Using 'Principal': { 'AWS': '123456789012' } grants access to all IAM users in that account.
Correct
This syntax grants access only to the root user of that account. To grant access to all IAM users/roles, use 'Principal': { 'AWS': 'arn:aws:iam::123456789012:root' }. However, even then, each principal must have an IAM policy allowing the action.
Mistake
S3 ACLs are the recommended way for cross-account access.
Correct
ACLs are legacy and not recommended. AWS recommends using bucket policies or access points. ACLs are disabled by default on new buckets. They also grant access to the entire account, not specific principals.
Mistake
Cross-account access works automatically if both accounts are in the same AWS Organization.
Correct
Being in the same organization does not automatically grant cross-account access. You still need to configure bucket policies and IAM roles. In fact, SCPs in the organization can further restrict access.
Mistake
You can attach an IAM policy to a bucket to grant cross-account access.
Correct
IAM policies are identity-based and cannot be attached to resources like S3 buckets. You use bucket policies (resource-based) to grant access to other accounts.
Reveal each answer, then mark whether you got it right. Score 60%+ to unlock the next chapter.
In same-account access, either the identity-based policy or the resource-based policy can allow the request. In cross-account access, both must explicitly allow the request. This is because AWS uses a 'two-way allow' model for cross-account: the resource owner (bucket policy) must grant access to the external principal, and the external principal's account (IAM policy) must also grant the principal permission to perform the action. If either policy denies or lacks an allow, the request fails.
Yes, but it is not recommended. ACLs are a legacy feature and are disabled by default on new S3 buckets (since April 2023). To use ACLs, you must enable them via the S3 Object Ownership setting. ACLs can grant access to another AWS account by specifying the account ID, but they cannot grant access to specific IAM roles or users within that account. AWS recommends using bucket policies or access points instead, as they provide finer control and better auditability.
You need to configure both the bucket policy and the KMS key policy. The bucket policy must grant the cross-account principal the necessary S3 actions (e.g., s3:GetObject). The KMS key policy must grant the same principal the kms:Decrypt permission (and kms:GenerateDataKey if writing). Without the key policy, the cross-account user will be able to list the bucket but will get an access denied error when trying to read or write objects. This is a common exam trap: always check for KMS permissions when SSE-KMS is used.
S3 access points simplify managing access for large-scale data sharing. Instead of writing complex bucket policies, you can create an access point for each consumer account and attach a dedicated policy to that access point. Access points also support network-level controls, such as restricting access to a specific VPC. They are especially useful when you have many consumers with different access needs. The consumer uses the access point ARN (instead of the bucket ARN) to access the data.
Common reasons include: (1) The IAM role/user in the requesting account does not have an IAM policy allowing the action. (2) The bucket policy specifies the principal incorrectly (e.g., using account ID instead of role ARN). (3) The bucket is encrypted with a KMS key and the key policy does not grant permissions. (4) An SCP in AWS Organizations denies the action. (5) The bucket has 'Block Public Access' settings that block the request if the principal is '*' or if the request comes from outside the VPC. Use CloudTrail to diagnose the exact error.
Yes, by specifying the principal as the root user of the other account: 'Principal': { 'AWS': 'arn:aws:iam::ACCOUNT_ID:root' }. This grants access to all IAM users and roles in that account, provided each has an IAM policy allowing the action. However, this is less secure than granting access to specific roles. It is better to use individual role ARNs in the bucket policy and have users assume those roles.
A resource-based policy is attached to a resource (like an S3 bucket or KMS key) and specifies who can access that resource (the principal) and what actions they can perform. An identity-based policy is attached to an IAM user, group, or role and specifies what resources that identity can access. For cross-account access, you need both: a resource-based policy on the bucket (and possibly KMS key) and an identity-based policy on the IAM role in the requesting account.
You've just covered Cross-Account S3 Access Patterns — now see how well it sticks with free SAA-C03 practice questions. Full explanations included, no account needed.
Done with this chapter?