SSD Nodes Learn Hosting plans →
Guias Matt ConnorPor Matt Connor · Atualizado 2026-08-24

Ansible ou Terraform: qual você precisa?

Terraform cria o VPS e Ansible configura o sistema. Entenda a divisão, por que provisioners falham, os comandos de handoff e quando usar só Ansible.

Ansible vs Terraform numa frase

Ansible vs Terraform não é uma escolha entre duas ferramentas que fazem o mesmo trabalho. O Terraform declara que infraestrutura existe: servidores, discos, redes e registos DNS. O Ansible declara o que deve ser verdadeiro dentro de uma máquina que já existe: pacotes, utilizadores, ficheiros de configuração e serviços em execução. O Terraform cria o VPS. O Ansible transforma esse VPS num servidor Web.

Ambos são declarativos e ambos são considerados infraestrutura como código (IaC). A diferença real está naquilo que cada um guarda. O Terraform grava um ficheiro de estado que associa cada recurso do código a um objeto real criado através de uma API. Assim, consegue determinar que a remoção de cinco linhas significa que um servidor tem de ser destruído. O Ansible não guarda estado entre execuções. Liga-se por SSH, inspeciona a máquina e altera apenas o que ainda não corresponde ao playbook.

Esta única diferença explica o restante deste guia, incluindo o motivo pelo qual misturar os dois trabalhos numa só ferramenta causa problemas.

O que o Terraform realmente faz

O Terraform comunica com uma API através de um plugin de provider. A página do registry do seu provider define os tipos de recurso que pode declarar. Por isso, um servidor num host e um servidor noutro host podem ter nomes de recursos diferentes e argumentos diferentes.

resource "cloud_server" "web" {
  name  = "web1"
  image = "ubuntu-24.04"
  type  = "small"
}

output "web_ip" {
  value = cloud_server.web.ipv4_address
}

Substitua cloud_server pelo tipo de recurso documentado pelo seu provider. O bloco output é a parte importante para este guia, porque é assim que o endereço sai do Terraform.

terraform init
terraform fmt -check
terraform validate
terraform plan -out=tfplan
terraform apply tfplan

terraform init transfere o provider e grava um ficheiro de lock. terraform plan mostra a diferença entre o seu código e o ficheiro de estado. O resultado termina com uma linha semelhante a Plan: 1 to add, 0 to change, 0 to destroy.. Leia essa linha sempre. Alguns argumentos não podem ser alterados no local. O plano indica isso com # forces replacement junto ao atributo, seguido de 1 to add, 0 to change, 1 to destroy. Aplicar esse plano elimina o servidor e cria um novo servidor vazio. É assim que se perdem dados que pareciam estar protegidos.

Guardar o plano num ficheiro e aplicar o ficheiro, em vez de executar um terraform apply isolado, garante que aquilo que foi revisto é aquilo que será executado. Entre os dois comandos, outra pessoa pode ter alterado a infraestrutura.

terraform.tfstate é a memória. Se o perder, o Terraform deixa de saber que esses servidores lhe pertencem. O próximo apply tenta então criar duplicados. Mantenha-o num backend remoto assim que mais de uma pessoa executar os comandos. Duas pessoas a executar apply ao mesmo tempo produzem isto:

Error: Error acquiring the state lock

O OpenTofu é um fork do Terraform com os mesmos comandos e o mesmo formato de ficheiro. Em julho de 2026, tudo neste guia funciona se escrever tofu em vez de terraform.

O que o Ansible realmente faz

O Ansible não precisa de agente nem de API. Abre uma ligação SSH, copia um pequeno módulo Python para o destino, executa-o e elimina-o. Tudo o que puder alcançar com SSH e uma palavra-passe de sudo pode ser configurado pelo Ansible.

- name: Base web server
  hosts: web
  become: true
  tasks:
    - name: Install nginx
      ansible.builtin.apt:
        name: nginx
        state: present
        update_cache: true

    - name: Ensure nginx is running at boot
      ansible.builtin.service:
        name: nginx
        state: started
        enabled: true
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml --check --diff
ansible-playbook -i inventory.ini site.yml

O módulo ping valida o SSH, o Python e o sudo antes de começar a depurar um playbook. Um resultado correto é web1 | SUCCESS => {"ping": "pong"}. A execução --check --diff é o equivalente mais próximo de um plano no Ansible: indica o que seria alterado sem alterar nada. No entanto, as tarefas que dependem de tarefas anteriores podem apresentar resultados incorretos em modo de verificação, porque a alteração anterior nunca foi realmente aplicada.

Todas as execuções terminam com um resumo como ok=6 changed=2 unreachable=0 failed=0. Execute o mesmo playbook duas vezes. A segunda execução deve indicar changed=0. Uma tarefa que indique changed em todas as execuções não é idempotente. Normalmente, é uma tarefa command ou shell que deveria usar um módulo real. Se este assunto for novo para si, comece por um primeiro playbook do Ansible num único VPS e desenvolva-o a partir daí.

Onde as duas ferramentas se sobrepõem e onde entram em conflito

O Terraform pode executar comandos num servidor novo com o provisioner remote-exec. A própria documentação da HashiCorp classifica os provisioners como último recurso. Existem boas razões para isso.

Um provisioner é executado apenas quando o recurso é criado. Edite o script e nada acontece no servidor existente, porque, do ponto de vista do Terraform, o recurso já corresponde ao código. As etapas do provisioner nunca aparecem em terraform plan, por isso a revisão não mostra qualquer sinal delas. Se o script falhar, o Terraform marca o recurso como comprometido, e o próximo apply destrói e recria um servidor que provavelmente estava funcional.

A falha também ocorre num momento inadequado. O provider comunica que o servidor foi criado assim que a API confirma essa operação, enquanto o sistema operativo ainda está a arrancar e o sshd ainda não está a escutar.

Error: remote-exec provisioner error
timeout - last error: dial tcp 203.0.113.10:22: connect: connection refused

O Ansible apresenta a tentação oposta. Os módulos de cloud podem criar servidores, e isso funciona para um número reduzido de máquinas. Mas deixa de ter o grafo de dependências e o ficheiro de estado. O Ansible cria um recurso sem problemas, mas, se remover a tarefa do playbook, o recurso continua em execução e continua a gerar custos, porque nada registou que alguma vez lhe pertencia.

A regra que resulta daqui é simples: deixe o Terraform gerir os objetos que uma API cria e destrói, e deixe o Ansible gerir tudo dentro de um sistema operativo já iniciado.

A transferência funcionou

A transferência é uma fronteira, não uma integração. O Terraform termina, publica um endereço e para. O Ansible começa a partir desse endereço.

terraform apply -auto-approve
terraform output -raw web_ip
printf '[web]\n%s ansible_user=root\n' "$(terraform output -raw web_ip)" > inventory.ini
ansible -i inventory.ini web -m ansible.builtin.ping
ansible-playbook -i inventory.ini site.yml

terraform output -raw imprime um valor sem aspas e sem um invólucro JSON. É isso que pretende dentro de uma substituição de shell. Para vários servidores, use terraform output -json e crie o inventário a partir desse resultado, porque -raw processa apenas uma string, um número ou um booleano.

Vale a pena manter o passo ping entre as duas ferramentas. Ele separa "o Terraform forneceu o endereço errado" de "o meu playbook tem um erro". Estes dois problemas parecem idênticos quando o playbook é a primeira coisa que alguma vez interage com o novo servidor.

Lendo o estado do Terraform como inventário do Ansible

Se preferir não criar um ficheiro de inventário, a coleção cloud.terraform lê o estado diretamente.

ansible-galaxy collection install cloud.terraform

Escreva terraform.yml junto ao seu playbook:

plugin: cloud.terraform.terraform_provider
project_path: /home/deploy/infra
ansible-inventory -i terraform.yml --graph
ansible-playbook -i terraform.yml site.yml

Há dois aspetos a conhecer antes de depender desta opção. O plugin executa terraform show em project_path, por isso esse diretório já tem de estar inicializado; caso contrário, o plugin falha. Além disso, não cria hosts a partir dos seus recursos de servidor: lê recursos ansible_host e ansible_group, que deve declarar no código Terraform através do provider do Ansible. Nada aparece em ansible-inventory --graph até os adicionar.

Um ficheiro de inventário gerado de forma simples é mais fácil de depurar e funciona com qualquer provider. O plugin torna-se útil quando o inventário ultrapassa algumas máquinas e a edição manual começa a gerar erros de escrita. Esse é também o ponto em que gerir vários servidores Linux a partir de uma única máquina de controlo passa a ser um fluxo de trabalho real, e não apenas um hábito.

Você realmente precisa do Terraform?

A maioria das pessoas que lê isto não precisa, pelo menos ainda não. O Terraform compensa quando criar e destruir infraestrutura é uma tarefa recorrente. Se você contratou um VPS por meio de um painel de controlo e pretende mantê-lo durante dois anos, o Terraform descreve algo que acontece uma vez e acrescenta um ficheiro de estado que você não pode perder.

Use o Terraform quando recria ambientes com frequência, quando o staging tem de corresponder exatamente à produção, quando várias pessoas alteram a infraestrutura e você quer um plano que possa ser revisto antes de qualquer eliminação, ou quando o que gere vai além dos servidores e inclui registos DNS, balanceadores de carga e regras de firewall mantidos numa API do fornecedor.

