Passer au contenu principal

Providers (Terraform)

Terraform et Ansible

Terraform et Ansible sont complémentaires, pas concurrents. Terraform provisionne l'infrastructure (VMs, réseau, DNS, stockage). Ansible configure les OS et déploie les applications. Articuler les deux correctement évite les zones grises où chaque outil essaie de faire le travail de l'autre.

Le pattern recommandé

  • Terraform : créer la VM, configurer le réseau, les security groups, les volumes, les DNS records
  • Ansible : configurer l'OS, installer les packages, déployer les applications, gérer les configurations
  • Interface : les outputs Terraform alimentent l'inventaire Ansible

Générer l'inventaire Ansible depuis les outputs

# outputs.tf
output "web_ips" {
  value = { for k, v in aws_instance.web : k => v.public_ip }
}

output "ansible_inventory" {
  value = templatefile("${path.module}/templates/inventory.tpl", {
    web_hosts = { for k, v in aws_instance.web : k => v.public_ip }
    db_host   = aws_db_instance.main.address
  })
}
# templates/inventory.tpl
[web]
%{ for name, ip in web_hosts ~}
${name} ansible_host=${ip} ansible_user=debian
%{ endfor ~}

[db]
db01 ansible_host=${db_host} ansible_user=admin
# Générer l'inventaire et lancer Ansible
terraform output -raw ansible_inventory > inventory/hosts.ini
ansible-playbook -i inventory/hosts.ini playbooks/site.yml

null_resource + local-exec

resource "null_resource" "configure_servers" {
  triggers = {
    # Relancer Ansible si les instances changent
    instance_ids = join(",", [for v in aws_instance.web : v.id])
  }

  provisioner "local-exec" {
    command = <<-EOT
      sleep 30    # attendre que SSH soit disponible
      ansible-playbook         -i "${join(",", [for v in aws_instance.web : "${v.public_ip}"])}, "         -u debian         --private-key ~/.ssh/id_ed25519         playbooks/site.yml
    EOT
  }

  depends_on = [aws_instance.web]
}

Cette approche fonctionne mais est fragile : la sortie Terraform reste bloquée pendant l'exécution d'Ansible, les erreurs Ansible ne propagent pas toujours proprement, et le re-run du playbook est difficile à isoler.

remote-exec : exécuter sur la cible

resource "aws_instance" "web" {
  # ...

  connection {
    type        = "ssh"
    user        = "debian"
    private_key = file(var.private_key_path)
    host        = self.public_ip
  }

  provisioner "remote-exec" {
    inline = [
      "sudo apt-get update",
      "sudo apt-get install -y python3",    # préparer pour Ansible
    ]
  }
}

Pattern pipeline recommandé

# .gitlab-ci.yml
stages: [terraform, ansible]

terraform_apply:
  stage: terraform
  script:
    - terraform init
    - terraform apply -auto-approve
    - terraform output -json > terraform_outputs.json
  artifacts:
    paths: [terraform_outputs.json]

ansible_deploy:
  stage: ansible
  dependencies: [terraform_apply]
  script:
    - python3 scripts/generate_inventory.py terraform_outputs.json > inventory/hosts.ini
    - ansible-playbook -i inventory/hosts.ini playbooks/site.yml

Ce pattern est propre : les deux étapes sont séparées, les outputs Terraform transitent via un artefact JSON, et chaque étape peut être rejouée indépendamment.