How to Create an IAM Policy to Restrict AWS User Access to a Specific VPC


5 views

Many AWS administrators need to restrict users to working within a specific Virtual Private Cloud (VPC) for security and compliance reasons. While IAM policies provide granular control, crafting the correct policy for VPC restrictions can be tricky.

The policy example in the question contains several subtle issues that prevent it from working as intended:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:*Vpc*",
                "ec2:*Subnet*",
                // ... other actions ...
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "ec2:Vpc": "arn:aws:ec2:region:account:vpc/vpc-id"
                }
            }
        }
    ]
}

Here's a working version that properly restricts access to a specific VPC and its resources:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeVpcs",
                "ec2:DescribeSubnets",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeRouteTables",
                "ec2:DescribeNetworkAcls",
                "ec2:DescribeInternetGateways",
                "ec2:DescribeVpcPeeringConnections"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:CreateSubnet",
                "ec2:CreateSecurityGroup",
                "ec2:CreateRouteTable",
                "ec2:CreateNetworkAcl",
                "ec2:CreateTags"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "ec2:Vpc": "arn:aws:ec2:region:account:vpc/vpc-id"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:RunInstances",
                "ec2:StartInstances",
                "ec2:StopInstances",
                "ec2:TerminateInstances"
            ],
            "Resource": [
                "arn:aws:ec2:region:account:instance/*",
                "arn:aws:ec2:region:account:volume/*",
                "arn:aws:ec2:region:account:network-interface/*",
                "arn:aws:ec2:region:account:subnet/*",
                "arn:aws:ec2:region:account:security-group/*"
            ],
            "Condition": {
                "StringEquals": {
                    "ec2:Vpc": "arn:aws:ec2:region:account:vpc/vpc-id"
                }
            }
        }
    ]
}

The original policy had several technical limitations:

  • Wildcard actions ("ec2:*Vpc*") don't work with resource-level permissions
  • The ec2:Vpc condition key isn't supported for all EC2 actions
  • Describe* actions need to be separated as they don't support resource-level permissions

When testing your policy:

aws ec2 describe-vpcs --vpc-ids vpc-12345678
aws ec2 run-instances --image-id ami-12345678 --instance-type t2.micro --subnet-id subnet-12345678
aws ec2 create-security-group --group-name MySecurityGroup --description "My security group" --vpc-id vpc-12345678

When managing AWS resources, you might need to restrict users to work exclusively within a specific VPC. The initial approach often involves crafting an IAM policy with conditions targeting the VPC ARN. However, as many developers discover, this doesn't always work as expected.

The policy you've tried is close but misses some critical aspects of how AWS evaluates VPC-related permissions:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:*Vpc*",
                "ec2:*Subnet*",
                "ec2:*Gateway*",
                "ec2:*Vpn*",
                "ec2:*Route*",
                "ec2:*Address*",
                "ec2:*SecurityGroup*",
                "ec2:*NetworkAcl*",
                "ec2:*DhcpOptions*",
                "ec2:RunInstances",
                "ec2:StopInstances",
                "ec2:StartInstances",
                "ec2:TerminateInstances",
                "ec2:Describe*"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "ec2:Vpc": "arn:aws:ec2:region:account:vpc/vpc-id"
                }
            }
        }
    ]
}

Here's an improved policy that actually works for restricting access to a specific VPC:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:RunInstances",
                "ec2:StartInstances",
                "ec2:StopInstances",
                "ec2:TerminateInstances"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "ec2:Vpc": "arn:aws:ec2:region:account:vpc/vpc-id"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:AttachVolume",
                "ec2:DetachVolume"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "ec2:InstanceVpc": "arn:aws:ec2:region:account:vpc/vpc-id"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:CreateSubnet",
                "ec2:CreateNetworkInterface",
                "ec2:CreateSecurityGroup"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "ec2:Vpc": "arn:aws:ec2:region:account:vpc/vpc-id"
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:Describe*"
            ],
            "Resource": "*"
        }
    ]
}

1. The condition key ec2:Vpc works for action-specific resources, not all VPC-related operations

2. Some actions require ec2:InstanceVpc instead of ec2:Vpc

3. Describe actions generally need to be unrestricted for the console to function properly

Always verify your policy using:

1. IAM Policy Simulator
2. AWS CLI dry-run commands
3. Test user accounts before applying to production

• Overusing wildcards in actions that don't support VPC conditions

• Forgetting that some resources (like EC2 instances) inherit VPC context from their placement

• Not accounting for dependent services that might need cross-VPC access