Organizations (SCPs)
SCP: restrict member accounts to approved Regions
A Region-deny SCP with global-service exemptions and a break-glass role, plus the companion SCP that stops member accounts from creating or taking over that role.
Published · Updated .
The problem
Data residency and cost control often mean workloads may run only in approved Regions, here eu-west-1 and eu-central-1. A service control policy (SCP) enforces that for every IAM user and role in the member accounts it covers, including their root users, and no IAM policy in the account can override it.
The hard part is the exceptions: global services with a single endpoint in us-east-1, and an emergency role. The previous version of this page exempted arn:aws:iam::*:role/BreakGlassAdmin and stopped there. That was a bypass: aws:PrincipalArn names the calling role, not who controls it, so any member-account administrator with iam:CreateRole or iam:UpdateAssumeRolePolicy could create or take over a role with that name.
This version adds a companion SCP that stops anyone in a member account from creating, changing or assuming that role, and says who can still maintain it.
The policies
Region-deny SCP with global-service exemptions
Service control policy (SCP). Attach to: Workload OUs, after testing on a sandbox OU. It has no effect on the management account.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyOutsideApprovedRegions",
"Effect": "Deny",
"NotAction": [
"a4b:*",
"acm:*",
"aws-marketplace-management:*",
"aws-marketplace:*",
"aws-portal:*",
"billing:*",
"budgets:*",
"ce:*",
"chime:*",
"cloudfront:*",
"config:*",
"consolidatedbilling:*",
"cur:*",
"directconnect:*",
"ec2:DescribeRegions",
"ec2:DescribeTransitGateways",
"ec2:DescribeVpnGateways",
"ecr-public:*",
"fms:*",
"freetier:*",
"globalaccelerator:*",
"health:*",
"iam:*",
"importexport:*",
"invoicing:*",
"kms:*",
"mobileanalytics:*",
"networkmanager:*",
"organizations:*",
"payments:*",
"pricing:*",
"route53-recovery-cluster:*",
"route53-recovery-control-config:*",
"route53-recovery-readiness:*",
"route53:*",
"route53domains:*",
"s3:GetAccountPublic*",
"s3:ListAllMyBuckets",
"s3:ListMultiRegionAccessPoints",
"s3:PutAccountPublic*",
"savingsplans:*",
"shield:*",
"sts:*",
"support:*",
"supportapp:*",
"supportplans:*",
"tax:*",
"trustedadvisor:*",
"waf-regional:*",
"waf:*",
"wafv2:*",
"wellarchitected:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"eu-central-1",
"eu-west-1"
]
},
"ArnNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/BreakGlassAdmin"
}
}
}
]
}The form generators on this site don't edit scp documents; copy it, then check it with IAM Access Analyzer as shown below.
Statement by statement
- DenyOutsideApprovedRegions: denies every action not in NotAction when the request goes to a Region other than eu-central-1 or eu-west-1 and the caller isn't BreakGlassAdmin; both conditions must match. The list is AWS's current Region-deny example (in the SCP examples repository linked from the Organizations User Guide) plus billing, payments, tax, Savings Plans, support-plan and ECR Public entries from Control Tower's Region deny control. It covers ACM and WAF (CloudFront needs both in us-east-1), billing, Health, Support, STS and account-level S3 calls. AWS's list also exempts kms:* and config:*, which are Regional, so they work in every Region; remove them if residency requires it, then retest (Route 53 DNSSEC needs a KMS key in us-east-1).
Companion SCP: protect the exempt role and the role that maintains it
Service control policy (SCP). Attach to: The organization root, or at least everywhere the Region-deny SCP applies.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ProtectExemptAndDeployerRoles",
"Effect": "Deny",
"Action": [
"iam:AttachRolePolicy",
"iam:CreateRole",
"iam:DeleteRole",
"iam:DeleteRolePermissionsBoundary",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:PutRolePermissionsBoundary",
"iam:PutRolePolicy",
"iam:TagRole",
"iam:UntagRole",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription"
],
"Resource": [
"arn:aws:iam::*:role/BreakGlassAdmin",
"arn:aws:iam::*:role/OrganizationAccountAccessRole"
],
"Condition": {
"ArnNotLike": {
"aws:PrincipalArn": "arn:aws:iam::*:role/OrganizationAccountAccessRole"
}
}
},
{
"Sid": "OnlySecurityAccountMayAssumeExemptRole",
"Effect": "Deny",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::*:role/BreakGlassAdmin",
"Condition": {
"StringNotEquals": {
"aws:PrincipalAccount": "444455556666"
}
}
}
]
}The form generators on this site don't edit scp documents; copy it, then check it with IAM Access Analyzer as shown below.
Statement by statement
- ProtectExemptAndDeployerRoles: denies creating, deleting or changing BreakGlassAdmin and OrganizationAccountAccessRole: trust policy, inline and attached policies, permissions boundary, settings such as maximum session duration, and tags (which matter once any policy keys on them). The only exempt caller is OrganizationAccountAccessRole, which AWS Organizations creates in the accounts it creates and which trusts the management account. Because that role is itself protected, nobody in a member account can create it, re-point its trust or widen it to become the exempt caller. Every exemption by name needs this: the statement that exempts a name must also protect it.
- OnlySecurityAccountMayAssumeExemptRole: denies sts:AssumeRole on BreakGlassAdmin to every covered principal outside the security account 444455556666. SCPs restrict the caller, so this holds even if the trust policy is too broad. Management-account principals aren't subject to SCPs; for them the trust policy alone decides.
Trust policy for BreakGlassAdmin
IAM role trust policy. Attach to: BreakGlassAdmin in each member account, created through OrganizationAccountAccessRole.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowBreakGlassOperatorsOnly",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::444455556666:role/BreakGlassOperator"
},
"Action": "sts:AssumeRole"
}
]
}The form generators on this site don't edit trust policy documents; copy it, then check it with IAM Access Analyzer as shown below.
Statement by statement
- AllowBreakGlassOperatorsOnly: only BreakGlassOperator in the security account can assume the role. Never trust the member account (arn:aws:iam::111122223333:root), a principal in it or a service principal. Prefer inline or AWS managed permissions: the companion SCP doesn't block iam:CreatePolicyVersion, so a customer managed policy could be edited to break or widen the role, though not to exempt anyone new.
Before you start
- Organizations with all features and SCPs enabled, and FullAWSAccess still attached: these SCPs only deny.
- A sandbox OU with a test account you can break.
- OrganizationAccountAccessRole in each member account, trusting only the management account. Organizations creates it in accounts it creates; for an invited account, create it before the account joins, because afterwards nobody inside can.
- A security account (444455556666) with a BreakGlassOperator role, MFA sign-in and an alert on use, plus an organization trail.
Deploy it step by step
- Choose the Regions and find what runs outside them. Service last accessed data shows which services an OU uses; CloudTrail's awsRegion field shows where. Watch for single-Region services: Lambda@Edge is created in us-east-1, and the IAM User Guide places Amazon Q Developer in chat applications and AWS Device Farm in us-west-2.
Commands (replace <placeholders>) aws iam generate-organizations-access-report --entity-path o-exampleorgid/r-<root-id>/ou-<ou-id> aws iam get-organizations-access-report --job-id <job-id> - Decide which role maintains the exempt role. Whatever creates BreakGlassAdmin joins your trust boundary. This page uses OrganizationAccountAccessRole, which by default only management-account principals can assume, and they already control every SCP. In Control Tower, use AWSControlTowerExecution: an ordinary role that SCPs apply to, which Control Tower's mandatory controls exempt and protect the same way. Service-managed StackSets deploy through a service role named stacksets-exec-* in each target account. SCPs apply to it, so exempting it needs that wildcard in both statements and trusts every StackSets administrator, delegated ones included. Self-managed StackSets run from the management account with --execution-role-name OrganizationAccountAccessRole work unchanged.
Commands (replace <placeholders>) aws iam get-role --role-name OrganizationAccountAccessRole --query Role.AssumeRolePolicyDocument - Validate, create and attach to a sandbox OU. Attach the companion first. AWS recommends testing SCPs in a separate OU or organization before widening the scope.
Commands (replace <placeholders>) aws accessanalyzer validate-policy --policy-type SERVICE_CONTROL_POLICY --policy-document file://scp.json aws accessanalyzer validate-policy --policy-type RESOURCE_POLICY --validate-policy-resource-type AWS::IAM::AssumeRolePolicyDocument --policy-document file://trust-policy.json aws organizations create-policy --type SERVICE_CONTROL_POLICY --name ProtectExemptRoles --description 'Protect BreakGlassAdmin and its deployer' --content file://protect-exempt-roles.json aws organizations create-policy --type SERVICE_CONTROL_POLICY --name ApprovedRegionsOnly --description 'Deny requests outside approved Regions' --content file://region-deny.json aws organizations attach-policy --policy-id <protect-policy-id> --target-id <sandbox-ou-id> aws organizations attach-policy --policy-id <region-deny-policy-id> --target-id <sandbox-ou-id> - Create BreakGlassAdmin through the deployer. Assume OrganizationAccountAccessRole in the sandbox account and create the role. If it already exists, inspect it: someone may have created it before the protection applied. With CloudFormation, use the deployer's credentials, not a stack service role, which IAM would treat as the caller.
Commands (replace <placeholders>) aws sts assume-role --role-arn arn:aws:iam::111122223333:role/OrganizationAccountAccessRole --role-session-name <your-name>-break-glass-setup aws iam create-role --role-name BreakGlassAdmin --assume-role-policy-document file://break-glass-trust.json --max-session-duration 3600 aws iam attach-role-policy --role-name BreakGlassAdmin --policy-arn arn:aws:iam::aws:policy/AdministratorAccess - Roll out one OU at a time. Once the checks pass, attach the companion SCP to the root, where it also covers future accounts, then the Region-deny SCP to one workload OU at a time, watching CloudTrail for denials.
Commands (replace <placeholders>) aws organizations attach-policy --policy-id <protect-policy-id> --target-id <root-id> aws organizations attach-policy --policy-id <region-deny-policy-id> --target-id <workload-ou-id> - Update the list in place. update-policy changes the policy everywhere it is attached, immediately, so trial changes as a separate policy on the sandbox OU. AWS says its example might not include the latest global services.
Commands (replace <placeholders>) aws accessanalyzer validate-policy --policy-type SERVICE_CONTROL_POLICY --policy-document file://scp.json aws organizations update-policy --policy-id <region-deny-policy-id> --content file://region-deny.json
Verify it without changing anything
These checks only read state, simulate, or use dry runs, so they are safe to run in any account. Exact error wording varies by service and over time.
Dry run in an approved Region
From the sandbox account, as an ordinary administrator role.
aws ec2 describe-vpcs --region eu-west-1 --dry-runExpected: DryRunOperation, EC2's way of saying the call would have succeeded. UnauthorizedOperation means your IAM policy doesn't allow ec2:DescribeVpcs, so the next check would prove nothing.
Dry run and a read-only call in an unapproved Region
EC2 documents DryRun as a permission check but not how it treats SCPs, so confirm with the real, read-only call.
aws ec2 describe-vpcs --region us-west-2 --dry-run
aws ec2 describe-vpcs --region us-west-2
aws sts decode-authorization-message --encoded-message <encoded-message>Expected: UnauthorizedOperation for both. An encoded message, once decoded (this needs sts:DecodeAuthorizationMessage), should show an explicit deny; other services say 'explicit deny in a service control policy'. Success means the SCP isn't in effect for this principal.
Global services still work
The S3 call targets an unapproved Region's endpoint on purpose.
aws iam get-account-summary
aws sts get-caller-identity
aws s3api list-buckets --region us-west-2Expected: All three succeed if IAM allows them. An SCP denial on list-buckets means s3:ListAllMyBuckets is missing from NotAction.
Simulate the exemption
The simulator evaluates SCPs and their conditions without sending a request, but doesn't fill in aws:PrincipalArn or aws:RequestedRegion, so pass both.
aws iam simulate-principal-policy --policy-source-arn arn:aws:iam::111122223333:role/BreakGlassAdmin --action-names ec2:RunInstances --context-entries "ContextKeyName=aws:PrincipalArn,ContextKeyValues=arn:aws:iam::111122223333:role/BreakGlassAdmin,ContextKeyType=string" "ContextKeyName=aws:RequestedRegion,ContextKeyValues=us-west-2,ContextKeyType=string" --query "EvaluationResults[].[EvalActionName,EvalDecision,OrganizationsDecisionDetail.AllowedByOrganizations]" --output table
aws iam simulate-principal-policy --policy-source-arn arn:aws:iam::111122223333:role/<workload-admin-role> --action-names ec2:RunInstances --context-entries "ContextKeyName=aws:PrincipalArn,ContextKeyValues=arn:aws:iam::111122223333:role/<workload-admin-role>,ContextKeyType=string" "ContextKeyName=aws:RequestedRegion,ContextKeyValues=us-west-2,ContextKeyType=string" --query "EvaluationResults[].[EvalActionName,EvalDecision,OrganizationsDecisionDetail.AllowedByOrganizations]" --output tableExpected: AllowedByOrganizations true for BreakGlassAdmin and false for the workload role; EvalDecision also includes identity policies. An empty Organizations column means SCPs weren't evaluated.
Confirm that BreakGlassAdmin can't be taken over
Both calls are read-only.
aws iam simulate-principal-policy --policy-source-arn arn:aws:iam::111122223333:role/<workload-admin-role> --action-names iam:CreateRole iam:DeleteRole iam:UpdateAssumeRolePolicy iam:AttachRolePolicy iam:PutRolePolicy sts:AssumeRole --resource-arns arn:aws:iam::111122223333:role/BreakGlassAdmin --context-entries "ContextKeyName=aws:PrincipalArn,ContextKeyValues=arn:aws:iam::111122223333:role/<workload-admin-role>,ContextKeyType=string" --query "EvaluationResults[].[EvalActionName,EvalDecision,OrganizationsDecisionDetail.AllowedByOrganizations]" --output table
aws iam get-role --role-name BreakGlassAdmin --query Role.AssumeRolePolicyDocumentExpected: AllowedByOrganizations false for every action, and a trust policy naming only arn:aws:iam::444455556666:role/BreakGlassOperator. Simulated as OrganizationAccountAccessRole, the IAM actions should be allowed by Organizations: that is the maintenance path.
Confirm where the policies are attached
In the management account. describe-effective-policy doesn't support SCPs.
aws organizations list-targets-for-policy --policy-id <protect-policy-id>
aws organizations list-targets-for-policy --policy-id <region-deny-policy-id>Expected: The companion SCP is attached at or above every target of the Region-deny SCP. A target with only the Region-deny SCP is a bypass.
Common mistakes and troubleshooting
Exempting a role without protecting it
The original bug. The exemption reaches every account the Region-deny SCP covers, so the companion must cover them too.
ARNs that don't match exactly
A role's aws:PrincipalArn includes its path, so role/BreakGlassAdmin matches only the default path. If you use a pattern such as role/BreakGlass*, protect the same pattern. An IAM user with that name isn't exempt: its ARN contains user/.
Endpoints and single-Region services
STS global-endpoint requests report us-east-1 even when served nearby, so keep sts:* exempt. Lambda@Edge and CloudFront metrics need us-east-1, which this list doesn't exempt. Some console pages show errors for denied Regions. Bedrock cross-Region inference needs AWS's CRIS variant.
Expecting the endpoint to control where data goes
aws:RequestedRegion controls the endpoint, not the effect: S3 replication writes to other Regions, and CreateBucket can target another Region (restrict it with s3:LocationConstraint).
Control Tower landing zones
Control Tower's own Region deny exempts AWSControlTowerExecution and several aws-controltower roles. This SCP doesn't, so it can break Control Tower in governed Regions you deny. Use Control Tower's control (CT.MULTISERVICE.PV.1 for an OU), or exempt and protect those roles too.
Limitations and production considerations
- SCPs don't apply to the management account, to service-linked roles or to principals from other accounts.
- Remaining trust: whoever can assume OrganizationAccountAccessRole, whoever can change SCPs (the management account and any delegated policy administrator) and the operators in 444455556666 can still use or change BreakGlassAdmin. The companion SCP removes member-account administrators from that list, not the management account.
- The NotAction list reflects AWS's examples on 2026-10-29 and needs upkeep; kms:* and config:* are compatibility choices, not requirements.
- An SCP can be at most 10,240 characters; the console strips whitespace outside strings, the CLI saves exactly what you send. At most 10 SCPs can be attached directly to a root, OU or account.
- Access Analyzer and this site's checks confirm structure, not how AWS will authorize a request. Test in a sandbox, and treat the simulator as a model.
Official AWS documentation
- Service control policies (SCPs)
- aws:RequestedRegion
- Service control policy examples (AWS Organizations)
- AWS: Denies access to AWS based on the requested Region (IAM User Guide)
- Region deny control applied to the OU (AWS Control Tower)
- Mandatory controls (AWS Control Tower)
- Accessing a member account that has OrganizationAccountAccessRole
- Creating OrganizationAccountAccessRole for an invited account
- Activate trusted access for StackSets with AWS Organizations
- Grant self-managed permissions (CloudFormation StackSets)
- IAM policy testing with the IAM policy simulator
- DescribeVpcs (DryRun parameter)
- Troubleshoot access denied error messages
- Manage AWS STS in an AWS Region
- Restrictions on Lambda@Edge
- Quotas and service limits for AWS Organizations
Account IDs, bucket and organization names are AWS documentation placeholders. Test in a non-production account before use (see the methodology).