AWS CloudFormation: VPC with Public and Private Subnets


This is an AWS CloudFormation template to create a VPC environment with public and private subnets. The subnets will be located at two different availability zones:

  • Availability Zone 1
    • Public Subnet 1
    • Private Subnet 1
  • Availability Zone 2
    • Public Subnet 2
    • Private Subnet 2

This template also create an NAT instance inside public subnet 1.

{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "A VPC environment in two availability zones with an NAT instance.",
"Parameters": {
"envPrefix": {
"Description": "Environment name prefix.",
"Type": "String",
"Default": "Test"
},
"vpcCidr": {
"Description": "VPC CIDR block.",
"Type": "String",
"Default": "10.4.0.0/16",
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
"ConstraintDescription": "Must be a valid IP CIDR range of the form x.x.x.x/x."
},
"publicSubnet1Cidr": {
"Description": "Public subnet 1 CIDR block.",
"Type": "String",
"Default": "10.4.0.0/24",
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
"ConstraintDescription": "Must be a valid IP CIDR range of the form x.x.x.x/x and subnet of VPC."
},
"privateSubnet1Cidr": {
"Description": "Private subnet 1 CIDR block.",
"Type": "String",
"Default": "10.4.1.0/24",
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
"ConstraintDescription": "Must be a valid IP CIDR range of the form x.x.x.x/x and subnet of VPC."
},
"publicSubnet2Cidr": {
"Description": "Public subnet 2 CIDR block.",
"Type": "String",
"Default": "10.4.10.0/24",
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
"ConstraintDescription": "Must be a valid IP CIDR range of the form x.x.x.x/x and subnet of VPC."
},
"privateSubnet2Cidr": {
"Description": "Private subnet 2 CIDR block.",
"Type": "String",
"Default": "10.4.11.0/24",
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
"ConstraintDescription": "Must be a valid IP CIDR range of the form x.x.x.x/x and subnet of VPC."
},
"subnet1AZ": {
"Description": "Subnet 1 availability zone.",
"Type": "AWS::EC2::AvailabilityZone::Name"
},
"subnet2AZ": {
"Description": "Subnet 2 availability zone.",
"Type": "AWS::EC2::AvailabilityZone::Name"
},
"natInstanceType": {
"Description": "Amazon EC2 instance type for the NAT instance. This instance will be put on public subnet 1.",
"Type": "String",
"Default": "t2.small",
"AllowedValues": [
"t2.micro", "t2.small", "t2.medium", "t2.large",
"m3.medium", "m3.large", "m3.xlarge", "m3.2xlarge",
"m4.large", "m4.xlarge", "m4.2xlarge", "m4.4xlarge", "m4.10xlarge"
]
},
"natSshAccessCidr": {
"Description": "IP CIDR from where you could SSH into NAT instance",
"Type": "String",
"MinLength": "9",
"MaxLength": "18",
"Default": "0.0.0.0/0",
"AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
"ConstraintDescription": "must be a valid CIDR range of the form x.x.x.x/x."
},
"natKeyName": {
"Description" : "Name of an existing EC2 KeyPair to enable SSH access to NAT instances.",
"Type": "AWS::EC2::KeyPair::KeyName",
"ConstraintDescription" : "Must be the name of an existing EC2 KeyPair."
}
},
"Mappings": {
"AWSNATAMI": {
"eu-central-1" : {"AMI": "ami-46073a5b"},
"sa-east-1" : {"AMI": "ami-fbfa41e6"},
"ap-northeast-1" : {"AMI": "ami-03cf3903"},
"eu-west-1" : {"AMI": "ami-6975eb1e"},
"us-east-1" : {"AMI": "ami-303b1458"},
"us-west-1" : {"AMI": "ami-7da94839"},
"us-west-2" : {"AMI": "ami-69ae8259"},
"ap-southeast-2" : {"AMI": "ami-e7ee9edd"},
"ap-southeast-1" : {"AMI": "ami-b49dace6"}
}
},
"Resources": {
"vpc": {
"Type": "AWS::EC2::VPC",
"Properties": {
"CidrBlock": {"Ref": "vpcCidr"},
"InstanceTenancy": "default",
"EnableDnsSupport": "true",
"EnableDnsHostnames": "true",
"Tags": [
{
"Key": "Name",
"Value": {"Fn::Join" : ["-", [{"Ref" : "envPrefix"}, "VPC"]]}
}
]
}
},
"publicSubnet1": {
"Type": "AWS::EC2::Subnet",
"DependsOn": ["vpc", "attachGateway"],
"Properties": {
"CidrBlock": {"Ref": "publicSubnet1Cidr"},
"AvailabilityZone": {"Ref" : "subnet1AZ"},
"VpcId": {"Ref": "vpc"},
"Tags": [
{
"Key": "Name",
"Value": {"Fn::Join" : ["-", [{"Ref" : "envPrefix"}, "Subnet-Public-1"]]}
}
]
}
},
"privateSubnet1": {
"Type": "AWS::EC2::Subnet",
"DependsOn": ["vpc", "attachGateway"],
"Properties": {
"CidrBlock": {"Ref": "privateSubnet1Cidr"},
"AvailabilityZone": {"Ref" : "subnet1AZ"},
"VpcId": {"Ref": "vpc"},
"Tags": [
{
"Key": "Name",
"Value": {"Fn::Join" : ["-", [{"Ref" : "envPrefix"}, "Subnet-Private-1"]]}
}
]
}
},
"publicSubnet2": {
"Type": "AWS::EC2::Subnet",
"DependsOn": ["vpc", "attachGateway"],
"Properties": {
"CidrBlock": {"Ref": "publicSubnet2Cidr"},
"AvailabilityZone": {"Ref" : "subnet2AZ"},
"VpcId": {"Ref": "vpc"},
"Tags": [
{
"Key": "Name",
"Value": {"Fn::Join" : ["-", [{"Ref" : "envPrefix"}, "Subnet-Public-2"]]}
}
]
}
},
"privateSubnet2": {
"Type": "AWS::EC2::Subnet",
"DependsOn": ["vpc", "attachGateway"],
"Properties": {
"CidrBlock": {"Ref": "privateSubnet2Cidr"},
"AvailabilityZone": {"Ref" : "subnet2AZ"},
"VpcId": {"Ref": "vpc"},
"Tags": [
{
"Key": "Name",
"Value": {"Fn::Join" : ["-", [{"Ref" : "envPrefix"}, "Subnet-Private-2"]]}
}
]
}
},
"inetGateway": {
"Type": "AWS::EC2::InternetGateway",
"DependsOn": ["vpc"],
"Properties": {
"Tags": [
{
"Key": "Name",
"Value": {"Fn::Join" : ["-", [{"Ref" : "envPrefix"}, "InternetGateway"]]}
}
]
}
},
"attachGateway": {
"Type": "AWS::EC2::VPCGatewayAttachment",
"DependsOn": ["vpc", "inetGateway"],
"Properties": {
"VpcId": {"Ref": "vpc"},
"InternetGatewayId": {"Ref": "inetGateway"}
}
},
"rtbPublic": {
"Type": "AWS::EC2::RouteTable",
"DependsOn": ["vpc", "attachGateway"],
"Properties": {
"VpcId": {"Ref": "vpc"},
"Tags": [
{
"Key": "Name",
"Value": {"Fn::Join" : ["-", [{"Ref" : "envPrefix"}, "RTB-Public"]]}
}
]
}
},
"routePublic": {
"Type": "AWS::EC2::Route",
"DependsOn": ["rtbPublic"],
"Properties": {
"DestinationCidrBlock": "0.0.0.0/0",
"RouteTableId": {"Ref": "rtbPublic"},
"GatewayId": {"Ref": "inetGateway"}
},
"DependsOn": "attachGateway"
},
"subnetRouteTableAssociationPublic1": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"DependsOn": ["rtbPublic", "publicSubnet1"],
"Properties": {
"RouteTableId": {"Ref": "rtbPublic"},
"SubnetId": {"Ref": "publicSubnet1"}
}
},
"subnetRouteTableAssociationPublic2": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"DependsOn": ["rtbPublic", "publicSubnet2"],
"Properties": {
"RouteTableId": {"Ref": "rtbPublic"},
"SubnetId": {"Ref": "publicSubnet2"}
}
},
"rtbPrivate": {
"Type": "AWS::EC2::RouteTable",
"DependsOn": ["vpc"],
"Properties": {
"VpcId": {"Ref": "vpc"},
"Tags": [
{
"Key": "Name",
"Value": {"Fn::Join" : ["-", [{"Ref" : "envPrefix"}, "RTB-Private"]]}
}
]
}
},
"subnetRouteTableAssociationPrivate1": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"DependsOn": ["rtbPublic", "privateSubnet1"],
"Properties": {
"RouteTableId": {"Ref": "rtbPrivate"},
"SubnetId": {"Ref": "privateSubnet1"}
}
},
"subnetRouteTableAssociationPrivate2": {
"Type": "AWS::EC2::SubnetRouteTableAssociation",
"DependsOn": ["rtbPublic", "privateSubnet2"],
"Properties": {
"RouteTableId": {"Ref": "rtbPrivate"},
"SubnetId": {"Ref": "privateSubnet2"}
}
},
"natEc2Instance": {
"Type": "AWS::EC2::Instance",
"DependsOn": ["vpc", "attachGateway", "publicSubnet1", "sgNAT"],
"Properties": {
"DisableApiTermination": "false",
"InstanceInitiatedShutdownBehavior": "stop",
"InstanceType": {"Ref": "natInstanceType"},
"ImageId": {"Fn::FindInMap": ["AWSNATAMI", {"Ref": "AWS::Region"}, "AMI"]},
"KeyName": {"Ref": "natKeyName"},
"Monitoring": "false",
"SourceDestCheck": "false",
"Tags": [
{
"Key": "Name",
"Value": {"Fn::Join" : ["-", [{"Ref" : "envPrefix"}, "NAT"]]}
}
],
"NetworkInterfaces": [
{
"DeleteOnTermination": "true",
"Description": "Primary network interface",
"DeviceIndex": 0,
"SubnetId": {"Ref": "publicSubnet1"},
"GroupSet": [
{"Ref": "sgNAT"}
],
"AssociatePublicIpAddress": "true"
}
]
}
},
"sgNAT": {
"Type": "AWS::EC2::SecurityGroup",
"DependsOn": ["vpc", "attachGateway"],
"Properties": {
"GroupDescription": "Security group for NAT instances",
"VpcId": {"Ref": "vpc"},
"SecurityGroupIngress": [
{
"IpProtocol": "tcp",
"FromPort": "0",
"ToPort": "1024",
"CidrIp": {"Ref": "privateSubnet1Cidr"}
},
{
"IpProtocol": "udp",
"FromPort": "0",
"ToPort": "1024",
"CidrIp": {"Ref": "privateSubnet1Cidr"}
},
{
"IpProtocol": "tcp",
"FromPort": "0",
"ToPort": "1024",
"CidrIp": {"Ref": "privateSubnet2Cidr"}
},
{
"IpProtocol": "udp",
"FromPort": "0",
"ToPort": "1024",
"CidrIp": {"Ref": "privateSubnet2Cidr"}
},
{
"IpProtocol": "tcp",
"FromPort": "22",
"ToPort": "22",
"CidrIp": {"Ref": "natSshAccessCidr"}
}
],
"SecurityGroupEgress": [
{
"IpProtocol": "-1",
"CidrIp": "0.0.0.0/0"
}
],
"Tags": [
{
"Key": "Name",
"Value": {"Fn::Join" : ["-", [{"Ref" : "envPrefix"}, "SG-NAT"]]}
}
]
}
},
"routePrivate": {
"Type": "AWS::EC2::Route",
"Properties": {
"DestinationCidrBlock": "0.0.0.0/0",
"RouteTableId": {"Ref": "rtbPrivate"},
"InstanceId": {"Ref": "natEc2Instance"}
}
}
}
}
view raw vpc-scenario-2.json hosted with ❤ by GitHub

One thought on “AWS CloudFormation: VPC with Public and Private Subnets

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s