<aside> 🧭

Module 02 · Variables, Facts & Precedence

This is where mid-level and senior candidates get separated. Anyone can write a variable; far fewer can explain which of six definitions actually wins, and why.

🧠 concept → 🧪 exercise → ✅ expected result (hidden) → 🎯 interview questions (answers hidden)

Prerequisite: Module 01. You have already met ansible_distribution (Exercise C2.1) and inventory_hostname (Exercise D4.1) — this module explains what they actually are.

</aside>


Part A · Variables from the ground up

A1 · What a variable is, and how you reference it

<aside> 📖

Official docs: Using Variables · Templating (Jinja2)

</aside>

<aside> 🫙

The analogy. Think of a labelled jar in your kitchen. The jar says sugar on the front, and when you write the recipe you deliberately do not care what is inside it — you write "add the contents of the sugar jar" and move on. Someone else can fill that jar with white sugar or brown, and your recipe still works without a single edit.

That is the whole point of a variable, and it is why one playbook can serve both staging and production. The {{ }} braces mean "go and look inside this jar, and put whatever you find right here". Keep the jar in mind — it comes back in D5, when the jar labelled sugar turns out to be full of salt.

</aside>

Ansible variables are referenced with Jinja2 templating syntax — double curly braces:

vars:
  http_port: 8080
  app_name: checkout

tasks:
  - name: Show the config
    ansible.builtin.debug:
      msg: "{{ app_name }} listens on port {{ http_port }}"

The naming rules — stricter than people expect

Rule Detail
Letters, digits, underscores only app_port ✅ · app-port ❌ · app.port ❌ · app port
Must not start with a digit 2nd_server ❌ · server_2
Must not collide with a Python keyword async, lambda, import are all invalid
Must not collide with a playbook keyword environment, name, hosts — technically allowed but they shadow real keywords and cause baffling bugs

<aside> ⚠️

Hyphens are the trap. app-port looks perfectly reasonable and is invalid — Jinja2 reads it as app minus port. You will not always get a clear error; sometimes you get a silent wrong value. Underscores, always.

</aside>

The quoting rule you cannot skip

msg: {{ http_port }}          # ❌ YAML sees a line starting with { and expects a dictionary
msg: "{{ http_port }}"        # ✅ quote it
msg: "Port is {{ http_port }}"  # ✅ fine either way, but quote for consistency
when: http_port == 8080       # ✅ NO braces here - when: is already a Jinja2 expression
when: "{{ http_port }} == 8080"  # ⚠️ works but is wrong style, and ansible-lint flags it

<aside> 🔑

The rule in one line: if a value starts with {{, quote the whole thing. And never put {{ }} inside when:, because when: is evaluated as a Jinja2 expression already — the braces are redundant and can break on complex expressions.

</aside>

🧪 Exercise A1.1 — Break the quoting rule on purpose

---
- name: Quoting demo
  hosts: localhost
  gather_facts: false
  vars:
    http_port: 8080
  tasks:
    - name: This one fails
      ansible.builtin.debug:
        msg: {{ http_port }}

🎯 Interview questions — Variable basics