How to loop thru win_updates until no updates left

1,613 views
Skip to first unread message

aut...@gmail.com

unread,
Jan 2, 2017, 10:39:48 AM1/2/17
to Ansible Project
Hi,

I'm using win_updates module to carry out windows patching and it works pretty good.
Sometime, updates have dependencies and multiple playbook execution required.

I was thinking, if possible, to loop the playbook until "found_update_count > 0" without the need of running ansible-playbook multiple time after each finish.

My playbook looks like this:

- hosts: win_server_1
  ignore_errors: true
  gather_facts: true

  tasks:
    - name: search wu
      win_updates:
        category_names:
          - UpdateRollups
          - CriticalUpdates
          - SecurityUpdates
        state:
          - searched
        log_path:
          - c:/temp/ansible_wu.txt
      register: searched
    
    - name: install wu
      win_updates:
        category_names:
          - UpdateRollups
          - CriticalUpdates
          - SecurityUpdates
        state:
          - installed
        log_path:
          - c:/temp/ansible_wu.txt
      register: installed
      when: searched.found_update_count > 0
    
    - name: reboot
      win_reboot:
        pre_reboot_delay_sec: 0
        test_command: whoami
        reboot_timeout_sec: 300
      when: installed.reboot_required = 'true'
      register: reboot


I cant figure out how to put this in playbook by myself, seeking for some guidance.

Thanks,

Trond Hindenes

unread,
Jan 2, 2017, 2:28:00 PM1/2/17
to Ansible Project
I can't from the top of my head recall if the "block" feature supports loops, but if it does I guess that's the best way to do this.

As far as I can remember, Microsoft's own config management tool for clients (SCCM) gets around this by simply doing 2 "passes" of patching. You could do that aswell, with some conditionals to only kick the second pass if it's needed.

aut...@gmail.com

unread,
Jan 3, 2017, 7:18:39 AM1/3/17
to Ansible Project
Hi,

Thank for the heads up but it seems like Block does not support 'until'

ERROR! 'until' is not a valid attribute for a Block

aut...@gmail.com

unread,
Jan 3, 2017, 7:56:05 AM1/3/17
to Ansible Project
I've also read a post by Brian Coca which stated that "blocks do not support any type of loop".
What other options can be used to re-run multiple tasks (one that checks/installs updates and the other reboots the server) until no updates available?

Danny Rehelis

