AiL – Subnets! Public Subnets! Wow, they are free, and we can au-to-mate!

(to the tune of the Ewoks Victory Song. Now it can be stuck in your head, and not just mine)

Picking up from last time, I needed to start creating things inside my Virtual Private Cloud, or VPC. The first things to create are subnets – public subnets, in particular. Without a public subnet, nothing that I run in the VPC can be accessed from the internet – nor can they access the internet in turn.

(Caveat: I’m not a networking engineer. This network design represents a standard industry practice, but may not be necessary or suitable for your needs)

What’s a Subnet?

It’s a miniature virtual network inside of the VPC. It’s analogous to an ethernet network. The servers within a subnet can all talk to each other – but can’t talk outside the subnet without instructions

What else do we need?

Besides the subnet, we will need to define:

  • an internet gateway, to allow servers in the subnet
  • a network routing table, with routes, to tell servers to use the gateway.

Also, we won’t be making one subnet. We’ll make two. This is a minor tease for later, but we need to make two because when we create a load balancer, we will need two public subnets for it. Note that everything we’re doing in this post is free (this will not be true for later posts!)

The Stack

---
AWSTemplateFormatVersion: '2010-09-09'
Description:
The Public Subnet, and associated routing information
# Metadata: # no metadata
Parameters:
Environment:
Type: String
Description:
Stack Environment Prefix.
PrimaryAvailabilityZone:
Type: AWS::EC2::AvailabilityZone::Name
Default: us-east-1a # Probably shouldn't set a default, as it makes this region dependent
SecondaryAvailabilityZone:
Type: AWS::EC2::AvailabilityZone::Name
Default: us-east-1b # Probably shouldn't set a default, as it makes this region dependent
#Mappings:
# Conditions: # No Conditions at this time.
# Transform: # No Transforms at this time
Resources:
# We need to create a VPC Gateway, and then attach it to the VPC.
VPCGateway:
# Using an Internet Gateway for now; may change to a VPN gateway if needed, but one step at a time.
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub "${Environment} VPC Internet Gateway"
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref VPCGateway
VpcId:
Fn::ImportValue: !Sub "${Environment}::VPC"
# We need a subnet for publicly available servers. We need two, so that we can register
# a load balancer.
PublicSubnetAZ1:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.1.0/24 # 10.0.10.0 -> 10.0.1.255
MapPublicIpOnLaunch: false # We will use elastic IPs for public-facing servers.
AvailabilityZone: !Ref PrimaryAvailabilityZone
VpcId:
Fn::ImportValue: !Sub "${Environment}::VPC"
Tags:
- Key: Name
Value: !Sub "${Environment} Public Subnet AZ1"
PublicSubnetAZ2:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.2.0/24 # 10.0.20.0 -> 10.0.2.255
MapPublicIpOnLaunch: false # We will use elastic IPs for public-facing servers.
AvailabilityZone: !Ref SecondaryAvailabilityZone
VpcId:
Fn::ImportValue: !Sub "${Environment}::VPC"
Tags:
- Key: Name
Value: !Sub "${Environment} Public Subnet AZ2"
# In order for subnets to receive traffic from the public, we need to create
# routing tables and rules.
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Fn::ImportValue: !Sub "${Environment}::VPC"
Tags:
- Key: Name
Value: !Sub "${Environment} Public Route Table"
PublicSubnetRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0 # We have no idea what IPs may be assigned; got to go global
GatewayId: !Ref VPCGateway
# The route can not be configured until the gateway is attached to the subnet.
DependsOn: VPCGatewayAttachment
PublicRouteTableAssociationAZ1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetAZ1
RouteTableId: !Ref PublicRouteTable
PublicRouteTableAssociationAZ2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetAZ2
RouteTableId: !Ref PublicRouteTable
Outputs:
PublicSubnetAZ1:
Description: The publicly accessible subnet
Value: !Ref PublicSubnetAZ1
Export:
Name: !Sub "${Environment}::PublicSubnetAZ1"
PublicSubnetAZ2:
Description: The publicly accessible subnet
Value: !Ref PublicSubnetAZ2
Export:
Name: !Sub "${Environment}::PublicSubnetAZ2"

Break It Down

Parameters

In this section, we once again ask for the stack environment. We’re going to be doing this every time, as it lets us distinguish between a production stack and testing stacks. We also get the user to specify two Availability Zones – the subnets will get created in those.

VPCGateway

The first two resources relate to the internet gateway. First, we define it, and then we attach it. This two-stage approach is fairly common with CloudFormation (though they sometimes have shortcuts)

This introduces the use of intrinsic functions. These are functions that get called as part of processing the CloudFormation stack. When using YAML, these can be called using the verbose mode (e.g. Fn::ImportValue), or with a shortcut (e.g. !Sub). However, you can’t do the shortcut twice.

The ImportValue function is the most important here. It makes a connection between this stack and the Globals stack made last time – it lets us use a reference to the VPC defined there, without having to look up the ID and either hardcode it or pass it as a parameter. By using the Sub function, I make sure I get the VPC for this test environment.

Public Subnet 1 and Public Subnet 2

The next two resources list the two public subnetsPublicSubnetAZ1 and PublicSubnetAZ2. (AZ stands for ‘Availability Zone’).

One important option here is the CidrBlock. This specifies the range of IP addresses that can be addressed within the subnet. I don’t want them overlapping (I don’t even know if you can!), so I use a simple technique to allocate the range.

Another thing to note is that I don’t actually want to assign public IPs to instances automatically. One reason is that the IPs get changed every time you create a server; that’s okay for temporary servers, but for long-term ones I want something more permanent. Amazon provides this in the form of Elastic IP addresses (yes, that’s another teaser for a later post)

Routing Tables

Right now, those subnets aren’t actually public. I need to put in a network route table, so that servers in these subnets (and yes, there aren’t any yet) can send traffic to the internet.

The PublicRouteTable holds the routes. The PublicSubnetRoute describes the route – sending all traffic out via the previously created VPCGateway. Note that there is a dependency on the VPCGatewayAttachment – the route can’t be associated with the gateway until it’s attached to a VPC, so we have to wait for that to get setup.

Finally, the route table gets associated with the newly created subnets.

Outputs

We don’t use the subnets here – that happens later. So we export the subnets out for wider consumption, just like the VPC was exported last time.

What’s next?

From here, we’ll go on to create a ‘bastion host’. This is a server that we can SSH into, so that we can then SSH into other servers. It will be the only server that we expose directly to the world (and, even then, only via SSH).

This bastion host will also do double-duty as a NAT Gateway, so that servers in private subnets can communicate with the outside internet.

All that will be coming up next time.

Author: Robert Watkins

My name is Robert Watkins. I am a software developer and have been for over 20 years now. I currently work for people, but my opinions here are in no way endorsed by them (which is cool; their opinions aren’t endorsed by me either). My main professional interests are in Java development, using Agile methods, with a historical focus on building web based applications. I’m also a Mac-fan and love my iPhone, which I’m currently learning how to code for. I live and work in Brisbane, Australia, but I grew up in the Northern Territory, and still find Brisbane too cold (after 22 years here). I’m married, with two children and one cat. My politics are socialist in tendency, my religious affiliation is atheist (aka “none of the above”), my attitude is condescending and my moral standing is lying down.

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 )

Facebook photo

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

Connecting to %s

%d bloggers like this: