Ansible Variables
Variables are what transform static playbooks into dynamic, reusable automation. Without variables, you would write a separate playbook for each environment, each server, and each configuration variation. With variables, you write one playbook and adjust its behaviour through data. This lesson covers every practical variable mechanism you need for professional Ansible work.
Defining Variables Inline with vars
---
- name: Deploy application
hosts: webservers
vars:
app_name: myapp
app_port: 8080
app_user: deploy
app_directory: /opt/{{ app_name }}
tasks:
- name: Create app directory
file:
path: "{{ app_directory }}"
state: directory
owner: "{{ app_user }}"The vars: block is the simplest way to define play-scoped variables. Note that variables can reference other variables: app_directory: /opt/{{ app_name }}. This is evaluated at runtime, so app_directory becomes /opt/myapp.
Variable Files with vars_files
As the number of variables grows, keeping them in the playbook becomes unwieldy. The vars_files directive loads variables from external YAML files:
---
- name: Deploy application
hosts: webservers
vars_files:
- vars/common.yml
- vars/{{ environment }}.yml
tasks: ...Contents of vars/common.yml:
app_name: myapp app_user: deploy log_level: INFO
Contents of vars/production.yml:
app_port: 443 db_host: prod-db.internal ssl_enabled: true log_level: WARNING
The dynamic file name vars/{{ environment }}.yml loads a different file based on the environment variable, which could be passed at runtime with -e environment=production. This pattern is one of the cleanest ways to manage environment-specific configuration.
Registering Task Output as Variables
The register keyword captures a task's return value and stores it as a variable for use in subsequent tasks:
- name: Check if application is running
command: systemctl is-active myapp
register: app_status
ignore_errors: true
- name: Start application if it is not running
service:
name: myapp
state: started
when: app_status.rc != 0The registered variable app_status is a dictionary containing the complete module return data. Common fields: .stdout, .stderr, .rc (return code), .changed, .failed. Use debug: var=app_status in a task to inspect the full structure of a registered variable.
Passing Variables at Runtime with -e
ansible-playbook deploy.yml -e "environment=production version=2.5.1" ansible-playbook deploy.yml -e "@production-vars.yml"
The -e flag passes extra variables at the highest precedence level — they override everything else. The @ syntax loads extra variables from a file. Runtime variables are ideal for things that change per-deployment: version numbers, deployment targets, feature flags.
Magic Variables
Ansible provides built-in variables always available in playbooks:
inventory_hostname— Current host name from inventoryansible_facts— Dictionary of all system factshostvars['hostname']['var_name']— Access another host's variablegroups['groupname']— List of hosts in a groupgroup_names— List of groups the current host belongs to
The debug Module for Variable Inspection
- name: Show variable values
debug:
var: app_directory
- name: Show a formatted message
debug:
msg: "Deploying {{ app_name }} version {{ version }} to {{ inventory_hostname }}"The debug module is your primary tool for understanding what variable values look like at runtime. Insert debug tasks freely during development; remove or comment them out before committing to version control.
Try This: Environment-Aware Playbook
Create a playbook that loads a different vars_files file based on an env variable. Create two files: vars/dev.yml with http_port: 8080 and vars/prod.yml with http_port: 443. Run the playbook twice: once with -e env=dev and once with -e env=prod. Add a debug task that prints the port. Verify the correct port appears in each run.
Summary
Variables make playbooks dynamic and reusable. The vars block defines inline play-scoped variables. vars_files loads variables from external YAML files, supporting dynamic file selection for environment-specific configuration. The register keyword captures task output for conditional logic. Runtime variables passed with -e override all other sources. The debug module reveals variable values at runtime for troubleshooting.
