Similar to public subnets, private subnets are also carved out from the same VPC CIDR block. The key difference lies in the routes that are added to these subnets. Functionally, they are used to host resources that will not be exposed to the internet. If you recall the AMI deployment from the previous chapter, we hosted the EC2 instance directly in the public subnet of the default VPC, by allocating a public IP. In real-life implementations, it’s not a good practice to expose the backend instances directly on the internet as it introduces architectural limitations around security, network ingress traffic inspection, and load balancing capabilities. So, resources such as backend servers, databases, and internal tools get deployed in these subnets:

Resources:

PrivateSubnet1:

Type: AWS::EC2::Subnet

Properties:

VpcId: !Ref VPC

AvailabilityZone: !Select [ 0, !GetAZs       ” ]

CidrBlock: !Ref PrivateSubnet1CIDR

MapPublicIpOnLaunch: false

Tags:

– Key: Name

Value: !Sub ${EnvironmentName} Private Subnet (AZ1)

PrivateRouteTable1:

Type: AWS::EC2::RouteTable

Properties:

VpcId: !Ref VPC

Tags:

– Key: Name

Value: !Sub ${EnvironmentName} Private Routes (AZ1)

DefaultPrivateRoute1:

Type: AWS::EC2::Route

Properties:

RouteTableId: !Ref PrivateRouteTable1

DestinationCidrBlock: 0.0.0.0/0

NatGatewayId: !Ref NatGateway1

PrivateSubnet1RouteTableAssociation:

Type: AWS::EC2::SubnetRouteTableAssociation Properties:

RouteTableId: !Ref PrivateRouteTable1 SubnetId: !Ref PrivateSubnet1 …

If you observe the outgoing traffic routes for the private subnet, you will see that we are leveraging another AWS service called a NAT gateway, which we’ll cover next.

NAT gateways

The resources that are deployed in private subnets are not exposed to the internet but still need outgoing internet connectivity to be able to download support patches, third-party hosted libraries, and so on. To support this outbound traffic flow, we must modify network routes in these subnets. Since these resources don’t have a public IP attached to them, they need something to Network Address Translation (NAT) the requests when the packets go out. This is where NAT gateways come into thepicture. They allow resources hosted in private subnets to have outbound internet connectivity. They are hosted in the public subnets, though, mainly because they want to route traffic to the internet via the internet gateway:

Resources:

NatGateway1EIP:

Type: AWS::EC2::EIP

DependsOn: InternetGatewayAttachment

Properties:

Domain: vpc

NatGateway1:

Type: AWS::EC2::NatGateway

Properties:

AllocationId: !GetAtt NatGateway1EIP.AllocationId SubnetId: !Ref PublicSubnet1 …

An important CloudFormation construct that we’ll cover here is DependsOn. This is used to mark explicit dependencies between two resources defined in the template.

We also use an Elastic IP (EIP) here, which is a static public IP address whose life cycle is independent of the resource it is attached to. You can move these IPs across several different AWS resources, which means you don’t lose them when underlying resources are deleted. Big enterprises often follow IP whitelisting requirements with their customers. EIPs come in handy in such situations as you don’t want to lose the IPs that have gone through lengthy approval processes already.

Tip

It is now possible to transfer EIPs across multiple accounts. The target account has 7 hours to accept this transfer once initiated. Organizations benefit from this transferability when they go through organizational restructuring and disaster recovery procedures.

Stack outputs

Earlier, we discussed a best practice to keep your CloudFormation templates modular and organize them with ownership in mind. The network stack we just defined can be deployed and managed by the networking team, independent of the application teams. The application owners, however, will need to consume some resource identifiers from this stack’s output (VPC ID, for example) in their own IaC stack definitions. This is where we can leverage stackoutputs and make the outputs from this stack available for use by others:

Outputs:

VPC:

Description: A reference to the created VPC

Value: !Ref VPC

Export:

Name: !Join [ “-“, [ !Ref “AWS::StackName”, vpc-id ]]

PublicSubnet1:

Description: A reference to the public subnet in the 1st Availability Zone

Value: !Ref PublicSubnet1

Export:

Name: !Join [ “-“, [ !Ref “AWS::StackName”, public-subnet1 ] ]

PublicSubnet2:

Description: A reference to the public subnet in the 2nd Availability Zone

Value: !Ref PublicSubnet2

Export:

Name: !Join [ “-“, [ !Ref “AWS::StackName”, public-subnet2 ] ]

PrivateSubnet1:

Description: A reference to the private subnet in the 1st Availability Zone

Value: !Ref PrivateSubnet1

Export:

Name: !Join [ “-“, [ !Ref “AWS::StackName”, private subnet1 ] ]

PrivateSubnet2:

Description: A reference to the private subnet in the 2nd Availability Zone

Value: !Ref PrivateSubnet2

Export:

Name: !Join [ “-“, [ !Ref “AWS::StackName”, private subnet2 ] ]

Typically, there should only be a handful of resources that you want to share with others. For example, it does not help much if we expose the internet gateway or the NAT gateway IDs since they will always be managed by the networking team.

Now that we’ve covered all the CloudFormation resource definitions, it is time to roll out the network stack in our AWS account.

Leave a Reply

Your email address will not be published. Required fields are marked *