win_package - Refactor with msp, appx support (#66931)

* win_package - Refactor with msp, appx support

* Added msi test for ALLUSERS

* Added some msix tests, refactored tests

* Added remaining msix tests

* Enable msix sideloading for tests

* Added remaining exe path tests

* Added basic msp tests

* Remove url options now the util no longer has them

* Fix file version check for older Windows hosts

* Remove no_proxy ansible-test setting

* Use same mechanism of become to copy the file with explicit creds
pull/67374/head
Jordan Borean 5 years ago committed by GitHub
parent 2d9328cb0f
commit 446a0c1b08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,10 @@
bugfixes:
- win_package - Handle quoted and unquoted strings in the registry ``UninstallString`` value - https://github.com/ansible/ansible/issues/40973
minor_changes:
- win_package - Added support for ``.msp`` packages - https://github.com/ansible/ansible/issues/22789
- win_package - Added support for specifying the HTTP method when getting files from a URL - https://github.com/ansible/ansible/issues/35377
- win_package - Added proxy support for retrieving packages from a URL - https://github.com/ansible/ansible/issues/43818
- win_package - Scan packages in the current user's registry hive - https://github.com/ansible/ansible/issues/45950
- win_package - Added support for ``.appx``, ``.msix``, ``.appxbundle``, and ``.msixbundle`` package - https://github.com/ansible/ansible/issues/50765
- win_package - Read uninstall strings from the ``QuietUninstallString`` if present to better support argumentless uninstalls of registry based packages.

@ -134,6 +134,9 @@ namespace Ansible.AccessToken
public enum LogonProvider
{
Default,
WinNT35,
WinNT40,
WinNT50,
}
public enum LogonType
@ -454,4 +457,4 @@ namespace Ansible.AccessToken
}
}
}
}
}

File diff suppressed because it is too large Load Diff

@ -17,10 +17,12 @@ module: win_package
version_added: "1.7"
short_description: Installs/uninstalls an installable package
description:
- Installs or uninstalls a package in either an MSI or EXE format.
- These packages can be sources from the local file system, network file share
- Installs or uninstalls software packages for Windows.
- Supports C(.exe), C(.msi), C(.msp), C(.appx), C(.appxbundle), C(.msix),
and C(.msixbundle).
- These packages can be sourced from the local file system, network file share
or a url.
- Please read the notes section around some caveats with this module.
- See I(provider) for more info on each package type that is supported.
options:
arguments:
description:
@ -28,6 +30,7 @@ options:
package.
- If the package is an MSI do not supply the C(/qn), C(/log) or
C(/norestart) arguments.
- This is only used for the C(msi), C(msp), and C(registry) providers.
- As of Ansible 2.5, this parameter can be a list of arguments and the
module will escape the arguments as necessary, it is recommended to use a
string when dealing with MSI packages due to the unique escaping issues
@ -37,6 +40,7 @@ options:
description:
- Set the specified path as the current working directory before installing
or uninstalling a package.
- This is only used for the C(msi), C(msp), and C(registry) providers.
type: path
version_added: '2.8'
creates_path:
@ -69,11 +73,25 @@ options:
C(3010).
- A return code of C(3010) usually means that a reboot is required, the
C(reboot_required) return value is set if the return code is C(3010).
- This is only used for the C(msi), C(msp), and C(registry) providers.
type: list
elements: int
default: [0, 3010]
log_path:
description:
- Specifies the path to a log file that is persisted after a package is
installed or uninstalled.
- This is only used for the C(msi) or C(msp) provider.
- When omitted, a temporary log file is used instead for those providers.
- This is only valid for MSI files, use C(arguments) for the C(registry)
provider.
type: path
version_added: '2.8'
password:
description:
- The password for C(user_name), must be set when C(user_name) is.
- This option is deprecated in favour of using become, see examples for
more information.
type: str
aliases: [ user_password ]
path:
@ -81,11 +99,10 @@ options:
- Location of the package to be installed or uninstalled.
- This package can either be on the local file system, network share or a
url.
- If the path is on a network share and the current WinRM transport doesn't
support credential delegation, then C(user_name) and C(user_password)
must be set to access the file.
- There are cases where this file will be copied locally to the server so
it can access it, see the notes for more info.
- When C(state=present), C(product_id) is not set and the path is a URL,
this file will always be downloaded to a temporary directory for
idempotency checks, otherwise the file will only be downloaded if the
package has not been installed based on the C(product_id) checks.
- If C(state=present) then this value MUST be set.
- If C(state=absent) then this value does not need to be set if
C(product_id) is.
@ -95,21 +112,66 @@ options:
- The product id of the installed packaged.
- This is used for checking whether the product is already installed and
getting the uninstall information if C(state=absent).
- You can find product ids for installed programs in the Windows registry
editor either at
C(HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall) or for 32 bit
programs at
C(HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall).
- This SHOULD be set when the package is not an MSI, or the path is a url
- For msi packages, this is the C(ProductCode) (GUID) of the package. This
can be found under the same registry paths as the C(registry) provider.
- For msp packages, this is the C(PatchCode) (GUID) of the package which
can found under the C(Details -> Revision number) of the file's properties.
- For msix packages, this is the C(Name) or C(PackageFullName) of the
package found under the C(Get-AppxPackage) cmdlet.
- For registry (exe) packages, this is the registry key name under the
registry paths specified in I(provider).
- This value is ignored if C(path) is set to a local accesible file path
and the package is not an C(exe).
- This SHOULD be set when the package is an C(exe), or the path is a url
or a network share and credential delegation is not being used. The
C(creates_*) options can be used instead but is not recommended.
- The C(productid) alias will be removed in Ansible 2.14.
type: str
aliases: [ productid ]
provider:
description:
- Set the package provider to use when searching for a package.
- The C(auto) provider will select the proper provider if I(path)
otherwise it scans all the other providers based on the I(product_id).
- The C(msi) provider scans for MSI packages installed on a machine wide
and current user context based on the C(ProductCode) of the MSI. Before
Ansible 2.10 only the machine wide context was searched.
- The C(msix) provider is used to install C(.appx), C(.msix),
C(.appxbundle), or C(.msixbundle) packages. These packages are only
installed or removed on the current use. The host must be set to allow
sideloaded apps or in developer mode. See the examples for how to enable
this. If a package is already installed but C(path) points to an updated
package, this will be installed over the top of the existing one.
- The C(msp) provider scans for all MSP patches installed on a machine wide
and current user context based on the C(PatchCode) of the MSP. A C(msp)
will be applied or removed on all C(msi) products that it applies to and
is installed. If the patch is obsoleted or superseded then no action will
be taken.
- The C(registry) provider is used for traditional C(exe) installers and
uses the following registry path to determine if a product was installed;
C(HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall),
C(HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall),
C(HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall), and
C(HKCU:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall).
Before Ansible 2.10 only the C(HKLM) hive was searched.
- Before Ansible 2.10 only the C(msi) and C(registry) providers were used.
choices:
- auto
- msi
- msix
- msp
- registry
default: auto
type: str
version_added: '2.10'
state:
description:
- Whether to install or uninstall the package.
- The module uses C(product_id) and whether it exists at the registry path
to see whether it needs to install or uninstall the package.
- The module uses I(product_id) to determine whether the package is
installed or not.
- For all providers but C(auto), the I(path) can be used for idempotency
checks if it is locally accesible filesystem path.
- The C(ensure) alias will be removed in Ansible 2.14.
type: str
choices: [ absent, present ]
default: present
@ -119,39 +181,61 @@ options:
- Username of an account with access to the package if it is located on a
file share.
- This is only needed if the WinRM transport is over an auth method that
does not support credential delegation like Basic or NTLM.
does not support credential delegation like Basic or NTLM or become is
not used.
- This option is deprecated in favour of using become, see examples for
more information.
type: str
aliases: [ user_name ]
validate_certs:
description:
- If C(no), SSL certificates will not be validated. This should only be
used on personally controlled sites using self-signed certificates.
- Before Ansible 2.4 this defaulted to C(no).
type: bool
default: yes
version_added: '2.4'
log_path:
# Overrides the options in url_windows
client_cert:
version_added: '2.10'
client_cert_password:
version_added: '2.10'
follow_redirects:
version_added: '2.10'
force_basic_auth:
version_added: '2.10'
headers:
version_added: '2.10'
http_agent:
version_added: '2.10'
maximum_redirection:
version_added: '2.10'
method:
version_added: '2.10'
proxy_password:
version_added: '2.10'
proxy_url:
version_added: '2.10'
proxy_use_default_credential:
version_added: '2.10'
proxy_username:
version_added: '2.10'
timeout:
description:
- Specifies the path to a log file that is persisted after an MSI package is installed or uninstalled.
- When omitted, a temporary log file is used for MSI packages.
- This is only valid for MSI files, use C(arguments) for other package types.
type: path
version_added: '2.8'
- Specifies how long the web download request can be pending before it
times out in seconds.
- Set to C(0) to specify an infinite timeout.
version_added: '2.10'
url_password:
version_added: '2.10'
url_username:
version_added: '2.10'
use_default_credential:
version_added: '2.10'
use_proxy:
version_added: '2.10'
extends_documentation_fragment:
- url_windows
notes:
- When C(state=absent) and the product is an exe, the path may be different
from what was used to install the package originally. If path is not set then
the path used will be what is set under C(UninstallString) in the registry
for that product_id.
- Not all product ids are in a GUID form, some programs incorrectly use a
different structure but this module should support any format.
- By default all msi installs and uninstalls will be run with the options
the path used will be what is set under C(QuietUninstallString) or
C(UninstallString) in the registry for that I(product_id).
- By default all msi installs and uninstalls will be run with the arguments
C(/log, /qn, /norestart).
- It is recommended you download the package first from the URL using the
M(win_get_url) module as it opens up more flexibility with what must be set
when calling C(win_package).
- Packages will be temporarily downloaded or copied locally when path is a
network location and credential delegation is not set, or path is a URL
and the file is not an MSI.
- All the installation checks under C(product_id) and C(creates_*) add
together, if one fails then the program is considered to be absent.
seealso:
@ -170,7 +254,7 @@ EXAMPLES = r'''
product_id: '{CF2BEA3C-26EA-32F8-AA9B-331F7E34BA97}'
arguments: /install /passive /norestart
- name: Install Visual C thingy with list of arguments instead of a string, and permanent log
- name: Install Visual C thingy with list of arguments instead of a string
win_package:
path: http://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe
product_id: '{CF2BEA3C-26EA-32F8-AA9B-331F7E34BA97}'
@ -178,13 +262,13 @@ EXAMPLES = r'''
- /install
- /passive
- /norestart
log_path: D:\logs\vcredist_x64-exe-{{lookup('pipe', 'date +%Y%m%dT%H%M%S')}}.log
- name: Install Remote Desktop Connection Manager from msi
- name: Install Remote Desktop Connection Manager from msi with a permanent log
win_package:
path: https://download.microsoft.com/download/A/F/0/AF0071F3-B198-4A35-AA90-C68D103BDCCF/rdcman.msi
product_id: '{0240359E-6A4C-4884-9E94-B397A02D893C}'
state: present
log_path: D:\logs\vcredist_x64-exe-{{lookup('pipe', 'date +%Y%m%dT%H%M%S')}}.log
- name: Uninstall Remote Desktop Connection Manager
win_package:
@ -202,14 +286,18 @@ EXAMPLES = r'''
state: absent
# 7-Zip exe doesn't use a guid for the Product ID
- name: Install 7zip from a network share specifying the credentials
- name: Install 7zip from a network share with specific credentials
win_package:
path: \\domain\programs\7z.exe
product_id: 7-Zip
arguments: /S
state: present
user_name: DOMAIN\User
user_password: Password
become: yes
become_method: runas
become_flags: logon_type=new_credential logon_flags=netcredentials_only
vars:
ansible_become_user: DOMAIN\User
ansible_become_password: Password
- name: Install 7zip and use a file version for the installation check
win_package:
@ -238,12 +326,40 @@ EXAMPLES = r'''
arguments: '/q /norestart'
state: present
expected_return_code: [0, 666, 3010]
- name: Install a .msp patch
win_package:
path: C:\Patches\Product.msp
state: present
- name: Remove a .msp patch
win_package:
product_id: '{AC76BA86-A440-FFFF-A440-0C13154E5D00}'
state: absent
- name: Enable installation of 3rd party MSIX packages
win_regedit:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock
name: AllowAllTrustedApps
data: 1
type: dword
state: present
- name: Install an MSIX package for the current user
win_package:
path: C:\Installers\Calculator.msix # Can be .appx, .msixbundle, or .appxbundle
state: present
- name: Uninstall an MSIX package using the product_id
win_package:
product_id: InputApp
state: absent
'''
RETURN = r'''
log:
description: The contents of the MSI log.
returned: installation/uninstallation failure for MSI packages
description: The contents of the MSI or MSP log.
returned: installation/uninstallation failure for MSI or MSP packages
type: str
sample: Installation completed successfully
rc:

