notes blog about

Terminology

Entity relationship:

Playbook

Module

Ansible workflow for each task

  1. generate a Python script
  2. copy the script to the servers (hosts)
  3. execute the script
  4. wait for the script to complete on all hosts

True vs yes

You’re best off writing playbooks for your org rather than trying to reuse generic playbooks.

One liners

$ ansible all -m ping
$ ansible all [-m command] -a uptime
$ ansible host1 -b -a "tail /var/log/syslog" # -b -> become
$ ansible host1 -b -m apt -a name=nginx
$ ansible all -i inventories/dev -b -m apt -a "name=nagios-nrpe-server state=absent"

$ ansible-tools help init

$ ansible -i stage waf1 -m fetch -a "src=/home/ubuntu/03_gen_whitelists \
dest=./roles/nginx-naxsi/templates/events_mngt/ flat=yes"

ansible -m authorized_key -a "key=\"{{lookup('file','/tmp/dude.pub')}}\" \
user=ubuntu stage=present" all -i inventories/dev

# Viewing all facts associated with a server
ansible server1 -m setup

Quoting

If you reference a variable right after the module name:

- name: perform some task
  command: "{{ myapp }} -a foo"

If your argument contains a collon:

- name: show a debug msg
  debug: "msg='The debug module will print a message: neat, eh?'"

Variables

1) In inventory file

2) In host_vars, group_vars directories

$ cat group_vars/production
db_primary_host: buda.example.org
# accessed as {{ db_primary_host }}

$ cat group_vars/production_dict
db:
  primary:
    host: buda.example.org
# accessed as {{ db.primary.host }}

3) In role’s defaults directory - have the lowest priority of any variables available

4) In vars section of a playbook - simplest way to define variables

vars:
  key_file: /etc/nginx/ssl/nginx.key
  cert_file: /etc/nginx/ssl/nginx.crt
  conf_file: /etc/nginx/sites-available/default
  server_name: localhost

5) In playbooks loaded by vars_file

vars_files:
 - nginx.yml

6) As arguments to a role

7) On the command line

See variables for more.

Roles

Primary mechanism for breaking a playbook into multiple files

If you think you might want to change the value of a variable in a role (via vars section of a play or role’s arguments), use a default variable (defaults). If you don’t want it to change, use a regular variable (vars).

Roles with variables:

- name: deploy postgres on vagrant
  hosts: db
  vars_files:
    - secrets.yml
  roles:
    - role: database
      database_name: "{{ mezzanine_proj_name }}"
      database_user: "{{ mezzanine_proj_name }}"

- name: deploy mezzanine on vagrant
  hosts: web
  vars_files:
    - secrets.yml
  roles:
    - role: mezzanine
      database_host: "{{ hostvars.db.ansible_eth1.ipv4.address }}"
      live_hostname: 192.168.33.10.xip.io
      domains:
        - 192.168.33.10.xip.io
        - www.192.168.33.10.xip.io

See roles for more.

Tips and tricks

Achieve idempotence with a command module:

- name: create a Vagrantfile
  command: vagrant init {{ box }} creates=Vagrantfile

Change the way Ansible identifies that a task has changed state (changed_when):

- name: initialize the database
    django_manage:
      command: createdb --noinput --nodata
      app_path: "{{ proj_path }}"
      virtualenv: "{{ venv_path }}"
    register: result
    #changed_when: '"Creating tables" in result.out|default("")'
    changed_when: result.out is defined and "Creating tables" in result.out
- name: Import logs into ES
  become: no
  shell: ./nxtool.py -c nxapi.json --files=/var/log/nginx/naxsi.log 2>&1 | perl -ne 'print $1 if /Written\s+(\d+)\s+events/'
  register: result
  changed_when: result.stdout != "0"  # NOTE: this was tricky to get right! :-)
  args:
    chdir: /home/ubuntu/nginx-naxsi/naxsi-master/nxapi
  tags:
    - nxapi
- name: Get hostname of the control host
  command: hostname
  register: hostname
  delegate_to: 127.0.0.1
  become: no
  # no yellow output :-)
  changed_when: false

View the output of a task:

- name: initialize the database
  django_manage:
    command: createdb --noinput --nodata
    app_path: "{{ proj_path }}"
    virtualenv: "{{ venv_path }}"
  failed_when: False # so the execution doesn't stop on failure
  register: result   # save the output to a variable
  
# print out the variable...
- debug: var=result

# stop the execution...
- fail:

Run a task and print its output, even if it fails:

- name: Run myprog
  command: /opt/myprog
  register: result
  ignore_errors: True

- debug: var=result

- debug: msg="Stop running the playbook if myprog failed"
  failed_when: result|failed # filters for registered variables:
  # - failed
  # - changed
  # - success
  # - skipped
  
# more tasks here

Have multiple ansible versions on a laptop:

mkdir ansibles
cd ansibles
# see https://github.com/ansible/ansible/releases for versions
VER=v2.5.2; git clone -b $VER --recursive https://github.com/ansible/ansible.git $VER
source ./$VER/hacking/env-setup
ansible --version

Deal with unreliable networks or services

The last four lines do the trick:

    - name: Safe-upgrade all system packages
      apt:
        upgrade: safe
        state: latest
        update_cache: yes
      tags:
        - sys-upgrade
      register: upgrade
      retries: 3
      delay: 3
      until: upgrade is not failed
    - name: Generate or renew cert
      command: certbot certonly --noninteractive --standalone -d {{ fqdn }}
      notify:
        - restart container
      register: result
      retries: 3
      delay: 3
      until: result.rc == 0

Sources