Schemas, packaging and delivery
Delivering Licensed Datasets to Your Cloud Bucket: Cross-Account Access Patterns
Quick answer
Cross-account delivery works when both sides grant access: the bucket owner allows the other account in a bucket policy, and the other account allows its own role in an IAM policy [1]. For licensed datasets, decide first whether the supplier pushes into your bucket or you pull from theirs, because that choice sets who owns the objects, who pays for transfer and who can revoke access. Then add key-policy grants for encrypted objects, short-lived credentials and access logging from day one.
By SourceX Editorial · Updated
- 01Your data spec
- 02Matched to businesses
- 03Rights and samples reviewed
- 04Licensed delivery
This page covers the mechanics on AWS, Google Cloud and Azure. For formats, manifests and the wider handover, start at the dataset delivery hub; for a plain-language overview, see how licensed data is delivered.
Push into your bucket or pull from the supplier's: who owns, pays and revokes
Push into a buyer-owned bucket is usually the better default for training data, because the objects land under your account, your retention rules and your logging. Pull from a supplier-owned bucket suits deliveries the supplier wants to keep revocable, or one dataset shared with several licensees. The decision table below compares the two on the points that matter in a license negotiation.
| Question | Push (supplier writes to buyer bucket) | Pull (buyer reads from supplier bucket) |
|---|---|---|
| Who owns the objects after delivery | Buyer, if object ownership is enforced as bucket owner | Supplier; buyer copies into its own storage |
| Who pays storage | Buyer | Supplier |
| Who pays transfer/egress | Supplier pays its egress; buyer pays nothing to receive within most providers | Supplier by default; buyer if Requester Pays is enabled [4] |
| Who can revoke | Buyer revokes the supplier's write role; supplier cannot claw back data | Supplier removes the bucket-policy grant or lets credentials expire |
| Typical credential | Buyer-created IAM role the supplier assumes, scoped to one prefix | Bucket policy or access point naming the buyer's account or role |
| Main failure mode | Objects written with the uploader as owner, unreadable by the buyer | Expired or over-broad grants; buyer copies before checksums are verified |
Egress cost allocation is a negotiation in its own right; see who pays for data egress. Whichever direction you choose, write the bucket, region, prefix layout and credential model into the delivery specification before the first object moves.
AWS S3: bucket policies, assumed roles and access points
On S3, cross-account access requires two grants: the bucket owner's bucket policy must allow the other account, and that account's administrator must delegate the permission to a user or role through an IAM policy [2]. If either side is missing, requests fail with AccessDenied, which is the most common first-day problem. AWS describes the three practical patterns as a bucket policy, an assumed role in the bucket owner's account, or cross-account access points [1].
For a supplier push, the cleanest pattern is a role in the buyer account (for example sx-delivery-writer) with a trust policy naming the supplier's account and an sts:ExternalId condition. The role's permissions allow s3:PutObject, s3:AbortMultipartUpload and s3:ListBucket on arn:aws:s3:::buyer-landing/incoming/vendor-a/* and nothing else. Because the supplier writes as a principal in the buyer's account, object ownership questions disappear.
If you grant the supplier's account directly in a bucket policy instead, set S3 Object Ownership to "bucket owner enforced" so ACLs are disabled and the bucket owner owns every object regardless of uploader [9]. Without it, older buckets can end up holding objects your own analysts cannot read.
For pull deliveries to several licensees, cross-account S3 Access Points let the bucket owner delegate access management for a shared dataset to a trusted account without duplicating the data [3]. Each licensee gets its own access point with its own policy, so revoking one licensee does not touch the others. Data vendors already document cross-account bucket access as a customer delivery path [5].
Illustrative example: invented to show structure; it does not describe an available dataset.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowBuyerReadDeliveryPrefix",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:role/buyer-ingest-reader" },
"Action": ["s3:GetObject", "s3:GetObjectVersion"],
"Resource": "arn:aws:s3:::supplier-outbound/deliveries/2026-10/*"
},
{
"Sid": "AllowBuyerListDeliveryPrefix",
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:role/buyer-ingest-reader" },
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::supplier-outbound",
"Condition": { "StringLike": { "s3:prefix": ["deliveries/2026-10/*"] } }
},
{
"Sid": "DenyInsecureTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": ["arn:aws:s3:::supplier-outbound", "arn:aws:s3:::supplier-outbound/*"],
"Condition": { "Bool": { "aws:SecureTransport": "false" } }
}
]
}
The buyer side then attaches an IAM policy to buyer-ingest-reader allowing the same actions on the same ARNs. Note the split between object-level resources (/*) for GetObject and the bucket ARN for ListBucket; mixing them up is a classic cause of "list works, get fails."
Encrypted objects need key grants, not just bucket grants
A bucket policy alone does not let another account read objects encrypted with SSE-KMS under a customer managed key; the reader also needs kms:Decrypt on that key. Cross-account KMS use requires a key policy statement in the key owner's account naming the external account (or a specific role), plus an IAM policy in the external account granting use of that key [10]. Callers must reference the full key ARN, because a bare key ID or alias is assumed to be in the caller's own account.
For a push, the supplier writing with x-amz-server-side-encryption: aws:kms into your bucket needs kms:GenerateDataKey on your key, plus kms:Decrypt for multipart uploads; for a pull, you need kms:Decrypt on theirs. Avoid an unconditioned "Principal": "*" allow in any key policy. If the license calls for envelope encryption outside the cloud provider (PGP or age on top of storage encryption), the key exchange is covered in encrypting dataset deliveries, and transport-level questions in whether licensed data is encrypted in transit.
Google Cloud Storage: IAM on the bucket, not project-wide
Sharing a GCS bucket with another project is an IAM binding on the bucket itself, not on the project. Grant the other side's service account roles/storage.objectCreator for a push or roles/storage.objectViewer for a pull, at bucket level, and enable uniform bucket-level access so object ACLs cannot drift from the bucket policy [8].
Two GCS specifics trip up deliveries. First, if the bucket uses a customer-managed Cloud KMS key, the bucket project's Cloud Storage service agent needs the Encrypter/Decrypter role on that key, and a key that is disabled or in another project without that grant will make reads and writes fail. Second, granting a role at project level rather than bucket level exposes every bucket in the project, which a data license almost never intends; use IAM Conditions on resource.name prefixes to narrow access to one delivery path.
Azure Blob Storage: SAS tokens and their expiry
On Azure, the usual cross-tenant delivery credential is a shared access signature scoped to one container, and Microsoft recommends the user delegation SAS, which is secured with Microsoft Entra credentials rather than the storage account key [7]. A user delegation SAS is signed with a user delegation key whose lifetime is capped at seven days, and stored access policies cannot be used with it. That cap is useful for a one-off delivery but forces a reissue process for multi-week transfers.
Scope the SAS tightly: sp=rl (read, list) for a pull, sp=cw (create, write) for a push, a single container, HTTPS only (spr=https) and the shortest workable se expiry. Account owners can set a SAS expiration policy that flags tokens exceeding a recommended validity window. Treat SAS URLs as secrets: send them over a secure channel, never in a ticket or email body, and plan how you will revoke one if it leaks.
For durable cross-tenant access, an Entra service principal with the Storage Blob Data Reader or Contributor role on the container is easier to audit than a chain of reissued SAS tokens.
Time-bound credentials and revocation in practice
Every cross-account grant for licensed data should have an end date, because the license has a term even when the IAM policy does not. On S3, that means presigned URLs for small one-off pulls, STS sessions with short DurationSeconds for role assumption, and a calendar item to remove bucket-policy statements at term end. On Azure, it is the SAS se parameter and user-delegation key lifetime; on GCS, signed URLs or time-bound IAM Conditions.
Revocation must be tested, not assumed. After the last delivery, confirm that the supplier's role assumption fails, the SAS returns 403 and any access point policy is removed. Post-delivery controls on your own copy are a separate problem, covered in access controls for licensed training data.
Logging and verification from the first object
Turn on access logging before the first write, because you cannot reconstruct who read or wrote an object after the fact. On AWS, enable CloudTrail data events for the delivery bucket (S3 server access logs are a cheaper, best-effort complement); on GCS, Data Access audit logs; on Azure, diagnostic settings for blob read and write operations. Route logs to a separate account or project the supplier cannot reach. The fields worth keeping and how long to retain them are in audit logging for licensed dataset access.
Logs prove access; manifests prove content. Require a manifest with object keys, sizes and SHA-256 checksums, and verify it before you copy anything onward, as described in dataset manifests and checksums. Record the bucket ARN, prefix, KMS key ARN and credential expiry alongside the manifest so lineage starts at the landing zone.
Pre-delivery checklist for cross-account buckets
Use this checklist in the kickoff call with the supplier's engineer; most failed deliveries trace back to one unchecked line.
Illustrative example: invented to show structure; it does not describe an available dataset.
- Direction agreed (push or pull) and written into the delivery specification
- Bucket, region and exact prefix agreed (for example
s3://buyer-landing/incoming/vendor-a/2026-10/) - Both sides of the grant in place: bucket or container policy and the other account's IAM policy [2]
- Object ownership enforced (S3) or uniform bucket-level access on (GCS)
- KMS key policy grants
kms:Decryptorkms:GenerateDataKeyto the named role, referenced by full ARN - Credentials time-bound: STS session, presigned URL, user delegation SAS or IAM Condition with an end date
- Requester Pays decision recorded if pulling from a supplier bucket [4]
- TLS enforced (
aws:SecureTransportdeny,spr=https) - Access logging on and routed to an account the supplier cannot modify
- Manifest and checksums delivered with the data and verified before onward copy
- Revocation test scheduled for the end of the delivery window
When a bucket is not the right handover
Object storage is not the only option for licensed data. Warehouse-native sharing such as Snowflake Secure Data Sharing copies no data between accounts and gives consumers read-only objects [6], which suits structured tables you query rather than files you train on. Very large or bandwidth-constrained transfers may need other methods, compared in offline transfer appliances and SFTP for dataset delivery. Whether you can license data that stays in a vendor's cloud is answered in this question page.
If you are scoping a dataset and want delivery mechanics settled early, you can describe the data you need to SourceX.
Getting licensed datasets delivered to your cloud account
SourceX sources operational datasets from US companies on request and manages the licensing, with every dataset rights-reviewed and delivered under a license that defines records, uses, term and delivery. Delivery runs through private, access-controlled workflows only after an executed agreement and supplier approval. Describe the dataset you need.
Sources
- AWS re:Post Knowledge Center, "How can I provide cross-account access to objects that are in Amazon S3 buckets?". https://repost.aws/knowledge-center/cross-account-access-s3
- Amazon Web Services (Amazon S3 User Guide), "Example 2: Bucket owner granting cross-account bucket permissions". https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-walkthroughs-managing-access-example2.html
- AWS Storage Blog, "Simplify and scale access management to shared datasets with cross-account Amazon S3 Access Points". https://aws.amazon.com/blogs/storage/simplify-and-scale-access-management-to-shared-datasets-with-cross-account-amazon-s3-access-points
- Amazon Web Services (Amazon S3 User Guide), "Using Requester Pays general purpose buckets for storage transfers and usage". https://docs.aws.amazon.com/AmazonS3/latest/dev/RequesterPaysBuckets.html
- AWS Storage Blog, "Setting up cross-account Amazon S3 access with S3 Access Points". https://aws.amazon.com/blogs/storage/setting-up-cross-account-amazon-s3-access-with-s3-access-points/
- Snowflake Inc. (Snowflake Documentation), "About Secure Data Sharing". https://docs.snowflake.com/en/user-guide/data-sharing-intro.html
- Microsoft Learn, "Create a user delegation SAS". https://learn.microsoft.com/en-us/rest/api/storageservices/create-user-delegation-sas
- Google Cloud, "IAM roles for Cloud Storage". https://cloud.google.com/storage/docs/access-control/iam-roles
- Amazon Web Services, "Controlling ownership of objects and disabling ACLs for your bucket". https://docs.aws.amazon.com/AmazonS3/latest/userguide/about-object-ownership.html
- Amazon Web Services, "Allowing users in other accounts to use a KMS key". https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-modifying-external-accounts.html
Tell us what your models need
Share scope, volume, language, format, timing and licensing requirements.