@ -1,21 +1,35 @@
---
# spaces are tricky, let's have one by default
test_win_package_path_safe: C:\ansible\win_package
test_win_package_path: C:\ansible\win package
test_win_package_log_path_install: C:\ansible\win package\test-install.log
test_win_package_log_path_uninstall: C:\ansible\win package\test-uninstall.log
test_win_package_good_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/roles/test_win_package/good.msi
test_win_package_reboot_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/roles/test_win_package/reboot.msi
test_win_package_bad_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/roles/test_win_package/bad.msi
test_win_package_exe_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/roles/test_win_package/7z.exe # TODO: change to it's own executable
test_path: '{{ remote_tmp_dir }}\win_package .ÅÑŚÌβŁÈ [$!@^&test(;)]'
test_win_package_good_id: '{223D9A13-653B-4231-A365-EDDC30B4F226}'
test_win_package_reboot_id: '{223D9A13-653B-4231-A365-EDDC30B4F227}'
test_win_package_exe_id: 7-Zip
# MSI packages
good_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/roles/test_win_package/good.msi
reboot_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/roles/test_win_package/reboot.msi
bad_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/roles/test_win_package/bad.msi
# define the below to run the network tests, all 3 msi's should exist in this path
# test_win_package_network_path: \\ANSIBLE\network
# MSIX tools
makeappx_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/win_package/makeappx.zip
# set the below to test a network path without credential delegation like Basic or NTLM
# test_win_package_network_username: ANSIBLE\User
# test_win_package_network_password: Password
# MSP packages - https://wixtoolset.org/documentation/manual/v3/patching/patch_building.html
patch_msi_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/win_package/patch.msi
patch_msp_url: https://ansible-ci-files.s3.amazonaws.com/test/integration/targets/win_package/patch.msp
patch_install_file: C:\Program Files (x86)\Patch Sample Directory\Sample.txt
good_id: '{223D9A13-653B-4231-A365-EDDC30B4F226}'
reboot_id: '{223D9A13-653B-4231-A365-EDDC30B4F227}'
patch_product_id: '{48C49ACE-90CF-4161-9C6E-9162115A54DD}'
patch_patch_id: '{224C316C-5894-4771-BABF-21A3AC1F75FF}'
msix_id: WinPackageMsix
msixbundle_id: WinPackageBundleMsix
appx_id: WinPackageAppx
appxbundle_id: WinPackageBundleAppx
registry_id: WinPackageRegistry
all_ids:
- '{{ good_id }}'
- '{{ reboot_id }}'
- '{{ patch_product_id }}'
- '{{ msix_id }}'
- '{{ msixbundle_id }}'
- '{{ appx_id }}'
- '{{ appxbundle_id }}'
- '{{ registry_id }}'

@ -0,0 +1,15 @@
---
- name: remove trusted root cert
win_certificate_store:
thumbprint: '{{ test_win_package_msix_packages.thumbprint }}'
store_location: LocalMachine
store_name: Root
state: absent
- name: remove sideloading mode for msix
win_regedit:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock
name: AllowAllTrustedApps
data: 0
type: dword
state: present

@ -0,0 +1,194 @@
#!powershell
# Copyright: (c) 2020, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.Basic
#Requires -Module Ansible.ModuleUtils.ArgvParser
#Requires -Module Ansible.ModuleUtils.CommandUtil
$spec = @{
options = @{
packages = @{
type = "list"
elements = "dict"
options = @{
identity = @{ type = "str"; required = $true }
version = @{ type = "str"; required = $true }
architecture = @{ type = "str" }
resource_id = @{ type = "str" }
min_version = @{ type = "str"; default = "10.0.17763.0" }
max_version = @{ type = "str"; default = "10.0.18362.0" }
filename = @{ type = "str"; required = $true }
}
}
bundles = @{
type = "list"
elements = "dict"
options = @{
files = @{ type = "list"; elements = "str"; required = $true }
filename = @{ type = "str"; required = $true }
}
}
publisher = @{ type = "str"; required = $true }
path = @{ type = "str"; required = $true }
makeappx_path = @{ type = "str"; required = $true }
signtool_path = @{ type = "str"; required = $true }
}
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$packages = $module.Params.packages
$bundles = $module.Params.bundles
$publisher = $module.Params.publisher
$path = $module.Params.path
$makeappxPath = $module.Params.makeappx_path
$signtoolPath = $module.Params.signtool_path
if (-not (Test-Path -LiteralPath $path)) {
$module.FailJson("The path at '$path' does not exist")
}
$manifest = @'
<?xml version="1.0" encoding="utf-8"?>
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities">
<Identity Name="{0}" Version="{1}" Publisher="{2}"{3}{4} />
<Properties>
<DisplayName>{0}DisplayName</DisplayName>
<PublisherDisplayName>PublisherDisplayName</PublisherDisplayName>
<Description>Test MSIX Package for win_package</Description>
<Logo>icon.png</Logo>
</Properties>
<Resources>
<Resource Language="en-us" />
</Resources>
<Dependencies>
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="{5}" MaxVersionTested="{6}" />
</Dependencies>
<Capabilities>
<rescap:Capability Name="runFullTrust"/>
</Capabilities>
<Applications>
<Application Id="MsixPackage" Executable="test.exe" EntryPoint="Windows.FullTrustApplication">
<uap:VisualElements DisplayName="{0}AppDisplayName" Description="Description" Square150x150Logo="icon.png" Square44x44Logo="icon.png" BackgroundColor="#464646"/>
</Application>
</Applications>
</Package>
'@
# bytes of http://1x1px.me/000000-0.png
$iconBytes = [System.Convert]::FromBase64String('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNiYAAAAAkAAxkR2eQAAAAASUVORK5CYII=')
$certParams = @{
# Can only create in the My store, so store it there temporarily.
CertStoreLocation = 'Cert:\CurrentUser\My'
FriendlyName = 'win_package test'
KeyUsage = 'DigitalSignature'
Subject = $publisher
TextExtension = @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}")
Type = 'Custom'
}
$cert = New-SelfSignedCertificate @certParams
try {
# Need to create a temporary pfx for signtool.exe and we need to import the cert to the Trusted Root store.
$module.Result.thumbprint = $cert.Thumbprint
$certPath = Join-Path -Path $module.Tmpdir -ChildPath 'cert.pfx'
$certPassword = ([char[]]([char]33..[char]126) | Sort-Object {Get-Random})[0..16] -join ''
$certPasswordSS = ConvertTo-SecureString -String $certPassword -AsPlainText -Force
$null = $cert | Export-PfxCertificate -FilePath $certPath -Password $certPasswordSS
$importParams = @{
FilePath = $certPath
CertStoreLocation = 'Cert:\LocalMachine\Root'
Password = $certPasswordSS
}
$null = Import-PfxCertificate @importParams
} finally {
$cert | Remove-Item -Force
}
$module.Result.changed = $true
foreach ($info in $packages) {
$architectureAttribute = ""
if ($info.architecture) {
$architectureAttribute = " ProcessorArchitecture=`"$($info.architecture)`""
}
$resourceIdAttribute = ""
if ($info.resource_id) {
$resourceIdAttribute = " ResourceId=`"$($info.resource_id)`""
}
$xml = $manifest -f @(
$info.identity, $info.version, $publisher, $architectureAttribute, $resourceIdAttribute, $info.min_version,
$info.max_version
)
$tempDir = Join-Path -Path $module.Tmpdir -ChildPath ([System.IO.Path]::GetRandomFileName())
New-Item -Path $tempDir -ItemType Directory > $null
Set-Content -LiteralPath (Join-Path -Path $tempDir -ChildPath 'AppxManifest.xml') -Value $xml
Set-Content -LiteralPath (Join-Path -Path $tempDir -ChildPath 'icon.png') -Value $iconBytes
Set-Content -LiteralPath (Join-Path -Path $tempDir -ChildPath 'test.exe') -Value ''
$outPath = Join-Path -Path $path -ChildPath $info.filename
$makeArguments = @($makeappxPath, 'pack', '/d', $tempDir, '/p', $outPath, '/o')
$res = Run-Command -command (Argv-ToString -arguments $makeArguments)
if ($res.rc -ne 0) {
$module.Result.rc = $res.rc
$module.Result.stdout = $res.stdout
$module.Result.stderr = $res.stderr
$module.FailJson("Failed to make package for $($info.filename): see stdout and stderr for more info")
}
Remove-Item -Literalpath $tempDir -Force -Recurse
$signArguments = @($signtoolPath, 'sign', '/a', '/v', '/fd', 'SHA256', '/f', $certPath, '/p', $certPassword,
$outPath)
$res = Run-Command -command (Argv-ToString -arguments $signArguments)
if ($res.rc -ne 0) {
$module.Result.rc = $res.rc
$module.Result.stdout = $res.stdout
$module.Result.stderr = $res.stderr
$module.FailJson("Failed to sign package for $($info.filename): see stdout and stderr for more info")
}
}
foreach ($info in $bundles) {
$tempDir = Join-Path -Path $module.Tmpdir -ChildPath ([System.IO.Path]::GetRandomFileName())
New-Item -Path $tempDir -ItemType Directory > $null
foreach ($name in $info.files) {
$sourcePath = Join-Path -Path $path -ChildPath $name
$targetPath = Join-Path -Path $tempDir -ChildPath $name
Move-Item -LiteralPath $sourcePath -Destination $targetPath
}
$outPath = Join-Path -Path $path -ChildPath $info.filename
$makeArguments = @($makeappxPath, 'bundle', '/d', $tempDir, '/p', $outPath, '/o')
$res = Run-Command -command (Argv-ToString -arguments $makeArguments)
if ($res.rc -ne 0) {
$module.Result.rc = $res.rc
$module.Result.stdout = $res.stdout
$module.Result.stderr = $res.stderr
$module.FailJson("Failed to make bundle for $($info.filename): see stdout and stderr for more info")
}
Remove-Item -LiteralPath $tempDir -Force -Recurse
$signArguments = @($signtoolPath, 'sign', '/a', '/v', '/fd', 'SHA256', '/f', $certPath, '/p', $certPassword,
$outPath)
$res = Run-Command -command (Argv-ToString -arguments $signArguments)
if ($res.rc -ne 0) {
$module.Result.rc = $res.rc
$module.Result.stdout = $res.stdout
$module.Result.stderr = $res.stderr
$module.FailJson("Failed to sign bundle for $($info.filename): see stdout and stderr for more info")
}
}
$module.ExitJson()

