Ansible Playbook: How to run Play based on when condition?
Asked Answered
Z

5

8

I have a playbook with multiple plays. Here is what it looks like:

- name: Play A
  hosts: localhost
  roles:
    - do task to get either B or C

- name: Play B
  hosts: Host B
  roles:
    - do necessary task

- name: Play C
  hosts: Host C
  roles:
    - do necessary task

- name: Play D
  host: localhost
  roles:
    - create_logs

Whenever the playbook is triggered, it will first run the first play to get either B or C result.

The result is to determine the Host attribute to run the play such as the IP address and the OS of the host.

After that, based on the result, for example if the result is B, I will run the Play B and skip the play C.

Else if the result is C, I will skip the play B and run the Play C.

Play D will be run as the last Play regardless of the result from the previous Plays.

How can I do this? I have tried using 'when' condition but it said that it is not an attribute for Play.

Zampino answered 24/3, 2020 at 7:19 Comment(0)
Z
-1

I have found other way on how to do it. The difference between Host B and Host C is only the IP address and the OS type.

Hence, I am editing the inventory/hosts file and refresh_inventory after the play completed necessary task when the playbook is running, then before finishing the last task, I edit back the inventory/hosts file to its default value that I have setup.

the parent playbook:

- name: Play A
  hosts: localhost
  roles:
    - do task to get either B or C
    - edit/replace ip address & host type for the targeted hosts in inventory/hosts file with using when condition
    - refresh_inventory

- name: Play B_C
  hosts: switches_host
  roles:
    - do necessary task

- name: Play D
  host: localhost
  roles:
    - create_logs
    - - edit/replace back to initial value

the inventory:

switches_host:
      ansible_host: s.s.s.s
      ansible_user: user
      ansible_ssh_pass: pswd
      ansible_network_os: xo.sx

Zampino answered 26/3, 2020 at 2:46 Comment(1)
It wont work if you have two concurrent playbooks which change the inventory at the same time. Better use add_host module and in-memory-inventory. Look at docs.catalystcloud.nz/tutorials/compute/…Trippet
C
8

I think I finally understood the question. I kept my original answer below which is still ok to skip a role


This is what I basically understand you want to do:

  1. determine a host you need to use based on a set of condition
  2. depending on the host that was chosen above, run a play or an other
  3. finally do some other tasks unconditionally

Here is a very basic yet fully functional example to achieve your requirement. I simplified it to a minimun set of tasks so it fits in a question. But you can add your roles back with the exact same concepts and inject your real tasks to select your targets.

The key in the playbook is to use the add_host module to push the needed host in the needed group to the in-memory inventory. For this example, any host will used the local connection so that it fakes a host but still uses localhost to run. Note that you could very well add several hosts in several groups.

I also added a basic check to make sure the value we pass for this fake host selection is inside a set of expected values. By default, the playbook uses host B but your can change it by passing -e host_we_need=C on the command line.

Enough talk: to the example ! To avoid some warnings, let's create a dummy inventory.yml which contains the two groups we need but empty:

---
group_B:
group_C:

Then the playbook:

---
- name: Determine the hosts we need
  hosts: localhost
  gather_facts: false

  tasks:
    - name: dummy task to fake you choice
      command: "echo {{ host_we_need | default('B') }}"
      register: host_choice

    - name: check condition to go on
      vars:
        allowed_hosts:
          - B
          - C
      assert:
        that:
          - host_choice.stdout in allowed_hosts
        fail_msg:
          - "Found host value is: {{ host_choice.stdout }}"
          - "It should be one of: {{ allowed_hosts | join(', ') }}"

    - name: add the needed host to in-memory inventory
      add_host:
        name: "{{ host_choice.stdout }}"
        groups:
          - "group_{{ host_choice.stdout }}"
        ansible_connection: local  # This is for a fake host for test only.

- name: play tasks for group_B
  hosts: group_B
  gather_facts: false

  tasks:
    - debug:
        msg: This a task for group_B

- name: play tasks for group_C
  hosts: group_C
  gather_facts: false

  tasks:
    - debug:
        msg: This is a task for group_C

- name: whatever leftover tasks on localhost
  hosts: localhost
  gather_facts: false

  tasks:
    - debug:
        msg: leftover localhost tasks

Gives (an example run for each situation)

$ ansible-playbook -i add inventory.yml playbook.yml

PLAY [Determine the hosts we need] **********************************

TASK [dummy task to fake you choice] ********************************
changed: [localhost]

TASK [check condition to go on] *************************************
ok: [localhost]

TASK [add the needed host to in-memory inventory] *******************
changed: [localhost]

PLAY [play tasks for group_B] ***************************************

TASK [debug] ********************************************************
ok: [B] => {
    "msg": "This a task for group_B"
}

PLAY [play tasks for group_C] ***************************************
skipping: no hosts matched

PLAY [whatever leftover tasks on localhost] *************************

