Building A Network

Overview

Lots of deployments start with building a network, and then working your way up the stack. This example shows you how to create a software-defined network called a Virtual Private Cloud (VPC) in Amazon Web Services using Fugue.

Prerequisites

This example is for people new to Fugue and Ludwig. You’re welcome to window-shop it, but if you want to really do the work shown here, you’ll need to do a few quick steps to set up the Fugue CLI and install the Fugue Conductor into your AWS account.

What We’ll Do In This Example

We’ll cover making networks on AWS using Fugue. We’ll use basic Ludwig to get the job done. We’ll see some imports, a little bit of Fugue’s network definition features, and some examples on the CLI.

What We’ll Have When We’re Done

A Virtual Private Cloud (VPC) in AWS, along with a couple of subnets.

How Long It Will Take

Assuming you’re new to Fugue, about 15 minutes. Less if you can skip some of the basics, of course.

Download

You can download the source code for this example here.

Get editor plug-ins here.

Let’s Go!

Boilerplate

To begin with, we’ll want to declare that our file is a composition (using the composition keyword), and thus an entry point. We’ll also import the base AWS library, as well as the EC2 library.

composition

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

Making a VPC

Now that we are ready to write a Fugue composition using AWS and EC2, we can start declaring infrastructure. The foundation of our network will be the VPC, which we’ll build with the EC2.Vpc.new constructor, which we imported with the Fugue.AWS.EC2 module.

example-vpc: EC2.Vpc.new {
  cidrBlock: "10.0.0.0/16",
  tags: [
    AWS.tag("Name", "My Cool VPC")
  ],
  region: AWS.Us-west-2
}

This gives us a nice class B network to work in with 65536 addresses in it, give or take. We’ve put the network in the us-west-2 region (Oregon), and given it the very desirable Name tag, My Cool VPC.

Making Some Subnets

We can’t directly launch instances in a VPC, and we’re going to want to segment our network anyway. Let’s make a couple of /24 subnets, both “public” (meaning they can be reached from the internet), but in different Availability Zones so that we have durability in the event of an AZ failure.

Subnet Validation at Compile-Time

Lots of Fugue modules (outside of the Core ones) contain compile-time validations for infrastructure. For example, let’s say you tried to make your public subnet with this CIDR block:

public-10-0-2-0: EC2.Subnet.new {
  cidrBlock: '10.1.2.0/24'...

The Ludwig compiler would give you an error right away, catching this mistake very early:

"/opt/fugue/lib/Fugue/AWS/EC2/Subnet.lw" (line 38, column 5):
error:

  38|     isValidCidrForVpc(cidrBlock, spec.vpc)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Subnet cidrBlock '10.1.2.0/24' must be a subset of the Vpc cidrBlock '10.0.0.0/16'

This could save you a lot of time and trouble, rather than figuring this out once things are already built!

public-10-0-1-0: EC2.Subnet.new {
  cidrBlock: '10.0.1.0/24',
  vpc: example-vpc,
  availabilityZone: AWS.A,
  mapPublicIpOnLaunch: True,
  defaultForAz: False
}

public-10-0-2-0: EC2.Subnet.new {
  cidrBlock: '10.0.2.0/24',
  vpc: example-vpc,
  availabilityZone: AWS.B,
  mapPublicIpOnLaunch: True,
  defaultForAz: False
}

Adding an Internet Gateway

Our public subnets aren’t going to be very effective without an Internet Gateway, which facilitates traffic flow between a VPC and the internet. We actually make our internet gateways in VPCs, but we’ll establish a route to them for our public subnets in the next step.

example-igw: EC2.InternetGateway.new {
  vpc: example-vpc
}

Adding a Route Table

A route table tells the VPC’s “implicit router” how to route traffic within the VPC’s subnets as well as to other networks, such as the internet.

First, we’ll write a route that sends internet-bound traffic to the internet gateway.

public-route: EC2.Route.new {
  destinationCidrBlock: "0.0.0.0/0",
  target: EC2.GatewayTarget(example-igw)
}

Then, we’ll include that route in a route table that we associate with our public subnets.

public-route-table: EC2.RouteTable.new {
  vpc: example-vpc,
  routes: [public-route],
  associations: [
    public-10-0-1-0,
    public-10-0-2-0
  ]
}

Adding Some Security Groups

Now we have all the basic networking bits to start to build some infrastructure that uses TCP/IP. But what we have so far doesn’t pay a whole lot of attention to security. That’s an onion that is many layers deep, but we should at least establish some security groups that we can apply to instances we launch.

First, let’s make a security group that allows traffic from an Internet-wide IP range to ingress. This will be appropriate for our internet-facing instances or Elastic Load Balancers, so we’ll hint at ELBs in the binding name.

example-elb-sg: EC2.SecurityGroup.new {
  description: "Allow http/s traffic from the Internet",
  ipPermissions: [
    inet-HTTP,
    inet-HTTPS
  ],
  ipPermissionsEgress: None,
  vpc: example-vpc
}

inet-HTTP: EC2.IpPermission.new {
  ipProtocol: "tcp",
  fromPort: 80,
  toPort: 80,
  target: EC2.IpRanges([
    EC2.IpRange(cidrIp: "0.0.0.0/0")
  ])
}
# Note the short form of this ^ is:
#   EC2.IpPermission.http(EC2.IpPermission.Target.all)
# Similar functions exist for common protocols. We've used the long form in this example to be more illustrative.

inet-HTTPS: EC2.IpPermission.new {
  ipProtocol: "tcp",
  fromPort: 443,
  toPort: 443,
  target: EC2.IpRanges([
    EC2.IpRange(cidrIp: "0.0.0.0/0")
  ])
}

Next, we probably want another security group that doesn’t allow any internet traffic, but exclusively allows traffic from instances in the security group we just created. We would likely use this security group for our application or web servers. To do this, we can use the previous security group as our target value for our IpPermission.

example-app-sg: EC2.SecurityGroup.new {
  description: "Allow traffic from the internet-facing SG",
  ipPermissions: [
    EC2.IpPermission.tcp(3000, EC2.IpPermission.Target.securityGroup(example-elb-sg)),
    # This ^ is the shorthand for both IP permissions and targets.
  ],
  vpc: example-vpc
}

Starting the Fugue process

We can manage our network infrastructure in a single process dedicated to networking concerns by running this composition.

$ fugue run Network.lw -a example-network
[ fugue run ] Running /Users/fugueUser/Network.lw...

Run Details:
    Account: default
    Alias: example-network

Compiling Ludwig file /Users/fugueUser/Network.lw
[ OK ] Successfully compiled. No errors.

Uploading compiled Ludwig composition to S3...
[ OK ] Successfully uploaded.

Requesting the Conductor to create and run process based on composition ...
[ DONE ] Process created and running.


State    Updated    Created    Account              FID                                   Alias            Last Message    Next Command
-------  ---------  ---------  -------------------  ------------------------------------  ---------------  --------------  --------------
Running  10:24am    10:24am    fugue-1505320402664  f577d60b-d68a-4c4e-b60b-fb99c30ee5c1  example-network                  run

After a couple minutes, we can see that Fugue has a successful, running process that embodies our network. This means Fugue has built the network, and is continually enforcing its configuration.

$ fugue status

Fugue Status Report for user/xxxxxxxxxxxx - Thu Sep 14 2017 10:25am

State    Updated    Created    Account              FID                                   Alias            Last Message    Next Command
-------  ---------  ---------  -------------------  ------------------------------------  ---------------  --------------  --------------
Running  10:24am    10:24am    fugue-1505320402664  a6adf36d-b145-4825-8bef-20f9faada4ac  example-network  SUCCEEDED

A quick check of the console confirms that we have the VPC as described.

The resulting VPC.

The resulting VPC.

One of the resulting subnets.

One of the resulting subnets.

The resulting route table.

The resulting route table.

Killing The Fugue Process

We don’t want extra VPCs sitting around gathering dust, so we’ll clean this one up by killing the process. It’s very easy to do:

$ fugue kill example-network
[ fugue kill ] Killing process with Alias: example-network

[ WARN ] Are you sure you want to kill the process with Alias: example-network? [y/N]:
Requesting the Conductor to kill Running composition with Alias: example-network...
[ Done ] The conductor is killing the process with Alias: example-network

Within a few minutes, you should see My Cool VPC gone from your account. No mess!

Next Steps

Want to see what else you can make with VPCs? Check out the next example. Or, now that you have a VPC and all the bits that make it work, you can start putting stuff into it! A great way to do that is to continue with the Building Compute: Instances example. You can also try on your own to add an Instance, AutoScaling Group, Elastic Load Balancer, or more! You can ponder VPC subnet designs to serve your organization.