FANDOM


References

Ansible 2.7

  • List of Behavioral Inventory Parameters
    • ansible_connection, ansible_host, ansible_port, ansible_user, ansible_ssh_pass, ansible_ssh_private_key_file, ...
    • ansible_become_pass (ansible_sudo_pass, ansible_su_pass)

Ansible 2.6

Ansible 2.4

Ansible Container

Concepts

playbook = play+

play = (host+, task+)

task = (name, module, ...)

Modules

Module Description Remarks
debug Print statements during execution
assert Asserts given expressions are true
fail Fail with custom message
pause Pause playbook execution
stat Retrieve file or file system status
file Sets attributes of files
copy Copies files to remote locations
archive Creates a compressed archive of one or more files or trees zip, tar cvf, tar cvzf
unarchive Unpacks an archive after (optionally) copying it from the local machine unzip, tar xvf, tar xvzf
lineinfile Ensure a particular line is in a file, or replace an existing line using a back-referenced regular expression
replace Replace all instances of a particular string in a file using a back-referenced regular expression
template Templates a file out to a remote server
shell Execute commands in nodes /bin/sh, /bin/bash
command Executes a command on a remote node
expect Executes a command and responds to prompts
get_url Downloads files from HTTP, HTTPS, or FTP to node curl, wget
uri Interacts with HTTP and HTTPS web services and supports Digest, Basic and WSSE HTTP authentication mechanisms curl, weg
apt Manages apt-packages
apt_repository Add or remove an APT repositories in Ubuntu and Debian
apt_key Add or remove an apt key, optionally downloading it
pip Manages Python library dependencies
systemd Controls systemd services on remote hosts systemd provides a system and service manager that runs as PID 1 and starts the rest of the system.
docker_container Manage the life cycle of docker containers
docker_image Manage docker images
git Deploy software (or files) from git checkouts

Facts

Category Fact Description Values Remarks
Common ansible_architecture "x86_64"
ansible_distribution "Ubuntu"
ansible_distribution_version "16.04"
ansible_distribution_major_version "16"
ansible_distribution_release "xenial"
ansible_hostname hostname
ansible_env ansible_env.HOME, ansible_env.PATH, ansible_env.PWD
ansible_interfaces ["lo", "eth0", "eth1", "eth2", "eth3", "docker0", ...] Array
ansible_os_family "Debian"
ansible_pkg_mgr "apt"
ansible_check_mode false
inventory_hostname name of the host as configured in Ansible's inventory host file (usually hosts.yml) "m001", "m002" not always same with ansible_hostname

Filters

