Terraform vs. CloudFormation: o guia definitivo

Publicados: 2022-03-11

Se, como eu, você vasculhou a Internet para ajudá-lo a escolher entre CloudFormation e Terraform como sua próxima ferramenta de infraestrutura como código (IaC) sem encontrar uma resposta definitiva, compartilhei sua dor por um longo tempo. Agora, tenho uma experiência significativa com ambas as ferramentas e posso tomar uma decisão informada sobre qual usar.

TL;DR

Para seu projeto IaC na AWS, escolha CloudFormation, porque:

  1. O CloudFormation faz uma distinção entre código (ou seja, modelos) e instanciações do código (ou seja, pilhas). No Terraform, não existe essa distinção. Mais sobre isso na próxima seção.
  2. O Terraform não lida muito bem com o gerenciamento básico de dependências. Mais sobre isso em uma seção posterior.

Diferenciando entre código e instanciações

Uma diferença entre o CloudFormation e o Terraform é como o código e as instanciações se relacionam entre si em cada serviço.

O CloudFormation tem o conceito de stack , que é a instanciação de um template. O mesmo modelo pode ser instanciado ad infinitum por um determinado cliente em uma determinada conta, entre contas ou por clientes diferentes.

O Terraform não tem esse conceito e requer um relacionamento um-para-um entre o código e sua instanciação. Seria semelhante a duplicar o código-fonte de um servidor web para cada servidor que você deseja executar ou duplicar o código sempre que precisar executar um aplicativo em vez de executar a versão compilada.

Esse ponto é bastante trivial no caso de uma configuração simples, mas rapidamente se torna um grande problema para operações de médio a grande porte. No Terraform, toda vez que você precisar criar uma nova pilha a partir de um código existente, será necessário duplicar o código. E copiar/colar arquivos de script é uma maneira muito fácil de se sabotar e corromper recursos que você não pretendia tocar.

Na verdade, o Terraform não tem um conceito de pilhas como o CloudFormation, o que mostra claramente que o Terraform foi criado desde o início para ter uma correspondência direta entre o código e os recursos que ele gerencia. Isso foi posteriormente corrigido parcialmente pelo conceito de ambientes (que desde então foram renomeados como “espaços de trabalho”), mas a maneira de usá-los torna incrivelmente fácil a implantação em um ambiente indesejado. Isso ocorre porque você precisa executar terraform workspace select antes da implantação, e esquecer essa etapa implantará no espaço de trabalho selecionado anteriormente, que pode ou não ser o desejado.

Na prática, é verdade que esse problema é mitigado pelo uso de módulos Terraform, mas mesmo no melhor dos casos, você exigiria uma quantidade significativa de código clichê. Na verdade, esse problema era tão grave que as pessoas precisavam criar uma ferramenta wrapper em torno do Terraform para resolver esse problema: o Terragrunt.

Gerenciamento de estado e permissões

Outra diferença importante entre o CloudFormation e o Terraform é como cada um deles gerencia o estado e as permissões.

O CloudFormation gerencia os estados da pilha para você e não oferece nenhuma opção. Mas os estados de pilha do CloudFormation foram sólidos em minha experiência. Além disso, o CloudFormation permite que usuários menos privilegiados gerenciem pilhas sem ter todas as permissões necessárias exigidas pela própria pilha. Isso ocorre porque o CloudFormation pode obter as permissões de uma função de serviço anexada à pilha em vez das permissões do usuário que executa a operação de pilha.

O Terraform exige que você forneça alguns back-ends para gerenciar estados. O padrão é um arquivo local, o que é totalmente insatisfatório, dado:

  1. A robustez do seu arquivo de estado está inteiramente ligada à robustez da máquina em que está armazenado.
  2. Isso praticamente impossibilita o trabalho em equipe.

Portanto, você precisa de um estado robusto e compartilhado, que na AWS geralmente é obtido usando um bucket do S3 para armazenar o arquivo de estado, acompanhado de uma tabela do DynamoDB para lidar com a simultaneidade.

Isso significa que você precisa criar um bucket do S3 e uma tabela do DynamoDB manualmente para cada pilha que deseja instanciar e também gerenciar as permissões manualmente para esses dois objetos para restringir o acesso de usuários menos privilegiados a dados aos quais não deveriam ter acesso. Se você tiver apenas algumas pilhas, isso não será um grande problema, mas se você tiver 20 pilhas para gerenciar, isso se tornará muito complicado.

A propósito, ao usar os workspaces do Terraform, não é possível ter uma tabela do DynamoDB por workspace. Isso significa que, se você quiser que um usuário do IAM com permissões mínimas para realizar implantações, esse usuário poderá mexer nos bloqueios de todos os espaços de trabalho porque as permissões do DynamoDB não são refinadas para o nível do item.