@ -0,0 +1,2 @@
dependencies:
- setup_remote_tmp_dir

@ -1,303 +0,0 @@
---
- name: install local exe (check mode)
win_package:
path: '{{test_win_package_path}}\7z.exe'
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: present
register: install_local_exe_check
check_mode: yes
- name: get result of install local exe (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: install_local_exe_actual_check
- name: assert install local exe (check mode)
assert:
that:
- install_local_exe_check is changed
- install_local_exe_check.reboot_required == False
- install_local_exe_actual_check.exists == False
- name: install local exe
win_package:
path: '{{test_win_package_path}}\7z.exe'
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: present
register: install_local_exe
- name: get result of install local exe
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: install_local_exe_actual
- name: assert install local exe
assert:
that:
- install_local_exe is changed
- install_local_exe.reboot_required == False
- install_local_exe.rc == 0
- install_local_exe_actual.exists == True
- name: install local exe (idempotent)
win_package:
path: '{{test_win_package_path}}\7z.exe'
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: present
register: install_local_exe_idempotent
- name: assert install local exe (idempotent)
assert:
that:
- install_local_exe_idempotent is not changed
- name: uninstall local exe with path (check mode)
win_package:
path: C:\Program Files\7-Zip\Uninstall.exe
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: absent
register: uninstall_path_local_exe_check
check_mode: yes
- name: get result of uninstall local exe with path (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: uninstall_path_local_exe_actual_check
- name: assert uninstall local exe with path (check mode)
assert:
that:
- uninstall_path_local_exe_check is changed
- uninstall_path_local_exe_check.reboot_required == False
- uninstall_path_local_exe_actual_check.exists == True
- name: uninstall local exe with path
win_package:
path: C:\Program Files\7-Zip\Uninstall.exe
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: absent
register: uninstall_path_local_exe
- name: get result of uninstall local exe with path
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: uninstall_path_local_exe_actual
- name: assert uninstall local exe with path
assert:
that:
- uninstall_path_local_exe is changed
- uninstall_path_local_exe.reboot_required == False
- uninstall_path_local_exe.rc == 0
- uninstall_path_local_exe_actual.exists == False
- name: uninstall local exe with path (idempotent)
win_package:
path: C:\Program Files\7-Zip\Uninstall.exe
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: absent
register: uninstall_path_local_exe_idempotent
- name: assert uninstall local exe with path (idempotent)
assert:
that:
- uninstall_path_local_exe_idempotent is not changed
- name: install url exe (check mode)
win_package:
path: '{{test_win_package_exe_url}}'
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: present
register: install_url_exe_check
check_mode: yes
- name: get result of install url exe (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: install_url_exe_actual_check
- name: assert install url exe (check mode)
assert:
that:
- install_url_exe_check is changed
- install_url_exe_check.reboot_required == False
- install_url_exe_actual_check.exists == False
- name: install url exe
win_package:
path: '{{test_win_package_exe_url}}'
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: present
register: install_url_exe
- name: get result of install url exe
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: install_url_exe_actual
- name: assert install url exe
assert:
that:
- install_url_exe is changed
- install_url_exe.reboot_required == False
- install_url_exe.rc == 0
- install_url_exe_actual.exists == True
- name: install url exe (idempotent)
win_package:
path: '{{test_win_package_exe_url}}'
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: present
register: install_url_exe_again
- name: assert install url exe (idempotent)
assert:
that:
- install_url_exe_again is not changed
- name: uninstall local exe with product_id (check mode)
win_package:
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: absent
register: uninstall_id_local_exe_check
check_mode: yes
- name: get result of uninstall local exe with product_id (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: uninstall_id_local_exe_actual_check
- name: assert uninstall local exe with product_id (check mode)
assert:
that:
- uninstall_id_local_exe_check is changed
- uninstall_id_local_exe_check.reboot_required == False
- uninstall_id_local_exe_actual_check.exists == True
- name: uninstall local exe with product_id
win_package:
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: absent
register: uninstall_id_local_exe
- name: get result of uninstall local exe with product_id
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: uninstall_id_local_exe_actual
- name: assert uninstall local exe with product_id
assert:
that:
- uninstall_id_local_exe is changed
- uninstall_id_local_exe.reboot_required == False
- uninstall_id_local_exe.rc == 0
- uninstall_id_local_exe_actual.exists == False
- name: uninstall local exe with product_id (idempotent)
win_package:
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: absent
register: uninstall_id_local_exe_idempotent
- name: assert uninstall local exe with product_id (idempotent)
assert:
that:
- uninstall_id_local_exe_idempotent is not changed
- name: install exe checking path
win_package:
path: '{{test_win_package_path}}\7z.exe'
arguments: /S
creates_path: C:\Program Files\7-Zip\7z.exe
register: install_exe_create_path
- name: get result of install exe checking path
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: install_exe_create_path_actual
- name: assert install exe checking path
assert:
that:
- install_exe_create_path.changed == True
- install_exe_create_path_actual.exists == True
- name: install exe checking path (idempotent)
win_package:
path: '{{test_win_package_path}}\7z.exe'
arguments: /S
creates_path: C:\Program Files\7-Zip\7z.exe
register: install_exe_create_path_again
- name: assert install exe checking path (idempotent)
assert:
that:
- not install_exe_create_path_again.changed == True
- name: install exe checking path and version
win_package:
path: '{{test_win_package_path}}\7z.exe'
arguments: /S
creates_path: C:\Program Files\7-Zip\7z.exe
creates_version: '16.04'
register: install_exe_create_version_match
- name: assert install exe checking path and version
assert:
that:
- install_exe_create_version_match is not changed
- name: install exe checking path and version mismatch
win_package:
path: '{{test_win_package_path}}\7z.exe'
arguments: /S
creates_path: C:\Program Files\7-Zip\7z.exe
creates_version: fail-version
register: install_exe_create_version_mismatch
- name: assert install exe checking path and version mistmatch
assert:
that:
- install_exe_create_version_mismatch is changed
- name: install exe checking service
win_package:
path: '{{test_win_package_path}}\7z.exe'
arguments: /S
creates_service: Netlogon
register: install_exe_create_service_match
- name: assert install exe checking service
assert:
that:
- install_exe_create_service_match is not changed
- name: install exe checking service mismatch
win_package:
path: '{{test_win_package_path}}\7z.exe'
arguments: /S
creates_service: fake-service
register: install_exe_create_service_mismatch
- name: assert install exe checking service mismatch
assert:
that:
- install_exe_create_service_mismatch is changed
- name: uninstall exe post tests
win_package:
arguments: /S
product_id: '{{test_win_package_exe_id}}'
state: absent

@ -2,14 +2,14 @@
---
- name: fail to install broken msi
win_package:
path: '{{test_win_package_path}}\bad.msi'
path: '{{ test_path }}\bad.msi'
state: present
register: fail_bad_rc
failed_when: "'unexpected rc from install' not in fail_bad_rc.msg and fail_bad_rc.exit_code != 1603"
failed_when: "'unexpected rc from' not in fail_bad_rc.msg and fail_bad_rc.rc != 1603"
- name: fail when not using an int for a return code
win_package:
path: '{{test_win_package_path}}\good.msi'
path: '{{ test_path }}\good.msi'
state: present
expected_return_code: 0,abc
register: fail_invalid_return_code
@ -27,19 +27,12 @@
register: fail_no_path_state_absent_no_id
failed_when: 'fail_no_path_state_absent_no_id.msg != "state is absent but any of the following are missing: path, product_id"'
- name: fail when product_id is not set and path is not a local MSI
win_package:
path: '{{test_win_package_good_url}}'
state: present
register: fail_install_url_no_id
failed_when: fail_install_url_no_id.msg != 'product_id is required when the path is not an MSI or the path is an MSI but not local'
- name: fail invalid local path
win_package:
path: '{{test_win_package_path}}\no file.msi'
path: '{{ test_path }}\no file.msi'
state: present
register: fail_invalid_local_path
failed_when: fail_invalid_local_path.msg != 'the file at the local path ' + test_win_package_path + '\\no file.msi cannot be reached'
failed_when: fail_invalid_local_path.msg != "the file at the path '" + test_path + "\\no file.msi' cannot be reached"
- name: fail invalid URL
win_package:
@ -47,26 +40,11 @@
product_id: 'id'
state: present
register: fail_invalid_url_path
failed_when: "fail_invalid_url_path.msg != 'the file at the URL http://fakeurl/file.msi cannot be reached: The remote name could not be resolved: \\'fakeurl\\''"
- name: fail invalid UNC path
win_package:
path: \\fakenetwork\unc file.msi
product_id: 'id'
state: present
register: fail_invalid_unc_path
failed_when: fail_invalid_unc_path.msg != 'the file at the UNC path \\\\fakenetwork\\unc file.msi cannot be reached, ensure the user_name account has access to this path or use an auth transport with credential delegation'
- name: fail when product_id is not set and path is not a local MSI
win_package:
path: '{{test_win_package_good_url}}'
state: present
register: fail_no_id_not_local_msi
failed_when: fail_no_id_not_local_msi.msg != 'product_id is required when the path is not an MSI or the path is an MSI but not local'
failed_when: "\"The remote name could not be resolved: 'fakeurl'\" not in fail_invalid_url_path.msg"
- name: fail to check version without creates_path
win_package:
path: '{{test_win_package_path}}\7z.exe'
path: '{{ test_path }}\good.msi'
state: present
creates_version: 1
register: fail_creates_version_without_path
@ -74,9 +52,9 @@
- name: fail to check version without when path is not a file
win_package:
path: '{{test_win_package_path}}\7z.exe'
path: '{{ test_path }}\good.msi'
state: present
creates_path: C:\Windows
creates_version: 1
register: fail_creates_version_not_a_file
failed_when: fail_creates_version_not_a_file.msg != 'creates_path must be a file not a directory when creates_version is set'
failed_when: "'creates_path must be a file not a directory when creates_version is set' not in fail_creates_version_not_a_file.msg"

@ -1,63 +1,71 @@
---
- name: ensure testing folders exists
- name: ensure testing folder exists
win_file:
path: '{{item}}'
path: '{{ test_path }}'
state: directory
with_items:
- '{{test_win_package_path}}'
- '{{test_win_package_path_safe}}'
- name: download msi files from S3 bucket
# Some of the registry_tests.yml create a badly formed unisntall string so remove the reg entry in case the test
# didn't get to cleaning itself up
- name: remove registry package path
win_regedit:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }}
state: absent
- name: download packages from S3 bucket
win_get_url:
url: '{{item.url}}'
dest: '{{test_win_package_path}}\{{item.name}}'
url: '{{ item.url }}'
dest: '{{ test_path }}\{{ item.name }}'
with_items:
- { url: '{{test_win_package_good_url}}', name: 'good.msi' }
- { url: '{{test_win_package_reboot_url}}', name: 'reboot.msi' }
- { url: '{{test_win_package_bad_url}}', name: 'bad.msi' }
# - { url: '{{test_win_package_exe_url}}', name: '7z.exe' }
- url: '{{ good_url }}'
name: good.msi
- url: '{{ reboot_url }}'
name: reboot.msi
- url: '{{ bad_url }}'
name: bad.msi
- url: '{{ patch_msi_url }}'
name: patch.msi
- url: '{{ patch_msp_url }}'
name: patch.msp
- name: make sure all test msi's are uninstalled before test
- name: make sure all test packages are uninstalled before test
win_package:
product_id: '{{item.id}}'
arguments: '{{item.args|default(omit)}}'
product_id: '{{ item }}'
state: absent
with_items:
- { id: '{{test_win_package_good_id}}' }
- { id: '{{test_win_package_reboot_id}}' }
# - { id: '{{test_win_package_exe_id}}', args: '/S' }
with_items: '{{ all_ids }}'
- block:
- name: run tests for expected failures
include_tasks: failure_tests.yml
- name: run tests for local and URL msi files
- name: run tests for msi files and URL paths
include_tasks: msi_tests.yml
# doesn't work 100% on AWS hosts, disabling for now until we get a better exe example
# - name: run tests for local and URL exe files
# include_tasks: exe_tests.yml
# The msix test setup will only work on Server 2019 or newer so conditionally run this
- name: check if we can run the msix tests
win_shell: |
$osVersion = [Version](Get-Item -LiteralPath "$env:SystemRoot\System32\kernel32.dll").VersionInfo.ProductVersion
$osVersion -ge [Version]"10.0.17763"
register: can_run_msix
changed_when: False
- name: run tests for msix packages
include_tasks: msix_tests.yml
when: can_run_msix.stdout | trim | bool
# these tests can be run manually by defining test_win_package_network_path
- name: run tests for network msi files (manual)
include_tasks: network_tests.yml
when: test_win_package_network_path is defined
- name: run tests for msp packages
include_tasks: msp_tests.yml
- name: run tests for registry packages
include_tasks: registry_tests.yml
always:
- name: make sure all test msi's are uninstalled after test
win_package:
product_id: '{{item.id}}'
arguments: '{{item.args|default(omit)}}'
- name: remove registry package path
win_regedit:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }}
state: absent
with_items:
- { id: '{{test_win_package_good_id}}' }
- { id: '{{test_win_package_reboot_id}}' }
# - { id: '{{test_win_package_exe_id}}', args: '/S' }
- name: cleanup test artifacts
win_file:
path: '{{item}}'
- name: make sure all test packages are uninstalled after test
win_package:
product_id: '{{ item }}'
state: absent
with_items:
- '{{test_win_package_path}}'
- '{{test_win_package_path_safe}}'
with_items: '{{ all_ids }}'

