AWS with Terraform Tutorial: AWS RDS Instances (13)

· Updated · 12 min read · Terraform & OpenTofu Tutorials

How to create Amazon Relational Database Service (RDS) Instances with Terraform #

Using the Terraform aws_db_instance resource block to configure, launch, and secure RDS instances.

Welcome to our tutorial series where we dive into cloud infrastructure deployment using Terraform or OpenTofu on AWS. In this series, the fundamentals are shown, guiding you through the process of minimizing resource usage and simplifying the deployment complexities associated with cloud infrastructure.

This comprehensive OpenTofu and Terraform tutorial guides you step-by-step through creating infrastructure in AWS using Terraform.

Terraform is an Infrastructure as Code (IaC) tool used to provision and manage infrastructure. It helps define and deploy resources across various cloud providers using code, making it easier to maintain and scale infrastructure.Terraform is an Infrastructure as Code (IaC) tool used to provision and manage infrastructure. It helps define and deploy resources across various cloud providers using code, making it easier to maintain and scale infrastructure.
Terraform
Basics
Terraform...
AWS is the world’s leading cloud platform, it is used by a wide range of organizations, from startups to large enterprises, to power their online businesses. AWS offers a wide range of services, including computing, storage, database, networking, analytics, machine learning, and artificial intelligence.AWS is the world’s leading cloud platform, it is used by a wide range of organizations, from startups to large enterprises, to power their online businesses. AWS offers a wide range of services, including computing, storage, database, networking, analytics, machine learning, and artificial intelligence.
AWS
Basics
AWS...
The Terraform official AWS provider acts as an abstraction layer that lets Terraform configurations written in HCL define AWS services and infrastructure using code (IaC). Internally Terraform and the AWS provider handle authentication, and make the necessary AWS API calls to query, create, modify, and destroy the resources.The Terraform official AWS provider acts as an abstraction layer that lets Terraform configurations written in HCL define AWS services and infrastructure using code (IaC). Internally Terraform and the AWS provider handle authentication, and make the necessary AWS API calls to query, create, modify, and destroy the resources.
Terraform
AWS Provider
Terraform...
How to Create, and Manage AWS VPCs with TerraformHow to Create, and Manage AWS VPCs with Terraform
AWS VPC
AWS VPC
How to configure and use the Terraform aws_subnet resource block to create and manage AWS Subnets inside a VPC.How to configure and use the Terraform aws_subnet resource block to create and manage AWS Subnets inside a VPC.
AWS Subnets
AWS Subnets
How to configure and use the Terraform aws_internet_gateway resource block to create and manage AWS Internet Gateway inside a VPC to enable Internet access to and from instances. How to configure and use the Terraform aws_internet_gateway resource block to create and manage AWS Internet Gateway inside a VPC to enable Internet access to and from instances.
AWS Internet
Gateway
AWS Internet...
How to configure and use the Terraform aws_nat_gateway and aws_eip resource blocks to create and manage AWS NAT Gateway and its corresponding Public IPs inside each availability zone to enable Internet access from instances in private subnets.How to configure and use the Terraform aws_nat_gateway and aws_eip resource blocks to create and manage AWS NAT Gateway and its corresponding Public IPs inside each availability zone to enable Internet access from instances in private subnets.
AWS NAT
Gateway
AWS NAT...
How to configure and use the Terraform aws_route_table, aws_route, and aws_main_route_table_association resource blocks to create and manage AWS Routing Tables.How to configure and use the Terraform aws_route_table, aws_route, and aws_main_route_table_association resource blocks to create and manage AWS Routing Tables.
AWS Routing
Tables
AWS Routing...
How to configure and use the Terraform aws_security_group and aws_security_group_rule resource blocks to create and manage AWS Security Groups and secure the infrastructure.How to configure and use the Terraform aws_security_group and aws_security_group_rule resource blocks to create and manage AWS Security Groups and secure the infrastructure.
AWS Security
Groups
AWS Security...
How to configure and use the Terraform aws_key_pair resource block to create and manage AWS Key Pairs for performing SSH Public Key Authentication into EC2 instances.How to configure and use the Terraform aws_key_pair resource block to create and manage AWS Key Pairs for performing SSH Public Key Authentication into EC2 instances.
AWS Key
Pairs
AWS Key...
How to configure and use the Terraform aws_ami data source block to find and use AWS AMIs as templates (root volume snapshot with operating system and applications) for EC2 instances.How to configure and use the Terraform aws_ami data source block to find and use AWS AMIs as templates (root volume snapshot with operating system and applications) for EC2 instances.
AWS AMIs
AWS AMIs
Using the Terraform aws_instance resource block to configure, launch, and secure EC2 instances.Using the Terraform aws_instance resource block to configure, launch, and secure EC2 instances.
AWS EC2
Instances
AWS EC2...
AWS RDS
AWS RDS
Using the Terraform aws_route53_delegation_set, aws_route53_zone, and aws_route53_record resource blocks to configure DNS in AWS. Using the Terraform aws_route53_delegation_set, aws_route53_zone, and aws_route53_record resource blocks to configure DNS in AWS.
AWS Route 53
(DNS)
AWS Route 53...
Amazon EC2 Auto Scaling groups keep the right number of instances running, replace failed ones and scale with the load.Amazon EC2 Auto Scaling groups keep the right number of instances running, replace failed ones and scale with the load.
AWS Auto
Scaling
AWS Auto...
Elastic Load Balancing distributes the traffic between healthy instances. The tutorial creates an Application Load Balancer with HTTPS.Elastic Load Balancing distributes the traffic between healthy instances. The tutorial creates an Application Load Balancer with HTTPS.
AWS Load
Balancers
AWS Load...
This tutorial shows how to create infrastructure in AWS using Terraform and configure the operating system and applications using Ansible.This tutorial shows how to create infrastructure in AWS using Terraform and configure the operating system and applications using Ansible.
Terraform,
AWS & Ansible
Terraform,...
Modules package resources behind variables and outputs so they can be reused. The tutorial refactors the network into a module.Modules package resources behind variables and outputs so they can be reused. The tutorial refactors the network into a module.
Terraform
Modules
Terraform...
A backend stores the Terraform state. The tutorial uses an encrypted, versioned S3 bucket with state locking.A backend stores the Terraform state. The tutorial uses an encrypted, versioned S3 bucket with state locking.
Terraform
Backends
Terraform...
fmt, validate, TFLint, Trivy, Checkov, terraform-docs, Infracost and pre-commit: check the code before it reaches AWS.fmt, validate, TFLint, Trivy, Checkov, terraform-docs, Infracost and pre-commit: check the code before it reaches AWS.
Terraform
Tools
Terraform...
Run checks, plans and approved applies automatically with GitHub Actions and OIDC, without access keys.Run checks, plans and approved applies automatically with GitHub Actions and OIDC, without access keys.
Terraform
CI/CD
Terraform...
Select a tutorial section 
Select a tutorial sectio...

