Cloudformation: How to create multiple subnets using Fn::Cidr
In this post we will learn to create multiple subnets with different Cidr blocks.
Lets say I want to create three subnets with Cidr block /24 & rest of three with Cidr block /20.
Of-course we can calculate it using any cidr calculator, but in this case we will have to provide the Cidr range manually for each subnet. But what if you have to provide VPC Cidr block dynamically instead of hardcoding it into your cloudformation script? Thats when Fn::Cidr function of cloudformation comes to rescue.

What is Fn::Cidr?
It is the intrinsic function of cloudformation that returns an array of CIDR address blocks. The number of CIDR blocks returned is dependent on the count parameter.
Fn::Cidr:
- ipBlock
- count
- cidrBits
Short form syntax can be:
!Cidr [ ipBlock, count, cidrBits ]
Here, ipBlock is your base Cidr block, to make it simple we will use our VPC Cidr block, count is number of Cidr’s to be generated. cidrBits is inverse of your subnet mask (to create /24 Cidr the cidrbits will 32–24 i.e 8).
Eg: To generate 3 Cidr blocks with /24
!Cidr ["10.0.0.0/16", 3, 8]
This will generate following three rows Cidr blocks
10.0.0.0/24
10.0.1.0/24
10.0.2.0/24
To use this in your cloudformation script you will need to make a use of Select.
SubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [0, !Cidr ["10.0.0.0/16", 3, 8]]
SubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [1, !Cidr ["10.0.0.0/16", 3, 8]]
SubnetC:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [2, !Cidr ["10.0.0.0/16", 3, 8]]
Now, lets move on to little complex task, now that we know how to use the Cidr function lets create three subnets with /24 Cidr block & rest of the three with /20.
For the first three i.e for /24 one’s our script will above script is still valid, but for /20 Cidr block we will need to do some calculation here. So the ipBlock, count will remain same and for cidrbits it will be 12 (32–20=12)
!Cidr ["10.0.0.0/16", 3, 8]
This will generate following rows:
10.0.0.0/20
10.0.16.0/20
10.0.32.0/20
This will suerly not work because the first row is overlapping with our /24 subnets (10.0.0.0/24, 10.0.1.0/24, 10.0.2.0/24), so we will have to start from row 2 which will not be conflicting with any of our /24 subnets Cidr’s, hence we will need to generate total of 4 rows instead of 3 so that we can skip the first row and use rest of the three rows:
!Cidr ["10.0.0.0/16", 4, 8]
10.0.0.0/20
10.0.16.0/20
10.0.32.0/20
10.0.48.0/20
So our final cloudformation script will be:
SubnetA:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [0, !Cidr ["10.0.0.0/16", 3, 8]]
SubnetB:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [1, !Cidr ["10.0.0.0/16", 3, 8]]
SubnetC:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [2, !Cidr ["10.0.0.0/16", 3, 8]]
SubnetD:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [1, !Cidr ["10.0.0.0/16", 4, 12]]
SubnetE:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [2, !Cidr ["10.0.0.0/16", 4, 12]]
SubnetF:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [3, !Cidr ["10.0.0.0/16", 4, 12]]
Conclusion
Fn:Cidr function of Cloudformation is easy to use and can be used to avoid hardcoding of subnet Cidr blocks.
Note: The ipBlock is hardcoded just for reference and we can use dynamic vpc cidr block by providing !Ref VpcCidr as well.