@ -2,20 +2,20 @@
# this test just makes sure the task doesn't fail when we set out expected rc
- name: install broken msi override expected rc
win_package:
path: '{{test_win_package_path}}\bad.msi'
path: '{{ test_path }}\bad.msi'
state: present
expected_return_code: 0,1603
- name: install local msi (check mode)
win_package:
path: '{{test_win_package_path}}\good.msi'
path: '{{ test_path }}\good.msi'
state: present
register: install_local_msi_check
check_mode: yes
- name: get result of install local msi (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ good_id }}
register: install_local_msi_actual_check
- name: assert install local msi (check mode)
@ -27,14 +27,14 @@
- name: install local msi with log
win_package:
path: '{{test_win_package_path}}\good.msi'
path: '{{ test_path }}\good.msi'
state: present
log_path: "{{test_win_package_log_path_install}}"
log_path: '{{ test_path }}\msi.log'
register: install_local_msi
- name: get result of install local msi
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ good_id }}
register: install_local_msi_actual
- name: assert install local msi
@ -47,7 +47,7 @@
- name: get result of install local msi log_path
win_stat:
path: "{{test_win_package_log_path_install}}"
path: '{{ test_path }}\msi.log'
register: install_local_msi_actual_log_path
- name: assert install local msi log path
@ -57,7 +57,7 @@
- name: install local msi (idempotent)
win_package:
path: '{{test_win_package_path}}\good.msi'
path: '{{ test_path }}\good.msi'
state: present
register: install_local_msi_idempotent
@ -68,14 +68,14 @@
- name: uninstall local msi with path (check mode)
win_package:
path: '{{test_win_package_path}}\good.msi'
path: '{{ test_path }}\good.msi'
state: absent
register: uninstall_path_local_msi_check
check_mode: yes
- name: get result of uninstall local msi with path (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ good_id }}
register: uninstall_path_local_msi_actual_check
- name: assert uninstall local msi with path (check mode)
@ -87,14 +87,14 @@
- name: uninstall local msi with path
win_package:
path: '{{test_win_package_path}}\good.msi'
path: '{{ test_path }}\good.msi'
state: absent
log_path: "{{test_win_package_log_path_uninstall}}"
log_path: '{{ test_path }}\msi uninstall.log'
register: uninstall_path_local_msi
- name: get result of uninstall local msi with path
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ good_id }}
register: uninstall_path_local_msi_actual
- name: assert uninstall local msi with path
@ -107,7 +107,7 @@
- name: get result of uninstall local msi with path
win_stat:
path: "{{test_win_package_log_path_uninstall}}"
path: '{{ test_path }}\msi uninstall.log'
register: uninstall_path_local_msi_actual_log_path
- name: assert uninstall local msi with path
@ -117,7 +117,7 @@
- name: uninstall local msi with path (idempotent)
win_package:
path: '{{test_win_package_path}}\good.msi'
path: '{{ test_path }}\good.msi'
state: absent
register: uninstall_path_local_msi_idempotent
@ -128,15 +128,15 @@
- name: install url msi (check mode)
win_package:
path: '{{test_win_package_good_url}}'
product_id: '{{test_win_package_good_id}}'
path: '{{ good_url }}'
product_id: '{{ good_id }}'
state: present
register: install_url_msi_check
check_mode: yes
- name: get result of install url msi (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ good_id }}
register: install_url_msi_actual_check
- name: assert install url msi (check mode)
@ -148,14 +148,14 @@
- name: install url msi
win_package:
path: '{{test_win_package_good_url}}'
product_id: '{{test_win_package_good_id}}'
path: '{{ good_url }}'
product_id: '{{ good_id }}'
state: present
register: install_url_msi
- name: get result of install url msi
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ good_id }}
register: install_url_msi_actual
- name: assert install url msi
@ -168,8 +168,8 @@
- name: install url msi (idempotent)
win_package:
path: '{{test_win_package_good_url}}'
product_id: '{{test_win_package_good_id}}'
path: '{{ good_url }}'
product_id: '{{ good_id }}'
state: present
register: install_url_msi_again
@ -180,14 +180,14 @@
- name: uninstall local msi with product_id (check mode)
win_package:
product_id: '{{test_win_package_good_id}}'
product_id: '{{ good_id }}'
state: absent
register: uninstall_id_local_msi_check
check_mode: yes
- name: get result of uninstall local msi with product_id (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ good_id }}
register: uninstall_id_local_msi_actual_check
- name: assert uninstall local msi with product_id (check mode)
@ -199,13 +199,13 @@
- name: uninstall local msi with product_id
win_package:
product_id: '{{test_win_package_good_id}}'
product_id: '{{ good_id }}'
state: absent
register: uninstall_id_local_msi
- name: get result of uninstall local msi with product_id
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ good_id }}
register: uninstall_id_local_msi_actual
- name: assert uninstall local msi with product_id
@ -218,7 +218,7 @@
- name: uninstall local msi with product_id (idempotent)
win_package:
product_id: '{{test_win_package_good_id}}'
product_id: '{{ good_id }}'
state: absent
register: uninstall_id_local_msi_idempotent
@ -229,14 +229,14 @@
- name: install local reboot msi (check mode)
win_package:
path: '{{test_win_package_path}}\reboot.msi'
path: '{{ test_path }}\reboot.msi'
state: present
register: install_local_reboot_msi_check
check_mode: yes
- name: get result of install local reboot msi (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_reboot_id}}
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ reboot_id }}
register: install_local_reboot_msi_actual_check
- name: assert install local reboot msi (check mode)
@ -248,13 +248,13 @@
- name: install local reboot msi
win_package:
path: '{{test_win_package_path}}\reboot.msi'
path: '{{ test_path }}\reboot.msi'
state: present
register: install_local_reboot_msi
- name: get result of install local reboot msi
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_reboot_id}}
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ reboot_id }}
register: install_local_reboot_msi_actual
- name: assert install local reboot msi
@ -267,7 +267,7 @@
- name: install local reboot msi (idempotent)
win_package:
path: '{{test_win_package_path}}\reboot.msi'
path: '{{ test_path }}\reboot.msi'
state: present
register: install_local_reboot_msi_idempotent
@ -278,7 +278,7 @@
- name: uninstall reboot msi after test
win_package:
path: '{{test_win_package_path}}\reboot.msi'
path: '{{ test_path }}\reboot.msi'
state: absent
- name: ensure the install folder is cleaned in case uninstall didn't work
@ -288,7 +288,7 @@
- name: install local msi with arguments (check mode)
win_package:
path: '{{test_win_package_path}}\good.MSI'
path: '{{ test_path }}\good.MSI'
state: present
arguments: ADDLOCAL=Cow
register: install_msi_argument_check
@ -314,7 +314,7 @@
- name: install local msi with arguments
win_package:
path: '{{test_win_package_path}}\good.MSI'
path: '{{ test_path}}\good.MSI'
state: present
arguments: ADDLOCAL=Cow
register: install_msi_argument
@ -340,7 +340,7 @@
- name: install local msi with arguments (idempotent)
win_package:
path: '{{test_win_package_path}}\good.MSI'
path: '{{ test_path}}\good.MSI'
state: present
arguments: ADDLOCAL=Cow
register: install_msi_argument_again
@ -352,29 +352,101 @@
- name: uninstall good msi after test
win_package:
path: '{{test_win_package_path}}\good.msi'
path: '{{ test_path }}\good.msi'
state: absent
- name: install good msi for all users
win_package:
path: '{{ test_path }}\good.msi'
state: present
arguments: ALLUSERS=1
register: install_good_all_users
- name: get result of install good msi for all users
win_shell: |
Add-Type -Namespace Msi -Name NativeMethods -UsingNamespace System.Text -MemberDefinition @'
[DllImport("Msi.dll", CharSet = CharSet.Unicode)]
public static extern UInt32 MsiGetProductInfoExW(
string szProductCode,
string szUserSid,
UInt32 dwContext,
string szProperty,
StringBuilder szValue,
ref UInt32 pcchValue);
'@
$productCode = '{{ good_id }}'
$sb = New-Object -TypeName System.Text.StringBuilder -ArgumentList 0
$sbLength = [UInt32]0
$null = [Msi.NativeMethods]::MsiGetProductInfoExW($productCode, [NullString]::Value, 4, "State", $sb, [ref]$sbLength)
$sbLength += 1
$null = $sb.EnsureCapacity($sbLength)
$null = [Msi.NativeMethods]::MsiGetProductInfoExW($productCode, [NullString]::Value, 4, "State", $sb, [ref]$sbLength)
[int]$sb.ToString()
register: install_good_all_users_actual
- name: assert install good msi for all users
assert:
that:
- install_good_all_users is changed
- install_good_all_users_actual.stdout | trim | int == 5 # INSTALLSTATE_DEFAULT
- name: install good msi for all users (idempotent)
win_package:
path: '{{ test_path }}\good.msi'
state: present
arguments: ALLUSERS=1
register: install_good_all_users_again
- name: assert install good msi for all users (idempotent)
assert:
that:
- not install_good_all_users_again is changed
- name: uninstall good msi for all users
win_package:
product_id: '{{ good_id }}'
state: absent
register: uninstall_good_all_users
- name: get result of uninstall good msi for all users
win_shell: |
Add-Type -Namespace Msi -Name NativeMethods -MemberDefinition @'
[DllImport("Msi.dll", CharSet = CharSet.Unicode)]
public static extern Int32 MsiQueryProductStateW(
string szProductCode);
'@
[Msi.NativeMethods]::MsiQueryProductStateW('{{ good_id }}')
register: uninstall_good_all_users_actual
- name: assert uninstall good msi for all users
assert:
that:
- uninstall_good_all_users is changed
- uninstall_good_all_users_actual.stdout | trim | int == -1 # INSTALLSTATE_UNKNOWN
- name: create custom install directory for msi install
win_file:
path: '{{test_win_package_path_safe}}\good'
path: '{{ test_path }}\msi install'
state: directory
- name: install msi to custom path using string arguments
win_package:
path: '{{test_win_package_path}}\good.msi'
path: '{{ test_path }}\good.msi'
state: present
arguments: ADDLOCAL=Cow INSTALLDIR={{test_win_package_path_safe}}\install
arguments: ADDLOCAL=Cow INSTALLDIR="{{ test_path }}\msi install"
register: install_msi_string_arguments
- name: get result of moo file after install local msi with string arguments
win_stat:
path: '{{test_win_package_path_safe}}\install\moo.exe'
path: '{{ test_path }}\msi install\moo.exe'
register: install_msi_string_arguments_moo
- name: get result of cow file after install local msi with string arguments
win_stat:
path: '{{test_win_package_path_safe}}\install\cow.exe'
path: '{{ test_path }}\msi install\cow.exe'
register: install_msi_string_arguments_cow
- name: assert results of install msi to custom path using string arguments
@ -388,38 +460,52 @@
- name: uninstall good msi after string argument test
win_package:
path: '{{test_win_package_path}}\good.msi'
path: '{{ test_path }}\good.msi'
state: absent
- name: install msi to custom path using list arguments
win_package:
path: '{{test_win_package_path}}\good.msi'
state: present
arguments:
- ADDLOCAL=Moo
- INSTALLDIR={{test_win_package_path_safe}}\install
register: install_msi_list_arguments
- name: get result of moo file after install local msi with list arguments
win_stat:
path: '{{test_win_package_path_safe}}\install\moo.exe'
register: install_msi_list_arguments_moo
- name: get result of cow file after install local msi with list arguments
win_stat:
path: '{{test_win_package_path_safe}}\install\cow.exe'
register: install_msi_list_arguments_cow
- name: assert results of install msi to custom path using list arguments
assert:
that:
- install_msi_list_arguments is changed
- install_msi_list_arguments.reboot_required == False
- install_msi_list_arguments.rc == 0
- install_msi_list_arguments_moo.stat.exists == True
- install_msi_list_arguments_cow.stat.exists == False
# MSI arguments KEY="value" are known to fail when set as a list, for this test just create a simple folder path that
# does not need to be escaped and cleanup at the end.
- name: create a simple spaceless folder for argument list test
win_file:
path: C:\ansible_win_package
state: directory
- name: uninstall good msi after list argument test
win_package:
path: '{{test_win_package_path}}\good.msi'
state: absent
- block:
- name: install msi to custom path using list arguments
win_package:
path: '{{ test_path }}\good.msi'
state: present
arguments:
- ADDLOCAL=Moo
- INSTALLDIR=C:\ansible_win_package
register: install_msi_list_arguments
- name: get result of moo file after install local msi with list arguments
win_stat:
path: C:\ansible_win_package\moo.exe
register: install_msi_list_arguments_moo
- name: get result of cow file after install local msi with list arguments
win_stat:
path: C:\ansible_win_package\cow.exe
register: install_msi_list_arguments_cow
- name: assert results of install msi to custom path using list arguments
assert:
that:
- install_msi_list_arguments is changed
- install_msi_list_arguments.reboot_required == False
- install_msi_list_arguments.rc == 0
- install_msi_list_arguments_moo.stat.exists == True
- install_msi_list_arguments_cow.stat.exists == False
- name: uninstall good msi after list argument test
win_package:
path: '{{ test_path }}\good.msi'
state: absent
always:
- name: remove spaceless folder for argument list test
win_file:
path: C:\ansible_win_package
state: absent

@ -0,0 +1,450 @@
---
- name: enable sideloading of apps for test
win_regedit:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppModelUnlock
name: AllowAllTrustedApps
data: 1
type: dword
state: present
notify: remove sideloading mode for msix
- name: download makeappx binaries
win_get_url:
url: '{{ makeappx_url }}'
dest: '{{ test_path }}\makeappx.zip'
- name: extract makeappx binaries
win_unzip:
src: '{{ test_path }}\makeappx.zip'
dest: '{{ test_path }}\makeappx'
- name: setup MSIX packages
win_make_appx:
packages:
- identity: '{{ msix_id }}'
version: 1.0.0.0
filename: WinPackage-1.0.0.0.msix
- identity: '{{ msix_id }}'
version: 1.0.0.1
filename: WinPackage-1.0.0.1.msix
- identity: '{{ appx_id }}'
version: 1.0.0.0
filename: WinPackage.appx
- identity: '{{ msixbundle_id }}'
version: 1.0.0.1
architecture: x64
resource_id: resid
filename: WinPackageBundle-x64.msix
- identity: '{{ msixbundle_id }}'
version: 1.0.0.1
architecture: x86
resource_id: resid
filename: WinPackageBundle-x86.msix
- identity: '{{ appxbundle_id }}'
version: 1.0.0.1
architecture: x64
resource_id: resid
filename: WinPackageBundle-x64.appx
- identity: '{{ appxbundle_id }}'
version: 1.0.0.1
architecture: x86
resource_id: resid
filename: WinPackageBundle-x86.appx
- identity: FailMsix
version: 1.2.3.4
min_version: 99.0.0.0
max_version: 99.0.0.0
filename: Fail.msix
bundles:
- files:
- WinPackageBundle-x64.msix
- WinPackageBundle-x86.msix
filename: WinPackageBundle.msixbundle
- files:
- WinPackageBundle-x64.appx
- WinPackageBundle-x86.appx
filename: WinPackageBundle.appxbundle
publisher: CN=Ansible Core, O=Ansible, L=Durhan, S=NC, C=USA
path: '{{ test_path }}'
makeappx_path: '{{ test_path }}\makeappx\makeappx.exe'
signtool_path: '{{ test_path }}\makeappx\signtool.exe'
become: yes # New-SelfSignedCertificate requires this to store the cert with key into the store.
become_method: runas
vars:
ansible_become_user: '{{ ansible_user }}'
ansible_become_pass: '{{ ansible_password }}'
register: test_win_package_msix_packages
notify: remove trusted root cert
- name: install msix (check mode)
win_package:
path: '{{ test_path }}\WinPackage-1.0.0.0.msix'
state: present
register: msix_install_check
check_mode: yes
- name: get result of install msix (check mode)
win_shell: if (Get-AppxPackage -Name '{{ msix_id }}') { $true } else { $false }
register: msix_install_actual_check
changed_when: False
- name: assert install msix (check mode)
assert:
that:
- msix_install_check is changed
- msix_install_check.rc == 0
- not msix_install_check.reboot_required
- not msix_install_actual_check.stdout | trim | bool
- name: install msix
win_package:
path: '{{ test_path }}\WinPackage-1.0.0.0.msix'
state: present
register: msix_install
- name: get result of install msix
win_shell: if (Get-AppxPackage -Name '{{ msix_id }}') { $true } else { $false }
register: msix_install_actual
changed_when: False
- name: assert install msix
assert:
that:
- msix_install is changed
- msix_install.rc == 0
- not msix_install.reboot_required
- msix_install_actual.stdout | trim | bool
- name: install msix (idempotence)
win_package:
path: '{{ test_path }}\WinPackage-1.0.0.0.msix'
state: present
register: msix_install_again
- name: assert install msix (idempotence)
assert:
that:
- not msix_install_again is changed
- name: install updated msix package
win_package:
path: '{{ test_path }}\WinPackage-1.0.0.1.msix'
state: present
register: msix_install_updated
- name: get result of install updated msix package
win_shell: Get-AppxPackage -Name '{{ msix_id }}' | Select-Object -ExpandProperty Version
changed_when: False
register: msix_install_updated_actual
- name: assert result of install updated msix package
assert:
that:
- msix_install_updated is changed
- msix_install_updated.rc == 0
- not msix_install_updated.reboot_required
- msix_install_updated_actual.stdout | trim == "1.0.0.1"
- name: fail to install older msix when new is present
win_package:
path: '{{ test_path }}\WinPackage-1.0.0.0.msix'
state: present
register: fail_msix_older
failed_when: "'unexpected status from Add-AppxPackage' not in fail_msix_older.msg"
- name: remove msix by path (check mode)
win_package:
path: '{{ test_path }}\WinPackage-1.0.0.1.msix'
state: absent
register: msix_uninstall_check
check_mode: yes
- name: get result of remove msix by path (check mode)
win_shell: if (Get-AppxPackage -Name '{{ msix_id }}') { $true } else { $false }
changed_when: False
register: msix_uninstall_actual_check
- name: assert results of remove msix by path (check mode)
assert:
that:
- msix_uninstall_check is changed
- msix_uninstall_check.rc == 0
- not msix_uninstall_check.reboot_required
- msix_uninstall_actual_check.stdout | trim | bool
- name: remove msix by path
win_package:
path: '{{ test_path }}\WinPackage-1.0.0.1.msix'
state: absent
register: msix_uninstall
- name: get result of remove msix by path
win_shell: if (Get-AppxPackage -Name '{{ msix_id }}') { $true } else { $false }
changed_when: False
register: msix_uninstall_actual
- name: assert results of remove msix by path
assert:
that:
- msix_uninstall is changed
- msix_uninstall.rc == 0
- not msix_uninstall.reboot_required
- not msix_uninstall_actual.stdout | trim | bool
- name: remove msix by path (idempotence)
win_package:
path: '{{ test_path }}\WinPackage-1.0.0.1.msix'
state: absent
register: msix_uninstall_again
- name: assert results of remove msix by path (idempotence)
assert:
that:
- not msix_uninstall_again is changed
# The install steps are the same as msix so no need for check and idempotency tests
- name: install appx
win_package:
path: '{{ test_path }}\WinPackage.appx'
state: present
register: appx_install
- name: get result of install appx
win_shell: if (Get-AppxPackage -Name '{{ appx_id }}') { $true } else { $false }
changed_when: False
register: appx_install_actual
- name: assert results of install appx
assert:
that:
- appx_install is changed
- appx_install.rc == 0
- not appx_install.reboot_required
- appx_install_actual.stdout | trim | bool
- name: remove appx by id (check mode)
win_package:
product_id: '{{ appx_id }}'
state: absent
register: appx_uninstall_check
check_mode: yes
- name: get result of remove appx (check mode)
win_shell: if (Get-AppxPackage -Name '{{ appx_id }}') { $true } else { $false }
changed_when: False
register: appx_uninstall_actual_check
- name: assert results of remove appx by id (check mode)
assert:
that:
- appx_uninstall_check is changed
- appx_uninstall_check.rc == 0
- not appx_uninstall_check.reboot_required
- appx_uninstall_actual_check.stdout | trim | bool
- name: remove appx by id
win_package:
product_id: '{{ appx_id }}'
state: absent
register: appx_uninstall
- name: get result of remove appx
win_shell: if (Get-AppxPackage -Name '{{ appx_id }}') { $true } else { $false }
changed_when: False
register: appx_uninstall_actual
- name: assert results of remove appx by id
assert:
that:
- appx_uninstall is changed
- appx_uninstall.rc == 0
- not appx_uninstall.reboot_required
- not appx_uninstall_actual.stdout | trim | bool
- name: remove appx by id (idempotence)
win_package:
product_id: '{{ appx_id }}'
state: absent
register: appx_uninstall_again
- name: assert results of remove appx by id (idempotence)
assert:
that:
- not appx_uninstall_again is changed
- name: validate failures are detected on a bad package
win_package:
path: '{{ test_path }}\Fail.msix'
state: present
register: fail_msix
failed_when: "'unexpected status from Add-AppxPackage' not in fail_msix.msg"
- name: install msixbundle (check mode)
win_package:
path: '{{ test_path }}\WinPackageBundle.msixbundle'
state: present
register: msixbundle_install_check
check_mode: yes
- name: get result of install msixbundle (check mode)
win_shell: if (Get-AppxPackage -Name '{{ msixbundle_id }}') { $true } else { $false }
changed_when: False
register: msixbundle_install_actual_check
- name: assert install msixbundle (check mode)
assert:
that:
- msixbundle_install_check is changed
- msixbundle_install_check.rc == 0
- not msixbundle_install_check.reboot_required
- not msixbundle_install_actual_check.stdout | trim | bool
- name: install msixbundle
win_package:
path: '{{ test_path }}\WinPackageBundle.msixbundle'
state: present
register: msixbundle_install
- name: get result of install msixbundle
win_shell: if (Get-AppxPackage -Name '{{ msixbundle_id }}') { $true } else { $false }
changed_when: False
register: msixbundle_install_actual
- name: assert install msixbundle
assert:
that:
- msixbundle_install is changed
- msixbundle_install.rc == 0
- not msixbundle_install.reboot_required
- msixbundle_install_actual.stdout | trim | bool
- name: install msixbundle (idempotence)
win_package:
path: '{{ test_path }}\WinPackageBundle.msixbundle'
state: present
register: msixbundle_install_again
- name: assert install msixbundle (idempotence)
assert:
that:
- not msixbundle_install_again is changed
- name: uninstall msixbundle by id (check mode)
win_package:
product_id: '{{ msixbundle_id }}'
state: absent
register: msixbundle_uninstall_check
check_mode: yes
- name: get result of uninstall msixbundle by id (check mode)
win_shell: if (Get-AppxPackage -Name '{{ msixbundle_id }}') { $true } else { $false }
changed_when: False
register: msixbundle_uninstall_actual_check
- name: assert uninstall msixbundle by id (check mode)
assert:
that:
- msixbundle_uninstall_check is changed
- msixbundle_uninstall_check.rc == 0
- not msixbundle_uninstall_check.reboot_required
- msixbundle_uninstall_actual_check.stdout | trim | bool
- name: uninstall msixbundle by id
win_package:
product_id: '{{ msixbundle_id }}'
state: absent
register: msixbundle_uninstall
- name: get result of uninstall msixbundle by id
win_shell: if (Get-AppxPackage -Name '{{ msixbundle_id }}') { $true } else { $false }
changed_when: False
register: msixbundle_uninstall_actual
- name: assert uninstall msixbundle by id
assert:
that:
- msixbundle_uninstall is changed
- msixbundle_uninstall.rc == 0
- not msixbundle_uninstall.reboot_required
- not msixbundle_uninstall_actual.stdout | trim | bool
- name: uninstall msixbundle by id (idempotence)
win_package:
product_id: '{{ msixbundle_id }}'
state: absent
register: msixbundle_uninstall_again
- name: assert uninstall msixbundle by id (idempotence)
assert:
that:
- not msixbundle_uninstall_again is changed
# The logic for appxbundle is the same for msixbundle no need for check and idempotence tests
- name: install appxbundle
win_package:
path: '{{ test_path }}\WinPackageBundle.appxbundle'
state: present
register: appxbundle_install
- name: get result of install appxbundle
win_shell: if (Get-AppxPackage -Name '{{ appxbundle_id }}') { $true } else { $false }
changed_when: False
register: appxbundle_install_actual
- name: assert install appxbundle
assert:
that:
- appxbundle_install is changed
- appxbundle_install.rc == 0
- not appxbundle_install.reboot_required
- appxbundle_install_actual.stdout | trim | bool
- name: uninstall appxbundle by path (check mode)
win_package:
path: '{{ test_path }}\WinPackageBundle.appxbundle'
state: absent
register: msixbundle_uninstall_check
check_mode: yes
- name: get result of uninstall appxbundle by path (check mode)
win_shell: if (Get-AppxPackage -Name '{{ appxbundle_id }}') { $true } else { $false }
changed_when: False
register: msixbundle_uninstall_actual_check
- name: assert uninstall appxbundle by path (check mode)
assert:
that:
- msixbundle_uninstall_check is changed
- msixbundle_uninstall_check.rc == 0
- not msixbundle_uninstall_check.reboot_required
- msixbundle_uninstall_actual_check.stdout | trim | bool
- name: uninstall appxbundle by path
win_package:
path: '{{ test_path }}\WinPackageBundle.appxbundle'
state: absent
register: msixbundle_uninstall
- name: get result of uninstall appxbundle by path
win_shell: if (Get-AppxPackage -Name '{{ appxbundle_id }}') { $true } else { $false }
changed_when: False
register: msixbundle_uninstall_actual
- name: assert uninstall appxbundle by path
assert:
that:
- msixbundle_uninstall is changed
- msixbundle_uninstall.rc == 0
- not msixbundle_uninstall.reboot_required
- not msixbundle_uninstall_actual.stdout | trim | bool
- name: uninstall appxbundle by path (idempotence)
win_package:
path: '{{ test_path }}\WinPackageBundle.appxbundle'
state: absent
register: msixbundle_uninstall_again
- name: assert uninstall appxbundle by path (idempotence)
assert:
that:
- not msixbundle_uninstall_again is changed

