Right, let’s talk about Service Control Policies (SCPs). Think of them as the constitution for your AWS organization. IAM policies govern what a single user or role can do; SCPs govern what they can even be allowed to do in the first place. They’re the ultimate guardrail, the parental controls for your AWS accounts. No matter how permissive an IAM policy gets, an SCP can slam the door shut. This is incredibly powerful and, if you mess it up, incredibly dangerous.

You attach SCPs to Organizational Units (OUs) or directly to accounts. Any SCP attached to a parent applies to every child account in a “union of all policies” model. But here’s the critical bit: it’s a deny-by-default system. If any SCP—anywhere in the hierarchy—denies an action, it’s denied. Full stop. This is why you can accidentally “break” an entire account by attaching a poorly written SCP to its OU. I’ve done it. It’s a rite of passage. Welcome to the club.

The Two Flavors of SCP: Allow Lists and Deny Lists

There are two fundamental strategies, and AWS, in its infinite wisdom, forces you to choose one for your entire organization root. This is one of those “questionable choice” moments. You can’t mix and match at the root level, so choose wisely.

Allow List (a.k.a. Deny All / Safe Mode): This is the most secure and my strong recommendation for any organization that isn’t a chaotic free-for-all. You explicitly whitelist the services and actions you want to allow. Anything not on the list is implicitly denied. It’s the ultimate “no” until you say “yes.”

Deny List (a.k.a. Allow All): This is the default. You start with everything allowed and then create SCPs to deny specific things you don’t want, like restricting access to certain regions. It’s easier to set up but inherently less secure. One missed service in your deny list, and it’s wide open.

You set this at the root level with a policy called FullAWSAccess. It’s a magic policy that allows everything. For an allow list setup, you remove this policy from the root and attach it only to OUs or accounts where you want full access. It feels backward, but it’s correct.

Anatomy of a Well-Crafted SCP

An SCP is just a JSON policy document, much like an IAM policy, but with a few key differences. The most important one: you cannot use principal elements (Principal, NotPrincipal). SCPs are about the what (actions and resources), not the who.

Let’s look at a classic example: preventing members of a “Sandbox” OU from leaving the us-east-1 region. This is a Deny List strategy.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DenyAllOutsideUseast1",
            "Effect": "Deny",
            "NotAction": [
                "a4b:*",
                "acm:*",
                "aws-portal:*",
                "budgets:*",
                "ce:*",
                "chime:*",
                "cloudfront:*",
                "config:*",
                "devicesupport:*",
                "ec2:DescribeRegions",
                "ec2:DescribeAvailabilityZones",
                "health:*",
                "iam:List*",
                "iam:Get*",
                "iam:ListRoles",
                "iam:ListUsers",
                "iam:ListAccountAliases",
                "importexport:*",
                "kms:List*",
                "kms:Get*",
                "kms:Describe*",
                "organizations:Describe*",
                "organizations:List*",
                "pricing:*",
                "s3:GetAccountPublic*",
                "s3:ListAllMyBuckets",
                "s3:HeadBucket",
                "s3:ListBucket",
                "support:*",
                "trustedadvisor:*",
                "awsbillingconsole:*",
                "license-manager:List*"
            ],
            "Resource": "*",
            "Condition": {
                "StringNotEquals": {
                    "aws:RequestedRegion": "us-east-1"
                }
            }
        }
    ]
}

Notice the NotAction? This is the clever part. The policy says “Deny any action that is not in this list, if the region is not us-east-1.” The list of actions in NotAction are mostly read-only APIs that are global or necessary for the console to even function. Without these exemptions, your users would get a confusing mess of permission errors just trying to log in. This is the pitfall most people stumble into. You must always carve out the global and necessary read-only calls.

The Allow List Power Move

Now, let’s see the more secure Allow List approach. Here, we’ll define a policy for a “Log Archive” account that can only interact with S3 and CloudWatch Logs.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowOnlyS3AndLogs",
            "Effect": "Allow",
            "Action": [
                "s3:*",
                "logs:*"
            ],
            "Resource": "*"
        }
    ]
}

That’s it. Because the organization root is in Allow List mode, this policy attached to the Log Archive account acts as a whitelist. The account can only perform S3 and CloudWatch Logs actions. Everything else—EC2, IAM, Lambda—is denied, even if an IAM policy inside the account allows it. This is the true power of SCPs: creating functionally isolated accounts.

Best Practices and Gotchas

  1. Test in a Dummy OU: Never test a new SCP directly on a production account or OU. Create a sacrificial test OU, move a test account into it, and apply the SCP there first. Your future self will thank you when you don’t lock every engineer out of the prod environment at 3 PM on a Friday.
  2. The Management Account is a Superuser: SCPs do not apply to the management account. None of them. The master account can do anything, which is why you should use it for literally nothing else except organization management. This is a frequently misunderstood edge case.
  3. Beware of Service-Linked Roles: Some AWS services need to create roles to function. A overly restrictive SCP can prevent this and break the service. You often need to add exemptions for iam:CreateServiceLinkedRole and similar actions.
  4. Use the SCP Policy Simulator: In the IAM console, there’s a Policy Simulator. Under Simulation Settings, you can choose to simulate SCPs. Use it. It’s your best friend for avoiding the aforementioned 3 PM Friday catastrophe.

SCPs are the bedrock of a multi-account strategy. They let you enforce hard security boundaries, ensuring your developers in the dev account can’t accidentally (or “accidentally”) drain your budget with Bitcoin-mining GPUs in prod. They require careful thought and testing, but once implemented, they let you sleep at night. And that’s no joke.