DEV Community

Marcelo Andrade
Marcelo Andrade

Posted on

Terraform refactoring

Estava trabalhando com Terraform gerando dados para testes da nova versão do Netbox.

Em vez de destruir todos os dados para reexecutar o código Terraform final , aproveitei para fazer uma demonstração de Terraform Refactoring com as diretivas moved.

Contexto

Originalmente, dois recursos netbox_region eram criados:

# Região de São Paulo
resource "netbox_region" "sp" {
  name = "São Paulo"
  description = "Estado de São Paulo"
  slug = "sp"
}

# Região de Brasília
resource "netbox_region" "df" {
  name = "Distrito Federal"
  description = "Distrito Federal"
  slug = "df"
}
Enter fullscreen mode Exit fullscreen mode

O resultado da aplicação do código acima pode ser inspecionado no arquivo de estados do Terraform:

$ terraform state list | fgrep netbox_region
netbox_region.df
netbox_region.sp
Enter fullscreen mode Exit fullscreen mode

Refactor para variáveis e objetos

Não é algo que agrada a todo mundo, mas você pode parametrizar as configurações usando objetos complexos.

Regra geral, eu não recomendo complexidade excessiva na definição de variáveis porque deixa tudo confuso e difícil para ler ou adaptar posteriormente. Mas vamos lá:

Podemos deixar o arquivo variables.tf:

variable "regions" {
    type = map(
        object({
            name = string
            description = string
            slug = string
    }))
    description = "Estados do Brasil"

    default = {
        sp = {
          name = "São Paulo"
          description = "Estado de São Paulo"
          slug = "sp"
        },
        df = {
            name = "Distrito Federal"
            description = "Distrito Federal"
            slug = "df"
        }    
    }
}
Enter fullscreen mode Exit fullscreen mode

Tudo isso me permite substituir os dois recursos netbox_region por um único bloco usando for_each.

(Vou ser honesto, não me parece uma troca muito inteligente!)

resource "netbox_region" "estado" {
  for_each = var.regions
  name = each.value.name
  description = each.value.description
  slug = each.value.slug
}
Enter fullscreen mode Exit fullscreen mode

Obviamente, se você fizer apenas isso, vai literalmente destruir ambos os recursos originais para criar dois novos. Dependendo do tipo de API com que o Terraform irá lidar, pode ser que nem seja possível fazer as alterações.

Neste código, o que acontece é isso aqui:

$ terraform plan | egrep 'Plan|# netbox'
  # netbox_region.df will be destroyed
  # netbox_region.estado["df"] will be created
  # netbox_region.estado["sp"] will be created
  # netbox_region.sp will be destroyed
  # netbox_site.df will be updated in-place
  # netbox_site.df_spc_1 will be updated in-place
  # netbox_site.sp will be updated in-place
  # netbox_site.sp_spc_1 will be updated in-place
Plan: 2 to add, 4 to change, 2 to destroy.
Enter fullscreen mode Exit fullscreen mode

Mas eu não estou modificando nada, não estou recriando nada, eu quero apenas substituir uma maneira de criar os recursos por outra.

Tradicionalmente, seria necessário fazer mudanças com o comando terraform state mv

$ terraform state mv netbox_region.sp 'netbox_region.estado["sp"]'
Move "netbox_region.sp" to "netbox_region.estado[\"sp\"]" 
Successfully moved 1 object(s).
$ terraform state mv netbox_region.df 'netbox_region.estado["df"]'
Move "netbox_region.df" to "netbox_region.estado[\"df\"]" 
Successfully moved 1 object(s).

$ terraform plan 
...
No changes. Your infrastructure matches the configuration.
Enter fullscreen mode Exit fullscreen mode

Desde a versão 1.1, entretanto, temos uma outra maneira mais legal que executar dezenas de comandos: os blocos moved.

Os blocos abaixo substituem os comandos terraform state mv:

moved {
  from = netbox_region.df
  to = netbox_region.estado["df"]
}

moved {
  from = netbox_region.sp
  to = netbox_region.estado["sp"]
}
Enter fullscreen mode Exit fullscreen mode

Com este bloco, o Terraform modifica o state e não faz operações na API remota:

$ terraform plan -out plan
...
Terraform will perform the following actions:

  # netbox_region.df has moved to netbox_region.estado["df"]
    resource "netbox_region" "estado" {
        id               = "1"
        name             = "Distrito Federal"
        # (3 unchanged attributes hidden)
    }

  # netbox_region.sp has moved to netbox_region.estado["sp"]
    resource "netbox_region" "estado" {
        id               = "2"
        name             = "Estao de São Paulo"
        # (3 unchanged attributes hidden)
    }

Plan: 0 to add, 0 to change, 0 to destroy.

$ terraform apply plan

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

$ terraform state list | fgrep netbox_region
netbox_region.estado["df"]
netbox_region.estado["sp"]
Enter fullscreen mode Exit fullscreen mode

Você pode se perguntar: mas para que eu substituiria dois comandos simples por um monte de blocos poluindo meu código Terraform?

Bem, primeiramente, trabalhar com blocos de configurações declarativas não é apenas temático, mas certamente uma alternativa muito melhor que um conjunto isolados de comandos imperativos.

Vários comandos imperativos quebram a "atomicidade" das operações, além de ser bem incômodo para execução de rollbacks.

(Nem sempre será possível contemplar 100% dos casos com blocos move, mas é sempre melhor ter uma alternativa declarativa a não ter nenhuma!)

Além disso, se você codifica um módulo que é usado por dezenas de instâncias de códigos diferentes, você precisa executar os comandos de terraform state mv em cada uma das instâncias.

Na maioria dos ambientes, além deste tipo de prática não ser bem vista, normalmente é particularmente difícil de conseguir os acessos a todos os arquivos.

Ao operar "dentro" de um módulo, agora você consegue abstrair boa parte das dores de cabeça que os usuários dos seus módulos podem ter.

Se você usa Terraform e pretende continuar usando no futuro, é bem provável que precise destes recursos nos seus códigos!


A página que fez o anúncio dos blocos moved é esta aqui e conta com vários outros exemplos em que as técnicas podem ajudar.

Também há um tutorial que pode ser seguido aqui para ajudar a praticar esta técnica.

Top comments (1)

Collapse
 
breno_santana profile image
Breno Santana

Obrigado pelo material excelente e pelas indicações da documentação oficial.