@ -0,0 +1,167 @@
---
- name: fail if base product is not applied
win_package:
path: '{{ test_path }}\patch.msp'
state: present
register: fail_no_product_found
failed_when: '"The specified patch does not apply to any installed MSI packages" not in fail_no_product_found.msg'
- name: install base package for msp patch
win_package:
path: '{{ test_path }}\patch.msi'
state: present
- name: install msp (check mode)
win_package:
path: '{{ test_path }}\patch.msp'
state: present
register: msp_install_check
check_mode: yes
- name: get result of install msp (check mode)
slurp:
path: '{{ patch_install_file }}'
register: msp_install_actual_check
- name: assert install msp (check mode)
assert:
that:
- msp_install_check is changed
- msp_install_check.rc == 0
- not msp_install_check.reboot_required
- msp_install_actual_check.content | b64decode == 'This is version 1.0'
- name: install msp
win_package:
path: '{{ test_path }}\patch.msp'
state: present
register: msp_install
- name: get result of install msp
slurp:
path: '{{ patch_install_file }}'
register: msp_install_actual
- name: assert install msp
assert:
that:
- msp_install is changed
- msp_install.rc == 0
- not msp_install.reboot_required
- msp_install_actual.content | b64decode == 'This is version 1.1'
- name: install msp (idempotence)
win_package:
path: '{{ test_path }}\patch.msp'
state: present
register: msp_install_again
- name: assert install msp (idempotence)
assert:
that:
- not msp_install_again is changed
- name: remove msp by path (check mode)
win_package:
path: '{{ test_path }}\patch.msp'
state: absent
register: msp_uninstall_path_check
check_mode: yes
- name: get result of remove msp by path (check mode)
slurp:
path: '{{ patch_install_file }}'
register: msp_uninstall_path_actual_check
- name: assert remove msp by path (check mode)
assert:
that:
- msp_uninstall_path_check is changed
- msp_uninstall_path_check.rc == 0
- not msp_uninstall_path_check.reboot_required
- msp_uninstall_path_actual_check.content | b64decode == 'This is version 1.1'
- name: remove msp by path
win_package:
path: '{{ test_path }}\patch.msp'
state: absent
register: msp_uninstall_path
- name: get result of remove msp by path
slurp:
path: '{{ patch_install_file }}'
register: msp_uninstall_path_actual
- name: assert remove msp by path
assert:
that:
- msp_uninstall_path is changed
- msp_uninstall_path.rc == 0
- not msp_uninstall_path.reboot_required
- msp_uninstall_path_actual.content | b64decode == 'This is version 1.0'
- name: remove msp by path (idempotence)
win_package:
path: '{{ test_path }}\patch.msp'
state: absent
register: msp_uninstall_path_again
- name: assert remove msp by path (idempotence)
assert:
that:
- not msp_uninstall_path_again is changed
- name: install patch again
win_package:
path: '{{ test_path }}\patch.msp'
state: present
- name: remove msp by id (check mode)
win_package:
product_id: '{{ patch_patch_id }}'
state: absent
register: msp_uninstall_id_check
check_mode: yes
- name: get result of remove msp by id (check mode)
slurp:
path: '{{ patch_install_file }}'
register: msp_uninstall_id_actual_check
- name: assert remove msp by id (check mode)
assert:
that:
- msp_uninstall_id_check is changed
- msp_uninstall_id_check.rc == 0
- not msp_uninstall_id_check.reboot_required
- msp_uninstall_id_actual_check.content | b64decode == 'This is version 1.1'
- name: remove msp by id
win_package:
product_id: '{{ patch_patch_id }}'
state: absent
register: msp_uninstall_id
- name: get result of remove msp by id
slurp:
path: '{{ patch_install_file }}'
register: msp_uninstall_id_actual
- name: assert remove msp by id
assert:
that:
- msp_uninstall_id is changed
- msp_uninstall_id.rc == 0
- not msp_uninstall_id.reboot_required
- msp_uninstall_id_actual.content | b64decode == 'This is version 1.0'
- name: remove msp by id (idempotence)
win_package:
product_id: '{{ patch_patch_id }}'
state: absent
register: msp_uninstall_id_again
- name: assert remove msp by id (idempotence)
assert:
that:
- not msp_uninstall_id_again is changed

