feat: add speaker notes to all 40 slides

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Arnaud Prémel-Cabic
2026-06-01 10:37:33 +02:00
parent 07ca8f2e3d
commit f8dc80027f

View File

@@ -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 &amp; apply from pull requests</li> <li><strong>Atlantis</strong> — open-source, plan &amp; apply from pull requests</li>
<li><strong>env0, Scalr</strong> — SaaS with policy &amp; cost management</li> <li><strong>env0, Scalr</strong> — SaaS with policy &amp; 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 &amp; GitHub Copilot</p> <p class="meta" style="margin-top:1.5em; font-size:0.45em; color:var(--ods-neutral-600);">🤖 Made with Claude &amp; 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>