Como melhorar a flexibilidade usando variáveis, dependências e condicionais em Terraform

Introdução

O Hashicorp Configuration Language (HCL), usado pelo Terraform, oferece muitas estruturas e capacidades úteis que estão presentes em outras linguagens de programação. A utilização de laços em seu código de infraestrutura pode reduzir consideravelmente a duplicação de código e aumentar a legibilidade, permitindo uma refatoração futura mais fácil e uma maior flexibilidade. O HCL também fornece algumas estruturas de dados comuns, como listas e mapas (também chamadas de arrays e dicionários respectivamente em outras linguagens), bem como condicionais para divisão de caminhos de execução.

Uma funcionalidade exclusiva do Terraform é a capacidade de especificar manualmente as dependências dos recursos. Embora o grafo de execução que ele constróa quando executando seu código já contenha as ligações detectadas (que são corretas na maioria dos casos), você pode encontrar-se em situações onde precisa forçar uma relação de dependência que o Terraform não conseguiu detetar.

Neste artigo, vamos revisar as estruturas de dados fornecidas pelo HCL, suas funcionalidades de loop para recursos (a chave count, for_each e for), as condicionais para lidar com valores conhecidos e desconhecidos, e as relações de dependência entre recursos.

Pré-requisitos

  • Uma Chave de Acesso Personalizada da DigitalOceano, que você pode criar via painel de controle da DigitalOceano. Você pode encontrar instruções no documento de produtos da DigitalOceano, Como Criar uma Chave de Acesso Personalizada.
  • Terraform instalado na sua máquina local e um projeto configurado com o provedor DigitalOcean. Complete Passo 1 e Passo 2 do tutorial Como Usar Terraform com DigitalOcean, e certifique-se de nomear a pasta do projeto terraform-flexibility, ao invés de loadbalance. Durante Passo 2, você não precisa incluir variável pvt_key e recurso de chave SSH quando configure o provedor.

Nota: Este tutorial foi testado especificamente com Terraform 1.0.2.

Tipos de Dados em HCL

Antes de você aprender mais sobre laços e outras funcionalidades do HCL que tornam seu código mais flexível, vamos revisitar os tipos de dados disponíveis e suas utilizações.

O idioma de configuração do Hashicorp suporta tipos primitivos e tipos complexos. Os tipos primitivos são strings, números e valores booleans, que são os tipos básicos que não podem ser derivados de outros. As duas classes de valores complexos, por outro lado, agrupam vários valores em um único. Os dois tipos de valores complexos são tipos estruturais e coleções. Os tipos estruturais permitem que valores de diferentes tipos sejam agrupados juntos. O exemplo principal é as definições de recursos que você usa para especificar o que sua infraestrutura terá como aspecto. Em contraste com os tipos estruturais, as coleções também agrupam valores, mas apenas de mesmo tipo. Os três tipos de coleções disponíveis no HCL que nos interessamos são listas, mapas e conjuntos.

Listas

Listas

As listas são semelhantes às arrays em outras linguagens de programação. Eles contém um número conhecido de elementos de mesma função, que pode ser acessado usando notação de índice de arrays ([]) por meio de índices inteiros, começando de 0. Aqui está um exemplo de uma declaração de variável de lista que contém nomes de Dropletos que você irá implantar nas próximas etapas:

variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third"]
}

Para o type, você especificou que é uma lista cujo tipo de elemento é string e então forneceu seu default valor. No HCL, valores enumerados entre parênteses signifiquem uma lista.

Mapas

Mapas são coleções de pares chave-valor, onde cada valor é acessado usando sua chave do tipo string. Existem duas maneiras de especificar mapas dentro de chaves: usando dois pontos (:) ou sinais de igualdade (=) para especificar valores. Em ambas as situações, o valor deve estar entre aspas. Quando usando dois pontos, a chave também deve estar delimitada.

