Terraform condicoes customizadas - Parte 2

O Terraform possui diversas alternativas para validar variáveis e configurações dentro do seu código. Essas validações ajudam a evitar erros e seguir boas práticas, assegurando que a infraestrutura seja robusta e confiável.

Conforme introduzido na primeira parte dessa publicação. Ao utilizar Terraform para provisionar infraestrutura, é crucial implementar condições customizadas para garantir a integridade e a eficácia da configuração.

Vamos falar nessa publicação sobre condições de validações, a forma mais comum referente a essa configuração é a validação de variáveis ​​no Terraform.

O que são Variáveis de Entrada no Terraform?

Antes de entrar nos exemplos práticos, vamos revisar rapidamente o que são variáveis de entrada. No Terraform, variáveis de entrada permitem que você torne seus módulos mais dinâmicos e reutilizáveis, aceitando diferentes valores conforme necessário.

Por que validar suas variáveis?

Isso é super importante para garantir que as configurações do seu código estejam corretas antes de serem aplicadas, evitando surpresas desagradáveis como:

  • Evitar erros de configuração.
  • Garantir que os valores passados estejam dentro de um intervalo aceitável.
  • Criar guard rails para boas práticas.
  • Melhorar a segurança e robustez dos seus scripts.

Além disso, sem validação, a mensagem de erro exibida ao usuário quando uma variável inválida é passada vem diretamente da API da AWS, que às vezes pode ser difícil de ler e pode não informar exatamente o que esta errado. Com a validação no Terraform, você pode especificar a mensagem de erro.

Exemplo 1: Convenção de nome

Um ótimo exemplo para validação de nomes é garantir que uma variável segue a convenção de nome determinada na empresa, vamos supor que queremos garantir que o padrão seja <environemnt>-<nome>-<digito> como por exemplo dev-ambiente-1 podemos criar uma validação para a variável da seguinte forma:

variable "name" {
  description = "Nome que segue a convenção 'env-palavra-dígito(s)'"
  type        = string

  validation {
    condition     = can(regex("^(dev|hml|prd)-([a-z]+)-([0-9]+)$", var.name))
    error_message = "O nome deve seguir a convenção 'env-palavra-dígito(s)'. Exemplos: 'dev-redis-1', 'prd-smtp-0', 'hml-docker-4'." 
  }
}

Agora, suponha que você forneça um valor inválido para a variável name, como test-algumacoisa-5. O output de erro no terminal ficaria assim:

╷
│ Error: Invalid value for variable
│   on main.tf line 3, in variable "name":
│    3: variable "name" {
│     ├────────────────
│     │ var.name is "test-algumacoisa-5"
│ 
│ O nome deve seguir a convenção 'env-palavra-dígito(s)'. Exemplos: 'dev-redis-1', 'prd-smtp-0', 'hml-docker-4'.
╵

Exemplo 2: Validando quantas instâncias serão criadas

Essa validação pode ser interessante para bloquearmos que seja criadas mais máquinas do que o permitido para determinados ambientes, por exemplo, temos um módulo para criação de EC2, nesse módulo temos uma variável chamada instance_count que o usuário informa quantas máquinas serão criadas.

O intuito é criar um guard rail para que não sejam criadas quantidades desproporcionais ao permitido pela equipe:

variable "instance_count" {
  description = "Número de instâncias"
  type        = number

  validation {
    condition     = var.instance_count >= 1 && var.instance_count <= 3
    error_message = "O número de instâncias deve estar entre 1 e 3."
  }
}

Agora se você estiver desenvolvendo e tentar criar mais do que 3 instâncias será retornado o seguinte erro para o usuário:

╷
│ Error: Invalid value for variable
│   on main.tf line 3, in variable "instance_count":
│    3: variable "instance_count" {
│     ├────────────────
│     │ var.instance_count is 4
│ 
│ O número de instâncias deve estar entre 1 e 3.

Exemplo 3: Padrão de tags

Atualmente na cloud tudo gira em torno de Finops, garantir a consistência e conformidade das tags aplicadas aos recursos é fundamental para uma boa gestão e organização.

Dessa forma vamos montar uma checagem para garantir que os padrões serão utilizados:

variable "tags" {
  description = "Mapeamento das tags a serem aplicadas aos recursos"
  type        = map(string)

  validation {
    condition = (
      contains(keys(var.tags), "cost-center") &&
      contains(["core", "business", "marketing"], var.tags["cost-center"])
    )
    error_message = "As tags obrigatórias são 'cost-center'. que deve possuir os seguintes values 'core', 'business' ou 'marketing'."
  }
}

Dessa forma se eu informar qualquer valor diferente dos que estão sendo esperado teremos o seguinte erro:

│ 
│   Error: Invalid value for variable
│   on main.tf line 3, in variable "tags": {
│     ├────────────────
│     │ var.tags is map of string with 1 element
│     │ var.tags["cost-center"] is "dummy"
│ 
│ As tags obrigatórias são 'cost-center'. que deve possuir os seguintes values 'core', 'business' ou 'marketing'.

Encerramento

Esperamos que este post tenha sido útil e que os exemplos apresentados possam servir de insight para implementação de vocês. No proximo post vamos falar sobre preconditions e postconditions, até a próxima!

Comentários