Gerenciamento de Dependências

Nesse ponto, tanto o CloudFormation quanto o Terraform podem ser um pouco complicados. Se você alterar o ID lógico (ou seja, o nome) de um recurso, ambos considerarão que o recurso antigo deve ser destruído e um novo deve ser criado. Portanto, geralmente é uma má ideia alterar o ID lógico dos recursos em qualquer ferramenta, especialmente para pilhas aninhadas no CloudFormation.

Conforme mencionado na primeira seção, o Terraform não lida com dependências básicas. Infelizmente, os desenvolvedores do Terraform não estão dando muita atenção ao problema de longa data, apesar da aparente falta de soluções alternativas.

Dado que o gerenciamento adequado de dependências é absolutamente crítico para uma ferramenta de IaC, esses problemas no Terraform colocam sua adequação em questão assim que as operações críticas de negócios estão envolvidas, como a implantação em um ambiente de produção. O CloudFormation dá uma sensação muito mais profissional, e a AWS está sempre muito atenta para garantir que oferece ferramentas de nível de produção para seus clientes. Em todos os anos que tenho usado o CloudFormation, nunca encontrei um problema com o gerenciamento de dependências.

O CloudFormation permite que uma pilha exporte algumas de suas variáveis ​​de saída, que podem ser reutilizadas por outras pilhas. Para ser honesto, essa funcionalidade é limitada, pois você não poderá instanciar mais de uma pilha por região. Isso ocorre porque você não pode exportar duas variáveis ​​com o mesmo nome e as variáveis ​​exportadas não têm namespaces.

O Terraform não oferece essas facilidades, então você fica com opções menos desejáveis. O Terraform permite importar o estado de outra pilha, mas dá acesso a todas as informações dessa pilha, incluindo os muitos segredos armazenados no estado. Alternativamente, uma pilha pode exportar algumas variáveis ​​na forma de um arquivo JSON armazenado em um bucket do S3, mas, novamente, essa opção é mais complicada: você precisa decidir qual bucket do S3 usar e fornecer as permissões apropriadas e gravar todos os código de canalização você mesmo em ambos os lados do escritor e do leitor.

Uma vantagem do Terraform é que ele possui fontes de dados. O Terraform pode, assim, consultar recursos não gerenciados pelo Terraform. No entanto, na prática, isso tem pouca relevância quando você deseja escrever um modelo genérico, porque você não assumirá nada sobre a conta de destino. O equivalente no CloudFormation é adicionar mais parâmetros de modelo, o que envolve repetição e potencial de erros; no entanto, na minha experiência, isso nunca foi um problema.

Voltando ao problema do gerenciamento de dependências do Terraform, outro exemplo é que você recebe um erro ao tentar atualizar as configurações de um balanceador de carga e obtém o seguinte:

 Error: Error deleting Target Group: ResourceInUse: Target group 'arn:aws:elasticloadbalancing:us-east-1:723207552760:targetgroup/strategy-api-default-us-east-1/14a4277881e84797' is currently in use by a listener or a rule status code: 400, request id: 833d8475-f702-4e01-aa3a-d6fa0a141905

O comportamento esperado seria que o Terraform detectasse que o grupo-alvo é uma dependência de algum outro recurso que não está sendo excluído e, consequentemente, não deveria tentar excluí-lo, mas também não deveria gerar um erro.

Operações

Embora o Terraform seja uma ferramenta de linha de comando, é muito claro que ele espera que um humano o execute, pois é muito interativo. É possível executá-lo em modo batch (ou seja, a partir de um script), mas isso requer alguns argumentos adicionais de linha de comando. O fato de o Terraform ter sido desenvolvido para ser executado por humanos por padrão é bastante intrigante, já que o objetivo de uma ferramenta de IaC é a automação.

O Terraform é difícil de depurar. As mensagens de erro geralmente são muito básicas e não permitem que você entenda o que está acontecendo de errado, nesse caso você terá que executar o Terraform com TF_LOG=debug , que produz uma enorme quantidade de saída para vasculhar. Para complicar isso, o Terraform às vezes faz chamadas de API para a AWS que falham, mas a falha não é um problema com o Terraform. Por outro lado, o CloudFormation fornece mensagens de erro razoavelmente claras com detalhes suficientes para permitir que você entenda onde está o problema.

Um exemplo de mensagem de erro do Terraform:

 Error: error reading S3 bucket Public Access Block: NoSuchBucket: The specified bucket does not exist status code: 404, request id: 19AAE641F0B4AC7F, host id: rZkgloKqxP2/a2F6BYrrkcJthba/FQM/DaZnj8EQq/5FactUctdREq8L3Xb6DgJmyKcpImipv4s=