@ -1,427 +0,0 @@
---
- name: install network msi (check mode)
win_package:
path: '{{test_win_package_network_path}}\good.msi'
product_id: '{{test_win_package_good_id}}'
state: present
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_msi_check
check_mode: yes
- name: get result of install network msi (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
register: install_network_msi_actual_check
- name: assert install network msi (check mode)
assert:
that:
- install_network_msi_check is changed
- install_network_msi_check.reboot_required == False
- install_network_msi_actual_check.exists == False
- name: install network msi
win_package:
path: '{{test_win_package_network_path}}\good.msi'
product_id: '{{test_win_package_good_id}}'
state: present
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_msi
- name: get result of install network msi
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
register: install_network_msi_actual
- name: assert install network msi
assert:
that:
- install_network_msi is changed
- install_network_msi.reboot_required == False
- install_network_msi.rc == 0
- install_network_msi_actual.exists == True
- name: install network msi (idempotent)
win_package:
path: '{{test_win_package_network_path}}\good.msi'
product_id: '{{test_win_package_good_id}}'
state: present
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_msi_idempotent
- name: assert install network msi (idempotent)
assert:
that:
- install_network_msi_idempotent is not changed
- name: uninstall network msi with path (check mode)
win_package:
path: '{{test_win_package_network_path}}\good.msi'
product_id: '{{test_win_package_good_id}}'
state: absent
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: uninstall_path_network_msi_check
check_mode: yes
- name: get result of uninstall network msi with path (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
register: uninstall_path_network_msi_actual_check
- name: assert uninstall network msi with path (check mode)
assert:
that:
- uninstall_path_network_msi_check is changed
- uninstall_path_network_msi_check.reboot_required == False
- uninstall_path_network_msi_actual_check.exists == True
- name: uninstall network msi with path
win_package:
path: '{{test_win_package_network_path}}\good.msi'
product_id: '{{test_win_package_good_id}}'
state: absent
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: uninstall_path_network_msi
- name: get result of uninstall network msi with path
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_good_id}}
register: uninstall_path_network_msi_actual
- name: assert uninstall network msi with path
assert:
that:
- uninstall_path_network_msi is changed
- uninstall_path_network_msi.reboot_required == False
- uninstall_path_network_msi.rc == 0
- uninstall_path_network_msi_actual.exists == False
- name: uninstall network msi with path (idempotent)
win_package:
path: '{{test_win_package_network_path}}\good.msi'
product_id: '{{test_win_package_good_id}}'
state: absent
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: uninstall_path_network_msi_idempotent
- name: assert uninstall network msi with path (idempotent)
assert:
that:
- uninstall_path_network_msi_idempotent is not changed
- name: install network reboot msi (check mode)
win_package:
path: '{{test_win_package_network_path}}\reboot.msi'
product_id: '{{test_win_package_reboot_id}}'
state: present
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_reboot_msi_check
check_mode: yes
- name: get result of install network reboot msi (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_reboot_id}}
register: install_network_reboot_msi_actual_check
- name: assert install network reboot msi (check mode)
assert:
that:
- install_network_reboot_msi_check is changed
- install_network_reboot_msi_check.reboot_required == False
- install_network_reboot_msi_actual_check.exists == False
- name: install network reboot msi
win_package:
path: '{{test_win_package_network_path}}\reboot.msi'
product_id: '{{test_win_package_reboot_id}}'
state: present
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_reboot_msi
- name: get result of install network reboot msi
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_reboot_id}}
register: install_network_reboot_msi_actual
- name: assert install network reboot msi
assert:
that:
- install_network_reboot_msi is changed
- install_network_reboot_msi.reboot_required == True
- install_network_reboot_msi.rc == 3010
- install_network_reboot_msi_actual.exists == True
- name: install network reboot msi (idempotent)
win_package:
path: '{{test_win_package_network_path}}\reboot.msi'
product_id: '{{test_win_package_reboot_id}}'
state: present
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_reboot_msi_idempotent
- name: assert install network reboot msi (idempotent)
assert:
that:
- install_network_reboot_msi_idempotent is not changed
- name: uninstall network msi with product_id (check mode)
win_package:
product_id: '{{test_win_package_reboot_id}}'
state: absent
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: uninstall_id_network_msi_check
check_mode: yes
- name: get result of uninstall network msi with product_id (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_reboot_id}}
register: uninstall_id_network_msi_actual_check
- name: assert uninstall network msi with product_id (check mode)
assert:
that:
- uninstall_id_network_msi_check is changed
- uninstall_id_network_msi_check.reboot_required == False
- uninstall_id_network_msi_actual_check.exists == True
- name: uninstall network msi with product_id
win_package:
product_id: '{{test_win_package_reboot_id}}'
state: absent
register: uninstall_id_network_msi
- name: get result of uninstall network msi with product_id
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_reboot_id}}
register: uninstall_id_network_msi_actual
- name: assert uninstall network msi with product_id
assert:
that:
- uninstall_id_network_msi is changed
- uninstall_id_network_msi.reboot_required == True
- uninstall_id_network_msi.rc == 3010
- uninstall_id_network_msi_actual.exists == False
- name: uninstall network msi with product_id (idempotent)
win_package:
product_id: '{{test_win_package_reboot_id}}'
state: absent
register: uninstall_id_network_msi_idempotent
- name: assert uninstall network msi with product_id (idempotent)
assert:
that:
- uninstall_id_network_msi_idempotent is not changed
- name: ensure the install folder is cleaned in case uninstall didn't work
win_file:
path: '%ProgramFiles(x86)%\Bovine University'
state: absent
- name: install network msi with arguments (check mode)
win_package:
path: '{{test_win_package_network_path}}\good.msi'
product_id: '{{test_win_package_good_id}}'
state: present
arguments: ADDLOCAL=Cow
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_msi_argument_check
check_mode: yes
- name: get result of moo file after install network msi with arguments (check mode)
win_stat:
path: '%ProgramFiles(x86)%\Bovine University\moo.exe'
register: install_network_msi_argument_moo_check
- name: get result of cow file after install network msi with arguments (check mode)
win_stat:
path: '%ProgramFiles(x86)%\Bovine University\cow.exe'
register: install_network_msi_argument_cow_check
- name: assert install network msi with arguments (check mode)
assert:
that:
- install_network_msi_argument_check is changed
- install_network_msi_argument_check.reboot_required == False
- install_network_msi_argument_moo_check.stat.exists == False
- install_network_msi_argument_cow_check.stat.exists == False
- name: install network msi with arguments
win_package:
path: '{{test_win_package_network_path}}\good.msi'
product_id: '{{test_win_package_good_id}}'
state: present
arguments: ADDLOCAL=Cow
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_msi_argument
- name: get result of moo file after install network msi with arguments
win_stat:
path: '%ProgramFiles(x86)%\Bovine University\moo.exe'
register: install_network_msi_argument_moo
- name: get result of cow file after install network msi with arguments
win_stat:
path: '%ProgramFiles(x86)%\Bovine University\cow.exe'
register: install_network_msi_argument_cow
- name: assert install network msi with arguments
assert:
that:
- install_network_msi_argument is changed
- install_network_msi_argument.reboot_required == False
- install_network_msi_argument.rc == 0
- install_network_msi_argument_moo.stat.exists == False
- install_network_msi_argument_cow.stat.exists == True
- name: install network msi with arguments (idempotent)
win_package:
path: '{{test_win_package_network_path}}\good.msi'
product_id: '{{test_win_package_good_id}}'
state: present
arguments: ADDLOCAL=Cow
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_msi_argument_again
- name: assert install network msi with arguments (idempotent)
assert:
that:
- install_network_msi_argument_again is not changed
- name: uninstall msi after test
win_package:
product_id: '{{test_win_package_good_id}}'
state: absent
- name: install network exe (check mode)
win_package:
path: '{{test_win_package_network_path}}\7z.exe'
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: present
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_exe_check
check_mode: yes
- name: get result of install network exe (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: install_network_exe_actual_check
- name: assert install network exe (check mode)
assert:
that:
- install_network_exe_check is changed
- install_network_exe_check.reboot_required == False
- install_network_exe_actual_check.exists == False
- name: install network exe
win_package:
path: '{{test_win_package_network_path}}\7z.exe'
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: present
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_exe
- name: get result of install network exe
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: install_network_exe_actual
- name: assert install network exe
assert:
that:
- install_network_exe is changed
- install_network_exe.reboot_required == False
- install_network_exe.rc == 0
- install_network_exe_actual.exists == True
- name: install network exe (idempotent)
win_package:
path: '{{test_win_package_network_path}}\7z.exe'
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: present
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: install_network_exe_idempotent
- name: assert install network exe (idempotent)
assert:
that:
- install_network_exe_idempotent is not changed
- name: uninstall network exe (check mode)
win_package:
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: absent
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: uninstall_network_exe_check
check_mode: yes
- name: get result of uninstall network exe (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: uninstall_network_exe_actual_check
- name: assert uninstall network exe (check mode)
assert:
that:
- uninstall_network_exe_check is changed
- uninstall_network_exe_check.reboot_required == False
- uninstall_network_exe_actual_check.exists == True
- name: uninstall network exe
win_package:
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: absent
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: uninstall_network_exe
- name: get result of uninstall network exe
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{test_win_package_exe_id}}
register: uninstall_network_exe_actual
- name: assert uninstall network exe
assert:
that:
- uninstall_network_exe is changed
- uninstall_network_exe.reboot_required == False
- uninstall_network_exe.rc == 0
- uninstall_network_exe_actual.exists == False
- name: uninstall network exe (idempotent)
win_package:
product_id: '{{test_win_package_exe_id}}'
arguments: /S
state: absent
user_name: '{{test_win_package_network_username|default(omit)}}'
user_password: '{{test_win_package_network_password|default(omit)}}'
register: uninstall_network_exe_idempotent
- name: assert uninstall network exe (idempotent)
assert:
that:
- uninstall_network_exe_idempotent is not changed

@ -0,0 +1,393 @@
---
# This symlink allows us to test paths with a space in the executable path
- name: create symbolic link in test folder to powershell
win_command: cmd.exe /c mklink "{{ test_path }}\powershell symlink.exe" C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
- name: copy across test script that creates a registry install entry
win_template:
src: registry_package.ps1.j2
dest: '{{ test_path }}\registry_package.ps1'
- name: install registry package not quoted and no spaces in path
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments:
- -File
- '{{ test_path }}\registry_package.ps1'
- HKLMx64
- UninstallString
- C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Command Remove-Item -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }} -Force -Recurse
state: present
- name: uninstall registry package not quoted and no spaces in path (check mode)
win_package:
product_id: '{{ registry_id }}'
state: absent
register: registry_uninstall_normal_check
check_mode: yes
- name: get result of uninstall registry package not quoted and no spaces in path (check mode)
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }}
register: registry_uninstall_normal_actual_check
- name: assert uninstall registry package not quoted and no spaces in path (check mode)
assert:
that:
- registry_uninstall_normal_check is changed
- registry_uninstall_normal_check.rc == 0
- not registry_uninstall_normal_check.reboot_required
- registry_uninstall_normal_actual_check.exists
- name: uninstall registry package not quoted and no spaces in path
win_package:
product_id: '{{ registry_id }}'
state: absent
register: registry_uninstall_normal
- name: get result of uninstall registry package not quoted and no spaces in path
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }}
register: registry_uninstall_normal_actual
- name: assert uninstall registry package not quoted and no spaces in path
assert:
that:
- registry_uninstall_normal is changed
- registry_uninstall_normal.rc == 0
- not registry_uninstall_normal.reboot_required
- not registry_uninstall_normal_actual.exists
- name: uninstall registry package not quoted and no spaces in path (idempotence)
win_package:
product_id: '{{ registry_id }}'
state: absent
register: registry_uninstall_normal_again
- name: assert uninstall registry package not quoted and no spaces in path (idempotence)
assert:
that:
- not registry_uninstall_normal_again is changed
- name: install registry package not quoted and spaces in path
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments:
- -File
- '{{ test_path }}\registry_package.ps1'
- HKLMx86
- QuietUninstallString
- '{{ test_path }}\powershell symlink.exe -Command Remove-Item -Path HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }} -Force -Recurse'
state: present
- name: uninstall registry package not quoted and spaces in path
win_package:
product_id: '{{ registry_id }}'
state: absent
register: registry_uninstall_not_quoted
- name: get result of uninstall registry package not quoted and spaces in path
win_reg_stat:
path: HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }}
register: registry_uninstall_not_quoted_actual
- name: assert uninstall registry package not quoted and spaces in path
assert:
that:
- registry_uninstall_not_quoted is changed
- registry_uninstall_not_quoted.rc == 0
- not registry_uninstall_not_quoted.reboot_required
- not registry_uninstall_not_quoted_actual.exists
- name: install registry package quoted and no spaces in path
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments:
- -File
- '{{ test_path }}\registry_package.ps1'
- HKCUx64
- UninstallString
- '"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -Command Remove-Item -Path HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }} -Force -Recurse'
state: present
- name: uninstall registry package quoted and no spaces in path
win_package:
product_id: '{{ registry_id }}'
state: absent
register: registry_uninstall_quoted_normal
- name: get result of uninstall registry package quoted and no spaces in path
win_reg_stat:
path: HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Unisntall\{{ registry_id }}
register: registry_uninstall_quoted_normal_actual
- name: assert uninstall registry package quoted and no spaces in path
assert:
that:
- registry_uninstall_quoted_normal is changed
- registry_uninstall_quoted_normal.rc == 0
- not registry_uninstall_quoted_normal.reboot_required
- not registry_uninstall_quoted_normal_actual.exists
- name: install registry package quoted and spaces in path
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments:
- -File
- '{{ test_path }}\registry_package.ps1'
- HKCUx86
- QuietUninstallString
- '"{{ test_path }}\powershell symlink.exe" -Command Remove-Item -Path HKCU:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }} -Force -Recurse'
state: present
- name: uninstall registry package quoted and spaces in path
win_package:
product_id: '{{ registry_id }}'
state: absent
register: registry_uninstall_quoted_spaces
- name: get result of uninstall registry package quoted and spaces in path
win_reg_stat:
path: HKCU:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }}
register: registry_uninstall_quoted_spaces_actual
- name: assert uninstall registry package quoted and spaces in path
assert:
that:
- registry_uninstall_quoted_spaces is changed
- registry_uninstall_quoted_spaces.rc == 0
- not registry_uninstall_quoted_spaces.reboot_required
- not registry_uninstall_quoted_spaces_actual.exists
- name: install registry package with unquoted env vars
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments:
- -File
- '{{ test_path }}\registry_package.ps1'
- HKLMx64
- UninstallString
- '%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe -Command Remove-Item -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }} -Force -Recurse'
state: present
- name: uninstall registry package with unquoted env vars
win_package:
product_id: '{{ registry_id }}'
state: absent
register: registry_uninstall_env
- name: get result of ininstall registry package with unquoted env vars
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }}
register: registry_uninstall_env_actual
- name: assert uninstall registry package with unquoted env vars
assert:
that:
- registry_uninstall_env is changed
- registry_uninstall_env.rc == 0
- not registry_uninstall_env.reboot_required
- not registry_uninstall_env_actual.exists
- name: install registry package quoted env vars
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments:
- -File
- '{{ test_path }}\registry_package.ps1'
- HKLMx64
- UninstallString
- '"%TestVar%\powershell symlink.exe" -Command Remove-Item -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }} -Force -Recurse'
state: present
- name: uninstall registry package quoted env vars
win_package:
product_id: '{{ registry_id }}'
state: absent
register: registry_uninstall_env_quoted
environment:
TestVar: '{{ test_path }}'
- name: get result of uninstall registry package quoted env vars
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }}
register: registry_uninstall_env_quoted_actual
- name: assert uninstall registry package quoted env vars
assert:
that:
- registry_uninstall_env_quoted is changed
- registry_uninstall_env_quoted.rc == 0
- not registry_uninstall_env_quoted.reboot_required
- not registry_uninstall_env_quoted_actual.exists
- name: install registry package for overriding path test
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments:
- -File
- '{{ test_path }}\registry_package.ps1'
- HKLMx64
- UninstallString
- Fail path
state: present
- name: uninstall registry package with overridden path and explicit error code
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
product_id: '{{ registry_id }}'
arguments: '-Command Remove-Item -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }}; exit 1'
expected_return_code: 1
state: absent
register: registry_uninstall_explicit_path
- name: get result of uninstall registry package with overridden path and explicit error code
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }}
register: registry_uninstall_explicit_path_actual
- name: assert unisntall registry package with overridden path and explicit error code
assert:
that:
- registry_uninstall_explicit_path is changed
- registry_uninstall_explicit_path.rc == 1
- not registry_uninstall_explicit_path.reboot_required
- not registry_uninstall_explicit_path_actual.exists
- name: create registry package for uninstal with explicit arguments test
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments:
- -File
- '{{ test_path }}\registry_package.ps1'
- HKLMx64
- QuietUninstallString
- C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
state: present
- name: uninstall registry package with explicit arguments and chdir
win_package:
product_id: '{{ registry_id }}'
arguments: -Command Remove-Item -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }} -Force -Recurse; [System.IO.File]::WriteAllText('{{ test_path }}\reg_out.txt', $pwd.Path, [System.Text.Encoding]::Unicode); exit 3010
state: absent
chdir: C:\Users
register: registry_uninstall_arguments
- name: get package result of uninstall registry package with explicit arguments and chdir
win_reg_stat:
path: HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }}
register: registry_uninstall_arguments_reg_actual
- name: get package output of uninstall registry package with explicit arguments and chdir
slurp:
path: '{{ test_path }}\reg_out.txt'
register: registry_uninstall_arguments_chdir_actual
- name: assert uninstall registry package with explicit arguments and chdir
assert:
that:
- registry_uninstall_arguments is changed
- registry_uninstall_arguments.rc == 3010
- registry_uninstall_arguments.reboot_required
- not registry_uninstall_arguments_reg_actual.exists
# backslash escaping makes it hard to compare the value, just compare the raw base64 string expected which is 'C:\Users' as UTF-16-LE
- registry_uninstall_arguments_chdir_actual.content == '//5DADoAXABVAHMAZQByAHMA'
- name: install package for creates_* tests
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments:
- -File
- '{{ test_path }}\registry_package.ps1'
- HKLMx64
- UninstallString
- powershell.exe -Command Remove-Item -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{{ registry_id }} -Force -Recurse
state: present
- name: get actual PowerShell file version for tests
win_shell: |
$path = '{{ test_path }}\powershell symlink.exe'
$version = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($path)
(New-Object -TypeName System.Version -ArgumentList @(
$version.FileMajorPart, $version.FileMinorPart, $version.FileBuildPart, $version.FilePrivatePart
)).ToString()
changed_when: False
register: powershell_version
- name: test creates_path overrides product_id
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments: echo hi
product_id: '{{ registry_id }}'
creates_path: C:\missing
state: present
register: creates_path
- name: test creates_version overrides product_id
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments: echo hi
product_id: '{{ registry_id }}'
creates_path: '{{ test_path }}\powershell symlink.exe'
creates_version: 1.0.0
state: present
register: creates_version
- name: assert test creates_path and creates_version override product_id is changed
assert:
that:
- creates_path is changed
- creates_version is changed
- name: test creates_path to existing but no product_id
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments: echo hi
product_id: Fake
creates_path: C:\Windows
state: present
register: creates_path_present
- name: test creates_version to existing but no product_id
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments: echo hi
product_id: Fake
creates_path: '{{ test_path }}\powershell symlink.exe'
creates_version: '{{ powershell_version.stdout | trim }}'
state: present
register: creates_version_present
- name: assert test creates_path to existing but no product_Id
assert:
that:
- not creates_path_present is changed
- not creates_version_present is changed
- name: test creates_service overrides product_id
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments: echo hi
product_id: '{{ registry_id }}'
creates_service: missing service
state: present
register: creates_service
- name: assert test creates_service overrides product_id
assert:
that:
- creates_service is changed
- name: test creates_service to existing but no product_id
win_package:
path: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
arguments: echo hi
product_id: Fake
creates_service: winrm
state: present
register: creates_service_present
- name: assert test creates_service to existing but no product_Id
assert:
that:
- not creates_service_present is changed

