Many Ansible users encounter this frustrating behavior: when referencing undefined variables in playbooks, Ansible silently replaces them with empty strings instead of failing. This default behavior can lead to subtle bugs that only surface during runtime, especially when dealing with mandatory configuration parameters.
In production environments, undefined variables typically indicate configuration errors that should fail fast rather than continue with potentially dangerous defaults. Consider this common scenario:
# playbook.yml
- name: Configure web server
hosts: webservers
vars_files:
- config.yml
tasks:
- name: Ensure Apache is installed
apt:
name: apache2
state: present
when: install_apache
If install_apache
is undefined, the task silently skips rather than alerting you to the missing configuration.
Ansible provides several mechanisms to make variable validation more strict:
# ansible.cfg
[defaults]
error_on_undefined_vars = True
# Alternative per-playbook approach
- name: Strict variable checking
hosts: all
vars:
required_vars:
- db_host
- db_port
- api_key
tasks:
- name: Validate required variables
assert:
that: lookup('vars', item) is defined
fail_msg: "Required variable '{{ item }}' is missing"
with_items: "{{ required_vars }}"
For more sophisticated validation, combine these approaches:
# roles/common/tasks/validate_vars.yml
- name: Check for undefined mandatory variables
fail:
msg: "Mandatory variable '{{ item.key }}' is not defined"
when: item.value is undefined
with_dict: "{{ mandatory_vars }}"
# In your playbook
vars:
mandatory_vars:
db_connection: "{{ db_host }}:{{ db_port }}"
secret_key: "{{ vault_secret_key }}"
Remember that Ansible's variable precedence affects these checks. A variable might be defined at a higher precedence level than where you're checking. The default
filter can help distinguish between intentionally empty values and truly undefined ones:
- name: Validate with default filter
assert:
that: "{{ some_var | default('THIS_IS_UNDEFINED') != 'THIS_IS_UNDEFINED' }}"
fail_msg: "some_var must be defined (empty is allowed)"
When working with Ansible playbooks, you might have noticed that referencing undefined variables doesn't cause the playbook to fail by default. Instead, Ansible silently substitutes them with empty strings. This behavior can lead to:
- Hidden configuration issues
- Unexpected application behavior
- Difficult-to-debug problems
To make Ansible fail when encountering undefined variables, you have several options:
1. Global Configuration
Add this to your ansible.cfg
:
[defaults] error_on_undefined_vars = True
2. Playbook-Level Option
Set it at the playbook level:
- hosts: all vars: required_var: "{{ undefined_var }}" tasks: - debug: msg: "This will fail if undefined_var is not set" vars_prompt: - name: undefined_var prompt: "Enter a value for undefined_var" private: no
3. Using the mandatory
Filter
For more granular control:
- hosts: all tasks: - debug: msg: "{{ undefined_var | mandatory }}"
Consider this problematic playbook:
- name: Configure web server hosts: webservers vars: http_port: "{{ web_port }}" tasks: - name: Ensure Apache is running service: name: httpd state: started enabled: yes
If web_port
is undefined, the playbook will run with an empty string. To fix this:
- name: Configure web server hosts: webservers vars: http_port: "{{ web_port | mandatory('web_port must be defined') }}" tasks: - name: Ensure Apache is running service: name: httpd state: started enabled: yes
For complex scenarios, you can combine these approaches:
- name: Validate all required variables hosts: localhost connection: local gather_facts: no tasks: - name: Check required variables assert: that: - "required_var1 is defined" - "required_var2 is defined" fail_msg: "Missing required variables"
- Always validate variables early in your playbook
- Use descriptive error messages with
mandatory
- Consider using
default()
filter for optional variables - Document all required variables in playbook comments