A seguinte definição de mapa contendo nomes de Droplet para diferentes ambientes está escrita usando o sinal de igualdade:

variable "droplet_env_names" {
  type = map(string)

  default = {
    development = "dev-droplet"
    staging = "staging-droplet"
    production = "prod-droplet"
  }
}

Se a chave começar com um número, você deve usar a sintaxe do dois pontos:

variable "droplet_env_names" {
  type = map(string)

  default = {
    "1-development": "dev-droplet"
    "2-staging": "staging-droplet"
    "3-production": "prod-droplet"
  }
}

Conjuntos

Conjuntos não suportam a ordem de elementos, o que significa que a percorrida de conjuntos não garante a mesma ordem cada vez e que seus elementos não podem ser acessados de uma maneira direcionada. Eles contêm elementos únicos repetidos exatamente uma vez, e especificar o mesmo elemento várias vezes resultará em eles sendo coalescidos, com apenas uma instância presente no conjunto.

A declaração the um conjunto é semelhante à declaração de uma lista, a única diferença sendo o tipo da variável:

variable "droplet_names" {
  type    = set(string)
  default = ["first", "second", "third", "fourth"]
}

Agora que você aprendeu sobre os tipos de estruturas de dados que o HCL oferece e revisou a sintaxe de listas, mapas e conjuntos, que usaremos ao longo deste tutorial, você passará para tentar algumas maneiras flexíveis de implantar várias instâncias do mesmo recurso em Terraform.

Configurando o Número de Recursos Usando a Chave count

Nesta seção, você criará múltiplas instâncias do mesmo recurso usando a chave count. A chave count é um parâmetro disponível em todos os recursos que especifica quantas instâncias devem ser criadas.

Você verá como ela funciona escrevendo um recurso de Droplet, que você armazenará em um arquivo chamado droplets.tf no diretório de projeto que você criou como parte dos pré-requisitos. Crie e abra-o para edição executando:

  1. nano droplets.tf

Adicione as seguintes linhas:

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  count  = 3
  image  = "ubuntu-20-04-x64"
  name   = "web"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

Este código define um recurso de Droplet chamado test_droplet, que executa o Ubuntu 20.04 com 1GB de RAM e um núcleo de CPU.

Observe que o valor de count está definido como 3, o que significa que o Terraform tentará criar três instâncias do mesmo recurso. Quando você estiver pronto, salve e feche o arquivo.

Você pode planejar o projeto para ver quais ações o Terraform faria executando:

  1. terraform plan -var "do_token=${DO_PAT}"

A saída será semelhante a isto:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.test_droplet[0] will be created + resource "digitalocean_droplet" "test_droplet" { ... name = "web" ... } # digitalocean_droplet.test_droplet[1] will be created + resource "digitalocean_droplet" "test_droplet" { ... name = "web" ... } # digitalocean_droplet.test_droplet[2] will be created + resource "digitalocean_droplet" "test_droplet" { ... name = "web" ... } Plan: 3 to add, 0 to change, 0 to destroy. ...

O resultado detalhado que o Terraform criará três instâncias de test_droplet, todas com o mesmo nome web. Enquanto é possível, não é preferido, então modifique a definição do Droplet para tornar o nome de cada instância única. Abra droplets.tf para editar:

  1. nano droplets.tf

Modifique a linha destacada:

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  count  = 3
  image  = "ubuntu-20-04-x64"
  name   = "web.${count.index}"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

Salve e fecha o arquivo.

O objeto count provida o parâmetro index, que contém o índice da iteração atual, começando de 0. O índice atual é substituído no nome do Droplet usando interpolação de strings, permitindo-lhe construir uma string dinamicamente adicionando variáveis. Você pode planejar o projeto novamente para ver as alterações:

  1. terraform plan -var "do_token=${DO_PAT}"

