Skip to content

Commit

Permalink
[Windows] Install windows update patch by msu file (vmware#525)
Browse files Browse the repository at this point in the history
Signed-off-by: Yanan Shen <yanans@vmware.com>
  • Loading branch information
123lzxm authored and ZouYuhua committed Jan 18, 2024
1 parent a977f3b commit acb350d
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 16 deletions.
4 changes: 3 additions & 1 deletion common/add_host_in_memory_inventory.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@
ansible_ssh_pipelining: "{{ add_host_in_memory_inventory_ssh_pipeline | default(false) }}"
ansible_remote_tmp: "{{ add_host_in_memory_inventory_remote_tmp | default(omit) }}"
ansible_shell_executable: "{{ add_host_in_memory_inventory_shell | default(omit) }}"

register: add_host_result
no_log: "{{ hide_secrets | default(false) }}"

- name: Display the add host result
ansible.builtin.debug: var=add_host_result
when: enable_debug is defined and enable_debug
no_log: "{{ hide_secrets | default(false) }}"
2 changes: 2 additions & 0 deletions common/set_vmware_module_hostname.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- vcenter_username is defined and vcenter_username
- vcenter_password is defined and vcenter_password
- datacenter is defined and datacenter
no_log: "{{ hide_secrets | default(false) }}"

- name: "Set ESXi hostname for Ansible VMware modules to connect"
ansible.builtin.set_fact:
Expand All @@ -25,6 +26,7 @@
vsphere_host_user_password: "{{ esxi_password }}"
vsphere_host_datacenter: "ha-datacenter"
when: vcenter_is_defined is undefined
no_log: "{{ hide_secrets | default(false) }}"

- name: "Set default VM folder when it's undefined"
ansible.builtin.set_fact:
Expand Down
48 changes: 37 additions & 11 deletions env_setup/env_cleanup.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,55 @@

# Need to revert to base snapshot of target VM firstly, or removing portgroup would fail
- name: "Revert to base snapshot then cleanup network testbed configurations"
when:
- router_vm_deployed is defined
- router_vm_deployed | bool
block:
- include_tasks: ../common/vm_revert_snapshot.yml
- name: "Revert to snapshot {{ base_snapshot_name }}"
include_tasks: ../common/vm_revert_snapshot.yml
vars:
snapshot_name: "{{ base_snapshot_name }}"
skip_if_not_exist: true
when:
- base_snapshot_exists is defined
- base_snapshot_exists | bool
# Clean up router VM, vSwitch and portgroup
- include_tasks: ../common/network_testbed_cleanup.yml
when:
- router_vm_deployed is defined
- router_vm_deployed | bool
- name: "Cleanup router VM and vSwitch"
include_tasks: ../common/network_testbed_cleanup.yml

- name: Cleanup new deployed VM
- name: "Cleanup new deployed VM"
when:
- cleanup_vm | bool
- new_vm is defined and new_vm | bool
block:
- include_tasks: ../common/vm_set_power_state.yml
- name: "Power off VM"
include_tasks: ../common/vm_set_power_state.yml
vars:
vm_power_state_set: 'powered-off'
- include_tasks: ../common/vm_remove.yml
- name: "Set VM cleaned flag is True"
- name: "Remove VM"
include_tasks: ../common/vm_remove.yml
- name: "Set VM cleaned flag to true and VM exists to false"
ansible.builtin.set_fact:
vm_cleaned_up: true
vm_exists: false

- name: "Restore base snapshot for Windows Update installation"
when:
- cleanup_vm | bool
- new_vm is defined and new_vm | bool
- win_update_snapshot_name is defined
- win_update_snapshot_name
- (vm_exists is defined and vm_exists)
block:
- name: "Revert to original base snapshot {{ win_update_snapshot_name }}"
include_tasks: ../common/vm_revert_snapshot.yml
vars:
snapshot_name: "{{ win_update_snapshot_name }}"
- name: "Remove the current base snapshot {{ base_snapshot_name }}"
include_tasks: ../common/vm_remove_snapshot.yml
vars:
snapshot_name: "{{ base_snapshot_name }}"
when: base_snapshot_exists
- name: "Rename snapshot {{ win_update_snapshot_name }} to {{ base_snapshot_name }}"
include_tasks: ../common/vm_rename_snapshot.yml
vars:
current_snapshot_name: "{{ win_update_snapshot_name }}"
new_snapshot_name: "{{ base_snapshot_name }}"
1 change: 1 addition & 0 deletions main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# Main playbook for launching guest OS validation testing on vSphere
- name: main
hosts: localhost
gather_facts: false
tasks:
- name: "Read variables from vars file"
ansible.builtin.include_vars:
Expand Down
17 changes: 17 additions & 0 deletions vars/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ base_snapshot_name: "BaseSnapshot"
#
# http_proxy_localhost: myproxy.company.com:8080

# To keep sensitive values out of your logs, such as passwords and usernames, mark tasks that expose them
# with the "no_log: True" attribute.
# This var is the value for attribute no_log.
# Default value is false.
#
# hide_secrets: false

#####################################
# Testbed parameters
#####################################
Expand Down Expand Up @@ -353,6 +360,16 @@ windows_product_key_upgrade: "XXXXX-XXXXX-XXXXX-XXXXX-XXXXX"
#
# gosc_dhcp_network: "VM Network"

# 7. windows_update_install
# For Windows testing only.
# Windows shared folder for storing the .msu file. E.g. \\WINDOWS_SERVER_ADDRESS\SHARE_FOLDER
windows_nfs_share: ''
# The path of the .msu file in above Windows shared folder. E.g. WindowsClient\Windows11\23H2
windows_nfs_msu_path: ''
# The use name and password for accessing above Windows shared folder.
# windows_nfs_username: "CHANGEME"
# windows_nfs_password: "CHANGEME"

#####################################
# GOS related parameters
#####################################
Expand Down
1 change: 1 addition & 0 deletions windows/gosv_testcase_list.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- import_playbook: deploy_vm/deploy_vm.yml
- import_playbook: check_inbox_driver/check_inbox_driver.yml
- import_playbook: wintools_complete_install_verify/wintools_complete_install_verify.yml
- import_playbook: windows_update_install/windows_update_install.yml
- import_playbook: guest_os_inplace_upgrade/guest_os_inplace_upgrade.yml
- import_playbook: secureboot_enable_disable/secureboot_enable_disable.yml
- import_playbook: check_efi_firmware/check_efi_firmware.yml
Expand Down
3 changes: 3 additions & 0 deletions windows/utils/add_windows_host.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
when:
- win_ansible_connection is defined
- win_ansible_connection | lower == 'winrm'
no_log: "{{ hide_secrets | default(false) }}"

- name: "Add specified Windows IP to the hosts"
add_host:
Expand All @@ -47,6 +48,8 @@
when: >
(win_ansible_connection is undefined) or
(win_ansible_connection | lower == 'psrp')
no_log: "{{ hide_secrets | default(false) }}"

- ansible.builtin.debug: var=add_host_result
when: enable_debug is defined and enable_debug
no_log: "{{ hide_secrets | default(false) }}"
11 changes: 7 additions & 4 deletions windows/utils/win_execute_cmd.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# Copyright 2021-2023 VMware, Inc.
# SPDX-License-Identifier: BSD-2-Clause
---
# Execute specified powershell command in Windows guest OS
# Execute specified PowerShell command in Windows guest OS
# Parameters:
# win_powershell_cmd: powershell command
# win_powershell_cmd: PowerShell command
# win_execute_cmd_ignore_error: true or false
# win_execute_cmd_no_log: true or false, default false.
# Return:
# win_powershell_cmd_output
#
Expand All @@ -19,12 +20,13 @@
ansible.builtin.set_fact:
win_powershell_cmd_output: ""

- name: "Execute powershell command '{{ win_powershell_cmd }}'"
- name: "Execute PowerShell command"
ansible.windows.win_shell: "{{ win_powershell_cmd }}"
register: win_powershell_cmd_output
ignore_errors: "{{ win_execute_cmd_ignore_error | default(false) }}"
delegate_to: "{{ vm_guest_ip }}"
ignore_unreachable: true
no_log: "{{ win_execute_cmd_no_log | default(false) }}"

- name: "Test VM and guest connection when guest unreachable"
block:
Expand All @@ -48,6 +50,7 @@
- win_powershell_cmd_output.unreachable is defined
- win_powershell_cmd_output.unreachable

- name: "Display the powershell commmand result"
- name: "Display the PowerShell commmand result"
ansible.builtin.debug: var=win_powershell_cmd_output
when: enable_debug
no_log: "{{ win_execute_cmd_no_log | default(false) }}"
74 changes: 74 additions & 0 deletions windows/windows_update_install/install_msu_file.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Copyright 2023 VMware, Inc.
# SPDX-License-Identifier: BSD-2-Clause
---
- name: "Set the .msu file installation related parameters"
ansible.builtin.set_fact:
msu_kb_number: "{{ msu_file_name | regex_search('(?i)KB\\d+') | upper }}"
msu_file_dest_path: "{{ msu_dir_path }}\\{{ msu_file_name }}"
msu_install_timeout: 7200
msu_become_user: "{{ 'SYSTEM' if guest_os_ansible_architecture == '32-bit' else 'Administrator' }}"

- name: "Enable the user Administrator"
include_tasks: ../utils/win_execute_cmd.yml
vars:
win_powershell_cmd: "net user Administrator /active:yes"
when: vm_username | lower != "administrator"

- name: "Install the .msu file"
ansible.windows.win_shell: >-
wusa.exe {{ msu_file_dest_path }} /quiet /norestart
delegate_to: "{{ vm_guest_ip }}"
register: install_msu_result
become: true
become_method: runas
become_user: "{{ msu_become_user }}"
async: "{{ msu_install_timeout }}"
poll: 0
environment:
ANSIBLE_WIN_ASYNC_STARTUP_TIMEOUT: 10

- name: "Check if the .msu file installation task started"
ansible.builtin.assert:
that:
- install_msu_result is defined
- install_msu_result.ansible_job_id is defined
fail_msg: "The result of above .msu file installation task executed in guest OS is not returned."

- name: "Check if the .msu file is installed before restart"
ansible.windows.win_shell: "Get-HotFix | Where-Object HotFixID -eq {{ msu_kb_number }}"
register: win_get_hotfix_result
ignore_errors: true
delegate_to: "{{ vm_guest_ip }}"
ignore_unreachable: true
until:
- win_get_hotfix_result.rc is defined
- win_get_hotfix_result.rc == 0
- win_get_hotfix_result.stdout_lines | length != 0
retries: "{{ msu_install_timeout // 300 }}"
delay: 300

- name: "The .msu file installation failed"
ansible.builtin.fail:
msg: "Failed to install the .msu file {{ msu_file_name }} in {{ msu_install_timeout }} seconds."
when:
- win_get_hotfix_result.failed is defined
- win_get_hotfix_result.failed

- name: "Restart guest OS after installing the .msu file"
include_tasks: ../utils/win_shutdown_restart.yml
vars:
set_win_power_state: "restart"
win_reboot_timeout: 1800

- name: "Check if the .msu file is installed after restart"
include_tasks: ../utils/win_execute_cmd.yml
vars:
win_powershell_cmd: "Get-HotFix | Where-Object HotFixID -eq {{ msu_kb_number }}"

- name: "Check the msu installation result after restart"
ansible.builtin.assert:
that:
- win_powershell_cmd_output.stdout_lines is defined
- win_powershell_cmd_output.stdout_lines | length != 0
fail_msg: "The .msu file {{ msu_file_name }} is not installed in guest OS."
success_msg: "The .msu file {{ msu_file_name }} is installed in guest OS."
60 changes: 60 additions & 0 deletions windows/windows_update_install/prepare_msu_file.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright 2023 VMware, Inc.
# SPDX-License-Identifier: BSD-2-Clause
---
- name: "Get unused driver letter"
include_tasks: ../utils/win_get_unused_drive_letter.yml

- name: "Initialize the .msu file path"
ansible.builtin.set_fact:
msu_file_src_path: "{{ drive_letter_new }}:\\{{ windows_nfs_msu_path }}\\*"
msu_dir_path: "C:\\msu"
msu_file_name: ""

- name: "Check if folder {{ msu_dir_path }} exists on guest OS"
include_tasks: ../utils/win_is_folder.yml
vars:
win_is_folder_path: "{{ msu_dir_path }}"

- name: "Create folder {{ msu_dir_path }} on guest OS"
include_tasks: ../utils/win_execute_cmd.yml
vars:
win_powershell_cmd: "mkdir {{ msu_dir_path }}"
when: not win_is_folder_result

- name: "Set mount command for accessing the shared folder"
ansible.builtin.set_fact:
win_nfs_mount_cmd: |-
{%- if windows_nfs_username is defined and windows_nfs_username and
windows_nfs_password is defined and windows_nfs_password -%}
{{ 'net use ' ~ drive_letter_new ~ ': ' ~ windows_nfs_share ~ ' ' ~ windows_nfs_password ~ ' /user:' ~ windows_nfs_username }}
{%- else -%}
{{ 'net use ' ~ drive_letter_new ~ ': ' ~ windows_nfs_share }}
{%- endif -%}
no_log: "{{ hide_secrets | default(false) }}"

- name: "Copy the .msu file to local disk of guest OS"
include_tasks: ../utils/win_execute_cmd.yml
vars:
win_powershell_cmd: >-
{{ win_nfs_mount_cmd }};
Copy-Item -Path {{ msu_file_src_path }} -Include *.msu -Destination {{ msu_dir_path }} -ErrorAction Stop;
net use {{ drive_letter_new }}: /delete
win_execute_cmd_no_log: "{{ hide_secrets | default(false) }}"

- name: "Get the .msu file name"
include_tasks: ../utils/win_execute_cmd.yml
vars:
win_powershell_cmd: >-
Get-ChildItem -Path {{ msu_dir_path }} -Include *.msu -Name -ErrorAction Stop;
- name: "Check if the .msu file is copied to {{ msu_dir_path }}"
ansible.builtin.assert:
that:
- win_powershell_cmd_output.stdout_lines is defined
- win_powershell_cmd_output.stdout_lines | length != 0
fail_msg: "The .msu file is not found in {{ msu_dir_path }} in guest OS."
success_msg: "The .msu file is copied to {{ msu_dir_path }} in guest OS."

- name: "Set the .msu file name"
ansible.builtin.set_fact:
msu_file_name: "{{ win_powershell_cmd_output.stdout_lines[0] }}"
Loading

0 comments on commit acb350d

Please sign in to comment.