Infrastructure as Code (IaC) helps maintain consistency, enables version control, enhances collaboration among teams, allows for easier replication of environments, streamlines the deployment and management of infrastructure boosting efficiency, and reducing errors in managing complex systems.

Prerequisites #

Read the previous sections of the tutorial, listed in the series index at the end of this page.

AWS RDS #

Amazon RDS (Relational Database Service) is a cloud-based service offered by Amazon Web Services (AWS) that simplifies the setup, operation, and scaling of relational databases such as MySQL, PostgreSQL, SQL Server, Oracle, and MariaDB and Amazon Aurora. It provides features like automated backups, high availability, and scalability, making it easier for developers to manage their databases without worrying about the underlying infrastructure.

Amazon RDS in a VPC: the web servers reach the primary database in a private subnet, RDS replicates synchronously to a standby in another Availability Zone, and the password is stored in Secrets Manager
RDS with a Multi-AZ standby, Secrets Manager and KMS

RDS instances require an RDS Subnet (called a DB subnet group in AWS) that includes at least two availability zones for redundancy and to enable Multi-AZ deployments and database maintenance operations (upgrades), a security rule, and optionally a KMS Key for RDS credentials.

Connectivity Diagrams #

Security should be taken as a priority when designing any infrastructure. Databases should be placed in private subnets and have limited exposure through security groups that deny all inbound traffic except the one originating from the back-end server ditwl-ec-back-end-123.