A saída será semelhante a esta:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.test_droplet[0] will be created + resource "digitalocean_droplet" "test_droplet" { ... name = "web.0" ... } # digitalocean_droplet.test_droplet[1] will be created + resource "digitalocean_droplet" "test_droplet" { ... name = "web.1" ... } # digitalocean_droplet.test_droplet[2] will be created + resource "digitalocean_droplet" "test_droplet" { ... name = "web.2" ... } Plan: 3 to add, 0 to change, 0 to destroy. ...

Este vez, as três instâncias de test_droplet terão seu índice em seus nomes, fazendo-os mais facilmente rastreáveis.

Você agora sabe como criar múltiplas instâncias de um recurso usando a chave count, bem como obter e usar o índice de uma instância durante a provisão. A seguir, você aprenderá como obter o nome do Droplet de uma lista.

Obter Nomes de Gotas De uma Lista

Na situação em que múltiplos instâncias do mesmo recurso precisam ter nomes personalizados, você pode obter-los dinamicamente de uma variável de lista que definiu. Durante o resto da guia, veremos várias maneiras de automatizar a deploy de gotas de um lista de nomes, promovendo flexibilidade e facilidade de uso.

Você primeiro precisa definir uma lista contendo os nomes das gotas. Abra um arquivo chamado variables.tf e edite-o:

  1. nano variables.tf

Adicione as linhas seguintes:

terraform-flexibility/variables.tf
variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third", "fourth"]
}

Salve e fecha o arquivo. Este código define uma lista chamada droplet_names, que contém os valores string first, second, third, e fourth.

Abra droplets.tf para editar:

  1. nano droplets.tf

Modifique as linhas destacadas:

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  count  = length(var.droplet_names)
  image  = "ubuntu-20-04-x64"
  name   =  var.droplet_names[count.index]
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

Para melhorar a flexibilidade, ao invés de especificar manualmente um número constante de elementos, você passa o tamanho da lista droplet_names para o parâmetro count, o qual sempre retornará o número de elementos na lista. Para o nome, você faz fetch no elemento da lista posicionado no índice count.index, usando notação de arrays. Salve e fecha o arquivo quando terminar.

Tente planejar o projeto novamente. Você receberá saída semelhante a esta:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.test_droplet[0] será criado + resource "digitalocean_droplet" "test_droplet" { ... + name = "first" ... } # digitalocean_droplet.test_droplet[1] será criado + resource "digitalocean_droplet" "test_droplet" { ... + name = "second" ... } # digitalocean_droplet.test_droplet[2] será criado + resource "digitalocean_droplet" "test_droplet" { ... + name = "third" ... } # digitalocean_droplet.test_droplet[3] será criado + resource "digitalocean_droplet" "test_droplet" { ... + name = "fourth" ... Plan: 4 to add, 0 to change, 0 to destroy. ...

Com base nestas modificações, quatro Droplet seriam implantados, nomeados sucessivamente a partir dos elementos da lista droplet_names.

Você aprenderam sobre count, suas características e sintaxe, e juntamente com uma lista, modificaram as instâncias de recursos. Agora você verá os desvantagens dela e como superá-los.

Entendendo os Desvantagens de count

Agora que você sabe como o count é usado, vamos examinar seus desvantagens quando modificamos a lista com a qual ele é usado.

Vamos tentar implantar os Droplet para a nuvem:

  1. terraform apply -var "do_token=${DO_PAT}"

Digite yes quando solicitado. O final de seu output será semelhante a isto:

Output
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Agora vamos criar mais um exemplo de Droplet adicionando ao droplet_names list. Abra variables.tf para edição:

  1. nano variables.tf

Adicione um novo elemento no início da lista:

terraform-flexibility/variables.tf
variable "droplet_names" {
  type    = list(string)
  default = ["zero", "first", "second", "third", "fourth"]
}

Depois que você estiver pronto, salve e feche o arquivo.

Planejar o projeto:

  1. terraform plan -var "do_token=${DO_PAT}"