@ -0,0 +1,26 @@
$ErrorActionPreference = 'Stop'
$productId = '{{ registry_id }}'
$regPath = switch ($args[0]) {
HKLMx64 { 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall' }
HKLMx86 { 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall' }
HKCUx64 { 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall' }
HKCUx86 { 'HKCU:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall' }
default { throw "Invalid registry path specified $($args[0])" }
}
$regProperty = $args[1]
$regUninstallString = $args[2]
#$regUninstallString = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($args[2]))
$null = New-Item -Path $regPath -Name $productId -Force
$propParams = @{
Path = "$regPath\$productId"
Force = $true
PropertyType = 'String'
}
New-ItemProperty -Name $regProperty -Value $regUninstallString @propParams
if ($regProperty -eq 'QuietUninstallString') {
New-ItemProperty -Name 'UninstallString' -Value 'Fail if used' @propParams
}

@ -7562,9 +7562,6 @@ lib/ansible/modules/windows/win_lineinfile.ps1 pslint:PSCustomUseLiteralPath
lib/ansible/modules/windows/win_mapped_drive.ps1 pslint:PSCustomUseLiteralPath
lib/ansible/modules/windows/win_netbios.ps1 validate-modules:parameter-list-no-elements
lib/ansible/modules/windows/win_optional_feature.ps1 validate-modules:parameter-list-no-elements
lib/ansible/modules/windows/win_package.ps1 pslint:PSCustomUseLiteralPath
lib/ansible/modules/windows/win_package.ps1 pslint:PSUseApprovedVerbs
lib/ansible/modules/windows/win_package.ps1 validate-modules:doc-elements-mismatch
lib/ansible/modules/windows/win_pagefile.ps1 pslint:PSCustomUseLiteralPath
lib/ansible/modules/windows/win_pagefile.ps1 pslint:PSUseDeclaredVarsMoreThanAssignments # New PR - bug test_path should be testPath
lib/ansible/modules/windows/win_pagefile.ps1 pslint:PSUseSupportsShouldProcess

Loading…
Cancel
Save