(IaC) Parte 5: Provisionando um banco de dados no AWS RDS com Terraform

pa, bora seguir na nossa trilha de IaC, hoje com Terraform e AWS RDS. Tenho certeza que vocês vão gostar, afinal o que o artigo vai abordar é bem utilizado no mercado e mostrarei um case real mesmo, com boas práticas. Pegue seu café, se acomode na sua cadeira e bora aprender mais sobre IaC e Terraform.🚀

Já sugiro que você veja sobre IaC nessa talk abaixo, onde o CTO da Hashicorp, Armon Dadgar, explica bem e cita a importância de infra como código:

Outro artigo interessante é esse abaixo:

Sem mais delongas, vamos nessa!

  • O que vamos ver nesse artigo?
    -
    Nosso cenário;
    - Instalação do Terraform;
    - Overview do Amazon RDS;
    - Organizando os arquivos “.tf”
    - Executando nosso plano com Terraform;
    - Aplicando as alterações;
    - Destruindo nossa infra no RDS;
    - Terraform Associate Certification.
  • Nosso cenário:

- Uma conta na AWS (free tier);
- Muita vontade de aprender IaC, Terraform e gostar de cloud computing 😅
- Uma distribuição Linux (Debian based);
- Gosto muito do VsCode para desenvolvimento, mas pode usar outra IDE de sua preferência.

  • Instalação do Terraform no Linux Ubuntu:

Para a instalação do Terraform, vou deixar um link abaixo, onde você pode baixar o binário, é bem simples de se instalar o binário do Terraform:

Assim que você acessar essa página, logo embaixo terá as opções de download do Terraform, podendo escolher entre Mac, Linux, Windows, Solaris, etc.

Basicamente, irei mostrar a instalação no Linux Ubuntu:

1- Instalação do pacote unzip:

$ apt install unzip -y

2- Confirme o número da versão mais recente no site do Terraform:

https://www.terraform.io/downloads.html

3- Baixe a versão mais recente do Terraform:

$ wget https://releases.hashicorp.com/terraform/0.12.24/terraform_0.12.24_linux_amd64.zip

4- Extraia o arquivo baixado:

$ unzip terraform_0.12.24_linux_amd64.zip

5- Mova o executável para o diretório /usr/local/bin:

$ sudo mv terraform /usr/local/bin/

6- Agora por fim, podemos executar o Terraform:

$ terraform --version
  • Overview do Amazon RDS?

O RDS é um serviço de banco de dados relacional fornecido pela Amazon. Vou mostrar o básico do básico do que consiste o serviço RDS da AWS, mas como é de extrema importância, sugiro que você leia a documentação abaixo:

Vejamos alguns itens que são importantes:

- É um serviço 100% gerenciado pela AWS;
- Permite escolher o tipo da instância;
- Escalável;
- Fornece recurso de backup;
- Alta performance;
- Seguro;
- Baixo custo;

Alguns exemplos de bancos de dados que podem ser criados:
- Postgres
- MySQL
- MariaDB
- Oráculo
- Microsoft SQL Server
- Aurora (banco de dados proprietário da AWS)

No nosso case, vamos demonstrar o deploy de uma instância MySQL no RDS, vai ser maneiro. Antes mesmo de estudar Terraform, eu já utilizava esse serviço mas sempre usava no console da AWS, com infraestrutura como código, é tudo na CLI mesmo, você seta a versão, a storage que precisa, o nome do banco, tudo na linha de comando, é topzera! Bora para o próximo passo!

  • Organizando os arquivos “.tf”

Bom, já que comentamos sobre IaC, sobre RDS e temos nosso Terraform instalado, vamos para a prática. Eu criei dois arquivos para fazer esse provisionamento, um de nome db.tf e outro variables.tf, que é meu arquivo de variáveis. Meu arquivo db.tf está bem simples, quero mostrar o básico mas de uma forma que você possa iniciar seus estudos com Terraform sem grandes dificuldades, veja como ficou o arquivo:

provider "aws" {
shared_credentials_file = "/home/user/.aws/credentials"
profile = "myprofile"
region = "sa-east-1"
}
resource "aws_db_instance" "default" {
allocated_storage = var.allocated_storage
storage_type = var.storage_type
engine = var.engine
engine_version = var.engine_version
instance_class = var.instance_class
name = var.name
username = var.username
password = var.password
port = var.port
identifier = var.identifier
parameter_group_name = var.parameter_group_name
skip_final_snapshot = var.skip_final_snapshot
}