TASK [debug] ********************************************************
ok: [localhost] => {
    "msg": "leftover localhost tasks"
}

PLAY RECAP **********************************************************
B         : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
localhost : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 


$ ansible-playbook -i inventory.yml playbook.yml -e host_we_need=C

PLAY [Determine the hosts we need] **********************************

TASK [dummy task to fake you choice] ********************************
changed: [localhost]

TASK [check condition to go on] *************************************
ok: [localhost]

TASK [add the needed host to in-memory inventory] *******************
changed: [localhost]

PLAY [play tasks for group_B] ***************************************
skipping: no hosts matched

PLAY [play tasks for group_C] ***************************************

TASK [debug] ********************************************************
ok: [C] => {
    "msg": "This is a task for group_C"
}

PLAY [whatever leftover tasks on localhost] *************************

TASK [debug] ********************************************************
ok: [localhost] => {
    "msg": "leftover localhost tasks"
}

PLAY RECAP **********************************************************
C         : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
localhost : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
 

(original answer which does not exactly fulfill the requirement)

You can add the when condition to your role.

- name: Play B
  hosts: Host B
  roles:
    - role: my_b_role
      when: my_condition | bool

The down side is that you need to add the condition to any role or task in your play.

Chicken answered 24/3, 2020 at 8:29 Comment(6)
Hi, thanks for the suggestion. But I think I haven't explain that the task in Play A did is to determine which Host it will go to. Means, I intend to not initiate at all the unnecessary Play.Zampino
@Chicken 'when' is not a valid attribute for a Play, did you test it?Rumble
@RizwanJavid what makes you think I added the when attribute to the play ? It is added to a role declaration inside the play as stated in my first sentence and written in my example. And yes, the above is tested (and copied/adapted from a playbook used every day in prod)Chicken
@Chicken you can skip the role not the play with this approach, that is what i mean, as owner has mentioned in question to skip the play; if he will follow your approach all hosts mentioned in this playbook, Host A, Host B, Host C, Host D, will be executed and roles can be skipped but execution of plays can't be. And how conditional variable set in Play A, will be available in play B- the explanation is not written.Rumble
@RizwanJavid probably a bit late but comming accross this answer I think I saw the light.... and updated it.Chicken
@Chicken better late than never. cheers dudeRumble
A
4

You can split the plays into different playbook files, add a task in Play A that uses the include module to conditionally include Play B or Play C. Then add the "include play D" task after the "include Play B/C" tasks.

Am‚lie answered 25/3, 2020 at 1:14 Comment(0)
T
1

I did not try it but you might set the host in the first play / role with the set_fact module. So you could use this variable for the host assignment in the following plays.

Or use dynamic groups instead : https://docs.ansible.com/ansible/latest/modules/group_by_module.html

Trippet answered 25/3, 2020 at 5:59 Comment(0)
C
0

1 - You can add a play which has a host with a null assigned to ansible_host parameter. In the inventory you have a host named xyz with ansible_host: null

2 - You can add a condition to every task, so if you send a value, the condition should be true else, it will use the null value and in this case the condition is false and the task will be skipped.

Example:

---
 - name: NTU CONFIGURATION 
   hosts: NTU
   vars:
     ansible_host: "{{ NTU_IP }}" 
   connection: local
   gather_facts: no
   tasks:

     - name: TASK 1 CREAT VLAN 1000 CONFIGURATION
       ios_config:
         lines: 
           - name cust_inet
         parents:
           - vlan 1000
       when: 
           - NTU == true
 - name: another play
   hosts: CPE
   vars:
     ansible_host: "{{ IP_MGMT }}" 
   connection: local
   gather_facts: no


   tasks:
Coexist answered 4/4, 2021 at 19:33 Comment(0)
Z
-1

I have found other way on how to do it. The difference between Host B and Host C is only the IP address and the OS type.

Hence, I am editing the inventory/hosts file and refresh_inventory after the play completed necessary task when the playbook is running, then before finishing the last task, I edit back the inventory/hosts file to its default value that I have setup.

the parent playbook:

- name: Play A
  hosts: localhost
  roles:
    - do task to get either B or C
    - edit/replace ip address & host type for the targeted hosts in inventory/hosts file with using when condition
    - refresh_inventory

- name: Play B_C
  hosts: switches_host
  roles:
    - do necessary task

- name: Play D
  host: localhost
  roles:
    - create_logs
    - - edit/replace back to initial value

the inventory:

switches_host:
      ansible_host: s.s.s.s
      ansible_user: user
      ansible_ssh_pass: pswd
      ansible_network_os: xo.sx

Zampino answered 26/3, 2020 at 2:46 Comment(1)
It wont work if you have two concurrent playbooks which change the inventory at the same time. Better use add_host module and in-memory-inventory. Look at docs.catalystcloud.nz/tutorials/compute/…Trippet

© 2022 - 2024 — McMap. All rights reserved.