NetAuto Academy

Ansible for Network Engineers

Variables, Jinja2, and the template Module

Intermediate13 min read

What you'll learn

  • That Ansible variables use the exact {{ }} Jinja2 syntax already learned
  • How playbook vars:, group_vars, and host_vars supply values to a template
  • How to use the template module to render the same vlan_config.j2 file from Phase 2.9
  • That the underlying rendering behavior is identical — only the caller changed
🎓 Teach This Lesson

Listen to a guided explanation of this lesson.

Spoken narration isn't supported in this browser.

You already know this syntax

Every Ansible playbook variable is written {{ variable_name }}. That is not Ansible's own invention — it's the exact Jinja2 expression syntax "Jinja2 Fundamentals Through Network Examples" (Configuration & Template Automation, Lesson 2) already taught.

Configuration & Template Automation
  1. Python calls Template.render()directly, in a script you wrote
  2. {{ hostname }}, {{ vlan.id }}Jinja2 expressions
This lesson
  1. Ansible's template module calls Jinja2 for you
  2. {{ hostname }}, {{ vlan.id }}the exact same syntax
Quick checkConcept check
Where does Ansible's {{ }} variable syntax actually come from?

Where playbook variables come from

Variables can be set directly in a play's vars: block, or in separate group_vars/host_vars files keyed by group or hostname:

group_vars/switches.yml
vlans:
- { id: 10, name: USERS }
- { id: 20, name: VOICE }

Any host in the 'switches' group automatically picks up this vlans variable — no need to repeat it in every playbook that uses it. This is the same list-of-dicts shape from Configuration & Template Automation Lesson 3, now living in an Ansible variables file instead of a Python dict.

Quick checkIdentify the correct code
A variable named vlans is defined in group_vars/switches.yml. Which playbook line correctly references its second item's id?

The same template file, a new caller

Here is the exact vlan_config.j2 file from Configuration & Template Automation's capstone — unchanged, byte for byte:

templates/vlan_config.j2
{% for vlan in vlans %}
vlan {{ vlan.id }}
name {{ vlan.name }}
{% endfor %}

Identical to the file used in Configuration & Template Automation Lesson 6. Nothing about the template itself changes — only what calls it does.

Ansible's template module renders a .j2 file exactly the way Environment(...).get_template(...).render(...) did in Lesson 5 of that module — just invoked declaratively, as a task, instead of a Python function call:

render_vlans.yml
- name: Render VLAN configuration from a template
hosts: localhost
connection: local
gather_facts: false

vars:
  vlans:
    - { id: 10, name: USERS }
    - { id: 20, name: VOICE }

tasks:
  - name: Render vlan_config.j2 for this device
    ansible.builtin.template:
      src: templates/vlan_config.j2
      dest: /tmp/SW1_vlans.cfg

src points at the same .j2 file; dest is where the rendered result is written. hosts: localhost and connection: local mean this genuinely runs with no network device — a real, deterministic Option B example.

This rendering was directly verified — the CLI wrapper around it wasn't

The vlan_config.j2 file above, and the exact VLAN data used throughout this module, were re-run through the same Jinja2 environment (trim_blocks=True, lstrip_blocks=True) verified in Configuration & Template Automation — confirmed to produce byte-identical output for every device in the fleet. Ansible's own template module is documented to use these same Jinja2 semantics internally. Running this specific playbook through the real ansible-playbook command was not independently executed in this authoring environment (see Lesson 2) — but the rendering logic underneath it is the same logic already proven correct.

/tmp/SW1_vlans.cfg — the verified rendered output for SW1's data
vlan 10
name USERS
vlan 20
name VOICE

This is the exact, directly-verified output for SW1's two required VLANs — identical to what Configuration & Template Automation's own script produced for the same device and the same template.

Quick checkPredict output
Using the same vlan_config.j2 file, what would the rendered output be for a device whose vlans variable is [{id: 10, name: USERS}] — just one VLAN?
Quick checkScenario
A playbook uses the template module to render vlan_config.j2, but the vlans variable was never defined anywhere the playbook can see it. Based on what Configuration & Template Automation Lesson 5 already established about Jinja2's default behavior, what's the most likely result?

Key takeaways

  • Ansible's {{ }} variable syntax is the exact Jinja2 already taught in Configuration & Template Automation — not a new templating language.
  • Playbook variables come from a play's vars:, or from group_vars/host_vars files keyed by group or hostname.
  • The template module renders a .j2 file exactly the way a Python script's Template.render() call already did — same file, same output, different caller.
  • The rendering logic itself was directly re-verified against Configuration & Template Automation's own proven output; running it through the real ansible-playbook CLI was not independently executed in this authoring environment.
  • Jinja2's silent-empty behavior for a missing variable (Configuration & Template Automation Lesson 5) applies here too — 'rendered successfully' still never means 'correct.'