OVHcloud

Configuration as Code

Puppet Puppet · Ansible Ansible · Terraform Terraform
— What's the difference and when to use what? —

FinistDevs · 2026

Arnaud Prémel-Cabic

Tech Lead @ OVHCloud

arnaud.premel-cabic@ovhcloud.com

"It works on my server."

Or: why doesn't it work here, when it works everywhere else?

You have a server. It works.

Great.

You have 10 servers.

Still manageable… but every small change means 10 SSH sessions, 10 vim edits, 10 chances to make a mistake.

$ ssh root@… $ vim /etc/… ssh ssh ssh

Now you have 100 servers.

Some 2 years old. Some a couple of months. Some freshly provisioned.
None of them are exactly alike.

! · · ·

Each one is unique. Unreproducible. Undocumented.

Welcome to Snowflake Hell.

This is fine

Configuration drift is silent… until it isn't.

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.

T0 T1 T2 ALL IDENTICAL DRIFTING… ! ! ! CHAOS

What if your infrastructure was just… code?

Yes, even your AI assistant can write it*. But you still need to understand what it deploys.

{ } deploy *Like this presentation 🤖

Configuration as Code

Managing infrastructure through machine-readable files, stored in version control.

Reproducible Same input → same result Versionable Track every change Auditable Who changed what & when

Meet the three musketeers of infrastructure.

Puppet  ·  Ansible  ·  Terraform — each fights a different battle.

Terraform

Start here. Before you configure a server, you need to have one.

HCL: HashiCorp Configuration Language

Declarative, human-readable — pure JSON works too.

  • terraform plan — preview what will change
  • Review — validate the plan before proceeding
  • terraform apply — create or update resources
  • terraform destroy — tear everything down
HCL Code plan review apply Resources

Terraform remembers what it built.

  • The .tfstate file maps code to real-world resources
  • Store it remotely — never commit it to Git
  • May contain sensitive values: credentials, tokens, secrets

Handle it with care. Don't feed it to your LLM.

main.tf network.tf dns.tf Configuration .tfstate state mapping Source of Truth VM VNet DNS Zone Real Resources

One tool. Every API.

  • 1000+ providers — AWS, GCP, Azure, Cloudflare, GitHub, Kubernetes…
  • Not just cloud — GitHub teams, Datadog monitors, PagerDuty schedules, DNS records

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.
    

In 2023, HashiCorp changed Terraform's license.

  • BSL instead of MPL — no longer truly open-source
  • The community responded: OpenTofu, by the OpenTF Foundation

Drop-in replacement. Fully compatible. Community-driven.

Terraform at scale needs a platform.

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

Ansible

Your servers are provisioned. Now make them do something.

Push, not pull. SSH, not agents.

  • YAML playbooks run tasks in order, across any number of hosts
  • No daemon. No certificate authority. Just Python + SSH
  • Idempotent modules — same playbook runs safely again and again
>_ Control Node PUSH SSH Managed Hosts no agent required

# 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
    

Not just configuration. Operations.

  • Patch 200 servers tonight
  • Roll out a kernel upgrade with a canary strategy
  • Run a compliance audit across your whole fleet

Ansible is the tool you reach for when you need to do something — once, or every week.

The community does the heavy lifting.

  • Ansible Galaxy — 10,000+ ready-made roles and collections
  • Don't write a playbook to install Docker from scratch — someone already did

Just ansible-galaxy install geerlingguy.docker.

Ansible at scale: open-source vs enterprise.

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.

Puppet

Your servers are configured. Now keep them that way.

Pull, not push. Agents, not SSH.

  • Every 30 minutes, each puppet-agent polls the Puppet Server
  • Compiles a catalog and enforces it locally

Drift is corrected automatically — without anyone lifting a finger.

catalog Puppet Server every 30 min pull agent agent agent agent agent Managed Nodes

# 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
    

Someone SSH'd in and changed something.

  • Puppet noticed. Puppet fixed it.
  • Continuous compliance — not just at deploy time. Every. 30. Minutes.
  • No more gardening your servers by hand
! Drift detected 1 Agent applies catalog 2 Compliant 3 continuous enforcement loop

Puppet is for large fleets that can't afford drift.

  • Continuous compliance, auditability, and guaranteed state — at scale
  • Best suited for enterprises with hundreds or thousands of long-lived servers
  • Fewer SaaS options than Terraform or Ansible

Puppet Enterprise and Foreman are self-hosted. No managed cloud offering.

The ecosystem outlives the company.

  • Vox Pupuli — 100+ open-source Puppet modules, community-maintained
  • OpenVox — an emerging open-source fork of the Puppet core

The community is strong, with or without Puppet Inc.

They're not competing. They're complementary.

Each one solves a different layer of the same problem.

Terraform — Provision VMs, networks, cloud resources Day 0 Ansible — Configure packages, services, app deployment Day 1 Puppet — Enforce continuous compliance, drift correction Day 2+

A common production setup:

  1. Terraform provisions the VM
  2. Ansible configures it and deploys the app
  3. Puppet continuously enforces compliance

Questions?

Thank you!

Arnaud Prémel-Cabic  ·  arnaud.premel-cabic@ovhcloud.com
Slides: ministicraft.pages.git.cloud.arnaud-pc.fr/finistdev-configuration-as-code/