Você receberá um resultado como este:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create ~ update in-place Terraform will perform the following actions: # digitalocean_droplet.test_droplet[0] será atualizado na próxima etapa ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "first" -> "zero" ... } # digitalocean_droplet.test_droplet[1] será atualizado na próxima etapa ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "second" -> "first" ... } # digitalocean_droplet.test_droplet[2] será atualizado na próxima etapa ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "third" -> "second" ... } # digitalocean_droplet.test_droplet[3] será criado ~ resource "digitalocean_droplet" "test_droplet" { ... ~ name = "fourth" -> "third" ... } # digitalocean_droplet.test_droplet[4] será criado + resource "digitalocean_droplet" "test_droplet" { ... + name = "fourth" ... } Plan: 1 to add, 4 to change, 0 to destroy. ...

A saída mostra que o Terraform renomeará os quatro primeiros Droplet e criará um quinto chamado quarto, porque considera as instâncias como uma lista ordenada e identifica os elementos (Droplet) pelo número de índice na lista. Assim é como o Terraform inicialmente considera as quatro Droplet:

Index Number 0 1 2 3
Droplet Name first second third fourth

Quando o novo Droplet zero é adicionado ao início, sua representação interna parece assim:

Index Number 0 1 2 3 4
Droplet Name zero first second third fourth

Os quatro Droplet iniciais agora são deslizados um lugar para a direita. O Terraform então compara as duas estratégias representadas em tabelas: na posição 0, o Droplet era chamado primeiro, e porque é diferente na segunda tabela, ele planeja uma ação de atualização. Isso continua até a posição 4, que não tem um elemento comparável na primeira tabela, e sim uma ação de provisionamento de Droplet.

O que significa é que adicionar um novo elemento à lista em qualquer lugar além do último resultaria em recursos sendo modificados quando não precisavam ser. Ações de atualização semelhantes seriam planejadas se um elemento da lista droplet_names fosse removido.

A falha principal de usar count para implantar uma quantidade dinâmica de instâncias diferentes do mesmo recurso é o rastreio incorreto de recursos. Para um número constante de instâncias constantes do mesmo recurso, count é uma solução simples que funciona bem. Em situações como esta, porém, quando algum atributo está sendo retirado de uma variável, o loop for_each, que você aprenderá mais tarde no tutorial, é uma escolha muito melhor.

Referência ao Recurso Atual (self)

Outro aspecto negativo de count é que referenciar uma instância arbitrária de um recurso por seu índice não é possível em alguns casos.

O exemplo principal é os provisorios de tempo de destruição, que são executados quando o recurso é planeado para ser destruído. O motivo é que a instância requerida pode não existir (já foi destruída) ou criaria um ciclo de dependências mutuas. Nesses casos, ao invés de referir-se ao objeto através da lista de instâncias, você pode acessar somente o recurso atual através do operador self.

Para demonstrar sua utilização, agora você adicionará um provisor de tempo de destruição local à definição do test_droplet, o que mostrará uma mensagem quando executado. Abra droplets.tf para editar:

  1. nano droplets.tf

Adicione as linhas destacadas abaixo:

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  count  = length(var.droplet_names)
  image  = "ubuntu-20-04-x64"
  name   =  var.droplet_names[count.index]
  region = "fra1"
  size   = "s-1vcpu-1gb"

  provisioner "local-exec" {
    when    = destroy
    command = "echo 'Droplet ${self.name} is being destroyed!'"
  }
}

Salve e fechte o arquivo.

O provisor local-exec executa uma comanda na máquina em que o Terraform está sendo executado. Porque o parâmetro when é setado para destroy, ele irá rodar apenas quando o recurso será destruído. A comanda que roda escreve uma string no stdout, que substitui o nome da instância atual usando self.name.

Porque você criará os Dropletos de maneira diferente na próxima seção, destruir as Dropletas atuais criadas com o seguinte comando:

  1. terraform destroy -var "do_token=${DO_PAT}"

