9. Playbooks

9.1. About Playbooks

  • a play links hosts / hostgroups with tasks or roles
  • a playbook contains one or more play
  • playbooks are the basis for a really simple configuration management
  • playbooks are expressed in YAML with a minimum syntax
  • a playbook can import other playbooks with import_playbook:
  • plays are executed in sequence
---
- name: first play            <- play name (optional)
  hosts: all                       <- host / hostgroup
  tasks:                           <- task list
  - name: the first task           <- first task
    apt:
      name: apache2
      state: latest

# playbook with imported playbooks
---
# file: site.yml
- import_playbook: webservers.yml
- import_playbook: dbservers.yml
  • it’s possible to set variables and settings in a playbook (e.g become, remote_user, etc.)
# advaned playbook with two plays and become & remote_user
---
- name: play 1
  hosts: all
  become: yes
  tasks:
  - name: install apache
    apt:
      name: apache2
      state: latest

- name: play 2
  hosts: all
  remote_user: special-user
  become: yes
  tasks:
  - name: install pip
    apt:
      name: python-pip
      state: latest

9.2. Create and run the first playbook

  • Create a new file
  • Start the YAML ---
  • Define the hosts:
  • Create the tasks: list
  • Add your first task
  • Save the file as apache2.yml
  • Run the playbook ansible-playbook apache2.yml
---
- hosts: web1.pascal.lab
  tasks:
  - name: install apache2
    apt:
      name: apache2
      state: latest

9.2.1. Action Result

  • there are 4 possible results after a run
PLAY RECAP *********************************************************************
web1.pascal.lab            : ok=2    changed=1    unreachable=0    failed=0
Result Description
ok nothing changed, all okay
changed the task changed the state
failed the task failed
unreachable the server was unreachable

9.2.2. Privilege Escalation

  • Ansible allows you ‘become’ another user, different from the user that logged into the machine (remote user).
  • these settings can be set from play to task level

9.3. Check Mode (Dry Run)

  • simulate a run with the --check option -> no changes are made (read-only)
  • skip tasks in check mode with when: not ansible_check_mode
  • ignore errors in check mode with ignore_errors: "{{ ansible_check_mode }}"
  • show the differences in a run with the --diff option (useful in combination with the check mode)
# check mode
ansible-playbook test.yml --check

# diff mode
ansible-playbook test.yml --check --diff

Important

Not all modules support the check / diff mode! But most of the primary core modules do. Other modules that do not support check mode will also take no action, but just will not report what changes they might have made.

9.4. Error Handling

  • ignore_errors: yes -> ignore all errors for a task
  • changed_when -> override the changed result of a task
  • failed_when -> control, if the task is failed
  • any_errors_fatal -> mark all hosts as failed if any tasks fails (in the playbook)
# ignore_errors
- name: this will not be counted as a failure
  command: /bin/false
  ignore_errors: yes

# failed_when
- 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

# changed_when
- shell: /usr/bin/billybass --mode="take me to the river"
  register: bass_result
  changed_when: "bass_result.rc != 2"

# this will never report 'changed' status
- shell: wall 'beep'
  changed_when: False

# mark all hosts as failed if any fails
- hosts: somehosts
  any_errors_fatal: true
  roles:
   - myrole

Hint

Be sure what you do, if you set ignore_errors: true

9.5. Tags

  • playbooks and tasks are supporting tags

  • useful to run only a specificed part

  • multiple tags are allowed

  • it’s also possible to skip tags

  • special tags:
    • never, prevent a task from running unless a tag is specifically requested (like debug & never)
    • always, always run a task, unless specifically skipped (--skip-tags always)
    • tagged, which run only tagged
    • untagged, which run only untagged
    • all, run all tasks
- name: install httpd package
  yum:
    name: httpd
    state: present
  tags:
    - httpd
    - installation
# run httpd and installation tags
ansible-playbook play.yml --tags "configuration,installation"

# skip notification tags
ansible-playbook example.yml --skip-tags "notification"

9.6. Playbook Options

  • --tags / --skip-tags -> only run / skip a specified tag
  • --limit -> only run on a specified host/hostgroup -> nützlich!
  • --step -> run step-by-step and confirm every task (yes/no/continue)
  • --check -> don’t execute, only check
  • --diff -> print out the differences, if implemented in the modules
  • --syntax-check -> YAML syntax check
  • --list-tasks -> list the tasks, that would be run in the playbooks
  • --list-hosts -> list the hosts, on which the plays would run
  • --force-handlers -> run handlers even if a task fails

9.7. Strategies

  • Linear -> Run each task before the next starts, 5 forks parallel (default)
  • Serial -> Run everything on a subset of hosts
  • free -> Run everything on each host until the end of the play

9.8. +Blocks

Requirement: Variables chapter

  • logical grouping of tasks (when, become, etc. can be set on block level)

  • also useful for error handling -> try / except / finally
    • rescue -> tasks to run after a fail e.g flush_handlers
    • always -> always executed tasks e.g good for clean-up / restore
---
- hosts:
    - web1.pascal.lab

  tasks:
  - name: my first block (added in ansible 2.3)
    block:
    - name: "task1 in block1"
      debug:
        msg: "execute normally"

    - name: "task2 in block1"
      command: /bin/false

    rescue:
    - name: "rescue block1"
      debug:
        msg: "caught an error"

    always:
    - name: "always block1"
      debug:
        msg: "this always executes"

    when: ansible_distribution == 'Debian'
    tags:
      - test1
      - blockxy

    become: yes