Ansible Module Reference

There are some common Ansible modules that use frequently, more details please refer to Ansible online document.

How to organize the ansible files structure, see this best practice.

play strategy

There are several play strategies in ansible:

1
2
3
4
- hosts: all
strategy: free
tasks:
# ...

The default is linear strategy with 5 forks parallelism.

gather_facts

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/gather_facts_module.html Variables related to remote systems are called facts.

This module is automatically called by playbooks to gather useful variables about remote hosts that can be used in playbooks.

1
2
3
4
5
- hosts: all
# disable gather facts if necessary
gather_facts: no
tasks:
...

For example, I can use ansible_memtotal_mb and ansible_processor_vcpus (processor number in /proc/cpuinfo) to config other daemons, they are both facts from remote machine:

1
jvm_heap_size: "{{ ansible_memtotal_mb // 2 | int }}m"

Here is an case about using ansible internal variable to get machine memory size: https://www.redhat.com/sysadmin/configuration-verification-ansible Then we can use it for example to calculate and set heap size used for ES.

magic variables

https://docs.ansible.com/ansible/latest/user_guide/playbooks_vars_facts.html#information-about-ansible-magic-variables

Variables related to Ansible are called magic variables. For example: hostvars ,groups and inventory_hostname.

add_host

Dynamically create host and group just like inventory file for later play use.

1
2
3
4
5
6
7
8
9
10
11
- name: Add host to group 'just_created' with variable foo=42
add_host:
hostname: '{{ ip_from_ec2 }}'
groups: just_created
# can have inventory parameters
ansible_host: '{{ inventory_hostname }}'
ansible_port: '{{ new_port }}'
ansible_ssh_private_key_file: "{{ ssh_priv_key }}"
ansible_ssh_common_args: "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
foo: 42
no_log: True

Delegation

https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_delegation.html Delegate tasks to different machine or group, for example localhost, etc.

There is also a shorthand syntax that you can use on a per-task basis: local_action, the same as delegate_to: 127.0.0.1:

1
2
3
4
5
6
7
8
9
10
11
12
13
---
- hosts: webservers
serial: 5

tasks:
- name: Take out of load balancer pool
ansible.builtin.command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
delegate_to: 127.0.0.1

- name: Recursively copy files from management server to target
local_action: >
ansible.builtin.command
rsync -a /path/to/files {{ inventory_hostname }}:/path/to/target/

serial

This keyword is useful when doing rolling upgrade or patching.

You can also set maximum failure percentage to abort play during the batch execution:

1
2
3
4
5
6
---
- name: test play
hosts: webservers
serial: 4
# if 2 out of 4 fails, abort execution
max_fail_percentage: 49

run_once

https://docs.ansible.com/ansible/latest/user_guide/playbooks_strategies.html#running-on-a-single-machine-with-run-once

Run task only on the first host in the batch, for example, setting on ES cluster master. Here provides other options like delegate_to and when: inventory_hostname == webservers[0].

retry block

By the time Ansible does not support retry block, but there is another workaround to implement this useful feature, for instance, to make group of tasks atomic, see this post:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- name: Group of tasks that are tightly coupled
block:
- name: Increment the retry count
set_fact:
retry_count: "{{ 1 if retry_count is undefined else retry_count | int + 1 }}"
# start block tasks
- name: Some task that might fail
...
- name: Some task that might fail
...

rescue:
- fail:
msg: Maximum retries of grouped tasks reached
when: retry_count | int == 3

- debug:
msg: "Task Group failed, let's give it another shot"

- include_task: coupled_task_group.yml
vars:
foo: 110

K8s management

挺有意思,看看这篇文章 和Helm做了一下比较:

Use Ansible on k8s management:

uri

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# PUT method and get response to result
- name: disable shard allocation
uri:
url: http://localhost:9200/_cluster/settings?format=json
method: PUT
status_code: 200
body_format: json
return_content: yes
headers:
Content-Type: application/json
body: "{\"persistent\": {\"cluster.routing.allocation.enable\": \"primaries\"}}"
# if request response is slow
timeout: 900
register: result

- name: display response body
debug:
var: result.json

# if reponse is an array of dicts
- name: Print certain element
debug:
var: result.json[0].xxx.yyy

pause

https://docs.ansible.com/ansible/latest/modules/pause_module.html

Pauses playbook execution for a set amount of time, or until a prompt is acknowledged. All parameters are optional. The default behavior is to pause with a prompt.

The pause module integrates into async/parallelized playbooks without any special considerations (see Rolling Updates). When using pauses with the serial playbook parameter (as in rolling updates) you are only prompted once for the current group of hosts.

Useful when debug certain task to see the execution result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# just pause 
- pause:

- name: Pause for 5 seconds
ansible.builtin.pause:
seconds: 5

# a helpful reminder of what to look out for post-update.
- pause:
prompt: "Make sure org.foo.FooOverload exception is not present"

# pause to get some sensitive input.
- pause:
prompt: "Enter a secret"
echo: no

skip

Sometimes I need to skip tasks on some machines with prompt confirmation, how to do this?

It seems there is no skip module in ansible, but we have workaroud, see this issue.

You can also apply tag and condition.

debug

https://docs.ansible.com/ansible/latest/modules/debug_module.html This module prints statements during execution and can be useful for debugging variables or expressions without necessarily halting the playbook.

Useful for debugging together with the when: directive.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
## Example that prints return information from the previous task
- shell: /usr/bin/uptime
register: result

## var option already runs in Jinja2 context and has an implicit {{ }} wrapping
- debug:
var: result
## this verbosity is associated with `-vv` parameter
verbosity: 2

## prints the loopback address and gateway for each host
- debug:
msg: System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}
when: ansible_default_ipv4.gateway is defined

fail

https://docs.ansible.com/ansible/latest/modules/fail_module.html This module fails the progress with a custom message. It can be useful for bailing out when a certain condition is met using when.

More error handling see: https://docs.ansible.com/ansible/latest/user_guide/playbooks_error_handling.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
- name: check async task status
ignore_errors: true
## check async task status
async_status:
jid: "{{ sleepTask.ansible_job_id }}"
register: job_result
until: job_result.finished
when: "inventory_hostname == groups.master[0]"
retries: 5
delay: 1

## other things to do

## fail the process if the retry failed
- fail:
msg: The time limit hit, the cluster may not be in ready status!
## it depends what output is in the register variable
when: job_result.failed == true

copy

https://docs.ansible.com/ansible/latest/modules/copy_module.html The copy module copies a file from the local or remote machine to a location on the remote machine (depends on the condition). 和template类似, 如果task下面有files 文件夹, 在不指定src路径的时候, eg: src: xxx.txt, 会从files文件夹里copy.

Use the fetch module to copy files from remote locations to the local box.

If you need variable interpolation in copied files, use the template module. Using a variable in the content field will result in unpredictable output.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# {{ baseDir }}/registry-certs/tls.crt is in control machine
- name: Copy secure docker registry ssl/tls certs to all worker nodes
any_errors_fatal: true
copy:
src: "{{ baseDir }}/registry-certs/tls.crt"
dest: "/etc/docker/certs.d/{{ service name }}:5000/tls.crt"
owner: root
group: root
mode: '0644'

# remote_src: yes means copy is happening on remote machine, the src is
# also in that remote machine.
# remote_src supports recursive copying as of version 2.8
- name: Copy a "sudoers" file on the remote machine for editing
copy:
src: /etc/sudoers
dest: /etc/sudoers.edit
remote_src: yes

# With delegate_to, it copies local ssh_key.path file(from control machine) to
# that fileserver_ip.
- name: Copy SSH key to file server for SCP use
no_log: "{{ task_no_log }}"
copy:
src: "{{ ssh_key.path }}"
dest: "/tmp/ns1.pk"
owner: root
group: root
mode: '0400'
delegate_to: "{{ fileserver_ip }}"

NOTE that mode: '0400' must use 4 digits, such as 0644 instead of 644, otherwise you write sticky bit. See ansible copy make sticky bit.

fetch

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/fetch_module.html This module works like copy, but in reverse. It is used for fetching files from remote machines and storing them locally in a file tree, organized by hostname.

Files that already exist at dest will be overwritten if they are different than the src.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# fetched file is marked by the remote hostname 
- name: Store file into /tmp/fetched/<src machine hostname>/tmp/somefile
fetch:
src: /tmp/somefile
dest: /tmp/fetched

# If dest ends with '/', it will use the basename of the source file, similar to
# the copy module.
# This can be useful if working with a single host, or if retrieving files that
# are uniquely named per host.
# If using multiple hosts with the same filename, the file will be overwritten
# for each host.
- name: Specifying a destination path
fetch:
src: /tmp/uniquefile
dest: /tmp/special/
flat: yes

template

https://docs.ansible.com/ansible/latest/modules/template_module.html 这个module在统一设置配置文件时很常用, 或者可以用来初始化 script template 中的参数, 然后传递 到各个host去运行.

Templates are processed by the Jinja2 templating language. Documentation on the template formatting can be found in the Template Designer Documentation.

Usually we have the ansible role structure:

1
2
3
4
5
6
7
8
9
10
11
12
roles/
install.components/
defaults/
main.yml
tasks/
main.yml
templates/
example.conf.j2
example.sh.j2
files/
bar.txt
foo.sh

When template works it picks source file from role’s templates/ folder. If the template file contains jinja2 placeholder, it will be interpolated.

1
2
3
4
5
6
7
8
# Copy from control machine to target nodes
- name: Template a file to /etc/files.conf
template:
src: /mytemplates/foo.j2
dest: /etc/file.conf
owner: bin
group: wheel
mode: '0644'

Besides the jinja2 built-in filters: https://jinja.palletsprojects.com/en/latest/templates/#list-of-builtin-filters There are Ansible supplies: https://ansible-docs.readthedocs.io/zh/stable-2.0/rst/playbooks_filters.html#jinja2-filters

Some useful cases:

  1. remove quotes: using regex_replace("\"", ""), or using slice cut xxx[1:-2]

shell

https://docs.ansible.com/ansible/latest/modules/shell_module.html

It is almost exactly like the command module but runs the command through a shell (/bin/sh) on the remote node.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# the symbol | is a Yaml formater
- name: Copy IIS docker images to specified worker nodes
any_errors_fatal: true
shell: |
/tmp/copyIISDockers.sh {{ iisDockers }} {{ imageTag }}
if [[ $? -eq 0 ]]; then
touch /tmp/copyIISImageToWorker.done
fi
when: "inventory_hostname == groups.master[0]"
args:
## A filename, when it already exists, this step will not be run.
creates: /tmp/copyIISImageToWorker.done
## A filename, when it does not exist, this step will not be run.
removes: /tmp/preTaskOk.done
## disable task warning
warn: no
## change the shell
executable: /bin/bash

- name: Change the working directory to somedir/ before executing the command.
shell: somescript.sh >> somelog.txt
args:
chdir: somedir/

command

https://docs.ansible.com/ansible/latest/modules/command_module.html The command module takes the command name followed by a list of space-delimited arguments. The given command will be executed on all selected nodes.

The command(s) will not be processed through the shell, so variables like $HOME and operations like “<”, “>”, “|”, “;” and “&” will not work. Use the shell module if you need these features.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- name: return motd to registered var
command: cat /etc/motd
register: mymotd

# 'args' is a task keyword, passed at the same level as the module
- name: Run command if /path/to/database does not exist (with 'args' keyword).
command: /usr/bin/make_database.sh db_user db_name
args:
creates: /path/to/database

# 'cmd' is module parameter
- name: Run command if /path/to/database does not exist (with 'cmd' parameter).
command:
cmd: /usr/bin/make_database.sh db_user db_name
creates: /path/to/database

service

https://docs.ansible.com/ansible/latest/modules/service_module.html Controls services on remote hosts. Supported init systems include BSD init, OpenRC, SysV, Solaris SMF, systemd, upstart.

1
2
3
4
- name: Start service httpd, if not started
service:
name: httpd
state: started

sysctl

https://docs.ansible.com/ansible/2.9/modules/sysctl_module.html Managing entries in sysctl.conf file:

1
2
3
4
5
- sysctl:
name: net.core.rmem_max
value: "{{ foo | int }}"
state: present
reload: yes

systemd

https://docs.ansible.com/ansible/latest/modules/systemd_module.html More dedicated then service module, controls systemd services on remote hosts.

1
2
3
4
5
6
- name: Enable and start docker
any_errors_fatal: true
systemd:
name: docker
enabled: yes
state: started

file

Set attributes of files, symlinks or directories. Alternatively, remove files, symlinks or directories.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- name: Create a directory if it does not exist
file:
path: /etc/some_directory
state: directory
mode: '0755'

- name: Touch a file, using symbolic modes to set the permissions (equivalent to 0644)
file:
path: /etc/foo.conf
state: touch
mode: u=rw,g=r,o=r

- name: Remove file (delete file)
file:
path: /etc/foo.txt
state: absent

- name: Recursively remove directory
file:
path: /etc/foo
state: absent

replace

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/replace_module.html

1
2
3
4
5
6
7
8
9
10
11
12
# Remember to escape regex character
- name: update elasticsearch jvm deprecated options
replace:
path: /etc/elasticsearch/jvm.options
regexp: "{{ update_item.old }}"
replace: "{{ update_item.new }}"
loop:
- { old: ^-XX:\+UseConcMarkSweepGC, new: 8-13:-XX:+UseConcMarkSweepGC }
- { old: ^-XX:CMSInitiatingOccupancyFraction=75, new: 8-13:-XX:CMSInitiatingOccupancyFraction=75 }
- { old: ^-XX:\+UseCMSInitiatingOccupancyOnly, new: 8-13:-XX:+UseCMSInitiatingOccupancyOnly }
loop_control:
loop_var: update_item

