This is the continuation of a Terraform demo to create a VPC in AWS with an EC2 instance connected to MariaDB database running in RDS (Amazon Relational Database Service) using a single Terraform plan.
Operating System and software configuration is done using Ansible after the infrastructure has been created.
AWS with Terraform: The Essential Guide: Sections
Our new tutorial series delves into cloud infrastructure deployment using Terraform or OpenTofu on AWS. In this series, the fundamentals are shown, guiding you through minimizing resource usage and simplifying the deployment complexities associated with cloud infrastructure. This tutorial series is a work in progress and will have these sections:
AWS RDS databases are ready-to-use databases backed by AWS experience and with easy-to-use administration tools.
Best Practice: Use Cloud Services When Possible
Focus all your resources and energy on configuring and developing what is really disruptive and unique. Databases, Email servers, DNS servers, and Queues are now a commodity.
Most of the time if you need full control of those services and If you don't have millions of customers there is a big probability that you have a wrong design.
The AWS RDS database is fully configured in the terraform.tfvars file.
Be aware of the password in clear text in terraform.tfvars. It will also show in terraform.tftstate and terraform.tfstate.backup. Currently, the recommended action for passwords in Terraform files is to change them after resource creation using a different tool (AWS CLI, AWS Console, Ansible, DB Client ...). A changed password in Terraform doesn't trigger a resource property change.
#------------------------ # RDS INSTANCES #------------------------ #------------------------ # MariaDB PRO 01 #------------------------ aws_rds_mariadb_pro_pub_01 = { identifier = "ditwl-rds-mariadb-pro-pub-01" allocated_storage = 20 #GB storage_type = "gp2" final_snapshot_id = "ditwl-rds-mariadb-pro-pub-01-final" skip_final_snapshot = false engine = "mariadb" engine_version = "10.2.11" instance_class = "db.t2.micro" password = "**************" username = "ditwlRDSPROdb01" #Start with a letter. Only numbers, letters, and _ accepted, 1 to 16 characters long availability_zone = "us-east-1a" backup_retention_period = 5 #db_subnet_group_name = See var aws_rds_sn_pro_01["name"] multi_az = false vpc_security_group_ids = "" parameter_group_name = "" allow_major_version_up = false publicly_accessible = true tag_private_name = "ditwl-rds-mariadb-pro-pub-01" tag_public_name = "ditwl-rds-mariadb-pro-pub-01" tag_app = "mariadb" tag_app_id = "mariadb-01" tag_os = "rds" tags_environment = "pro" tag_cost_center = "ditwl-permanent" } #------------------------ # RDS Security Group #------------------------ aws_sg_rds_mariadb_pro_pub_01 = { sec_name = "ditwl-aws-sg-rds-mariadb-pro-pub-01" sec_description = "ditwl - MariaDb server access rules - Pub, Env: PRO" allow_all_outbound = false } #------------------------ # Allow access from my Instances to DB port #------------------------ aws_sr_rds_mariadb_pro_pub_01_instances_to_db_port = { type = "ingress" from_port = "3306" to_port = "3306" protocol = "tcp" #source_security_group_id module.aws_sec_group_ec2_default.id description = "Access from Instances to DB port" }
aws_rds_pro_mariadb_01.tf is used to create the security group, assign the security rule, and launch the database.
#---------------------- # RDS MariaDB #---------------------- # SECURITY # Group module "aws_sg_rds_mariadb_pro_pub_01" { source = "./modules/aws/security/group" vpc_id = module.aws_network_vpc.id name = var.aws_sg_rds_mariadb_pro_pub_01["sec_name"] description = var.aws_sg_rds_mariadb_pro_pub_01["sec_description"] } # Rules # Access from instances in group aws_sec_group_ec2_default to DB Port module "aws_sr_rds_mariadb_pro_pub_01_instances_to_db_port" { source = "./modules/aws/security/rule/source_group" security_group_id = module.aws_sg_rds_mariadb_pro_pub_01.id type = var.aws_sr_rds_mariadb_pro_pub_01_instances_to_db_port["type"] from_port = var.aws_sr_rds_mariadb_pro_pub_01_instances_to_db_port["from_port"] to_port = var.aws_sr_rds_mariadb_pro_pub_01_instances_to_db_port["to_port"] protocol = var.aws_sr_rds_mariadb_pro_pub_01_instances_to_db_port["protocol"] source_security_group_id = module.aws_sg_ec2_default.id description = var.aws_sr_rds_mariadb_pro_pub_01_instances_to_db_port["description"] } module "aws_rds_mariadb_pro_pub_01" { source = "./modules/aws/rds/instance" identifier = var.aws_rds_mariadb_pro_pub_01["identifier"] allocated_storage = var.aws_rds_mariadb_pro_pub_01["allocated_storage"] storage_type = var.aws_rds_mariadb_pro_pub_01["storage_type"] final_snapshot_id = var.aws_rds_mariadb_pro_pub_01["final_snapshot_id"] skip_final_snapshot = var.aws_rds_mariadb_pro_pub_01["skip_final_snapshot"] engine = var.aws_rds_mariadb_pro_pub_01["engine"] engine_version = var.aws_rds_mariadb_pro_pub_01["engine_version"] instance_class = var.aws_rds_mariadb_pro_pub_01["instance_class"] password = var.aws_rds_mariadb_pro_pub_01["password"] username = var.aws_rds_mariadb_pro_pub_01["username"] availability_zone = var.aws_rds_mariadb_pro_pub_01["availability_zone"] backup_retention_period = var.aws_rds_mariadb_pro_pub_01["backup_retention_period"] # Workaround for dependency. We need Terraform to wait for aws_rds_sn_pro_01 creation before the RDS DB can use it. db_subnet_group_name = module.aws_rds_sn_pub_pro_01.id #aws_rds_sn_pro_01["name"] multi_az = var.aws_rds_mariadb_pro_pub_01["multi_az"] vpc_security_group_ids = [module.aws_sg_rds_mariadb_default.id,module.aws_sg_rds_mariadb_pro_pub_01.id] publicly_accessible = var.aws_rds_mariadb_pro_pub_01["publicly_accessible"] #parameter_group_name = aws_db_parameter_group.rds_pos_96_db_parameter_group_01.id allow_major_version_up = var.aws_rds_mariadb_pro_pub_01["allow_major_version_up"] tag_private_name = var.aws_rds_mariadb_pro_pub_01["tag_private_name"] tag_public_name = var.aws_rds_mariadb_pro_pub_01["tag_public_name"] tag_app = var.aws_rds_mariadb_pro_pub_01["tag_app"] tag_app_id = var.aws_rds_mariadb_pro_pub_01["tag_app_id"] tag_os = var.aws_rds_mariadb_pro_pub_01["tag_os"] tags_environment = var.aws_rds_mariadb_pro_pub_01["tags_environment"] tag_cost_center = var.aws_rds_mariadb_pro_pub_01["tag_cost_center"] }
A workaround is used to create a dependency between the RDS subnet group and the RDS database. See RDS subnet group creation in part II (VPC/RDS Sub Nets).
db_subnet_group_name = module.aws_rds_sn_pub_pro_01.id
To launch the RDS database the subnet needs to exist, we provide the name of the RDS subnet using the output ID of the RDS subnet as source, this way Terraform is forced to create the RDS subnet first to evaluate the output ID.
Best Practice: Tag all your AWS Resources
Tags can be used to identify resources, split costs between cost centers, and for further configuration, provisioning, and monitoring with dynamic inventories using Ansible or other tools. See AWS Tagging best practices.
Continue the Terraform and Ansible demo, see:
IT Wonder Lab tutorials are based on the diverse experience of Javier Ruiz, who founded and bootstrapped a SaaS company in the energy sector. His company, later acquired by a NASDAQ traded company, managed over €2 billion per year of electricity for prominent energy producers across Europe and America. Javier has over 25 years of experience in building and managing IT companies, developing cloud infrastructure, leading cross-functional teams, and transitioning his own company from on-premises, consulting, and custom software development to a successful SaaS model that scaled globally.
Are you looking for cloud automation best practices tailored to your company?