A mensagem de erro acima mostra uma mensagem de erro clara que na verdade não reflete o problema subjacente (que neste caso foi um problema de permissões).

Essa mensagem de erro também mostra como o Terraform às vezes pode se pintar em um canto. Por exemplo, se você criar um bucket do S3 e um recurso aws_s3_bucket_public_access_block nesse bucket e se, por algum motivo, fizer algumas alterações no código do Terraform que destrói esse bucket, por exemplo, na pegadinha “change implica delete and create” descrita acima— O Terraform ficará preso tentando carregar o aws_s3_bucket_public_access_block mas falhando continuamente com o erro acima. O comportamento correto do Terraform seria substituir ou excluir o aws_s3_bucket_public_access_block conforme apropriado.

Por fim, você não pode usar os scripts auxiliares do CloudFormation com o Terraform. Isso pode ser um aborrecimento, especialmente se você pretende usar cfn-signal, que informa ao CloudFormation que uma instância do EC2 terminou de se inicializar e está pronta para atender a solicitações.

Sintaxe, comunidade e reversão

Em termos de sintaxe, o Terraform tem uma boa vantagem em comparação com o CloudFormation - ele suporta loops. Mas na minha própria experiência, esse recurso pode se tornar um pouco perigoso. Normalmente, um loop seria usado para criar vários recursos idênticos; no entanto, quando você deseja atualizar a pilha com uma contagem diferente, pode haver uma chance de você precisar vincular os recursos antigos e novos (por exemplo, usando zipmap() para combinar valores de dois arrays que agora são de tamanhos diferentes porque um array tem o tamanho do tamanho do loop antigo e o outro tem o tamanho do tamanho do novo loop). É verdade que tal problema pode acontecer sem loops, mas sem loops, o problema seria muito mais evidente para quem escreve o roteiro. O uso de loops nesse caso ofusca o problema.

Se a sintaxe do Terraform ou a sintaxe do CloudFormation é melhor é principalmente uma questão de preferências. O CloudFormation inicialmente suportava apenas JSON, mas os modelos JSON são muito difíceis de ler. Felizmente, o CloudFormation também suporta YAML, que é muito mais fácil de ler e permite comentários. A sintaxe do CloudFormation tende a ser bastante detalhada, no entanto.

A sintaxe do Terraform usa HCL, que é um tipo de derivado JSON e é bastante idiossincrático. O Terraform oferece mais funções do que o CloudFormation e geralmente são mais fáceis de entender. Portanto, pode-se argumentar que o Terraform tem uma pequena vantagem nesse ponto.

Outra vantagem do Terraform é seu conjunto prontamente disponível de módulos mantidos pela comunidade, e isso simplifica a criação de modelos. Um problema pode ser que esses módulos podem não ser seguros o suficiente para cumprir os requisitos de uma organização. Portanto, para organizações que exigem um alto nível de segurança, pode ser necessário revisar esses módulos (bem como outras versões à medida que vierem).

De um modo geral, os módulos do Terraform são muito mais flexíveis do que as pilhas aninhadas do CloudFormation. Uma pilha aninhada do CloudFormation tende a ocultar tudo abaixo dela. A partir da pilha aninhada, uma operação de atualização mostraria que a pilha aninhada será atualizada, mas não mostra em detalhes o que acontecerá dentro da pilha aninhada.

Um ponto final, que na verdade pode ser controverso, é que o CloudFormation tenta reverter implantações com falha. Esse é um recurso bastante interessante, mas infelizmente pode ser muito longo (por exemplo, pode levar até três horas para o CloudFormation decidir que uma implantação no Elastic Container Service falhou). Por outro lado, em caso de falha, o Terraform simplesmente para onde quer que esteja. Se um recurso de reversão é uma coisa boa ou não é discutível, mas passei a apreciar o fato de que uma pilha é mantida em um estado de funcionamento o máximo possível quando uma espera mais longa é uma compensação aceitável.

Em defesa do Terraform vs. CloudFormation

O Terraform tem vantagens sobre o CloudFormation. O mais importante, na minha opinião, é que ao aplicar uma atualização, o Terraform mostra todas as alterações que você está prestes a fazer, incluindo detalhamento de todos os módulos que está usando. Por outro lado, o CloudFormation, ao usar pilhas aninhadas, mostra apenas que a pilha aninhada precisa ser atualizada, mas não fornece uma maneira de detalhar os detalhes. Isso pode ser frustrante, pois esse tipo de informação é muito importante saber antes de clicar no botão “ir”.

