Secure AWS Organizations with Service Control policies

Use the Service Control Policy feature to manage access to resources in an AWS multi-account setup based on AWS Organizations

The typical AWS journey begins with a single account. The so-called root user creates the account and has super-user privileges over everything. The root user creates a first IAM user with admin privileges and everything else happens from there. With only a single account, it’s not possible to cleanly separate dev and prod workloads and the IAM principals that access them. You could have dev and prod IAM users that can only access their respective resources managed by IAM policies and you could only hope that the IAM policies do not contain errors. So the next logical step would be to use two independent accounts, one for dev and one for prod workloads. Now you can guarantee a clean separation, since AWS accounts are isolated from each other.

Every new account also needs its own root user with a new set of credentials, tightly secured. Workloads are now separated from each other and IAM users will only see what’s inside their accounts. But what if a developer with an assigned IAM User in the dev account needs to deploy something in the prod account as well? You want to avoid creating users in both accounts for the same person. This can be achieved by using cross-account roles. In short, you would create an IAM role in the production account with the necessary permissions for handling deployments and allow the IAM user in the dev account to assume this role. You can find an example here. This works fine as long as the number of AWS accounts is small.

Different scenario. Let’s assume you hand over the task of writing IAM policies for IAM users to someone you trust as administrator. How can you guarantee that certain operations are never allowed by mistake without manually auditing every change in every policy[1]?
You’d like to use some higher entity that could limit permissions even for IAM users with the managed IAM policy AdministratorAccess attached. Historically that would be the root user, but it should not be used for tasks such as this, only for account administration.

What we’ve learned so far is that an AWS account is the natural and isolated unit to work with. But a setup with more than a handful of accounts is too complex to manage. We’d also like to centralize user-management and account-management when the number of users or accounts increases.

With the introduction of the account management service AWS Organizations, the mentioned issues could be addressed. AWS Organizations lets you logically group accounts into so called Organizational Units (OUs) where you then apply some sort of policy based access controls. Here we focus on the access control feature, the Service Control Policy (SCP).

A multi-account scenario using AWS Organizations

When you create an AWS Organization[2] you make one of your accounts the management account of the organization and the remaining ones become member accounts. In the management account you then create service control policies that you attach to your OUs, containing the member accounts[3], thus restricting permissions on an account level. These permissions affect every IAM entity in an account. Even the root users actions can be limited.

Let’s assume you used AWS Organizations to set up a multi-account environment for managing multiple tenants. Each tenant has its own sandbox account with its own set of managed IAM users (tenant users). Your internal users are managed by AWS SSO in the management account of the AWS Organization. Let’s further assume that you fully trust your internal users including account root users. On the contrary, you’d like to restrict the privileges of tenant users for safety reasons.

A quick solution could be to check the tenant users’ attached policies regularly for correctness and against the principle of least privilege, but at any time, a policy could be overwritten by a malicious tenant user with IAM admin privileges or by mistake with an allow rule. With the help of SCPs you just set your guardrails once and guarantee that they won’t be overwritten.

If you check the diagram of the Policy evaluation logic shown below, you will see that the organization SCPs are interpreted before any identity-based policy and will overrule what comes after. In other words, if you explicitly deny a certain action with a SCP, it is not even allowed for IAM users with the managed AdministratorAccess policy attached or the root user.

AWS Policy evaluation logic diagram

AWS Policy evaluation logic diagram. Source: AWS Documentation.

In the next section, after showing the elements of a SCP, we will present an example scenario where a SCP will be used to manage access to the Cost & Billing Dashboard depending on which type of Principal is used to access it.

SCP: Deny access to Cost and Billing functions

Every SCP is a JSON file containing the following elements: A Version element, defining the version of the policy language and one or more Statement elements, serving as containers for the actual policy. The policy statement contains Effect, Action, Resource and optional Condition Elements. The Effect can be either Deny or Allow. Actionlists actions of a service, like s3:GetObject, and Resource defines the object(s) the action applies to. Now, let’s see what our SCP to secure our Cost & Billing Dashboard can look like.

 "Version": "2012-10-17",
 "Statement": [
     "Condition": {
         "ArnNotLike": {
             "aws:PrincipalARN": [ "arn:aws:iam::*:role/aws-reserved/*/AWSReservedSSO_*",
                                   "arn:aws:iam::*:root" ]
     "Effect": "Deny",
     "Action": [
     "Resource": "*"

This SCP is used to deny access ("Effect": "Deny"), making it a deny list. In the Action block of the policy, we use the prefixes aws-portal, cur and ce, and * wildcards for the action, to block access to AWS Billing, Cost and Usage Report and the AWS Cost Explorer Service, respectively[4]. Here we also use the wildcard * for the Resource element.

In this scenario, the member accounts of our AWS Organization are owned by different tenants which use IAM users or roles. We don’t want to give access to the services defined in the Action block to the tenant users, which has already been achieved by this SCP. What we want to allow is access for the internal users that are managed by AWS SSO and use roles to get inside the organization member accounts. For this we need a Condition element in the policy that is using an Amazon Resource Name (ARN) condition operator ArnNotLike to filter out the Principal ARN arn:aws:iam::*:role/aws-reserved/*/AWSReservedSSO_* of the SSO users and also the root user.


In this blog post, we briefly looked at the history and use cases that led AWS to introduce AWS Organizations and Service Control Policies to safely manage access to resources in a multi-account environment. We know we only touched the surface of a complex topic! If you are interested in learning more about best practices, we encourage you to check out these articles (AWS for Industries, AWS Cloud Operations & Migrations Blog, AWS Security Blog) and get in touch with us.


  1. Ignoring Permissions boundaries here.

  2. Preferably using AWS Control Tower set up by superwerker. superwerker bundles Control Tower and more services together and installs them in an automated fashion. Please see our introductory superwerker blog post.

  3. SCPs do not affect the management account, its IAM users, IAM roles or root user. You can find
    best practices for the management account here.

  4. For finding the right actions and service prefixes the Service Authorization Reference helps.