Terraform

uma breve introdução

  1. Introdução IaC
    • Gestão
    • Agilidade
    • Imutabilidade
  2. Ferramenta Terraform
    • Proposito
    • Comandos básicos
    • Componentes
    • Modulos
    • Fluxo de Trabalho
  3. Testes e automação
  4. Conclusão

Introdução

Infraestrutura como código

Gestão

Learning Curve
  1. Definições
    1. Requisites
    2. Factibilidade
  2. Esforço Inicial
    1. Tecnologias
    2. Conhecimento

Title Text

Configuration Management

Evolução

  1. + Configurações
  2. Alterações das configurações
  3. Novos serviços
  4. Remoção de serviços

Manutenção

  1. Config. desconhecida
  2. Interface de alteração
  3. Percepção dos componentes

Agilidade???

Imutabilidade

Terraform

uma, entre tantas ferramentas

Propósito

  • Descrição em auto nível de recursos
  • Referências e dependências

 

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
}

nÃO É..

ferramenta de gerenciamento de configurações:

  • Chef
  • Puppet

providers

Componentes

Estado (state)

Arquivos

  • terraform.tfstate
  • terraform.tfstate.backup

Informações sensíveis

Gerenciado por - Backend:

  • local
  • 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"
    }
  },
  .....
  .....

Componentes

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:

  • Tipos:
    • primitivos:
      • string, number, bool
    • não primitivos:
      • list, map, object etc
  • Descrição
  • Valor padrão
/**
 * 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
}

Componentes

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:

  • valor
  • descrição
  • dependência
  • sensível

 

/**
 * 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,
  ]
}

Componentes

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"]
  }
}

Componentes

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
}

Componentes

Funções

  1. Reuso
  2. Composição
  3. Configuração
  4. Customização
  5. Debug
  6. Teste
  7. Versionamento
  8. Documentação
 
#!/bin/bash 
$ cd terraform/module
# Arquitetura de pastas sugerida para modulos e aplicações
$ tree
├── LICENSE
├── main.tf
├── outputs.tf
├── README.md
└── variables.tf

Modulos

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
}

A Ferramenta

Terraform CLI

Comandos Básicos

Terraform CLI

Comandos Básicos

# 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

Comandos Básicos

 

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 de trabalho

Fluxo sugerido por aprendizado

Prós:

  1. Evita conflito de states
  2. Ajuda na seleção de valores
  3. Suporta config. por ambiente

Cons:

  1. Aumenta a complixidade
  2. Gestão de ambientes por workspaces
  3. Selecionar ambiente e credencial corretos
/** 
 * 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

Felipe F. Rocha

felipefonsecarocha@gmail.com

 

OBRIGADO!!!

Terraform, breve introdução

By Felipe Fonseca Rocha

Terraform, breve introdução

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.

  • 375