Compare commits
2 Commits
fa76a3a19a
...
f8dc80027f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8dc80027f | ||
|
|
07ca8f2e3d |
12
README.md
12
README.md
@@ -1,7 +1,17 @@
|
|||||||
# Configuration as Code — Puppet vs Ansible vs Terraform
|
# Configuration as Code: Terraform, Ansible & Puppet — The Three Musketeers of Infrastructure
|
||||||
|
|
||||||
Presentation for the **Finistère Dev Meetup**.
|
Presentation for the **Finistère Dev Meetup**.
|
||||||
|
|
||||||
|
## 📝 Abstract
|
||||||
|
|
||||||
|
> "It works on my server." One machine, hand-configured, impossible to reproduce — and the problem only gets worse as you scale to ten, a hundred, a thousand servers. Configuration drift creeps in silently, and nobody remembers exactly how anything was set up.
|
||||||
|
>
|
||||||
|
> What if your infrastructure was just… code?
|
||||||
|
>
|
||||||
|
> This talk introduces **Configuration as Code** through its three best-known tools — **Terraform**, **Ansible**, and **Puppet**. We'll see what each one actually does: how Terraform provisions infrastructure declaratively and tracks its state, how Ansible configures servers and runs operations over SSH, and how Puppet's agents continuously enforce compliance and heal drift at scale. Along the way we'll touch on the real-world questions that matter — licensing changes, open-source forks, enterprise vs community editions, and tooling at scale.
|
||||||
|
>
|
||||||
|
> The punchline: these tools aren't competitors. They're complementary, each solving a different layer of the same problem. By the end you'll understand where each one fits — and how a typical production setup uses all three together: Terraform to provision the VM, Ansible to configure it and deploy the app, and Puppet to keep it compliant, forever.
|
||||||
|
|
||||||
## 🌐 View the slides
|
## 🌐 View the slides
|
||||||
|
|
||||||
👉 **https://ministicraft.pages.git.cloud.arnaud-pc.fr/finistdev-configuration-as-code/**
|
👉 **https://ministicraft.pages.git.cloud.arnaud-pc.fr/finistdev-configuration-as-code/**
|
||||||
|
|||||||
270
index.html
270
index.html
@@ -129,6 +129,13 @@
|
|||||||
<img src="https://cdn.simpleicons.org/terraform" alt="Terraform" style="height:0.9em; vertical-align:middle;"> Terraform
|
<img src="https://cdn.simpleicons.org/terraform" alt="Terraform" style="height:0.9em; vertical-align:middle;"> Terraform
|
||||||
<br>— What's the difference and when to use what? —</p>
|
<br>— What's the difference and when to use what? —</p>
|
||||||
<p class="meta">FinistDevs · 2026</p>
|
<p class="meta">FinistDevs · 2026</p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Welcome — quick intro to the topic: how we manage infrastructure as code.</li>
|
||||||
|
<li>Three tools compared: Puppet, Ansible, Terraform — what each is for, when to pick which.</li>
|
||||||
|
<li>Goal: not "which is best" but understanding their distinct roles.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 2 : Speaker intro ─────────────────────────────────── -->
|
<!-- ─── SLIDE 2 : Speaker intro ─────────────────────────────────── -->
|
||||||
@@ -136,18 +143,37 @@
|
|||||||
<h2>Arnaud Prémel-Cabic</h2>
|
<h2>Arnaud Prémel-Cabic</h2>
|
||||||
<p>Tech Lead @ OVHCloud</p>
|
<p>Tech Lead @ OVHCloud</p>
|
||||||
<p><small style="color:var(--ods-text);">arnaud.premel-cabic@ovhcloud.com</small></p>
|
<p><small style="color:var(--ods-text);">arnaud.premel-cabic@ovhcloud.com</small></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Tech Lead at OVHcloud — work with these tools daily.</li>
|
||||||
|
<li>Keep it short, get to the content.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 3 ─────────────────────────────────────────────────── -->
|
<!-- ─── SLIDE 3 ─────────────────────────────────────────────────── -->
|
||||||
<section>
|
<section>
|
||||||
<h2>"It works on my server."</h2>
|
<h2>"It works on my server."</h2>
|
||||||
<p><em>Why doesn't it work here when it works everywhere else?</em></p>
|
<p><em>Why doesn't it work here when it works everywhere else?</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>The classic excuse — everyone's heard it (or said it).</li>
|
||||||
|
<li>Hook: the real problem isn't the code, it's the un-managed environment.</li>
|
||||||
|
<li>Sets up the whole talk: configuration we can't reproduce.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 4 ─────────────────────────────────────────────────── -->
|
<!-- ─── SLIDE 4 ─────────────────────────────────────────────────── -->
|
||||||
<section>
|
<section>
|
||||||
<h2>You have a server. It works.</h2>
|
<h2>You have a server. It works.</h2>
|
||||||
<p><em>Great.</em></p>
|
<p><em>Great.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>One server, configured by hand — totally fine at this scale.</li>
|
||||||
|
<li>No tooling needed yet. The pain starts when you grow.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<!-- SVG1 — single server, green check -->
|
<!-- SVG1 — single server, green check -->
|
||||||
<svg width="700" height="250" viewBox="0 0 700 250" style="margin-top:0.5em;" xmlns="http://www.w3.org/2000/svg">
|
<svg width="700" height="250" viewBox="0 0 700 250" style="margin-top:0.5em;" xmlns="http://www.w3.org/2000/svg">
|
||||||
<!-- Server body -->
|
<!-- Server body -->
|
||||||
@@ -179,6 +205,12 @@
|
|||||||
<li>10 manual edits</li>
|
<li>10 manual edits</li>
|
||||||
<li>10 chances to make a mistake</li>
|
<li>10 chances to make a mistake</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Manual edits don't scale — repetition breeds error.</li>
|
||||||
|
<li>Did you apply the change to all 10? Identically? You can't be sure.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<!-- SVG2 — laptop + 10 servers fan-out -->
|
<!-- SVG2 — laptop + 10 servers fan-out -->
|
||||||
<svg width="720" height="260" viewBox="0 0 720 260" style="margin-top:0.5em;" xmlns="http://www.w3.org/2000/svg">
|
<svg width="720" height="260" viewBox="0 0 720 260" style="margin-top:0.5em;" xmlns="http://www.w3.org/2000/svg">
|
||||||
<!-- Laptop body -->
|
<!-- Laptop body -->
|
||||||
@@ -269,6 +301,13 @@
|
|||||||
<li>Some 2 years old. Some a few months. Some brand new.</li>
|
<li>Some 2 years old. Some a few months. Some brand new.</li>
|
||||||
<li>None of them are exactly alike.</li>
|
<li>None of them are exactly alike.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Different ages, different patch levels, manual tweaks over time.</li>
|
||||||
|
<li>Point at the snowflakes/warnings in the diagram — each box is subtly different.</li>
|
||||||
|
<li>This is the reality of a hand-managed fleet.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<!-- SVG3 — 100-server chaos grid with drift indicators -->
|
<!-- SVG3 — 100-server chaos grid with drift indicators -->
|
||||||
<svg width="700" height="260" viewBox="0 0 700 260" style="margin-top:0.5em;" xmlns="http://www.w3.org/2000/svg">
|
<svg width="700" height="260" viewBox="0 0 700 260" style="margin-top:0.5em;" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
@@ -404,6 +443,13 @@
|
|||||||
<section>
|
<section>
|
||||||
<h2>Unique. Unreproducible. Undocumented.</h2>
|
<h2>Unique. Unreproducible. Undocumented.</h2>
|
||||||
<p><em>Welcome to Snowflake Hell.</em></p>
|
<p><em>Welcome to Snowflake Hell.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>"Snowflake servers" — unique, fragile, impossible to recreate.</li>
|
||||||
|
<li>The "this is fine" meme — we've all normalized this chaos.</li>
|
||||||
|
<li>Lighten the mood, then pivot to the consequences.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<img src="https://media.giphy.com/media/QMHoU66sBXqqLqYvGO/giphy.gif"
|
<img src="https://media.giphy.com/media/QMHoU66sBXqqLqYvGO/giphy.gif"
|
||||||
alt="This is fine"
|
alt="This is fine"
|
||||||
style="height:220px; margin-top:0.5em; border-radius:6px;">
|
style="height:220px; margin-top:0.5em; border-radius:6px;">
|
||||||
@@ -418,6 +464,13 @@
|
|||||||
<li>Can't scale reliably</li>
|
<li>Can't scale reliably</li>
|
||||||
<li>Can't onboard a new server without fear</li>
|
<li>Can't onboard a new server without fear</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Define drift: state slowly diverging from intent, T0 → T2 in the diagram.</li>
|
||||||
|
<li>It's silent — no alarm goes off until prod breaks.</li>
|
||||||
|
<li>These four pains are why we need Configuration as Code.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<svg width="750" height="220" style="margin-top:0.5em;" viewBox="0 0 750 220" xmlns="http://www.w3.org/2000/svg">
|
<svg width="750" height="220" style="margin-top:0.5em;" viewBox="0 0 750 220" xmlns="http://www.w3.org/2000/svg">
|
||||||
<line x1="60" y1="180" x2="700" y2="180" stroke="#666666" stroke-width="2.5"/>
|
<line x1="60" y1="180" x2="700" y2="180" stroke="#666666" stroke-width="2.5"/>
|
||||||
<polygon points="700,174 720,180 700,186" fill="#666666"/>
|
<polygon points="700,174 720,180 700,186" fill="#666666"/>
|
||||||
@@ -483,12 +536,26 @@
|
|||||||
<circle cx="652" cy="124" r="3" fill="#00a344"/>
|
<circle cx="652" cy="124" r="3" fill="#00a344"/>
|
||||||
</svg>
|
</svg>
|
||||||
<small style="position:absolute; bottom:40px; right:0; color:var(--ods-neutral-600); font-size:0.45em;">*Like this presentation 🤖</small>
|
<small style="position:absolute; bottom:40px; right:0; color:var(--ods-neutral-600); font-size:0.45em;">*Like this presentation 🤖</small>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>The pitch: describe infra in files, deploy reproducibly.</li>
|
||||||
|
<li>Aside on AI: it can write the code, but you must understand what it deploys — own the result.</li>
|
||||||
|
<li>Fun fact: this deck itself was built with AI assistance.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 10 ────────────────────────────────────────────────── -->
|
<!-- ─── SLIDE 10 ────────────────────────────────────────────────── -->
|
||||||
<section>
|
<section>
|
||||||
<h2>Configuration as Code</h2>
|
<h2>Configuration as Code</h2>
|
||||||
<p>Machine-readable files. Version-controlled. Automated.</p>
|
<p>Machine-readable files. Version-controlled. Automated.</p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Three core benefits: reproducible, versionable, auditable.</li>
|
||||||
|
<li>Same input → same result. Git history = change log. Know who changed what & when.</li>
|
||||||
|
<li>This is the foundation all three tools share.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<svg width="750" height="220" style="margin-top:0.5em;" viewBox="0 0 750 220" xmlns="http://www.w3.org/2000/svg">
|
<svg width="750" height="220" style="margin-top:0.5em;" viewBox="0 0 750 220" xmlns="http://www.w3.org/2000/svg">
|
||||||
<!-- Reproducible -->
|
<!-- Reproducible -->
|
||||||
<g transform="translate(125,80)">
|
<g transform="translate(125,80)">
|
||||||
@@ -536,12 +603,25 @@
|
|||||||
<img src="https://cdn.simpleicons.org/terraform" alt="Terraform" style="height:0.9em; vertical-align:middle;"> <span class="tf-col">Terraform</span>
|
<img src="https://cdn.simpleicons.org/terraform" alt="Terraform" style="height:0.9em; vertical-align:middle;"> <span class="tf-col">Terraform</span>
|
||||||
</p>
|
</p>
|
||||||
<p><em>Each solves a different problem.</em></p>
|
<p><em>Each solves a different problem.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Introduce the three: Puppet, Ansible, Terraform.</li>
|
||||||
|
<li>Tease the punchline early: they're complementary, not rivals.</li>
|
||||||
|
<li>We'll go through them in deploy order: provision → configure → enforce.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 12 : Terraform intro ──────────────────────────────── -->
|
<!-- ─── SLIDE 12 : Terraform intro ──────────────────────────────── -->
|
||||||
<section class="s-tf">
|
<section class="s-tf">
|
||||||
<h2><span class="tf-col">Terraform</span></h2>
|
<h2><span class="tf-col">Terraform</span></h2>
|
||||||
<p>Start here. Before you configure a server, you need to have one.</p>
|
<p>Start here. Before you configure a server, you need to have one.</p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>First layer: provisioning. You can't configure what doesn't exist.</li>
|
||||||
|
<li>Terraform's job is creating the infrastructure itself.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
@@ -554,12 +634,24 @@
|
|||||||
<li>Written in <strong>Go</strong></li>
|
<li>Written in <strong>Go</strong></li>
|
||||||
<li>BSL license since 2023 (was MPL)</li>
|
<li>BSL license since 2023 (was MPL)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>The IaC standard for cloud provisioning.</li>
|
||||||
|
<li>HashiCorp, 2014, Go. Flag the 2023 license change — we'll come back to it (OpenTofu).</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 14 : Terraform concepts ───────────────────────────── -->
|
<!-- ─── SLIDE 14 : Terraform concepts ───────────────────────────── -->
|
||||||
<section class="s-tf">
|
<section class="s-tf">
|
||||||
<h2>HCL: HashiCorp Configuration Language</h2>
|
<h2>HCL: HashiCorp Configuration Language</h2>
|
||||||
<p>Declarative, human-readable — pure JSON works too.</p>
|
<p>Declarative, human-readable — pure JSON works too.</p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Declarative: you describe the desired end state, not the steps.</li>
|
||||||
|
<li>HCL is the common language across all HashiCorp tools.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 15 : Terraform workflow ───────────────────────────── -->
|
<!-- ─── SLIDE 15 : Terraform workflow ───────────────────────────── -->
|
||||||
@@ -571,6 +663,13 @@
|
|||||||
<li><code>terraform apply</code> — create or update resources</li>
|
<li><code>terraform apply</code> — create or update resources</li>
|
||||||
<li><code>terraform destroy</code> — tear everything down</li>
|
<li><code>terraform destroy</code> — tear everything down</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Key safety feature: plan lets you preview before anything changes.</li>
|
||||||
|
<li>Always review the plan — this is what makes Terraform safe in prod.</li>
|
||||||
|
<li>apply is idempotent; destroy cleanly removes everything it created.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<svg width="900" height="150" style="margin-top:0.5em;" viewBox="0 0 900 150" xmlns="http://www.w3.org/2000/svg">
|
<svg width="900" height="150" style="margin-top:0.5em;" viewBox="0 0 900 150" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="wf-arrow" viewBox="0 0 10 7" refX="10" refY="3.5" markerWidth="10" markerHeight="7" orient="auto-start-auto">
|
<marker id="wf-arrow" viewBox="0 0 10 7" refX="10" refY="3.5" markerWidth="10" markerHeight="7" orient="auto-start-auto">
|
||||||
@@ -630,6 +729,13 @@
|
|||||||
<li>May contain sensitive values: credentials, tokens, secrets</li>
|
<li>May contain sensitive values: credentials, tokens, secrets</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><em>Handle with care.</em></p>
|
<p><em>Handle with care.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>State is how Terraform knows what it already built — maps code to real resources.</li>
|
||||||
|
<li>Two big gotchas: store it remotely (team access + locking), never commit it (secrets inside).</li>
|
||||||
|
<li>Lost or corrupt state = Terraform loses track of reality.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<svg width="750" height="220" style="margin-top:0.5em;" viewBox="0 0 750 220" xmlns="http://www.w3.org/2000/svg">
|
<svg width="750" height="220" style="margin-top:0.5em;" viewBox="0 0 750 220" xmlns="http://www.w3.org/2000/svg">
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="st-arrow" viewBox="0 0 8 6" refX="8" refY="3" markerWidth="8" markerHeight="6" orient="auto-start-auto">
|
<marker id="st-arrow" viewBox="0 0 8 6" refX="8" refY="3" markerWidth="8" markerHeight="6" orient="auto-start-auto">
|
||||||
@@ -700,6 +806,13 @@
|
|||||||
<li>Not just cloud — DNS, monitoring, CI/CD, anything with an API</li>
|
<li>Not just cloud — DNS, monitoring, CI/CD, anything with an API</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><em>If it has an API, there's a Terraform provider for it.</em></p>
|
<p><em>If it has an API, there's a Terraform provider for it.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>The real power: one workflow for everything, not just one cloud.</li>
|
||||||
|
<li>Examples beyond cloud: DNS records, GitHub repos, monitoring dashboards.</li>
|
||||||
|
<li>Providers are the plugin ecosystem that makes Terraform universal.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 18 : Terraform code ───────────────────────────────── -->
|
<!-- ─── SLIDE 18 : Terraform code ───────────────────────────────── -->
|
||||||
@@ -727,6 +840,13 @@ resource "ovh_domain_zone_record" "web" {
|
|||||||
target = openstack_compute_instance_v2.web.access_ip_v4
|
target = openstack_compute_instance_v2.web.access_ip_v4
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Real OVHcloud example: spin up an instance, then point a DNS record at it.</li>
|
||||||
|
<li>Note the implicit dependency — the DNS record references the instance's IP, so Terraform orders them automatically.</li>
|
||||||
|
<li>Two different providers (OpenStack + OVH) working together in one file.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 19 : Terraform CLI ────────────────────────────────── -->
|
<!-- ─── SLIDE 19 : Terraform CLI ────────────────────────────────── -->
|
||||||
@@ -753,6 +873,13 @@ ovh_domain_zone_record.web: Creation complete after 3s [id=456]
|
|||||||
|
|
||||||
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
|
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Walk the lifecycle: init (download providers) → plan → apply.</li>
|
||||||
|
<li>"2 added, 0 changed, 0 destroyed" — exactly the diff you reviewed, nothing more.</li>
|
||||||
|
<li>Re-running apply with no changes does nothing — that's idempotence.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 20 : OpenTofu ─────────────────────────────────────── -->
|
<!-- ─── SLIDE 20 : OpenTofu ─────────────────────────────────────── -->
|
||||||
@@ -763,6 +890,13 @@ Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
|
|||||||
<li>The community responded: <strong>OpenTofu</strong>, by the OpenTF Foundation</li>
|
<li>The community responded: <strong>OpenTofu</strong>, by the OpenTF Foundation</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><em>Drop-in replacement. Fully compatible. Community-driven.</em></p>
|
<p><em>Drop-in replacement. Fully compatible. Community-driven.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>2023: HashiCorp moved to BSL — restricts commercial competitors, not truly open-source anymore.</li>
|
||||||
|
<li>Community forked it: OpenTofu, now under the Linux Foundation.</li>
|
||||||
|
<li>Practical takeaway: drop-in compatible, swap the binary. Worth knowing for licensing-sensitive orgs.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 21 : Terraform platforms ──────────────────────────── -->
|
<!-- ─── SLIDE 21 : Terraform platforms ──────────────────────────── -->
|
||||||
@@ -774,12 +908,25 @@ Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
|
|||||||
<li><strong>Atlantis</strong> — open-source, plan & apply from pull requests</li>
|
<li><strong>Atlantis</strong> — open-source, plan & apply from pull requests</li>
|
||||||
<li><strong>env0, Scalr</strong> — SaaS with policy & cost management</li>
|
<li><strong>env0, Scalr</strong> — SaaS with policy & cost management</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Running Terraform from a laptop doesn't scale to a team.</li>
|
||||||
|
<li>You need: shared/locked state, RBAC, audit, plan-on-PR, policy & cost guardrails.</li>
|
||||||
|
<li>Range from SaaS (HCP, Spacelift, env0) to self-hosted open-source (Atlantis).</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 22 : Ansible intro ────────────────────────────────── -->
|
<!-- ─── SLIDE 22 : Ansible intro ────────────────────────────────── -->
|
||||||
<section class="s-ansible">
|
<section class="s-ansible">
|
||||||
<h2><span class="ansible-col">Ansible</span></h2>
|
<h2><span class="ansible-col">Ansible</span></h2>
|
||||||
<p>Your servers are provisioned. Now make them do something.</p>
|
<p>Your servers are provisioned. Now make them do something.</p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Second layer: configuration. The VMs exist — now install and set things up.</li>
|
||||||
|
<li>Transition: Terraform built the box, Ansible makes it useful.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
@@ -792,6 +939,12 @@ Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
|
|||||||
<li>Acquired by <strong>Red Hat</strong> in 2015</li>
|
<li>Acquired by <strong>Red Hat</strong> in 2015</li>
|
||||||
<li>Written in <strong>Python</strong> — Apache 2.0 license</li>
|
<li>Written in <strong>Python</strong> — Apache 2.0 license</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Key differentiator: agentless — nothing to install on targets.</li>
|
||||||
|
<li>Red Hat backed, Python, genuinely open-source (Apache 2.0) — contrast with Terraform's BSL.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 24 : Ansible concepts ─────────────────────────────── -->
|
<!-- ─── SLIDE 24 : Ansible concepts ─────────────────────────────── -->
|
||||||
@@ -802,6 +955,13 @@ Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
|
|||||||
<li>Nothing to install on target servers — just <strong>Python + SSH</strong></li>
|
<li>Nothing to install on target servers — just <strong>Python + SSH</strong></li>
|
||||||
<li>Idempotent modules — same playbook runs safely again and again</li>
|
<li>Idempotent modules — same playbook runs safely again and again</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Push model: control node connects out over SSH and runs tasks — point at the diagram.</li>
|
||||||
|
<li>"Agentless" = just needs Python + SSH on the target, no daemon.</li>
|
||||||
|
<li>Idempotent modules: re-running is safe, only changes what's needed.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<svg width="750" height="250" viewBox="0 0 750 250" xmlns="http://www.w3.org/2000/svg" style="margin-top:0.5em;">
|
<svg width="750" height="250" viewBox="0 0 750 250" xmlns="http://www.w3.org/2000/svg" style="margin-top:0.5em;">
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="a-push" viewBox="0 0 10 7" refX="9" refY="3.5" markerWidth="9" markerHeight="7" orient="auto">
|
<marker id="a-push" viewBox="0 0 10 7" refX="9" refY="3.5" markerWidth="9" markerHeight="7" orient="auto">
|
||||||
@@ -865,6 +1025,13 @@ Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
|
|||||||
name: nginx
|
name: nginx
|
||||||
state: restarted
|
state: restarted
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Plain YAML — tasks run top to bottom against the "webservers" group.</li>
|
||||||
|
<li>become: true = run as root (sudo).</li>
|
||||||
|
<li>Handlers are the neat bit: restart nginx only if the config actually changed.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 26 : Ansible CLI ──────────────────────────────────── -->
|
<!-- ─── SLIDE 26 : Ansible CLI ──────────────────────────────────── -->
|
||||||
@@ -890,6 +1057,13 @@ changed: [finistdevs-web]
|
|||||||
PLAY RECAP ********************************************************************
|
PLAY RECAP ********************************************************************
|
||||||
finistdevs-web : ok=4 changed=3 unreachable=0 failed=0 skipped=0
|
finistdevs-web : ok=4 changed=3 unreachable=0 failed=0 skipped=0
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>"ok" vs "changed" — ok means already in desired state, changed means it acted. That's idempotence visible in the output.</li>
|
||||||
|
<li>The handler only fired because the config task reported "changed".</li>
|
||||||
|
<li>Run it again and everything would be "ok", changed=0.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 27 : Ansible operations ───────────────────────────── -->
|
<!-- ─── SLIDE 27 : Ansible operations ───────────────────────────── -->
|
||||||
@@ -901,6 +1075,13 @@ finistdevs-web : ok=4 changed=3 unreachable=0 failed=0 skipped=0
|
|||||||
<li>Run a compliance audit across your whole fleet</li>
|
<li>Run a compliance audit across your whole fleet</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><em>The go-to tool for one-off tasks and recurring operations.</em></p>
|
<p><em>The go-to tool for one-off tasks and recurring operations.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Ansible isn't only "set up a server once" — it shines for ad-hoc operations.</li>
|
||||||
|
<li>Imperative/orchestration angle: patching, rolling upgrades, canary, audits across the fleet.</li>
|
||||||
|
<li>This is where it differs most from Puppet's "always converge" model.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 28 : Ansible Galaxy ───────────────────────────────── -->
|
<!-- ─── SLIDE 28 : Ansible Galaxy ───────────────────────────────── -->
|
||||||
@@ -911,6 +1092,13 @@ finistdevs-web : ok=4 changed=3 unreachable=0 failed=0 skipped=0
|
|||||||
<li>Don't write a playbook to install Docker from scratch — someone already did</li>
|
<li>Don't write a playbook to install Docker from scratch — someone already did</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><em>Just <code>ansible-galaxy install geerlingguy.docker</code>.</em></p>
|
<p><em>Just <code>ansible-galaxy install geerlingguy.docker</code>.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Galaxy = the package registry for reusable roles/collections.</li>
|
||||||
|
<li>Don't reinvent common setups — pull a battle-tested role (geerlingguy is the famous example).</li>
|
||||||
|
<li>Huge productivity multiplier; just vet what you import.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 29 : Ansible platforms ────────────────────────────── -->
|
<!-- ─── SLIDE 29 : Ansible platforms ────────────────────────────── -->
|
||||||
@@ -922,12 +1110,25 @@ finistdevs-web : ok=4 changed=3 unreachable=0 failed=0 skipped=0
|
|||||||
<li><strong>Semaphore</strong> — lightweight open-source alternative</li>
|
<li><strong>Semaphore</strong> — lightweight open-source alternative</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><em>Core engine remains Apache 2.0 — truly open-source.</em></p>
|
<p><em>Core engine remains Apache 2.0 — truly open-source.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>At scale you want a UI/scheduler/RBAC layer on top of the CLI.</li>
|
||||||
|
<li>AWX (free) → Ansible Automation Platform (Red Hat, supported) is the main path; Semaphore is a lighter option.</li>
|
||||||
|
<li>Reassure: the core stays Apache 2.0 — no license rug-pull like Terraform.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 30 : Puppet intro ─────────────────────────────────── -->
|
<!-- ─── SLIDE 30 : Puppet intro ─────────────────────────────────── -->
|
||||||
<section class="s-puppet">
|
<section class="s-puppet">
|
||||||
<h2><span class="puppet-col">Puppet</span></h2>
|
<h2><span class="puppet-col">Puppet</span></h2>
|
||||||
<p>Your servers are configured. Now keep them that way.</p>
|
<p>Your servers are configured. Now keep them that way.</p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Third layer: enforcement. Configured once isn't enough — drift creeps back.</li>
|
||||||
|
<li>Puppet's job: keep state correct continuously, forever.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
@@ -940,6 +1141,12 @@ finistdevs-web : ok=4 changed=3 unreachable=0 failed=0 skipped=0
|
|||||||
<li>Puppet Inc. acquired by <strong>Perforce</strong> in 2022</li>
|
<li>Puppet Inc. acquired by <strong>Perforce</strong> in 2022</li>
|
||||||
<li>Written in <strong>Ruby</strong> and <strong>Clojure</strong></li>
|
<li>Written in <strong>Ruby</strong> and <strong>Clojure</strong></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>The oldest of the three (2005) — pioneered config management.</li>
|
||||||
|
<li>Now owned by Perforce. We'll touch on what that means for the community later.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 32 : Puppet concepts ──────────────────────────────── -->
|
<!-- ─── SLIDE 32 : Puppet concepts ──────────────────────────────── -->
|
||||||
@@ -950,6 +1157,13 @@ finistdevs-web : ok=4 changed=3 unreachable=0 failed=0 skipped=0
|
|||||||
<li>Compiles a catalog and enforces it locally</li>
|
<li>Compiles a catalog and enforces it locally</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><em>Drift is corrected automatically.</em></p>
|
<p><em>Drift is corrected automatically.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Opposite model to Ansible: pull, not push — agents reach out to the server.</li>
|
||||||
|
<li>Every ~30 min the agent fetches a catalog and converges the node — point at the diagram.</li>
|
||||||
|
<li>This is the key idea: enforcement runs on a loop, not just at deploy.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<svg width="750" height="250" viewBox="0 0 750 250" xmlns="http://www.w3.org/2000/svg" style="margin-top:0.5em;">
|
<svg width="750" height="250" viewBox="0 0 750 250" xmlns="http://www.w3.org/2000/svg" style="margin-top:0.5em;">
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="a-pull" viewBox="0 0 10 7" refX="9" refY="3.5" markerWidth="9" markerHeight="7" orient="auto">
|
<marker id="a-pull" viewBox="0 0 10 7" refX="9" refY="3.5" markerWidth="9" markerHeight="7" orient="auto">
|
||||||
@@ -1007,6 +1221,13 @@ class webserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Same nginx example as Ansible — compare the styles side by side.</li>
|
||||||
|
<li>Pure declarative: describe resources (package, file, service) and desired state, not steps.</li>
|
||||||
|
<li>notify chains the dependency: config change → restart service.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 34 : Puppet CLI ───────────────────────────────────── -->
|
<!-- ─── SLIDE 34 : Puppet CLI ───────────────────────────────────── -->
|
||||||
@@ -1027,6 +1248,13 @@ Notice: /Stage[main]/Webserver/Service[nginx]/ensure: started
|
|||||||
|
|
||||||
Notice: Applied catalog in 12.34 seconds
|
Notice: Applied catalog in 12.34 seconds
|
||||||
</code></pre>
|
</code></pre>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Agent run: fetch catalog → compare to actual → apply only the diffs.</li>
|
||||||
|
<li>This normally runs automatically every 30 min; -t is a manual trigger for demo.</li>
|
||||||
|
<li>Next run with no drift would report no changes.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 35 : Puppet drift detection ───────────────────────── -->
|
<!-- ─── SLIDE 35 : Puppet drift detection ───────────────────────── -->
|
||||||
@@ -1037,6 +1265,13 @@ Notice: Applied catalog in 12.34 seconds
|
|||||||
<li>Continuous compliance — not just at deploy time. <strong>Every. 30. Minutes.</strong></li>
|
<li>Continuous compliance — not just at deploy time. <strong>Every. 30. Minutes.</strong></li>
|
||||||
<li>No manual remediation</li>
|
<li>No manual remediation</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>The killer feature: self-healing. Someone hand-edits a file → Puppet reverts it next run.</li>
|
||||||
|
<li>Walk the loop in the diagram: drift detected → agent applies → compliant → repeat.</li>
|
||||||
|
<li>This is what "continuous compliance" means in practice.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<svg width="750" height="200" viewBox="0 0 750 200" xmlns="http://www.w3.org/2000/svg" style="margin-top:0.5em;">
|
<svg width="750" height="200" viewBox="0 0 750 200" xmlns="http://www.w3.org/2000/svg" style="margin-top:0.5em;">
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="a-cycle" viewBox="0 0 10 7" refX="9" refY="3.5" markerWidth="9" markerHeight="7" orient="auto">
|
<marker id="a-cycle" viewBox="0 0 10 7" refX="9" refY="3.5" markerWidth="9" markerHeight="7" orient="auto">
|
||||||
@@ -1079,6 +1314,13 @@ Notice: Applied catalog in 12.34 seconds
|
|||||||
<li>Fewer SaaS options than Terraform or Ansible</li>
|
<li>Fewer SaaS options than Terraform or Ansible</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><em>Puppet Enterprise and Foreman are self-hosted. No managed cloud offering.</em></p>
|
<p><em>Puppet Enterprise and Foreman are self-hosted. No managed cloud offering.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Sweet spot: large fleets of long-lived servers where drift control matters.</li>
|
||||||
|
<li>Trade-off vs the others: fewer SaaS options, more setup — it's self-hosted.</li>
|
||||||
|
<li>Honest framing: overkill for a handful of ephemeral cloud VMs.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 37 : Puppet community ─────────────────────────────── -->
|
<!-- ─── SLIDE 37 : Puppet community ─────────────────────────────── -->
|
||||||
@@ -1089,12 +1331,26 @@ Notice: Applied catalog in 12.34 seconds
|
|||||||
<li><strong>OpenVox</strong> — an emerging open-source fork of the Puppet core</li>
|
<li><strong>OpenVox</strong> — an emerging open-source fork of the Puppet core</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><em>The community is strong, with or without Puppet Inc.</em></p>
|
<p><em>The community is strong, with or without Puppet Inc.</em></p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Addresses the "is Puppet dying?" worry after the Perforce acquisition.</li>
|
||||||
|
<li>Vox Pupuli keeps the modules alive; OpenVox forks the core (echoes the OpenTofu story).</li>
|
||||||
|
<li>Reassurance: the open ecosystem outlives any single vendor.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 38 : They're complementary ────────────────────────── -->
|
<!-- ─── SLIDE 38 : They're complementary ────────────────────────── -->
|
||||||
<section>
|
<section>
|
||||||
<h2>They're not competing. They're complementary.</h2>
|
<h2>They're not competing. They're complementary.</h2>
|
||||||
<p>Each solves a different layer of the same problem.</p>
|
<p>Each solves a different layer of the same problem.</p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>The payoff slide — the "vs" framing was a trap. They stack.</li>
|
||||||
|
<li>Terraform provisions → Ansible configures → Puppet enforces. Three layers.</li>
|
||||||
|
<li>"Which should I use?" → depends which layer of the problem you have.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
<svg width="800" height="280" viewBox="0 0 800 280" xmlns="http://www.w3.org/2000/svg" style="margin-top:0.5em;">
|
<svg width="800" height="280" viewBox="0 0 800 280" xmlns="http://www.w3.org/2000/svg" style="margin-top:0.5em;">
|
||||||
<defs>
|
<defs>
|
||||||
<marker id="a-down" viewBox="0 0 10 10" refX="5" refY="10" markerWidth="10" markerHeight="10" orient="auto">
|
<marker id="a-down" viewBox="0 0 10 10" refX="5" refY="10" markerWidth="10" markerHeight="10" orient="auto">
|
||||||
@@ -1131,6 +1387,13 @@ Notice: Applied catalog in 12.34 seconds
|
|||||||
<li><span class="ansible-col">Ansible</span> configures it and deploys the app</li>
|
<li><span class="ansible-col">Ansible</span> configures it and deploys the app</li>
|
||||||
<li><span class="puppet-col">Puppet</span> continuously enforces compliance</li>
|
<li><span class="puppet-col">Puppet</span> continuously enforces compliance</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Concrete recap of how they fit together end to end.</li>
|
||||||
|
<li>You don't have to use all three — but they layer cleanly when you do.</li>
|
||||||
|
<li>Pick by your actual need: just provisioning? Terraform. Ad-hoc ops? Ansible. Drift control? Puppet.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- ─── SLIDE 40 : Closing ──────────────────────────────────────── -->
|
<!-- ─── SLIDE 40 : Closing ──────────────────────────────────────── -->
|
||||||
@@ -1142,6 +1405,13 @@ Notice: Applied catalog in 12.34 seconds
|
|||||||
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>
|
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>
|
</p>
|
||||||
<p class="meta" style="margin-top:1.5em; font-size:0.45em; color:var(--ods-neutral-600);">🤖 Made with Claude & GitHub Copilot</p>
|
<p class="meta" style="margin-top:1.5em; font-size:0.45em; color:var(--ods-neutral-600);">🤖 Made with Claude & GitHub Copilot</p>
|
||||||
|
<aside class="notes">
|
||||||
|
<ul>
|
||||||
|
<li>Thank the audience, point to the slides URL.</li>
|
||||||
|
<li>Open the floor for questions.</li>
|
||||||
|
<li>Backup topics if quiet: Kubernetes operators, Pulumi, secrets management.</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user