Skip to content

Using Terraform to provision a managed MariaDB server in AWS

How to rapidly provision a MariaDB in the cloud ? Various option are available.
A very effective approach is to provision MariaDB with Terraform. Terraform is a powerful tool to deploy infrastructure as code. Terraform is developed by Hashicorp that started their business with the very successful Vagrant deployment tool. Terraform allows you to describe through HCL langage all the components of you infrastructure. It is aimed to make it easier to work with multiple cloud providers by abstracting the resources description.

Terraform architecture includes a large set of providers : Azure, AWS, OpenStack … Of course each cloud provider has its specificities regarding available resources. if for example we consider the Terraform AWS provider it allows you to deploy all the low level AWS resources :
VPC, subnets, route, security groups, loadbalancer …
But it also allows you to deploy high level AWS resources including managed resources :
managed RDS databases (Amazon RDS for MariaDB), managed kubernetes cluster(EKS).

If for example you want to create a managed MariaDB RDS database in the AWS cloud it is quite straightforward.

You need to install aws-iam-authenticatorto CLI be able to create the terraform user. We do not want to open the AWS web console and do any click 😉 !

$ curl -o aws-iam-authenticator https://amazon-eks.s3-us-west-2.amazonaws.com/1.10.3/2018-07-26/bin/darwin/amd64/aws-iam-authenticator
$ chmod +x ./aws-iam-authenticator
$ cp ./aws-iam-authenticator $HOME/bin/aws-iam-authenticator

You also need to install the AWS client. You will need this one in case you want to create a S3 bucket to store the terraform state.

$ curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
$ unzip awscli-bundle.zip
$ sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws

Creation of the terraform AWS user

$ aws iam create-user --user-name terraform
$ aws iam attach-user-policy --user-name terraform --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
$ aws iam create-access-key --user-name terraform

Once this is done you are ready to use Terraform. you first reference variables defined in your terraform.tfvars file that describe you AWS credentials. You then describe all the resources you want to provision.

# cloud provider and access details
provider "aws" {
  access_key = "${var.access_key}"
  secret_key = "${var.secret_key}"
  region     = "${var.aws_region}"
}
 
module "vpc" {
  source           = "./network/vpc"
  cidr_block       = "${var.cidr_block}"
 }
 
module "subnets" {
  source           = "./network/subnets"
  vpc_id           = "${module.vpc.vpc_id}"
  vpc_cidr_block   = "${module.vpc.vpc_cidr_block}"
}
 
module "route" {
  source              = "./network/route"
  main_route_table_id = "${module.vpc.main_route_table_id}"
  gw_id               = "${module.vpc.gw_id}"
 
  subnets = [
    "${module.subnets.subnets}",
  ]
}
 
 module "sec_group_rds" {
   source         = "./network/sec_group"
   vpc_id         = "${module.vpc.vpc_id}"
   vpc_cidr_block = "${module.vpc.vpc_cidr_block}"
 } 
 
module "mariadbrds" {
  source = "./rds"
 
  subnets = [
    "${module.subnets.subnets}",
  ]
 
  db_sub_gr_name    = "mariadbrdssub_gr_name"
  sec_grp_rds       = "${module.sec_group_rds.sec_grp_rds}"
  identifier        = "mariadbrds"
  storage_type      = "${var.storage_type}"
  allocated_storage = "${var.allocated_storage}"
  db_engine         = "mariadb"
  engine_version    = "10.3"
  instance_class    = "${var.instance_class}"
  db_username       = "${var.db_username}"
  db_password       = "${var.db_password}"
}

Terraform allows you to structure your code through modules. In our case the rds module contains the description of the privionned MariaDB RDS managed database. In fact this RDS module is generic and could be used to create also PostgreSQL RDS instance. It generates a MariaDB database because of the parameter define in the main.tf calling this module.

data "aws_availability_zones" "available" {}
 
resource "aws_db_subnet_group" "db_sub_gr" {
  description = "terrafom db subnet group"
  name        = "${var.db_sub_gr_name}"
  subnet_ids  = ["${var.subnets}"]
 
  tags {
    Name = "${terraform.workspace}"
  }
}
 
resource "aws_db_instance" "db" {
  identifier        = "${var.identifier}"
  storage_type      = "${var.storage_type}"
  allocated_storage = "${var.allocated_storage[terraform.workspace]}"
  engine            = "${var.db_engine}"
  engine_version    = "${var.engine_version}"
  instance_class    = "${var.instance_class[terraform.workspace]}"
  name              = "${terraform.workspace}"
  username          = "${var.db_username}"
  password          = "${var.db_password}"
 
  vpc_security_group_ids = [
     "${var.sec_grp_rds}",
   ]
 
  db_subnet_group_name = "${aws_db_subnet_group.db_sub_gr.id}"
  storage_encrypted    = false
  skip_final_snapshot  = true
  publicly_accessible = true
  multi_az             = false
 
  tags {
    Name = "${terraform.workspace}"
  }
}

Once we have created these HCL files describing our resources through code we can provision them. To use terraform we need a few step. we need first to initialize the terraform state. Here for simplicity we store it in local file. It could be store in AWS S3.

terraform init

We then need to validate the step that will be done when we apply.

terraform plan

After that if we agree with the plan we can really create the ressources.

terraform apply

Once all the resource have been created we can access our MariaDB instance from the internet.

mysql -h mariadbrds.cskpmjlwgzoy.us-east-1.rds.amazonaws.com -P 3306 -u root -p

This is a very basic example exposing the database to the internet which is not something you would do for production

Regarding terraform state in real world you would not store it on you laptop. Realistically you might want to store you terraform state in an AWS S3 object. this is quite simple to achive. You create an S3 bucket and configure your backend.tf file accordingly.

aws s3 mb s3://terra-state-bucket-sfrezefo1 --region us-east-1 
aws s3api put-bucket-versioning --bucket terra-state-bucket-sfrezefo1 --versioning-configuration Status=Enabled

You specify in your backend.tf file :

terraform {
  backend "s3" {
    bucket = "terra-state-bucket-sfrezefo3"
    key    = "tfstate"
    region = "us-east-2"
  }
}

As terraform is about infrastructure as code it also makes a lot of sense to associate you infrastructure description to a git repository so that you can track changes in you infrastructure. Thus when you evolve you infrastructure you can track the complete history of infrastructure versions. This correspond to the devops principle that you can fail fast as long as you are able to revert rapidly to a stable state.

git init
git add .
git commit -m “first version of my infrastructure :-)”

You would then push your code to a shared repo to allow collaboration from multiple devops that could be accountable for different parts of the infrastructure.
What is great about Terraform is that through its state it will update the infrastructure incrementally deleting or adding only the changed resources.
We could do the same thing with Azure for which there exists an Azure Terraform provider and a managed version of MariaDB : Azure Database for MariaDB

Leave a Reply

Your email address will not be published. Required fields are marked *