The CFO does not feel an SCP. The staff engineer does, the first time a sandbox role cannot disable GuardDuty and a production S3 bucket still accepts a wide resource policy from a forgotten integration. AWS resource control policies vs SCP is the distinction that closes that gap. Service control policies cap what principals in the account can do. Resource control policies cap what resource-based policies are allowed to grant, including to principals outside the organization. When SCPs are not enough, the bucket policy is usually why.
Rutagon builds that pair as part of a data perimeter, not as a policy museum. We already described the wider control set in AWS data perimeter architecture for SOC 2. This article is the RCP versus SCP cut: which document you attach, what an auditor can sample, and when an SCP-only org still leaks.
We do not claim a FedRAMP authorization or a SOC 2 certification. We design the control so a Type 2 sample has a policy document, an attachment, and a denied test. That is engineering on AWS cloud infrastructure.
AWS Resource Control Policies vs SCP When Principals Are Not Enough
An SCP is an Organizations policy that limits IAM principals in member accounts: users, roles, and the root user of that account. It does not change what a resource policy can allow. A bucket policy, key policy, KMS grant, SNS topic policy, or EventBridge resource policy can still permit a principal that is not in your account. The SCP never sees that call if the caller never assumes a role you own.
That is the classic confused-deputy and “we SCPed s3:PutBucketPolicy” miss. You stopped your own engineers from widening the bucket. You did not stop an existing wildcard principal on the bucket, and you did not stop a new resource policy that names an external account. RCPs exist to put a ceiling on those resource policies.
AWS documents RCPs in the Organizations resource control policies guide. RCPs are allow-list style in effect: they restrict what resource-based policies may grant. They apply to a growing list of services. If a service is not in the RCP support list, an RCP will not save you there. That limitation belongs in the design note you hand an auditor. Do not imply RCPs cover every AWS resource type.
SCPs remain mandatory. RCPs do not replace the “no disable logging, no leave the org, no create IAM users” ceiling on your own principals. Teams that attach RCPs and delete SCPs have only moved the hole.
When an SCP Story Fails an Evidence Review
Auditors who sample CC6-style logical access will ask how you prevent public or cross-account data access. An SCP that denies s3:PutBucketPublicAccessBlock is not an answer to “who can read this bucket.” The answer is account public-access blocks, bucket policies, RCPs that forbid Principal: "*" except for the exceptions you list, and VPC endpoint policies if that is your path.
We see SCP-only orgs fail the sample when:
A vendor integration used a bucket policy with the vendor account. The SCP never touched it. The vendor was offboarded in IAM and SSO. The bucket policy still named them.
A developer attached a key policy that allowed another account “for a restore.” The restore finished. The key policy stayed.
CloudTrail or Config write to a log bucket with a resource policy that is wider than the trail role. The SCP on the app account cannot see the log-archive account’s resource policy.
RCPs on the log-archive and data accounts close the last case. Offboarding checklists that include resource-policy search close the first two. Neither is a conformance pack. Packs detect. RCPs prevent a class of resource-policy grants. Detective coverage still belongs in AWS Config conformance packs for the services RCPs do not yet cover.
How We Attach Both Without Locking Delivery
The pattern we ship: SCPs at the organization root or at OU boundaries for identity and logging invariants. RCPs on the data OU and the log-archive OU first, because that is where resource policies matter. App OUs get RCPs after we inventory existing resource policies. Attaching a tight RCP to an OU that already has a wildcard bucket policy will break the integration on the next evaluation. Inventory first.
Exceptions are named. If a specific external account must remain on a bucket, the RCP and the bucket policy both name that account, and the exception has an owner and a ticket. “Allow all of AWS” is not an exception. It is the hole.
IAM Access Analyzer stays on. RCPs reduce the chance of a new hole. Analyzer shows the holes that already exist. We do not treat Analyzer findings as the control. We treat them as the backlog the RCP is meant to stop growing.
Terraform for Organizations policies is awkward in some setups because management-account permissions are tight. We still store the JSON in git. The attachment is a change-controlled apply from the management or delegated admin path you already use for SCPs. Do not edit RCPs only in the console.
What an Auditor Can Sample
We prepare a folder, not a slide:
The SCP JSON and the OU or account it attaches to.
The RCP JSON and the same attachment evidence.
A describe-effective-policy or console screenshot with timestamp is weaker than the git commit plus the Organizations API output from the assessment window.
A failed API call that shows an SCP denial and a failed PutBucketPolicy or equivalent that shows an RCP denial. Two denials, two stories.
The exception register: external principals still allowed, why, who owns them.
Restore and backup paths that cross accounts need their own resource policies. Those belong in the exception register or they belong in AWS Backup restore testing so the copy is a tested control, not a standing wide key policy.
A Minimal RCP Shape
The first RCP we usually attach is not a 200-line novel. It forbids resource policies from granting * principals on S3 and KMS except for the service principals those services require, and it forbids granting to accounts outside a list. AWS-managed RCP examples exist in the documentation. We adapt them to the org’s account list. We do not paste a blog policy into production without an inventory.
{
"Statement": [
{
"Sid": "EnforceOrgPrincipalsOnS3",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:PrincipalOrgID": "${ORG_ID}"
},
"Bool": {
"aws:PrincipalIsAWSService": "false"
}
}
}
]
}
That sketch is a teaching shape. Service principals, encryption-context, and Break-glass roles need explicit conditions. A naive deny will break CloudFront origin access, Config delivery, or a log-archive writer. We test those paths in a non-production OU before the data OU.
Frequently Asked Questions
Do resource control policies replace service control policies?
No. SCPs limit principals inside member accounts, while RCPs limit what resource-based policies may grant, including to callers you do not own. You need both for a data perimeter. Dropping either one leaves a hole: your own roles can disable logging, or bucket and key policies stay the back door.
Which should we attach first in a mid-market org?
Keep the SCPs you already operate, then inventory resource policies in the data and log-archive accounts and attach RCPs there first, with exceptions for known integrations. App accounts come after, because that is where surprise bucket policies live. Attaching a tight RCP org-wide on day one without inventory is how you page the on-call for a vendor file drop.
Do RCPs apply to every AWS service?
No. AWS publishes the services and actions RCPs support. If your risk is a resource type outside that list, use SCPs, resource policies you own, VPC endpoints, and detective rules, and write that gap down. An auditor would rather see a scoped control plus a listed exception than a claim that RCPs cover the whole catalog.
How do RCPs interact with resource policies that already exist?
RCPs do not rewrite the bucket policy text. They constrain what that policy is allowed to grant at evaluation time, so an existing wildcard principal can start failing after the RCP attaches. Diff effective access with Access Analyzer and a staging OU before you attach to production data accounts.
If you want an architecture review tied to audit evidence — SCP and RCP JSON, attachments, denial tests, and the exception register — call 907-841-8407, email contact@rutagon.com, or use rutagon.com/contact.