AWS Resource Control Policies (RCPs) Explained: A Practical Guide to Resource-Level Security
Source: Dev.to
Modern AWS Governance with Resource Control Policies (RCPs)
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 mis‑configured, resulting in overly broad access or unintended exposure.
That gap is exactly what Resource Control Policies (RCPs) were created to address.
What RCPs Do
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.
Note: RCPs 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. |
Hands‑On Demo
⚠️ It is highly advisable to test RCPs in a dev/test account before applying them in production. Do not apply directly at the root level.
In this demo we cover two scenarios:
- Enforce HTTPS‑only access across all S3 buckets
- Prevent external accounts from decrypting KMS keys (or disable cross‑account use of KMS keys)
Scenario #1 – Enforce HTTPS‑only on S3 Buckets
1. Create an over‑permissive bucket policy
{
"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/*"
}
]
}
The bucket contains a simple file with the text RCP HTTPS test.
2. Verify HTTP and HTTPS both work
-
HTTP test

-
HTTPS test

3. Apply an RCP at the account level to allow only HTTPS

4. Observe the result
-
HTTP request → AccessDenied (highlighted in the screenshot)

-
HTTPS request → succeeds
Takeaway: Once an RCP is applied, it governs both existing and newly created resource policies. Ensure you review which resources are intentionally shared across the organization before enforcing an RCP.
Scenario #2 – Prevent External Accounts from Decrypting KMS Keys
This example blocks decryption of AWS KMS keys for any principal except a whitelisted principal defined in the key policy.
1. Sample KMS key policy (granting cross‑account access)
{
"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": "*"
}
2. Test decryption without RCP
-
Encrypt a file using the KMS key in Account A (
714XXXXXXXXX). -
Decrypt the file from both:
- Account A (same account) – succeeds.
- Account B (
599XXXXXXXXX) – also succeeds because of the cross‑account permission.
Account A decryption result

Account B decryption result (truncated for brevity)
3. Apply an RCP that disallows cross‑account decryption
After attaching the appropriate RCP at the account or OU level, attempts from Account B to decrypt the key are denied, while Account A retains its ability to use the key.
Result: The RCP enforces the “maximum permissions” rule on the KMS resource, overriding the permissive key policy for any external principal.
Summary
- IAM controls who can do what on which resources.
- SCPs set the maximum permissions an IAM principal can ever have in an account/OU.
- RCPs set the maximum permissions that can ever be applied to specific resources, regardless of the caller.
By adding RCPs you close the final governance gap, ensuring that even a perfectly‑written IAM policy or SCP cannot be bypassed by a mis‑configured resource policy. Test thoroughly in non‑production accounts, then roll out strategically to protect your most critical resources.
Applying the RCP to Account A
{
"Effect": "Deny",
"Principal": "*",
"Action": [
"kms:Decrypt",
"kms:Encrypt",
"kms:GenerateDataKey"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalAccount": [
"714XXXXXXXXX"
]
}
}
}
Before Implementing Resource Control Policies (RCPs)
RCPs are a powerful AWS feature for designing data perimeters. They protect data, block breaches, and enforce real cloud perimeters—but if deployed carelessly, they can also break production workloads.
We must create and test RCPs before applying them 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 resources 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
- Development OUs before production
Also test interactions between Service Control Policies (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.

