16 KiB
Ansible
- TL;DR
- Templating
- Roles
- Output formatting
- Troubleshooting
- Print all known variables
- Force notified handlers to run at a specific point
- Run specific tasks even in check mode
- Dry-run only specific tasks
- Set up recursive permissions on a directory so that directories are set to 755 and files to 644
- Only run a task when another has a specific result
- Define when a task changed or failed
- Set environment variables for a play, role or task
- Set variables to the value of environment variables
- Check if a list contains an item and fail otherwise
- Define different values for
true/false/null - Force a task or play to use a specific Python interpreter
- Further readings
- Sources
TL;DR
# Install.
pip3 install --user ansible && port install sshpass # darwin
sudo pamac install ansible sshpass # manjaro linux
# Show hosts' ansible facts.
ansible -i hostfile -m setup all
ansible -i host1,hostn, -m setup host1 -u remote-user
ansible -i localhost, -c local -km setup localhost
# Check the syntax of a playbook.
# This will *not* execute the plays inside it.
ansible-playbook path/to/playbook.yml --syntax-check
# Execute a playbook.
ansible-playbook path/to/playbook.yml -i hosts.list
ansible-playbook path/to/playbook.yml -i host1,host2,hostn, -l hosts,list
ansible-playbook path/to/playbook.yml -i host1,host2,other, -l hosts-pattern
# Show what changes (with details) a play whould apply to the local machine.
ansible-playbook path/to/playbook.yml -i localhost, -c local -vvC
# Only execute tasks with specific tags.
ansible-playbook path/to/playbook.yml --tags "configuration,packages"
# Avoid executing tasks with specific tags.
ansible-playbook path/to/playbook.yml --skip-tags "system,user"
# Check what tasks will be executed.
ansible-playbook example.yml --list-tasks
ansible-playbook example.yml --list-tasks --tags "configuration,packages"
ansible-playbook example.yml --list-tasks --skip-tags "system,user"
# List roles installed from Galaxy.
ansible-galaxy list
# Install roles from Galaxy.
ansible-galaxy install namespace.role
ansible-galaxy install --roles-path ~/ansible-roles namespace.role
ansible-galaxy install namespace.role,v1.0.0
ansible-galaxy install git+https://github.com/namespace/role.git,commit-hash
ansible-galaxy install -r requirements.yml
# Remove roles installed from Galaxy.
ansible-galaxy remove namespace.role
Templating
Ansible leverages Jinja2 templating, which can be used directly in tasks or through the template module.
All Jinja2's standard filters and tests can be used, with the addition of:
- specialized filters for selecting and transforming data
- tests for evaluating template expressions
- lookup plugins for retrieving data from external sources for use in templating
All templating happens on the Ansible controller, before the task is sent and executed on the target machine.
Updated [examples] are available.
# Remove empty or false values from a list piping it to 'select()'.
# Returns ["string"].
- vars:
list: ["", "string", 0, false]
ansible.builtin.debug:
var: list | select
# Remove only empty strings from a list 'reject()'ing them.
# Returns ["string", 0, false].
- vars:
list: ["", "string", 0, false]
ansible.builtin.debug:
var: list | reject('match', '^$')
# Merge two lists.
# Returns ["a", "b", "c", "d"].
- vars:
list1: ["a", "b"]
list2: ["c", "d"]
ansible.builtin.debug:
var: list1 + list2
# Dedupe elements in a list.
# Returns ["a", "b"].
- vars:
list: ["a", "b", "b", "a"]
ansible.builtin.debug:
var: list | unique
# Sort a list by version number (not lexicographically).
# Returns ['2.7.0', '2.8.0', '2.9.0', '2.10.0' '2.11.0'].
- vars:
list: ['2.8.0', '2.11.0', '2.7.0', '2.10.0', '2.9.0']
ansible.builtin.debug:
var: list | community.general.version_sort
# Generate a random password.
# Returns a random string following the specifications.
- vars:
password: "{{ lookup('password', '/dev/null length=32 chars=ascii_letters,digits,punctuation') }}"
ansible.builtin.debug:
var: password
# Hash a password.
# Returns a hash of the requested type.
- vars:
password: abcd
salt: "{{ lookup('community.general.random_string', special=false) }}"
ansible.builtin.debug:
var: password | password_hash('sha512', salt)
# Get a variable's type.
- ansible.builtin.debug:
var: "'string' | type_debug"
Tests
Return a boolean result.
# Compare semver version numbers.
- ansible.builtin.debug:
var: "'2.0.0-rc.1+build.123' is version('2.1.0-rc.2+build.423', 'ge', version_type='semver')"
Loops
# Get the values of some special variables.
# See the 'Further readings' section for the full list.
- ansible.builtin.debug:
var: "{{ item }}"
with_items: ["ansible_local", "playbook_dir", "role_path"]
# Fail when any of the given variables is an empty string.
# Returns the ones which are empty.
- when: lookup('vars', item) == ''
ansible.builtin.fail:
msg: "The {{ item }} variable is an empty string"
loop:
- variable1
- variableN
# Iterate thrugh nested loops.
- vars:
middles:
- 'middle1'
- 'middle2'
ansible.builtin.debug:
msg: "{{ item[0] }}, {{ item[1] }}, {{ item[2] }}"
with_nested:
- ['outer1', 'outer2']
- "{{ middles }}"
- ['inner1', 'inner2']
Roles
Get roles
Roles can be either created:
ansible-galaxy init role-name
or installed from Ansible Galaxy:
---
# requirements.yml
collections:
- community.docker
ansible-galaxy install mcereda.boinc_client
ansible-galaxy install --roles-path ~/ansible-roles namespace.role
ansible-galaxy install namespace.role,v1.0.0
ansible-galaxy install git+https://github.com/namespace/role.git,0b7cd353c0250e87a26e0499e59e7fd265cc2f25
ansible-galaxy install -r requirements.yml
Role dependencies
---
# role/meta/main.yml
dependencies:
- role: common
vars:
some_parameter: 3
- role: postgres
vars:
dbname: blarg
other_parameter: 12
Output formatting
Introduced in Ansible 2.5
Change Ansible's output setting the stdout callback to json or yaml:
ANSIBLE_STDOUT_CALLBACK=yaml
# ansible.cfg
[defaults]
stdout_callback = json
yaml will set tasks output only to be in the defined format:
$ ANSIBLE_STDOUT_CALLBACK=yaml ansible-playbook --inventory=localhost.localdomain, ansible/localhost.configure.yml -vv --check
PLAY [Configure localhost] *******************************************************************
TASK [Upgrade system packages] ***************************************************************
task path: /home/user/localhost.configure.yml:7
ok: [localhost.localdomain] => changed=false
cmd:
- /usr/bin/zypper
- --quiet
- --non-interactive
…
update_cache: false
The json output format will be a single, long JSON file:
$ ANSIBLE_STDOUT_CALLBACK=yaml ansible-playbook --inventory=localhost.localdomain, ansible/localhost.configure.yml -vv --check
{
"custom_stats": {},
"global_custom_stats": {},
"plays": [
{
"play": {
…
"name": "Configure localhost"
},
"tasks": [
{
"hosts": {
"localhost.localdomain": {
…
"action": "community.general.zypper",
"changed": false,
…
"update_cache": false
}
}
…
…
}
Troubleshooting
Print all known variables
Print the special variable vars as a task:
- name: Debug all variables
ansible.builtin.debug: var=vars
Force notified handlers to run at a specific point
Use the meta plugin with the flush_handlers option:
- name: Force all notified handlers to run at this point, not waiting for normal sync points
ansible.builtin.meta: flush_handlers
Run specific tasks even in check mode
Add the check_mode: false pair to the task:
- name: this task will make changes to the system even in check mode
check_mode: false
ansible.builtin.command: /something/to/run --even-in-check-mode
Dry-run only specific tasks
Add the check_mode: true pair to the task:
- name: This task will always run under checkmode and not change the system
check_mode: true
ansible.builtin.lineinfile:
line: "important file"
dest: /path/to/file.conf
state: present
Set up recursive permissions on a directory so that directories are set to 755 and files to 644
Use the special X mode setting in the file plugin:
- name: Fix files and directories' permissions
ansible.builtin.file:
dest: /path/to/some/dir
mode: u=rwX,g=rX,o=rX
recurse: yes
Only run a task when another has a specific result
When a task executes, it also stores the two special values changed and failed in its results. You can use those as conditions to execute the next ones:
- name: Trigger task
ansible.builtin.command: any
register: trigger_task
ignore_errors: true
- name: Run only on change
when: trigger_task.changed
ansible.builtin.debug: msg="The trigger task changed"
- name: Run only on failure
when: trigger_task.failed
ansible.builtin.debug: msg="The trigger task failed"
Alternatively, you can use special checks built for this:
- name: Run only on success
when: trigger_task is succeeded
ansible.builtin.debug: msg="The trigger task succeeded"
- name: Run only on change
when: trigger_task is changed
ansible.builtin.debug: msg="The trigger task changed"
- name: Run only on failure
when: trigger_task is failed
ansible.builtin.debug: msg="The trigger task failed"
- name: Run only on skip
when: trigger_task is skipped
ansible.builtin.debug: msg="The trigger task skipped"
Define when a task changed or failed
This lets you avoid using ignore_errors.
Use the changed_when and failed_when attributes to define your own conditions:
- name: Task with custom results
ansible.builtin.command: any
register: result
changed_when:
- result.rc == 2
- result.stderr | regex_search('things changed')
failed_when:
- result.rc != 0
- not (result.stderr | regex_search('all good'))
Set environment variables for a play, role or task
Environment variables can be set at a play, block, or task level using the environment keyword:
- name: Use environment variables for a task
environment:
HTTP_PROXY: http://example.proxy
ansible.builtin.command: curl ifconfig.io
The environment keyword does not affect Ansible itself or its configuration settings, the environment for other users, or the execution of other plugins like lookups and filters; variables set with environment do not automatically become Ansible facts, even when set at the play level.
Set variables to the value of environment variables
Use the lookup() plugin with the env option:
- name: Use a local environment variable
ansible.builtin.debug: msg="HOME={{ lookup('env', 'HOME') }}"
Check if a list contains an item and fail otherwise
- name: Check if a list contains an item and fail otherwise
when: item not in list
ansible.builtin.fail: msg="item not in list"
Define different values for true/false/null
Create a test and define two values: the first will be returned when the test returns true, the second will be returned when the test returns false (Ansible 1.9+):
{{ (ansible_pkg_mgr == 'zypper') | ternary('gnu_parallel', 'parallel')) }}
Since Ansible 2.8 you can define a third value to be returned when the test returns null:
{{ autoscaling_enabled | ternary(true, false, omit) }}
Force a task or play to use a specific Python interpreter
Just set it in the Play's or Task's variables:
vars:
ansible_python_interpreter: /usr/local/bin/python3.9
Further readings
- Templating
- Templating examples
- Roles
- Tests
- Special variables
- Automating Helm using Ansible
- Edit .ini file in other servers using Ansible PlayBook
- Yes and No, True and False
- Ansible Galaxy user guide
- Windows playbook example
Sources
- Removing empty values from a list and assigning it to a new list
- Human-Readable Output Format
- How to append to lists
- Check if a list contains an item in ansible
- Working with versions
- How to install SSHpass on Mac
- Include task only if file exists
- Unique filter of list in jinja2
- Only do something if another action changed
- How to recursively set directory and file permissions