lineinfile

https://docs.ansible.com/ansible/latest/modules/lineinfile_module.html

If you use sed in command module, you will get warning, you can disable the warning by add warn: false or use lineinfile module.

This module ensures a particular line is in a file, or replace an existing line using a back-referenced regular expression.

This is primarily useful when you want to change a single line in a file only.

See the replace module if you want to change multiple, similar lines or check blockinfile if you want to insert/update/remove a block of lines in a file. For other cases, see the copy or template modules.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# remove
- name: remove old node role syntax in elasticsearch.yml file
lineinfile:
path: /etc/elasticsearch/elasticsearch.yml
regexp: "{{ remove_item }}"
state: absent
loop:
- ^node\.data
- ^node\.master
- ^node\.roles
loop_control:
loop_var: remove_item

# insert
- name: Update G1GC config for jdk 14+ in jvm options file
lineinfile:
path: /etc/elasticsearch/jvm.options
insertafter: "^# To use G1GC uncomment the lines below."
line: "{{ add_item }}"
loop:
- 14-:-XX:+UseG1GC
- 14-:-XX:G1ReservePercent=25
- 14-:-XX:InitiatingHeapOccupancyPercent=30
loop_control:
loop_var: add_itema

NOTE that if run multiple times, only the first time take effect! it will not remove or insert duplicates.

mount

https://docs.ansible.com/ansible/latest/modules/mount_module.html This module controls active and configured mount points in /etc/fstab

For /etc/exports(nfs server side config), no dedicated module for it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- name: Edit /etc/fstab file to mount share directory
any_errors_fatal: true
mount:
path: "/mnt"
src: "{{ dfsFileServer }}:{{ dfsDataDir }}"
fstype: nfs
opts: "defaults,timeo=10,retrans=3,rsize=1048576,wsize=1048576"
state: mounted

- name: Edit /etc/fstab file to unmout share directory
any_errors_fatal: true
mount:
path: "/mnt"
fstype: nfs
opts: "defaults,timeo=10,retrans=3,rsize=1048576,wsize=1048576"
state: absent

asynchronous

https://docs.ansible.com/ansible/latest/user_guide/playbooks_async.html By default task in playbook blocks, this may not always be desirable, or you may be running operations that take longer than the SSH timeout.

This module can be use to create progress bar for long time task.

Notice that async task can only be accessed in the same playbook.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
---
- name: async task in background
any_errors_fatal: true
shell: |
echo "start first task"
sleep 20
touch /tmp/sleep.done
## when poll > 0, task still blocks
## when poll = 0, task run in background
poll: 0
## explicitly sets the timeout 1000 seconds
async: 1000
## register for later check
register: sleepTask
when: "inventory_hostname == groups.master[0]"
args:
creates: /tmp/sleep.done

- name: check async task status
any_errors_fatal: true
## check async task status
async_status:
jid: "{{ sleepTask.ansible_job_id }}"
register: job_result
until: job_result.finished
when: "inventory_hostname == groups.master[0]"
retries: 30
delay: 3

ini_file

1
2
3
4
5
6
7
8
- name: "Add override config file for xxx service"
ini_file:
path: /etc/systemd/system/xxx.service.d/override.conf
section: Service
option: MemoryLimit
value: "800M"
no_extra_spaces: yes
mode: 0644

synchronize

The rsync command wrapper.

loop

https://docs.ansible.com/ansible/latest/user_guide/playbooks_loops.html 需要注意的是在有嵌套循环时,要rename default item, for example, below loop is a inner loop, rename the loop var to inner_item, otherwise you get warning message:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# main.yml
- include_tasks: inner.yml
loop:
- 1
- 2
- 3
loop_control:
loop_var: outer_item

# inner.yml
- name: Add several users
debug:
msg: "{{ inner_item.name }} and {{ inner_item.groups }}"
loop:
- { name: 'testuser1', groups: 'wheel' }
- { name: 'testuser2', groups: 'root' }
loop_control:
loop_var: inner_item

variables

  • inventory_hostname
  • groups
  • jinjia2 template
    }}```
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    If the vars expresson is long, can modify it into multipe lines:
    ```yaml
    # using > instead of |, > will ignore the line return ane assemble the
    # multi-line into one line.
    result: >
    "{{ ((groups['data'] | length) == 0
    and (groups['data_hot'] | length) == 0
    and (groups['data_warm'] | length) == 0)
    | ternary('it is OK', 'bad!')
    }}"

conditional

  • when clause
1
2
3
## 不是每个module都支持creates的
args:
creates: xxx
0%