New features in Ansible 2.0: Blocks

⚠️ Warning
This post is more than 10 years old. I do not delete posts, because even old information is still useful, but please know that some material on this page may be outdated or incorrect. Thanks!
Sep 12, 2015
The following is an excerpt from Chapter 5 of Ansible for DevOps, a book on Ansible by Jeff Geerling.

Introduced in Ansible 2.0.0 (still in active development, currently in alpha), Blocks allow you to group related tasks together and apply particular task parameters on the block level. They also allow you to handle errors inside the blocks in a way similar to most programming languages' exception handling.

Here's an example playbook that uses blocks with when to run group of tasks specific to one platform without when parameters on each task:

---
- hosts: web
  tasks:
    # Install and configure Apache on RedHat/CentOS hosts.
    - block:
        - yum: name=httpd state=installed
        - template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
        - service: name=httpd state=started enabled=yes
      when: ansible_os_family == 'RedHat'
      sudo: yes

    # Install and configure Apache on Debian/Ubuntu hosts.
    - block:
        - apt: name=apache2 state=installed
        - template: src=httpd.conf.j2 dest=/etc/apache2/apache2.conf
        - service: name=apache2 state=started enabled=yes
      when: ansible_os_family == 'Debian'
      sudo: yes

If you want to perform a series of tasks with one set of task parameters (e.g. with_items, when, or sudo) applied, blocks are quite handy.

Blocks are also useful if you want to be able to gracefully handle failures in certain tasks. There might be a task that connects your app to a monitoring service that's not essential for a deployment to succeed, so it would be better to gracefully handle a failure than to bail out of the entire deployment!

Here's how to use a block to gracefully handle task failures:

tasks:
  - block:
      - name: Shell script to connect the app to a monitoring service.
        script: monitoring-connect.sh
    rescue:
      - name: This will only run in case of an error in the block.
        debug: msg="There was an error in the block."
    always:
      - name: This will always run, no matter what.
        debug: msg="This always executes."

Tasks inside the block will be run first. If there is a failure in any task in block, tasks inside rescue will be run. The tasks inside always will always be run, whether or not there were failures in either block or rescue.

Blocks can be very helpful for building reliable playbooks, but just like exceptions in programming languages, block/rescue/always failure handling can overcomplicate things. If it's easier to maintain idempotency using failed_when per-task to define acceptable failure conditions, or to structure your playbook in a different way, it may not be necessary to use block/rescue/always.

Read more about Blocks on the official Blocks documentation page.

Read Ansible for DevOps, available on LeanPub: