Networking in AWS (Terraform version)
Tags: tutorial, devops, aws, terraform
Published: 2022-07-22 03:12:18

Earlier this week I created a post detailing [how to make a well architected vpc in AWS](https://dev.to/shahab96/networking-in-aws-5h59). I thought I'd revisit the topic and talk more about how you would implement this in an actual project.

If you work with AWS for your employer or for your personal needs, you probably have a few resources you create and use in different AWS services. It can get annoying to keep track of what your stack consists of if all you have to go off is your own memory or a document with some list of resources. This is where infrastructure as code comes in. The idea is that you would have a template of some form which would deploy your required stack for you. You can then deploy multiple instances of this template to serve as different environments by simply passing in different input parameters. This is becoming the standard process for managing software projects, as engineers write the application code and then also write the template describing the infrastructure their code would run on.

In this post I will be walking you through the post I made earlier but using [Terraform](https://www.terraform.io/) for all of the infrastructure. I won't be covering what Terraform is (not in this post at least) or how you would configure your AWS provider. I will only be covering the creation of the network exactly as in my other post.

I do use one of the more uncommon Terraform functions, [cidrsubnet](https://www.terraform.io/language/functions/cidrsubnet) in this template. It's a VERY useful function and I recommend familiarizing yourself with it. To summarize, the first argument is your VPC cidr, the second is which subnet you are currently trying to get the cidr block for, and the third argument is the number of bits you are adding to the subnet mask.

```hcl
# For this example we will assume that the value being given to this variable is ["Public", "Private", "Isolated"]
variable "subnet_names" {
  type = list(string)
}

# Create the VPC.
resource "aws_vpc" "this" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "MyVpc"
  }
}

# Adopt the default route table into our terraform state
resource "aws_default_route_table" "this" {
  default_route_table_id = aws_vpc.this.default_route_table_id

  tags = {
    Name = "Unused"
  }
}

# Adopt the default network ACL into our terraform state
resource "aws_default_network_acl" "this" {
  default_network_acl_id = aws_vpc.this.default_network_acl_id

  # By adding no rules this resource defaults to deny all

  tags = {
    Name = "Unused"
  }
}

# Create our Public, Private and Isolated subnets
resource "aws_subnet" "this" {
  count = length(var.subnet_names)

  vpc_id     = aws_vpc.this.id
  cidr_block = cidrsubnet("10.0.0.0/16", count.index, 2)

  tags = {
    Name = var.subnet_names[count.index]
  }
}

# Create a route table for each of our subnets
resource "aws_route_table" "this" {
  count = length(var.subnet_names)

  vpc_id = aws_vpc.this.id
  
  tags = {
    Name = var.subnet_names[count.index]
  }
}

# Associate our route tables with the appropriate subnets
resource "aws_route_table_association" "this"
  count = length(var.subnet_names)

  route_table_id = aws_route_table.this[count.index]
  subnet_id      = aws_subnet.this[count.index]
}

# Create the internet gateway
resource "aws_internet_gateway" "this" {
  vpc_id = aws_vpc.this.id

  tags = {
    Name = "InternetGateway"
  }
}

# Create the Elastic IP
resource "aws_eip" "this" {
  vpc = true
 
  tags = {
    Name = "My Elastic IP"
  }
}

# Create the NAT Gateway
resource "aws_nat_gateway" "this" {
  allocation_id     = aws_eip.this.id
  subnet            = aws_subnet.this[0].id
  connectivity_type = "public"

  tags = {
    Name = "NAT Gateway"
  }
}

# Create our Public subnet route
resource "aws_route" "public" {
  route_table_id         = aws_route_table.this[0].id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id             = aws_internet_gateway.this.id
}

# Create our Private subnet route
resource "aws_route" "private" {
  route_table_id         = aws_route_table.this[1].id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id         = aws_nat_gateway.this.id
}

# Create our Network ACLs
resource "aws_network_acl" "this" {
  count = length(var.subnet_names)
 
  vpc_id = aws_vpc.this.id

  tags = {
    Name = var.subnet_names[count.index]
  }
}

# Associate each ACL with its subnet
resource "aws_network_acl_association" "this" {
  count = length(var.subnet_names)

  network_acl_id = aws_network_acl.this[count.index].id
  subnet_id      = aws_subnet.this[count.index].id
}

# Create the ACL rule for our Public subnet
resource "aws_network_acl_rule" "public" {
  network_acl_id = aws_network_acl.this[0].id
  rule_number    = 100
  rule_action    = "allow"
  protocol       = "tcp"
  to_port        = 443
  from_port      = 443
  cidr_block     = "0.0.0.0/0"
}

# Create the ACL rule for our Private subnet
resource "aws_network_acl_rule" "private" {
  network_acl_id = aws_network_acl.this[1].id
  rule_number    = 100
  rule_action    = "allow"
  protocol       = "tcp"
  to_port        = 1024
  from_port      = 65535
  cidr_block     = aws_vpc.this.cidr_block
}

# Create the ACL rule for our Isolated subnet
resource "aws_network_acl_rule" "isolated" {
  network_acl_id = aws_network_acl.this[2].id
  rule_number    = 100
  rule_action    = "allow"
  protocol       = "tcp"
  to_port        = 5432
  from_port      = 5432
  cidr_block     = aws_vpc.this.cidr_block
}
```