Entre sim quando solicitado. Você receberá a execução do provisor local-exec sendo executada quatro vezes:

Output
... digitalocean_droplet.test_droplet[0] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet first is being destroyed!'"] digitalocean_droplet.test_droplet[1] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet second is being destroyed!'"] digitalocean_droplet.test_droplet[1] (local-exec): Droplet second is being destroyed! digitalocean_droplet.test_droplet[2] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet third is being destroyed!'"] digitalocean_droplet.test_droplet[2] (local-exec): Droplet third is being destroyed! digitalocean_droplet.test_droplet[3] (local-exec): Executing: ["/bin/sh" "-c" "echo 'Droplet fourth is being destroyed!'"] digitalocean_droplet.test_droplet[3] (local-exec): Droplet fourth is being destroyed! digitalocean_droplet.test_droplet[0] (local-exec): Droplet first is being destroyed! ...

Neste passo, você aprendeu as desvantagens do count. Agora você aprenderá sobre o construido de laço for_each, que supera eles e funciona com um maior número de tipos de variáveis.

Looping Usando for_each

Nesta seção, você considerará o laço for_each, sua sintaxe, e como ele ajuda a flexibilidade quando definindo recursos com várias instâncias.

for_each é um parâmetro disponível em cada recurso, mas ao contrário de count, que requer um número de instâncias para criar, for_each aceita um mapa ou um conjunto. Cada elemento da coleção fornecida é percorrida uma vez e é criada uma instância para ele. for_each faz com que a chave e o valor estejam disponíveis sob a palavra-chave each como atributos (a chave e o valor do par como each.key e each.value, respectivamente). Quando um conjunto é fornecido, a chave e o valor serão os mesmos.

Como ele fornece o elemento atual no objeto each, você não precisará acessar manualmente o elemento desejado, como fez com as listas. No caso de conjuntos, isso nem sequer é possível, já que internamente não tem uma ordem observável. As listas também podem ser passadas, mas elas devem primeiro ser convertidas em um conjunto usando a função toset.

O principal benefício de usar for_each, além de ser capaz de enumeração de todos os três tipos de dados de coleção, é que somente os elementos afetados serão modificados, criados ou excluídos. Se você mudar a ordem dos elementos na entrada, nenhuma ação será planejada, e se você adicionar, remover ou modificar um elemento da entrada, ações apropriadas serão planejadas apenas para esse elemento.

Vamos convertê-lo do recurso Droplet de count para for_each e ver como funciona praticamente. Abra droplets.tf para edição executando:

  1. nano droplets.tf

Modifique as linhas destacadas:

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  for_each = toset(var.droplet_names)
  image    = "ubuntu-20-04-x64"
  name     = each.value
  region   = "fra1"
  size     = "s-1vcpu-1gb"
}

Você pode remover o provisionador local-exec. Quando terminar, salve e feche o arquivo.

A primeira linha substitui count e chama for_each, passando a lista droplet_names na forma de um conjunto usando a função toset, que converte automaticamente a entrada fornecida. Para o nome de Droplet, você especifica each.value, que mantém o valor do elemento atual do conjunto de nomes de Droplet.

Planeje o projeto executando:

  1. terraform plan -var "do_token=${DO_PAT}"

A saída detalhará as etapas que o Terraform faria:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.test_droplet["first"] will be created + resource "digitalocean_droplet" "test_droplet" { ... + name = "first" ... } # digitalocean_droplet.test_droplet["fourth"] will be created + resource "digitalocean_droplet" "test_droplet" { ... + name = "fourth" ... } # digitalocean_droplet.test_droplet["second"] will be created + resource "digitalocean_droplet" "test_droplet" { ... + name = "second" ... } # digitalocean_droplet.test_droplet["third"] will be created + resource "digitalocean_droplet" "test_droplet" { ... + name = "third" ... } # digitalocean_droplet.test_droplet["zero"] will be created + resource "digitalocean_droplet" "test_droplet" { ... + name = "zero" ... } Plan: 5 to add, 0 to change, 0 to destroy. ...

