Building a two tier infrastructure with Terraform in AWS
Tags: aws, terraform
Published: 2021-03-26 15:01:36

During the lockdown I had an intriguing conversation with a close buddy of mine from the university. During our usual undirected and wild conversations I asked what's the newest tech he's been dabbling in. He mentioned Terraform and he spoke about how awesome it was . I was totally intrigued so set myself on this the path of discovering this amazing IaC (Infrastructure as Code) tool.

I didn't know what to expect and I feared I wouldn't grasp the concepts because this was all new to me and I hadn't ever taken a swim in the DevOps pool, yada yada yada 😁. Down the rabbit hole I went.

In this project I deployed a FastAPI in my first tier and a PostgreSQL and Ansible machines as my second tier. Could have designed a proper frontend but I am horrible with frontend😅 and all my pals had a ton of things to work on as well. I'll be focusing on the deployment of the infrastructure into AWS in this write-up.


Let's get down to it!😎

---

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/op00xyrhytxxdmq861ki.jpg)

---

To get started we need to prepare:
*get yourself a AWS free tier account. this will give you limited but enough access to all AWS has to offer for a year

* get AWS cli configured. Useful AWS doc to do this
* get the Terraform CLI

* get FastAPI installed in a virtual environment. Getting FastAPI installed __(not necessary here so you could ignore)__

Since the focus of this article isn't going to be on FastAPI, I'll won't be showing how to put it together. The FastAPI docs are one of the best and will give you a good guide on how to get something going in no time.

---

To get the ball rolling, in AWS you need to create a VPC. What's a VPC you might be asking😳? A VPC(virtual private cloud) is your personal isolated corner within the AWS cloud. [Read more?](https://www.infoq.com/articles/aws-vpc-explained/)

{% gist https://gist.github.com/KlenamJohnson-Fiah/371eab689aac4e921d5515b2eff5846c file=vpc.tf %}

You might want to ignore the data source defined above the resource definition. To read more about [Data Sources](https://www.terraform.io/docs/language/data-sources/index.html). It's beyond the scope. I'll love to define all the attributes but I'll only explain attributes that were a hell for me, mostly because I can be slow in the head sometimes🤣. [Terraform docs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs) for providers(AWS in this case) explain these attributes so well.

---

The next thing to do do will be to divide this VPC CIDR block into smaller portions for our private and public subnets. The DB needs to be associated with a subnet. In here, I associate it with both subnets.

{% gist https://gist.github.com/KlenamJohnson-Fiah/371eab689aac4e921d5515b2eff5846c file=subnets.tf %}

---

With the subnet provisioned we have to create security groups. Security groups are rules that govern what comes in and out of our instance.(An instance is virtual server in the AWS cloud).

I have three Security groups to manage my instances -

* **aws_security_group.backend_security_groups** - to manage my FastAPI backend

* **aws_security_group.db_security_groups** - to manage my PostgreSQL server

* **aws_security_group.only_ssh_bastion** - to manage my Bastion server(What's this weird thing😭? A Bastion server? Will explain soon)

{% gist https://gist.github.com/KlenamJohnson-Fiah/371eab689aac4e921d5515b2eff5846c file=security_groups.tf %}

---

We need now need an internet gateway to communicate with the outside world

{% gist https://gist.github.com/KlenamJohnson-Fiah/371eab689aac4e921d5515b2eff5846c file=internet_gateway.tf %}

---

With that done we need a way to direct traffic within our VPC. In walks routing tables🚶‍♂️. By default when you create a VPC it comes with a default route table but I will advise you create custom route tables. This helps in troubleshooting issues.

We will create route tables that point to the *Internet Gateway, VPC Internet Gateway* and one between our *FastAPI* and *Database* instances.

Here I'll explain the attributes the best way I understand them.

{% gist https://gist.github.com/KlenamJohnson-Fiah/371eab689aac4e921d5515b2eff5846c file=routing_tables.tf %}


Taking the **aws_route_table.backend_route_table:**
* **vpc_id:** this associates the route table to our VPC

> **route:**

> **cidr_block = "0.0.0.0/0"** ._What this is saying is, when you see any IP that doesn't belong within our VPC IP range direct this towards the internet gateway. Same logic applies to **aws_route_table.nat_route_table.**_

We then need to associate these routes to subnets. So we associate them to the public and private subnets

---

We then need a way for our instances in the private subnet to communicate with the internet but not the internet getting to the them. A VPC NAT [Network Address Translation](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html) Gateway will serve that purpose. This helps when we need our servers to pull updates and patches.

{% gist https://gist.github.com/KlenamJohnson-Fiah/371eab689aac4e921d5515b2eff5846c file=nat.tf %}


---

What next? This is taking forever to put together😡. I agree but remember when I said I went down the rabbit hole🤪?

---

To be able to reach instances and resources in our public subnet from outside our VPC, we need to assign Elastic IP Addresses to them. AWS gives you five eips when you use the free tier and it's yours till you release them(destroy the build). _[Read more](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html)_

---

We are almost there. Hold your focus for 10 more minutes!

---

We need our instances now. AWS instances are best described by me as our virtual servers. We will need three (for the FastAPI backend, Bastion and Ansible control node). In the snips below you will see some other stuff like *provisioner* and *connection*. You can ignore these since they only help us perform some provisioning on our instances when we are spinning them up. It does this by using SSH. Not our focus for now.

{% gist https://gist.github.com/KlenamJohnson-Fiah/371eab689aac4e921d5515b2eff5846c file=instances.tf %}

---

Now to answer some questions👩‍🏫. "Chris, what's a Bastion Server?". A Bastion Server is a special server that reduces the attack vector of your infrastructure by limiting access to the restricted part of your setup. Some call it a jump sever. So to get to the instances in the private network(in this case my ansible control node) I need to SSH into the Bastion server then hop onto my Ansible control node by SSH from the Bastion. Super, right?🧑‍🚀

---

We do the same for our database but amazon has special instances called RDS(Relational Database Server) that can be provisioned on build automatically.

{% gist https://gist.github.com/KlenamJohnson-Fiah/371eab689aac4e921d5515b2eff5846c file=db_instance.tf %}

---

Now to get it over with… I left this very important one for last to make sure you read to the end.😈. To tell Terraform what Provider you are going to be building your infrastructure we need a provider block.

{% gist https://gist.github.com/KlenamJohnson-Fiah/371eab689aac4e921d5515b2eff5846c file=provider.tf %}

Phwee! we are done. There are a couple of other nuances you will need to figure out. What's a lesson without an assignment?😂. You will need variables to provide your code with your *aws access key*, *aws secret key*, *instance type*, provide tags, *path to private key(for SSH)*, *database name*, *database password*. Notice in the code a lot of __var.*__, it means I'm calling variable. You will need separate SSH keys for your Bastion Server and Ansible. Why would you use the same SSH key for both? Kinda forfeits the purpose security wise. You can find the codes [here](https://github.com/KlenamJohnson-Fiah/Terraform).

Happy Computing!✌️💻