unread,
Jan 4, 2017, 1:44:36 PM1/4/17
to ansible...@googlegroups.com
Anyone? :-(

--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-project+unsubscribe@googlegroups.com.
To post to this group, send email to ansible-project@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/792346e5-85b6-4b00-a345-9edd1bb2934a%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Danny Rehelis - autogun [AT] gmail.com

Dag Wieers

unread,
Jan 8, 2017, 3:01:33 PM1/8/17
to ansible...@googlegroups.com
On Wed, 4 Jan 2017, Danny Rehelis wrote:

> Anyone? :-(

Never tried it myself, but wouldn't this work ?

- include: update-windows.yml
until: <condition>
retries: 10

You will have to make sure that the condition is based on facts from the
tasks in your taskbook, which include win_updates and win_reboot.

The only other option I see is to add win_reboot capabilities to
win_updates, which would be a shame if that's what is needed.

--
Dag

Danny Rehelis

unread,
Jan 8, 2017, 4:38:17 PM1/8/17
to ansible...@googlegroups.com
I actually tried this and it didn't work.

--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-project+unsubscribe@googlegroups.com.
To post to this group, send email to ansible-project@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

aut...@gmail.com

unread,
Jan 9, 2017, 8:44:50 AM1/9/17
to Ansible Project
I've found a working solution, it's ugly and probably will cause more headache but it works.

My solution looks like this:

---
- hosts: localhost
  ignore_errors: true
#  gather_facts: true

  tasks:
   - shell: ansible-playbook wu.yml
     register: results
     until: "'\"found_update_count\": 0' in results.stdout"
         retries: 5


wu.yml:

---
- hosts: bunch_of_hosts
  ignore_errors: true
  #gather_facts: true

  tasks:
    - block:
        - name: search wu
          win_updates:
            category_names:
              - CriticalUpdates
              - SecurityUpdates
              - UpdateRollups
            state:
              - searched
            log_path:
              - c:/temp/ansible_wu.txt
          register: searched
        - debug: var=searched
       
        - name: install wu
          win_updates:
            category_names:
              - CriticalUpdates
              - SecurityUpdates
              - UpdateRollups
            state:
              - installed
            log_path:
              - c:/temp/ansible_wu.txt
          when: searched.found_update_count > 0
          register: installed
        - debug: var=installed
        
        - name: reboot
          win_reboot:
            pre_reboot_delay_sec: 0
            test_command: whoami
            reboot_timeout_sec: 300
          when: installed.reboot_required
          register: rebooted
        - debug: var=rebooted

This way, it does exactly what I was after.

J Hawkesworth

unread,
Jan 11, 2017, 9:26:01 AM1/11/17
to Ansible Project
Nice trick, thank you for sharing this.

Jon
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To post to this group, send email to ansible...@googlegroups.com.

Matt Davis

unread,
Jan 11, 2017, 9:02:26 PM1/11/17
to Ansible Project
The solution Dag posted is what I've always done, and it works great for me. I've been advocating for block loop support (as a cleaner solution to exactly this issue) since before it shipped, but I don't have the bandwidth to implement myself right now, and around here it's kinda "put up or shut up". ;) If it doesn't work for you, let us know why and maybe we can get it figured out.

I really wouldn't recommend the "run the playbook in a loop" thing- you lose a lot of output fidelity and error handling, and it's really just a way more expensive way to do what Dag suggested.

I actually originally wrote win_updates with a wrapper action that would handle the reboots automatically, but for various reasons (that I can't recall) decided to abandon the wrapper before I shipped it...

-Matt

Danny Rehelis

unread,
Jan 12, 2017, 4:28:05 AM1/12/17
to ansible...@googlegroups.com
Hi Matt,

Dag's solution was something I really hoped to work but then I found this post by Brian Coca - https://groups.google.com/forum/#!topic/ansible-project/xGGe6WADtH0

Seems like this is not possible because "Include is not a module, more like a preprocessing macro."

--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-project+unsubscribe@googlegroups.com.
To post to this group, send email to ansible-project@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/2ebf7ecd-8da0-4b1d-99ba-f886721a9d2c%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Jinesh Choksi

unread,
Jan 12, 2017, 12:07:51 PM1/12/17
to Ansible Project
Hi Danny,



> Right now looping over includes is doable by doing a with_ statement. What I trying to achieve right now would look like:
>
>- include: run-in-loop.yml
>  with_sequence: start=0 end=<+INFINITE>
>  loop_control:
>    loop_var: main_item


You would need to break execution in the included playbook if you detect that updates are done using - http://stackoverflow.com/a/22760829

Regards,
Jinesh

Matt Davis

unread,
Jan 12, 2017, 5:26:29 PM1/12/17
to Ansible Project
That post is really old- include looping has worked for a long time...

aut...@gmail.com

unread,
Jan 13, 2017, 5:54:39 AM1/13/17
to Ansible Project
Then I cannot figure out what am I doing wrong, can you please assist?

main.yml:

---
- hosts: windows_servers
  ignore_errors: true
  gather_facts: false

  tasks:
    - name: inner playbook
      include: update-windows.yml
      until: 'searched_inner.found_update_count > 0' 
      retries: 10
    - debug: var=searched_inner

update-windows.yml:

- name: search wu
  win_updates:
    category_names:
      - CriticalUpdates
      - SecurityUpdates
      - UpdateRollups
    state:
      - searched
    log_path:
      - c:/temp/ansible_wu.txt
  register: searched

- set_fact:
    searched_inner: "{{ searched }}"

and when I execute it, no until loop is actually happening:

[root@ansiblecm more_tires]# ansible-playbook main.yml --limit try

PLAY [windows_servers] *********************************************************

TASK [search wu] ***************************************************************
 [WARNING]: Module invocation had junk after the JSON data:

changed: [try]

TASK [set_fact] ****************************************************************
ok: [try]

TASK [debug] *******************************************************************
ok: [try] => {
    "searched_inner": {
        "changed": true, 
        "found_update_count": 189,
        "installed_update_count": 0, 
        "reboot_required": false, 
        "updates": {
            "0025cccd-6122-483a-a646-dd3ea3ff861f": {
                "id": "0025cccd-6122-483a-a646-dd3ea3ff861f", 
                "installed": false, 
                "kb": [
                    "3179574"
                ], 
                "title": "Update for Windows Server 2012 R2 (KB3179574)"
            }, 
...
...
...
PLAY RECAP *********************************************************************
try                        : ok=3    changed=1    unreachable=0    failed=0   

Playbook run took 0 days, 0 hours, 2 minutes, 4 seconds


Why until is not triggered here by until: 'searched_inner.found_update_count > 0'
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To post to this group, send email to ansible...@googlegroups.com.

Jinesh Choksi

unread,
Jan 13, 2017, 9:14:58 AM1/13/17
to Ansible Project
- "until: is not currently supported on includes." (https://github.com/ansible/ansible/issues/17098)

- "When you call include, Ansible actually places tasks from included file into the execution queue after the current task." (http://stackoverflow.com/a/38481496)


The example below is a hack but it has a useful property whereby tasks in the inner taskbook will not be run if the play was ended. i.e.

    calling inner-taskbook 1st time
    - running tasks

    calling inner-taskbook 2nd time
    - running tasks

    calling inner-taskbook 3rd time
    - running tasks
    - play ended because we've determined that we've finished the work

    calling inner-taskbook 4th time
    - noop

    calling inner-taskbook 5th time
    - noop

    calling inner-taskbook nth time
    - noop


So if you configure the with_sequence parameters as you would have done the "until:" command's "retries" attribute then you kinda simulate the retry functionality with the example below.

You will notice that the taskbook does get "included" 100 times but the tasks in it are ONLY executed the number of times actually required.



run.sh:
---snip---
#!/usr/bin/env bash

ansible-playbook -vvv  -i 'localhost,' -c local main.yml
---snip---



main.yml:

---
- hosts:
   - localhost

  tasks:
   - name: "Performing tasks before windows updates."
     debug: msg="blah blah"

    - name: "Calling playbook that will install windows updates."
     include: inner-taskbook.yml
     with_sequence: start=1 end=100
     loop_control:
       loop_var: main_item

    - name: "Performing tasks after windows updates."
     debug: msg="blah blah blah"



inner-taskbook.yml:
- name: "Performing Windows updates, printing loop counter for reference {{ main_item }}."
 debug: var=main_item

- name: "Task to determine whether further updates are needed."
 set_fact:
   updates_needed: "{{ 10|random }}"

- name: "Ending play if no further updates needed. {{ updates_needed }}"
 meta: end_play
 when: updates_needed  == '1'



Jinesh Choksi

unread,
Jan 13, 2017, 9:41:49 AM1/13/17
to Ansible Project
Ack. I just noticed that the example below is not a good solution since it never did run the "Performing tasks after windows updates." task in the main.yml file.

Sorry. Please ignore the example.

Danny Rehelis

unread,
Jan 19, 2017, 3:00:52 PM1/19/17
to ansible...@googlegroups.com
Bummer, this was the best solution so far!

--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-project+unsubscribe@googlegroups.com.
To post to this group, send email to ansible-project@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/d109f167-b52d-40f4-bc34-7c7bc9f5ffcd%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages