The situation
The company has grown to twenty-five AWS accounts over four years. Networking has converged on wanting a single Transit Gateway in the shared-services account; today every workload account peers VPCs directly or runs its own egress. DNS for on-premises zones is resolved via Route 53 Resolver forwarding rules currently duplicated in every account. Encryption at rest for cross-account S3 writes is “each account has its own KMS key and we reshuffle bucket policies every time someone new needs access”, a pattern that has produced three incidents this year. The data platform team has built a Glue Data Catalog with roughly forty databases and several hundred tables that analytics teams in three BUs want to query from their own accounts. The Parameter Store holds standard configuration, service discovery endpoints, feature flag defaults, region allow-lists, duplicated by hand into every account by a shell script that has not been run since July.
The architecture team proposes consolidating everything in a shared-services account. Every workload account becomes a spoke. The question is how each central resource is made visible to the spokes.
What actually matters
“Pick a sharing mechanism” is the wrong framing. There’s no single mechanism that fits all five resources, and the interesting work is understanding why, so the team picks a different primitive per resource deliberately rather than forcing one choice to cover everything.
Central source of truth is the point. One of the symptoms driving this project is drift, the shell script that hasn’t run since July, the KMS keys reshuffled per bucket policy, the Resolver rules that got out of sync across accounts. The goal isn’t “copy things faster”; it’s “have one authoritative copy that everyone references”. A sharing mechanism where the single copy stays in the hub and consumers reference it directly is architecturally cheaper than N synchronised copies, even when the synchronisation is reliable. Duplication should be an exception, not the default.
Capability, not preference. The sharing landscape isn’t a menu; it’s a set of service-specific capabilities we can use or not use. Managed cross-account sharing covers only the resource types its integration list names; everything else needs a service-specific primitive on the resource side. No amount of “we prefer one mechanism” changes which mechanism a given resource type actually supports. The attribute to filter on is can this mechanism share this resource type, not which mechanism do we like best.
Permission model, plural. Each candidate mechanism expresses access through a different primitive: a managed share plus consumer IAM, a resource policy naming principals, a replication destination, a purpose-built feature. The team has to be able to audit every one of them, hand each to security review, and revoke cleanly. Multiple mechanisms mean multiple audit surfaces, not a good thing in isolation, but manageable if each one covers a distinct set of resources rather than overlapping arbitrarily. Clean separation matters.
The cross-account dual-grant applies almost everywhere. Whether sharing goes through a managed primitive (which creates implicit IAM grants underneath) or through resource policies directly, the principal in the spoke still needs IAM allowing the action and the resource side still needs to allow the principal. Missing either side is a silent deny. The mental model for every resource has to be “two policies, both sides”, even when one side is managed.
Cost is mostly about scale, not unit price. A RAM share is one configuration per resource. A resource-policy grant is one policy per resource per consumer. Replication is ongoing transfer plus storage per account. At 25 accounts and small sets of resources, no single approach is cost-prohibitive; what matters is which approach produces the least paperwork and therefore the least drift over time.
Availability independence is the one reason to duplicate. If a spoke must function when the hub is degraded, a central resource is a dependency we’ve introduced. For most shared config that’s fine. For a region allow-list read on every Lambda cold start, the latency and availability coupling is a reason to keep a local copy. Duplication isn’t the default, but it’s a legitimate choice where the latency or availability shape demands it.
What we’ll filter on
- Is the resource type RAM-shareable? Objective capability check.
- Can the single hub copy be the source of truth, or does sharing require duplication?
- Does the mechanism express access cleanly (one primitive per consumer, not sprawl)?
- Is the operational overhead proportionate to the number of consumers?
- Does the consumer get a local-feeling handle, or do they need the hub’s ARN?
- Does the mechanism couple availability between hub and spoke?
The sharing landscape
AWS Resource Access Manager (RAM). Managed cross-account sharing. Create a resource share, add resources, name principals (accounts, OUs, or the Organization). Supported types include Transit Gateway, Route 53 Resolver rules, subnets, License Manager licenses, and Glue Data Catalog databases and tables (via Lake Formation v3). Spokes see the resource natively in their own console.
Service-specific resource-based policies. Policies attached directly to resources naming Principal. KMS key policies (KMS is not RAM-shareable), SSM Parameter Store resource policies on advanced-tier parameters (2024), S3 bucket policies, Secrets Manager resource policies, Lambda function policies.
Replication. A copy is created in the consumer account and kept in sync. ECR cross-account, CodeArtifact upstream repositories, Secrets Manager cross-Region replicas, Aurora Global Database, AWS Backup cross-account copies. Fits images, artefacts, and data, not configuration or networking.
Service-specific cross-account primitives. S3 Access Points and Multi-Region Access Points, Lake Formation cross-account grants, delegated administrator patterns for Security Hub, GuardDuty, Config aggregator.
Duplicate per account. Every account holds its own copy. Right for resources that genuinely belong per account. Wrong when the resource is intended to be central, the anti-pattern the scenario is trying to eliminate.
Side by side
| Mechanism | Central source of truth | Works for this resource type | Clean permission model | Ops overhead |
|---|---|---|---|---|
| RAM share | ✓ | TGW, Resolver rules, subnets, Glue (via LF) | ✓ | ✓ (low) |
| Resource-based policy | ✓ | KMS, SSM (adv), S3, Secrets, Lambda | ✓ | — (per consumer) |
| Replication | ✗ | ECR, CodeArtifact, Aurora, Secrets, Backup | ✓ | ✗ (transfer × N) |
| Service-specific primitive | ✓ | S3 APs, Lake Formation | ✓ | ✓ |
| Duplicate per account | ✗ | Anything | ✓ (isolated) | ✗ (N copies) |
No single row is all ticks because no single mechanism is correct for every resource type. The table is read per resource, not per architecture.
Matching resources to primitives
Each resource, the mechanism it actually wants
Transit Gateway. RAM. A RAM share with the TGW as resource and the Organization (or a specific OU) as principal. Spokes attach their VPCs; attachments appear in the spoke console and are billed to the spoke. TGW route tables stay on the hub, with associations and propagations configured centrally, a spoke cannot modify routing. The textbook RAM case and the reason RAM exists.
Route 53 Resolver rules. RAM. The Resolver outbound endpoint and the forwarding rules live in the hub. The rules are the shareable resource. A RAM share with the Organization exposes them to every spoke; each spoke associates the rule with its own VPCs via route53resolver:AssociateResolverRule. The endpoint itself isn’t shared, it stays hub-owned.
KMS keys, key policies. The key policy names the spoke account (or specific role ARNs) as Principal with the permitted actions (kms:GenerateDataKey, kms:Decrypt, kms:DescribeKey). The spoke’s IAM policy separately allows those actions on the key ARN. Both sides required. Aliases are per-account and do not cross the boundary; spokes reference the key by full ARN.
Glue Data Catalog. Lake Formation cross-account v3 (RAM underneath). A data steward in the hub grants SELECT on a specific database or table to a spoke account or principal; the RAM share propagates the grant. Spokes see the shared database in their own Glue catalogue and can query with Athena or EMR against the hub’s metadata directly, no catalogue duplication. The underlying S3 data needs its own access path: bucket policies or Access Points granting read to the spoke roles, and the KMS key policy on whichever encryption key the objects use. The catalogue share grants metadata; the data grants are separate.
SSM Parameter Store, resource policies on advanced-tier parameters. PutResourcePolicy on an advanced-tier parameter, naming the spoke account or role as Principal and allowing ssm:GetParameter and ssm:GetParameters. The spoke retrieves by full ARN (arn:aws:ssm:<region>:<hub-account>:parameter/<name>). Standard-tier parameters must be upgraded to advanced tier to carry a resource policy; advanced tier has a per-parameter monthly cost.
A worked allow trace
Athena query from analytics-prod against the hub’s Glue catalogue. What has to be true?
- RAM share accepted. The hub has a RAM share containing the Glue database
sales_martwith Organization as principal. Analytics-prod seessales_martin its Glue console listed under the hub’s account ID. - Lake Formation permission. A grant in the hub for
SELECTonsales_mart.ordersto the specific role ARN in analytics-prod that Athena assumes. - Spoke IAM allows Athena.
athena:StartQueryExecution,athena:GetQueryResults,glue:GetDatabase,glue:GetTable. - Underlying S3 readable. The bucket policy on the data bucket in the hub names the analytics-prod role as
Principaland allowss3:GetObjectpluss3:ListBucket. - KMS key readable. The key policy names the analytics-prod role and allows
kms:Decrypt; analytics-prod’s IAM also allowskms:Decrypton the key’s ARN.
Five line-ups across three mechanisms. Miss one and the query fails somewhere between “permission denied on the metadata”, “no results returned”, and “cannot decrypt object”. The mental model is not “I have access to the database”, it’s “I have a Lake Formation grant, my bucket read is allowed, and my decryption is allowed.”
When to duplicate anyway
Sharing is not free. A RAM share adds a hub dependency on every spoke; a KMS key policy is a cross-account blast radius waiting to be misconfigured; an SSM resource policy is a thing to audit. Duplicate when:
- Availability independence matters. A spoke that must function during a hub outage is cheaper with a local copy of config than a cross-account dependency. A region allow-list read on every Lambda cold start probably wants a local parameter.
- Compliance boundaries. An account in strict PCI or regulated scope may not be permitted to resolve names for, or decrypt data owned by, accounts outside its scope.
- Latency or throughput. A parameter read on every request from an advanced-tier shared parameter adds a cross-account API call; the per-request cost difference matters at scale.
When you duplicate, do it deliberately: one authoritative value in the hub, an EventBridge-plus-Lambda job (or CI pipeline) copying to spokes, a tag identifying source, an alarm on drift.
What’s worth remembering
- RAM is the managed sharing service for an enumerated list of resource types. Transit Gateway, Route 53 Resolver rules, subnets, Glue databases and tables (via Lake Formation), License Manager licenses, yes. KMS keys, SSM parameters, Secrets Manager secrets, ECR repositories, no.
- Resource-based policies are the per-service path for things RAM does not cover.
- Cross-account policy-based access is always a dual-grant. The resource’s policy must name the principal; the principal’s IAM must allow the action on the resource’s ARN.
- Replication is for images, artefacts, and data, not configuration or networking.
- Lake Formation cross-account sharing v3 uses RAM underneath. Data access (S3 + KMS) sits beside the metadata share as a separate line-up.
- SSM Parameter Store resource policies are advanced-tier only. Upgrading is an explicit tier change with a per-parameter monthly cost.
- KMS aliases don’t cross accounts, spokes reference keys by full ARN.
- The correct answer is a mix. RAM for what it covers, resource policies for what it doesn’t, duplication only when isolation, availability, or latency demands it.
- Duplicate deliberately when you do, one authoritative source, a replication pipeline, drift alarms.