Felipe Fonseca Rocha
Curioso por natureza e grande apreciador de novos conhecimentos, independente da disciplina!
Nos possibilita a trabalhar de uma maneira ordenada para criar e gerênciar infraestrutura de forma legível.
// S3 bucket to save IaC state
resource "aws_s3_bucket" "backend" {
bucket = "${var.project}-backend"
}
// S3 Policy access of states
resource "aws_s3_bucket_policy" "backend-policy" {
bucket = aws_s3_bucket.backend.id
policy = jsonencode({
Version: "2012-10-17",
Statement: [
{
Principal:{
AWS:"*"
},
Effect: "Allow",
Action: [
"s3:ListBucket"
],
Resource: aws_s3_bucket.backend.arn
},
{
Principal:{
AWS:"*"
},
Effect: "Allow",
Action: [
"s3:GetObject",
"s3:PutObject"
],
Resource: "${aws_s3_bucket.backend.arn}/*"
}
]
})
}
<BLOCK TYPE> "<BLOCK LABEL>" "<BLOCK LABEL>" {
# Block body
<IDENTIFIER> = <EXPRESSION> # Argument
}
ferramenta de gerenciamento de configurações:
Estado (state)
Arquivos
Informações sensíveis
Gerenciado por - Backend:
remoto (s3, consu etc.)
(nunca em VCS)
Importar estado atual (terraforming)
Workspaces (branches)
{
"version": 4,
"terraform_version": "0.12.18",
"serial": 116,
"lineage": "f6fe0bd8-042b-d670-2067-160660e09f59",
"outputs": {
"amazon_linux_id": {
"value": "ami-idamazon",
"type": "string"
},
"api_nlb_dns": {
"value": "xproject-lb-dev-idamazon.elb.us-east-1.amazonaws.com",
"type": "string"
},
"api_repository_url": {
"value": "xxxxxxxxxxxx.dkr.ecr.us-east-1.amazonaws.com/xproject-rep-dev",
"type": "string"
},
"arn_database_ocp": {
"value": "arn:aws:s3:::xproject-bkp-ocp-dev",
"type": "string"
},
"db_nlb_dns": {
"value": "xproject-db-lb-dev-idamazon.elb.us-east-1.amazonaws.com",
"type": "string"
},
"ubuntu_sql_standard_id": {
"value": "ami-idamazon",
"type": "string"
}
},
.....
.....
Backend S3
Sincronização de state
Lock de state usando um banco de dados
terraform {
required_version = ">= 0.12"
backend "s3" {
bucket="backend-dev"
key="terraform.tfstate"
region="us-east-1"
profile="dev"
dynamodb_table="terraform-lock"
}
}
Estado (remote state)
# Using a partial configure to replace during initialization
export TF_CLI_ARGS_init="-backend-config=<partial_config>"
Reusabilidade do código
Configurações dinâmicas
Estrutura:
/**
* Id de imagem
*/
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."
default = "ami-xxxxxxxxxx"
}
/**
* Valores locais tipo de instancia
*/
locals {
instance_type = "t2.micro"
}
/**
* Recurso instancia EC2 usando imagem (OS e config)
* e tamanho definido
*/
resource "aws_instance" "example" {
instance_type = local.instace_type
ami = var.image_id
}
Variáveis
#!/bin/bash
# Atribuíção direta durante o apply
terraform apply -var="image_id=ami-abc123"
# Atribuíção direta por arquivo
terraform apply -var-file="testing.tfvars"
Retorno de parametros
Referenciar outros modulos
Estrutura:
/**
* Recurso instancia EC2 usando imagem (OS e config)
* e tamanho definido
*/
resource "aws_instance" "example" {
instance_type = var.instace_type
ami = var.image_id
}
/**
* Retorna endereço de ip da instancia para
* ser utilizado para configuração de arquivo
* externo
*/
output "instance_ip_addr" {
value = aws_instance.example.private_ip
description = "The private IP address of the example instance."
depends_on = [
# Security group rule must be created before this IP address could
# actually be used, otherwise the services will be unreachable.
aws_security_group_rule.local_access,
]
}
Saídas
#!/bin/bash
# Exporta valores de output do state para console
terraform output <resource>
# Parameter to a module (not mandatory)
variable "container_port_mappings" {
type = list(object({
hostPort = number
containerPort = number
protocol = string
}))
default = [{
hostPort = 0
containerPort = 8081
protocol = "tcp"
}]
}
# Output from a module
output "swagger_s3_id" {
value = aws_s3_bucket.bkp-lambda-swagger.id
}
# Variable in files can be
# used to configure a entire module
# backend.dev.tfvars
bucket="backend-dev"
key="terraform.tfstate"
region="us-east-1"
profile="dev"
dynamodb_table="terraform-lock"
Procurar por recursos
Dados de provisioners
exemplos:
template_file (redenrizar)
archive_file (zip)
específicos de providers
/**
* zip o arquivo em questão
*/
data "archive_file" "upsert-rds" {
type = "zip"
source_dir = "${local.paths.template}${local.lambda_names.upsert-rds}"
output_path = "${local.paths.output}${local.lambda_names.upsert-rds}.zip"
}
/**
* Retorna o arquivo renderizado no formato original
* substituindo todas as ocorrencias de ${cluster_name}
* pelo valor retronado do output do modulo
*/
data "template_file" "ecs_ec2_user_data" {
template = file("${path.module}/templates/user-data.sh")
vars = {
cluster_name = module.ecs.cluster_name
}
}
/**
* Retorna as imagens disponiveis com o filtro
* abaixo
*/
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
name_regex = "^amzn2-ami-hvm.*-ebs"
filter {
name = "architecture"
values = ["x86_64"]
}
}
Dados externos
# Data loaded to be used in terraform config
# This case from file to user-data
data "template_file" "ecs_ec2_user_data" {
template = file("${path.module}/templates/user-data.sh")
vars = {
cluster_name = module.ecs.cluster_name
}
}
# Search data of AMI code instances Image
data "aws_ami" "amazon_linux_ec2" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn-ami-*-amazon-ecs-optimized"]
}
filter {
name = "owner-alias"
values = ["amazon"]
}
}
Funções de manipulação ou transformação dos dados
Console Terraform
/**
* valida baseado no tamanho da string da imagem
* e formato retornando erro especificado
*/
variable "num_instances" {
type = number
description = "Number of instances to be created"
default = 5
}
/**
* valida baseado no tamanho da string da imagem
* e formato retornando erro especificado
*/
variable "image_id" {
type = string
description = "The id of the machine image (AMI) to use for the server."
validation {
condition = length(var.image_id) > 4 && substr(var.image_id, 0, 4) == "ami-"
error_message = "The image_id value must be a valid AMI id, starting with \"ami-\"."
}
}
/**
* cria 5 instancias com a mesma imagem
* formata onome de acordo com o indice
*/
resource "aws_instance" "exemple" {
count = var.num_instances
name = format("teste-%02d",count.index)
ami = var.image_id
}
Funções
#!/bin/bash
$ cd terraform/module
# Arquitetura de pastas sugerida para modulos e aplicações
$ tree
├── LICENSE
├── main.tf
├── outputs.tf
├── README.md
└── variables.tf
Criação de um modulo
/**
* Main.tf
* Gerenciador de remote state - s3 Backend
* Modulo de VPC
*/
provider "aws" {
profile = "educate"
region = var.region
}
terraform {
backend "s3" {
key = "terraform.tfstate"
profile = "educate"
dynamodb_table = "terraform-lock"
region = "us-east-1"
bucket = "backend-educate"
}
}
module "vpc" {
source = "./vpc"
environment = var.environment
project = var.project
azs = 2
}
module "vpc" {
source = "git::git@github.com/felipefrocha/iac-projects.git"
environment = var.environment
project = var.project
azs = 2
}
module "vpc" {
source = "hashicorp/aws/vpc"
environment = var.environment
project = var.project
azs = 2
}
# Module configure EC2 to a database
module "database" {
source = "./modules/database"
# Environment variables
branch = var.branch[local.workspace]
db_pass = local.db_password
sql_port = var.public_database_port
# Deploy Environment
environment = local.workspace
# configure other modules inside
region = var.aws_region
group = var.group[local.workspace]
instance_type = local.db_instance_type
# Aws Parameters to another services
queue_name = local.db_notification_queue
key_name = aws_key_pair.ec2_key_pair.key_name
# Small configs
name = local.db_resources_name
project = var.project
# Can be destroyed
stage = var.stage_stable
# Network setup
vpc_id = module.vpc.vpc_id
private_subnets = module.vpc.private_subnets
cidr_blocks = flatten([module.vpc.private_subnets_cidr_blocks, module.vpc.public_subnets_cidr_blocks])
# Storage setup
bucket_name = local.bkp_bucket_name
}
Terraform CLI
# Limit the version
# Define your backend
terraform {
required_version = ">= 0.12"
backend "local" {
path = "path/to/terraform.tfstate"
}
}
# Define provider
# define profile (AWS CLI)
# sudo apt install awscli
# pip3 install awscli
# aws configure --profile dev
# it will use this credentials
provider "aws" {
profile = "dev"
region = "us-east-1"
}
# AWS free-tier
resource "aws_instance" "example" {
ami = data.aws_ami.amazon_linux_ec2.
instance_type = "t2.micro"
}
#!/bin/bash
# Terraform initialization
# Initialize backend
# Initialize providers
# Initialize modules
terraform init -backend-config='backend.dev.tfvars'
# Terraform validation
# Check syntax
terraform validade
# 'pré lint'
terraform fmt -recursive
# Terraform plan - dry-run
terraform plan -out='terraform.tfplan'
# Applying previous plan
terraform apply "terraform.tfplan"
# Terraform show execution plan & waits confirmation
terraform apply
# Auto approve
terraform apply -auto-approve
Terraform CLI
Planejamento
Grafo de recursos e relações
Visualização de alterações
# Execution Plan
terraform plan -out="directory/pathfile"
# Resource Graph
sudo apt install graphviz -y
sudo snap install inkscape
terrafrom graph | dot -Tsvg > graph.svg
Terraform CLI
Fluxo sugerido por aprendizado
Prós:
Cons:
/**
* Workspace can be used to select profiles
* and help set values of variables
* to promote better configurations to each
* environment
*/
locals {
environment = {
dev = "dev"
hml = "hml"
prd = "prd"
}
profile_select = contains(
keys(local.environment),
terraform.workspace
) ? terraform.workspace : "default"
workspace = local.environment[local.profile_select]
}
/**
* Profiles of aws credentials
*/
provider "aws" {
region = var.aws_region
profile = terraform.workspace
}
/**
* Differentiate resources
*/
resource "aws_cloudwatch_log_group" "teste" {
name = "${var.project}-ecs-${local.workspace}"
}
Workspaces e ambientes
By Felipe Fonseca Rocha
Este breve deck é sobre #Terraform, sumarizando conceitos básicos dessa ferramenta, e apresentando alguns comandos, e fluxo de trabalho proposto/desejado. Foca em apresentar um básico do como trabalhar com módulos e melhores práticas.
Curioso por natureza e grande apreciador de novos conhecimentos, independente da disciplina!