How to completely lock down an AWS account with a service control policy (SCP)

Prevent usage of inactive accounts

Author's image
Tamás Sallai
5 mins

Cloud accounts are usually seen as machinery, humming away 24/7, working all the time to fulfill a business need. This is certainly true for production applications, but most workloads are not like this. A developer goes to an office in the morning, works until late afternoon then goes home. When he is not working, the account just sits there, doing practically nothing.

And this happens for longer periods of time too. Someone might be working on a project then moves to another one just to come back to the first one months later.

The problem is that these inactive accounts still have all the power of a normal account, and they significantly increase the attack surface. It doesn't matter if an account is unused for months if a misplaced access key leads that it starts mining Bitcoin.

It would be better to inactivate AWS accounts when they are not needed. Fortunately, it is possible with service control policies.


Service control policies (SCPs) allow outside control over the account. Because of this, this is the most powerful policy type in AWS as there is no way around them, even the root user is limited. When an SCP is in place there is no way to remove it or negate its effect without accessing the master account.

The SCP is a feature of AWS Organizations which is the service that allows creating multiple accounts without going through the usual process of registration. With Organizations, you can create a new account programmatically and it allows managing hundreds and thousands of accounts.

The account that has billing information filled in is called the master account. This is the one you created the traditional way and charges your bank card. The accounts created using Organizations are called member accounts. These are dependent on the master account for billing.

Organizations allow the master account to attach SCPs to member accounts to limit what they can do. These are IAM policies that are evaluated before anything else in the policy evaluation logic. With SCPs an account can be limited in which regions it can use resources, what services it can use, and even what resources it can not delete or modify.

Without access to the master account these policies can not be changed or removed. This makes SCPs a great tool to enforce limitations in an account. The master account can not be limited by an SCP.

Lockdown policy

To prevent an account from being used an SCP that denies everything can be used:

	"Version": "2012-10-17",
	"Statement": [
			"Sid": "VisualEditor0",
			"Effect": "Deny",
			"Action": [
			"Resource": "*"

This puts an explicit deny on all actions on all resources, so everything that can be denied will be. Certain things like logging in or running the sts get-caller-identity can not be denied but everything related to resources can.

Note the policy ID as we'll need that to lock down an account:


Now that the account structure and the lockdown policy is ready, let's see how to use the AWS CLI to lock and unlock a member account!


Locking an account is attaching the lockdown policy to the account. Using the master account, use this call:

aws organizations attach-policy --target-id <account id> --policy-id <policy id>

This attaches the policy and that prevents anything from happening inside the account. No new instances can be run, no information can be queried about any resource inside it, even Lambda functions that are already added won't work. Here is how the IAM console looks like with an admin account:

And the best thing is that these restrictions also apply to the root user. In practice, as long as this policy is attached the account is unusable in any ways.


To unlock the account and get back to normal all you need to do is detach the policy with the master account:

aws organizations detach-policy --target-id <account id> --policy-id <policy id>

Auto unlock and lock script

Let's bring this a step further! With some scripting it is possible to unlock the account for a shell session and auto-lock it when the session ends. Effectively, this setup minimizes the time the account is usable to when it's needed which is perfect for a development account.

Here's a simple script to achieve that:

aws organizations detach-policy --target-id $ACCOUNTID --policy-id $POLICYID || true


aws organizations attach-policy --target-id $ACCOUNTID --policy-id $POLICYID

It unlocks the account then starts zsh. When the shell finishes by typing exit, the account gets locked automatically. Furthermore, it sets the AWS_PROFILE environment variable so that AWS CLI calls inside the shell session go to the member account. The AWS_SDK_LOAD_CONFIG is needed for the AWS SDK to support the profile setting.

A corresponding profile can be configured for the AWS CLI:

[profile sandbox]
role_arn = <role arn>

The above setting works when you have a user specified in the credentials file. But in my case it used the AWS_ACCESS_KEY_ID, the AWS_SECRET_ACCESS_KEY, and the AWS_SESSION_TOKEN environment variables and this setup does not play well with profiles. When both the keys and the profile is set in the environment the former takes precedence and that prevents the jump into the account.

The solution is to create a temporary credentials file and unset the keys from the environment:

echo "[default]\naws_access_key_id=$AWS_ACCESS_KEY_ID\naws_secret_access_key=$AWS_SECRET_ACCESS_KEY\naws_session_token=$AWS_SESSION_TOKEN" > $CRED_FILE

aws organizations detach-policy --target-id $ACCOUNTID --policy-id $POLICYID || true



aws organizations attach-policy --target-id $ACCOUNTID --policy-id $POLICYID


Service control policies are powerful security controls that are greatly underutilized. With some configurations, an account that is only usable when needed is possible, and a setup like this greatly improves security.

August 11, 2020
In this article