Make playbooks reusable with variables — defined inline, in files, and per host or group — and use facts, the data Ansible auto-discovers about each machine.
Why: variables keep values out of tasks so one playbook serves many cases. Define them in a vars block and reference them with {{ name }} — the Jinja2 expansion syntax. Always quote a value that starts with {{ so YAML does not mistake it for a dictionary.
- name: Use variables
hosts: local
vars:
app_name: myapp
app_dir: /tmp/myapp
tasks:
- name: Create the app directory
ansible.builtin.file:
path: "{{ app_dir }}"
state: directory
- name: Drop a marker file
ansible.builtin.copy:
dest: "{{ app_dir }}/{{ app_name }}.txt"
content: "app: {{ app_name }}\n"Why: real inventories vary per host and group — a different port here, a different domain there. Ansible auto-loads variables from group_vars/<group>.yml and host_vars/<host>.yml next to your inventory. This keeps environment differences out of the playbook entirely; the playbook stays generic.
# group_vars/web.yml — applies to every host in the [web] group
http_port: 80
worker_processes: 4
# host_vars/web1.example.com.yml — just this host
http_port: 8080Why: before running tasks, Ansible gathers "facts" — the OS, IP addresses, CPU count, memory, and much more — and exposes them as ansible_facts (and handy top-level names). Use them to make playbooks adapt to each machine instead of hardcoding. Run the setup module to see them all.
- name: Use gathered facts
hosts: local
tasks:
- name: Show OS and memory
ansible.builtin.debug:
msg: >-
This is {{ ansible_facts['distribution'] }}
{{ ansible_facts['distribution_version'] }}
with {{ ansible_facts['memtotal_mb'] }} MB RAMWhy: register captures the output of a task into a variable so a later task can use it. The registered value holds the command output, return code, and whether it changed — the basis for conditionals (next lesson). This is how tasks pass information down the playbook.
- name: Capture and reuse output
hosts: local
tasks:
- name: Check the kernel version
ansible.builtin.command: uname -r
register: kernel
changed_when: false # a read-only command never "changes" anything
- name: Report it
ansible.builtin.debug:
msg: "Kernel is {{ kernel.stdout }}"