Build Windows Templates in RHV
This commit is contained in:
29
roles/oatakan.windows_update/.travis.yml
Normal file
29
roles/oatakan.windows_update/.travis.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
language: python
|
||||
python: "2.7"
|
||||
|
||||
# Use the new container infrastructure
|
||||
sudo: false
|
||||
|
||||
# Install ansible
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- python-pip
|
||||
|
||||
install:
|
||||
# Install ansible
|
||||
- pip install ansible
|
||||
|
||||
# Check ansible version
|
||||
- ansible --version
|
||||
|
||||
# Create ansible.cfg with correct roles_path
|
||||
- printf '[defaults]\nroles_path=../' >ansible.cfg
|
||||
|
||||
script:
|
||||
# Basic role syntax check
|
||||
- ansible-playbook tests/test.yml -i tests/inventory --syntax-check
|
||||
|
||||
notifications:
|
||||
webhooks: https://galaxy.ansible.com/api/v1/notifications/
|
||||
21
roles/oatakan.windows_update/LICENSE
Normal file
21
roles/oatakan.windows_update/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Orcun Atakan
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
53
roles/oatakan.windows_update/README.md
Normal file
53
roles/oatakan.windows_update/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# windows_update
|
||||
This repo contains an Ansible role that updates Windows systems. This role mainly utilizes the win_update module, in
|
||||
addition to that it provides retry capability for older OSes with many updates pending such as Windows 2008 R2 and
|
||||
Windows 7. It also reports on installed updates.
|
||||
|
||||
> **_Note:_** This role is provided as an example only. Do not use this in production. You can fork/clone and add/remove steps for your environment based on your organization's security and operational requirements.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too:
|
||||
|
||||
- name: update windows systems
|
||||
hosts: all
|
||||
gather_facts: True
|
||||
become: no
|
||||
vars:
|
||||
win_update_category_names:
|
||||
- CriticalUpdates
|
||||
- DefinitionUpdates
|
||||
- SecurityUpdates
|
||||
- UpdateRollups
|
||||
- Updates
|
||||
|
||||
roles:
|
||||
- oatakan.windows_update
|
||||
|
||||
For disconnected environments, you can overwrite this variable to point to a local copy of a script to enable winrm:
|
||||
|
||||
**winrm_enable_script_url:** https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1
|
||||
|
||||
you can also localize virtio-win and update the virtio_iso_url variable to point to your local url:
|
||||
|
||||
**virtio_iso_url:** https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/virtio-win-0.1.173-2/virtio-win.iso
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
Orcun Atakan
|
||||
25
roles/oatakan.windows_update/defaults/main.yml
Normal file
25
roles/oatakan.windows_update/defaults/main.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
|
||||
update_retry_limit: 10
|
||||
win_update_category_names:
|
||||
- CriticalUpdates
|
||||
- DefinitionUpdates
|
||||
- SecurityUpdates
|
||||
- UpdateRollups
|
||||
- Updates
|
||||
|
||||
hotfix_download_location: "{{ ansible_env.TEMP }}"
|
||||
|
||||
hotfixes_group_1:
|
||||
- kb: KB3020369
|
||||
file: Windows6.1-KB3020369-x64.msu
|
||||
url: https://download.microsoft.com/download/F/D/3/FD3728D5-0D2F-44A6-B7DA-1215CC0C9B75/Windows6.1-KB3020369-x64.msu
|
||||
- kb: KB3125574
|
||||
file: windows6.1-kb3125574-v4-x64_2dafb1d203c8964239af3048b5dd4b1264cd93b9.msu
|
||||
url: http://download.windowsupdate.com/d/msdownload/update/software/updt/2016/05/windows6.1-kb3125574-v4-x64_2dafb1d203c8964239af3048b5dd4b1264cd93b9.msu
|
||||
- kb: KB4474419
|
||||
file: windows6.1-kb4474419-v3-x64_b5614c6cea5cb4e198717789633dca16308ef79c.msu
|
||||
url: http://download.windowsupdate.com/c/msdownload/update/software/secu/2019/09/windows6.1-kb4474419-v3-x64_b5614c6cea5cb4e198717789633dca16308ef79c.msu
|
||||
- kb: KB4490628
|
||||
file: windows6.1-kb4490628-x64_d3de52d6987f7c8bdc2c015dca69eac96047c76e.msu
|
||||
url: http://download.windowsupdate.com/c/msdownload/update/software/secu/2019/03/windows6.1-kb4490628-x64_d3de52d6987f7c8bdc2c015dca69eac96047c76e.msu
|
||||
229
roles/oatakan.windows_update/files/win-updates.ps1
Normal file
229
roles/oatakan.windows_update/files/win-updates.ps1
Normal file
@@ -0,0 +1,229 @@
|
||||
param($global:RestartRequired=0,
|
||||
$global:MoreUpdates=0,
|
||||
$global:MaxCycles=5,
|
||||
$MaxUpdatesPerCycle=500)
|
||||
|
||||
$Logfile = "C:\Windows\Temp\win-updates.log"
|
||||
|
||||
function LogWrite {
|
||||
Param ([string]$logstring)
|
||||
$now = Get-Date -format s
|
||||
Add-Content $Logfile -value "$now $logstring"
|
||||
Write-Host $logstring
|
||||
}
|
||||
|
||||
function Check-ContinueRestartOrEnd() {
|
||||
$RegistryKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update"
|
||||
$RegistryEntry = "CustomRebootRequired"
|
||||
switch ($global:RestartRequired) {
|
||||
0 {
|
||||
$prop = (Get-ItemProperty $RegistryKey).$RegistryEntry
|
||||
if ($prop) {
|
||||
LogWrite "Restart Registry Entry Exists - Removing It"
|
||||
Remove-ItemProperty -Path $RegistryKey -Name $RegistryEntry -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
||||
LogWrite "No Restart Required"
|
||||
Check-WindowsUpdates
|
||||
|
||||
if (($global:MoreUpdates -eq 1) -and ($script:Cycles -le $global:MaxCycles)) {
|
||||
Install-WindowsUpdates
|
||||
} elseif ($script:Cycles -gt $global:MaxCycles) {
|
||||
LogWrite "Exceeded Cycle Count - Stopping"
|
||||
} else {
|
||||
LogWrite "Done Installing Windows Updates"
|
||||
}
|
||||
}
|
||||
1 {
|
||||
$prop = (Get-ItemProperty $RegistryKey).$RegistryEntry
|
||||
if (-not $prop) {
|
||||
LogWrite "Restart Registry Entry Does Not Exist - Creating It"
|
||||
Set-ItemProperty -Path $RegistryKey -Name $RegistryEntry -Value "1"
|
||||
} else {
|
||||
LogWrite "Restart Registry Entry Exists Already"
|
||||
}
|
||||
|
||||
#LogWrite "Restart Required - Restarting..."
|
||||
#Restart-Computer
|
||||
}
|
||||
default {
|
||||
LogWrite "Unsure If A Restart Is Required"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Install-WindowsUpdates() {
|
||||
$script:Cycles++
|
||||
LogWrite "Evaluating Available Updates with limit of $($MaxUpdatesPerCycle):"
|
||||
$UpdatesToDownload = New-Object -ComObject 'Microsoft.Update.UpdateColl'
|
||||
$script:i = 0;
|
||||
$CurrentUpdates = $SearchResult.Updates
|
||||
while($script:i -lt $CurrentUpdates.Count -and $script:CycleUpdateCount -lt $MaxUpdatesPerCycle) {
|
||||
$Update = $CurrentUpdates.Item($script:i)
|
||||
if (($Update -ne $null) -and (!$Update.IsDownloaded)) {
|
||||
[bool]$addThisUpdate = $false
|
||||
if ($Update.InstallationBehavior.CanRequestUserInput) {
|
||||
LogWrite "> Skipping: $($Update.Title) because it requires user input"
|
||||
} else {
|
||||
if (!($Update.EulaAccepted)) {
|
||||
LogWrite "> Note: $($Update.Title) has a license agreement that must be accepted. Accepting the license."
|
||||
$Update.AcceptEula()
|
||||
[bool]$addThisUpdate = $true
|
||||
$script:CycleUpdateCount++
|
||||
} else {
|
||||
[bool]$addThisUpdate = $true
|
||||
$script:CycleUpdateCount++
|
||||
}
|
||||
}
|
||||
|
||||
if ([bool]$addThisUpdate) {
|
||||
LogWrite "Adding: $($Update.Title)"
|
||||
$UpdatesToDownload.Add($Update) |Out-Null
|
||||
}
|
||||
}
|
||||
$script:i++
|
||||
}
|
||||
|
||||
if ($UpdatesToDownload.Count -eq 0) {
|
||||
LogWrite "No Updates To Download..."
|
||||
} else {
|
||||
LogWrite 'Downloading Updates...'
|
||||
$ok = 0;
|
||||
while (! $ok) {
|
||||
try {
|
||||
$Downloader = $UpdateSession.CreateUpdateDownloader()
|
||||
$Downloader.Updates = $UpdatesToDownload
|
||||
$Downloader.Download()
|
||||
$ok = 1;
|
||||
} catch {
|
||||
LogWrite $_.Exception | Format-List -force
|
||||
LogWrite "Error downloading updates. Retrying in 30s."
|
||||
$script:attempts = $script:attempts + 1
|
||||
Start-Sleep -s 30
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$UpdatesToInstall = New-Object -ComObject 'Microsoft.Update.UpdateColl'
|
||||
[bool]$rebootMayBeRequired = $false
|
||||
LogWrite 'The following updates are downloaded and ready to be installed:'
|
||||
foreach ($Update in $SearchResult.Updates) {
|
||||
if (($Update.IsDownloaded)) {
|
||||
LogWrite "> $($Update.Title)"
|
||||
$UpdatesToInstall.Add($Update) |Out-Null
|
||||
|
||||
if ($Update.InstallationBehavior.RebootBehavior -gt 0){
|
||||
[bool]$rebootMayBeRequired = $true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($UpdatesToInstall.Count -eq 0) {
|
||||
LogWrite 'No updates available to install...'
|
||||
$global:MoreUpdates=0
|
||||
$global:RestartRequired=0
|
||||
break
|
||||
}
|
||||
|
||||
if ($rebootMayBeRequired) {
|
||||
LogWrite 'These updates may require a reboot'
|
||||
$global:RestartRequired=1
|
||||
}
|
||||
|
||||
LogWrite 'Installing updates...'
|
||||
|
||||
$Installer = $script:UpdateSession.CreateUpdateInstaller()
|
||||
$Installer.Updates = $UpdatesToInstall
|
||||
$InstallationResult = $Installer.Install()
|
||||
|
||||
LogWrite "Installation Result: $($InstallationResult.ResultCode)"
|
||||
LogWrite "Reboot Required: $($InstallationResult.RebootRequired)"
|
||||
LogWrite 'Listing of updates installed and individual installation results:'
|
||||
if ($InstallationResult.RebootRequired) {
|
||||
$global:RestartRequired=1
|
||||
} else {
|
||||
$global:RestartRequired=0
|
||||
}
|
||||
|
||||
for($i=0; $i -lt $UpdatesToInstall.Count; $i++) {
|
||||
New-Object -TypeName PSObject -Property @{
|
||||
Title = $UpdatesToInstall.Item($i).Title
|
||||
Result = $InstallationResult.GetUpdateResult($i).ResultCode
|
||||
}
|
||||
LogWrite "Item: " $UpdatesToInstall.Item($i).Title
|
||||
LogWrite "Result: " $InstallationResult.GetUpdateResult($i).ResultCode;
|
||||
}
|
||||
|
||||
Check-ContinueRestartOrEnd
|
||||
}
|
||||
|
||||
function Check-WindowsUpdates() {
|
||||
LogWrite "Checking For Windows Updates"
|
||||
$Username = $env:USERDOMAIN + "\" + $env:USERNAME
|
||||
|
||||
New-EventLog -Source $ScriptName -LogName 'Windows Powershell' -ErrorAction SilentlyContinue
|
||||
|
||||
$Message = "Script: " + $ScriptPath + "`nScript User: " + $Username + "`nStarted: " + (Get-Date).toString()
|
||||
|
||||
Write-EventLog -LogName 'Windows Powershell' -Source $ScriptName -EventID "104" -EntryType "Information" -Message $Message
|
||||
LogWrite $Message
|
||||
|
||||
$script:UpdateSearcher = $script:UpdateSession.CreateUpdateSearcher()
|
||||
$script:successful = $FALSE
|
||||
$script:attempts = 0
|
||||
$script:maxAttempts = 12
|
||||
while(-not $script:successful -and $script:attempts -lt $script:maxAttempts) {
|
||||
try {
|
||||
$script:SearchResult = $script:UpdateSearcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")
|
||||
$script:successful = $TRUE
|
||||
} catch {
|
||||
LogWrite $_.Exception | Format-List -force
|
||||
LogWrite "Search call to UpdateSearcher was unsuccessful. Retrying in 10s."
|
||||
$script:attempts = $script:attempts + 1
|
||||
Start-Sleep -s 10
|
||||
}
|
||||
}
|
||||
|
||||
if ($SearchResult.Updates.Count -ne 0) {
|
||||
$Message = "There are " + $SearchResult.Updates.Count + " more updates."
|
||||
LogWrite $Message
|
||||
try {
|
||||
for($i=0; $i -lt $script:SearchResult.Updates.Count; $i++) {
|
||||
LogWrite $script:SearchResult.Updates.Item($i).Title
|
||||
LogWrite $script:SearchResult.Updates.Item($i).Description
|
||||
LogWrite $script:SearchResult.Updates.Item($i).RebootRequired
|
||||
LogWrite $script:SearchResult.Updates.Item($i).EulaAccepted
|
||||
}
|
||||
$global:MoreUpdates=1
|
||||
} catch {
|
||||
LogWrite $_.Exception | Format-List -force
|
||||
LogWrite "Showing SearchResult was unsuccessful. Rebooting."
|
||||
$global:RestartRequired=1
|
||||
$global:MoreUpdates=0
|
||||
Check-ContinueRestartOrEnd
|
||||
LogWrite "Show never happen to see this text!"
|
||||
Restart-Computer
|
||||
}
|
||||
} else {
|
||||
LogWrite 'There are no applicable updates'
|
||||
$global:RestartRequired=0
|
||||
$global:MoreUpdates=0
|
||||
}
|
||||
}
|
||||
|
||||
$script:ScriptName = $MyInvocation.MyCommand.ToString()
|
||||
$script:ScriptPath = $MyInvocation.MyCommand.Path
|
||||
$script:UpdateSession = New-Object -ComObject 'Microsoft.Update.Session'
|
||||
$script:UpdateSession.ClientApplicationID = 'Packer Windows Update Installer'
|
||||
$script:UpdateSearcher = $script:UpdateSession.CreateUpdateSearcher()
|
||||
$script:SearchResult = New-Object -ComObject 'Microsoft.Update.UpdateColl'
|
||||
$script:Cycles = 0
|
||||
$script:CycleUpdateCount = 0
|
||||
|
||||
Check-WindowsUpdates
|
||||
if ($global:MoreUpdates -eq 1) {
|
||||
Install-WindowsUpdates
|
||||
} else {
|
||||
Check-ContinueRestartOrEnd
|
||||
}
|
||||
2
roles/oatakan.windows_update/meta/.galaxy_install_info
Normal file
2
roles/oatakan.windows_update/meta/.galaxy_install_info
Normal file
@@ -0,0 +1,2 @@
|
||||
install_date: Wed Apr 21 17:13:32 2021
|
||||
version: master
|
||||
30
roles/oatakan.windows_update/meta/main.yml
Normal file
30
roles/oatakan.windows_update/meta/main.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
---
|
||||
galaxy_info:
|
||||
author: Orcun Atakan
|
||||
description: Ansible galaxy role for updating Microsoft Windows systems
|
||||
role_name: windows_update
|
||||
company: Red Hat
|
||||
|
||||
license: MIT
|
||||
|
||||
min_ansible_version: 2.5
|
||||
|
||||
platforms:
|
||||
- name: Windows
|
||||
versions:
|
||||
- all
|
||||
|
||||
cloud_platforms:
|
||||
- amazon
|
||||
- google
|
||||
- azure
|
||||
- azure
|
||||
- vmware
|
||||
- ovirt
|
||||
|
||||
galaxy_tags:
|
||||
- windows
|
||||
- update
|
||||
- template
|
||||
|
||||
dependencies: []
|
||||
49
roles/oatakan.windows_update/tasks/main.yml
Normal file
49
roles/oatakan.windows_update/tasks/main.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
---
|
||||
|
||||
- name: disable firewall for Domain, Public and Private profiles
|
||||
win_shell: Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False
|
||||
when: "'Windows Server 2012' in ansible_distribution"
|
||||
|
||||
- name: disable firewall for Domain, Public and Private profiles
|
||||
win_shell: netsh advfirewall set allprofiles state off
|
||||
when: "'Windows Server 2008' in ansible_distribution or 'Windows 7' in ansible_distribution"
|
||||
|
||||
- name: get used space before update
|
||||
win_shell: Get-PSDrive C | Select-Object Used | ConvertTo-Json
|
||||
register: used_space_before_update
|
||||
ignore_errors: yes
|
||||
|
||||
- include_tasks: updates-all.yml
|
||||
when:
|
||||
- "'Windows Server 2008' not in ansible_distribution"
|
||||
- "'Windows 7' not in ansible_distribution"
|
||||
|
||||
#- include_tasks: updates-powershell.yml
|
||||
# when:
|
||||
# - install_updates | bool
|
||||
# - "'Windows Server 2008' in ansible_distribution"
|
||||
|
||||
- include_tasks: updates-win2008r2.yml
|
||||
when:
|
||||
- "'Windows Server 2008' in ansible_distribution or 'Windows 7' in ansible_distribution"
|
||||
|
||||
- name: get used space after update
|
||||
win_shell: Get-PSDrive C | Select-Object Used | ConvertTo-Json
|
||||
register: used_space_after_update
|
||||
ignore_errors: yes
|
||||
|
||||
- debug:
|
||||
msg:
|
||||
- "Used space before update: {{ ((used_space_before_update.stdout | from_json)['Used']|int / (1024*1024*1024)) | round(2, 'floor') }} GB"
|
||||
- "Used space after update: {{ ((used_space_after_update.stdout | from_json)['Used']|int / (1024*1024*1024)) | round(2, 'floor') }} GB"
|
||||
when:
|
||||
- used_space_before_update.stdout is defined
|
||||
- used_space_after_update.stdout is defined
|
||||
|
||||
- name: enabled firewall for Domain, Public and Private profiles
|
||||
win_shell: Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True
|
||||
when: "'Windows Server 2012' in ansible_distribution"
|
||||
|
||||
- name: enable firewall for Domain, Public and Private profiles
|
||||
win_shell: netsh advfirewall set allprofiles state on
|
||||
when: "'Windows Server 2008' in ansible_distribution or 'Windows 7' in ansible_distribution"
|
||||
43
roles/oatakan.windows_update/tasks/updates-all.yml
Normal file
43
roles/oatakan.windows_update/tasks/updates-all.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
|
||||
- name: check for available updates
|
||||
win_updates:
|
||||
category_names: "{{ win_update_category_names }}"
|
||||
blacklist: "{{ win_update_blacklist | default(omit) }}"
|
||||
state: searched
|
||||
register: available_updates
|
||||
|
||||
- debug:
|
||||
msg: |
|
||||
{{ inventory_hostname }} has {{ available_updates.found_update_count }} updates available.
|
||||
{% for key, value in available_updates.updates.items() %}
|
||||
- {{ value.title }}
|
||||
{% endfor %}
|
||||
when: available_updates.updates is defined
|
||||
|
||||
- include_tasks: updates-with-retry.yml
|
||||
when:
|
||||
- available_updates.updates is defined
|
||||
- available_updates.found_update_count > 0
|
||||
|
||||
- name: check for missing updates.
|
||||
win_updates:
|
||||
state: searched
|
||||
register: available_updates
|
||||
|
||||
- name: list missing updates
|
||||
debug:
|
||||
var: available_updates
|
||||
|
||||
- name: check to see if update is finished
|
||||
win_shell: gwmi -Class win32_computersystem -ComputerName 127.0.0.1 | select -ExpandProperty username -ErrorAction Stop
|
||||
register: logon_status
|
||||
until: logon_status is success
|
||||
delay: 10
|
||||
retries: 100
|
||||
ignore_errors: yes
|
||||
when: "'Server' not in ansible_distribution"
|
||||
|
||||
- name: reboot windows
|
||||
win_reboot:
|
||||
when: "'Server' not in ansible_distribution"
|
||||
98
roles/oatakan.windows_update/tasks/updates-powershell.yml
Normal file
98
roles/oatakan.windows_update/tasks/updates-powershell.yml
Normal file
@@ -0,0 +1,98 @@
|
||||
---
|
||||
|
||||
- name: update over multiple reboots
|
||||
block:
|
||||
- name: check for available updates
|
||||
win_updates:
|
||||
category_names:
|
||||
- CriticalUpdates
|
||||
- DefinitionUpdates
|
||||
- SecurityUpdates
|
||||
- UpdateRollups
|
||||
- Updates
|
||||
state: searched
|
||||
register: available_updates
|
||||
|
||||
- debug:
|
||||
msg: |
|
||||
{{ inventory_hostname }} has {{ available_updates.found_update_count }} updates available.
|
||||
{% for key, value in available_updates.updates.items() %}
|
||||
- {{ value.title }}
|
||||
{% endfor %}
|
||||
when: available_updates.updates is defined
|
||||
|
||||
- block:
|
||||
- name: install windows updates using powershell script
|
||||
script: win-updates.ps1
|
||||
become: yes
|
||||
become_method: runas
|
||||
become_user: SYSTEM
|
||||
when:
|
||||
- available_updates.updates is defined
|
||||
- available_updates.found_update_count > 0
|
||||
|
||||
rescue:
|
||||
- name: reboot the system to recover from a failed update
|
||||
win_reboot:
|
||||
reboot_timeout: 7200
|
||||
|
||||
- name: wait for system to be responsive after update
|
||||
wait_for_connection:
|
||||
delay: 60
|
||||
sleep: 10
|
||||
timeout: 600
|
||||
|
||||
- name: check to see if reboot is required
|
||||
win_reg_stat:
|
||||
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update
|
||||
name: CustomRebootRequired
|
||||
register: update_reboot_required_key
|
||||
|
||||
- name: reboot the system to continue with the update
|
||||
win_reboot:
|
||||
reboot_timeout: 7200
|
||||
when: update_reboot_required_key.exists
|
||||
|
||||
- name: check for missing updates
|
||||
win_updates:
|
||||
category_names:
|
||||
- CriticalUpdates
|
||||
- DefinitionUpdates
|
||||
- SecurityUpdates
|
||||
- UpdateRollups
|
||||
- Updates
|
||||
state: searched
|
||||
register: missing_updates
|
||||
|
||||
- debug:
|
||||
msg: |
|
||||
{{ inventory_hostname }} has {{ missing_updates.found_update_count }} updates still missing.
|
||||
{% for key, value in missing_updates.updates.items() %}
|
||||
- {{ value.title }}
|
||||
{% endfor %}
|
||||
when: missing_updates.updates is defined
|
||||
|
||||
- block:
|
||||
- name: set update count
|
||||
set_fact:
|
||||
update_retry_count: '{{ update_retry_count | default(0) | int + 1 }}'
|
||||
|
||||
- name: still more updates - need to retry
|
||||
fail:
|
||||
msg: >
|
||||
'{{ inventory_hostname }} has {{ missing_updates.found_update_count }} updates still missing.
|
||||
{{ (update_retry_limit | int) - (update_retry_count | int) }} more retries left'
|
||||
when: ((update_retry_limit | int) - (update_retry_count | int) > 0)
|
||||
when: missing_updates.found_update_count > 0
|
||||
|
||||
- name: ensure the CustomRebootRequired key doesn't exist
|
||||
win_regedit:
|
||||
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update
|
||||
name: CustomRebootRequired
|
||||
state: absent
|
||||
|
||||
rescue:
|
||||
- debug:
|
||||
msg: "Still more updates remaining - retrying..."
|
||||
|
||||
- include_tasks: updates-powershell.yml
|
||||
81
roles/oatakan.windows_update/tasks/updates-win2008r2.yml
Normal file
81
roles/oatakan.windows_update/tasks/updates-win2008r2.yml
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
|
||||
- name: ensure Windows ADK with DISM is installed
|
||||
win_chocolatey:
|
||||
name: windows-adk-deploy
|
||||
state: present
|
||||
version: 10.0.17134.0
|
||||
register: install_windows_adk_deploy
|
||||
|
||||
- name: ensure PATH contains Windows ADK
|
||||
win_path:
|
||||
scope: machine
|
||||
state: present
|
||||
elements: "C:\\Program Files (x86)\\Windows Kits\\10\\Assessment and Deployment Kit\\Deployment Tools\\amd64\\DISM"
|
||||
|
||||
- name: download hotfix group 1 (PS >= 4)
|
||||
win_get_url:
|
||||
url: '{{ item.url }}'
|
||||
dest: '{{ hotfix_download_location }}\{{ item.file }}'
|
||||
loop: "{{ hotfixes_group_1 }}"
|
||||
|
||||
- name: install hotfix group 1
|
||||
win_hotfix:
|
||||
source: '{{ hotfix_download_location }}\{{ item.file }}'
|
||||
state: present
|
||||
register: hotfix_install_group_1
|
||||
loop: "{{ hotfixes_group_1 }}"
|
||||
when: ansible_powershell_version is version('4', '>=')
|
||||
|
||||
- name: install hotfix (PS == 3)
|
||||
win_shell: '{{ hotfix_download_location }}\{{ item.file }} /quiet /norestart'
|
||||
register: hotfix_install_group_1
|
||||
loop: "{{ hotfixes_group_1 }}"
|
||||
when: ansible_powershell_version is version('3', '==')
|
||||
|
||||
- name: debug hotfix installation result
|
||||
debug:
|
||||
var: hotfix_install_group_1
|
||||
|
||||
- name: ensure hotfix file is removed (group 1)
|
||||
win_file:
|
||||
path: '{{ hotfix_download_location }}\{{ item.file }}'
|
||||
state: absent
|
||||
loop: "{{ hotfixes_group_1 }}"
|
||||
|
||||
- name: reboot from starting update
|
||||
win_reboot:
|
||||
|
||||
- name: check for available updates
|
||||
win_updates:
|
||||
category_names: "{{ win_update_category_names }}"
|
||||
blacklist: "{{ win_update_blacklist | default(omit) }}"
|
||||
state: searched
|
||||
register: available_updates
|
||||
|
||||
- debug:
|
||||
msg: |
|
||||
{{ inventory_hostname }} has {{ available_updates.found_update_count }} updates available.
|
||||
{% for key, value in available_updates.updates.items() %}
|
||||
- {{ value.title }}
|
||||
{% endfor %}
|
||||
when: available_updates.updates is defined
|
||||
|
||||
- include_tasks: updates-with-retry.yml
|
||||
when:
|
||||
- available_updates.updates is defined
|
||||
- available_updates.found_update_count > 0
|
||||
|
||||
- name: check for missing updates.
|
||||
win_updates:
|
||||
state: searched
|
||||
register: available_updates
|
||||
|
||||
- name: list missing updates
|
||||
debug:
|
||||
var: available_updates
|
||||
|
||||
- name: make sure Windows ADK with DISM for Server 2008 R2 is not installed
|
||||
win_chocolatey:
|
||||
name: windows-adk-deploy
|
||||
state: absent
|
||||
84
roles/oatakan.windows_update/tasks/updates-with-retry.yml
Normal file
84
roles/oatakan.windows_update/tasks/updates-with-retry.yml
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
|
||||
- name: update over multiple reboots
|
||||
block:
|
||||
- block:
|
||||
- name: install all windows updates
|
||||
win_updates:
|
||||
category_names: "{{ win_update_category_names }}"
|
||||
blacklist: "{{ (win_update_blacklist | default([])) + (failed_kb | default([])) }}"
|
||||
whitelist: "{{ win_update_whitelist | default(omit) }}"
|
||||
reboot: yes
|
||||
register: installed_updates
|
||||
|
||||
rescue:
|
||||
- name: reboot the system to recover from a failed update
|
||||
win_reboot:
|
||||
reboot_timeout: 7200
|
||||
|
||||
- name: set failed KB to skip
|
||||
set_fact:
|
||||
failed_kb: "{{ failed_kb|default([]) + [installed_updates.msg | regex_replace('^.*\\((KB.*)\\).*','\\1')] }}"
|
||||
when:
|
||||
- installed_updates.msg is defined
|
||||
- ('Failed' in installed_updates.msg)
|
||||
- ('KB' in installed_updates.msg)
|
||||
|
||||
- name: fail to retry
|
||||
fail:
|
||||
msg: "There are failed updates: {{ failed_kb | join(' ') }}"
|
||||
when:
|
||||
- failed_kb is defined
|
||||
- failed_kb | length > 0
|
||||
|
||||
- name: wait for system to be responsive after update
|
||||
wait_for_connection:
|
||||
delay: 60
|
||||
sleep: 10
|
||||
timeout: 600
|
||||
|
||||
- name: work on any skipped KB
|
||||
win_updates:
|
||||
category_names: "{{ win_update_category_names }}"
|
||||
blacklist: "{{ win_update_blacklist | default(omit) }}"
|
||||
whitelist: "{{ failed_kb | default([]) }}"
|
||||
reboot: yes
|
||||
register: installed_updates_retry_skipped
|
||||
when:
|
||||
- failed_kb is defined
|
||||
- failed_kb | length > 0
|
||||
|
||||
- name: check for missing updates
|
||||
win_updates:
|
||||
category_names: "{{ win_update_category_names }}"
|
||||
blacklist: "{{ win_update_blacklist | default(omit) }}"
|
||||
state: searched
|
||||
register: missing_updates
|
||||
|
||||
- debug:
|
||||
msg: |
|
||||
{{ inventory_hostname }} has {{ missing_updates.found_update_count }} updates still missing.
|
||||
{% for key, value in missing_updates.updates.items() %}
|
||||
- {{ value.title }}
|
||||
{% endfor %}
|
||||
when: missing_updates.updates is defined
|
||||
|
||||
- name: still more updates - need to retry
|
||||
fail:
|
||||
msg: >
|
||||
'{{ inventory_hostname }} has {{ missing_updates.found_update_count }} updates still missing.
|
||||
{{ (update_retry_limit | int) - (update_retry_count | int) }} more retries left'
|
||||
when:
|
||||
- missing_updates.found_update_count > 0
|
||||
- ((update_retry_limit | int) - (update_retry_count | int) >= 0)
|
||||
|
||||
rescue:
|
||||
- name: set update count
|
||||
set_fact:
|
||||
update_retry_count: '{{ update_retry_count | default(0) | int + 1 }}'
|
||||
|
||||
- debug:
|
||||
msg: "Still more updates remaining - retrying... ({{ update_retry_count }}/{{ update_retry_limit }})"
|
||||
|
||||
- include_tasks: updates-with-retry.yml
|
||||
when: ((update_retry_limit | int) - (update_retry_count | int) >= 0)
|
||||
1
roles/oatakan.windows_update/tests/inventory
Normal file
1
roles/oatakan.windows_update/tests/inventory
Normal file
@@ -0,0 +1 @@
|
||||
localhost
|
||||
7
roles/oatakan.windows_update/tests/test.yml
Normal file
7
roles/oatakan.windows_update/tests/test.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- hosts: localhost
|
||||
gather_facts: False
|
||||
connection: local
|
||||
become: no
|
||||
roles:
|
||||
- ../.
|
||||
Reference in New Issue
Block a user