Creating an AWS RDS instance with Terraform
Tags: terraform, dataops, devops, cloud
Published: 2023-09-18 18:07:58

## TL;DR

Here's the Terraform code to create an RDS Postgres instance:

```terraform
# RDS instance
resource "aws_db_instance" "my_project_db" {
  engine               = "postgres"
  instance_class       = "db.t3.micro"
  username             = "sidewalk"
  password             = "${var.postgres_pw}"
  allocated_storage    = 20
  publicly_accessible = true
}
```

--- 

## Creating an RDS instance

Amazon Relational Database Service (RDS) is a managed database service provided by AWS that simplifies the process of setting up, operating, and scaling a relational database in the cloud.

Here is a quick recap of some RDS notable features:
- Multi-AZ
	- Supports standby instance in another AZ that helps with failure recover 
- Read replicas
	- Offload read requests
	- Can be used in disaster recovery plan
- Automatic backups
	- Configurable time window
- Configurable storage 
- IAM authentication
- RDS Enhance monitoring 

### Starting in the Management Console (UI)

First, we create a free RDS Postgres instance through the Management Console. Since this was the first AWS service on this account, it automatically created a VPC [^1] and a Security Group [^2] to ensure the instance was securely isolated and protected by a firewall.

To see if the Postgres database is working, we can use PGAdmin, but we also need to go through the following steps:

- Make the RDS instance publicly available
- Add our personal IP to the Security Group's inbound rules used by the database so we can connect to it from outside the VPC (our personal computer):
    - Although not recommended, we can all ports `0-655353` and all IPs `0.0.0.0/0` to the inbound rules so everyone can connect to our database

Everything seems to be working as expected. 

### Importing existing resources to Terraform code

Now, let's start with `terraform import` to import my database instance to my configuration file so I can manage it from there.

First, we create the most basic version of the `aws_db_instance` resource on our Terraform `main.tf` file:

```terraform
resource "aws_db_instance" "my_project_db" {
  engine               = "postgres"
  instance_class       = "db.t2.micro"
  username             = "sidewalk"
  password             = "${var.postgres_pw}"
}
```

You probably noticed we're using a variable instead of writing the password in plain text. Here's the easiest way to do it:

- Create a file called `vars.tf` in the same folder as the `main.tf` file and add the following code to it:
```terraform
variable "super_secret_password" {
  default = "ThisPassword123!"
}
```

Now you can call it from your main terraform code like this `${var.super_secret_password}`

Ok, now it's time to run `terraform import aws_instance.my_project_db <db-instance-id>` (you can find the `<db-instance-id>` in the Management Console) – it seems like the database was imported successfully.

There was a small problem though – the database we created through the Management Console had the parameter `instance_class = db.t3.micro` while our Terraform code had `instance_class = db.t2.micro`. 

When we run `terraform plan`, it says that a particular resource _must be replaced_. Then, when we run `terraform apply`, the database created through the Management Console is deleted and we get an error saying there were some important parameters missing to create the new one – in this case, the `allocated_storage` parameter.

### Key learnings on using `terraform import`:

- To import an existing infrastructure from AWS, you must have the resource in the Terraform code with all required parameters
- Make sure the configuration you have on your configuration file matches exactly what you have on AWS
- `terraform plan` won't show if there are required parameters missing, only `terraform apply`

### Creating resources from Terraform code

Now that we know how this works, let's go ahead and create the database directly from the Terraform code using [AWS documentation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_instance) on `aws_db_instance`:

```terraform
resource "aws_db_instance" "my_project_db" {
  engine               = "postgres"
  instance_class       = "db.t3.micro"
  username             = "sidewalk"
  password             = "${var.postgres_pw}"
  allocated_storage    = 20
}
```

We can run `terraform plan` to make sure everything is looking good and `terraform apply` to create the database. After the database is created, we can type `terraform show` to see the infrastructure's current state – the VPC, the Security Group, etc.

### Key learnings on creating infrastructure using Terraform code:

- It automatically created it on the only VPC available on that AWS account
- It automatically added the only Security Group available on that AWS account

And here's the updated Terraform code to make the RDS instance publicly accessible:

```terraform
# RDS instance
resource "aws_db_instance" "my_project_db" {
  engine               = "postgres"
  instance_class       = "db.t3.micro"
  username             = "sidewalk"
  password             = "${var.postgres_pw}"
  allocated_storage    = 20
  publicly_accessible = true
}
```

[^1]: VPC – Virtual Private Cloud is a virtual network infrastructure that allows you to create a logically isolated network environment. It is essentially a virtual data center in the cloud
[^2]: A Security Group is a virtual firewall that controls the inbound and outbound traffic for one or more instances. Every instance within a VPC in AWS must be associated with at least one security group.