Skip to content

How to make VPC changes in CDK without breaking the stack?

0

I wanted to understand the right way to create a VPC and its resources, in a way that later changes will cause the stack to break and without having to delete the entire VPC to apply such changes (details below) I was surprised to learn that using a very basic stack and applying a small change will result in a failure of the stack due to a conflict in the CIRD block.

I would love to understand the right way to create the resources.

Here is a simple example for creating a simple VPC and then making a change in the subnets of the vpc:

create the vpc with private isolated subnet:

class PersistentVpcTestStack(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)

        cider_block = '10.18.0.0/16'

        vpc = ec2.Vpc(
            self,
            'TestVpc',
            max_azs=3,
            subnet_configuration=[
                ec2.SubnetConfiguration(
                    name='PrivateIsolated',
                    subnet_type=ec2.SubnetType.PRIVATE_ISOLATED,
                    cidr_mask=24,
                )
            ],
            ip_addresses=ec2.IpAddresses.cidr(cider_block),
            restrict_default_security_group=True,
        )

If I then want to add a public security group, I might change it to this: The change there is only to update the subnet_configuration to include the public subnet:

        vpc = ec2.Vpc(
            self,
            'TestVpc',
            max_azs=3,
            subnet_configuration=[
                ec2.SubnetConfiguration(
                    name='PublicSubnet',
                    subnet_type=ec2.SubnetType.PUBLIC,
                    cidr_mask=24,
                ),
                ec2.SubnetConfiguration(
                    name="PrivateSubnetWithEgress",
                    subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS,
                    cidr_mask=24,
                ),
            ],
            ip_addresses=ec2.IpAddresses.cidr(cider_block),
            restrict_default_security_group=True,
        )

This results in a broken stack since there is a conflict between the CIDR blocks of the existing subnet(s) and one(s) being created. How should I create the resources to avoid this? or how should apply such changes?

7:55:42 AM | CREATE_FAILED | AWS::EC2::Subnet | TestVpc/PublicSubnetSubnet1/Subnet Resource handler returned message: "The CIDR '10.18.0.0/24' conflicts with another subnet (Service: Ec2, Status Code: 400, Request ID: e9db5c56-b1e0-4c31-a2dd-25f6fc8ccfe0) (SDK Attempt Co unt: 1)" (RequestToken: e5d889cc-518d-75c2-94dc-4087c4fe3e03, HandlerErrorCode: AlreadyExists) 7:55:42 AM | CREATE_FAILED | AWS::EC2::Subnet | TestVpc/PublicSubnetSubnet2/Subnet Resource handler returned message: "The CIDR '10.18.1.0/24' conflicts with another subnet (Service: Ec2, Status Code: 400, Request ID: 2d3e4d89-19cc-466a-97e5-523f15c191ef) (SDK Attempt Co unt: 1)" (RequestToken: e676da70-b2f4-f895-4195-ebb481e19b95, HandlerErrorCode: AlreadyExists)

Thank you for your help!

I also asked this question here and will update on any useful info: https://github.com/aws/aws-cdk/discussions/34681

1 Answer
0

When making changes to VPC configurations in AWS CDK, you need to be careful to avoid conflicts with existing resources. Here are some approaches to handle VPC changes without breaking your stack:

Option 1: Use Vpc.fromLookup() for Existing VPCs

If you need to modify a VPC that's already deployed, the safest approach is to use Vpc.fromLookup() to reference the existing VPC rather than trying to modify it directly. This avoids creating CloudFormation exports and gives you more flexibility when stacks need to be updated independently:

vpc = ec2.Vpc.fromLookup(
self,
"ExistingVpc",
# You can use various lookup options like vpc_id, tags, etc.
vpc_id="vpc-12345"  # or use other lookup criteria
)

Then you can create new subnets or other resources within this VPC without modifying the VPC definition itself.

Option 2: Plan Your Subnet CIDR Blocks Carefully

When initially creating your VPC, plan your CIDR allocations to accommodate future growth. For example, reserve specific CIDR ranges for different subnet types:

vpc = ec2.Vpc(
self,
'TestVpc',
max_azs=3,
subnet_configuration=[
ec2.SubnetConfiguration(
name='PublicSubnet',
subnet_type=ec2.SubnetType.PUBLIC,
cidr_mask=24,
# Specify exact CIDR ranges if needed
),
ec2.SubnetConfiguration(
name="PrivateSubnetWithEgress",
subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS,
cidr_mask=24,
),
ec2.SubnetConfiguration(
name='PrivateIsolated',
subnet_type=ec2.SubnetType.PRIVATE_ISOLATED,
cidr_mask=24,
)
],
ip_addresses=ec2.IpAddresses.cidr('10.18.0.0/16'),
)

Option 3: Create Subnets Manually

For more control, you can create the VPC with subnetConfiguration: [] and then manually create and configure subnets:

# Create VPC without automatic subnet configuration
vpc = ec2.Vpc(
self,
'TestVpc',
ip_addresses=ec2.IpAddresses.cidr('10.18.0.0/16'),
# Disable standard subnet configuration
subnet_configuration=[]
)

# Manually create subnets with specific CIDR blocks
public_subnet = ec2.PublicSubnet(
self,
'PublicSubnet1',
vpc_id=vpc.vpc_id,
availability_zone='us-east-1a',
cidr_block='10.18.100.0/24',  # Use a different CIDR range
map_public_ip_on_launch=True
)

private_subnet = ec2.PrivateSubnet(
self,
'PrivateSubnet1',
vpc_id=vpc.vpc_id,
availability_zone='us-east-1a',
cidr_block='10.18.200.0/24'  # Use a different CIDR range
)

Option 4: Create Separate Stacks

Consider separating your VPC infrastructure into multiple stacks:

  1. A base VPC stack that creates the VPC and core networking
  2. Additional stacks that add resources to the VPC

This approach allows you to modify resources in the dependent stacks without affecting the base VPC.

The error you're seeing occurs because CDK is trying to create new subnets with CIDR blocks that conflict with existing subnets. When you initially created the VPC with PRIVATE_ISOLATED subnets, they were assigned CIDR blocks starting from the beginning of your VPC CIDR range (10.18.0.0/24, 10.18.1.0/24, etc.). When you try to add PUBLIC subnets later, CDK tries to use the same CIDR blocks, causing the conflict.

For existing VPCs that need modification, the safest approach is to use Vpc.fromLookup() and then add new resources manually with carefully chosen CIDR blocks that don't conflict with existing ones.
Sources
aws-cdk-lib.aws_ec2 module · AWS CDK
VPC Route tables in CDK | AWS re:Post
class Vpc (construct) · AWS CDK
AWS CDK: Create a Subnet and Launch Instance in it With Existing VPC | AWS re:Post

answered a year ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.