Writing July 6, 2017 · 6 min read

A Simple Virtual Private Cloud deployed via a Fugue Composition


In part one of this series, I talked about the differences between [Infrastructure as Config, and Infrastructure as Code.] (/blog/code-vs-config/) In this article, we’ll dive into the creation of a simple Virtual Private Cloud (VPC) on AWS using a Fugue composition.

Here’s what we’re going to create. It’s a basic example of a Virtual Private Cloud (VPC) which spans two availability zones (AZ) in a single region. We’re going to create four subnets (two in each AZ, and deploy a NAT Gateway in each AZ for redundancy.

Fugue Infrastructure

To create this infrastructure, we’ll first need to create a [composition] (https://docs.fugue.co/user-guide-terms.html). Think of a Composition as a program, written in [Ludwig] (https://docs.fugue.co/user-guide-terms.html#term-ludwig); Ludwig is a Domain Specific Language (DSL) we’ll use to create our Composition files. Once created, the Composition is interpreted by the Fugue conductor to create infrastructure and resources in your AWS account.

The first couple of lines in a Ludwig are interesting, so let’s start there first.

composition

import Fugue.AWS as AWS
import Fugue.AWS.EC2 as EC2

The first line is important; it defines the file we’re creating a composition file. Only composition files can be executed by the Fugue conductor.

Lines three and four are examples of importing Libraries into your composition files. A set of [Standard Libraries] (https://docs.fugue.co/standard-library.html) ship with Fugue which should be enough to get you started. Later on, you could create libraries of your own or customize the default set of libraries provided by Fugue.

We all know the importance tagging of resources deployed in AWS. Lines six and seven show one way you could define default tag values. Once set, we can use them throughout the composition file.

Now that we’ve introduced a Ludwig file let’s get to building some infrastructure. The first thing we’re going to do is to create a VPC. Since we’ve imported a library from the Fugue Standard Library creating the VPC can be down in just a few lines.

dualAZ-vpc: EC2.Vpc.new {
  cidrBlock: "10.0.0.0/16",
  region: AWS.Us-west-2,
  tags: [project-tag,department-tag]
}

I used the bare minimum to create this VPC, but you could extend this functionality by providing more options for DHCP and DNS configuration.

Following the concept of building infrastructure that can survive a failure within AWS, we’ll spread our subnets (four in total), across two AZs.

public-10-0-0-0: EC2.Subnet.new {
  cidrBlock: '10.0.0.0/27',
  vpc: dualAZ-vpc,
  availabilityZone: AWS.A,
  mapPublicIpOnLaunch: False,
  defaultForAz: False,
  tags: [project-tag,department-tag]
}

public-10-0-64-0: EC2.Subnet.new {
  cidrBlock: '10.0.64.0/27',
  vpc: dualAZ-vpc,
  availabilityZone: AWS.B,
  mapPublicIpOnLaunch: False,
  defaultForAz: False,
  tags: [project-tag,department-tag]
}

private-10-0-16-0: EC2.Subnet.new {
  cidrBlock: '10.0.16.0/20',
  vpc: dualAZ-vpc,
  availabilityZone: AWS.A,
  mapPublicIpOnLaunch: False,
  defaultForAz: False,
  tags: [project-tag,department-tag]
}

private-10-0-80-0: EC2.Subnet.new {
  cidrBlock: '10.0.80.0/20',
  vpc: dualAZ-vpc,
  availabilityZone: AWS.B,
  mapPublicIpOnLaunch: False,
  defaultForAz: False,
  tags: [project-tag,department-tag]
}

You can see that we’ve created two public and two provide subnets, and by using the ‘availablityZone’ option we’re able to define the AZ where each subnet is deployed. There are a few other options here I’d like to point out. First, ‘mapPublicIpOnLaunch’ allows us to automatically assign public IP addresses to EC2 instances associated with the subnet. I don’t normally do this, so I’ve set this to ‘False. Also ‘defaultForAz’ allows us to define the subnet as the default location for all EC2 instances launched in the AZ where the subnet resides. I’ve also set this to ‘False.’

I like to use the VPC deployment strategies laid out by Nathan McCourtney in [Practical VPC Design] (https://medium.com/aws-activate-startup-blog/practical-vpc-design-8412e1a18dcc). Using this VPC design means we’ll create very small public subnets and very large private subnets and we’ll leave lots of space to grow later on.

NOTE: Your options here will change based on the AWS region you are using to deploy your VPC.

Now that we have our VPC and subnets created let’s attach our VPC to something. We’re going to connect the VPC to the Public Internet by using an Internet Gateway (IGW). With just a few lines we’re able to create the IGW and associate it with the VPC.

vpc-igw: EC2.InternetGateway.new {
  vpc: dualAZ-vpc,
  tags: [project-tag,department-tag]
}

Now that our VPC has connectivity let’s create two NAT Gateways. Remember, NAT Gateways require public IP addresses to function, so we’ll define two Elastic IP Addresses (EIP) first.

nat-gatewayA-ip: EC2.ElasticIP.new {
  region: AWS.Us-west-2
}

nat-gatewayB-ip: EC2.ElasticIP.new {
  region: AWS.Us-west-2
}

Now that we have two EIPs ready, we can create the NAT Gateways. Just like we’ve seen in the previous code examples, we’re able to create NAT Gateways with just a few lines of code. All we need to do is decide which subnets will house our NAT Gateways (public subnets) and the EIPs we’ll like to associate with each of the Gateways.

nat-gatewayA: EC2.NatGateway.new {
  subnet: public-10-0-0-0,
  elasticIP: nat-gatewayA-ip
}

nat-gatewayB: EC2.NatGateway.new {
  subnet: public-10-0-64-0,
  elasticIP: nat-gatewayB-ip
}

At this point, all that’s left to do is to create route tables. I like to create a route table for each subnet. By doing this, I have fine-grained control over the routing of traffic within the VPC. So I need to create a total of four route tables.

Before created the route tables themselves, let’s define the routes we’ll use. Just like our route tables, we’ll need to create four routes.

public-routeA: EC2.Route.new {
  destinationCidrBlock: '0.0.0.0/0',
  target: EC2.GatewayTarget(vpc-igw)
}

public-routeB: EC2.Route.new {
  destinationCidrBlock: '0.0.0.0/0',
  target: EC2.GatewayTarget(vpc-igw)
}

private-routeA: EC2.Route.new {
  destinationCidrBlock: '0.0.0.0/0',
  target: EC2.NatTarget(nat-gatewayA)
}

private-routeB: EC2.Route.new {
  destinationCidrBlock: '0.0.0.0/0',
  target: EC2.NatTarget(nat-gatewayB)
}

With just a couple of lines for each route, we can setup some basic routing. You can see here; public routes use the previously created IGW as their default route while the private routes use the NAT Gateways as their default route.

Once the routes are set up, we need to create the route tables and associate each subnet to the correct route table.

public-route-tableA: EC2.RouteTable.new {
  vpc: dualAZ-vpc,
  routes: [public-routeA],
  associations: [
    public-10-0-0-0
  ],
  tags: [project-tag,department-tag]
}

public-route-tableB: EC2.RouteTable.new {
  vpc: dualAZ-vpc,
  routes: [public-routeB],
  associations: [
    public-10-0-64-0
  ],
  tags: [project-tag,department-tag]
}

private-route-tableA: EC2.RouteTable.new {
  vpc: dualAZ-vpc,
  routes: [private-routeA],
  associations: [
    private-10-0-16-0
  ],
  tags: [project-tag,department-tag]
}

private-route-tableB: EC2.RouteTable.new {
  vpc: dualAZ-vpc,
  routes: [private-routeB],
  associations: [
    private-10-0-80-0
  ],
  tags: [project-tag,department-tag]
}

There’s a little more going on here than in our other code samples. To setup the route tables we need to associate them with a VPC, the routes as well as the subnet(s).

There you have it; a simple VPC implemented using Fugue. In the next article, we’ll create a few EC2 instances, an Elastic Load Balancer (ELB), a Multi-AZ RDS DB instance, and security groups to protect our infrastructure.

All writing Start a conversation