@ -4,34 +4,58 @@
# Copyright: (c) 2017, Ansible Project
# Copyright: (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Requires -Module Ansible.ModuleUtils.Legacy
#AnsibleRequires -CSharpUtil Ansible.Basic
# Requires -Module Ansible.ModuleUtils. CommandUtil
# Requires -Module Ansible.ModuleUtils. AddType
# Requires -Module Ansible.ModuleUtils.ArgvParser
# Requires -Module Ansible.ModuleUtils.ArgvParser
# Requires -Module Ansible.ModuleUtils.CommandUtil
$ErrorActionPreference = 'Stop'
$spec = @ {
options = @ {
$params = Parse-Args -arguments $args -supports_check_mode $true
arguments = @ { type = " raw " }
$check_mode = Get-AnsibleParam -obj $params -name " _ansible_check_mode " -type " bool " -default $false
expected_return_code = @ { type = " list " ; elements = " int " ; default = @ ( 0 , 3010 ) }
path = @ { type = " str " }
$arguments = Get-AnsibleParam -obj $params -name " arguments "
chdir = @ { type = " path " }
$expected_return_code = Get-AnsibleParam -obj $params -name " expected_return_code " -type " list " -default @ ( 0 , 3010 )
product_id = @ { type = " str " ; aliases = @ ( , " productid " ) }
$path = Get-AnsibleParam -obj $params -name " path " -type " str "
state = @ { type = " str " ; default = " present " ; choices = " absent " , " present " ; aliases = @ ( , " ensure " ) }
$chdir = Get-AnsibleParam -obj $params -name " chdir " -type " path "
username = @ { type = " str " ; aliases = @ ( , " user_name " ) }
$product_id = Get-AnsibleParam -obj $params -name " product_id " -type " str " -aliases " productid "
password = @ { type = " str " ; no_log = $true ; aliases = @ ( , " user_password " ) }
$state = Get-AnsibleParam -obj $params -name " state " -type " str " -default " present " -validateset " absent " , " present " -aliases " ensure "
validate_certs = @ { type = " bool " ; default = $true }
$username = Get-AnsibleParam -obj $params -name " username " -type " str " -aliases " user_name "
creates_path = @ { type = " path " }
$password = Get-AnsibleParam -obj $params -name " password " -type " str " -failifempty ( $null -ne $username ) -aliases " user_password "
creates_version = @ { type = " str " }
$validate_certs = Get-AnsibleParam -obj $params -name " validate_certs " -type " bool " -default $true
creates_service = @ { type = " str " }
$creates_path = Get-AnsibleParam -obj $params -name " creates_path " -type " path "
log_path = @ { type = " path " }
$creates_version = Get-AnsibleParam -obj $params -name " creates_version " -type " str "
}
$creates_service = Get-AnsibleParam -obj $params -name " creates_service " -type " str "
required_by = @ {
$log_path = Get-AnsibleParam -obj $params -name " log_path " -type " path "
creates_version = " creates_path "
}
$result = @ {
required_if = @ (
changed = $false
@ ( " state " , " present " , @ ( " path " ) ) ,
reboot_required = $false
@ ( " state " , " absent " , @ ( " path " , " product_id " ) , $true )
)
required_together = @ ( , @ ( " username " , " password " ) )
supports_check_mode = $true
}
}
$module = [ Ansible.Basic.AnsibleModule ] :: Create ( $args , $spec )
$check_mode = $module . CheckMode
$arguments = $module . Params . arguments
$expected_return_code = $module . Params . expected_return_code
$path = $module . Params . path
$chdir = $module . Params . chdir
$product_id = $module . Params . product_id
$state = $module . Params . state
$username = $module . Params . username
$password = $module . Params . password
$validate_certs = $module . Params . validate_certs
$creates_path = $module . Params . creates_path
$creates_version = $module . Params . creates_version
$creates_service = $module . Params . creates_service
$log_path = $module . Params . log_path
$module . Result . reboot_required = $false
if ( $null -ne $arguments ) {
if ( $null -ne $arguments ) {
# convert a list to a string and escape the values
# convert a list to a string and escape the values
if ( $arguments -is [ array ] ) {
if ( $arguments -is [ array ] ) {
@ -59,26 +83,6 @@ if ($null -ne $username) {
$credential = New-Object -TypeName PSCredential -ArgumentList $username , $sec_user_password
$credential = New-Object -TypeName PSCredential -ArgumentList $username , $sec_user_password
}
}
$valid_return_codes = @ ( )
foreach ( $rc in ( $expected_return_code ) ) {
try {
$int_rc = [ Int32 ] :: Parse ( $rc )
$valid_return_codes + = $int_rc
} catch {
Fail-Json -obj $result -message " failed to parse expected return code $rc as an integer "
}
}
if ( $null -eq $path ) {
if ( -not ( $state -eq " absent " -and $null -ne $product_id ) ) {
Fail-Json -obj $result -message " path can only be null when state=absent and product_id is not null "
}
}
if ( $null -ne $creates_version -and $null -eq $creates_path ) {
Fail-Json -obj $result -Message " creates_path must be set when creates_version is set "
}
$msi_tools = @ "
$msi_tools = @ "
using System ;
using System ;
using System . Runtime . InteropServices ;
using System . Runtime . InteropServices ;
@ -115,7 +119,7 @@ namespace Ansible {
}
}
" @
" @
Add- Type -TypeDefinition @ "
Add- CSharpType -AnsibleModule $module -References @ "
public enum LocationType {
public enum LocationType {
Empty ,
Empty ,
Local ,
Local ,
@ -129,7 +133,7 @@ Function Download-File($url, $path) {
try {
try {
$web_client . DownloadFile ( $url , $path )
$web_client . DownloadFile ( $url , $path )
} catch {
} catch {
Fail-Json -obj $result -message " failed to download $url to $( $path ) : $( $_ . Exception . Message ) "
$module . FailJson ( " failed to download $url to $( $path ) : $( $_ . Exception . Message ) " , $_ )
}
}
}
}
@ -185,7 +189,7 @@ Function Get-ProgramMetadata($state, $path, $product_id, [PSCredential]$credenti
try {
try {
New-PSDrive -Name win_package -PSProvider FileSystem -Root $file_path -Credential $credential -Scope Script
New-PSDrive -Name win_package -PSProvider FileSystem -Root $file_path -Credential $credential -Scope Script
} catch {
} catch {
Fail-Json -obj $result -message " failed to connect network drive with credentials: $( $_ . Exception . Message ) "
$module . FailJson ( " failed to connect network drive with credentials: $( $_ . Exception . Message ) " , $_ )
}
}
$test_path = " win_package:\ $file_name "
$test_path = " win_package:\ $file_name "
} else {
} else {
@ -215,15 +219,15 @@ Function Get-ProgramMetadata($state, $path, $product_id, [PSCredential]$credenti
} else {
} else {
# we can get the product_id if the path is an msi and is either a local file or unc file with credential delegation
# we can get the product_id if the path is an msi and is either a local file or unc file with credential delegation
if ( ( $metadata . msi -eq $true ) -and ( ( $metadata . location_type -eq [ LocationType ] :: Local ) -or ( $metadata . location_type -eq [ LocationType ] :: Unc -and $null -eq $credential ) ) ) {
if ( ( $metadata . msi -eq $true ) -and ( ( $metadata . location_type -eq [ LocationType ] :: Local ) -or ( $metadata . location_type -eq [ LocationType ] :: Unc -and $null -eq $credential ) ) ) {
Add- Type -TypeDefinition $msi_tools
Add- CSharpType -AnsibleModule $module -References $msi_tools
try {
try {
$metadata . product_id = [ Ansible.MsiTools ] :: GetPackageProperty ( $path , " ProductCode " )
$metadata . product_id = [ Ansible.MsiTools ] :: GetPackageProperty ( $path , " ProductCode " )
} catch {
} catch {
Fail-Json -obj $result -message " failed to get product_id from MSI at $( $path ) : $( $_ . Exception . Message ) "
$module . FailJson ( " failed to get product_id from MSI at $( $path ) : $( $_ . Exception . Message ) " , $_ )
}
}
} elseif ( $null -eq $creates_path -and $null -eq $creates_service ) {
} elseif ( $null -eq $creates_path -and $null -eq $creates_service ) {
# we need to fail without the product id at this point
# we need to fail without the product id at this point
Fail-Json $result " product_id is required when the path is not an MSI or the path is an MSI but not local "
$module . FailJson ( " product_id is required when the path is not an MSI or the path is an MSI but not local " )
}
}
}
}
@ -259,7 +263,7 @@ Function Get-ProgramMetadata($state, $path, $product_id, [PSCredential]$credenti
$version_matched = $creates_version -eq $existing_version
$version_matched = $creates_version -eq $existing_version
$metadata . installed = $version_matched
$metadata . installed = $version_matched
} else {
} else {
Fail-Json -obj $result -message " creates_path must be a file not a directory when creates_version is set "
$module . FailJson ( " creates_path must be a file not a directory when creates_version is set " )
}
}
}
}
}
}
@ -272,7 +276,7 @@ Function Get-ProgramMetadata($state, $path, $product_id, [PSCredential]$credenti
# finally throw error if path is not valid unless we want to uninstall the package and it already is
# finally throw error if path is not valid unless we want to uninstall the package and it already is
if ( $null -ne $metadata . path_error -and ( -not ( $state -eq " absent " -and $metadata . installed -eq $false ) ) ) {
if ( $null -ne $metadata . path_error -and ( -not ( $state -eq " absent " -and $metadata . installed -eq $false ) ) ) {
Fail-Json -obj $result -message $metadata . path_error
$module . FailJson ( $metadata . path_error )
}
}
return $metadata
return $metadata
@ -360,7 +364,7 @@ if ($state -eq "absent") {
try {
try {
$process_result = Run-Command @command_args
$process_result = Run-Command @command_args
} catch {
} catch {
Fail-Json -obj $result -message " failed to run uninstall process ( $( $command_args [ 'command' ] ) ): $( $_ . Exception . Message ) "
$module . FailJson ( " failed to run uninstall process ( $( $command_args [ 'command' ] ) ): $( $_ . Exception . Message ) " , $_ )
}
}
if ( ( $null -ne $log_path ) -and ( Test-Path -LiteralPath $log_path ) ) {
if ( ( $null -ne $log_path ) -and ( Test-Path -LiteralPath $log_path ) ) {
@ -369,20 +373,20 @@ if ($state -eq "absent") {
$log_content = $null
$log_content = $null
}
}
$ r esult. rc = $process_result . rc
$ module. R esult. rc = $process_result . rc
if ( $ valid_return_codes -notcontains $process_result . rc ) {
if ( $ expected_return_code -notcontains $process_result . rc ) {
$ r esult. stdout = Convert-Encoding -string $process_result . stdout
$ module. R esult. stdout = Convert-Encoding -string $process_result . stdout
$ r esult. stderr = Convert-Encoding -string $process_result . stderr
$ module. R esult. stderr = Convert-Encoding -string $process_result . stderr
if ( $null -ne $log_content ) {
if ( $null -ne $log_content ) {
$ r esult. log = $log_content
$ module. R esult. log = $log_content
}
}
Fail-Json -obj $result -message " unexpected rc from uninstall $uninstall_exe $( $uninstall_arguments ) : see rc, stdout and stderr for more details "
$module . FailJson ( " unexpected rc from uninstall $uninstall_exe $( $uninstall_arguments ) : see rc, stdout and stderr for more details " )
} else {
} else {
$ r esult. failed = $false
$ module. R esult. failed = $false
}
}
if ( $process_result . rc -eq 3010 ) {
if ( $process_result . rc -eq 3010 ) {
$ r esult. reboot_required = $true
$ module. R esult. reboot_required = $true
}
}
}
}
} finally {
} finally {
@ -394,7 +398,7 @@ if ($state -eq "absent") {
}
}
}
}
$ r esult. changed = $true
$ module. R esult. changed = $true
}
}
} else {
} else {
if ( $program_metadata . installed -eq $false ) {
if ( $program_metadata . installed -eq $false ) {
@ -448,7 +452,7 @@ if ($state -eq "absent") {
try {
try {
$process_result = Run-Command @command_args
$process_result = Run-Command @command_args
} catch {
} catch {
Fail-Json -obj $result -message " failed to run install process ( $( $command_args [ 'command' ] ) ): $( $_ . Exception . Message ) "
$module . FailJson ( " failed to run install process ( $( $command_args [ 'command' ] ) ): $( $_ . Exception . Message ) " , $_ )
}
}
if ( ( $null -ne $log_path ) -and ( Test-Path -LiteralPath $log_path ) ) {
if ( ( $null -ne $log_path ) -and ( Test-Path -LiteralPath $log_path ) ) {
@ -457,20 +461,20 @@ if ($state -eq "absent") {
$log_content = $null
$log_content = $null
}
}
$ r esult. rc = $process_result . rc
$ module. R esult. rc = $process_result . rc
if ( $ valid_return_codes -notcontains $process_result . rc ) {
if ( $ expected_return_code -notcontains $process_result . rc ) {
$ r esult. stdout = Convert-Encoding -string $process_result . stdout
$ module. R esult. stdout = Convert-Encoding -string $process_result . stdout
$ r esult. stderr = Convert-Encoding -string $process_result . stderr
$ module. R esult. stderr = Convert-Encoding -string $process_result . stderr
if ( $null -ne $log_content ) {
if ( $null -ne $log_content ) {
$ r esult. log = $log_content
$ module. R esult. log = $log_content
}
}
Fail-Json -obj $result -message " unexpected rc from install $install_exe $( $install_arguments ) : see rc, stdout and stderr for more details "
$module . FailJson ( " unexpected rc from install $install_exe $( $install_arguments ) : see rc, stdout and stderr for more details " )
} else {
} else {
$ r esult. failed = $false
$ module. R esult. failed = $false
}
}
if ( $process_result . rc -eq 3010 ) {
if ( $process_result . rc -eq 3010 ) {
$ r esult. reboot_required = $true
$ module. R esult. reboot_required = $true
}
}
}
}
} finally {
} finally {
@ -482,8 +486,9 @@ if ($state -eq "absent") {
}
}
}
}
$ r esult. changed = $true
$ module. R esult. changed = $true
}
}
}
}
Exit-Json -obj $result
$module . ExitJson ( )