Puppet ·
Ansible ·
Terraform
— What's the difference and when to use what? —
FinistDevs · 2026
Tech Lead @ OVHCloud
arnaud.premel-cabic@ovhcloud.com
Or: why doesn't it work here, when it works everywhere else?
Great.
Some 2 years old. Some a couple of months. Some freshly provisioned.
None of them are exactly alike.
Welcome to Snowflake Hell.
Prod breaks on a Tuesday. You can't reproduce the bug locally. You can't scale reliably. You can't onboard a new server without fear.
Yes, even your AI assistant can write it*. But you still need to understand what it deploys.
*Like this presentation 🤖Managing infrastructure through machine-readable files, stored in version control.
Puppet · Ansible · Terraform — each fights a different battle.
Start here. Before you configure a server, you need to have one.
Declarative, human-readable — pure JSON works too.
terraform plan — preview what will changeterraform apply — create or update resourcesterraform destroy — tear everything down.tfstate file maps code to real-world resourcesHandle it with care. Don't feed it to your LLM.
If it has an API, there's a Terraform provider for it.
# main.tf
terraform {
required_providers {
openstack = { source = "terraform-provider-openstack/openstack", version = "~> 3.0" }
ovh = { source = "ovh/ovh", version = "~> 1.0" }
}
}
resource "openstack_compute_instance_v2" "web" {
name = "finistdevs-web"
image_name = "Debian 12"
flavor_name = "b3-8"
network { name = "Ext-Net" }
}
resource "ovh_domain_zone_record" "web" {
zone = "example.com"
subdomain = "finistdevs"
fieldtype = "A"
target = openstack_compute_instance_v2.web.access_ip_v4
}
$ terminal
$ terraform init
Initializing provider plugins...
- Finding terraform-provider-openstack/openstack versions matching "~> 3.0"...
- Finding ovh/ovh versions matching "~> 1.0"...
- Installing terraform-provider-openstack/openstack v3.0.0...
- Installing ovh/ovh v1.3.0...
Terraform has been successfully initialized!
$ terraform plan
openstack_compute_instance_v2.web: Refreshing state...
Plan: 2 to add, 0 to change, 0 to destroy.
$ terraform apply
openstack_compute_instance_v2.web: Creating...
openstack_compute_instance_v2.web: Creation complete after 45s [id=abc-123]
ovh_domain_zone_record.web: Creating...
ovh_domain_zone_record.web: Creation complete after 3s [id=456]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Drop-in replacement. Fully compatible. Community-driven.
Terraform Enterprise / HCP Terraform — HashiCorp's commercial offering: remote state, RBAC, audit logs
Spacelift — GitOps-first CI/CD for Terraform (and OpenTofu)
Atlantis — open-source: plan & apply triggered by pull requests
env0, Scalr — SaaS alternatives with policy & cost management
Your servers are provisioned. Now make them do something.
# playbook/webserver.yml
- name: Configure web server
hosts: webservers
become: true
tasks:
- name: Install nginx
ansible.builtin.package:
name: nginx
state: present
- name: Deploy nginx config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: Restart nginx
handlers:
- name: Restart nginx
ansible.builtin.service:
name: nginx
state: restarted
$ terminal
$ ansible-playbook -i inventory playbook/webserver.yml
PLAY [Configure web server] ***************************************************
TASK [Gathering Facts] ********************************************************
ok: [finistdevs-web]
TASK [Install nginx] **********************************************************
changed: [finistdevs-web]
TASK [Deploy nginx config] ****************************************************
changed: [finistdevs-web]
RUNNING HANDLER [Restart nginx] ***********************************************
changed: [finistdevs-web]
PLAY RECAP ********************************************************************
finistdevs-web : ok=4 changed=3 unreachable=0 failed=0 skipped=0
Ansible is the tool you reach for when you need to do something — once, or every week.
Just ansible-galaxy install geerlingguy.docker.
AWX — open-source web UI, API, and scheduler for Ansible
Ansible Automation Platform (Red Hat) — enterprise version of AWX, with support & integrations
Semaphore — lightweight open-source alternative to AWX
The core Ansible engine remains Apache 2.0 — truly open-source.
Your servers are configured. Now keep them that way.
puppet-agent polls the Puppet ServerDrift is corrected automatically — without anyone lifting a finger.
# manifests/webserver.pp
class webserver {
package { 'nginx':
ensure => installed,
}
file { '/etc/nginx/nginx.conf':
ensure => file,
content => template('webserver/nginx.conf.erb'),
notify => Service['nginx'],
}
service { 'nginx':
ensure => running,
enable => true,
}
}
$ terminal
$ puppet agent -t
Info: Using environment 'production'
Info: Retrieving pluginfacts
Info: Caching catalog for finistdevs-web.example.com
Info: Applying configuration version '1713052408'
Notice: /Stage[main]/Webserver/Package[nginx]/ensure: created
Notice: /Stage[main]/Webserver/File[/etc/nginx/nginx.conf]/content:
--- /etc/nginx/nginx.conf
+++ /tmp/puppet-file20260413
Notice: /Stage[main]/Webserver/Service[nginx]/ensure: started
Notice: Applied catalog in 12.34 seconds
Puppet Enterprise and Foreman are self-hosted. No managed cloud offering.
The community is strong, with or without Puppet Inc.
Each one solves a different layer of the same problem.
Thank you!
Arnaud Prémel-Cabic · arnaud.premel-cabic@ovhcloud.com
Slides: ministicraft.pages.git.cloud.arnaud-pc.fr/finistdev-configuration-as-code/