Em comparação com o uso de count, o Terraform agora considera cada instância individualmente, e não como elementos de uma lista ordenada. Cada instância está ligada a um elemento do conjunto fornecido, conforme indicado pela string elemento mostrada nas chaves adjacentes a cada recurso que será criado.

Aplique o plano ao cloud executando:

  1. terraform apply -var "do_token=${DO_PAT}"

Digite yes quando solicitado. Quando terminar, você removerá um elemento da lista droplet_names para mostrar que outras instâncias não serão afetadas. Abra variables.tf para edição:

  1. nano variables.tf

Alterar a lista para parecer assim:

terraform-flexibility/variables.tf
variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third", "fourth"]
}

Salvar e fechar o arquivo.

Planejar o projeto novamente, e você receberá a seguinte saída:

Output
... Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: - destroy Terraform will perform the following actions: # digitalocean_droplet.test_droplet["zero"] will be destroyed - resource "digitalocean_droplet" "test_droplet" { ... - name = "zero" -> null ... } Plan: 0 to add, 0 to change, 1 to destroy. ...

Nesta vez, o Terraform destruiria apenas a instância removida (zero) e não tocaria em nenhuma das outras instâncias, o que é o comportamento correto.

Neste passo, você aprendera sobre for_each, como usá-lo e seus benefícios em relação a count. Em seguida, você aprenderá sobre o laço for, sua sintaxe e uso, e quando ele pode ser usado para automatizar certas tarefas.

Usando Laços com for

O laço for funciona com coleções e cria uma nova coleção aplicando uma transformação a cada elemento de entrada. O tipo exato da saída depende se o loop está rodeado de parêntesis ([]) ou chaves ({}), que fornecem uma lista ou um mapa, respectivamente. Como tal, ele é adequado para consultar recursos e formar saídas estruturadas para processamento posterior.

A sintaxe geral do laço for é:

for element in collection:
transform(element)
if condition

Semelhante às outras linguagens de programação, você primeiro nomeia a variável de travessamento (elemento) e especifica o colection para enumerar. O corpo do loop é o passo de transformação, e a clausula opcional if pode ser usada para filtrar a coleção de entrada.

Você agora trabalhará com alguns exemplos usando saídas. Você irá armazenar elas em um arquivo chamado outputs.tf. Crie-lo para edição executando o seguinte comando:

  1. nano outputs.tf

Adicione as linhas seguintes ao arquivo para salvar os parâmetros de pares de nomes das instâncias Droplet implantadas e suas adressas IP:

terraform-flexibility/outputs.tf
output "ip_addresses" {
  value = {
    for instance in digitalocean_droplet.test_droplet:
    instance.name => instance.ipv4_address
  }
}

Este código especifica uma saída chamada ip_addresses, e especifica um loop que percorre sobre as instâncias da resource_test_droplet que você personalizou nas etapas anteriores. Porque o loop está encerrado entre parênteses, sua saída será um mapa. A etapa de transformação para mapas é semelhante a funções lambda em outras linguagens de programação, e aqui ela cria um par chave-valor combinando o nome da instância como chave com sua IP privada como valor.

Salve e fechar o arquivo, então atualize o estado do Terraform para contar com a saída nova de infraestrutura executando:

  1. terraform refresh -var "do_token=${DO_PAT}"

O comando refresh do Terraform atualiza o estado local com o estado real da infraestrutura no cloud.

Então, verifique os conteudos dos outputs:

Output
ip_addresses
= { "first" = "ip_address" "fourth" = "ip_address" "second" = "ip_address" "third" = "ip_address" }

O Terraform mostrou o conteúdo da saída ip_addresses, que é um mapa construído pela loop for. A ordem das entradas pode ser diferente para você. O loop funciona sem interrupções para cada número de entradas—seu novo Droplet será criado sem mais entrada manual e também aparecerá automaticamente na saída desta vez.