AWS Terraform Essentials Security Groups RDS Design

Definition of RDS Instances with Terraform #

Previous sections of this AWS with Terraform guide have covered basic information about Terraform and AWS and have configured and used the AWS Terraform provider to create a VPC, four subnets, one Internet Gateway, two NAT Gateways, three Routing Tables, Security Groups, and a Private Key.

Add the following blocks to the terraform-aws-tutorial.tf created in previous sections.

RDS Subnet: ditwl-dbsng-zab-pro-pri #

An RDS Subnet expanding private subnets: ditwl-sn-za-pro-pri-02 and ditwl-sn-zb-pro-pri-06 providing:

  • High Availability: Deploying RDS instances across multiple subnets ensures high availability by distributing the instances across different availability zones (AZs). In case of failures in one AZ, the instances in the other AZs remain operational, minimizing downtime.
  • Fault Tolerance: Having RDS instances in multiple subnets increases fault tolerance. If one subnet or availability zone experiences issues such as network failures or hardware problems, the RDS instances in other subnets can continue to operate normally.
  • Maintenance and failover: with subnets in at least two availability zones, RDS can place the standby replica of a Multi-AZ deployment in a different zone, and recover the database in another zone if a zone is lost.

RDS Subnets are created using terraform resource block aws_db_subnet_group.

# Subnet group for the RDS service
resource "aws_db_subnet_group" "ditwl-dbsng-zab-pro-pri" {
  name       = "ditwl-dbsng-zab-pro-pri"
  subnet_ids = [aws_subnet.ditwl-sn-za-pro-pri-02.id, aws_subnet.ditwl-sn-zb-pro-pri-06.id]
}

Security group: ditwl-sg-rds-001 #

A security group for the RDS Instances. It enables inbound connectivity only from the back-end server ditwl-ec-back-end-123.

Security Groups for RDS are created the same way as for EC2, using terraform resource blocks aws_security_group and aws_security_group_rule.

# Create a Security Group for the RDS Instances
resource "aws_security_group" "ditwl-sg-rds-001" {
  name        = "ditwl-sg-rds-001"
  vpc_id      = aws_vpc.ditlw-vpc.id
  description = "RDS Instance Security"
}

# Allow access from the back-end to port TCP 3306 (MariaDb)
resource "aws_security_group_rule" "ditwl-sr-back-end-to-db" {
  security_group_id        = aws_security_group.ditwl-sg-back-end.id
  type                     = "ingress"
  from_port                = 3306
  to_port                  = 3306
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.ditwl-sg-back-end.id
  description              = "Allow access from the back-end to port TCP 3306 (MariaDb)"
}

KMS Key: ditwl-kmsalias-rds-001-key #

With manage_master_user_password = true, RDS generates a secure random password for the administrator, stores it in AWS Secrets Manager and rotates it periodically, which reduces the risk of unauthorized access and keeps the password out of the Terraform code and state. The secret is encrypted with an AWS Key Management Service (KMS) key, which can be the default AWS managed key or a key created for the database.

KMS Keys are created using the Terraform resource block aws_kms_key, an alias can be specified to give an easy-to-remember name using the Terraform resource block aws_kms_alias.

# KMS Secrets Manager
resource "aws_kms_key" "ditwl-kms-rds-001-key" {
  description = "RDS ditwl-rds-001 Key"
   tags = {
     "Name"         = "ditwl-kms-rds-001-key"  
  }
}

# KMS Alias
resource "aws_kms_alias" "ditwl-kmsalias-rds-001-key" {
  name          = "alias/ditwl-kms-rds-001-key"
  target_key_id = aws_kms_key.ditwl-kms-rds-001-key.key_id
}

RDS Instance/s: ditwl-rds-001

RDS Instances are available for multiple database engines and instance classes. Additionally, it can be configured in Multi-AZ deployment where at least 2 instances are deployed in different availability zones and its data is replicated automatically. Multi-AZ maintains a primary node and a standby instance with automatic database failover that typically takes one to two minutes. A more advanced configuration (Multi-AZ DB cluster) has two readable standby instances and a failover of about 35 seconds.

RDS Instances are created using the Terraform resource block aws_db_instance.

# RDS (Database)
resource "aws_db_instance" "ditwl-rds-001" {
  identifier           = "ditwl-rds-001"
  engine               = "mariadb" //oracle-ee
  db_name              = "ditwldb01"  //The name of the database to create
  allocated_storage    = 20 
  instance_class       = "db.t4g.micro" //2vCPU	1G Mem (Burstable Graviton2 Processor 64-bit Arm)
  db_subnet_group_name = "ditwl-dbsng-zab-pro-pri"
  username             = "admin"
  manage_master_user_password   = true
  master_user_secret_kms_key_id = aws_kms_key.ditwl-kms-rds-001-key.key_id  
  skip_final_snapshot  = true
  backup_retention_period = 0
  depends_on = [ aws_db_subnet_group.ditwl-dbsng-zab-pro-pri ]
}

Run the Terraform Plan #

Open a command line shell in the directory where the file terraform-aws-tutorial.tf is located, and run the Terraform or OpenTofu plan and apply commands.

Plan using OpenTofu #

Run tofu plan to generate and review the execution plan. Check each line and value to make sure that it corresponds to the desired change.

Terraform will refresh the state by comparing it with the Cloud resources and produce a plan for the resources that need to be created, updated, or destroyed.

In the previous section of the tutorial, some resources were commented out to reduce the infrastructure cost during development. The RDS instances have dependencies with some previous resources, please uncomment all resources to proceed with the Terraform plan.

Run the Terraform plan:

$ tofu plan
data.aws_ami.ubuntu-23-04-arm64-minimal: Reading...
...
aws_main_route_table_association.ditwl-rta-default: Refreshing state... [id=rtbassoc-09cc07db0cbfb0a7a]

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

OpenTofu will perform the following actions:

  # aws_db_instance.ditwl-rds-001 will be created
  + resource "aws_db_instance" "ditwl-rds-001" {
      + allocated_storage                     = 20
      + db_name                               = "ditwldb01"
      + db_subnet_group_name                  = "ditwl-dbsng-zab-pro-pri"
      + engine                                = "mariadb"
      + identifier                            = "ditwl-rds-001"
      + instance_class                        = "db.t4g.micro"
      + manage_master_user_password           = true
      ...
      + tags_all                              = {
          + "cost_center" = "marketing-department"
          + "environment" = "pro"
          + "owner"       = "IT Wonder Lab"
        }
      + timezone                              = (known after apply)
      + username                              = "admin"
      + vpc_security_group_ids                = (known after apply)
    }

  # aws_db_subnet_group.ditwl-dbsng-zab-pro-pri will be created
  + resource "aws_db_subnet_group" "ditwl-dbsng-zab-pro-pri" {
      + name                    = "ditwl-dbsng-zab-pro-pri"
      + subnet_ids              = [
          + "subnet-006842d378df0ac09",
          + "subnet-06a52a222034e1007",
        ]
    ...
    }

  # aws_kms_alias.ditwl-kmsalias-rds-001-key will be created
  + resource "aws_kms_alias" "ditwl-kmsalias-rds-001-key" {
      + name           = "alias/ditwl-kms-rds-001-key"
    }

  # aws_kms_key.ditwl-kms-rds-001-key will be created
  + resource "aws_kms_key" "ditwl-kms-rds-001-key" {
      + description                        = "RDS ditwl-rds-001 Key"
      + tags_all                           = {
          + "Name"        = "ditwl-kms-rds-001-key"
          + "cost_center" = "marketing-department"
          + "environment" = "pro"
          + "owner"       = "IT Wonder Lab"
        }
    }

  # aws_security_group.ditwl-sg-rds-001 will be created
  + resource "aws_security_group" "ditwl-sg-rds-001" {
      + arn                    = (known after apply)
      + description            = "RDS Instance Security"
      + name                   = "ditwl-sg-rds-001"
      + tags_all               = {
          + "cost_center" = "marketing-department"
          + "environment" = "pro"
          + "owner"       = "IT Wonder Lab"
        }
      + vpc_id                 = "vpc-0b0b93490d844bd26"
    }

  # aws_security_group_rule.ditwl-sr-back-end-to-db will be created
  + resource "aws_security_group_rule" "ditwl-sr-back-end-to-db" {
      + description              = "Allow access from the back-end to port TCP 3306 (MariaDb)"
      + from_port                = 3306
      + protocol                 = "tcp"
      + security_group_id        = "sg-08c87b959de8e880f"
      + security_group_rule_id   = (known after apply)
      + source_security_group_id = "sg-08c87b959de8e880f"
      + to_port                  = 3306
      + type                     = "ingress"
    }

Plan: 6 to add, 0 to change, 0 to destroy.

Apply the changes using OpenTofu #

Run tofu apply to generate and apply the execution plan. OpenTofu will generate a new plan and ask for confirmation before applying the changes. Review the changes and answer yes or no to apply the changes in AWS.

$ tofu apply
data.aws_ami.ubuntu-23-04-arm64-minimal: Reading...
...
aws_main_route_table_association.ditwl-rta-default: Refreshing state... [id=rtbassoc-09cc07db0cbfb0a7a]

OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  + create

OpenTofu will perform the following actions:

  # aws_db_instance.ditwl-rds-001 will be created
  + resource "aws_db_instance" "ditwl-rds-001" {
    ...
    }

  # aws_db_subnet_group.ditwl-dbsng-zab-pro-pri will be created
  + resource "aws_db_subnet_group" "ditwl-dbsng-zab-pro-pri" {
    ...
    }

  # aws_kms_alias.ditwl-kmsalias-rds-001-key will be created
  + resource "aws_kms_alias" "ditwl-kmsalias-rds-001-key" {
    ...
    }

  # aws_kms_key.ditwl-kms-rds-001-key will be created
  + resource "aws_kms_key" "ditwl-kms-rds-001-key" {
    ...
    }

  # aws_security_group.ditwl-sg-rds-001 will be created
  + resource "aws_security_group" "ditwl-sg-rds-001" {
    ...
    }

  # aws_security_group_rule.ditwl-sr-back-end-to-db will be created
  + resource "aws_security_group_rule" "ditwl-sr-back-end-to-db" {
    ...
    }

Plan: 6 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  OpenTofu will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_security_group_rule.ditwl-sr-back-end-to-db: Creating...
aws_kms_key.ditwl-kms-rds-001-key: Creating...
aws_db_subnet_group.ditwl-dbsng-zab-pro-pri: Creating...
aws_security_group.ditwl-sg-rds-001: Creating...
aws_security_group_rule.ditwl-sr-back-end-to-db: Creation complete after 2s [id=sgrule-3481528461]
aws_db_subnet_group.ditwl-dbsng-zab-pro-pri: Creation complete after 3s [id=ditwl-dbsng-zab-pro-pri]
aws_security_group.ditwl-sg-rds-001: Creation complete after 5s [id=sg-0beca4086969e7711]
aws_kms_key.ditwl-kms-rds-001-key: Creation complete after 8s [id=85140d6a-5030-4408-90d0-e8fe73218960]
aws_kms_alias.ditwl-kmsalias-rds-001-key: Creating...
aws_db_instance.ditwl-rds-001: Creating...
aws_kms_alias.ditwl-kmsalias-rds-001-key: Creation complete after 0s [id=alias/ditwl-kms-rds-001-key]
aws_db_instance.ditwl-rds-001: Still creating... [10s elapsed]
...
aws_db_instance.ditwl-rds-001: Still creating... [3m50s elapsed]
aws_db_instance.ditwl-rds-001: Creation complete after 3m52s [id=db-5YYLZZV5SR65HM2GAWM3YRXOQM]

