AWS generates the ipv6 CIDR block for VPCs so its not possible to determine ahead of time. The generated CIDR block looks something like: 2a05:d018:84c:c500::/56
and is always size 56.
When creating a subnet you have to specify a size 64 block using the full prefixed value. E.g. 2a05:d018:84c:c501::/64
.
It's possible to look up the ipv6 CIDR blocks for a VPC in cloudformation, but this returns the full value, not just the prefix. To create a subnet we need to be able to append something 01::/64
to the prefix to create the 64 sized block for the subnet.
I've seen solutions that use a lambda function, but this greatly complicated the templates. I'd like to do this using just the built-in intrinsic functions available in the templates.
When deploying a VPC with ipv6 subnets in the same stack, how can you generate valid ipv6 CIDR blocks for the subnets?
Here is a one liner that does the same thing using the Fn::Cidr intrinsic function.
!Select [1, !Cidr [!Select [0, !GetAtt 'Vpc.Ipv6CidrBlocks'], 256, 64]]
For a given block 2a05:d018:84c:c500::/56
this will give you 2a05:d018:84c:c501::/64
Increment the first index to get the next block.
!Select [2, !Cidr [!Select [0, !GetAtt 'Vpc.Ipv6CidrBlocks'], 256, 64]]
will give you 2a05:d018:84c:c502::/64
Also here is a full minimal example including the crucial steps of using an AWS::EC2::VPCCidrBlock
resource to attach the IPv6 block to the VPC and using the DependsOn
property to make sure that the VPCCidrBlock is attached before the Subnet is created.
Resources:
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Sub '10.255.0.0/16'
VpcCidrBlockIpv6:
Type: 'AWS::EC2::VPCCidrBlock'
Properties:
VpcId: !Ref 'Vpc'
AmazonProvidedIpv6CidrBlock: true
PrivateSubnet:
Type: AWS::EC2::Subnet
DependsOn: VpcCidrBlockIpv6 # Wait for IPv6 CIDR to be attached to VPC before creating subnet
Properties:
AvailabilityZone: !Select [ 0, !GetAZs '' ]
VpcId: !Ref 'Vpc'
AssignIpv6AddressOnCreation: true
CidrBlock: !Sub '10.255.0.0/20'
Ipv6CidrBlock: !Select [1, !Cidr [!Select [0, !GetAtt 'Vpc.Ipv6CidrBlocks'], 256, 64]]
Here's a way to calculate the first subnet in YAML:
Fn::Sub:
- "${VpcPart}${SubnetPart}"
- SubnetPart: 01::/64
VpcPart: !Select [0, !Split ['00::/56', !Select [0,!GetAtt YourVpc.Ipv6CidrBlocks]]]
You can determine the prefix by using a combination of Fn::Split
(on 00::/56
) and Fn::Select
to get the prefix. Then you can append your own value to create the subnet CIDR blocks using Fn::Join
. The following example assumes you have a VPC with one or more Ipv6 CIDR blocks associated with it.
Use this value for the Ipv6CidrBlock
property on the subnet.
{
"Fn::Join": [
"",
[
{
"Fn::Select": [
0,
{
"Fn::Split": [
"00::/56",
{
"Fn::Select": [
0,
{
"Fn::GetAtt": [
"Vpc",
"Ipv6CidrBlocks"
]
}
]
}
]
}
]
},
"01::/64"
]
]
}