Envolvendo o loop em parênteses quadrados, você pode fazer com que a saída seja uma lista. Por exemplo, você pode exibir apenas os endereços IP do Droplet, o que é útil para software externo que possa parsear dados. O código teria um aspecto assim:

terraform-flexibility/outputs.tf
output "ip_addresses" {
  value = [
    for instance in digitalocean_droplet.test_droplet:
    instance.ipv4_address
  ]
}

Aqui, a etapa de transformação selecionou o atributo IP do Droplet. Ele iria dar a seguinte saída:

Output
ip_addresses
= [ "ip_address", "ip_address", "ip_address", "ip_address", ]

Como foi notado antes, você pode filtrar a coleção de entrada usando o cláusula if. Aqui é como você escreveria o loop para filtrar por a região fra1:

terraform-flexibility/outputs.tf
output "ip_addresses" {
  value = [
    for instance in digitalocean_droplet.test_droplet:
    instance.ipv4_address
    if instance.region == "fra1"
  ]
}

No HCL, o operador == verifica a igualdade dos valores das duas partes—aqui ele checa se instance.region é igual a fra1. Se sim, a condição passa e o instance é transformado e adicionado à saída, caso contrário, é ignorado. A saída deste código seria a mesma que a anterior, porque todos os instâncias de Droplet estão na região fra1, conforme definição do recurso test_droplet. A condição if também é útil quando você deseja filtrar a coleção de entrada por outros valores no seu projeto, como tamanho do Droplet ou distribuição.

Porque você irá criar recursos de maneira diferente na seção seguinte, destrua as atuais implantadas executando o comando seguinte:

  1. terraform destroy -var "do_token=${DO_PAT}"

Entre sim quando solicitado para concluir o processo.

Vamos ao loop for, sua sintaxe e exemplos de uso em saídas. Agora você aprenderá sobre condicionais e como eles podem ser usados juntamente com count.

Diretivas e Condicionais

Em uma das seções anteriores, você viu a chave count e como funciona. Você agora vai aprender sobre operadores condicionais ternários, que pode ser usado em outras partes do seu código Terraform e como ele pode ser usado com count.

A sintaxe do operador ternário é:

condition ? value_if_true : value_if_false

condição é uma expressão que computa para um booleano (verdadeiro ou falso). Se a condição for verdadeira, então a expressão valida value_if_true. No entanto, se a condição for falsa, o resultado será value_if_false.

O principal uso dos operadores ternários é permitir ou desabilitar a criação de único recurso de acordo com o conteúdo de uma variável. Isso pode ser feito passando o resultado da comparação (seja 1 ou 0) para a chave count no desejado recurso.

Na função de fazer uso do operador ternário para obter um único elemento de uma lista ou conjunto, você pode usar a função one. Se a coleção for vazia, ela retorna null. Caso contrário, ela retorna o único elemento na coleção, ou lança um erro se houver múltiplos.

Abra o arquivo variables.tf para editar:

  1. nano variables.tf

Adicione as linhas destacadas:

terraform-flexibility/variables.tf
variable "droplet_names" {
  type    = list(string)
  default = ["first", "second", "third", "fourth"]
}

variable "create_droplet" {
  type = bool
  default = true
}

Este código define a variável create_droplet, que controlará se um Droplet será criado. Primeiro, abra o arquivo droplets.tf para editar:

Adicione as linhas destacadas:

  1. nano droplets.tf

Este código define a variável create_droplet de tipo bool. Salve e fecha o arquivo.

terraform-flexibility/droplets.tf
resource "digitalocean_droplet" "test_droplet" {
  count  = var.create_droplet ? 1 : 0
  image  = "ubuntu-20-04-x64"
  name   =  "test_droplet"
  region = "fra1"
  size   = "s-1vcpu-1gb"
}

