Ansible
Ansible is declarative convergence over SSH: describe the end state and let it make reality match, safely re-runnable. Where Terraform provisions infra, Ansible configures it.
sudo apt install -y ansible). You can target localhost, or reuse the Multipass VMs from the Linux module as an inventory.Ansible pushes over SSH (or local) and reports back. Start with the simplest possible action.
Ping the local connection through Ansible.
$ ansible localhost -m pingYou get "ping": "pong" — Ansible reached the host and ran a module.
Reveal solution
$ ansible localhost -m pingThe core idea: a playbook describes an end state, not steps. Run it again and nothing changes — changed=0. This is what makes automation safe to repeat.
Write a playbook that ensures a file exists, then run it twice.
$ ansible-playbook site.yml # second runFirst run: changed=1. Second run: changed=0 — the end state already holds. That's idempotency.
Reveal solution
$ cat > site.yml <<EOF - hosts: localhost tasks: - name: ensure file copy: { content: "managed", dest: "/tmp/managed.txt" } EOF $ ansible-playbook site.yml $ ansible-playbook site.yml # changed=0
Configs are generated from templates + variables, so one playbook fits many hosts.
Render a template that injects a variable into a file.
$ cat /tmp/rendered.confThe rendered file contains your variable's value — one template, many possible outputs.
Reveal solution
$ echo "greeting = {{ msg }}" > tmpl.j2 $ cat > tpl.yml <<EOF - hosts: localhost vars: { msg: "hello from ansible" } tasks: - template: { src: tmpl.j2, dest: /tmp/rendered.conf } EOF $ ansible-playbook tpl.yml $ cat /tmp/rendered.conf
Reach engineers who read the man page
Native, contextual, no tracking — this is how the curriculum stays free.
Using command/shell for everything breaks idempotency — it runs every time and always reports changed. The fix is a proper module or a creates: guard.
Write a task that always reports changed, then make it idempotent.
$ ansible-playbook bad.yml # second run still changed — then fix and re-checkAdding creates: (or switching to a state-based module) makes the second run report changed=0. Idempotency restored.
Reveal solution
# bad: - shell: touch /tmp/x (always changed) # good: - shell: touch /tmp/x # args: { creates: /tmp/x } (changed only once)
Push over SSH, idempotent end-state tasks, templating — Ansible converges hosts safely and repeatably. Terraform builds the servers; Ansible configures them; both plug into your GitHub Actions pipelines.