Ansible Advanced Workshop

Installation

CentOS/RHEL: yum install ansible
Note: to enable repositories (EPEL or RHEL), check above documentation for reference.

Fedora: dnf install ansible

Documentation

Link to online documentation tree: https://docs.ansible.com

From CLI after install Ansible:

  • Module list: ansible-doc -l

  • Module consultation: ansible-doc <module name>

  • Example: ansible-doc service

Inventory

Inventory can take an INI or YAML format.

An example for a static inventory file

INI format

node9

[group1]
node1
node2

[group2]
node3 http_port=80

[group3]
node[10:20]

[prod]
group1
group3

[group1:vars]
var1=value1

YAML format

all:
  hosts:
    node9:
  children:
    group1:
      hosts:
        node1:
        node2:
      vars:
        var1: value1
    group2:
      hosts:
        node3:
          http_port: 80
    group3:
      hosts:
        node[10:20]:
    prod:
      children:
        group1:
        group3:

To ensure proper configuration and outcome, followings can be used:

  • json inventory output: ansible-inventory --list

  • list inventory output: ansible-inventory --graph

How to connect to your controlled node?

No additional agent needed on controlled nodes. It’s SSH or WinRM.

  • Username and password.

  • SSH key pairs.

  • …​

Depends on your existing SSHD configuration.

Apply least privilege approach!

Configuration

  • /etc/ansible/ansible.cfg

  • ~/.ansible.cfg

  • ./ansible.cfg

See which configuration file is used with ansible --version.

[defaults]
inventory = ./inventory
remote_user = user
ask_pass = false

[privilege_escalation]
become = true
become_method = sudo
become_user = root
become_ask_pass = false

You can use become: true at the playbook or task level.

You can also use -b at the command line.

Pinging your machines

ansible -m ping -i inventory all

Or with a playbook:

playbook-ping.yml

Idempotence

  • Use modules as much as possible!

  • Resort to using shell or command only when a module is not available.

Being idempotent allows the defined task to run one time or a thousand times without having an adverse effect on the target system, only ever making the change once. In other words, if a change is required to get the system into its desired state, the change is made; and if the device is already in its desired state, no change is made. This is unlike most traditional custom scripts and the copy and pasting of CLI commands into a terminal window. When the same command or script is executed repeatedly on the same system, errors are (sometimes) raised.

— Jason Edelman
Network Automation with Ansible

Style guide

Old deprecated way:

- name: http service state
  service: name=httpd state=started enabled=yes

The good way:

- name: http service state
  service:
    name: httpd
    state: started
    enabled: yes

Example:

A simple playbook

playbook.yml

Troubleshooting

  • register module: store the output of a task to use/investigate laster

  • debug module: display the content of a var

  • -v or -vv up to -vvvv: to be used with ansible-playbook

  • --syntax-check: syntax checking

  • --check: "dry-run" or "noop" mode

  • --step: take a pause at every task

  • --start-at-task="start httpd service": start from a specific task of the playbook

Variables

Variable precedence from least to greatest (the last listed variables winning prioritization):

  • command line values (eg “-u user”)

  • role defaults

  • inventory file or script group vars

  • inventory group_vars/all

  • playbook group_vars/all

  • inventory group_vars/*

  • playbook group_vars/*

  • inventory file or script host vars

  • inventory host_vars/*

  • playbook host_vars/*

  • host facts / cached set_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)

  • include_vars

  • set_facts / registered vars

  • role (and include_role) params

  • include params

  • extra vars (always win precedence)

playbook-with-var.yml

Using variables: "{{ variable_key }}"

Careful! You might need to surround the variable between quotes.

  - name: install package
    yum:
      name: "{{ package_name }}"
      state: latest
  - name: install Python library
    yum:
      name: python3_{{ library_name }}
      state: latest

This is OK too:

  - name: install Python library
    yum:
      name: "python3_{{ library_name }}"
      state: latest

Storing secrets with vaults

Never store secrets in plain text!

ansible-vault create secret.yml

password: super_secret

ansible-playbook playbook-vault.yml --ask-vault-pass

Facts

gather_facts: true (default is true)

"ansible_facts": {
    "_facts_gathered": true,
    "all_ipv4_addresses": [
        "192.168.122.132"
    ],
    "all_ipv6_addresses": [
        "fe80::6392:81c2:92fd:af3e"
    ],
[...]

playbook-facts.yml

Note
setup module gather facts about remote host: https://docs.ansible.com/ansible/latest/modules/setup_module.html

Local facts

/etc/ansible/facts.d/preferences.fact

[general]
org=ibm
type=small

ansible -i inventory all -m setup -a "filter=ansible_local"

playbook-local-fact.yml

Loops and conditionals

playbook-loop.yml: loop over a list

playbook-loop-advanced.yml: loops over a dictionary stored in a variable

playbook-facts.yml: demonstrating the usage of the when conditional

Handlers

Handlers are triggered when a task changes something (appears yellow).

Run playbook-handler.yml once, then run again and notice the difference.

Important
Make sure the MOTD is in a different state then expected prior to running the first time, to see expected result.

Running a non-idempotent script

playbook-script.yml

simple-script.sh

Fail on purpose

fail module.

Rescue blocks

playbook-block.yml

Tags

playbook-tags.yml

ansible-playbook playbook-tags.yml -t red
ansible-playbook playbook-tags.yml -t hat
ansible-playbook playbook-tags.yml -t "red","hat"

Jinja2 templates

playbook-template.yml

motd.j2

Timestamps

playbook-timestamp.yml

Rolling upgrades

playbook-rolling.yml

Now change the serial value from 1 to 3.

When you’re doing rolling upgrades of a cluster, you might want to use any_errors_fatal: True.

Limits

ansible-playbook --limit host1 playbook.yml

Performances

By default, Ansible runs each task on all hosts affected by a play before starting the next task on any host, using 5 forks.

forks: 5 in ansible.cfg

Roles

Under the roles directory (to create if needed):

ansible-galaxy init my_new_role

playbook-role.yml

Delegating jobs

By default, playbooks are run against an inventory

hosts: all

playbook-delegated.yml

Interacting with APIs

uri module!

ansible-playbook playbook-api.yml --ask-vault-pass

In Memory Inventory

Add a host (and alternatively a group) to the ansible-playbook in-memory inventory

Typically useful when working with OpenStack, etc.

Overriding the changed result

changed_when: "output.rc != 2"

Does Ansible has an API?

Tower!

Galaxy

Tooling

Demonstrate vscode + ansible + git plugin.

Zen of Python

python -m this

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Write module

You can write your own modules!