Blocks

Blocks allow for logical grouping of tasks and in play error handling. Most of what you can apply to a single task (with the exception of loops) can be applied at the block level, which also makes it much easier to set data or directives common to the tasks. This does not mean the directive affects the block itself, but is inherited by the tasks enclosed by a block. i.e. a when will be applied to the tasks, not the block itself.

Block example

  1. tasks:
  2. - name: Install Apache
  3. block:
  4. - yum:
  5. name: "{{ item }}"
  6. state: installed
  7. with_items:
  8. - httpd
  9. - memcached
  10. - template:
  11. src: templates/src.j2
  12. dest: /etc/foo.conf
  13. - service:
  14. name: bar
  15. state: started
  16. enabled: True
  17. when: ansible_facts['distribution'] == 'CentOS'
  18. become: true
  19. become_user: root

In the example above, each of the 3 tasks will be executed after appending the when condition from the blockand evaluating it in the task’s context. Also they inherit the privilege escalation directives enabling “become to root”for all the enclosed tasks.

New in version 2.3: The name: keyword for block: was added in Ansible 2.3.

Error Handling

Blocks also introduce the ability to handle errors in a way similar to exceptions in most programming languages.Blocks only deal with ‘failed’ status of a task. A bad task definition, an undefined variable or an unreachable host are not rescuable errors.

Block error handling example

  1. tasks:
  2. - name: Handle the error
  3. block:
  4. - debug:
  5. msg: 'I execute normally'
  6. - name: i force a failure
  7. command: /bin/false
  8. - debug:
  9. msg: 'I never execute, due to the above task failing, :-('
  10. rescue:
  11. - debug:
  12. msg: 'I caught an error, can do stuff here to fix it, :-)'

This will ‘revert’ the failed status of the task for the run and the play will continue as if it had succeeded.

There is also an always section, that will run no matter what the task status is.

Block with always section

  1. - name: Always do X
  2. block:
  3. - debug:
  4. msg: 'I execute normally'
  5. - name: i force a failure
  6. command: /bin/false
  7. - debug:
  8. msg: 'I never execute :-('
  9. always:
  10. - debug:
  11. msg: "This always executes, :-)"

They can be added all together to do complex error handling.

Block with all sections

  1. - name: Attempt and graceful roll back demo
  2. block:
  3. - debug:
  4. msg: 'I execute normally'
  5. - name: i force a failure
  6. command: /bin/false
  7. - debug:
  8. msg: 'I never execute, due to the above task failing, :-('
  9. rescue:
  10. - debug:
  11. msg: 'I caught an error'
  12. - name: i force a failure in middle of recovery! >:-)
  13. command: /bin/false
  14. - debug:
  15. msg: 'I also never execute :-('
  16. always:
  17. - debug:
  18. msg: "This always executes"

The tasks in the block would execute normally, if there is any error the rescue section would get executedwith whatever you need to do to recover from the previous error.The always section runs no matter what previous error did or did not occur in the block and rescue sections.It should be noted that the play continues if a rescue section completes successfully as it ‘erases’ the error status (but not the reporting),this means it won’t trigger max_fail_percentage nor any_errors_fatal configurations but will appear in the playbook statistics.

Another example is how to run handlers after an error occurred :

Block run handlers in error handling

  1. tasks:
  2. - name: Attempt and graceful roll back demo
  3. block:
  4. - debug:
  5. msg: 'I execute normally'
  6. notify: run me even after an error
  7. - command: /bin/false
  8. rescue:
  9. - name: make sure all handlers run
  10. meta: flush_handlers
  11. handlers:
  12. - name: run me even after an error
  13. debug:
  14. msg: 'This handler runs even on error'

New in version 2.1.

Ansible also provides a couple of variables for tasks in the rescue portion of a block:

  • ansible_failed_task
  • The task that returned ‘failed’ and triggered the rescue. For example, to get the name use ansible_failed_task.name.
  • ansible_failed_result
  • The captured return result of the failed task that triggered the rescue. This would equate to having used this var in the register keyword.

See also