How to Enforce AWS Region Restrictions Using IAM Policies to Prevent Cross-Region Resource Creation


62 views

Many AWS administrators face this scenario: despite clear organizational policies requiring resources to be created in eu-west-1, developers keep spawning EC2 instances and S3 buckets in us-east-1 due to AWS's default region behavior. This creates operational overhead, compliance issues, and unexpected costs.

The most effective solution is implementing IAM policies with explicit region restrictions. Here's a comprehensive policy example:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "NotAction": [
                "iam:*",
                "organizations:*",
                "route53:*",
                "cloudfront:*",
                "sts:*"
            ],
            "Resource": "*",
            "Condition": {
                "StringNotEquals": {
                    "aws:RequestedRegion": "eu-west-1"
                }
            }
        }
    ]
}

The policy works by:

  • Denying all actions except global services (IAM, Route53 etc.)
  • Using StringNotEquals condition to restrict to eu-west-1
  • Whitelisting necessary global services that don't operate regionally

For organizations with more complex needs:

// Multi-region restriction example
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "NotAction": [
                "iam:*",
                "route53:*"
            ],
            "Resource": "*",
            "Condition": {
                "ForAnyValue:StringNotEquals": {
                    "aws:RequestedRegion": [
                        "eu-west-1",
                        "eu-central-1"
                    ]
                }
            }
        }
    ]
}

Combine IAM policies with AWS Config rules for additional protection:

// AWS Config rule to detect non-compliant resources
{
    "ConfigRuleName": "restrict-resources-to-region",
    "Description": "Ensures resources are created only in eu-west-1",
    "Source": {
        "Owner": "AWS",
        "SourceIdentifier": "RESOURCES_IN_REGION"
    },
    "InputParameters": {
        "regionName": "eu-west-1"
    },
    "Scope": {
        "ComplianceResourceTypes": [
            "AWS::EC2::Instance",
            "AWS::S3::Bucket"
        ]
    }
}

For organizations using AWS Organizations, SCPs provide account-wide enforcement:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DenyAllOutsideIreland",
            "Effect": "Deny",
            "Action": "*",
            "Resource": "*",
            "Condition": {
                "StringNotEquals": {
                    "aws:RequestedRegion": "eu-west-1"
                },
                "Null": {
                    "aws:RequestedRegion": "false"
                }
            }
        }
    ]
}

To minimize disruption:

  • Set eu-west-1 as default in AWS CLI profiles
  • Configure region in SDK initialization for all applications
  • Create CloudFormation templates with hardcoded regions
# AWS CLI configuration example
[profile dev-team]
region = eu-west-1
output = json

Implement CloudWatch alarms to detect region policy violations:

aws cloudwatch put-metric-alarm \
    --alarm-name "CrossRegionResourceCreation" \
    --metric-name "ResourceCount" \
    --namespace "AWS/Usage" \
    --statistic "Sum" \
    --dimensions "Name=Resource,Value=EC2:Instances" "Name=Region,Value=us-east-1" \
    --period 300 \
    --evaluation-periods 1 \
    --threshold 1 \
    --comparison-operator "GreaterThanOrEqualToThreshold"

In multi-region AWS environments, developers frequently create resources in unintended regions (typically us-east-1) due to:

  • CLI default configurations
  • Forgetting to specify --region parameter
  • SDKs falling back to default regions
  • Console users not changing the region selector

Here's a comprehensive IAM policy that enforces eu-west-1 exclusively:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "NotAction": [
        "iam:*",
        "organizations:*",
        "budgets:*",
        "support:*",
        "sts:*",
        "cloudfront:*",
        "route53:*",
        "s3:Get*",
        "s3:List*"
      ],
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": "eu-west-1"
        }
      }
    }
  ]
}

The policy works through several critical mechanisms:

  • The Deny effect takes precedence over any Allow permissions
  • NotAction whitelists global services that don't require region specification
  • The aws:RequestedRegion condition key checks API calls
  • S3 read operations are exempted since bucket locations are region-specific

For organizations using AWS Organizations, implement this via SCPs:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyAllOutsideIreland",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": "eu-west-1",
          "aws:PrincipalArn": [
            "arn:aws:iam::*:role/breakglass-admin",
            "arn:aws:iam::*:user/emergency-user"
          ]
        }
      }
    }
  ]
}

Certain scenarios require special consideration:

  • Global services like IAM, Route53, and CloudFront
  • Cross-region operations for DR purposes
  • CI/CD pipelines that need multi-region deployments

Create explicit Allow policies for these use cases with strong naming conventions like AllowGlobalServices.

Complement IAM policies with AWS Config rules:

# config-rule-check-region.py
import boto3

def evaluate_compliance(config_item, rule_parameters):
    if config_item['resourceType'] not in rule_parameters['ApplicableResourceTypes']:
        return 'NOT_APPLICABLE'
        
    return 'NON_COMPLIANT' if config_item['awsRegion'] != rule_parameters['TargetRegion'] else 'COMPLIANT'

To minimize productivity impact:

  1. Set AWS_DEFAULT_REGION in shared environment variables
  2. Configure region in AWS CLI profiles
  3. Create IDE plugins that validate region selection