Run pre/post tasks once with serial 1

I have a playbook where I would like to do a task once before starting a couple roles and once after roles with serial 1. I thought I could accomplish this with pre and post tasks but it looks like they are called once per server.

you want run_once, not serial

My real use case is a patching playbook. I have a collection of servers I want to patch one at a time (serial: 1) but there are a set of decommision steps I only want to run once for the entire group. Then patch the servers one by one. Then there is a set of commission tasks I want to run once for the entire group. If I put run once on the pre/post tasks. It still gets run once per play eg once per host.

If it helps, the full context is this is a cassandra cluster. I want to stop the repair service, patch the whole cluster 1 node at a time, then start the repair service.

How to make this work is documented under note section of run_once.
http://docs.ansible.com/ansible/latest/playbooks_delegation.html#run-once

Thanks.

when: inventory_hostname == ansible_play_hosts[0]

Is exactly what I need.

In case someone else has this issue, for my post task I that I only want to run on the last host I used the below conditional. Results in skipped tasks but it does accomplish the goal. Not sure if there is a more elegant way to say run on the last host.

when: inventory_hostname == ansible_play_hosts[ ansible_play_hosts | length - 1]

This results in

TASK [command] **************************************************************************************************************************************
changed: [mdl-swch01] => {“changed”: true, “cmd”: “echo "pre"”, “delta”: “0:00:00.002647”, “end”: “2018-02-15 11:19:37.098522”, “rc”: 0, “start”: “2018-02-15 11:19:37.095875”, “stderr”: “”, “stderr_lines”: , “stdout”: “pre”, “stdout_lines”: [“pre”]}

TASK [command] **************************************************************************************************************************************
changed: [mdl-swch01] => {“changed”: true, “cmd”: “echo "task 1"”, “delta”: “0:00:00.003688”, “end”: “2018-02-15 11:19:43.144388”, “rc”: 0, “start”: “2018-02-15 11:19:43.140700”, “stderr”: “”, “stderr_lines”: , “stdout”: “task 1”, “stdout_lines”: [“task 1”]}

TASK [command] **************************************************************************************************************************************
changed: [mdl-swch01] => {“changed”: true, “cmd”: “echo "task 2"”, “delta”: “0:00:00.002865”, “end”: “2018-02-15 11:19:49.189866”, “rc”: 0, “start”: “2018-02-15 11:19:49.187001”, “stderr”: “”, “stderr_lines”: , “stdout”: “task 2”, “stdout_lines”: [“task 2”]}

TASK [command] **************************************************************************************************************************************
skipping: [mdl-swch01] => {“changed”: false, “skip_reason”: “Conditional result was False”}

PLAY [Hello World Linux] ****************************************************************************************************************************

TASK [command] **************************************************************************************************************************************
skipping: [mdl-swch02] => {“changed”: false, “skip_reason”: “Conditional result was False”}

TASK [command] **************************************************************************************************************************************
changed: [mdl-swch02] => {“changed”: true, “cmd”: “echo "task 1"”, “delta”: “0:00:00.002640”, “end”: “2018-02-15 11:19:50.853990”, “rc”: 0, “start”: “2018-02-15 11:19:50.851350”, “stderr”: “”, “stderr_lines”: , “stdout”: “task 1”, “stdout_lines”: [“task 1”]}

TASK [command] **************************************************************************************************************************************
changed: [mdl-swch02] => {“changed”: true, “cmd”: “echo "task 2"”, “delta”: “0:00:00.002785”, “end”: “2018-02-15 11:19:51.261013”, “rc”: 0, “start”: “2018-02-15 11:19:51.258228”, “stderr”: “”, “stderr_lines”: , “stdout”: “task 2”, “stdout_lines”: [“task 2”]}

TASK [command] **************************************************************************************************************************************
changed: [mdl-swch02] => {“changed”: true, “cmd”: “echo "post"”, “delta”: “0:00:00.003841”, “end”: “2018-02-15 11:19:51.683943”, “rc”: 0, “start”: “2018-02-15 11:19:51.680102”, “stderr”: “”, “stderr_lines”: , “stdout”: “post”, “stdout_lines”: [“post”]}

Gah not quite there. I use meta_end play when I calculate that a patch isn’t relevant so that we don’t spend time decomissioning a host that doesn’t need a patch. Problem is this skips the post_tasks if the last server in the group isn’t relevant. I tried using handlers but that results in 1 execution per node.

I think I might need to re-think my playbook organization and simplify. I could use a workflow in tower to accomplish this simpler without skipped tasks.

when: inventory_hostname == ansible_play_hosts | last

Maybe 3 plays in a playbook will work for you

- name: Pre
  hosst:

- name: Main
  hosts:
  serial: 1

- name: Post
  hosts:

Since you want to run 3 sets of tasks on different sets of servers it would be a lot cleaner to split your playbook into three plays instead of one:

  • hosts: patchgroup[0]
    tasks:

  • debug: msg=pre

  • hosts: patchgroup
    serial: 1
    tasks:

  • debug: msg=patch

  • hosts: patchgroup[0]
    tasks:

  • debug: msg=post

This way you won’t have a bunch of skipped tasks and you can end_play during patching without affecting your post tasks, since they’re a separate play.

Thanks for the help everyone. That is exactly what I was missing. I forgot you could have different plays in the same playbook. My playbook now looks like: