Ansible Reference Card
Contents
- 1 Overview
- 2 Setup Ansible
- 3 Working with Playbooks
- 4 Variables and Inclusions
- 5 Task Control
- 6 Jinja2 Templates
- 7 File Handling
- 8 Working with Roles
- 9 Optimizing Ansible
- 10 Ansible Vault
- 11 Troubleshooting Ansible
1 Overview
Ansible Command | Ansible Navigator Equivalent |
---|---|
Inventory Commands | |
ansible-inventory -i inventory --list | ansible-navigator inventory -i inventory --list -m stdout |
ansible-inventory -i inventory --graph | ansible-navigator inventory -i inventory --graph -m stdout |
Vault Commands (Mostly Local, Remain the Same) | |
ansible-vault create secret.yml | ansible-vault create secret.yml (Remains the same) |
ansible-vault encrypt secret.yml | ansible-vault encrypt secret.yml (Remains the same) |
ansible-vault decrypt secret.yml | ansible-vault decrypt secret.yml (Remains the same) |
ansible-vault view secret.yml | ansible-vault view secret.yml (Remains the same) |
Playbook Commands | |
ansible-playbook playbook.yml | ansible-navigator run playbook.yml -m stdout |
ansible-playbook -i inventory playbook.yml | ansible-navigator run -i inventory playbook.yml -m stdout |
ansible-playbook -b playbook.yml | ansible-navigator run -b playbook.yml -m stdout |
ansible-playbook -e "var=value" playbook.yml | ansible-navigator run -e "var=value" playbook.yml -m stdout |
Ad-Hoc Commands | |
ansible all -m ping | ansible-navigator exec "ansible all -m ping" -m stdout |
ansible localhost -m setup | ansible-navigator exec "ansible localhost -m setup" -m stdout ansible-navigator exec "ansible localhost -m setup -a filter=*ipv4* webserver" |
ansible localhost -m setup | ansible-navigator exec "ansible localhost -m setup" -m stdout ansible-navigator exec "ansible localhost -m setup -a filter=*ipv4* webserver" |
ansible webservers -a "df -h" | ansible-navigator exec "ansible webservers -a 'df -h'" -m stdout |
Console Commands | |
ansible-console -b linux_servers | ansible-navigator exec "ansible-console -b linux_servers" -m stdout |
Configuration and Documentation | |
ansible-config dump | ansible-navigator config -m stdout |
ansible-doc -l | ansible-navigator doc -l -m stdout |
ansible-doc -s module_name | ansible-navigator doc module_name -m stdout |
- bashrc example with aliases
# Alias for ansible-playbook alias nansible-playbook="ansible-navigator run -m stdout" # Alias for ansible (Ad-Hoc Commands) alias nansible="function _nansible() { ansible-navigator exec -m stdout \"ansible \$@\"; }; _nansible" # Alias for ansible-inventory alias nansible-inventory="ansible-navigator inventory -m stdout" # Alias for ansible-config alias nansible-config="ansible-navigator config -m stdout" # Alias for ansible-doc alias nansible-doc="ansible-navigator doc -m stdout" # Alias for ansible-vault (Remains the same) alias nansible-vault="ansible-vault" # Alias for ansible-galaxy alias nansible-galaxy="ansible-navigator galaxy -m stdout" # Alias for ansible-test (if applicable) alias nansible-test="ansible-navigator test -m stdout"
1.2 Inventory
1.2.1 Documentation
- show all inventory plugins
ansible-doc -t inventory -l
- show ini style inventory (default)
ansible-doc -t inventory ini
- adoc, documentation helper
curl -O $HOME/bin/adoc https://raw.githubusercontent.com/joe-speedboat/shell.scripts/master/adoc.sh chmod 755 $HOME/bin/adoc
1.2.2 Export a uniq list of all hosts in inventory
ansible-inventory --list | jq -r '..|.hosts?|arrays|.[]' | sort | uniq
1.2.3 Examples
/etc/ansible/hosts
[apache] web[01:05] ansible_user=devops [nginx] web[10:12] web13 ansible_port=222 has_java = False 10.0.1.[250:253] [nginx:vars] http_port=8080 [webservers:children] nginx apache
1.2.4 Dynamic Inventory Example
cat inventory/dynamic-inventory.sh #!/bin/bash echo '{ "web": ["www1", "www2", "www3"], "db": ["db1", "db2", "oracle1"] }'
cat inventory/hosts [sudo] vm20 ansible_user=ansible
chmod 700 inventory/dynamic-inventory.sh ls -l inventory/
-rwxr-xr-x. 1 root root 94 Dec 14 15:01 dynamic-inventory.sh -rw-r--r--. 1 root root 34 Dec 14 15:03 hosts
</source>
[root@ansible lab]# ansible -i inventory/ --list-hosts web hosts (3): www1 www2 www3 [root@ansible lab]# ansible -i inventory/ --list-hosts sudo hosts (1): vm20
1.2.5 Dynamic Inventory via DNS Zone Transfer
1.2.6 Commands
- query inventory for specific hosts
ansible web01 --list-hosts ansible 'all:!kvm' -i /etc/ansible/hosts --list-hosts
- show host involved by playbook
ansible-playbook --list-hosts tests/qa.yml
- run module on specific host
ansible vm06 -m setup
1.2.7 cmd export/import
ansible-inventory --list --export --yaml > i.yaml ansible --list-hosts all -i i.yaml ansible -i i.yaml -m ping all
2 Setup Ansible
2.1 Install with pip
dnf -y remove ansible-collection-ansible-posix ansible dnf -y install python38-pip python38 sshpass su - $ANSIBLE_USER python3.8 -m pip install --user ansible echo '#ANSIBLE SETUP PATH=$HOME/.local/bin:$HOME/bin:$HOME ' > $HOME/.bashrc
2.2 Install with yum
curl -L ansible.bitbull.ch | bash
2.3 Configure Ansible
- Order of config file sources
ANSIBLE_CONFIG=/opt/ansible.cfg -> ./ansible.cfg -> $HOME/.ansible.cfg -> /etc/ansible/ansible.cfg
- Config Sections
egrep '^\[' /etc/ansible/ansible.cfg [defaults] [privilege_escalation] [ssh_connection] [accelerate] [selinux] [colors]
2.3.1 show non default values
ansible-config dump --only-changed -t all
2.3.2 defaults
forks = 5 # specify number of parallel processes to use host_key_checking = True vault_password_file = /path/to/vault_password_file ansible_managed = Ansible managed: {file} on {host} display_skipped_hosts = True display_args_to_stdout = True
2.3.3 privilege_escalation
become=True # to enable privilege escalation
2.3.3.1 Example
# define user in inventory on ansible host vm20 ansible_user=ansible # create and configure user on destination host useradd ansible su - ansible mkdir .ssh chmod 700 .ssh vi .ssh/authorized_keys # add ansible host pub key chmod 600 .ssh/authorized_keys echo 'ansible ALL=(ALL)NOPASSWD: ALL' > /etc/sudoers.d/ansible visudo -cf /etc/sudoers.d/ansible # verify configuration [root@ansible lab]# ansible vm20 -m command -a w vm20 | SUCCESS | rc=0 >> 14:24:49 up 48 min, 2 users, load average: 0.00, 0.01, 0.05 USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT root pts/0 192.168.223.59 13:43 5:05 0.05s 0.00s tail -f /var/log/secure ansible pts/1 192.168.223.43 14:24 0.00s 0.10s 0.00s /bin/bash -c sudo -H -S -n -u root /bin/bash -c 'echo BECOME-SUCCESS-fwkpkpwhbtkkrxdktmuwvgjmi...
2.4 vim settings
- $HOME/.vimrc
set expandtab set autoindent set tabstop=2 set shiftwidth=2 set softtabstop=2
2.5 Configure Cisco IOS devices
ansible-galaxy collection install ansible.netcommon
- ios_playbook_example.yml
- hosts: switch-aa-325-06 gather_facts: no vars: #### ansible v5 #ansible_connection: ansible.netcommon.network_cli #ansible_network_os: cisco.ios.ios #### ansible v2.9 ansible_connection: network_cli ansible_network_os: ios ansible_user: admin ansible_password: xxx ansible_become: yes ansible_become_method: enable ansible_become_password: xxx pre_tasks: - name: Gather facts ios_facts: when: ansible_network_os == 'ios' tasks: - name: run ios cmd ios_command: commands: show version register: version - name: print cmd output debug: var: version - name: print all native facts debug: var: ansible_facts - name: backup config (ios) ios_config: backup_options: dir_path: /tmp/ios-backup backup: yes register: backup_ios_location when: ansible_network_os == 'ios' - name: print ios backup config debug: var: backup_ios_location
2.6 Configure Fortigate devices
Note that forti modules are not really well maintained.
It's always try and error, I succeeded only with ansible v5 for real world usage
some of them use https connection, some only ssh!
ansible-galaxy collection install fortinet.fortimanager ansible-galaxy collection install fortinet.fortios:1.1.7 #version depends on forti-os release!
2.6.1 backup config
- name: backup fortigate config hosts: fw01 collections: - fortinet.fortios vars: vdom: "root" ansible_httpapi_use_ssl: yes ansible_httpapi_validate_certs: no ansible_httpapi_port: 8443 # ansible_user: xxx # ansible_password: yyy connection: httpapi gather_facts: no tasks: - name: backup global or a_specific_vdom settings fortinet.fortios.fortios_system_config_backup_restore: config: "system config backup" host: "{{ inventory_hostname }}:{{ ansible_httpapi_port }}" username: "{{ ansible_user }}" password: "{{ ansible_password }}" https: "{{ ansible_httpapi_use_ssl }}" ssl_verify: "{{ ansible_httpapi_validate_certs }}" backup: yes scope: global vdom: "{{ vdom|default('root') }}" filename: "./{{ inventory_hostname }}.cfg"
2.6.2 native cmd configuration
Since Fortigate Ansible modules do often not work as expected and are difficult to handle, we prefer native cmd configuration.
This one is compatible with native target configuration, so you can use backup, facts, ... along with native cmd writing.
- name: write cmds to fortigate devices with ansible hosts: fortifw01 gather_facts: no vars: ansible_connection: httpapi ansible_httpapi_port: 844 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false ansible_network_os: fortinet.fortios.fortios ansible_password: xxx ansible_user: forti-ops vdom: root pre_tasks: - name: gather facts for forti devices fortios_facts: vdom: "{{ vdom|default('root') }}" gather_subset: - fact: system_current-admins_select - fact: system_firmware_select - fact: system_fortimanager_status - fact: system_ha-checksums_select - fact: system_interface_select - fact: system_status_select - fact: system_time_select when: ansible_network_os|lower == 'fortinet.fortios.fortios' tasks: - name: define fw config fact for bgp set_fact: target_cfg: | config vdom edit "{{ fw_vdom }}" config router bgp set as {{ fw_as }} set router-id {{ fw_if_v4 }} set ebgp-multipath enable set ibgp-multipath enable config neighbor edit "{{ sw_if_v4 }}" set activate6 disable set bfd enable set remote-as {{ as_fusion }} next edit "{{ sw_if_v6 }}" set activate disable set bfd enable set remote-as {{ as_fusion }} end end - name: verbose print target_cfg for firewall bgp fw_vdom={{ fw_vdom}} fw_as={{ fw_as }} debug: msg: "{{ target_cfg.split('\n') }}" - name: apply bgp config to fortigate device vars: ansible_connection: ssh raw: "{{ target_cfg }}" register: cmd failed_when: > ("Command fail" in cmd.stdout) or (cmd.rc > 0) - name: display output bgp fw_vdom={{ fw_vdom}} fw_as={{ fw_as }} debug: msg: | {{ cmd.stdout_lines }}
2.7 Configure Watchguard devices
Watchguard devices have ssh service running at port 4118 by default, but sadly there are no ansible modules around.
Do not try to solve this issue with "ansible raw module". It just does not work. It just logs-in to the watchguard, than it does not recognize the prompt and wait until timeout.
So this is the "most lean" workaround I found for dropping a few lines to an Watchguard device, eg. kicking remote backup.
--- - name: backup watchguard config hosts: nfw-x001-01 gather_facts: no vars: ftp_user: ftp-backup-user ftp_password: yyy ftp_hostname: ftphost.domain.local #ansible_connection: local #ansible_password: xxx #ansible_port: 4118 #ansible_user: status tasks: - name: raw local ftp backup kicking raw: | sshpass -p '{{ ansible_password }}' \ ssh -C \ -o StrictHostKeyChecking=no \ -o Port={{ ansible_port }} \ -o User={{ ansible_user }} \ -tt {{ inventory_hostname }} <<EOF export config to ftp://{{ ftp_user }}:{{ ftp_password }}@{{ ftp_hostname }}/{{ inventory_hostname }}.xml exit EOF register: cmd failed_when: > ("Invalid input detected at" in cmd.stdout) or not (cmd.stdout|regex_search("\\nWG.*>>"))
2.8 Daily helpers
2.8.1 play options
- run each task as fast as possible
- hosts: all strategy: free remote_user: ansible-ops fact_gathering: no tasks:
2.8.2 extended alive checking
--- - name: check if host is online right now ping: register: ping failed_when: false - name: wait for host to become online wait_for: port: "{{ (ansible_port|default(ansible_ssh_port))|default(22) }}" host: '{{ (ansible_ssh_host|default(ansible_host))|default(inventory_hostname) }}' search_regex: OpenSSH delay: 10 # do not check for at least 10 sec connection: local when: ping.failed == true - name: finally, we decide to proceed or fail setup: when: ping.failed == false
2.8.3 useful tasks and options
- name: validate vni value fail: msg: vni must be greater than 5000 when: vni|int <= 5000 --- - name: check if host is member of spine hostgroup set_fact: dev_type_inv: 'spine' when: "'spine' in group_names" --- - name: verify mariadb root access command: mysql -u root -ne "show databases;" register: mariadb_access_test notify: restart rsyslog no_log: True ignore_errors: True changed_when: False failed_when: false
2.8.4 print multiline message
- name: Print several lines of text vars: msg: | This is the first line. This is the second line with a variable like {{ inventory_hostname }}. And here could be more... debug: msg: "{{ msg.split('\n') }}"
3 Working with Playbooks
3.1 Loop examples
--- - hosts: localhost vars: var1: this is one var array1: [aaa, bbb] array2: - ccc - ddd dictionary1: { key1: value1, key2: value2 } dictionary2: key3: value3 key4: value4 multi_array1: user1: name: bob nr: 123 user2: name: mike nr: 456 tasks: - name: show var1 debug: msg: "{{ var1 }}" - name: loop array1 debug: msg: "{{ item }}" with_items: "{{ array1 }}" - name: loop array2 debug: msg: "{{ item }}" with_items: "{{ array2 }}" - name: loop dictionary1 debug: msg: "{{ item.key }}: {{ item.value }}" with_dict: "{{ dictionary1 }}" - name: loop dictionary2 debug: msg: "{{ item.key }}: {{ item.value }}" with_dict: "{{ dictionary2 }}" - name: "multi_array1['user1']['name']" debug: msg: "multi_array1['user1']['name']: {{ multi_array1['user1']['name'] }}" - name: "multi_array1.user2'.name" debug: msg: "multi_array1.user2'.name: {{ multi_array1.user2.name }}" - name: multi_array1 loop with_dict debug: msg: "User {{ item.key }} is {{ item.value.name }} with nr {{ item.value.nr }}" with_dict: '{{ multi_array1 }}' - name: loop with with_fileglob debug: msg: "/etc/ansible/{{item}}" with_fileglob: "/etc/ansible/*" ...
3.1.1 Example Output
PLAY [localhost] ****************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************ ok: [localhost] TASK [show var1] ****************************************************************************************************** ok: [localhost] => { "msg": "this is one var" } TASK [loop array1] **************************************************************************************************** ok: [localhost] => (item=aaa) => { "item": "aaa", "msg": "aaa" } ok: [localhost] => (item=bbb) => { "item": "bbb", "msg": "bbb" } TASK [loop array2] **************************************************************************************************** ok: [localhost] => (item=ccc) => { "item": "ccc", "msg": "ccc" } ok: [localhost] => (item=ddd) => { "item": "ddd", "msg": "ddd" } TASK [loop dictionary1] *********************************************************************************************** ok: [localhost] => (item={'key': u'key2', 'value': u'value2'}) => { "item": { "key": "key2", "value": "value2" }, "msg": "key2: value2" } ok: [localhost] => (item={'key': u'key1', 'value': u'value1'}) => { "item": { "key": "key1", "value": "value1" }, "msg": "key1: value1" } TASK [loop dictionary2] *********************************************************************************************** ok: [localhost] => (item={'key': u'key3', 'value': u'value3'}) => { "item": { "key": "key3", "value": "value3" }, "msg": "key3: value3" } ok: [localhost] => (item={'key': u'key4', 'value': u'value4'}) => { "item": { "key": "key4", "value": "value4" }, "msg": "key4: value4" } TASK [multi_array1['user1']['name']] ********************************************************************************** ok: [localhost] => { "msg": "multi_array1['user1']['name']: bob" } TASK [multi_array1.user2'.name] *************************************************************************************** ok: [localhost] => { "msg": "multi_array1.user2'.name: mike" } TASK [multi_array1 loop with_dict] ************************************************************************************ ok: [localhost] => (item={'key': u'user2', 'value': {u'nr': 456, u'name': u'mike'}}) => { "item": { "key": "user2", "value": { "name": "mike", "nr": 456 } }, "msg": "User user2 is mike with nr 456" } ok: [localhost] => (item={'key': u'user1', 'value': {u'nr': 123, u'name': u'bob'}}) => { "item": { "key": "user1", "value": { "name": "bob", "nr": 123 } }, "msg": "User user1 is bob with nr 123" } TASK [loop with with_fileglob] **************************************************************************************** ok: [localhost] => (item=/etc/ansible/hosts) => { "item": "/etc/ansible/hosts", "msg": "/etc/ansible//etc/ansible/hosts" } ok: [localhost] => (item=/etc/ansible/ansible.cfg) => { "item": "/etc/ansible/ansible.cfg", "msg": "/etc/ansible//etc/ansible/ansible.cfg" } ok: [localhost] => (item=/etc/ansible/ansible.cfg.rpmnew) => { "item": "/etc/ansible/ansible.cfg.rpmnew", "msg": "/etc/ansible//etc/ansible/ansible.cfg.rpmnew" } PLAY RECAP ************************************************************************************************************ localhost : ok=10 changed=0 unreachable=0 failed=0
3.2 Merging Dictionaries, dynamic Variables
- hosts: localhost vars: _default: name: chris name2: lompetier _custom1: name: calvin name2: hobbes _custom2: name: god tasks: - set_fact: {"{{ item.key }}":"{{ item.value }}"} with_dict: "{{_default|combine(_custom1)|combine(_custom2)}}"
3.2.1 Example Output
PLAY [localhost] ************************************************************************************************************************************************************************************************** TASK [Gathering Facts] ******************************************************************************************************************************************************************************************** ok: [localhost] TASK [set_fact] *************************************************************************************************************************************************************************************************** ok: [localhost] => (item={'key': 'name', 'value': 'god'}) ok: [localhost] => (item={'key': 'name2', 'value': 'hobbes'}) PLAY RECAP ******************************************************************************************************************************************************************************************************** localhost : ok=4 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
4 Variables and Inclusions
4.1 include_tasks vs. import_tasks
In Ansible, both include_tasks and import_tasks are used to include a set of tasks from another file into your playbook or role. However, they differ significantly in when and how they include these tasks, which affects their behavior regarding variables, loops, and conditionals.
4.1.1 Key Differences
4.1.1.1 1. Timing of Inclusion
- import_tasks: Includes tasks statically at parse time. This means Ansible reads and incorporates the tasks into the playbook before any plays or tasks are executed.
- include_tasks: Includes tasks dynamically at runtime. Ansible reads and executes these tasks during the playbook run, at the point where the include_tasks directive is called.
4.1.1.2 2. Variable Evaluation
- import_tasks: Variables used in the imported tasks must be available at parse time. Runtime variables (those defined during the play) are not accessible.
- include_tasks: Can utilize variables that are defined or modified at runtime, allowing for dynamic task inclusion based on the current play context.
4.1.1.3 3. Support for Loops and Conditionals
- import_tasks: Cannot be used with loops (with_items, loop) or conditionals that depend on runtime variables.
- include_tasks: Supports loops and conditionals, making it possible to include tasks multiple times or based on conditions evaluated during play execution.
4.1.2 Examples
4.1.2.1 1. Using include_tasks with a Loop and Runtime Variable
* name: Dynamic Task Inclusion Example hosts: all vars: task_files: - setup.yml - install.yml - configure.yml tasks: - name: Include task files dynamically include_tasks: "{{ item }}" loop: "{{ task_files }}"
Explanation: Here, include_tasks is used within a loop to include multiple task files. The task_files variable can be modified at runtime, and the inclusion happens dynamically.
4.1.2.2 2. Using import_tasks Statically
* name: Static Task Inclusion Example hosts: all tasks: - name: Import common tasks import_tasks: common_tasks.yml
Explanation: import_tasks includes common_tasks.yml at parse time. All tasks in common_tasks.yml become part of the playbook before execution starts.
4.1.2.3 3. Conditional Inclusion with include_tasks
* name: Conditional Task Inclusion Example hosts: all vars: deploy_environment: "production" tasks: - name: Include production tasks include_tasks: production_tasks.yml when: deploy_environment == "production" - name: Include development tasks include_tasks: development_tasks.yml when: deploy_environment == "development"
Explanation: Based on the value of deploy_environment, different task files are included at runtime.
4.1.2.4 4. Attempting to Use import_tasks with a Runtime Variable (Not Recommended)
* name: Incorrect Usage of import_tasks hosts: all vars: task_file: "{{ 'tasks_' + environment + '.yml' }}" environment: "staging" tasks: - name: Import tasks based on environment import_tasks: "{{ task_file }}"
Explanation: This will fail because import_tasks cannot use variables that are defined at runtime (environment in this case) to determine which tasks to import.
4.1.3 When to Use Which
- Use include_tasks when:
- You need to include tasks based on variables or conditions that are only known at runtime. - You want to loop over task files or include them conditionally. - You require dynamic behavior in your playbook.
- Use import_tasks when:
- You have a static set of tasks to include that do not depend on runtime variables. - You prefer all tasks to be known at parse time for clarity or for tooling that analyzes playbooks. - You want to ensure that any syntax errors in the included tasks are caught before execution.
4.1.4 Summary
- import_tasks is for static inclusion at parse time, without support for runtime variables, loops, or dynamic conditions.
- include_tasks is for dynamic inclusion at runtime, supporting loops, conditionals, and runtime variables.
Understanding these differences helps you decide which directive to use based on the needs of your playbook. If you require flexibility and dynamic behavior, include_tasks is the appropriate choice. If your tasks are static and you want them included upfront, import_tasks is suitable.
Additional Tip: In newer versions of Ansible, the distinction between these two directives has become more pronounced, so it's good practice to choose the one that aligns with your playbook's requirements to avoid unexpected behaviors.
4.2 Ansible variable precedence
Ansible variable overriding documentation
In 2.x, we have made the order of precedence more specific (with the last listed variables winning prioritization):
- role defaults [1]
- inventory file or script group vars [2]
- inventory group_vars/all
- playbook group_vars/all
- inventory group_vars/*
- playbook group_vars/*
- inventory file or script host vars [2]
- inventory host_vars/*
- playbook host_vars/*
- host facts
- play vars
- play vars_prompt
- play vars_files
- role vars (defined in role/vars/main.yml)
- block vars (only for tasks in block)
- task vars (only for the task)
- role (and include_role) params
- include params
- include_vars
- set_facts / registered vars
- extra vars (always win precedence)
Basically, anything that goes into “role defaults” (the defaults folder inside the role) is the most malleable and easily overridden. Anything in the vars directory of the role overrides previous versions of that variable in namespace. The idea here to follow is that the more explicit you get in scope, the more precedence it takes with command line -e extra vars always winning. Host and/or inventory variables can win over role defaults, but not explicit includes like the vars directory or an include_vars task.
5 Task Control
5.1 Parallel execution
# playbook - name: do not wait for tasks to finish on all hosts before proceed hosts: all strategy: free # playbook - name: do run 10 hosts at once hosts: all serial: 10 # playbook - name: do run 1 host in first batch, then 10% on each of the next batches hosts: all serial: - 1 - 10% # task - name: run this task one by one on each host command: /path/to/cpu_intensive_command throttle: 1 # task - name: run this task only on one host in group file: path: /srv/backup state: directory run_once: True delegate_to: localhost
5.2 shell
- name: get list of services without Ansible warning shell: "service --status-all 2>&1 | awk {'print $4'}" args: warn: false # set warn=false to prevent warning register: services_list - name: show results debug: var: services_list
5.3 error handling
- name: this will not be counted as a failure command: /bin/false ignore_errors: yes - name: Fail task when the command error output prints FAILED command: /usr/bin/example-command -x -y -z register: command_result failed_when: "'FAILED' in command_result.stderr" - name: Fail task when both files are identical raw: diff foo/file1 bar/file2 register: diff_cmd failed_when: diff_cmd.rc == 0 or diff_cmd.rc >= 2 - name: Check if a file exists in temp and fail task if it does command: ls /tmp/this_should_not_be_here register: result failed_when: - result.rc == 0 - '"No such" not in result.stdout' - name: example of many failed_when conditions with OR shell: "./myBinary" register: ret failed_when: > ("No such file or directory" in ret.stdout) or (ret.stderr != '') or (ret.rc == 10) - command: /bin/fake_command register: result ignore_errors: True changed_when: - '"ERROR" in result.stderr' - result.rc == 2
5.4 conditions
Operation | Example |
---|---|
Equal (value is a string) | ansible_facts['machine'] == "x86_64"
|
Equal (value is numeric) | max_memory == 512
|
Less than | min_memory < 128
|
Greater than | min_memory > 256
|
Less than or equal to | min_memory <= 256
|
Greater than or equal to | min_memory >= 512
|
Not equal to | min_memory != 512
|
Variable exists | min_memory is defined
|
Variable does not exist | min_memory is not defined
|
Boolean variable is true. The values of 1, True, or yes evaluate to true. | memory_available
|
Boolean variable is false. The values of 0, False, or no evaluate to false. | not memory_available
|
First variable's value is present as a value in second variable's list | ansible_facts['distribution'] in supported_distros
|
- name: Activate license uri: method: PUT url: "{{ api_uri }}/{{ license_api }}?acknowledge=true" user: "{{ api_basic_auth_username | default(omit)}}" password: "{{ api_basic_auth_password | default(omit)}}" body_format: json body: "{{ license }}" return_content: yes force_basic_auth: yes validate_certs: "{{ validate_certs }}" register: license_activated no_log: True failed_when: > license_activated.status != 200 or license_activated.json.license_status is not defined or license_activated.json.license_status != 'valid' - name: verbose print target_cfg for firewall zone {{ fw_zone_name }} debug: msg: "{{ target_cfg.split('\n') }}" when: verbose_output|bool - name: find hosts with ee_is_master=true in hostvars of inventory group elastic set_fact: elastic_masters: "{{ elastic_masters|default(['']) + [ hostvars[item]['inventory_hostname_short'] ]}}" with_items: "{{ groups['elastic'] }}" when: "hostvars[item]['ee_is_master']|default('true')|bool is true" - name: define configuration string for AUTO mode set_fact: graylog_elasticsearch_hosts: "http://{{ elastic_masters|select()|unique|join(':9200,http://') }}:9200" when: (( ansible_play_hosts_all|length ) > 1 ) and graylog_elasticsearch_hosts == 'auto'
6 Jinja2 Templates
7 File Handling
8 Working with Roles
9 Optimizing Ansible
9.1 ansible.cfg
[inventory] enable_plugins = host_list, script, auto, yaml, ini, toml [defaults] inventory = ./inventory roles_path = ./roles collections_paths = ./collections log_path = ansible.log deprecation_warnings = False action_warnings = False nocows = 1 force_color = True host_key_checking = False display_skipped_hosts = False forks = 20
10 Ansible Vault
10.1 Set Variable with bad characters
app_password: >- =aU"4'3/\fdgfs/¦
11 Troubleshooting Ansible
11.1 AWX Debugging
11.1.1 CLI Ansible Debugging in CustomEE
grep ansible /proc/*/comm /usr/bin/python3 /usr/local/bin/ansible -u deploy_awx_prod --become --become-method sudo --become-user root mytarget.domain.com -i /runner/inventory/hosts -e@/runner/env/extravars -m debug -a "var=ansible_python_interpreter"