Modern AWS environments are built for scale — multiple accounts, shared teams, and resources that need to operate across boundaries. While this flexibility enables agility, it also introduces a critical governance challenge: even when IAM policies and SCPs are correctly configured, resource policies can still be misconfigured, resulting in overly broad access or unintended exposure.
That gap is exactly what Resource Control Policies (RCPs) were created to address.
RCPs let you enforce non-negotiable rules directly on resources, such as:
- This resource can never be shared outside the organization
- Only approved identities can assume roles here
- Even admins can’t break these rules
They don’t replace IAM or SCPs — they fill the last missing governance layer.
| Layer | What it controls |
|---|---|
| IAM | Which principals can perform which actions on which resources, within organizational guardrails. |
| SCP | The maximum permissions IAM principals in an account or OU can ever have; they restrict but do not grant access. |
| RCP | The maximum permissions that can ever apply to specific resources in accounts or OUs, regardless of which principal calls them. |
Let's now jump to hands-on for RCP, how it works.
Note : It is highly advisable to test RCP on dev/test account before applying. Don't apply directly at Root level.
Hands-On
In this demo, we will be covering two scenarios:
- Enforce HTTPS-only across all S3 buckets
- Prevent external accounts from decrypting KMS keys. or Disable cross-account use of KMS keys.
Scenario #1 : Enforce HTTPS-only across all S3 buckets
I have created S3 bucket and intentionally set over permissive access as follows. I have uploaded simple file which has text RCP HTTPS test
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3rcppolicy",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::XXXX-XXXXX-demo-XXXXXXX/*"
}
]
}
Now if you access files using HTTP and HTTPs, both will work :
HTTP Test:
Now, let's apply RCP at account level to make sure only HTTPs access is allowed
After attaching RCP, you can see below HTTP requests gets AccessDenied (highlighted part) but HTTPS works.
As you have also observed that, once we apply RCP its applicable to current and new resource policies as well. So while applying RCP make sure that which resources are intentionally shared across org.
Scenario #2 : Prevent external accounts from decrypting KMS keys.
This example blocks the decryption of AWS KMS (Key Management Service) keys for any principal except whitelisted principal in policy.
Part of this demo I have create KSM key and given access for decrypt to other account in KMS policy.
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::714XXXXXXXXX:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow use of the key",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::599XXXXXXXXX:root"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*"
},
I'm encrypting one file using KMS key in AccountA(714XXXXXXXXX) and will try to decrypt it using assuming IAM role from AccountA(714XXXXXXXXX) and AccountB(599XXXXXXXXX).
Without RCP applied, we able to decrypt it.
Next, we apply the RCP directly to Account A (714XXXXXXXXX). With this policy in place, access is restricted so that only users or roles from Account A can perform the allowed actions, regardless of how individual resource or IAM policies are configured.
{
"Effect": "Deny",
"Principal": "*",
"Action": [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalAccount": [
"714XXXXXXXXX"
]
}
}
}
If I perform same operation for decryption, I get error for AccountB(599XXXXXXXXX) with explicit Deny in RCP.
Before implementing Resource Control Policies - RCPs
RCPs are useful feature from AWS to design data perimeter in AWS, it protect data, block breaches, and enforce real cloud perimeters — but if deployed carelessly, they can also break production workloads. We should must create and test RCPs before applying to critical workloads.
Before you deploy any RCP, make sure it passes through a formal test cycle:
What to test
Use AWS IAM Access Analyzer to understand:
- Which resources are currently public
- Which are shared externally
- Which identities depend on cross-account access
This helps you avoid breaking legitimate access paths.
Then validate policy logic using:
- IAM Policy Simulator
- Controlled sandbox accounts
- Dev OUs before prod
Also test interactions between SCPs and RCPs.
Remember: a deny from either one will block access.
Other High-Impact RCP Use Cases to Explore
AWS maintains a fantastic repository of real-world RCP patterns.
- 🔐 Enforce Org-Only STS Access
- 🔑 Lock Down OIDC Providers (GitHub Actions, etc.)
- 🗄️ Block External Sharing of Data Stores
When used thoughtfully, RCPs close the last remaining gaps around data access, cross-account trust, and misconfigurations.
Good security isn’t about trusting people — it’s about designing systems that remain safe even when mistakes happen.
Thanks for reading — hope this helps you build safer, more resilient AWS environments.






Top comments (0)