Apply complete! Resources: 6 added, 0 changed, 0 destroyed

Review the result, it should be 6 (resource) added, 0 changed, and 0 destroyed.

You can check that the RDS instance has been created in the AWS Console.

Navigate to AWS Console: RDS databases.

RDS us east

Access the RDS admin password using the AWS Console: AWS Secrets Manager: Secrets.

rds db Secrets Manager Secrets Manager us east

Accessing the Database #

The RDS database instances are located in private subnets that don’t give public IP addresses to their instances. Accessing private RDS instances requires a VPN (Site-to-Site VPN, Direct Connect) or a bastion instance. AWS Systems Manager Session Manager can forward a local port to the database through an instance, without opening any inbound port.

Destroy the infrastructure using OpenTofu #

AWS will bill the time that the infrastructure is running. To reduce the cost during Terraform development or when the infrastructure is not needed anymore terraform destroy will be used.

Terraform destroy terminates all the resources created by the plan.

Run tofu destroy to generate and apply the destruction plan. OpenTofu will generate a new plan with the resources to terminate and ask for confirmation before applying the changes.

All data will be lost but you will be able to recreate the infrastructure when needed using tofu apply.

$ tofu destroy
...
aws_subnet.ditwl-sn-za-pro-pri-02: Destroying... [id=subnet-06a52a222034e1007]
aws_kms_key.ditwl-kms-rds-001-key: Destruction complete after 1s
aws_subnet.ditwl-sn-za-pro-pri-02: Destruction complete after 2s
aws_subnet.ditwl-sn-zb-pro-pri-06: Destruction complete after 2s
aws_vpc.ditlw-vpc: Destroying... [id=vpc-0b0b93490d844bd26]
aws_vpc.ditlw-vpc: Destruction complete after 3s

Destroy complete!

AWS RDS Cost #

RDS instances have different prices depending on their type, region, purchase model (on-demand, saving plan, or spot), and Multi-AZ configuration. Check Amazon RDS Pricing.

Common Questions About AWS RDS #

Can Terraform manage RDS backups and maintenance? #

Yes, Terraform can manage RDS backups and maintenance. Automated backups and retention period (backup_retention_period) can be configured using Terraform. A configurable maintenance window controls when maintenance tasks, such as updates and patching, occur on the RDS instances.

Is it possible to modify existing RDS instances with Terraform? #

Terraform allows you to modify existing RDS instances by updating the corresponding resource blocks in the Terraform configuration file. Some changes require RDS Instance destruction, other parameters can be applied without destroying the databases.

What are the advantages of using Terraform with AWS RDS? #

Using Terraform with AWS RDS offers several advantages, including:

  • Infrastructure as code: Define and manage RDS resources using version-controlled configuration files.
  • Automation: Easily provision, modify, and tear down RDS instances and associated resources programmatically.
  • Consistency: Ensure consistency across environments by deploying RDS instances using the same configuration every time.
  • Collaboration: Enable teams to collaborate on infrastructure changes using Terraform configuration files stored in a shared repository.

Next Steps #

Continue with AWS Route 53 (DNS), or see the full series index.

#AWS #AWS RDS #AWS Terraform Tutorial #OpenTofu #Terraform