Eu poderia adicionar outros recursos/atributos para o RDS, mas para começo isso é o suficiente, depois conforme sua demanda da infra for aumentando você pode adicionar mais recursos, módulos e automatizar ainda mais.

Dica de boas práticas:

Ao expor credenciais de cloud oroviders (ex: AWS) no código, use variáveis de ambiente para gerenciar isso, eu usei o recurso de shared_credentials_file.

Agora, vamos ver o arquivo de variables.tf que ficou da seguinte forma:

variable "engine" {
description = "The database engine"
type = string
default = "mysql"
}
variable "allocated_storage" {
description = "The amount of allocated storage."
type = number
default = 20
}
variable "storage_type" {
description = "type of the storage"
type = string
default = "gp2"
}
variable "username" {
description = "Username for the master DB user."
default = "databaseteste"
type = string
}
variable "password" {
description = "password of the database"
default = "password"
type = string
}
variable "instance_class" {
description = "The RDS instance class"
default = "db.t2.micro"
type = string
}
variable "parameter_group_name" {
description = "Name of the DB parameter group to associate"
default = "default.mysql5.7"
type = string
}
variable "engine_version" {
description = "The engine version"
default = "5.7"
type = number
}
variable "skip_final_snapshot" {
description = "skip snapshot"
default = "true"
type = string
}
variable "identifier" {
description = "The name of the RDS instance"
default = "terraform-database-test"
type = string
}
variable "port" {
description = "The port on which the DB accepts connections"
default = "3306"
type = number
}
variable "name" {
description = "The database name"
default = "Mysqldatabase"
type = string
}

Agora que temos nossos arquivos criados, vamos primeiramente entender alguns dos principais comandos do Terraform, você provavelmente utilizará todos eles no dia a dia.

Dica de boas práticas:

O Terraform não possui recurso nativo para controlar dados sensíveis no código (até onde conheço), como a senha do banco. Eu exemplifiquei a nível de estudo mesmo, mas o correto é usar as boas práticas, por isso na sua infra, utilize recursos do seu provider. Na AWS é possível jogar os dados no SSM Parameter Store ou no Secret Manager.
Outra opção seria usar o terraform-sops, indicação de um colega do grupo.

Mais uma dica, é possível integrar o Terraform ao Vault.

  • Comandos mais utilizados do Terraform:

O comando terraform init vai baixar todos os plugins do provider AWS, o qual estamos utilizando. O comando terraform plan gera um plano da sua infraestrutura, ele gera um arquivo binário que só o Terraform pode ver, logo depois tem o comando terraform apply que aplica esse plano, ele vai olhar no arquivo de estado, e vai conectar na AWS e aplicar o plano.

Dica de boas práticas

No momento da execução do seu plano, execute o comando terraform plan utilizando a opção --out para que o plano seja guardado em um outro arquivo, ele vai ler tudo que está declarado nos blocos do provider e dos recursos, e vai gerar um plano dentro do arquivo que você especificar.

Bom, vamos executar nosso comando terraform init :

$ terraform initInitializing the backend...Initializing provider plugins...The following providers do not have any version constraints in configuration,
so the latest version was installed.
To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.
* provider.aws: version = "~> 2.58"Terraform has been successfully initialized!You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

Agora que ele baixou todos os plugins do provider AWS, vamos para o próximo comando, onde iremos criar o plano da nossa infra no RDS, usando o comando terraform plan --out rds-plan

