New features in Ansible 2.0: Blocks

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:

Comments

Hello Jeff,
Thanks for your helpful posts.

Not sure if this will reach you as this is an old post.. I am wondering if you have any idea /way to to run a block or an import task in a loop till a condition is met.

I have tried various combination,
-- Block with until , retries <-- unsupported
-- include_tasks with until, retries <--does not go into retries , just runs once

Any input will be really helpful!

thanks
Rads

I would ask about this in the ansible room on Freenode IRC or the Google Group. Some things are not fully supported on the block level... and I can sometimes get more flexibility using include_tasks.