Files
finistdev-configuration-as-…/index.html
ministicraft fe2811c51c fix: set fixed slide dimensions and scaling for consistent sizing
Set 1280x720 canvas with margin 0.08 and scale range 0.2–2.0
so all slides render at the same size regardless of content.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-14 00:50:32 +02:00

431 lines
19 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Configuration as Code — Puppet vs Ansible vs Terraform</title>
<!-- Reveal.js (vendored) -->
<link rel="stylesheet" href="vendor/reveal.js/dist/reset.css">
<link rel="stylesheet" href="vendor/reveal.js/dist/reveal.css">
<link rel="stylesheet" href="vendor/reveal.js/dist/white.css">
<!-- Highlight.js for code blocks -->
<link rel="stylesheet" href="vendor/reveal.js/plugin/highlight/github.css">
<style>
:root {
--puppet-color: #A06010;
--ansible-color: #CC0000;
--terraform-color: #7B42BC;
--strip-color: #00A9FF;
}
.reveal .slides section {
text-align: left;
border-top: 6px solid var(--strip-color);
}
.reveal h1, .reveal h2 {
text-align: left;
}
.reveal h1 { font-size: 1.8em; }
.reveal h2 { font-size: 1.4em; line-height: 1.2; }
.reveal p, .reveal li { font-size: 0.85em; }
.reveal code { font-size: 0.7em; }
.reveal pre { width: 100%; }
.title-slide { text-align: center !important; }
.title-slide h1 { text-align: center; font-size: 2em; margin-bottom: 0.2em; }
.title-slide .subtitle { font-size: 1em; color: #444; }
.title-slide .meta { font-size: 0.7em; color: #555; margin-top: 1em; }
.title-slide .meta a { color: var(--strip-color); }
.puppet-col { color: var(--puppet-color); }
.ansible-col { color: var(--ansible-color); }
.tf-col { color: var(--terraform-color); }
.filename {
font-family: monospace;
font-size: 0.65em;
color: #555;
margin-bottom: 0.2em;
}
.slide-logo {
position: absolute;
bottom: 0.6em;
right: 0.8em;
width: 40px;
height: 40px;
opacity: 0.8;
}
.ovh-logo {
height: 36px;
width: auto;
margin-top: 1.2em;
display: block;
margin-left: auto;
margin-right: auto;
}
</style>
</head>
<body>
<div class="reveal">
<div class="slides">
<!-- ─── SLIDE 1 : Title ─────────────────────────────────────────── -->
<section class="title-slide">
<h1>⚙️ Configuration as Code</h1>
<p class="subtitle">Puppet · Ansible · Terraform<br>— What's the difference and when to use what? —</p>
<p class="meta">FinistDevs · 2026</p>
<img src="https://cdn.simpleicons.org/ovh/00A9FF" alt="OVHcloud" class="ovh-logo">
</section>
<!-- ─── SLIDE 2 : Speaker intro ────────────────────────────────── -->
<section>
<h2>Arnaud Prémel-Cabic</h2>
<p>Tech Lead @ OVHCloud</p>
<p style="color:#555; font-size:0.75em;">arnaud.premel-cabic@ovhcloud.com</p>
<img src="https://cdn.simpleicons.org/ovh/00A9FF" alt="OVHcloud" style="height:32px; width:auto; margin-top:1em;">
</section>
<!-- ─── SLIDE 3 ──────────────────────────────────────────────────── -->
<section>
<h2>"It works on my server."</h2>
<p><em>Or: why doesn't it work here, when it works everywhere else?</em></p>
</section>
<!-- ─── SLIDE 4 ──────────────────────────────────────────────────── -->
<section>
<h2>You have a server. It works.</h2>
<p><em>Great.</em></p>
</section>
<!-- ─── SLIDE 5 ──────────────────────────────────────────────────── -->
<section>
<h2>You have 10 servers.</h2>
<p><em>Still manageable… but every small change means 10 SSH sessions, 10 vim edits, 10 chances to make a mistake.</em></p>
</section>
<!-- ─── SLIDE 6 ──────────────────────────────────────────────────── -->
<section>
<h2>Now you have 100 servers.</h2>
<p><em>Some 2 years old. Some a couple of months. Some freshly provisioned.<br>None of them are exactly alike.</em></p>
</section>
<!-- ─── SLIDE 7 ──────────────────────────────────────────────────── -->
<section>
<h2>Each one is unique. Unreproducible. Undocumented.</h2>
<p><em>Welcome to Snowflake Hell.</em></p>
<img src="https://media.giphy.com/media/QMHoU66sBXqqLqYvGO/giphy.gif"
alt="This is fine"
style="height:220px; margin-top:0.5em; border-radius:6px;">
</section>
<!-- ─── SLIDE 8 ──────────────────────────────────────────────────── -->
<section>
<h2>Configuration drift is silent… until it isn't.</h2>
<p><em>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.</em></p>
</section>
<!-- ─── SLIDE 9 ──────────────────────────────────────────────────── -->
<section>
<h2>What if your infrastructure was just… code?</h2>
<p><em>Yes, even your AI assistant can write it. But you still need to understand what it deploys.</em></p>
</section>
<!-- ─── SLIDE 10 ──────────────────────────────────────────────────── -->
<section>
<h2>Configuration as Code</h2>
<p>Managing infrastructure through machine-readable files, stored in version control.</p>
<p style="margin-top:0.8em; color:#666;">Reproducible &nbsp;·&nbsp; Versionable &nbsp;·&nbsp; Auditable</p>
</section>
<!-- ─── SLIDE 11 ──────────────────────────────────────────────────── -->
<section>
<h2>Meet the three musketeers of infrastructure.</h2>
<p><span class="puppet-col">Puppet</span> &nbsp;·&nbsp; <span class="ansible-col">Ansible</span> &nbsp;·&nbsp; <span class="tf-col">Terraform</span> — each fights a different battle.</p>
</section>
<!-- ─── SLIDE 12 : Terraform intro ──────────────────────────────── -->
<section class="s-tf">
<h2>🏗️ <span class="tf-col">Terraform</span></h2>
<p>Start here. Before you configure a server, you need to have one.</p>
</section>
<!-- ─── SLIDE 13 : Terraform concepts ───────────────────────────── -->
<section class="s-tf">
<h2>HCL: HashiCorp Configuration Language.</h2>
<p>Declarative, human-readable — and pure JSON works too.<br>
<code>terraform plan</code> previews &nbsp;·&nbsp; <code>terraform apply</code> creates &nbsp;·&nbsp; <code>terraform destroy</code> removes.</p>
</section>
<!-- ─── SLIDE 14 : Terraform state ──────────────────────────────── -->
<section class="s-tf">
<h2>Terraform remembers what it built.</h2>
<p>The <code>.tfstate</code> file maps code to real-world resources. Store it remotely.<br>
Depending on what you manage, it can contain plaintext sensitive values — credentials, tokens, secrets.<br>
<em>Handle it with care. Don't feed it to your LLM.</em></p>
</section>
<!-- ─── SLIDE 15 : Terraform providers ──────────────────────────── -->
<section class="s-tf">
<h2>One tool. Every API.</h2>
<p>1000+ providers: AWS, GCP, Azure, Cloudflare, GitHub, Kubernetes…<br>
Not just cloud — manage GitHub teams, Datadog monitors, PagerDuty schedules, DNS records.<br>
<em>If it has an API, there's a Terraform provider for it.</em></p>
</section>
<!-- ─── SLIDE 16 : Terraform code ───────────────────────────────── -->
<section class="s-tf">
<p class="filename"># main.tf</p>
<pre><code class="language-hcl" data-trim>
terraform {
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
}
}
provider "aws" { region = "eu-west-3" }
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = { Name = "finistdevs-web" }
}
output "public_ip" {
value = aws_instance.web.public_ip
}
</code></pre>
</section>
<!-- ─── SLIDE 17 : OpenTofu ──────────────────────────────────────── -->
<section class="s-tf">
<h2>In 2023, HashiCorp changed Terraform's license.</h2>
<p>BSL instead of MPL — no longer truly open-source.<br>
The community responded: <strong>OpenTofu</strong>, by the OpenTF Foundation, is the open-source fork.<br>
<em>Drop-in replacement. Fully compatible. Community-driven.</em></p>
</section>
<!-- ─── SLIDE 18 : Terraform platforms ──────────────────────────── -->
<section class="s-tf">
<h2>Terraform at scale needs a platform.</h2>
<p>
<strong>Terraform Enterprise / HCP Terraform</strong> — HashiCorp's commercial offering: remote state, RBAC, audit logs<br>
<strong>Spacelift</strong> — GitOps-first CI/CD for Terraform (and OpenTofu)<br>
<strong>Atlantis</strong> — open-source: plan &amp; apply triggered by pull requests<br>
<strong>env0, Scalr</strong> — SaaS alternatives with policy &amp; cost management
</p>
</section>
<!-- ─── SLIDE 19 : Ansible intro ─────────────────────────────────── -->
<section class="s-ansible">
<h2><span class="ansible-col">Ansible</span></h2>
<p>Your servers are provisioned. Now make them do something.</p>
</section>
<!-- ─── SLIDE 20 : Ansible concepts ──────────────────────────────── -->
<section class="s-ansible">
<h2>Push, not pull. SSH, not agents.</h2>
<p>YAML playbooks run tasks in order, across any number of hosts.<br>
No daemon. No certificate authority. Just Python + SSH.<br>
<em>Idempotent modules ensure the same playbook can run safely again and again.</em></p>
<img src="https://media.giphy.com/media/3oEjI6SIIHBdRxXI40/giphy.gif"
alt="It just works"
style="height:180px; margin-top:0.5em; border-radius:6px;">
</section>
<!-- ─── SLIDE 21 : Ansible code ──────────────────────────────────── -->
<section class="s-ansible">
<p class="filename"># playbook/webserver.yml</p>
<pre><code class="language-yaml" data-trim>
- 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
</code></pre>
</section>
<!-- ─── SLIDE 22 : Ansible operations ───────────────────────────── -->
<section class="s-ansible">
<h2>Not just configuration. Operations.</h2>
<p>Patch 200 servers tonight. Roll out a kernel upgrade with a canary strategy. Run a compliance audit across your whole fleet.<br>
<em>Ansible is the tool you reach for when you need to do something — once, or every week.</em></p>
</section>
<!-- ─── SLIDE 23 : Ansible Galaxy ────────────────────────────────── -->
<section class="s-ansible">
<h2>The community does the heavy lifting.</h2>
<p><strong>Ansible Galaxy</strong> — 10,000+ ready-made roles and collections.<br>
Don't write a playbook to install Docker from scratch. Someone already did.<br>
<em>Just <code>ansible-galaxy install geerlingguy.docker</code>.</em></p>
</section>
<!-- ─── SLIDE 24 : Ansible platforms ────────────────────────────── -->
<section class="s-ansible">
<h2>Ansible at scale: open-source vs enterprise.</h2>
<p>
<strong>AWX</strong> — open-source web UI, API, and scheduler for Ansible<br>
<strong>Ansible Automation Platform</strong> (Red Hat) — enterprise version of AWX, with support &amp; integrations<br>
<strong>Semaphore</strong> — lightweight open-source alternative to AWX<br>
<em>The core Ansible engine remains Apache 2.0 — truly open-source.</em>
</p>
</section>
<!-- ─── SLIDE 25 : Puppet intro ─────────────────────────────────── -->
<section class="s-puppet">
<h2>🐾 <span class="puppet-col">Puppet</span></h2>
<p>Your servers are configured. Now keep them that way.</p>
</section>
<!-- ─── SLIDE 26 : Puppet concepts ─────────────────────────────── -->
<section class="s-puppet">
<h2>Pull, not push. Agents, not SSH.</h2>
<p>Every 30 minutes, each puppet-agent polls the Puppet Server, compiles a catalog, and enforces it.<br>
<em>Drift is corrected automatically — without anyone lifting a finger.</em></p>
</section>
<!-- ─── SLIDE 27 : Puppet code ──────────────────────────────────── -->
<section class="s-puppet">
<p class="filename"># manifests/webserver.pp</p>
<pre><code class="language-puppet" data-trim>
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,
}
}
</code></pre>
</section>
<!-- ─── SLIDE 28 : Puppet drift detection ──────────────────────── -->
<section class="s-puppet">
<h2>Someone SSH'd in and changed something.</h2>
<p>Puppet noticed. Puppet fixed it.<br>
<em>Continuous compliance — not just at deploy time. Every. 30. Minutes.</em><br>
No more gardening your servers by hand.</p>
<img src="https://media.giphy.com/media/l3q2K5jinAlChoCLS/giphy.gif"
alt="Automated maintenance"
style="height:180px; margin-top:0.5em; border-radius:6px;">
</section>
<!-- ─── SLIDE 29 : Puppet platforms ────────────────────────────── -->
<section class="s-puppet">
<h2>Puppet is for large fleets that can't afford drift.</h2>
<p>Continuous compliance, auditability, and guaranteed state — at scale.<br>
Best suited for enterprises with hundreds or thousands of long-lived servers.<br>
<em>Fewer friendly SaaS options than Terraform or Ansible.<br>
Puppet Enterprise and Foreman are self-hosted. No managed cloud offering.</em></p>
</section>
<!-- ─── SLIDE 30 : Puppet community ────────────────────────────── -->
<section class="s-puppet">
<h2>The ecosystem outlives the company.</h2>
<p><strong>Vox Pupuli</strong> — 100+ open-source Puppet modules, community-maintained.<br>
<strong>OpenVox</strong> — an emerging open-source fork of the Puppet core.<br>
<em>The community is strong, with or without Puppet Inc.</em></p>
</section>
<!-- ─── SLIDE 31 : They're complementary ───────────────────────── -->
<section>
<h2>They're not competing. They're complementary.</h2>
<p>Each one solves a different layer of the same problem.</p>
<img src="https://media.giphy.com/media/j2pWZpr5RlpCodOB0d/giphy.gif"
alt="Assembling"
style="height:200px; margin-top:0.5em; border-radius:6px;">
</section>
<!-- ─── SLIDE 32 : Real-world stack ─────────────────────────────── -->
<section>
<h2>A common production setup:</h2>
<ol>
<li class="fragment"><span class="tf-col">Terraform</span> provisions the VM</li>
<li class="fragment"><span class="ansible-col">Ansible</span> configures it and deploys the app</li>
<li class="fragment"><span class="puppet-col">Puppet</span> continuously enforces compliance</li>
</ol>
</section>
<!-- ─── SLIDE 33 : Closing ──────────────────────────────────────── -->
<section class="title-slide">
<h1>Questions?</h1>
<p class="subtitle">Thank you!</p>
<p class="meta" style="margin-top:2em;">
Arnaud Prémel-Cabic &nbsp;·&nbsp; arnaud.premel-cabic@ovhcloud.com<br>
Slides: <a href="https://ministicraft.pages.git.cloud.arnaud-pc.fr/finistdev-configuration-as-code/" target="_blank">ministicraft.pages.git.cloud.arnaud-pc.fr/finistdev-configuration-as-code/</a>
</p>
</section>
</div>
</div>
<script src="vendor/reveal.js/dist/reveal.js"></script>
<script src="vendor/reveal.js/plugin/notes/notes.js"></script>
<script src="vendor/reveal.js/plugin/highlight/highlight.js"></script>
<script>
const toolLogos = {
's-tf': 'https://cdn.simpleicons.org/terraform',
's-ansible': 'https://cdn.simpleicons.org/ansible',
's-puppet': 'https://cdn.simpleicons.org/puppet/C17F00'
};
const altNames = {'s-tf': 'Terraform', 's-ansible': 'Ansible', 's-puppet': 'Puppet'};
Object.entries(toolLogos).forEach(([cls, src]) => {
document.querySelectorAll('section.' + cls).forEach(s => {
const img = document.createElement('img');
img.src = src;
img.alt = altNames[cls];
img.className = 'slide-logo';
s.appendChild(img);
});
});
Reveal.initialize({
width: 1280,
height: 720,
margin: 0.08,
minScale: 0.2,
maxScale: 2.0,
hash: true,
slideNumber: 'c/t',
transition: 'slide',
backgroundTransition: 'fade',
controls: true,
progress: true,
center: false,
plugins: [ RevealNotes, RevealHighlight ]
});
</script>
</body>
</html>