One of the most powerful tools to use as a DevOps engineer is Terraform. Simply put, Terraform is
an infrastructure as code (IaC) tool that lets you define both cloud and on-prem resources in human-readable configuration files that you can version, reuse, and share.
This means that you can actually create a whole infrastructure using declarative code. In other words, you are able to manage resources like storages as well as databases using only code.
One of the main benefits in using IaC is to have a better control of your resources and make them scalable. Even more: if you are interested in using multiple cloud services, you can control all the resources from one place, saving you time from clicking and opening multiple tabs in your browser to access each cloud service console.
In this article, I will provide a guide on how to create a free-tier AWS RDS database instance using Terraform. I write for you who want to create a RDS database instance but don't want to deal too much with AWS console. Or if you just want to learn a bit the basics of Terraform. Let's get started!
Prerequisites
First of all, you need to have:
- An AWS account eligible to use RDS free-tier
- An AWS Access Key and Secret Key with Full Access to RDS associated to a new IAM user (or use root at your own risk)
- The Terraform CLI installed
Selecting and pointing to AWS provider
First of all, let's create a folder to initialize a Terraform template:
mkdir terraform-aws-free-rds
cd terraform-aws-free-rds
In this folder, create a provider.tf
that is going to be our entry point to declare which provider are we going to use. This is where we define For now, its content will be:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.43.0"
}
}
}
provider "aws" {}
This will tell Terraform: "I want to use the AWS provider to start my infrastructure!"
But still Terraform don't know to which region we are going to create our resources or even how to access our AWS account to make these changes. In order to do so, we are going to create an AWS access key and secret key.
We also do not want to use it explicitly in our provider.tf
file. So we are going to create a variables.tf
file and put this:
variable "aws_access_key" {
type = string
description = "AWS access key"
}
variable "aws_secret_key" {
type = string
description = "AWS secret key"
}
This will declare to Terraform that we have these two variables that can be accessed using var.aws_secret_key
and var.aws_access_key
and set using a terraform.tfvars
file.
Create your terraform.tfvars
and put your credentials:
aws_access_key = "your-access-key"
aws_secret_key = "your-secret-keys"
This way, Terraform will know which values these variables have and use it on our provider.tf
file. Terraform will able to create, update or delete resources using this account. One more step and our provider will be completely set:
# on `provider.tf`
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.43.0"
}
}
}
provider "aws" {
region = "us-east-1"
access_key = var.aws_access_key
secret_key = var.aws_secret_key
}
Now Terraform will indeed know to which region and which account the resources are going to be created.
Declare your RDS resource
Now we just need to tell Terraform to create a RDS resource. As we are using aws provider to Terraform, you may check this documentation to understand a bit more of what we are going to write on the main.tf
file.
resource "aws_db_instance" "my-free-rds-db-instance" {
allocated_storage = 20
engine = "postgres"
engine_version = "16.1"
identifier = "my-free-rds-db-instance"
instance_class = "db.t3.micro"
storage_encrypted = false
publicly_accessible = true
delete_automated_backups = true
skip_final_snapshot = true
db_name = "database_name"
username = "database_username"
password = "database_password"
apply_immediately = true
multi_az = false
}
Basically, what is happening here is that:
- We tell Terraform to use AWS provider, using the credentials we set in
terraform.tfvars
- We declare a
aws_db_instance
calledmy-free-rds-db-instance
and specify all the instance configuration that would be also specified via AWS console if we were creating using it.
This configuration is known to be a free tier in RDS for 12 months. There are other configurations but for the PostgreSQL is this one:
750 hours of Amazon RDS Single-AZ db.t2.micro, db.t3.micro, and db.t4g.micro Instances usage running MySQL, MariaDB, PostgreSQL databases each month. ... 20 GB of General Purpose SSD (gp2) storage per month.
If you check in the main.tf
file, we define this configuration in a declarative way.
Spinning up your Terraform resources
Now run
terraform init # to initialize the dependency lock file
Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/aws versions matching "5.43.0"...
- Installing hashicorp/aws v5.43.0...
- Installed hashicorp/aws v5.43.0 (signed by HashiCorp)
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.
Terraform has been successfully initialized!
...
Now we have our AWS provider set in our Terraform context, which will enable us to tell our local Terraform CLI commands which version of the provider to use.
We may see the execution plan of the resources we have declared:
terraform plan
The output should be something like this:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_db_instance.my-free-rds-db-instance will be created
+ resource "aws_db_instance" "my-free-rds-db-instance" {
+ address = (known after apply)
+ allocated_storage = 20
+ apply_immediately = true
+ arn = (known after apply)
+ auto_minor_version_upgrade = true
+ availability_zone = (known after apply)
+ backup_retention_period = (known after apply)
+ backup_target = (known after apply)
+ backup_window = (known after apply)
+ ca_cert_identifier = (known after apply)
+ character_set_name = (known after apply)
+ copy_tags_to_snapshot = false
+ db_name = "database_name"
+ db_subnet_group_name = (known after apply)
+ delete_automated_backups = true
+ domain_fqdn = (known after apply)
+ endpoint = (known after apply)
+ engine = "postgres"
+ engine_version = "16.1"
+ engine_version_actual = (known after apply)
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ identifier = "my-free-rds-db-instance"
+ identifier_prefix = (known after apply)
+ instance_class = "db.t3.micro"
+ iops = (known after apply)
+ kms_key_id = (known after apply)
+ latest_restorable_time = (known after apply)
+ license_model = (known after apply)
+ listener_endpoint = (known after apply)
+ maintenance_window = (known after apply)
+ master_user_secret = (known after apply)
+ master_user_secret_kms_key_id = (known after apply)
+ monitoring_interval = 0
+ monitoring_role_arn = (known after apply)
+ multi_az = false
+ nchar_character_set_name = (known after apply)
+ network_type = (known after apply)
+ option_group_name = (known after apply)
+ parameter_group_name = (known after apply)
+ password = (sensitive value)
+ performance_insights_enabled = false
+ performance_insights_kms_key_id = (known after apply)
+ performance_insights_retention_period = (known after apply)
+ port = (known after apply)
+ publicly_accessible = true
+ replica_mode = (known after apply)
+ replicas = (known after apply)
+ resource_id = (known after apply)
+ skip_final_snapshot = true
+ snapshot_identifier = (known after apply)
+ status = (known after apply)
+ storage_encrypted = false
+ storage_throughput = (known after apply)
+ storage_type = (known after apply)
+ tags_all = (known after apply)
+ timezone = (known after apply)
+ username = "database_username"
+ vpc_security_group_ids = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.
This tells us which resources we declared and that are going to be created, changed or destroyed using the code we wrote.
As Bane said in The Dark Knight Rises: "It doesn't matter who we are, what matters is our plan", after reviewing and check the plan Terraform showed us, it is time to apply it:
terraform apply -auto-approve
This will apply the plan and should take like three minutes to AWS make the health check of the instance available.
aws_db_instance.my-free-rds-db-instance: Creating...
aws_db_instance.my-free-rds-db-instance: Still creating... [10s elapsed]
aws_db_instance.my-free-rds-db-instance: Still creating... [20s elapsed]
...
If you log in to your AWS account and go to RDS, you'll see that the instance is going to be in the creating state:
After a few minutes, it will be available and Terraform will tell us:
...
aws_db_instance.my-free-rds-db-instance: Still creating... [3m30s elapsed]
aws_db_instance.my-free-rds-db-instance: Still creating... [3m40s elapsed]
aws_db_instance.my-free-rds-db-instance: Creation complete after 3m49s [id=db-UZADWSYL266LYV2W6D6H7CUM4U]
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
You may check the endpoint to the instance via AWS Console or just accessing your terraform.tfstate
and looking for the address
. To connect, you just connect like it was a usual database.
Remember to use SSL to make the connection, otherwise you'll get an error. You could also add a parameter group in your Terraform files to make the
force_ssl
to 0 in your db instance.
Conclusion
This was a simple guide to introduce the readers on Terraform and also help them to create a free PostgreSQL RDS instance in AWS.
I hope this helps you to spin up a free PostgresQL to simulate a production-ready application and learn a bit more about using Terraform.
This project code is available on Github: terraform-aws-free-rds
Top comments (1)
Thank you very much! It is exactly what I needed <3