Cross-account data access

Lesson 4 of 4 in Authentication and Authorization over Data.

Nothing about a cross-account read is one permission, and almost every real fault is a question of which link refused. The chain has four links, owned by two different teams, and each can refuse on its own:

  1. The reader's own identity policy, in the consumer account. Necessary, and nowhere near sufficient — an identity policy cannot reach into another account by itself.
  2. The resource offer, in the producer account: a bucket policy naming the consumer, a Glue Data Catalog resource policy, or a Lake Formation grant fulfilled through AWS RAM.
  3. The catalog grant, if Lake Formation is enforcing — because IAM alone cannot express a column filter, so a shared table with no SELECT grant behind it is visible and unreadable.
  4. The KMS key policy of the key the data is encrypted with. Decryption is a separate authorization from retrieval, and this is the link people forget, because it lives in a different console from everything else.

The single most useful thing to learn here is that the error shapes differ, which turns a day of guessing into a minute of reading:

  • AccessDenied on the metadata call — the reader's own identity policy never allowed the action.
  • EntityNotFoundException on the table — the resource was never shared. The table is invisible, not forbidden, which is why this gets misdiagnosed as a wrong database name.
  • AccessDeniedException on lakeformation:GetDataAccess — shared and visible, but no grant. Metadata without data.
  • AccessDenied from kms:Decrypt — every access link said yes. The reader can list and describe, and fails the moment it asks for content. This is the one that gets blamed on replication, networking or "the file is corrupt".

And one hard constraint: an AWS managed key has a key policy you cannot edit, so it can never authorise a cross-account read. A customer managed key is a requirement for sharing encrypted data, not a preference.

There are three mechanisms for sharing data across accounts and the exam expects you to pick on what is being shared.

Lake Formation cross-account grants share catalog tables with fine-grained filters attached. Which mechanism carries the grant depends on the account's cross-account version setting: the earliest version required a matching Glue Data Catalog resource policy alongside every grant, while later versions grant through AWS RAM alone — the recipient accepts a resource share, and named-principal grants to accounts in the same AWS Organizations organization are accepted automatically. A grant that silently does nothing is usually a version mismatch, and upgrading is a deliberate migration because the old resource-policy statements must be cleaned up afterwards.

A Redshift datashare shares warehouse objects. Remember the two-way handshake and the requirement that both clusters be encrypted for cross-account sharing.

An S3 bucket policy shares objects. Use it when the consumer genuinely needs files — a partner pulling a daily extract — and accept that it is whole-object granularity. Pair it with bucket owner enforced Object Ownership, which is now the default for new buckets: ACLs are disabled, the bucket owner owns every object regardless of who wrote it, and bucket policy becomes the only access mechanism. That setting exists because of a specific data lake fault — a partner lands a file in your bucket, the writing account owns the object, and your own analytics role cannot read it.

Two cross-cutting notes. Access from outside the organization needs more care than inside it: an aws:PrincipalOrgID condition is the usual guard, and for genuinely external parties prefer a purpose-built extract over access to the lake. And the consumer's identity policy is still required — a producer-side grant never substitutes for it, which is why "we granted them access and it still fails" is so often a missing policy in the other account.

Where the deeper mechanics live on this site rather than here: policy evaluation order and the interaction of explicit deny, boundaries and service control policies is in the evaluation chain and cross-account authorization; KMS key policies, grants and the kms:ViaService condition are in key types, key policies, and grants.

Diagnosing a cross-account read that fails

  1. Read the error, not the architecture

    Four links produce four distinguishable failures. Thirty seconds reading the exact exception name and the service that raised it is worth an hour of inspecting policies, because it tells you which of two accounts to look in.

  2. EntityNotFoundException — the table is not there

    The resource was never shared with this account, so the consumer cannot see it at all. Check the AWS RAM share was created and accepted (shares to accounts outside the organization need explicit acceptance), and check the cross-account version setting. Misdiagnosed constantly as a typo in the database name.

  3. AccessDenied on the metadata call

    The consumer’s own identity policy is the problem — the role needs glue:Get* and, for a governed table, lakeformation:GetDataAccess. A producer-side grant does not supply the caller’s own permission, and a Lake Formation SELECT grant with no glue:Get* in IAM fails before the data question is even asked.

  4. AccessDeniedException on GetDataAccess

    Shared and visible, but not granted: metadata without data. The consumer sees the schema and gets nothing back. Usually a resource share created without the corresponding Lake Formation grant, or a grant made to the account rather than to the specific principal that is querying.

  5. AccessDenied from kms:Decrypt

    Everything worked until the bytes. The key policy must allow the consumer principal, and the consumer’s identity policy must allow kms:Decrypt on that key ARN — both sides again. If the data is on an AWS managed key, stop: that policy cannot be edited and the only fix is re-encrypting under a customer managed key.

  6. Rows return, but fewer than expected

    Not a failure — a filter. A data filter is doing exactly its job, or an LF-Tag expression matched fewer tables than you thought. Check the effective permissions for that principal rather than the grant you remember making; inheritance makes those two different things.

  7. Fix on both sides, then re-test with the consumer’s own role

    The persistent trap in testing: an administrator in the producer account can read everything and proves nothing. Assume the consumer role and run the query, or the test is meaningless. And expect a short propagation delay after a policy change before re-testing.

Key terms: The cross-account data chain, AWS Glue Data Catalog resource policy, Lake Formation cross-account version, Bucket owner enforced (S3 Object Ownership), Amazon S3 Access Grants

Interactive checkpoint quiz (2 questions) — open this page in a browser to take it.