$ terraform plan --out rds-plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.
------------------------------------------------------------------------An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:# aws_db_instance.default[0] will be created
+ resource "aws_db_instance" "default" {
+ address = (known after apply)
+ allocated_storage = 20
+ apply_immediately = (known after apply)
+ arn = (known after apply)
+ auto_minor_version_upgrade = true
+ availability_zone = (known after apply)
+ backup_retention_period = (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_subnet_group_name = (known after apply)
+ delete_automated_backups = true
+ endpoint = (known after apply)
+ engine = "mysql"
+ engine_version = "5.7"
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ identifier = "terraform-database-test"
+ identifier_prefix = (known after apply)
+ instance_class = "db.t2.micro"
+ kms_key_id = (known after apply)
+ license_model = (known after apply)
+ maintenance_window = (known after apply)
+ monitoring_interval = 0
+ monitoring_role_arn = (known after apply)
+ multi_az = (known after apply)
+ name = "mydb"
+ option_group_name = (known after apply)
+ parameter_group_name = "default.mysql5.7"
+ 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 = false
+ replicas = (known after apply)
+ resource_id = (known after apply)
+ skip_final_snapshot = true
+ status = (known after apply)
+ storage_type = "gp2"
+ timezone = (known after apply)
+ username = "databaseteste"
+ vpc_security_group_ids = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.------------------------------------------------------------------------Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

Pode ver que muitos recursos só saberemos após o comando terraform apply . Agora temos que aplicar esse plano que foi gerado, usando o comando terraform apply novamente:

$ terraform apply rds-planAn execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:# aws_db_instance.default[0] will be created
+ resource "aws_db_instance" "default" {
+ address = (known after apply)
+ allocated_storage = 20
+ apply_immediately = (known after apply)
+ arn = (known after apply)
+ auto_minor_version_upgrade = true
+ availability_zone = (known after apply)
+ backup_retention_period = (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_subnet_group_name = (known after apply)
+ delete_automated_backups = true
+ endpoint = (known after apply)
+ engine = "mysql"
+ engine_version = "5.7"
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ identifier = "terraform-database-test"
+ identifier_prefix = (known after apply)
+ instance_class = "db.t2.micro"
+ kms_key_id = (known after apply)
+ license_model = (known after apply)
+ maintenance_window = (known after apply)
+ monitoring_interval = 0
+ monitoring_role_arn = (known after apply)
+ multi_az = (known after apply)
+ name = "mydb"
+ option_group_name = (known after apply)
+ parameter_group_name = "default.mysql5.7"
+ 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 = false
+ replicas = (known after apply)
+ resource_id = (known after apply)
+ skip_final_snapshot = true
+ status = (known after apply)
+ storage_type = "gp2"
+ timezone = (known after apply)
+ username = "databaseteste"
+ vpc_security_group_ids = (known after apply)
}
Plan: 1 to add, 0 to change, 0 to destroy.Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yesaws_db_instance.default[0]: Creating...
aws_db_instance.default[0]: Still creating... [10s elapsed]
aws_db_instance.default[0]: Still creating... [20s elapsed]
aws_db_instance.default[0]: Still creating... [30s elapsed]
aws_db_instance.default[0]: Still creating... [40s elapsed]
aws_db_instance.default[0]: Still creating... [50s elapsed]

Veja que os recursos estão sendo criados ainda pelo Terraform, demora alguns segundos/minutos.

Vamos no console da AWS para ver como que ficou nossa instância:

Instância criada no AWS RDS
Sumário da instância MySQL criada

Eu poderia mostrar como acessar esse banco de dados e criar outros recursos, mas isso vai fazer o artigo ficar extremamente extenso. Vamos para a última etapa, que é destruir nossa infra no RDS. Para isso vamos usar o comando terraform destroy que basicamente vai no arquivo de estado, olha como estão os recursos, vai no provider da AWS e destrói tudo.

Pode ver que ele logo avisa, vou destruir tudo, ok?

$ terraform destroy 
aws_db_instance.default[0]: Refreshing state... [id=terraform-database-test]
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:# aws_db_instance.default[0] will be destroyed
Instância sendo deletada

Olha nossa instância sendo destruída… 😧

Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yesaws_db_instance.default[0]: Destroying... [id=terraform-database-test]
aws_db_instance.default[0]: Still destroying... [id=terraform-database-test, 10s elapsed]
aws_db_instance.default[0]: Still destroying... [id=terraform-database-test, 20s elapsed]

Pronto, a nossa instância MySQL foi criada e destruída no serviço de RDS da Amazon usando o Terraform.

  • Considerações finais

No artigo não foi mencionado sobre o HCL (Linguagem de configuração criada pelo HashiCorp), deixei abaixo um link da documentação para que você possa verificar:

  • Terraform Associate Certification

Recentemente a HashiCorp anunciou que duas de suas ferramentas (Terraform e Vault) estão com exames abertos aos público para certificação, abaixo deixei o anúncio para que você possa olhar e lhe motivar a estudar e ser um profissional certificado.

Isso é tudo pessoal! Obrigado por lerem, por curtirem o artigo, fiquem a vontade para sugerir algo, pode comentar também se não foi o que você esperava, estamos aqui para melhorar sempre e contribuir! #VAIIII 🚀

Senior Linux Administrator at IBM

Senior Linux Administrator at IBM