Use apenas o Ansible quando os servidores têm uma vida longa e são poucos, e quando a pergunta diária é "este servidor está configurado corretamente?" e não "este servidor existe?". Um único playbook que prepara um servidor novo cobre o mesmo que os primeiros dez minutos num VPS novo, com a vantagem de ser executado da mesma forma no servidor seguinte.

A ordem de aprendizagem decorre disso. O Ansible compensa logo no primeiro servidor que você administra. O Terraform compensa a partir do terceiro ambiente que você recria.

O que falha na transferência

O servidor não está pronto. O Terraform termina com sucesso, mas o Ansible falha imediatamente.

fatal: [web1]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 203.0.113.10 port 22: Connection refused", "unreachable": true}

A API devolveu um endereço antes de sshd começar a escutar. Aguarde pela porta em vez de adicionar uma pausa fixa. O Ansible tem ansible.builtin.wait_for_connection para este caso específico; execute-o como a primeira tarefa do play. Quando o mesmo playbook visar um grupo em vez de um único servidor acabado de criar, defina antecipadamente o que deve acontecer quando um host continuar inacessível, porque o Ansible exclui esse host do restante da execução e a linha de resumo é o único local onde informa isso.

A chave do host mudou. Destruiu e recriou o servidor, e o novo servidor responde no mesmo endereço com uma chave nova.

Host key verification failed.

Remova a entrada antiga com ssh-keygen -R 203.0.113.10. Isto acontece frequentemente quando o Terraform passa a tratar da reconstrução, o que é uma boa razão para evitar reconstruções frequentes em máquinas que armazenam dados.

O Sudo falha. fatal: [web1]: FAILED! => {"msg": "Missing sudo password"} significa que become: true precisa de uma palavra-passe nesse host. Configure o sudo sem palavra-passe para o utilizador de implementação ou passe --ask-become-pass.

O Terraform quer destruir algo que não alterou. O plano mostra alterações que nunca escreveu, o que significa que a infraestrutura real divergiu do código, normalmente porque alguém alterou uma definição no painel web do provider. Execute terraform plan -refresh-only para ver apenas essa diferença e decida se o código ou o recurso ativo está incorreto. Nunca aplique um plano destrutivo que não consiga explicar linha a linha.

O Ansible indica alterações em todas as execuções. Uma tarefa shell sem uma condição creates ou when é executada incondicionalmente. Isto não é um problema meramente cosmético, porque significa que já não pode usar changed=0 como sinal de que um servidor está no estado pretendido.

FAQ

O Terraform pode substituir o Ansible?

Não para a configuração dentro de um servidor. O Terraform pode executar scripts com o provisioner remote-exec, mas estes scripts são executados apenas na criação do recurso, nunca aparecem no terraform plan e marcam o recurso como inválido quando falham. Isso agenda a destruição e a recriação no próximo apply. O Terraform não tem um equivalente de um módulo que verifique se o nginx já está instalado e não faça nada nesse caso. Use o Terraform para criar a máquina e depois transfira a configuração para o Ansible.

O Ansible pode substituir o Terraform?

Para um número reduzido de servidores de longa duração, sim. O Ansible tem módulos de cloud que criam servidores. Se encomendar duas instâncias VPS e as mantiver, isso pode ser suficiente. O que perde é o ficheiro de estado e o grafo de dependências: se remover uma tarefa do playbook, o recurso continua em execução e continua a gerar custos, porque o Ansible nunca registou que o criou. O Terraform teria planeado a destruição.

Qual dos dois devo aprender primeiro?

Ansible, se já administra servidores. O investimento compensa logo no primeiro servidor, requer apenas SSH e o conhecimento aplica-se a um servidor que tenha encomendado manualmente. O Terraform compensa mais tarde, quando recria ambientes repetidamente ou gere recursos do provider para além dos servidores, como registos DNS e regras de firewall.

Como passo o IP do novo servidor do Terraform para o Ansible?

Declare um output no código do Terraform e leia-o depois do apply. terraform output -raw web_ip imprime apenas o valor, para substituição numa shell, e terraform output -json fornece todos os outputs de uma vez quando existem vários hosts. Escreva esse valor num ficheiro de inventário ou instale a collection cloud.terraform e indique ansible-inventory -i terraform.yml --graph para o diretório do projeto.

Porque é que o meu playbook falha logo depois de o Terraform terminar?

O provider informa que o servidor foi criado assim que a respetiva API o indica, enquanto o sistema operativo ainda está a arrancar. Por isso, o SSH é recusado durante os primeiros segundos. O erro é UNREACHABLE! com Connection refused. Faça de ansible.builtin.wait_for_connection a primeira tarefa do play, em vez de adivinhar uma duração para o sleep, porque o tempo de arranque varia consoante a imagem e o plano.