-
-
Save milesjordan/d86942718f8d4dc20f9f331913e7367a to your computer and use it in GitHub Desktop.
--- | |
AWSTemplateFormatVersion: '2010-09-09' | |
Description: Public VPC and subnets | |
Resources: | |
# | |
# Public VPC | |
# | |
PublicVpc: | |
Type: AWS::EC2::VPC | |
Properties: | |
CidrBlock: 172.31.0.0/16 | |
InstanceTenancy: default | |
Tags: | |
- Key: Name | |
Value: Public VPC | |
IPv6CidrBlock: | |
Type: AWS::EC2::VPCCidrBlock | |
Properties: | |
VpcId: !Ref PublicVpc | |
AmazonProvidedIpv6CidrBlock: true | |
# | |
# Internet gateways (ipv4, and egress for ipv6) | |
# | |
InternetGateway: | |
Type: AWS::EC2::InternetGateway | |
Properties: | |
Tags: | |
- Key: Name | |
Value: Public VPC Internet Access | |
InternetGatewayAttachment: | |
Type: AWS::EC2::VPCGatewayAttachment | |
Properties: | |
VpcId: !Ref PublicVpc | |
InternetGatewayId: !Ref InternetGateway | |
EgressOnlyInternetGateway: | |
Type: AWS::EC2::EgressOnlyInternetGateway | |
Properties: | |
VpcId: !Ref PublicVpc | |
# | |
# Routing - public subnets | |
# | |
PublicSubnetRouteTable: | |
Type: AWS::EC2::RouteTable | |
Properties: | |
VpcId: !Ref PublicVpc | |
Tags: | |
- Key: Name | |
Value: Route Table for Public Subnets in Public VPC | |
PublicSubnetDefaultRoute: | |
DependsOn: InternetGatewayAttachment | |
Type: AWS::EC2::Route | |
Properties: | |
DestinationCidrBlock: 0.0.0.0/0 | |
RouteTableId: !Ref PublicSubnetRouteTable | |
GatewayId: !Ref InternetGateway | |
PublicSubnetDefaultIpv6Route: | |
Type: AWS::EC2::Route | |
Properties: | |
DestinationIpv6CidrBlock: ::/0 | |
RouteTableId: !Ref PublicSubnetRouteTable | |
EgressOnlyInternetGatewayId: !Ref EgressOnlyInternetGateway | |
# | |
# Routing - private subnets | |
# | |
PrivateSubnetRouteTable: | |
Type: AWS::EC2::RouteTable | |
Properties: | |
VpcId: !Ref PublicVpc | |
Tags: | |
- Key: Name | |
Value: Route Table for Private Subnets in Public VPC | |
PrivateSubnetDefaultIpv6Route: | |
Type: AWS::EC2::Route | |
Properties: | |
DestinationIpv6CidrBlock: ::/0 | |
RouteTableId: !Ref PrivateSubnetRouteTable | |
EgressOnlyInternetGatewayId: !Ref EgressOnlyInternetGateway | |
# | |
# Access control | |
# | |
PrivateSubnetsNetworkAcl: | |
Type: AWS::EC2::NetworkAcl | |
Properties: | |
VpcId: !Ref PublicVpc | |
Tags: | |
- Key: Name | |
Value: Private Subnet on Public VPC ACL | |
PrivateSubnetsNetworkAclInboundEntry: | |
Type: AWS::EC2::NetworkAclEntry | |
Properties: | |
NetworkAclId: !Ref PrivateSubnetsNetworkAcl | |
RuleNumber: 1 | |
PortRange: | |
From: 22 # SSH | |
To: 22 | |
Protocol: 6 # TCP | |
RuleAction: allow | |
Egress: false | |
CidrBlock: 172.31.0.0/16 | |
PrivateSubnetsNetworkAclIpv6OutboundEntry: | |
Type: AWS::EC2::NetworkAclEntry | |
Properties: | |
NetworkAclId: !Ref PrivateSubnetsNetworkAcl | |
RuleNumber: 1 | |
Protocol: -1 | |
RuleAction: allow | |
Egress: true | |
Ipv6CidrBlock: ::/0 | |
PrivateSubnetsNetworkAclOutboundEntry: | |
Type: AWS::EC2::NetworkAclEntry | |
Properties: | |
NetworkAclId: !Ref PrivateSubnetsNetworkAcl | |
RuleNumber: 2 | |
Protocol: -1 | |
RuleAction: allow | |
Egress: true | |
CidrBlock: 0.0.0.0/0 | |
# | |
# Public subnet A | |
# | |
PublicSubnetA: | |
Type: AWS::EC2::Subnet | |
Properties: | |
CidrBlock: 172.31.0.0/20 | |
Ipv6CidrBlock: | |
Fn::Sub: | |
- "${VpcPart}${SubnetPart}" | |
- SubnetPart: '01::/64' | |
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt PublicVpc.Ipv6CidrBlocks ]]] | |
AvailabilityZone: !Select [ 0, !GetAZs '' ] | |
VpcId: !Ref PublicVpc | |
MapPublicIpOnLaunch: 'true' | |
Tags: | |
- Key: Name | |
Value: Public Subnet A | |
PublicSubnetARouteTableAssociation: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
RouteTableId: !Ref PublicSubnetRouteTable | |
SubnetId: !Ref PublicSubnetA | |
# | |
# Private subnet A | |
# | |
PrivateSubnetA: | |
Type: AWS::EC2::Subnet | |
Properties: | |
CidrBlock: 172.31.48.0/20 | |
Ipv6CidrBlock: | |
Fn::Sub: | |
- "${VpcPart}${SubnetPart}" | |
- SubnetPart: 'a1::/64' | |
VpcPart: !Select [ 0, !Split [ '00::/56', !Select [ 0, !GetAtt PublicVpc.Ipv6CidrBlocks ]]] | |
AssignIpv6AddressOnCreation: true | |
AvailabilityZone: !Select [ 0, !GetAZs '' ] | |
VpcId: !Ref PublicVpc | |
Tags: | |
- Key: Name | |
Value: Private Subnet A in Public VPC | |
PrivateSubnetAAclAssociation: | |
Type: AWS::EC2::SubnetNetworkAclAssociation | |
Properties: | |
NetworkAclId: !Ref PrivateSubnetsNetworkAcl | |
SubnetId: !Ref PrivateSubnetA | |
PrivateSubnetARouteTableAssociation: | |
Type: AWS::EC2::SubnetRouteTableAssociation | |
Properties: | |
RouteTableId: !Ref PrivateSubnetRouteTable | |
SubnetId: !Ref PrivateSubnetA |
I suggest adding DependsOn: IPv6CidrBlock
to the AWS::EC2::Subnet
resources.
Otherwise you can get a race condition with the Subnet failing on the GetAtt VPC.Ipv6CidrBlocks
operation.
Thanks for this post!
Thanks for your work! I based my own template on it and found !Cidr ( https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-cidr.html) to make the subnet calculation more simple for a scenario with multiple subnets.
thanks for the ipv6 subnet piece.
Thanks for this post it really helped me !!!
I suggest adding
DependsOn: IPv6CidrBlock
to theAWS::EC2::Subnet
resources.Otherwise you can get a race condition with the Subnet failing on the
GetAtt VPC.Ipv6CidrBlocks
operation.
+1
I suggest using intrinsic function Fn::Cidr to break the /48 VPC Cidr block into /64 rather than hard coding the SubnetPart.
I suggest using intrinsic function Fn::Cidr to break the /48 VPC Cidr block into /64 rather than hard coding the SubnetPart.
Indeed, it looks like the docs for this function have an example of doing exactly this (search for "Creating an IPv6 enabled VPC"): https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-cidr.html
Thanks for the suggestions! Indeed, using the newer Fn::Cidr function and adding DependsOn would be a nice addition. I'm glad this helped some of you out!
The routing issue looks to fixed now.