Então, para modificar a declaração do Droplet, abra o arquivo droplets.tf para editar executando:

Modifique seu arquivo como o seguinte:

  1. terraform plan -var "do_token=${DO_PAT}" -var "create_droplet=false"

Para count, você usa um operador ternário para retornar 1 se a variável create_droplet estiver definida em valor de true, ou 0 se falso, o que resultará em nenhum Droplet sendo provisionado. Salve e fecha o arquivo quando terminar.

Output
Changes to Outputs: + ip_addresses = {} You can apply this plan to save these new output values to the Terraform state, without changing any real infrastructure.

Planeje a execução do plano de projeto com a variável definida para false executando:Você receberá o seguinte output:Como create_droplet foi passada com o valor de false, a quantidade de instâncias é 0, e não haverá Droplets criados, portanto, não haverá endereços IP para sair.

Você já viu como usar o operador condicional ternário junto com a chave count para habilitar uma flexibilidade maior na escolha de quais recursos desejar deployar. A seguir, você aprenderá sobre explicitamente definir dependências de recursos para suas instâncias.

Definindo Dependências Explícitas de Recursos

Enquanto criando o plano de execução do seu projeto, o Terraform deteta as cadeias de dependências entre recursos e implícito ordena-los de maneira que serem construídos em ordem apropriada. Na maioria dos casos, é capaz de detectar relacionamentos por meio de todas as expressões em recursos e construir um gráfico.

No entanto, quando um recurso precisa de configurações de controle de acesso já implantadas no provedor de nuvem para ser provisionado, não há sinal clara para o Terraform que eles sejam relacionados behavioralmente. Em vez disso, o Terraform não saberá que eles dependem de outros comportamentos. Nesses casos, a dependência deve ser especificada manualmente usando o argumento depends_on.

O campo depends_on está disponível em cada recurso e é usado para especificar os link de dependências ocultos entre recursos específicos. As dependências ocultas ocorrem quando um recurso depende de outro sem usar nenhuma das suas informações declarativas, o que faria o Terraform conectá-los de uma maneira diferente.

Aqui é um exemplo de como depends_on é especificado em código:

resource "digitalocean_droplet" "droplet" {
  image  = "ubuntu-20-04-x64"
  name   = "web"
  region = "fra1"
  size   = "s-1vcpu-1gb"

  depends_on = [
    # Recursos...
  ]
}

Aceita uma lista de referências para outros recursos, e não aceita expressões arbitrárias.

depends_on deve ser usado raramente, e apenas quando todas as outras opções estiverem esgotadas. Sua utilização indica que o que você está tentando declarar está passando fora dos limites do sistema de detecção automática de dependências do Terraform; pode significar que o recurso está explicitamente dependendo de mais recursos do que precisa.

Você já conheceu as funcionalidades do HCL que melhoram a flexibilidade e escalabilidade do seu código, como o count para especificar o número de instâncias de recursos a serem implantados e o for_each como um método avançado de loopar sobre tipos de dados colecionais e personalizar instâncias. Quando usado corretamente, eles reduzem significativamente a duplicação de código e a sobrecarga operacional de gerenciar a infraestrutura implantada.

Conclusão

Neste artigo, você veio entender as características do HCL que melhoram a flexibilidade e escalabilidade do seu código, como o count para especificar o número de instâncias de recursos a serem implantados e o for_each como um método avançado de loopar sobre tipos de dados colecionais e personalizar instâncias. Quando usado corretamente, eles reduzem significativamente a duplicação de código e a sobrecarga operacional de gerenciar a infraestrutura implantada.

Este tutorial faz parte da série Como Gerenciar Infraestrutura com Terraform. A série abrange vários tópicos do Terraform, desde a instalação do Terraform pela primeira vez até o gerenciamento de projetos complexos.

Source:
https://www.digitalocean.com/community/tutorials/how-to-improve-flexibility-using-terraform-variables-dependencies-and-conditionals