Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Windows] Install windows update patch by msu file #525

Merged
merged 21 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) }}"
123lzxm marked this conversation as resolved.
Show resolved Hide resolved

- 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)
123lzxm marked this conversation as resolved.
Show resolved Hide resolved
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"
123lzxm marked this conversation as resolved.
Show resolved Hide resolved
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
123lzxm marked this conversation as resolved.
Show resolved Hide resolved

- 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 -%}
123lzxm marked this conversation as resolved.
Show resolved Hide resolved
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) }}"

123lzxm marked this conversation as resolved.
Show resolved Hide resolved
- 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