VPC Layouts .... Easy Way
Tags: terraform, aws, serverless
Published: 2023-04-20 11:53:35

Choosing the "correct" AWS VPC layout can sometimes be pretty tricky. You cannot make many changes of any real value once you pick the size of your VPC and where you decide to break apart your subnets. On top of that, VPC layouts and subnetting can be difficult. 

I created something similar to this but with logic all built into a Terraform module. However, trying to put any complex logic in Terraform can be a test of will power and often feels like a square-peg-round-hole situation. Recently I was trying to update it with some additional logic to make it a little more flexible. There were a few curse words used, then finally I took a step back to ask myself if there could be a better way to do this.  

I present my solution and presenting it to you with the hope that it could help other people with their VPC design and layouts. I would love to get some feedback as well.

## API Getting Started
`POST: https://api.rusticators.dev/v1/vpc`

### Body
There are only three required parameters you need to pass in with your request body. There is a fourth optional value that defaults to false if you do not specify it but we will get to that below.

```json
{
    "vpc_type": "a",
    "cidr_block": "10.144.0.0",
    "subnet_mask": 22
}
```

#### Important Notes

- VPC Types available so far are `a` and `b`.
- The VPC subnet size can be anything between a `/16` all the way through a `/24`.

### API Response
```json
{
    "public": [
        "10.144.0.0/26",
        "10.144.0.64/26",
        "10.144.0.128/26"
    ],
    "private": [
        "10.144.1.0/24",
        "10.144.2.0/24",
        "10.144.3.0/24"
    ]
}
```
The way this works is that you can use any 10.x range to create a VPC and it will calculate the appropriate subnet layout depending on what VPC Type you select.

There are a few different layouts that I have found to work well.  The catch with subnet math is that most of the time it does not play well with being able to use the full amount of IP space.  Add in the fact that each subnet in AWS has an overhead of 5 IPs that are unusable as they are reserved for AWS networking reasons. There is a little trade-off or balance between trying to achieve the most amount of unusable space while also providing a VPC layout that offers up some protection for "blast radius" and general high availability best practices.

Typically I generally recommend a VPC with three availability zones.  This would be a VPC Type `A`.  However there are a few locations where three is not always possible.  I have also included a VPC layout that is built around 2 availability zones. This would be VPC Type `B`


## How Can I Use This?
So you may be asking, how does this relate back to Terraform?  Here is where I propose how this can then be pulled back in Terraform and consumed as a resource.

```terraform
data "http" "vpc_layout" {
  url    = "https://api.rusticators.dev/v1/vpc"
  method = "POST"

  request_body = jsonencode({
    "vpc_type"    = var.vpc_type,
    "cidr_block"  = var.cidr_block,
    "subnet_mask" = var.subnet_mask,
    "ephemeral"   = var.ephemeral_subnet
  })
}

resource "aws_vpc" "main" {
  cidr_block = "${var.cidr_block}/${var.subnet_mask}"
}


resource "aws_subnet" "public_subnets" {
  for_each   = toset(jsondecode(data.http.vpc_layout.response_body).public)
  vpc_id     = aws_vpc.main.id
  cidr_block = each.value
}

resource "aws_subnet" "private_subnets" {
  for_each   = toset(jsondecode(data.http.vpc_layout.response_body).private)
  vpc_id     = aws_vpc.main.id
  cidr_block = each.value
}

resource "aws_subnet" "private_subnets" {
  for_each   = toset(jsondecode(data.http.vpc_layout.response_body).private)
  vpc_id     = aws_vpc.main.id
  cidr_block = each.value
}

resource "aws_subnet" "ephemeral_subnet" {
  count      = var.ephemeral_subnet ? 1 : 0
  for_each   = toset(jsondecode(data.http.vpc_layout.response_body).ephemeral)
  vpc_id     = aws_vpc.main.id
  cidr_block = each. Value
}
```

The beauty of potentially doing it this way is that it just a simple for loop and no matter how many subnets you have it will create them all with the single resource.


### Ephemeral Subnets
There is the extra boolean option for creating an ephemeral subnet.  The subnet itself does not dissapear or go away.  Named it that way because its intention is mainly for workloads or instances that may be for temporary operations or things you just need to spin up and spin down for testing. A classic example may be a jumpbox.  

Secondary to this is that most of the time CIDR math does not always play nicely with the complete address space you have for your VPC.  More often than not it leaves "extra" space that is tough to use in any meaningful format.  

This is where the ephemeral subnets come from.  The extra space that is left over but can still be utilized for something otherwise its going to be wasted.  

With all the VPC layouts so far, there is only one ephemeral subnet that will be created.  It is in only one availability zone so its highly recommended that you use it for temporary stuff since its not truly going to be "highly available".  

They can be provisioned just by adding in the `ephemeral` attribute to the body of your request.  It is optional of course and does not have to be specified and its `false` by default.  

```json
{
    "vpc_type": "a",
    "cidr_block": "10.144.0.0",
    "subnet_mask": 20,
    "ephemeral": true
}
```

## Final Thoughts

I have tried quite a few different VPC layouts over the years and I have landed on these since its gives me the highest amount of IP's utilized inside the VPC with minimal amount of waste.  With every subnet you create in AWS land there is always going to be 5 IP's that are used for networking purposes.  The more subnets you create the more you lose just to overhead.

Going to be working on the formal documentation on this and hope to have that finished here soon so I will come back and update this when its out there.  

The quest with this is a pretty simple one and I hope it can help someone out here in the future. I am curious if someone has thoughts so do not hesistate to message me and I am open to feedback.