Tanto o CloudFormation quanto o Terraform oferecem suporte a extensões. No CloudFormation, é possível gerenciar os chamados “recursos personalizados” usando uma função do AWS Lambda de sua própria criação como back-end. Para o Terraform, as extensões são muito mais fáceis de escrever e fazem parte do código. Portanto, há uma vantagem para o Terraform nesse caso.

O Terraform pode lidar com muitos fornecedores de nuvem. Isso coloca o Terraform em uma posição de poder unificar uma determinada implantação entre várias plataformas de nuvem. Por exemplo, digamos que você tenha uma única carga de trabalho distribuída entre a AWS e o Google Cloud Platform (GCP). Normalmente, a parte AWS da carga de trabalho seria implantada usando o CloudFormation e a parte do GCP usando o Cloud Deployment Manager do GCP. Com o Terraform, você pode usar um único script para implantar e gerenciar ambas as pilhas em suas respectivas plataformas de nuvem. Dessa forma, você só precisa implantar uma pilha em vez de duas.

Não argumentos para Terraform vs. CloudFormation

Existem alguns não-argumentos que continuam circulando pela internet. A maior delas é que, como o Terraform é multinuvem, você pode usar uma ferramenta para implantar todos os seus projetos, não importa em qual plataforma de nuvem eles sejam feitos. Tecnicamente, isso é verdade, mas não é a grande vantagem que pode parecer, especialmente ao gerenciar projetos típicos de nuvem única. A realidade é que há uma correspondência quase um para um entre os recursos declarados (por exemplo) no CloudFormation e os mesmos recursos declarados em um script do Terraform. Como você precisa conhecer os detalhes dos recursos específicos da nuvem de qualquer maneira, a diferença se resume à sintaxe, que dificilmente é o maior ponto problemático no gerenciamento de implantações.

Alguns argumentam que, usando o Terraform, pode-se evitar o aprisionamento do fornecedor. Este argumento não é válido no sentido de que, ao usar o Terraform, você fica bloqueado pela HashiCorp (o criador do Terraform), da mesma forma que, usando o CloudFormation, você fica bloqueado pela AWS e assim por diante para outras nuvens. plataformas.

O fato de os módulos do Terraform serem mais fáceis de usar é para mim de menor importância. Em primeiro lugar, acredito que a AWS deseja deliberadamente evitar hospedar um único repositório para modelos do CloudFormation baseados na comunidade devido à responsabilidade percebida por falhas de segurança feitas pelo usuário e violações de vários programas de conformidade.

Em um nível mais pessoal, entendo perfeitamente os benefícios do uso de bibliotecas no caso de desenvolvimento de software, pois essas bibliotecas podem ser executadas facilmente em dezenas de milhares de linhas de código. No caso do IaC, no entanto, o tamanho do código geralmente é muito menor, e esses módulos geralmente têm algumas dezenas de linhas. Usar copiar/colar na verdade não é uma ideia tão ruim no sentido de que evita problemas para manter a compatibilidade e delegar sua segurança a pessoas desconhecidas.

Usar copiar/colar é desaprovado por muitos desenvolvedores e engenheiros de DevOps, e há boas razões por trás disso. No entanto, meu ponto de vista é que usar copiar/colar para trechos de código permite que você o adapte facilmente às suas necessidades, e não há necessidade de criar uma biblioteca e gastar muito tempo para torná-lo genérico. A dor de manter esses trechos de código geralmente é muito baixa, a menos que seu código seja duplicado em, digamos, uma dúzia ou mais de modelos. Nesse caso, apropriar o código e usá-lo como pilhas aninhadas faz sentido, e os benefícios de não se repetir provavelmente são maiores do que o incômodo de não poder ver o que vai ser atualizado dentro da pilha aninhada quando você faz uma atualização Operação.

Conclusão do CloudFormation vs. Terraform

Com o CloudFormation, a AWS deseja fornecer a seus clientes uma ferramenta sólida que funcionará conforme o esperado em todos os momentos. A equipe da Terraform também, é claro, mas parece que um aspecto crucial de suas ferramentas, o gerenciamento de dependências, infelizmente não é uma prioridade.

O Terraform pode ter um lugar em seu projeto, especialmente se você tiver uma arquitetura multinuvem; nesse caso, os scripts do Terraform são uma maneira de unificar o gerenciamento de recursos entre os vários fornecedores de nuvem que você está usando. Mas você ainda pode evitar as desvantagens do Terraform nesse caso usando apenas o Terraform para gerenciar pilhas já implementadas usando suas respectivas ferramentas IaC específicas da nuvem.

A sensação geral do Terraform vs. CloudFormation é que o CloudFormation, embora imperfeito, é mais profissional e confiável, e eu definitivamente o recomendaria para qualquer projeto que não seja especificamente multinuvem.