Filter Description Applied to Syntax Remarks
json_query query a complex JSON structure and iterate over it using a loop structure an object JMESPath Specification
regex_replace replace text in a string with regex string Regex in Python 3
regex_search search a string with a regex string
select Filters a sequence of objects by applying a test to each object, and only selecting the objects with the test succeeding. a sequence of objects
selectattr Filters a sequence of objects by applying a test to the specified attribute of each object, and only selecting the objects with the test succeeding. a sequence of objects selectattr(attrName, test, value)
(test : equalto, ne, gt, ge, number, odd, ...)
reject Filters a sequence of objects by applying a test to each object, and rejecting the objects with the test succeeding. a sequence of objects
rejectattr Filters a sequence of objects by applying a test to the specified attribute of each object, and rejecting the objects with the test succeeding. a sequence of objects
map Applies a filter on a sequence of objects or looks up an attribute. This is useful when dealing with lists of objects but you are really only interested in a certain value of it. a sequence of objects map(attribute="attrName")
map(filterName, [arg1, arg2, arg3, ...)
attr Get an attribute of an object. an object
list Convert the value into a list.
flatten Flatten a list a sequence of objects flatten([levels=n])
sort Sort an iterable iterable sort(value, reverse=False, case_sensitive=False, attribute=None)
common.quorum.download.constellation|regex_replace('^.*/', '')

configDefault.outputs|selectattr("type", "eq", "logstash")|first

['ripple']|map('extract', common, ['tls', 'dn', 'c'])|list|first|default('KO')

Tests

Test Description Applied to Syntax Remarks
directory, file, link, abs, mount provide information about a path on the controller
failed, succeeded, changed, skipped check the status of tasks
match, search match strings against a substring or a regex string
version compare a version number string version(compared_version, operator) <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne

Plugins

Category Plugin Description Remarks
fileglob Matches all files in a single directory, non-recursively, that match a pattern

Jinja2

JInja2 Initialization Parameters

Parameter Description Default Remarks
trim_blocks If this is set to True the first newline after a block is removed (block, not variable tag!) False
lstrip_blocks If this is set to True leading spaces and tabs are stripped from the start of a line to a block. False
keep_trailing_newline Preserve the trailing newline when rendering templates. False
newline_sequence The sequence that starts a newline. Must be one of '\r', '\n' or '\r\n'. '\n'

Jinja2 Operators

Category Name Operator Description Remarks
Math Addition +
Subtraction -
Multiplication *
Division /
Division to truncated integer // 20 // 7 == 2
Comparison Equal to ==
Inequal to !=
Greater than >
Lower than <
Logic And and
Or or
Not not
Misc In in
Is is Perform a test
Filter | Apply a filter
String concatenation ~

Readings

JMESPath

Expressions

Expression Syntax Description Remarks
Hash Wildcard Expression * Return a list of the hash element’s values. Any subsequent expression will be evaluated against each individual element in the list
List Wildcard Expression [*] Return all the elements in a list. Any subsequent expressions will be evaluated against each individual element.
Multi-Select List Expression [ expr1, expr2, expr3 ... ] Extract a subset of elements as a list from a JSON hash
Multi-Select Hash Expression [ key1: expr1, key2: expr2, ...] Extract a subset of elements as a hash from a JSON hash
Filter Expression [? boolean expr ] Select JSON elements based on a comparison to another expression for each element in an array
Flatten Operator [] Merge sublists in the current result into a single list. Filter first and flatten last

Operators

Operator Name Remarks
== Equality
!= Inequality
< Less than
<= Less than or equal to
> Greater than
>= Greater than or equal to

Examples

hostvars.*.ripple.[validators, trackers][][].name

Roles

Role Location Remarks
apparmor https://github.com/Oefenweb/ansible-apparmor Remove apparmor in Debian-like systems.

Readings

Templating

Filters / Tests

Collection data-type

String

Security

Modules

Examples

Tips and Tricks

Print out variables and expressions using Ansible console and debug module

Get into console using ansible-console command and then use debug task

$ ansible-console -l m001
 
...
 
sshuser@all (1)[f:5]$ debug msg="{{ hostvars }}"
 
...
 
sshuser@all (1)[f:5]$ exit
 
$

Using Jinja statements inside play

  - name: Start constellation nodes
    vars:
      tls: "{{ item.constellation.tls|default(common.quorum.constellation.tls) }}"
 
      protocol: >-2        {%- if tls == 'strict' -%}'http'{%- else -%}'http'{%- endif -%}    shell: |
      ...

To build more complex object as an task scope variable, build script variable using statements then print out the script variable using expression at the last line

  - name: Generate validators file ('validators.txt')
    vars:
      pubKeys: |
        {%- set pubKeys = [] -%}          {%- for lines in hostvars|json_query('*.extract_validator_public_key.results[].stdout_lines')|sort(attribute='0') -%}            {%- if lines[0] in item.peers -%}              {%- do pubKeys.append(lines[1]) -%}            {%- endif -%}          {%- endfor -%}        {{ pubKeys }}    template:
    ...

Safe escaping undefined variable

Use default filter.

when: not fabric.generate.crypto.skip|default(false)
with_item: favorites.movies|default([])
{% for member in members|default([]) %}
  {%- for message in (member.mailbox|default({})).messages|default([]) -%}
  ...
  {%- endfor -%}
{% endfor %}

Safe drill-down using json_query filter

If you want access deep descendant node from a safe node without escaping every step using default filter, you can use json_query filter which utilizes JMESPath internally.

- name: Generate CSR for TLS certificate if not exists
  openssl_csr:
    path: "{{ ansible_env.HOME }}/ripple/nodes/{{ item.name }}/tls/tls-server.csr"
    privatekey_path: "{{ ansible_env.HOME }}/ripple/nodes/{{ item.name }}/tls/tls-server.key"
    state: present
    force: false
    mode: 0600
    country_name: "{{ item|json_query('tls.dn.countryName')|default(common.ripple.tls.baseName.countryName, true) }}"    state_or_province_name: "{{ item|json_query('tls.dn.stateOrProvinceName')|default(common.ripple.tls.baseName.stateOrProvinceName, true) }}"    locality_name: "{{ item|json_query('tls.dn.localityName')|default(common.ripple.tls.baseName.localityName, true) }}"    organization_name: "{{ item|json_query('tls.dn.organizationName')|default(common.ripple.tls.baseName.organizationName, true) }}"    organizational_unit_name: "{{ item|json_query('tls.dn.organizationalUnitName')|default(common.ripple.tls.baseName.organizationalUnitName, true) }}"    email_address: "{{ item|json_query('tls.dn.emailAddress')|default(common.ripple.tls.baseName.emailAddress, true) }}"    common_name: "{{ item|json_query('tls.dn.commonName')|default(item.name, true) }}"  when: true
  become: false
  with_items: "{{ (ripple|default({})).validators|default([]) }}"
  register: generate_validator_tls_csr
  tags: ['tls']

When using multi-select-list expression of JMESPath, an item in the resulted list can be null or nested. So, it should be filtered using select and flattened.

- name: Backup and clean artifacts including data, configuration, and logs
  var:
    timestamp: "{{ lookup('pipe', 'date date +%Y%m%d'T'%H%M%Z -u') }}"
  file:
    src: "{{ ansible_env.HOME }}/ripple/nodes/{{ item.name }}/"
    dest: "{{ ansible_env.HOME }}/ripple/nodes/{{ item.name }}_{{ timestamp }}/"
  when: common.ripple.flags.cleans
  with_items: "{{ ripple|default({})|json_query('[validators, trackers]')|select|flatten }}"

Filtering and drilling down the entire hostvars using selectattr and map filters

With 4 host variable files under host_vars directory

  • host_vars/m001.yml
quorum: 
  nodes:
    - name: node1
      host: "{{ inventory_hostname }}"
      type: permissioned
    - name: node2
      host: "{{ inventory_hostname }}"
      type: permissioned
  • host_vars/m002yml
quorum: 
  nodes:
    - name: node3
      host: "{{ inventory_hostname }}"
      type: permissioned
  • host_vars/m003yml
quorum: 
  portal:
  • host_vars/m004yml
grafana:

The following task would print out only 3 quorum nodes

  - name: Print out Quorum nodes
    debug:
      msg: "{{ item }}"
    with_items: "{{ hostvars.values()|selectattr('quorum', 'defined')|map(attribute='quorum')|selectattr('nodes', 'defined')|map(attribute='nodes')|sum(start=[])|list }}" 
    # when: (hostvars[item].quorum|default({})).nodes
    when: true

Filtering and drilling down the entire hostvars using json_query filter

You can draw the same information from the entire hostvars more conveniently using json_query filter.

  - name: Print out Quorum nodes
    debug:
      msg: "{{ hostvars|json_query('*.quorum.nodes')|flatten|sort(attribute='name')|reverse|list }}"
    when: true

Or you can use json_query more aggressively

  - name: Print out Quorum nodes
    debug:
      msg: "{{ hostvars|json_query('*.quorum.nodes[]')|sort(attribute='name')|reverse|list }}"
    when: true

Even more aggressively (may not work)

  - name: Print out Quorum nodes
    debug:
      msg: "{{ hostvars|json_query('*.quorum.nodes[] | sort_by([], &name)')|reverse|list }}"
    when: true


Another example

  - name: Print out Quorum nodes
    debug:
      msg: "{{ hostvars|json_query('*.ripple.validators[?name!=`v0`][]')|sort(attribute='name')|reverse|list|json_query('[*].config.ports[?name==`port_peer`][]') }}"
   when: true

Fine tuning line breaks and indentations for Jinja2 templates

Default setting for line breaks and trimming should be specified at the top of the template file

#jinja2:line_statement_prefix: '%', trim_blocks: False, lstrip_blocks: True, keep_trailing_newline: True

For simple loop without other nested conditionals or nested loops, use {% for ... -%} ... {% endfor %}

#jinja2:line_statement_prefix: '%', trim_blocks: False, lstrip_blocks: True, keep_trailing_newline: True
[validators]
{% for key in pubKeys -%}
  {{ key }}
{% endfor %}

Generating and prettify configuration file

Generate configuration file with trim_blocks disabled and prettify the file with replace module.

#jinja2:line_statement_prefix: '%', trim_blocks: False, lstrip_blocks: True, keep_trailing_newline: True
{#
For, detailed help, refer https://github.com/ripple/rippled/blob/1.0.0/cfg/rippled-example.cfg
#}
# Configuration for Ripple node named {{ item.name }}
{# 1. Server #}
[server]
{% for port in item.config.ports -%}
  {{ port.name }}
{% endfor -%}
 
{%- for port in item.config.ports %}
[{{ port.name }}]
port = {{ port.port }}
ip = {{ port.ip }}
  {% if port.admin is defined %}{{ 'admin = ' ~ port.admin }}{% endif %}
protocol = {{ port.protocol }}
  {% if port.ssl_key is defined %}{{ 'ssl_key = ' ~ port.ssl_key }}{% endif %}
  {% if port.ssl_cert is defined %}{{ 'ssl_cert = ' ~ port.ssl_cert }}{% endif %}
{% endfor -%}
 
{%- if item.config.rpc_startup is defined %}
[rpc_startup]
  {% for cmd in item.config.rpc_startup -%}
    {{ cmd|to_json }}
  {% endfor %}
{%- endif %}
 
{# [websocket_ping_frequency] #}
 
{# 2. Peer Protocol #}
{# [ips] #}
 
{# [ips_fixed] #}
 
[peer_private]
1
 
[peers_max]
{{ common.ripple.configDefault.peers_max }}
 
[node_size]
medium
  # https://github.com/ripple/rippled/blob/1.0.0/cfg/rippled-example.cfg
  - name: Generate validator configuration from template
    template:
      src: "{{ playbook_dir }}/../templates/rippled.cfg.j2"
      dest: "{{ ansible_env.HOME }}/ripple/nodes/{{ item.name }}/rippled.cfg"
      newline_sequence: '\n'
      mode: "u=rw,g=r,o=r"
    become: flase
    with_items: "{{ (ripple|default({})).validators|default([]) }}"
    tags: [genConfig]
  - name: Prettify validator configuration file
    replace:
      path: "{{ ansible_env.HOME }}/ripple/nodes/{{ item.name }}/rippled.cfg"
      regexp: "\n{3,}"
      replace: "\n\n"
    become: flase
    with_items: "{{ (ripple|default({})).validators|default([]) }}"
    tags: [genConfig]

Using multiple vault passwords

To use multiple vault passwords to encrypt strings

  • Define all the passwords with vault_identity_list in ansible.cfg
  • Specify the label of the password to use with --encrypt-vault-id option when calling ansible-vault encrypt_string command.

Example ansible.cfg

[defaults]
inventory = hosts.yml
gathering = smart
fact_caching = jsonfile
fact_caching_connection = ./fact_cache
fact_caching_timeout = 60
retry_files_save_path = plays/retry
ask_sudo_pass = True
ask_vault_pass = False
vault_identity_list = dev@vault-pass-test.sh, test@vault-pass-test.sh
jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n

; https://docs.ansible.com/ansible/latest/plugins/vars/host_group_vars.html
[yaml_valid_extensions]
defaults = [u'.yml', u'.yaml']

Example vault-pass-test.sh

#!/bin/sh

echo 5678

The commandline to use dev password

$ ansible-vault encrypt_string --encrypt-vault-id dev abcd