ansible-test pssa update and new rules (#76256)

pull/76277/head
Jordan Borean 3 years ago committed by GitHub
parent 90de24da7b
commit 9985b8a975
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,17 @@
minor_changes:
- ansible-test pslint - Updated ``PowerShellScriptAnalyzer`` to 1.20.0
- >-
ansible-test pslint - Added the `AvoidLongLines <https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/AvoidLongLines.md>`_
rule set to a length of 160.
- >-
ansible-test pslint - Added the `PlaceOpenBrace <https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/PlaceOpenBrace.md>`_
rule set to enforce open braces on the same line and a subsequent newline.
- >-
ansible-test pslint - Added the `PlaceCloseBrace <https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/PlaceCloseBrace.md>`_
rule set to enforce close braces on a newline.
- >-
ansible-test pslint - Added the `UseConsistentIndentation <https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/UseConsistentIndentation.md>`_
rule to enforce indentation is done with 4 spaces.
- >-
ansible-test pslint - Added the `UseConsistentWhitespace <https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/UseConsistentWhitespace.md>`_
rule to enforce whitespace consistency in PowerShell.

@ -66,28 +66,24 @@ Param (
[switch]$EnableCredSSP
)
Function Write-Log
{
Function Write-ProgressLog {
$Message = $args[0]
Write-EventLog -LogName Application -Source $EventSource -EntryType Information -EventId 1 -Message $Message
}
Function Write-VerboseLog
{
Function Write-VerboseLog {
$Message = $args[0]
Write-Verbose $Message
Write-Log $Message
Write-ProgressLog $Message
}
Function Write-HostLog
{
Function Write-HostLog {
$Message = $args[0]
Write-Output $Message
Write-Log $Message
Write-ProgressLog $Message
}
Function New-LegacySelfSignedCert
{
Function New-LegacySelfSignedCert {
Param (
[string]$SubjectName,
[int]$ValidDays = 1095
@ -129,10 +125,9 @@ Function New-LegacySelfSignedCert
$AlternativeName += $hostFQDN
$IAlternativeNames = New-Object -ComObject X509Enrollment.CAlternativeNames
foreach ($AN in $AlternativeName)
{
foreach ($AN in $AlternativeName) {
$AltName = New-Object -ComObject X509Enrollment.CAlternativeName
$AltName.InitializeFromString(0x3,$AN)
$AltName.InitializeFromString(0x3, $AN)
$IAlternativeNames.Add($AltName)
}
@ -162,8 +157,7 @@ Function New-LegacySelfSignedCert
return $parsed_cert.Thumbprint
}
Function Enable-GlobalHttpFirewallAccess
{
Function Enable-GlobalHttpFirewallAccess {
Write-Verbose "Forcing global HTTP firewall access"
# this is a fairly naive implementation; could be more sophisticated about rule matching/collapsing
$fw = New-Object -ComObject HNetCfg.FWPolicy2
@ -217,80 +211,71 @@ Function Enable-GlobalHttpFirewallAccess
}
# Setup error handling.
Trap
{
Trap {
$_
Exit 1
}
$ErrorActionPreference = "Stop"
# Get the ID and security principal of the current user account
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal = new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
# Check to see if we are currently running "as Administrator"
if (-Not $myWindowsPrincipal.IsInRole($adminRole))
{
if (-Not $myWindowsPrincipal.IsInRole($adminRole)) {
Write-Output "ERROR: You need elevated Administrator privileges in order to run this script."
Write-Output " Start Windows PowerShell by using the Run as Administrator option."
Exit 2
}
$EventSource = $MyInvocation.MyCommand.Name
If (-Not $EventSource)
{
If (-Not $EventSource) {
$EventSource = "Powershell CLI"
}
If ([System.Diagnostics.EventLog]::Exists('Application') -eq $False -or [System.Diagnostics.EventLog]::SourceExists($EventSource) -eq $False)
{
If ([System.Diagnostics.EventLog]::Exists('Application') -eq $False -or [System.Diagnostics.EventLog]::SourceExists($EventSource) -eq $False) {
New-EventLog -LogName Application -Source $EventSource
}
# Detect PowerShell version.
If ($PSVersionTable.PSVersion.Major -lt 3)
{
Write-Log "PowerShell version 3 or higher is required."
If ($PSVersionTable.PSVersion.Major -lt 3) {
Write-ProgressLog "PowerShell version 3 or higher is required."
Throw "PowerShell version 3 or higher is required."
}
# Find and start the WinRM service.
Write-Verbose "Verifying WinRM service."
If (!(Get-Service "WinRM"))
{
Write-Log "Unable to find the WinRM service."
If (!(Get-Service "WinRM")) {
Write-ProgressLog "Unable to find the WinRM service."
Throw "Unable to find the WinRM service."
}
ElseIf ((Get-Service "WinRM").Status -ne "Running")
{
ElseIf ((Get-Service "WinRM").Status -ne "Running") {
Write-Verbose "Setting WinRM service to start automatically on boot."
Set-Service -Name "WinRM" -StartupType Automatic
Write-Log "Set WinRM service to start automatically on boot."
Write-ProgressLog "Set WinRM service to start automatically on boot."
Write-Verbose "Starting WinRM service."
Start-Service -Name "WinRM" -ErrorAction Stop
Write-Log "Started WinRM service."
Write-ProgressLog "Started WinRM service."
}
# WinRM should be running; check that we have a PS session config.
If (!(Get-PSSessionConfiguration -Verbose:$false) -or (!(Get-ChildItem WSMan:\localhost\Listener)))
{
If (!(Get-PSSessionConfiguration -Verbose:$false) -or (!(Get-ChildItem WSMan:\localhost\Listener))) {
If ($SkipNetworkProfileCheck) {
Write-Verbose "Enabling PS Remoting without checking Network profile."
Enable-PSRemoting -SkipNetworkProfileCheck -Force -ErrorAction Stop
Write-Log "Enabled PS Remoting without checking Network profile."
Write-ProgressLog "Enabled PS Remoting without checking Network profile."
}
Else {
Write-Verbose "Enabling PS Remoting."
Enable-PSRemoting -Force -ErrorAction Stop
Write-Log "Enabled PS Remoting."
Write-ProgressLog "Enabled PS Remoting."
}
}
Else
{
Else {
Write-Verbose "PS Remoting is already enabled."
}
@ -310,8 +295,7 @@ if ($token_value -ne 1) {
# Make sure there is a SSL listener.
$listeners = Get-ChildItem WSMan:\localhost\Listener
If (!($listeners | Where-Object {$_.Keys -like "TRANSPORT=HTTPS"}))
{
If (!($listeners | Where-Object { $_.Keys -like "TRANSPORT=HTTPS" })) {
# We cannot use New-SelfSignedCertificate on 2012R2 and earlier
$thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays
Write-HostLog "Self-signed SSL certificate generated; thumbprint: $thumbprint"
@ -329,15 +313,13 @@ If (!($listeners | Where-Object {$_.Keys -like "TRANSPORT=HTTPS"}))
Write-Verbose "Enabling SSL listener."
New-WSManInstance -ResourceURI 'winrm/config/Listener' -SelectorSet $selectorset -ValueSet $valueset
Write-Log "Enabled SSL listener."
Write-ProgressLog "Enabled SSL listener."
}
Else
{
Else {
Write-Verbose "SSL listener is already active."
# Force a new SSL cert on Listener if the $ForceNewSSLCert
If ($ForceNewSSLCert)
{
If ($ForceNewSSLCert) {
# We cannot use New-SelfSignedCertificate on 2012R2 and earlier
$thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays
@ -361,45 +343,37 @@ Else
}
# Check for basic authentication.
$basicAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object {$_.Name -eq "Basic"}
$basicAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object { $_.Name -eq "Basic" }
If ($DisableBasicAuth)
{
If (($basicAuthSetting.Value) -eq $true)
{
If ($DisableBasicAuth) {
If (($basicAuthSetting.Value) -eq $true) {
Write-Verbose "Disabling basic auth support."
Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $false
Write-Log "Disabled basic auth support."
Write-ProgressLog "Disabled basic auth support."
}
Else
{
Else {
Write-Verbose "Basic auth is already disabled."
}
}
Else
{
If (($basicAuthSetting.Value) -eq $false)
{
Else {
If (($basicAuthSetting.Value) -eq $false) {
Write-Verbose "Enabling basic auth support."
Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $true
Write-Log "Enabled basic auth support."
Write-ProgressLog "Enabled basic auth support."
}
Else
{
Else {
Write-Verbose "Basic auth is already enabled."
}
}
# If EnableCredSSP if set to true
If ($EnableCredSSP)
{
If ($EnableCredSSP) {
# Check for CredSSP authentication
$credsspAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object {$_.Name -eq "CredSSP"}
If (($credsspAuthSetting.Value) -eq $false)
{
$credsspAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object { $_.Name -eq "CredSSP" }
If (($credsspAuthSetting.Value) -eq $false) {
Write-Verbose "Enabling CredSSP auth support."
Enable-WSManCredSSP -role server -Force
Write-Log "Enabled CredSSP auth support."
Write-ProgressLog "Enabled CredSSP auth support."
}
}
@ -410,44 +384,37 @@ If ($GlobalHttpFirewallAccess) {
# Configure firewall to allow WinRM HTTPS connections.
$fwtest1 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS"
$fwtest2 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS" profile=any
If ($fwtest1.count -lt 5)
{
If ($fwtest1.count -lt 5) {
Write-Verbose "Adding firewall rule to allow WinRM HTTPS."
netsh advfirewall firewall add rule profile=any name="Allow WinRM HTTPS" dir=in localport=5986 protocol=TCP action=allow
Write-Log "Added firewall rule to allow WinRM HTTPS."
Write-ProgressLog "Added firewall rule to allow WinRM HTTPS."
}
ElseIf (($fwtest1.count -ge 5) -and ($fwtest2.count -lt 5))
{
ElseIf (($fwtest1.count -ge 5) -and ($fwtest2.count -lt 5)) {
Write-Verbose "Updating firewall rule to allow WinRM HTTPS for any profile."
netsh advfirewall firewall set rule name="Allow WinRM HTTPS" new profile=any
Write-Log "Updated firewall rule to allow WinRM HTTPS for any profile."
Write-ProgressLog "Updated firewall rule to allow WinRM HTTPS for any profile."
}
Else
{
Else {
Write-Verbose "Firewall rule already exists to allow WinRM HTTPS."
}
# Test a remoting connection to localhost, which should work.
$httpResult = Invoke-Command -ComputerName "localhost" -ScriptBlock {$env:COMPUTERNAME} -ErrorVariable httpError -ErrorAction SilentlyContinue
$httpResult = Invoke-Command -ComputerName "localhost" -ScriptBlock { $using:env:COMPUTERNAME } -ErrorVariable httpError -ErrorAction SilentlyContinue
$httpsOptions = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$httpsResult = New-PSSession -UseSSL -ComputerName "localhost" -SessionOption $httpsOptions -ErrorVariable httpsError -ErrorAction SilentlyContinue
If ($httpResult -and $httpsResult)
{
If ($httpResult -and $httpsResult) {
Write-Verbose "HTTP: Enabled | HTTPS: Enabled"
}
ElseIf ($httpsResult -and !$httpResult)
{
ElseIf ($httpsResult -and !$httpResult) {
Write-Verbose "HTTP: Disabled | HTTPS: Enabled"
}
ElseIf ($httpResult -and !$httpsResult)
{
ElseIf ($httpResult -and !$httpsResult) {
Write-Verbose "HTTP: Enabled | HTTPS: Disabled"
}
Else
{
Write-Log "Unable to establish an HTTP or HTTPS remoting session."
Else {
Write-ProgressLog "Unable to establish an HTTP or HTTPS remoting session."
Throw "Unable to establish an HTTP or HTTPS remoting session."
}
Write-VerboseLog "PS Remoting has been successfully configured for Ansible."

@ -18,24 +18,21 @@
# 6.3 is 2012 R2
if ($PSVersionTable.psversion.Major -ge 3)
{
if ($PSVersionTable.psversion.Major -ge 3) {
Write-Output "Powershell 3 Installed already; You don't need this"
Exit
}
$powershellpath = "C:\powershell"
function download-file
{
function download-file {
param ([string]$path, [string]$local)
$client = new-object system.net.WebClient
$client.Headers.Add("user-agent", "PowerShell")
$client.downloadfile($path, $local)
}
if (!(test-path $powershellpath))
{
if (!(test-path $powershellpath)) {
New-Item -ItemType directory -Path $powershellpath
}
@ -53,8 +50,7 @@ if (!(test-path $powershellpath))
#You may need to reboot after the .NET install if so just run the script again.
# If the Operating System is above 6.2, then you already have PowerShell Version > 3
if ([Environment]::OSVersion.Version.Major -gt 6)
{
if ([Environment]::OSVersion.Version.Major -gt 6) {
Write-Output "OS is new; upgrade not needed."
Exit
}
@ -64,25 +60,20 @@ $osminor = [environment]::OSVersion.Version.Minor
$architecture = $ENV:PROCESSOR_ARCHITECTURE
if ($architecture -eq "AMD64")
{
if ($architecture -eq "AMD64") {
$architecture = "x64"
}
else
{
else {
$architecture = "x86"
}
if ($osminor -eq 1)
{
if ($osminor -eq 1) {
$DownloadUrl = "http://download.microsoft.com/download/E/7/6/E76850B8-DA6E-4FF5-8CCE-A24FC513FD16/Windows6.1-KB2506143-" + $architecture + ".msu"
}
elseif ($osminor -eq 0)
{
elseif ($osminor -eq 0) {
$DownloadUrl = "http://download.microsoft.com/download/E/7/6/E76850B8-DA6E-4FF5-8CCE-A24FC513FD16/Windows6.0-KB2506146-" + $architecture + ".msu"
}
else
{
else {
# Nothing to do; In theory this point will never be reached.
Exit
}

@ -2,7 +2,7 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
param(
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Payload
[Parameter(Mandatory = $true)][System.Collections.IDictionary]$Payload
)
# help with debugging errors as we don't have visibility of this running process
@ -49,7 +49,12 @@ $ps.Runspace = $rs
Write-AnsibleLog "INFO - adding global functions to PowerShell pipeline script" "async_watchdog"
$ps.AddScript($script:common_functions).AddStatement() > $null
$ps.AddScript($script:wrapper_functions).AddStatement() > $null
$ps.AddCommand("Set-Variable").AddParameters(@{Name="common_functions"; Value=$script:common_functions; Scope="script"}).AddStatement() > $null
$function_params = @{
Name = "common_functions"
Value = $script:common_functions
Scope = "script"
}
$ps.AddCommand("Set-Variable").AddParameters($function_params).AddStatement() > $null
Write-AnsibleLog "INFO - adding $($actions[0]) to PowerShell pipeline script" "async_watchdog"
$ps.AddScript($entrypoint).AddArgument($payload) > $null
@ -79,7 +84,8 @@ if ($job_async_result.IsCompleted) {
$module_result = ConvertFrom-AnsibleJson -InputObject $job_output
# TODO: check for conflicting keys
$result = $result + $module_result
} catch {
}
catch {
$result.failed = $true
$result.msg = "failed to parse module output: $($_.Exception.Message)"
# return output back to Ansible to help with debugging errors
@ -91,7 +97,8 @@ if ($job_async_result.IsCompleted) {
Set-Content -Path $resultfile_path -Value $result_json
Write-AnsibleLog "INFO - wrote output to $resultfile_path" "async_watchdog"
} else {
}
else {
Write-AnsibleLog "ERROR - reached timeout on async job, stopping job" "async_watchdog"
$ps.BeginStop($null, $null) > $null # best effort stop

@ -2,7 +2,7 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
param(
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Payload
[Parameter(Mandatory = $true)][System.Collections.IDictionary]$Payload
)
$ErrorActionPreference = "Stop"
@ -74,7 +74,8 @@ $bootstrap_wrapper = {
try {
$pipe.Connect()
$pipe.Read($input_bytes, 0, $bytes_length) > $null
} finally {
}
finally {
$pipe.Close()
}
$exec = [System.Text.Encoding]::UTF8.GetString($input_bytes)
@ -114,12 +115,12 @@ $pipe = New-Object -TypeName System.IO.Pipes.NamedPipeServerStream -ArgumentList
try {
Write-AnsibleLog "INFO - creating async process '$exec_args'" "async_wrapper"
$process = Invoke-CimMethod -ClassName Win32_Process -Name Create -Arguments @{CommandLine=$exec_args}
$process = Invoke-CimMethod -ClassName Win32_Process -Name Create -Arguments @{CommandLine = $exec_args }
$rc = $process.ReturnValue
Write-AnsibleLog "INFO - return value from async process exec: $rc" "async_wrapper"
if ($rc -ne 0) {
$error_msg = switch($rc) {
$error_msg = switch ($rc) {
2 { "Access denied" }
3 { "Insufficient privilege" }
8 { "Unknown failure" }
@ -163,7 +164,8 @@ try {
$pipe.Write($payload_bytes, 0, $payload_bytes.Count)
$pipe.Flush()
$pipe.WaitForPipeDrain()
} finally {
}
finally {
$pipe.Close()
}

@ -2,7 +2,7 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
param(
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Payload
[Parameter(Mandatory = $true)][System.Collections.IDictionary]$Payload
)
#Requires -Module Ansible.ModuleUtils.AddType
@ -17,7 +17,8 @@ Function Get-EnumValue($enum, $flag_type, $value) {
$raw_enum_value = $value.Replace('_', '')
try {
$enum_value = [Enum]::Parse($enum, $raw_enum_value, $true)
} catch [System.ArgumentException] {
}
catch [System.ArgumentException] {
$valid_options = [Enum]::GetNames($enum) | ForEach-Object -Process {
(($_ -creplace "(.)([A-Z][a-z]+)", '$1_$2') -creplace "([a-z0-9])([A-Z])", '$1_$2').ToString().ToLower()
}
@ -26,15 +27,17 @@ Function Get-EnumValue($enum, $flag_type, $value) {
return $enum_value
}
Function Get-BecomeFlags($flags) {
Function Get-BecomeFlag($flags) {
$logon_type = [Ansible.AccessToken.LogonType]::Interactive
$logon_flags = [Ansible.Become.LogonFlags]::WithProfile
if ($null -eq $flags -or $flags -eq "") {
$flag_split = @()
} elseif ($flags -is [string]) {
}
elseif ($flags -is [string]) {
$flag_split = $flags.Split(" ")
} else {
}
else {
throw "become_flags must be a string, was $($flags.GetType())"
}
@ -52,7 +55,8 @@ Function Get-BecomeFlags($flags) {
value = $flag_value
}
$logon_type = Get-EnumValue @enum_details
} elseif ($flag_key -eq "logon_flags") {
}
elseif ($flag_key -eq "logon_flags") {
$logon_flag_values = $flag_value.Split(",")
$logon_flags = 0 -as [Ansible.Become.LogonFlags]
foreach ($logon_flag_value in $logon_flag_values) {
@ -67,7 +71,8 @@ Function Get-BecomeFlags($flags) {
$logon_flag = Get-EnumValue @enum_details
$logon_flags = $logon_flags -bor $logon_flag
}
} else {
}
else {
throw "become_flags key '$flag_key' is not a valid runas flag, must be 'logon_type' or 'logon_flags'"
}
}
@ -96,8 +101,9 @@ if ($null -eq $password) {
}
try {
$logon_type, $logon_flags = Get-BecomeFlags -flags $Payload.become_flags
} catch {
$logon_type, $logon_flags = Get-BecomeFlag -flags $Payload.become_flags
}
catch {
Write-AnsibleError -Message "internal error: failed to parse become_flags '$($Payload.become_flags)'" -ErrorRecord $_
$host.SetShouldExit(1)
return
@ -139,7 +145,8 @@ try {
$stdout = $result.StandardOut
try {
$stdout = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($stdout))
} catch [FormatException] {
}
catch [FormatException] {
# output wasn't Base64, ignore as it may contain an error message we want to pass to Ansible
Write-AnsibleLog "WARN - become process stdout was not base64 encoded as expected: $stdout"
}
@ -147,7 +154,8 @@ try {
$host.UI.WriteLine($stdout)
$host.UI.WriteErrorLine($result.StandardError.Trim())
$host.SetShouldExit($result.ExitCode)
} catch {
}
catch {
Write-AnsibleError -Message "internal error: failed to become user '$username'" -ErrorRecord $_
$host.SetShouldExit(1)
}

@ -2,7 +2,7 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
param(
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Payload
[Parameter(Mandatory = $true)][System.Collections.IDictionary]$Payload
)
#AnsibleRequires -Wrapper module_wrapper
@ -159,7 +159,8 @@ try {
try {
&$entrypoint @params
} finally {
}
finally {
# Processing here is kept to an absolute minimum to make sure each task runtime is kept as small as
# possible. Once all the tests have been run ansible-test will collect this info and process it locally in
# one go.
@ -180,14 +181,16 @@ try {
$utf8_no_bom = New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false
[System.IO.File]::WriteAllbytes($coverage_output_path, $utf8_no_bom.GetBytes($code_cov_json))
}
} finally {
}
finally {
try {
if ($breakpoint_info) {
foreach ($b in $breakpoint_info.Breakpoints) {
Remove-PSBreakpoint -Breakpoint $b
}
}
} finally {
}
finally {
Write-AnsibleLog "INFO - Remove temp coverage folder '$temp_path'" "coverage_wrapper"
Remove-Item -LiteralPath $temp_path -Force -Recurse
}

@ -22,7 +22,7 @@ begin {
[String] The JSON string to deserialize.
#>
param(
[Parameter(Mandatory=$true, Position=0)][String]$InputObject
[Parameter(Mandatory = $true, Position = 0)][String]$InputObject
)
# we can use -AsHashtable to get PowerShell to convert the JSON to
@ -30,8 +30,9 @@ begin {
# 6.0, fall back to a manual conversion for older versions
$cmdlet = Get-Command -Name ConvertFrom-Json -CommandType Cmdlet
if ("AsHashtable" -in $cmdlet.Parameters.Keys) {
return ,(ConvertFrom-Json -InputObject $InputObject -AsHashtable)
} else {
return , (ConvertFrom-Json -InputObject $InputObject -AsHashtable)
}
else {
# get the PSCustomObject and then manually convert from there
$raw_obj = ConvertFrom-Json -InputObject $InputObject
@ -47,18 +48,20 @@ begin {
foreach ($prop in $InputObject.PSObject.Properties.GetEnumerator()) {
$new_value.($prop.Name) = (ConvertTo-Hashtable -InputObject $prop.Value)
}
return ,$new_value
} elseif ($InputObject -is [Array]) {
return , $new_value
}
elseif ($InputObject -is [Array]) {
$new_value = [System.Collections.ArrayList]@()
foreach ($val in $InputObject) {
$new_value.Add((ConvertTo-Hashtable -InputObject $val)) > $null
}
return ,$new_value.ToArray()
} else {
return ,$InputObject
return , $new_value.ToArray()
}
else {
return , $InputObject
}
}
return ,(ConvertTo-Hashtable -InputObject $raw_obj)
return , (ConvertTo-Hashtable -InputObject $raw_obj)
}
}
@ -107,7 +110,7 @@ $($ErrorRecord.InvocationInfo.PositionMessage)
ErrorRecord is passed through.
#>
param(
[Parameter(Mandatory=$true)][String]$Message,
[Parameter(Mandatory = $true)][String]$Message,
[System.Management.Automation.ErrorRecord]$ErrorRecord = $null
)
$result = @{
@ -130,8 +133,8 @@ $($ErrorRecord.InvocationInfo.PositionMessage)
an env value on the Windows host that this is run on to enable.
#>
param(
[Parameter(Mandatory=$true, Position=0)][String]$Message,
[Parameter(Position=1)][String]$Wrapper
[Parameter(Mandatory = $true, Position = 0)][String]$Message,
[Parameter(Position = 1)][String]$Wrapper
)
$log_path = $env:ANSIBLE_EXEC_DEBUG
@ -150,7 +153,8 @@ $($ErrorRecord.InvocationInfo.PositionMessage)
[System.IO.FileAccess]::Write, [System.IO.FileShare]::ReadWrite)
try {
$fs.Write($msg_bytes, 0, $msg_bytes.Length)
} finally {
}
finally {
$fs.Close()
}
}
@ -186,7 +190,8 @@ $($ErrorRecord.InvocationInfo.PositionMessage)
Write-AnsibleLog "INFO - checking if actual os version '$actual_os_version' is less than the min os version '$min_os_version'" "exec_wrapper"
if ($actual_os_version -lt $min_os_version) {
Write-AnsibleError -Message "internal error: This module cannot run on this OS as it requires a minimum version of $min_os_version, actual was $actual_os_version"
$msg = "internal error: This module cannot run on this OS as it requires a minimum version of $min_os_version, actual was $actual_os_version"
Write-AnsibleError -Message $msg
exit 1
}
}
@ -196,7 +201,8 @@ $($ErrorRecord.InvocationInfo.PositionMessage)
Write-AnsibleLog "INFO - checking if actual PS version '$actual_ps_version' is less than the min PS version '$min_ps_version'" "exec_wrapper"
if ($actual_ps_version -lt $min_ps_version) {
Write-AnsibleError -Message "internal error: This module cannot run as it requires a minimum PowerShell version of $min_ps_version, actual was $actual_ps_version"
$msg = "internal error: This module cannot run as it requires a minimum PowerShell version of $min_ps_version, actual was $actual_ps_version"
Write-AnsibleError -Message $msg
exit 1
}
}
@ -218,10 +224,12 @@ $($ErrorRecord.InvocationInfo.PositionMessage)
if ($encoded_output -and $null -ne $output) {
$b64_output = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($output))
Write-Output -InputObject $b64_output
} else {
}
else {
$output
}
} catch {
}
catch {
Write-AnsibleError -Message "internal error: failed to run exec_wrapper action $action" -ErrorRecord $_
exit 1
}

@ -2,7 +2,7 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
param(
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Payload
[Parameter(Mandatory = $true)][System.Collections.IDictionary]$Payload
)
#AnsibleRequires -Wrapper module_wrapper
@ -38,7 +38,8 @@ if ($Payload.ContainsKey("coverage") -and $null -ne $host.Runspace -and $null -n
$params = @{
Payload = $Payload
}
} else {
}
else {
# get the common module_wrapper code and invoke that to run the module
$module = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Payload.module_entry))
$variables = [System.Collections.ArrayList]@(@{ Name = "complex_args"; Value = $Payload.module_args; Scope = "Global" })
@ -58,7 +59,8 @@ $entrypoint = [ScriptBlock]::Create($entrypoint)
try {
&$entrypoint @params
} catch {
}
catch {
# failed to invoke the PowerShell module, capture the exception and
# output a pretty error for Ansible to parse
$result = @{

@ -2,7 +2,7 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
param(
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Payload
[Parameter(Mandatory = $true)][System.Collections.IDictionary]$Payload
)
#AnsibleRequires -Wrapper module_wrapper

@ -83,7 +83,7 @@ if ($Modules) {
Write-AnsibleLog "INFO - create module util '$($module.Key)' for $ModuleName" "module_wrapper"
$module_name = $module.Key
$module_code = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($module.Value))
$ps.AddCommand("New-Module").AddParameters(@{Name=$module_name; ScriptBlock=[ScriptBlock]::Create($module_code)}) > $null
$ps.AddCommand("New-Module").AddParameters(@{Name = $module_name; ScriptBlock = [ScriptBlock]::Create($module_code) }) > $null
$ps.AddCommand("Import-Module").AddParameter("WarningAction", "SilentlyContinue") > $null
$ps.AddCommand("Out-Null").AddStatement() > $null
}
@ -108,9 +108,10 @@ if ($Breakpoints.Count -gt 0) {
'AddLineBreakpoint', [System.Reflection.BindingFlags]'Instance, NonPublic'
)
foreach ($b in $Breakpoints) {
$set_method.Invoke($ps.Runspace.Debugger, [Object[]]@(,$b)) > $null
$set_method.Invoke($ps.Runspace.Debugger, [Object[]]@(, $b)) > $null
}
} else {
}
else {
$ps.Runspace.Debugger.SetBreakpoints($Breakpoints)
}
}
@ -126,7 +127,8 @@ $new_out = New-Object -TypeName System.IO.StringWriter -ArgumentList $sb
try {
[System.Console]::SetOut($new_out)
$module_output = $ps.Invoke()
} catch {
}
catch {
# uncaught exception while executing module, present a prettier error for
# Ansible to parse
$error_params = @{
@ -147,7 +149,8 @@ try {
Write-AnsibleError @error_params
$host.SetShouldExit(1)
return
} finally {
}
finally {
[System.Console]::SetOut($orig_out)
$new_out.Dispose()
}
@ -166,9 +169,11 @@ if ($ps.InvocationStateInfo.State -eq "Failed" -and $ModuleName -ne "script") {
# options.
if ($null -eq $reason) {
$error_params.Message += ": Unknown error"
} elseif ($reason.PSObject.Properties.Name -contains "ErrorRecord") {
}
elseif ($reason.PSObject.Properties.Name -contains "ErrorRecord") {
$error_params.ErrorRecord = $reason.ErrorRecord
} else {
}
else {
$error_params.Message += ": $($reason.ToString())"
}

@ -67,12 +67,12 @@ Function Add-CSharpType {
//TypeAccelerator -Name <AcceleratorName> -TypeName <Name of compiled type>
#>
param(
[Parameter(Mandatory=$true)][AllowEmptyCollection()][String[]]$References,
[Parameter(Mandatory = $true)][AllowEmptyCollection()][String[]]$References,
[Switch]$IgnoreWarnings,
[Switch]$PassThru,
[Parameter(Mandatory=$true, ParameterSetName="Module")][Object]$AnsibleModule,
[Parameter(ParameterSetName="Manual")][String]$TempPath = $env:TMP,
[Parameter(ParameterSetName="Manual")][Switch]$IncludeDebugInfo,
[Parameter(Mandatory = $true, ParameterSetName = "Module")][Object]$AnsibleModule,
[Parameter(ParameterSetName = "Manual")][String]$TempPath = $env:TMP,
[Parameter(ParameterSetName = "Manual")][Switch]$IncludeDebugInfo,
[String[]]$CompileSymbols = @()
)
if ($null -eq $References -or $References.Length -eq 0) {
@ -86,7 +86,8 @@ Function Add-CSharpType {
if ([System.IntPtr]::Size -eq 4) {
$defined_symbols.Add('X86') > $null
} else {
}
else {
$defined_symbols.Add('AMD64') > $null
}
@ -100,10 +101,12 @@ Function Add-CSharpType {
if ($null -ne $is_windows) {
if ($is_windows.Value) {
$defined_symbols.Add("WINDOWS") > $null
} else {
}
else {
$defined_symbols.Add("UNIX") > $null
}
} else {
}
else {
$defined_symbols.Add("WINDOWS") > $null
}
@ -155,7 +158,8 @@ Function Add-CSharpType {
$assembly_path = $match.Groups["Name"].Value
if ($parameter_type -eq "Type") {
$assembly_path = ([Type]$assembly_path).Assembly.Location
} else {
}
else {
if (-not ([System.IO.Path]::IsPathRooted($assembly_path))) {
$assembly_path = Join-Path -Path $lib_assembly_location -ChildPath $assembly_path
}
@ -174,7 +178,7 @@ Function Add-CSharpType {
$type_matches = $type_pattern.Matches($reference)
foreach ($match in $type_matches) {
$type_accelerators.Add(@{Name=$match.Groups["Name"].Value; TypeName=$match.Groups["TypeName"].Value})
$type_accelerators.Add(@{Name = $match.Groups["Name"].Value; TypeName = $match.Groups["TypeName"].Value })
}
}
@ -246,18 +250,21 @@ Function Add-CSharpType {
$code_ms.Seek(0, [System.IO.SeekOrigin]::Begin) > $null
$pdb_ms.Seek(0, [System.IO.SeekOrigin]::Begin) > $null
$compiled_assembly = [System.Runtime.Loader.AssemblyLoadContext]::Default.LoadFromStream($code_ms, $pdb_ms)
} finally {
}
finally {
$code_ms.Close()
$pdb_ms.Close()
}
} else {
}
else {
# compile the code using CodeDom on PSDesktop
# configure compile options based on input
if ($PSCmdlet.ParameterSetName -eq "Module") {
$temp_path = $AnsibleModule.Tmpdir
$include_debug = $AnsibleModule.Verbosity -ge 3
} else {
}
else {
$temp_path = $TempPath
$include_debug = $IncludeDebugInfo.IsPresent
}
@ -321,7 +328,7 @@ Function Add-CSharpType {
$type_matches = $type_pattern.Matches($reference)
foreach ($match in $type_matches) {
$type_accelerators.Add(@{Name=$match.Groups["Name"].Value; TypeName=$match.Groups["TypeName"].Value})
$type_accelerators.Add(@{Name = $match.Groups["Name"].Value; TypeName = $match.Groups["TypeName"].Value })
}
}
if ($ignore_warnings.Count -gt 0) {

@ -5,7 +5,7 @@
# https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments
# https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
Function Escape-Argument($argument, $force_quote=$false) {
Function Escape-Argument($argument, $force_quote = $false) {
# this converts a single argument to an escaped version, use Join-Arguments
# instead of this function as this only escapes a single string.
@ -14,7 +14,8 @@ Function Escape-Argument($argument, $force_quote=$false) {
# argument does not need escaping (and we don't want to force it),
# return as is
return $argument
} else {
}
else {
# we need to quote the arg so start with "
$new_argument = '"'
@ -28,16 +29,18 @@ Function Escape-Argument($argument, $force_quote=$false) {
}
$current_char = $argument[$i]
if ($i -eq ($argument.Length -1) -and $current_char -eq "\") {
if ($i -eq ($argument.Length - 1) -and $current_char -eq "\") {
# We are at the end of the string so we need to add the same \
# * 2 as the end char would be a "
$new_argument += ("\" * ($num_backslashes + 1) * 2)
} elseif ($current_char -eq '"') {
}
elseif ($current_char -eq '"') {
# we have a inline ", we need to add the existing \ but * by 2
# plus another 1
$new_argument += ("\" * (($num_backslashes * 2) + 1))
$new_argument += $current_char
} else {
}
else {
# normal character so no need to escape the \ we have counted
$new_argument += ("\" * $num_backslashes)
$new_argument += $current_char
@ -50,7 +53,7 @@ Function Escape-Argument($argument, $force_quote=$false) {
}
}
Function Argv-ToString($arguments, $force_quote=$false) {
Function Argv-ToString($arguments, $force_quote = $false) {
# Takes in a list of un escaped arguments and convert it to a single string
# that can be used when starting a new process. It will escape the
# characters as necessary in the list.

@ -2,13 +2,13 @@
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)
Function Backup-File {
<#
<#
.SYNOPSIS
Helper function to make a backup of a file.
.EXAMPLE
Backup-File -path $path -WhatIf:$check_mode
#>
[CmdletBinding(SupportsShouldProcess=$true)]
[CmdletBinding(SupportsShouldProcess = $true)]
Param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
@ -21,7 +21,8 @@ Function Backup-File {
$backup_path = "$path.$pid." + [DateTime]::Now.ToString("yyyyMMdd-HHmmss") + ".bak";
Try {
Copy-Item -LiteralPath $path -Destination $backup_path
} Catch {
}
Catch {
throw "Failed to create backup file '$backup_path' from '$path'. ($($_.Exception.Message))"
}
}

@ -28,15 +28,17 @@ Function Convert-ListToSnakeCase($list) {
foreach ($value in $list) {
if ($value -is [Hashtable]) {
$new_value = Convert-DictToSnakeCase -dict $value
} elseif ($value -is [Array] -or $value -is [System.Collections.ArrayList]) {
}
elseif ($value -is [Array] -or $value -is [System.Collections.ArrayList]) {
$new_value = Convert-ListToSnakeCase -list $value
} else {
}
else {
$new_value = $value
}
[void]$snake_list.Add($new_value)
}
return ,$snake_list
return , $snake_list
}
# converts a dict/hashtable keys from camelCase to snake_case
@ -51,14 +53,16 @@ Function Convert-DictToSnakeCase($dict) {
$value = $dict_entry.Value
if ($value -is [Hashtable]) {
$snake_dict.$snake_key = Convert-DictToSnakeCase -dict $value
} elseif ($value -is [Array] -or $value -is [System.Collections.ArrayList]) {
}
elseif ($value -is [Array] -or $value -is [System.Collections.ArrayList]) {
$snake_dict.$snake_key = Convert-ListToSnakeCase -list $value
} else {
}
else {
$snake_dict.$snake_key = $value
}
}
return ,$snake_dict
return , $snake_dict
}
# this line must stay at the bottom to ensure all defined module parts are exported

@ -8,11 +8,13 @@ Function Load-CommandUtils {
.SYNOPSIS
No-op, as the C# types are automatically loaded.
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Cannot change the name now")]
Param()
$msg = "Load-CommandUtils is deprecated and no longer needed, this cmdlet will be removed in a future version"
if ((Get-Command -Name Add-DeprecationWarning -ErrorAction SilentlyContinue) -and (Get-Variable -Name result -ErrorAction SilentlyContinue)) {
Add-DeprecationWarning -obj $result.Value -message $msg -version 2.12
} else {
}
else {
$module = Get-Variable -Name module -ErrorAction SilentlyContinue
if ($null -ne $module -and $module.Value.GetType().FullName -eq "Ansible.Basic.AnsibleModule") {
$module.Value.Deprecate($msg, "2.12")
@ -47,13 +49,15 @@ Function Get-ExecutablePath {
if ($full_path -ne $executable -and $directory -ne $null) {
$file = Get-Item -LiteralPath "$directory\$executable" -Force -ErrorAction SilentlyContinue
} else {
}
else {
$file = Get-Item -LiteralPath $executable -Force -ErrorAction SilentlyContinue
}
if ($null -ne $file) {
$executable_path = $file.FullName
} else {
}
else {
$executable_path = [Ansible.Process.ProcessUtil]::SearchPath($executable)
}
return $executable_path
@ -110,7 +114,7 @@ Function Run-Command {
# run the command and get the results
$command_result = [Ansible.Process.ProcessUtil]::CreateProcess($executable, $command, $working_directory, $environment, $stdin, $output_encoding_override)
return ,@{
return , @{
executable = $executable
stdout = $command_result.StandardOut
stderr = $command_result.StandardError

@ -12,14 +12,16 @@ result from that.
Function Test-AnsiblePath {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)][string]$Path
[Parameter(Mandatory = $true)][string]$Path
)
# Replacement for Test-Path
try {
$file_attributes = [System.IO.File]::GetAttributes($Path)
} catch [System.IO.FileNotFoundException], [System.IO.DirectoryNotFoundException] {
}
catch [System.IO.FileNotFoundException], [System.IO.DirectoryNotFoundException] {
return $false
} catch [NotSupportedException] {
}
catch [NotSupportedException] {
# When testing a path like Cert:\LocalMachine\My, System.IO.File will
# not work, we just revert back to using Test-Path for this
return Test-Path -Path $Path
@ -27,7 +29,8 @@ Function Test-AnsiblePath {
if ([Int32]$file_attributes -eq -1) {
return $false
} else {
}
else {
return $true
}
}
@ -35,12 +38,13 @@ Function Test-AnsiblePath {
Function Get-AnsibleItem {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)][string]$Path
[Parameter(Mandatory = $true)][string]$Path
)
# Replacement for Get-Item
try {
$file_attributes = [System.IO.File]::GetAttributes($Path)
} catch {
}
catch {
# if -ErrorAction SilentlyCotinue is set on the cmdlet and we failed to
# get the attributes, just return $null, otherwise throw the error
if ($ErrorActionPreference -ne "SilentlyContinue") {
@ -50,9 +54,11 @@ Function Get-AnsibleItem {
}
if ([Int32]$file_attributes -eq -1) {
throw New-Object -TypeName System.Management.Automation.ItemNotFoundException -ArgumentList "Cannot find path '$Path' because it does not exist."
} elseif ($file_attributes.HasFlag([System.IO.FileAttributes]::Directory)) {
}
elseif ($file_attributes.HasFlag([System.IO.FileAttributes]::Directory)) {
return New-Object -TypeName System.IO.DirectoryInfo -ArgumentList $Path
} else {
}
else {
return New-Object -TypeName System.IO.FileInfo -ArgumentList $Path
}
}

@ -4,9 +4,8 @@
Set-StrictMode -Version 2.0
$ErrorActionPreference = "Stop"
Function Set-Attr($obj, $name, $value)
{
<#
Function Set-Attr($obj, $name, $value) {
<#
.SYNOPSIS
Helper function to set an "attribute" on a psobject instance in PowerShell.
This is a convenience to make adding Members to the object easier and
@ -16,24 +15,20 @@ Function Set-Attr($obj, $name, $value)
#>
# If the provided $obj is undefined, define one to be nice
If (-not $obj.GetType)
{
If (-not $obj.GetType) {
$obj = @{ }
}
Try
{
Try {
$obj.$name = $value
}
Catch
{
Catch {
$obj | Add-Member -Force -MemberType NoteProperty -Name $name -Value $value
}
}
Function Exit-Json($obj)
{
<#
Function Exit-Json($obj) {
<#
.SYNOPSIS
Helper function to convert a PowerShell object to JSON and output it, exiting
the script
@ -42,8 +37,7 @@ Function Exit-Json($obj)
#>
# If the provided $obj is undefined, define one to be nice
If (-not $obj.GetType)
{
If (-not $obj.GetType) {
$obj = @{ }
}
@ -55,9 +49,8 @@ Function Exit-Json($obj)
Exit
}
Function Fail-Json($obj, $message = $null)
{
<#
Function Fail-Json($obj, $message = $null) {
<#
.SYNOPSIS
Helper function to add the "msg" property and "failed" property, convert the
PowerShell Hashtable to JSON and output it, exiting the script
@ -67,12 +60,14 @@ Function Fail-Json($obj, $message = $null)
if ($obj -is [hashtable] -or $obj -is [psobject]) {
# Nothing to do
} elseif ($obj -is [string] -and $null -eq $message) {
}
elseif ($obj -is [string] -and $null -eq $message) {
# If we weren't given 2 args, and the only arg was a string,
# create a new Hashtable and use the arg as the failure message
$message = $obj
$obj = @{ }
} else {
}
else {
# If the first argument is undefined or a different type,
# make it a Hashtable
$obj = @{ }
@ -90,9 +85,8 @@ Function Fail-Json($obj, $message = $null)
Exit 1
}
Function Add-Warning($obj, $message)
{
<#
Function Add-Warning($obj, $message) {
<#
.SYNOPSIS
Helper function to add warnings, even if the warnings attribute was
not already set up. This is a convenience for the module developer
@ -101,16 +95,16 @@ Function Add-Warning($obj, $message)
if (-not $obj.ContainsKey("warnings")) {
$obj.warnings = @()
} elseif ($obj.warnings -isnot [array]) {
}
elseif ($obj.warnings -isnot [array]) {
throw "Add-Warning: warnings attribute is not an array"
}
$obj.warnings += $message
}
Function Add-DeprecationWarning($obj, $message, $version = $null)
{
<#
Function Add-DeprecationWarning($obj, $message, $version = $null) {
<#
.SYNOPSIS
Helper function to add deprecations, even if the deprecations attribute was
not already set up. This is a convenience for the module developer
@ -118,7 +112,8 @@ Function Add-DeprecationWarning($obj, $message, $version = $null)
#>
if (-not $obj.ContainsKey("deprecations")) {
$obj.deprecations = @()
} elseif ($obj.deprecations -isnot [array]) {
}
elseif ($obj.deprecations -isnot [array]) {
throw "Add-DeprecationWarning: deprecations attribute is not a list"
}
@ -128,23 +123,22 @@ Function Add-DeprecationWarning($obj, $message, $version = $null)
}
}
Function Expand-Environment($value)
{
<#
Function Expand-Environment($value) {
<#
.SYNOPSIS
Helper function to expand environment variables in values. By default
it turns any type to a string, but we ensure $null remains $null.
#>
if ($null -ne $value) {
[System.Environment]::ExpandEnvironmentVariables($value)
} else {
}
else {
$value
}
}
Function Get-AnsibleParam($obj, $name, $default = $null, $resultobj = @{}, $failifempty = $false, $emptyattributefailmessage, $ValidateSet, $ValidateSetErrorMessage, $type = $null, $aliases = @())
{
<#
Function Get-AnsibleParam {
<#
.SYNOPSIS
Helper function to get an "attribute" from a psobject instance in PowerShell.
This is a convenience to make getting Members from an object easier and
@ -156,6 +150,18 @@ Function Get-AnsibleParam($obj, $name, $default = $null, $resultobj = @{}, $fail
Get-AnsibleParam also supports Parameter validation to save you from coding that manually
Note that if you use the failifempty option, you do need to specify resultobject as well.
#>
param (
$obj,
$name,
$default = $null,
$resultobj = @{},
$failifempty = $false,
$emptyattributefailmessage,
$ValidateSet,
$ValidateSetErrorMessage,
$type = $null,
$aliases = @()
)
# Check if the provided Member $name or aliases exist in $obj and return it or the default.
try {
@ -180,20 +186,24 @@ Function Get-AnsibleParam($obj, $name, $default = $null, $resultobj = @{}, $fail
if ($ValidateSet -contains ($obj.$name)) {
$value = $obj.$name
} else {
}
else {
if ($null -eq $ValidateSetErrorMessage) {
#Auto-generated error should be sufficient in most use cases
$ValidateSetErrorMessage = "Get-AnsibleParam: Argument $name needs to be one of $($ValidateSet -join ",") but was $($obj.$name)."
}
Fail-Json -obj $resultobj -message $ValidateSetErrorMessage
}
} else {
}
else {
$value = $obj.$name
}
} catch {
}
catch {
if ($failifempty -eq $false) {
$value = $default
} else {
}
else {
if (-not $emptyattributefailmessage) {
$emptyattributefailmessage = "Get-AnsibleParam: Missing required argument: $name"
}
@ -224,45 +234,51 @@ Function Get-AnsibleParam($obj, $name, $default = $null, $resultobj = @{}, $fail
Fail-Json -obj $resultobj -message "Get-AnsibleParam: Parameter '$name' has an invalid path '$value' specified."
}
}
} elseif ($type -eq "str") {
}
elseif ($type -eq "str") {
# Convert str types to real Powershell strings
$value = $value.ToString()
} elseif ($type -eq "bool") {
}
elseif ($type -eq "bool") {
# Convert boolean types to real Powershell booleans
$value = $value | ConvertTo-Bool
} elseif ($type -eq "int") {
}
elseif ($type -eq "int") {
# Convert int types to real Powershell integers
$value = $value -as [int]
} elseif ($type -eq "float") {
}
elseif ($type -eq "float") {
# Convert float types to real Powershell floats
$value = $value -as [float]
} elseif ($type -eq "list") {
}
elseif ($type -eq "list") {
if ($value -is [array]) {
# Nothing to do
} elseif ($value -is [string]) {
}
elseif ($value -is [string]) {
# Convert string type to real Powershell array
$value = $value.Split(",").Trim()
} elseif ($value -is [int]) {
}
elseif ($value -is [int]) {
$value = @($value)
} else {
}
else {
Fail-Json -obj $resultobj -message "Get-AnsibleParam: Parameter '$name' is not a YAML list."
}
# , is not a typo, forces it to return as a list when it is empty or only has 1 entry
return ,$value
return , $value
}
return $value
}
#Alias Get-attr-->Get-AnsibleParam for backwards compat. Only add when needed to ease debugging of scripts
If (-not(Get-Alias -Name "Get-attr" -ErrorAction SilentlyContinue))
{
If (-not(Get-Alias -Name "Get-attr" -ErrorAction SilentlyContinue)) {
New-Alias -Name Get-attr -Value Get-AnsibleParam
}
Function ConvertTo-Bool
{
<#
Function ConvertTo-Bool {
<#
.SYNOPSIS
Helper filter/pipeline function to convert a value to boolean following current
Ansible practices
@ -270,40 +286,43 @@ Function ConvertTo-Bool
$is_true = "true" | ConvertTo-Bool
#>
param(
[parameter(valuefrompipeline=$true)]
[parameter(valuefrompipeline = $true)]
$obj
)
process {
$boolean_strings = "yes", "on", "1", "true", 1
$obj_string = [string]$obj
if (($obj -is [boolean] -and $obj) -or $boolean_strings -contains $obj_string.ToLower()) {
return $true
} else {
}
else {
return $false
}
}
}
Function Parse-Args($arguments, $supports_check_mode = $false)
{
<#
Function Parse-Args {
<#
.SYNOPSIS
Helper function to parse Ansible JSON arguments from a "file" passed as
the single argument to the module.
.EXAMPLE
$params = Parse-Args $args
#>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Cannot change the name now")]
param ($arguments, $supports_check_mode = $false)
$params = New-Object psobject
If ($arguments.Length -gt 0)
{
If ($arguments.Length -gt 0) {
$params = Get-Content $arguments[0] | ConvertFrom-Json
}
Else {
$params = $complex_args
}
$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
If ($check_mode -and -not $supports_check_mode)
{
If ($check_mode -and -not $supports_check_mode) {
Exit-Json @{
skipped = $true
changed = $false
@ -314,17 +333,14 @@ Function Parse-Args($arguments, $supports_check_mode = $false)
}
Function Get-FileChecksum($path, $algorithm = 'sha1')
{
<#
Function Get-FileChecksum($path, $algorithm = 'sha1') {
<#
.SYNOPSIS
Helper function to calculate a hash of a file in a way which PowerShell 3
and above can handle
#>
If (Test-Path -LiteralPath $path -PathType Leaf)
{
switch ($algorithm)
{
If (Test-Path -LiteralPath $path -PathType Leaf) {
switch ($algorithm) {
'md5' { $sp = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider }
'sha1' { $sp = New-Object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider }
'sha256' { $sp = New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider }
@ -336,39 +352,36 @@ Function Get-FileChecksum($path, $algorithm = 'sha1')
If ($PSVersionTable.PSVersion.Major -ge 4) {
$raw_hash = Get-FileHash -LiteralPath $path -Algorithm $algorithm
$hash = $raw_hash.Hash.ToLower()
} Else {
}
Else {
$fp = [System.IO.File]::Open($path, [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite);
$hash = [System.BitConverter]::ToString($sp.ComputeHash($fp)).Replace("-", "").ToLower();
$fp.Dispose();
}
}
ElseIf (Test-Path -LiteralPath $path -PathType Container)
{
ElseIf (Test-Path -LiteralPath $path -PathType Container) {
$hash = "3";
}
Else
{
Else {
$hash = "1";
}
return $hash
}
Function Get-PendingRebootStatus
{
<#
Function Get-PendingRebootStatus {
<#
.SYNOPSIS
Check if reboot is required, if so notify CA.
Function returns true if computer has a pending reboot
#>
$featureData = Invoke-CimMethod -EA Ignore -Name GetServerFeature -Namespace root\microsoft\windows\servermanager -Class MSFT_ServerManagerTasks
$regData = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" "PendingFileRenameOperations" -EA Ignore
$CBSRebootStatus = Get-ChildItem "HKLM:\\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing" -ErrorAction SilentlyContinue| Where-Object {$_.PSChildName -eq "RebootPending"}
if(($featureData -and $featureData.RequiresReboot) -or $regData -or $CBSRebootStatus)
{
$CBSRebootStatus = Get-ChildItem "HKLM:\\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing" -ErrorAction SilentlyContinue |
Where-Object { $_.PSChildName -eq "RebootPending" }
if (($featureData -and $featureData.RequiresReboot) -or $regData -or $CBSRebootStatus) {
return $True
}
else
{
else {
return $False
}
}

@ -3,7 +3,10 @@
#Requires -Module Ansible.ModuleUtils.PrivilegeUtil
Function Load-LinkUtils() {
Function Load-LinkUtils {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Cannot change the name now")]
param ()
$link_util = @'
using Microsoft.Win32.SafeHandles;
using System;
@ -314,8 +317,12 @@ namespace Ansible
throw new Exception(errorMessage);
}
string printName = new string(buffer.PathBuffer, (int)(buffer.PrintNameOffset / SIZE_OF_WCHAR) + pathOffset, (int)(buffer.PrintNameLength / SIZE_OF_WCHAR));
string substituteName = new string(buffer.PathBuffer, (int)(buffer.SubstituteNameOffset / SIZE_OF_WCHAR) + pathOffset, (int)(buffer.SubstituteNameLength / SIZE_OF_WCHAR));
string printName = new string(buffer.PathBuffer,
(int)(buffer.PrintNameOffset / SIZE_OF_WCHAR) + pathOffset,
(int)(buffer.PrintNameLength / SIZE_OF_WCHAR));
string substituteName = new string(buffer.PathBuffer,
(int)(buffer.SubstituteNameOffset / SIZE_OF_WCHAR) + pathOffset,
(int)(buffer.SubstituteNameLength / SIZE_OF_WCHAR));
// TODO: should we check for \?\UNC\server for convert it to the NT style \\server path
// Remove the leading Windows object directory \?\ from the path if present
@ -432,7 +439,7 @@ Function New-Link($link_path, $link_target, $link_type) {
throw "link_target '$link_target' does not exist, cannot create link"
}
switch($link_type) {
switch ($link_type) {
"link" {
$type = [Ansible.LinkType]::SymbolicLink
}

@ -13,7 +13,8 @@ Function Import-PrivilegeUtil {
$msg = "Import-PrivilegeUtil is deprecated and no longer needed, this cmdlet will be removed in a future version"
if ((Get-Command -Name Add-DeprecationWarning -ErrorAction SilentlyContinue) -and (Get-Variable -Name result -ErrorAction SilentlyContinue)) {
Add-DeprecationWarning -obj $result.Value -message $msg -version 2.12
} else {
}
else {
$module = Get-Variable -Name module -ErrorAction SilentlyContinue
if ($null -ne $module -and $module.Value.GetType().FullName -eq "Ansible.Basic.AnsibleModule") {
$module.Value.Deprecate($msg, "2.12")
@ -37,7 +38,7 @@ Function Get-AnsiblePrivilege {
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][String]$Name
[Parameter(Mandatory = $true)][String]$Name
)
if (-not [Ansible.Privilege.PrivilegeUtil]::CheckPrivilegeName($Name)) {
@ -49,7 +50,8 @@ Function Get-AnsiblePrivilege {
if ($privilege_info.ContainsKey($Name)) {
$status = $privilege_info.$Name
return $status.HasFlag([Ansible.Privilege.PrivilegeAttributes]::Enabled)
} else {
}
else {
return $null
}
}
@ -70,11 +72,11 @@ Function Set-AnsiblePrivilege {
#>
[CmdletBinding(SupportsShouldProcess)]
param(
[Parameter(Mandatory=$true)][String]$Name,
[Parameter(Mandatory=$true)][bool]$Value
[Parameter(Mandatory = $true)][String]$Name,
[Parameter(Mandatory = $true)][bool]$Value
)
$action = switch($Value) {
$action = switch ($Value) {
$true { "Enable" }
$false { "Disable" }
}
@ -82,7 +84,8 @@ Function Set-AnsiblePrivilege {
$current_state = Get-AnsiblePrivilege -Name $Name
if ($current_state -eq $Value) {
return # no change needs to occur
} elseif ($null -eq $current_state) {
}
elseif ($null -eq $current_state) {
# once a privilege is removed from a token we cannot do anything with it
throw [System.InvalidOperationException] "Cannot $($action.ToLower()) the privilege '$Name' as it has been removed from the token"
}

@ -9,7 +9,8 @@ Function Convert-FromSID($sid) {
$account_object = New-Object System.Security.Principal.SecurityIdentifier($sid)
try {
$nt_account = $account_object.Translate([System.Security.Principal.NTAccount])
} catch {
}
catch {
Fail-Json -obj @{} -message "failed to convert sid '$sid' to a logon name: $($_.Exception.Message)"
}
@ -17,7 +18,8 @@ Function Convert-FromSID($sid) {
}
Function Convert-ToSID {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingEmptyCatchBlock", "", Justification="We don't care if converting to a SID fails, just that it failed or not")]
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingEmptyCatchBlock", "",
Justification = "We don't care if converting to a SID fails, just that it failed or not")]
param($account_name)
# Converts an account name to a SID, it can take in the following forms
# SID: Will just return the SID value that was passed in
@ -34,21 +36,25 @@ Function Convert-ToSID {
try {
$sid = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $account_name
return $sid.Value
} catch {}
}
catch {}
if ($account_name -like "*\*") {
$account_name_split = $account_name -split "\\"
if ($account_name_split[0] -eq ".") {
$domain = $env:COMPUTERNAME
} else {
}
else {
$domain = $account_name_split[0]
}
$username = $account_name_split[1]
} elseif ($account_name -like "*@*") {
}
elseif ($account_name -like "*@*") {
$account_name_split = $account_name -split "@"
$domain = $account_name_split[1]
$username = $account_name_split[0]
} else {
}
else {
$domain = $null
$username = $account_name
}
@ -59,15 +65,18 @@ Function Convert-ToSID {
if ($domain -eq $env:COMPUTERNAME) {
$adsi = [ADSI]("WinNT://$env:COMPUTERNAME,computer")
$group = $adsi.psbase.children | Where-Object { $_.schemaClassName -eq "group" -and $_.Name -eq $username }
} else {
}
else {
$group = $null
}
if ($group) {
$account = New-Object System.Security.Principal.NTAccount($username)
} else {
}
else {
$account = New-Object System.Security.Principal.NTAccount($domain, $username)
}
} else {
}
else {
# when in a domain NTAccount(String) will favour domain lookups check
# if username is a local user and explictly search on the localhost for
# that account
@ -75,14 +84,16 @@ Function Convert-ToSID {
$user = $adsi.psbase.children | Where-Object { $_.schemaClassName -eq "user" -and $_.Name -eq $username }
if ($user) {
$account = New-Object System.Security.Principal.NTAccount($env:COMPUTERNAME, $username)
} else {
}
else {
$account = New-Object System.Security.Principal.NTAccount($username)
}
}
try {
$account_sid = $account.Translate([System.Security.Principal.SecurityIdentifier])
} catch {
}
catch {
Fail-Json @{} "account_name $account_name is not a valid account, cannot get SID: $($_.Exception.Message)"
}

@ -214,11 +214,13 @@ Function Get-AnsibleWebRequest {
if ($UseDefaultCredential -and $web_request -is [System.Net.HttpWebRequest]) {
$web_request.UseDefaultCredentials = $true
} elseif ($UrlUsername) {
}
elseif ($UrlUsername) {
if ($ForceBasicAuth) {
$auth_value = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $UrlUsername, $UrlPassword)))
$web_request.Headers.Add("Authorization", "Basic $auth_value")
} else {
}
else {
$credential = New-Object -TypeName System.Net.NetworkCredential -ArgumentList $UrlUsername, $UrlPassword
$web_request.Credentials = $credential
}
@ -238,7 +240,8 @@ Function Get-AnsibleWebRequest {
$cert = New-Object -TypeName "$crypto_ns.X509Certificate2" -ArgumentList @(
$ClientCert, $ClientCertPassword
)
} catch [System.Security.Cryptography.CryptographicException] {
}
catch [System.Security.Cryptography.CryptographicException] {
Write-Error -Message "Failed to read client certificate at '$ClientCert'" -Exception $_.Exception -Category SecurityError
return
}
@ -250,9 +253,11 @@ Function Get-AnsibleWebRequest {
if (-not $UseProxy) {
$proxy = $null
} elseif ($ProxyUrl) {
}
elseif ($ProxyUrl) {
$proxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $ProxyUrl, $true
} else {
}
else {
$proxy = $web_request.Proxy
}
@ -264,11 +269,13 @@ Function Get-AnsibleWebRequest {
# property. We cannot set UseDefaultCredentials so we just set the Credentials to the
# DefaultCredentials in the CredentialCache which does the same thing.
$proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
} elseif ($ProxyUsername) {
}
elseif ($ProxyUsername) {
$proxy.Credentials = New-Object -TypeName System.Net.NetworkCredential -ArgumentList @(
$ProxyUsername, $ProxyPassword
)
} else {
}
else {
$proxy.Credentials = $null
}
}
@ -305,7 +312,8 @@ Function Get-AnsibleWebRequest {
if ($Headers -and $Headers.ContainsKey("User-Agent")) {
if ($HttpAgent -eq $ansible_web_request_options.http_agent.default) {
$HttpAgent = $Headers['User-Agent']
} elseif ($null -ne $Module) {
}
elseif ($null -ne $Module) {
$Module.Warn("The 'User-Agent' header and the 'http_agent' was set, using the 'http_agent' for web request")
}
}
@ -316,7 +324,8 @@ Function Get-AnsibleWebRequest {
safe {
if ($web_request.Method -in @("GET", "HEAD")) {
$web_request.AllowAutoRedirect = $true
} else {
}
else {
$web_request.AllowAutoRedirect = $false
}
}
@ -325,7 +334,8 @@ Function Get-AnsibleWebRequest {
if ($MaximumRedirection -eq 0) {
$web_request.AllowAutoRedirect = $false
} else {
}
else {
$web_request.MaximumAutomaticRedirections = $MaximumRedirection
}
}
@ -388,16 +398,16 @@ Function Invoke-WithWebRequest {
#>
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[Parameter(Mandatory = $true)]
[System.Object]
[ValidateScript({ $_.GetType().FullName -eq 'Ansible.Basic.AnsibleModule' })]
$Module,
[Parameter(Mandatory=$true)]
[Parameter(Mandatory = $true)]
[System.Net.WebRequest]
$Request,
[Parameter(Mandatory=$true)]
[Parameter(Mandatory = $true)]
[ScriptBlock]
$Script,
@ -415,7 +425,8 @@ Function Invoke-WithWebRequest {
try {
$Body.CopyTo($request_st)
$request_st.Flush()
} finally {
}
finally {
$request_st.Close()
}
}
@ -423,7 +434,8 @@ Function Invoke-WithWebRequest {
try {
try {
$web_response = $Request.GetResponse()
} catch [System.Net.WebException] {
}
catch [System.Net.WebException] {
# A WebResponse with a status code not in the 200 range will raise a WebException. We check if the
# exception raised contains the actual response and continue on if IgnoreBadResponse is set. We also
# make sure we set the status_code return value on the Module object if possible
@ -436,7 +448,8 @@ Function Invoke-WithWebRequest {
$Module.Result.status_code = $_.Exception.Response.StatusCode
throw $_
}
} else {
}
else {
throw $_
}
}
@ -445,7 +458,8 @@ Function Invoke-WithWebRequest {
# A FileWebResponse won't have these properties set
$Module.Result.msg = "OK"
$Module.Result.status_code = 200
} else {
}
else {
$Module.Result.msg = $web_response.StatusDescription
$Module.Result.status_code = $web_response.StatusCode
}
@ -454,10 +468,12 @@ Function Invoke-WithWebRequest {
try {
# Invoke the ScriptBlock and pass in WebResponse and ResponseStream
&$Script -Response $web_response -Stream $response_stream
} finally {
}
finally {
$response_stream.Dispose()
}
} finally {
}
finally {
if ($web_response) {
$web_response.Close()
}
@ -483,28 +499,28 @@ Function Get-AnsibleWebRequestSpec {
# Kept here for backwards compat as this variable was added in Ansible 2.9. Ultimately this util should be removed
# once the deprecation period has been added.
$ansible_web_request_options = @{
method = @{ type="str" }
follow_redirects = @{ type="str"; choices=@("all","none","safe"); default="safe" }
headers = @{ type="dict" }
http_agent = @{ type="str"; default="ansible-httpget" }
maximum_redirection = @{ type="int"; default=50 }
timeout = @{ type="int"; default=30 } # Was defaulted to 10 in win_get_url but 30 in win_uri so we use 30
validate_certs = @{ type="bool"; default=$true }
method = @{ type = "str" }
follow_redirects = @{ type = "str"; choices = @("all", "none", "safe"); default = "safe" }
headers = @{ type = "dict" }
http_agent = @{ type = "str"; default = "ansible-httpget" }
maximum_redirection = @{ type = "int"; default = 50 }
timeout = @{ type = "int"; default = 30 } # Was defaulted to 10 in win_get_url but 30 in win_uri so we use 30
validate_certs = @{ type = "bool"; default = $true }
# Credential options
client_cert = @{ type="str" }
client_cert_password = @{ type="str"; no_log=$true }
force_basic_auth = @{ type="bool"; default=$false }
url_username = @{ type="str" }
url_password = @{ type="str"; no_log=$true }
use_default_credential = @{ type="bool"; default=$false }
client_cert = @{ type = "str" }
client_cert_password = @{ type = "str"; no_log = $true }
force_basic_auth = @{ type = "bool"; default = $false }
url_username = @{ type = "str" }
url_password = @{ type = "str"; no_log = $true }
use_default_credential = @{ type = "bool"; default = $false }
# Proxy options
use_proxy = @{ type="bool"; default=$true }
proxy_url = @{ type="str" }
proxy_username = @{ type="str" }
proxy_password = @{ type="str"; no_log=$true }
proxy_use_default_credential = @{ type="bool"; default=$false }
use_proxy = @{ type = "bool"; default = $true }
proxy_url = @{ type = "str" }
proxy_username = @{ type = "str" }
proxy_password = @{ type = "str"; no_log = $true }
proxy_use_default_credential = @{ type = "bool"; default = $false }
}
$export_members = @{

@ -15,22 +15,24 @@ $module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$test_username = $module.Params.test_username
$test_password = $module.Params.test_password
Function Assert-Equals {
Function Assert-Equal {
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
[Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
)
process {
$matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) {
$Actual.Count | Assert-Equals -Expected $Expected.Count
$Actual.Count | Assert-Equal -Expected $Expected.Count
for ($i = 0; $i -lt $Actual.Count; $i++) {
$actual_value = $Actual[$i]
$expected_value = $Expected[$i]
Assert-Equals -Actual $actual_value -Expected $expected_value
Assert-Equal -Actual $actual_value -Expected $expected_value
}
$matched = $true
} else {
}
else {
$matched = $Actual -ceq $Expected
}
@ -48,6 +50,7 @@ Function Assert-Equals {
$module.FailJson("AssertionError: actual != expected")
}
}
}
$current_user = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
@ -58,15 +61,16 @@ $tests = [Ordered]@{
$h_token = [Ansible.AccessToken.TokenUtil]::OpenProcessToken($h_process, "Query")
try {
$h_token.IsClosed | Assert-Equals -Expected $false
$h_token.IsInvalid | Assert-Equals -Expected $false
$h_token.IsClosed | Assert-Equal -Expected $false
$h_token.IsInvalid | Assert-Equal -Expected $false
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token)
$actual_user | Assert-Equals -Expected $current_user
} finally {
$actual_user | Assert-Equal -Expected $current_user
}
finally {
$h_token.Dispose()
}
$h_token.IsClosed | Assert-Equals -Expected $true
$h_token.IsClosed | Assert-Equal -Expected $true
}
"Open process token of another process" = {
@ -74,21 +78,24 @@ $tests = [Ordered]@{
try {
$h_process = [Ansible.AccessToken.TokenUtil]::OpenProcess($proc_info.Id, "QueryInformation", $false)
try {
$h_process.IsClosed | Assert-Equals -Expected $false
$h_process.IsInvalid | Assert-Equals -Expected $false
$h_process.IsClosed | Assert-Equal -Expected $false
$h_process.IsInvalid | Assert-Equal -Expected $false
$h_token = [Ansible.AccessToken.TokenUtil]::OpenProcessToken($h_process, "Query")
try {
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token)
$actual_user | Assert-Equals -Expected $current_user
} finally {
$actual_user | Assert-Equal -Expected $current_user
}
finally {
$h_token.Dispose()
}
} finally {
}
finally {
$h_process.Dispose()
}
$h_process.IsClosed | Assert-Equals -Expected $true
} finally {
$h_process.IsClosed | Assert-Equal -Expected $true
}
finally {
$proc_info | Stop-Process
}
}
@ -98,11 +105,13 @@ $tests = [Ordered]@{
try {
$h_process = [Ansible.AccessToken.TokenUtil]::OpenProcess(4, "QueryInformation", $false)
$h_process.Dispose() # Incase this doesn't fail, make sure we still dispose of it
} catch [Ansible.AccessToken.Win32Exception] {
}
catch [Ansible.AccessToken.Win32Exception] {
$failed = $true
$_.Exception.Message | Assert-Equals -Expected "Failed to open process 4 with access QueryInformation (Access is denied, Win32ErrorCode 5 - 0x00000005)"
$msg = "Failed to open process 4 with access QueryInformation (Access is denied, Win32ErrorCode 5 - 0x00000005)"
$_.Exception.Message | Assert-Equal -Expected $msg
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
"Duplicate access token primary" = {
@ -111,22 +120,24 @@ $tests = [Ordered]@{
try {
$dup_token = [Ansible.AccessToken.TokenUtil]::DuplicateToken($h_token, "Query", "Anonymous", "Primary")
try {
$dup_token.IsClosed | Assert-Equals -Expected $false
$dup_token.IsInvalid | Assert-Equals -Expected $false
$dup_token.IsClosed | Assert-Equal -Expected $false
$dup_token.IsInvalid | Assert-Equal -Expected $false
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($dup_token)
$actual_user | Assert-Equals -Expected $current_user
$actual_user | Assert-Equal -Expected $current_user
$actual_stat = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($dup_token)
$actual_stat.TokenType | Assert-Equals -Expected ([Ansible.AccessToken.TokenType]::Primary)
$actual_stat.ImpersonationLevel | Assert-Equals -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]::Anonymous)
} finally {
$actual_stat.TokenType | Assert-Equal -Expected ([Ansible.AccessToken.TokenType]::Primary)
$actual_stat.ImpersonationLevel | Assert-Equal -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]::Anonymous)
}
finally {
$dup_token.Dispose()
}
$dup_token.IsClosed | Assert-Equals -Expected $true
} finally {
$dup_token.IsClosed | Assert-Equal -Expected $true
}
finally {
$h_token.Dispose()
}
}
@ -140,16 +151,18 @@ $tests = [Ordered]@{
try {
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($dup_token)
$actual_user | Assert-Equals -Expected $current_user
$actual_user | Assert-Equal -Expected $current_user
$actual_stat = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($dup_token)
$actual_stat.TokenType | Assert-Equals -Expected ([Ansible.AccessToken.TokenType]::Impersonation)
$actual_stat.ImpersonationLevel | Assert-Equals -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]"$_")
} finally {
$actual_stat.TokenType | Assert-Equal -Expected ([Ansible.AccessToken.TokenType]::Impersonation)
$actual_stat.ImpersonationLevel | Assert-Equal -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]"$_")
}
finally {
$dup_token.Dispose()
}
}
} finally {
}
finally {
$h_token.Dispose()
}
}
@ -162,25 +175,26 @@ $tests = [Ordered]@{
$tested = $false
foreach ($h_token in [Ansible.AccessToken.TokenUtil]::EnumerateUserTokens($system_sid, "Duplicate, Impersonate, Query")) {
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token)
$actual_user | Assert-Equals -Expected $system_sid
$actual_user | Assert-Equal -Expected $system_sid
[Ansible.AccessToken.TokenUtil]::ImpersonateToken($h_token)
try {
$current_sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
$current_sid | Assert-Equals -Expected $system_sid
} finally {
$current_sid | Assert-Equal -Expected $system_sid
}
finally {
[Ansible.AccessToken.TokenUtil]::RevertToSelf()
}
$current_sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
$current_sid | Assert-Equals -Expected $current_user
$current_sid | Assert-Equal -Expected $current_user
# Will keep on looping for each SYSTEM token it can retrieve, we only want to test 1
$tested = $true
break
}
$tested | Assert-Equals -Expected $true
$tested | Assert-Equal -Expected $true
}
"Get token privileges" = {
@ -191,8 +205,8 @@ $tests = [Ordered]@{
$actual_privs = [Ansible.AccessToken.Tokenutil]::GetTokenPrivileges($h_token)
$actual_stat = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($h_token)
$actual_privs.Count | Assert-Equals -Expected $priv_info.Count
$actual_privs.Count | Assert-Equals -Expected $actual_stat.PrivilegeCount
$actual_privs.Count | Assert-Equal -Expected $priv_info.Count
$actual_privs.Count | Assert-Equal -Expected $actual_stat.PrivilegeCount
foreach ($info in $priv_info) {
$info_split = $info.Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)
@ -200,14 +214,16 @@ $tests = [Ordered]@{
$priv_enabled = $info_split[-1] -eq "Enabled"
$actual_priv = $actual_privs | Where-Object { $_.Name -eq $priv_name }
$actual_priv -eq $null | Assert-Equals -Expected $false
$actual_priv -eq $null | Assert-Equal -Expected $false
if ($priv_enabled) {
$actual_priv.Attributes.HasFlag([Ansible.AccessToken.PrivilegeAttributes]::Enabled) | Assert-Equals -Expected $true
} else {
$actual_priv.Attributes.HasFlag([Ansible.AccessToken.PrivilegeAttributes]::Disabled) | Assert-Equals -Expected $true
$actual_priv.Attributes.HasFlag([Ansible.AccessToken.PrivilegeAttributes]::Enabled) | Assert-Equal -Expected $true
}
else {
$actual_priv.Attributes.HasFlag([Ansible.AccessToken.PrivilegeAttributes]::Disabled) | Assert-Equal -Expected $true
}
}
}
} finally {
finally {
$h_token.Dispose()
}
}
@ -219,25 +235,27 @@ $tests = [Ordered]@{
$actual_priv = [Ansible.AccessToken.Tokenutil]::GetTokenPrivileges($h_token)
$actual_stat = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($h_token)
$actual_stat.TokenId.GetType().FullName | Assert-Equals -Expected "Ansible.AccessToken.Luid"
$actual_stat.AuthenticationId.GetType().FullName | Assert-Equals -Expected "Ansible.AccessToken.Luid"
$actual_stat.ExpirationTime.GetType().FullName | Assert-Equals -Expected "System.Int64"
$actual_stat.TokenId.GetType().FullName | Assert-Equal -Expected "Ansible.AccessToken.Luid"
$actual_stat.AuthenticationId.GetType().FullName | Assert-Equal -Expected "Ansible.AccessToken.Luid"
$actual_stat.ExpirationTime.GetType().FullName | Assert-Equal -Expected "System.Int64"
$actual_stat.TokenType | Assert-Equals -Expected ([Ansible.AccessToken.TokenType]::Primary)
$actual_stat.TokenType | Assert-Equal -Expected ([Ansible.AccessToken.TokenType]::Primary)
$os_version = [Version](Get-Item -LiteralPath $env:SystemRoot\System32\kernel32.dll).VersionInfo.ProductVersion
if ($os_version -lt [Version]"6.1") {
# While the token is a primary token, Server 2008 reports the SecurityImpersonationLevel for a primary token as Impersonation
$actual_stat.ImpersonationLevel | Assert-Equals -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]::Impersonation)
} else {
$actual_stat.ImpersonationLevel | Assert-Equals -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]::Anonymous)
}
$actual_stat.DynamicCharged.GetType().FullName | Assert-Equals -Expected "System.UInt32"
$actual_stat.DynamicAvailable.GetType().FullName | Assert-Equals -Expected "System.UInt32"
$actual_stat.GroupCount.GetType().FullName | Assert-Equals -Expected "System.UInt32"
$actual_stat.PrivilegeCount | Assert-Equals -Expected $actual_priv.Count
$actual_stat.ModifiedId.GetType().FullName | Assert-Equals -Expected "Ansible.AccessToken.Luid"
} finally {
$actual_stat.ImpersonationLevel | Assert-Equal -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]::Impersonation)
}
else {
$actual_stat.ImpersonationLevel | Assert-Equal -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]::Anonymous)
}
$actual_stat.DynamicCharged.GetType().FullName | Assert-Equal -Expected "System.UInt32"
$actual_stat.DynamicAvailable.GetType().FullName | Assert-Equal -Expected "System.UInt32"
$actual_stat.GroupCount.GetType().FullName | Assert-Equal -Expected "System.UInt32"
$actual_stat.PrivilegeCount | Assert-Equal -Expected $actual_priv.Count
$actual_stat.ModifiedId.GetType().FullName | Assert-Equal -Expected "Ansible.AccessToken.Luid"
}
finally {
$h_token.Dispose()
}
}
@ -246,23 +264,25 @@ $tests = [Ordered]@{
$h_token = [Ansible.AccessToken.TokenUtil]::LogonUser($test_username, $null, $test_password, "Interactive", "Default")
try {
$actual_elevation_type = [Ansible.AccessToken.TokenUtil]::GetTokenElevationType($h_token)
$actual_elevation_type | Assert-Equals -Expected ([Ansible.AccessToken.TokenElevationType]::Limited)
$actual_elevation_type | Assert-Equal -Expected ([Ansible.AccessToken.TokenElevationType]::Limited)
$actual_linked = [Ansible.AccessToken.TokenUtil]::GetTokenLinkedToken($h_token)
try {
$actual_linked.IsClosed | Assert-Equals -Expected $false
$actual_linked.IsInvalid | Assert-Equals -Expected $false
$actual_linked.IsClosed | Assert-Equal -Expected $false
$actual_linked.IsInvalid | Assert-Equal -Expected $false
$actual_elevation_type = [Ansible.AccessToken.TokenUtil]::GetTokenElevationType($actual_linked)
$actual_elevation_type | Assert-Equals -Expected ([Ansible.AccessToken.TokenElevationType]::Full)
$actual_elevation_type | Assert-Equal -Expected ([Ansible.AccessToken.TokenElevationType]::Full)
$actual_stat = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($actual_linked)
$actual_stat.TokenType | Assert-Equals -Expected ([Ansible.AccessToken.TokenType]::Impersonation)
} finally {
$actual_stat.TokenType | Assert-Equal -Expected ([Ansible.AccessToken.TokenType]::Impersonation)
}
finally {
$actual_linked.Dispose()
}
$actual_linked.IsClosed | Assert-Equals -Expected $true
} finally {
$actual_linked.IsClosed | Assert-Equal -Expected $true
}
finally {
$h_token.Dispose()
}
}
@ -286,29 +306,32 @@ $tests = [Ordered]@{
try {
$actual_linked = [Ansible.AccessToken.TokenUtil]::GetTokenLinkedToken($h_token)
try {
$actual_linked.IsClosed | Assert-Equals -Expected $false
$actual_linked.IsInvalid | Assert-Equals -Expected $false
$actual_linked.IsClosed | Assert-Equal -Expected $false
$actual_linked.IsInvalid | Assert-Equal -Expected $false
$actual_elevation_type = [Ansible.AccessToken.TokenUtil]::GetTokenElevationType($actual_linked)
$actual_elevation_type | Assert-Equals -Expected ([Ansible.AccessToken.TokenElevationType]::Full)
$actual_elevation_type | Assert-Equal -Expected ([Ansible.AccessToken.TokenElevationType]::Full)
$actual_stat = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($actual_linked)
$actual_stat.TokenType | Assert-Equals -Expected ([Ansible.AccessToken.TokenType]::Primary)
} finally {
$actual_stat.TokenType | Assert-Equal -Expected ([Ansible.AccessToken.TokenType]::Primary)
}
finally {
$actual_linked.Dispose()
}
$actual_linked.IsClosed | Assert-Equals -Expected $true
} finally {
$actual_linked.IsClosed | Assert-Equal -Expected $true
}
finally {
[Ansible.AccessToken.TokenUtil]::RevertToSelf()
}
} finally {
}
finally {
$h_token.Dispose()
}
$tested = $true
break
}
$tested | Assert-Equals -Expected $true
$tested | Assert-Equal -Expected $true
}
"Failed to get token information" = {
@ -318,13 +341,16 @@ $tests = [Ordered]@{
$failed = $false
try {
[Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token)
} catch [Ansible.AccessToken.Win32Exception] {
}
catch [Ansible.AccessToken.Win32Exception] {
$failed = $true
$_.Exception.Message | Assert-Equals -Expected "GetTokenInformation(TokenUser) failed to get buffer length (Access is denied, Win32ErrorCode 5 - 0x00000005)"
} finally {
$msg = "GetTokenInformation(TokenUser) failed to get buffer length (Access is denied, Win32ErrorCode 5 - 0x00000005)"
$_.Exception.Message | Assert-Equal -Expected $msg
}
finally {
$h_token.Dispose()
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
"Logon with valid credentials" = {
@ -333,39 +359,42 @@ $tests = [Ordered]@{
$h_token = [Ansible.AccessToken.TokenUtil]::LogonUser($test_username, $null, $test_password, "Network", "Default")
try {
$h_token.IsClosed | Assert-Equals -Expected $false
$h_token.IsInvalid | Assert-Equals -Expected $false
$h_token.IsClosed | Assert-Equal -Expected $false
$h_token.IsInvalid | Assert-Equal -Expected $false
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token)
$actual_user | Assert-Equals -Expected $expected_sid
} finally {
$actual_user | Assert-Equal -Expected $expected_sid
}
finally {
$h_token.Dispose()
}
$h_token.IsClosed | Assert-Equals -Expected $true
$h_token.IsClosed | Assert-Equal -Expected $true
}
"Logon with invalid credentials" = {
$failed = $false
try {
[Ansible.AccessToken.TokenUtil]::LogonUser("fake-user", $null, "fake-pass", "Network", "Default")
} catch [Ansible.AccessToken.Win32Exception] {
}
catch [Ansible.AccessToken.Win32Exception] {
$failed = $true
$_.Exception.Message.Contains("Failed to logon fake-user") | Assert-Equals -Expected $true
$_.Exception.Message.Contains("Win32ErrorCode 1326 - 0x0000052E)") | Assert-Equals -Expected $true
$_.Exception.Message.Contains("Failed to logon fake-user") | Assert-Equal -Expected $true
$_.Exception.Message.Contains("Win32ErrorCode 1326 - 0x0000052E)") | Assert-Equal -Expected $true
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
"Logon with invalid credential with domain account" = {
$failed = $false
try {
[Ansible.AccessToken.TokenUtil]::LogonUser("fake-user", "fake-domain", "fake-pass", "Network", "Default")
} catch [Ansible.AccessToken.Win32Exception] {
}
catch [Ansible.AccessToken.Win32Exception] {
$failed = $true
$_.Exception.Message.Contains("Failed to logon fake-domain\fake-user") | Assert-Equals -Expected $true
$_.Exception.Message.Contains("Win32ErrorCode 1326 - 0x0000052E)") | Assert-Equals -Expected $true
$_.Exception.Message.Contains("Failed to logon fake-domain\fake-user") | Assert-Equal -Expected $true
$_.Exception.Message.Contains("Win32ErrorCode 1326 - 0x0000052E)") | Assert-Equal -Expected $true
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
}

@ -5,22 +5,24 @@
$module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
Function Assert-Equals {
Function Assert-Equal {
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
[Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
)
process {
$matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) {
$Actual.Count | Assert-Equals -Expected $Expected.Count
$Actual.Count | Assert-Equal -Expected $Expected.Count
for ($i = 0; $i -lt $Actual.Count; $i++) {
$actual_value = $Actual[$i]
$expected_value = $Expected[$i]
Assert-Equals -Actual $actual_value -Expected $expected_value
Assert-Equal -Actual $actual_value -Expected $expected_value
}
$matched = $true
} else {
}
else {
$matched = $Actual -ceq $Expected
}
@ -37,6 +39,7 @@ Function Assert-Equals {
$module.Result.method = $call_stack.Position.Text
$module.FailJson("AssertionError: actual != expected")
}
}
}
# Would be great to move win_whomai out into it's own module util and share the
@ -437,101 +440,101 @@ $tests = @{
"Runas standard user" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
$stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
}
"Runas admin user" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
$stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
}
"Runas SYSTEM" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "System"
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected "S-1-5-18"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $system_integrity_sid
$stdout.LogonType | Assert-Equal -Expected "System"
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equal -Expected "S-1-5-18"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $system_integrity_sid
$with_domain = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NT AUTHORITY\System", $null, "whoami.exe")
$with_domain.StandardOut | Assert-Equals -Expected "nt authority\system`r`n"
$with_domain.StandardOut | Assert-Equal -Expected "nt authority\system`r`n"
}
"Runas LocalService" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("LocalService", $null,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Service"
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected "S-1-5-19"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $system_integrity_sid
$stdout.LogonType | Assert-Equal -Expected "Service"
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equal -Expected "S-1-5-19"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $system_integrity_sid
$with_domain = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NT AUTHORITY\LocalService", $null, "whoami.exe")
$with_domain.StandardOut | Assert-Equals -Expected "nt authority\local service`r`n"
$with_domain.StandardOut | Assert-Equal -Expected "nt authority\local service`r`n"
}
"Runas NetworkService" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NetworkService", $null,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Service"
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected "S-1-5-20"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $system_integrity_sid
$stdout.LogonType | Assert-Equal -Expected "Service"
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equal -Expected "S-1-5-20"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $system_integrity_sid
$with_domain = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NT AUTHORITY\NetworkService", $null, "whoami.exe")
$with_domain.StandardOut | Assert-Equals -Expected "nt authority\network service`r`n"
$with_domain.StandardOut | Assert-Equal -Expected "nt authority\network service`r`n"
}
"Runas without working dir set" = {
$expected = "$env:SystemRoot\system32`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null,
'powershell.exe $pwd.Path', $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"Runas with working dir set" = {
$expected = "$env:SystemRoot`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null,
'powershell.exe $pwd.Path', $env:SystemRoot, $null, "")
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"Runas without environment set" = {
$expected = "Windows_NT`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null,
'powershell.exe $env:TEST; $env:OS', $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"Runas with environment set" = {
@ -541,52 +544,53 @@ $tests = @{
}
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null,
'cmd.exe /c set', $null, $env_vars, "")
("TEST=tesTing" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
("TEST2=Testing 2" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
("OS=Windows_NT" -cnotin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
("TEST=tesTing" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equal -Expected $true
("TEST2=Testing 2" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equal -Expected $true
("OS=Windows_NT" -cnotin $actual.StandardOut.Split("`r`n")) | Assert-Equal -Expected $true
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"Runas with string stdin" = {
$expected = "input value`r`n`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null,
'powershell.exe [System.Console]::In.ReadToEnd()', $null, $null, "input value")
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"Runas with string stdin and newline" = {
$expected = "input value`r`n`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null,
'powershell.exe [System.Console]::In.ReadToEnd()', $null, $null, "input value`r`n")
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"Runas with byte stdin" = {
$expected = "input value`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null,
'powershell.exe [System.Console]::In.ReadToEnd()', $null, $null, [System.Text.Encoding]::UTF8.GetBytes("input value"))
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"Missing executable" = {
$failed = $false
try {
[Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, "fake.exe")
} catch {
}
catch {
$failed = $true
$_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "Ansible.Process.Win32Exception"
$_.Exception.InnerException.GetType().FullName | Assert-Equal -Expected "Ansible.Process.Win32Exception"
$expected = 'Exception calling "CreateProcessAsUser" with "3" argument(s): "CreateProcessWithTokenW() failed '
$expected += '(The system cannot find the file specified, Win32ErrorCode 2)"'
$_.Exception.Message | Assert-Equals -Expected $expected
$_.Exception.Message | Assert-Equal -Expected $expected
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
"CreateProcessAsUser with lpApplicationName" = {
@ -594,112 +598,114 @@ $tests = @{
$full_path = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $full_path,
"Write-Output 'abc'", $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $full_path,
"powershell.exe Write-Output 'abc'", $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcessAsUser with stderr" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $null,
"powershell.exe [System.Console]::Error.WriteLine('hi')", $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected ""
$actual.StandardError | Assert-Equals -Expected "hi`r`n"
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected ""
$actual.StandardError | Assert-Equal -Expected "hi`r`n"
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcessAsUser with exit code" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $null,
"powershell.exe exit 10", $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected ""
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 10
$actual.StandardOut | Assert-Equal -Expected ""
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 10
}
"Local account with computer name" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("$env:COMPUTERNAME\$standard_user", $become_pass,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
$stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
}
"Local account with computer as period" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser(".\$standard_user", $become_pass,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
$stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
}
"Local account with invalid password" = {
$failed = $false
try {
[Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, "incorrect", "powershell.exe Write-Output abc")
} catch {
}
catch {
$failed = $true
$_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "Ansible.AccessToken.Win32Exception"
$_.Exception.InnerException.GetType().FullName | Assert-Equal -Expected "Ansible.AccessToken.Win32Exception"
# Server 2008 has a slightly different error msg, just assert we get the error 1326
($_.Exception.Message.Contains("Win32ErrorCode 1326")) | Assert-Equals -Expected $true
($_.Exception.Message.Contains("Win32ErrorCode 1326")) | Assert-Equal -Expected $true
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
"Invalid account" = {
$failed = $false
try {
[Ansible.Become.BecomeUtil]::CreateProcessAsUser("incorrect", "incorrect", "powershell.exe Write-Output abc")
} catch {
}
catch {
$failed = $true
$_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "System.Security.Principal.IdentityNotMappedException"
$_.Exception.InnerException.GetType().FullName | Assert-Equal -Expected "System.Security.Principal.IdentityNotMappedException"
$expected = 'Exception calling "CreateProcessAsUser" with "3" argument(s): "Some or all '
$expected += 'identity references could not be translated."'
$_.Exception.Message | Assert-Equals -Expected $expected
$_.Exception.Message | Assert-Equal -Expected $expected
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
"Interactive logon with standard" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
$stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
}
"Batch logon with standard" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
"Batch", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
$stdout.LogonType | Assert-Equal -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
}
"Network logon with standard" = {
@ -709,15 +715,15 @@ $tests = @{
}
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
"Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
$stdout.LogonType | Assert-Equal -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
}
"Network with cleartext logon with standard" = {
@ -727,31 +733,31 @@ $tests = @{
}
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
"NetworkCleartext", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "NetworkCleartext"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
$stdout.LogonType | Assert-Equal -Expected "NetworkCleartext"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
}
"Logon without password with standard" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, [NullString]::Value, "WithProfile",
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
# Too unstable, there might be another process still lingering which causes become to steal instead of using
# S4U. Just don't check the type and source to verify we can become without a password
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
# $stdout.LogonType | Assert-Equals -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
# $stdout.SourceName | Assert-Equals -Expected "ansible"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
# $stdout.LogonType | Assert-Equal -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
# $stdout.SourceName | Assert-Equal -Expected "ansible"
$stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
}
"Logon without password and network type with standard" = {
@ -761,45 +767,45 @@ $tests = @{
}
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, [NullString]::Value, "WithProfile",
"Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
# Too unstable, there might be another process still lingering which causes become to steal instead of using
# S4U. Just don't check the type and source to verify we can become without a password
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
# $stdout.LogonType | Assert-Equals -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
# $stdout.SourceName | Assert-Equals -Expected "ansible"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid
# $stdout.LogonType | Assert-Equal -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
# $stdout.SourceName | Assert-Equal -Expected "ansible"
$stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
}
"Interactive logon with admin" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
$stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
}
"Batch logon with admin" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
"Batch", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
$stdout.LogonType | Assert-Equal -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
}
"Network logon with admin" = {
@ -809,15 +815,15 @@ $tests = @{
}
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
"Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
$stdout.LogonType | Assert-Equal -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
}
"Network with cleartext logon with admin" = {
@ -827,15 +833,15 @@ $tests = @{
}
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
"NetworkCleartext", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "NetworkCleartext"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
$stdout.LogonType | Assert-Equal -Expected "NetworkCleartext"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
}
"Fail to logon with null or empty password" = {
@ -847,29 +853,30 @@ $tests = @{
# string won't go the S4U route.
[Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $null, "WithProfile",
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
} catch {
}
catch {
$failed = $true
$_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "Ansible.AccessToken.Win32Exception"
$_.Exception.InnerException.GetType().FullName | Assert-Equal -Expected "Ansible.AccessToken.Win32Exception"
# Server 2008 has a slightly different error msg, just assert we get the error 1326
($_.Exception.Message.Contains("Win32ErrorCode 1326")) | Assert-Equals -Expected $true
($_.Exception.Message.Contains("Win32ErrorCode 1326")) | Assert-Equal -Expected $true
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
"Logon without password with admin" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, [NullString]::Value, "WithProfile",
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
# Too unstable, there might be another process still lingering which causes become to steal instead of using
# S4U. Just don't check the type and source to verify we can become without a password
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
# $stdout.LogonType | Assert-Equals -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
# $stdout.SourceName | Assert-Equals -Expected "ansible"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
# $stdout.LogonType | Assert-Equal -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
# $stdout.SourceName | Assert-Equal -Expected "ansible"
$stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
}
"Logon without password and network type with admin" = {
@ -879,17 +886,17 @@ $tests = @{
}
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, [NullString]::Value, "WithProfile",
"Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
# Too unstable, there might be another process still lingering which causes become to steal instead of using
# S4U. Just don't check the type and source to verify we can become without a password
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
# $stdout.LogonType | Assert-Equals -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true
# $stdout.SourceName | Assert-Equals -Expected "ansible"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
# $stdout.LogonType | Assert-Equal -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $true
# $stdout.SourceName | Assert-Equal -Expected "ansible"
$stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
}
"Logon without profile with admin" = {
@ -900,45 +907,45 @@ $tests = @{
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0,
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $false
$stdout.SourceName | Assert-Equals -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid
$stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equal -Expected $false
$stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
}
"Logon with network credentials and no profile" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("fakeuser", "fakepassword", "NetcredentialsOnly",
"NewCredentials", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "NewCredentials"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $current_user.MandatoryLabelSid.Value
$stdout.LogonType | Assert-Equal -Expected "NewCredentials"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $current_user.MandatoryLabelSid.Value
# while we didn't set WithProfile, the new process is based on the current process
$stdout.ProfileLoaded | Assert-Equals -Expected $current_user.ProfileLoaded
$stdout.SourceName | Assert-Equals -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $current_user.UserSid.Value
$stdout.ProfileLoaded | Assert-Equal -Expected $current_user.ProfileLoaded
$stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equal -Expected $current_user.UserSid.Value
}
"Logon with network credentials and with profile" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("fakeuser", "fakepassword", "NetcredentialsOnly, WithProfile",
"NewCredentials", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "NewCredentials"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $current_user.MandatoryLabelSid.Value
$stdout.ProfileLoaded | Assert-Equals -Expected $current_user.ProfileLoaded
$stdout.SourceName | Assert-Equals -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $current_user.UserSid.Value
$stdout.LogonType | Assert-Equal -Expected "NewCredentials"
$stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $current_user.MandatoryLabelSid.Value
$stdout.ProfileLoaded | Assert-Equal -Expected $current_user.ProfileLoaded
$stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equal -Expected $current_user.UserSid.Value
}
}
@ -965,7 +972,8 @@ try {
$user_obj = $adsi.Create("User", $user)
$user_obj.SetPassword($become_pass)
$user_obj.SetInfo()
} else {
}
else {
$user_obj.SetPassword($become_pass)
}
$user_obj.RefreshCache()
@ -973,13 +981,17 @@ try {
if ($user -eq $standard_user) {
$standard_user_sid = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList @($user_obj.ObjectSid.Value, 0)).Value
$group = [System.Security.Principal.WellKnownSidType]::BuiltinUsersSid
} else {
}
else {
$admin_user_sid = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList @($user_obj.ObjectSid.Value, 0)).Value
$group = [System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid
}
$group = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $group, $null).Value
[string[]]$current_groups = $user_obj.Groups() | ForEach-Object {
New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList @($_.GetType().InvokeMember("objectSID", "GetProperty", $null, $_, $null), 0)
New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList @(
$_.GetType().InvokeMember("objectSID", "GetProperty", $null, $_, $null),
0
)
}
if ($current_groups -notcontains $group) {
$group_obj = $adsi.Children | Where-Object {
@ -995,7 +1007,8 @@ try {
$test = $test_impl.Key
&$test_impl.Value
}
} finally {
}
finally {
Remove-Item -LiteralPath $tmp_dir -Force -Recurse
foreach ($user in $standard_user, $admin_user) {
$user_obj = $adsi.Children | Where-Object { $_.SchemaClassName -eq "User" -and $_.Name -eq $user }

@ -9,10 +9,13 @@ $result = @{
changed = $false
}
Function Assert-Equals($actual, $expected) {
Function Assert-Equal($actual, $expected) {
if ($actual -cne $expected) {
$call_stack = (Get-PSCallStack)[1]
$error_msg = "AssertionError:`r`nActual: `"$actual`" != Expected: `"$expected`"`r`nLine: $($call_stack.ScriptLineNumber), Method: $($call_stack.Position.Text)"
$error_msg = -join @(
"AssertionError:`r`nActual: `"$actual`" != Expected: `"$expected`"`r`nLine: "
"$($call_stack.ScriptLineNumber), Method: $($call_stack.Position.Text)"
)
Fail-Json -obj $result -message $error_msg
}
}
@ -34,15 +37,16 @@ namespace Namespace1
}
'@
$res = Add-CSharpType -References $code
Assert-Equals -actual $res -expected $null
Assert-Equal -actual $res -expected $null
$actual = [Namespace1.Class1]::GetString($false)
Assert-Equals $actual -expected "Hello World"
Assert-Equal $actual -expected "Hello World"
try {
[Namespace1.Class1]::GetString($true)
} catch {
Assert-Equals ($_.Exception.ToString().Contains("at Namespace1.Class1.GetString(Boolean error)`r`n")) -expected $true
}
catch {
Assert-Equal ($_.Exception.ToString().Contains("at Namespace1.Class1.GetString(Boolean error)`r`n")) -expected $true
}
$code_debug = @'
@ -62,17 +66,18 @@ namespace Namespace2
}
'@
$res = Add-CSharpType -References $code_debug -IncludeDebugInfo
Assert-Equals -actual $res -expected $null
Assert-Equal -actual $res -expected $null
$actual = [Namespace2.Class2]::GetString($false)
Assert-Equals $actual -expected "Hello World"
Assert-Equal $actual -expected "Hello World"
try {
[Namespace2.Class2]::GetString($true)
} catch {
}
catch {
$tmp_path = [System.IO.Path]::GetFullPath($env:TMP).ToLower()
Assert-Equals ($_.Exception.ToString().ToLower().Contains("at namespace2.class2.getstring(boolean error) in $tmp_path")) -expected $true
Assert-Equals ($_.Exception.ToString().Contains(".cs:line 10")) -expected $true
Assert-Equal ($_.Exception.ToString().ToLower().Contains("at namespace2.class2.getstring(boolean error) in $tmp_path")) -expected $true
Assert-Equal ($_.Exception.ToString().Contains(".cs:line 10")) -expected $true
}
$code_tmp = @'
@ -93,19 +98,21 @@ namespace Namespace3
'@
$tmp_path = $env:USERPROFILE
$res = Add-CSharpType -References $code_tmp -IncludeDebugInfo -TempPath $tmp_path -PassThru
Assert-Equals -actual $res.GetType().Name -expected "RuntimeAssembly"
Assert-Equals -actual $res.Location -expected ""
Assert-Equals -actual $res.GetTypes().Length -expected 1
Assert-Equals -actual $res.GetTypes()[0].Name -expected "Class3"
Assert-Equal -actual $res.GetType().Name -expected "RuntimeAssembly"
Assert-Equal -actual $res.Location -expected ""
Assert-Equal -actual $res.GetTypes().Length -expected 1
Assert-Equal -actual $res.GetTypes()[0].Name -expected "Class3"
$actual = [Namespace3.Class3]::GetString($false)
Assert-Equals $actual -expected "Hello World"
Assert-Equal $actual -expected "Hello World"
try {
[Namespace3.Class3]::GetString($true)
} catch {
Assert-Equals ($_.Exception.ToString().ToLower().Contains("at namespace3.class3.getstring(boolean error) in $($tmp_path.ToLower())")) -expected $true
Assert-Equals ($_.Exception.ToString().Contains(".cs:line 10")) -expected $true
}
catch {
$actual = $_.Exception.ToString().ToLower().Contains("at namespace3.class3.getstring(boolean error) in $($tmp_path.ToLower())")
Assert-Equal $actual -expected $true
Assert-Equal ($_.Exception.ToString().Contains(".cs:line 10")) -expected $true
}
$warning_code = @'
@ -130,15 +137,17 @@ namespace Namespace4
$failed = $false
try {
Add-CSharpType -References $warning_code
} catch {
}
catch {
$failed = $true
Assert-Equals -actual ($_.Exception.Message.Contains("error CS0219: Warning as Error: The variable 'a' is assigned but its value is never used")) -expected $true
$actual = $_.Exception.Message.Contains("error CS0219: Warning as Error: The variable 'a' is assigned but its value is never used")
Assert-Equal -actual $actual -expected $true
}
Assert-Equals -actual $failed -expected $true
Assert-Equal -actual $failed -expected $true
Add-CSharpType -References $warning_code -IgnoreWarnings
$actual = [Namespace4.Class4]::GetString($true)
Assert-Equals -actual $actual -expected "Hello World"
Assert-Equal -actual $actual -expected "Hello World"
$reference_1 = @'
using System;
@ -181,7 +190,7 @@ namespace Namespace6
Add-CSharpType -References $reference_1, $reference_2
$actual = [Namespace6.Class6]::GetString()
Assert-Equals -actual $actual -expected "Hello World"
Assert-Equal -actual $actual -expected "Hello World"
$ignored_warning = @'
using System;
@ -202,7 +211,7 @@ namespace Namespace7
'@
Add-CSharpType -References $ignored_warning
$actual = [Namespace7.Class7]::GetString()
Assert-Equals -actual $actual -expected "abc"
Assert-Equal -actual $actual -expected "abc"
$defined_symbol = @'
using System;
@ -225,7 +234,7 @@ namespace Namespace8
'@
Add-CSharpType -References $defined_symbol -CompileSymbols "SYMBOL1"
$actual = [Namespace8.Class8]::GetString()
Assert-Equals -actual $actual -expected "symbol"
Assert-Equal -actual $actual -expected "symbol"
$type_accelerator = @'
using System;
@ -245,7 +254,7 @@ namespace Namespace9
'@
Add-CSharpType -Reference $type_accelerator
$actual = [AnsibleType]::GetString()
Assert-Equals -actual $actual -expected "a"
Assert-Equal -actual $actual -expected "a"
$missing_type_class = @'
using System;
@ -266,11 +275,12 @@ namespace Namespace10
$failed = $false
try {
Add-CSharpType -Reference $missing_type_class
} catch {
}
catch {
$failed = $true
Assert-Equals -actual $_.Exception.Message -expected "Failed to find compiled class 'MissingClass' for custom TypeAccelerator."
Assert-Equal -actual $_.Exception.Message -expected "Failed to find compiled class 'MissingClass' for custom TypeAccelerator."
}
Assert-Equals -actual $failed -expected $true
Assert-Equal -actual $failed -expected $true
$arch_class = @'
using System;
@ -293,7 +303,7 @@ namespace Namespace11
}
'@
Add-CSharpType -Reference $arch_class
Assert-Equals -actual ([Namespace11.Class11]::GetIntPtrSize()) -expected ([System.IntPtr]::Size)
Assert-Equal -actual ([Namespace11.Class11]::GetIntPtrSize()) -expected ([System.IntPtr]::Size)
$lib_set = @'
using System;
@ -316,7 +326,7 @@ try {
finally {
Remove-Item -LiteralPath env:\LIB
}
Assert-Equals -actual ([Namespace12.Class12]::GetString()) -expected "b"
Assert-Equal -actual ([Namespace12.Class12]::GetString()) -expected "b"
$result.res = "success"
Exit-Json -obj $result

@ -8,22 +8,24 @@
$module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
Function Assert-Equals {
Function Assert-Equal {
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
[Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
)
process {
$matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) {
$Actual.Count | Assert-Equals -Expected $Expected.Count
$Actual.Count | Assert-Equal -Expected $Expected.Count
for ($i = 0; $i -lt $Actual.Count; $i++) {
$actual_value = $Actual[$i]
$expected_value = $Expected[$i]
Assert-Equals -Actual $actual_value -Expected $expected_value
Assert-Equal -Actual $actual_value -Expected $expected_value
}
$matched = $true
} else {
}
else {
$matched = $Actual -ceq $Expected
}
@ -40,6 +42,7 @@ Function Assert-Equals {
$module.Result.method = $call_stack.Position.Text
$module.FailJson("AssertionError: actual != expected")
}
}
}
$tmp_dir = $module.Tmpdir
@ -47,7 +50,7 @@ $tmp_dir = $module.Tmpdir
$tests = @{
"Test backup file with missing file" = {
$actual = Backup-File -path (Join-Path -Path $tmp_dir -ChildPath "missing")
$actual | Assert-Equals -Expected $null
$actual | Assert-Equal -Expected $null
}
"Test backup file in check mode" = {
@ -55,12 +58,12 @@ $tests = @{
Set-Content -LiteralPath $orig_file -Value "abc"
$actual = Backup-File -path $orig_file -WhatIf
(Test-Path -LiteralPath $actual) | Assert-Equals -Expected $false
(Test-Path -LiteralPath $actual) | Assert-Equal -Expected $false
$parent_dir = Split-Path -LiteralPath $actual
$backup_file = Split-Path -Path $actual -Leaf
$parent_dir | Assert-Equals -Expected $tmp_dir
($backup_file -match "^file-check\.txt\.$pid\.\d{8}-\d{6}\.bak$") | Assert-Equals -Expected $true
$parent_dir | Assert-Equal -Expected $tmp_dir
($backup_file -match "^file-check\.txt\.$pid\.\d{8}-\d{6}\.bak$") | Assert-Equal -Expected $true
}
"Test backup file" = {
@ -69,13 +72,13 @@ $tests = @{
Set-Content -LiteralPath $orig_file -Value $content
$actual = Backup-File -path $orig_file
(Test-Path -LiteralPath $actual) | Assert-Equals -Expected $true
(Test-Path -LiteralPath $actual) | Assert-Equal -Expected $true
$parent_dir = Split-Path -LiteralPath $actual
$backup_file = Split-Path -Path $actual -Leaf
$parent_dir | Assert-Equals -Expected $tmp_dir
($backup_file -match "^file\.txt\.$pid\.\d{8}-\d{6}\.bak$") | Assert-Equals -Expected $true
(Get-Content -LiteralPath $actual -Raw) | Assert-Equals -Expected "$content`r`n"
$parent_dir | Assert-Equal -Expected $tmp_dir
($backup_file -match "^file\.txt\.$pid\.\d{8}-\d{6}\.bak$") | Assert-Equal -Expected $true
(Get-Content -LiteralPath $actual -Raw) | Assert-Equal -Expected "$content`r`n"
}
}

@ -5,7 +5,7 @@
$ErrorActionPreference = 'Stop'
Function Assert-Equals($actual, $expected) {
Function Assert-Equal($actual, $expected) {
if ($actual -cne $expected) {
Fail-Json @{} "actual != expected`nActual: $actual`nExpected: $expected"
}
@ -41,33 +41,40 @@ foreach ($entry in $output_dict.GetEnumerator()) {
$value = $entry.Value
if ($value -is [Hashtable]) {
Assert-Equals -actual $key -expected "inner_hash_table"
Assert-Equal -actual $key -expected "inner_hash_table"
foreach ($inner_hash in $value.GetEnumerator()) {
Assert-Equals -actual $inner_hash.Name -expected $inner_hash.Value
Assert-Equal -actual $inner_hash.Name -expected $inner_hash.Value
}
} elseif ($value -is [Array] -or $value -is [System.Collections.ArrayList]) {
}
elseif ($value -is [Array] -or $value -is [System.Collections.ArrayList]) {
if ($key -eq "list_dict") {
foreach ($inner_list in $value) {
if ($inner_list -is [Hashtable]) {
foreach ($inner_list_hash in $inner_list.GetEnumerator()) {
Assert-Equals -actual $inner_list_hash.Name -expected $inner_list_hash.Value
Assert-Equal -actual $inner_list_hash.Name -expected $inner_list_hash.Value
}
}
} elseif ($inner_list -is [String]) {
elseif ($inner_list -is [String]) {
# this is not a string key so we need to keep it the same
Assert-Equals -actual $inner_list -expected "stringTwo"
} else {
Assert-Equals -actual $inner_list -expected 0
Assert-Equal -actual $inner_list -expected "stringTwo"
}
else {
Assert-Equal -actual $inner_list -expected 0
}
}
}
} elseif ($key -eq "empty_list") {
Assert-Equals -actual $value.Count -expected 0
} elseif ($key -eq "single_list") {
Assert-Equals -actual $value.Count -expected 1
} else {
elseif ($key -eq "empty_list") {
Assert-Equal -actual $value.Count -expected 0
}
elseif ($key -eq "single_list") {
Assert-Equal -actual $value.Count -expected 1
}
else {
Fail-Json -obj $result -message "invalid key found for list $key"
}
} else {
Assert-Equals -actual $key -expected $value
}
else {
Assert-Equal -actual $key -expected $value
}
}

@ -16,7 +16,7 @@ $exe_directory = Split-Path -Path $exe -Parent
$exe_filename = Split-Path -Path $exe -Leaf
$test_name = $null
Function Assert-Equals($actual, $expected) {
Function Assert-Equal($actual, $expected) {
if ($actual -cne $expected) {
Fail-Json -obj $result -message "Test $test_name failed`nActual: '$actual' != Expected: '$expected'"
}
@ -24,10 +24,10 @@ Function Assert-Equals($actual, $expected) {
$test_name = "full exe path"
$actual = Run-Command -command "`"$exe`" arg1 arg2 `"arg 3`""
Assert-Equals -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "arg1`r`narg2`r`narg 3`r`n"
Assert-Equals -actual $actual.stderr -expected ""
Assert-Equals -actual $actual.executable -expected $exe
Assert-Equal -actual $actual.rc -expected 0
Assert-Equal -actual $actual.stdout -expected "arg1`r`narg2`r`narg 3`r`n"
Assert-Equal -actual $actual.stderr -expected ""
Assert-Equal -actual $actual.executable -expected $exe
$test_name = "exe in special char dir"
$tmp_dir = Join-Path -Path $env:TEMP -ChildPath "ansible .ÅÑŚÌβŁÈ [$!@^&test(;)]"
@ -36,66 +36,70 @@ try {
$exe_special = Join-Path $tmp_dir -ChildPath "PrintArgv.exe"
Copy-Item -LiteralPath $exe -Destination $exe_special
$actual = Run-Command -command "`"$exe_special`" arg1 arg2 `"arg 3`""
} finally {
}
finally {
Remove-Item -LiteralPath $tmp_dir -Force -Recurse
}
Assert-Equals -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "arg1`r`narg2`r`narg 3`r`n"
Assert-Equals -actual $actual.stderr -expected ""
Assert-Equals -actual $actual.executable -expected $exe_special
Assert-Equal -actual $actual.rc -expected 0
Assert-Equal -actual $actual.stdout -expected "arg1`r`narg2`r`narg 3`r`n"
Assert-Equal -actual $actual.stderr -expected ""
Assert-Equal -actual $actual.executable -expected $exe_special
$test_name = "invalid exe path"
try {
$actual = Run-Command -command "C:\fakepath\$exe_filename arg1"
Fail-Json -obj $result -message "Test $test_name failed`nCommand should have thrown an exception"
} catch {
Assert-Equals -actual $_.Exception.Message -expected "Exception calling `"SearchPath`" with `"1`" argument(s): `"Could not find file 'C:\fakepath\$exe_filename'.`""
}
catch {
$expected = "Exception calling `"SearchPath`" with `"1`" argument(s): `"Could not find file 'C:\fakepath\$exe_filename'.`""
Assert-Equal -actual $_.Exception.Message -expected $expected
}
$test_name = "exe in current folder"
$actual = Run-Command -command "$exe_filename arg1" -working_directory $exe_directory
Assert-Equals -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "arg1`r`n"
Assert-Equals -actual $actual.stderr -expected ""
Assert-Equals -actual $actual.executable -expected $exe
Assert-Equal -actual $actual.rc -expected 0
Assert-Equal -actual $actual.stdout -expected "arg1`r`n"
Assert-Equal -actual $actual.stderr -expected ""
Assert-Equal -actual $actual.executable -expected $exe
$test_name = "no working directory set"
$actual = Run-Command -command "cmd.exe /c cd"
Assert-Equals -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "$($pwd.Path)`r`n"
Assert-Equals -actual $actual.stderr -expected ""
Assert-Equals -actual $actual.executable.ToUpper() -expected "$env:SystemRoot\System32\cmd.exe".ToUpper()
Assert-Equal -actual $actual.rc -expected 0
Assert-Equal -actual $actual.stdout -expected "$($pwd.Path)`r`n"
Assert-Equal -actual $actual.stderr -expected ""
Assert-Equal -actual $actual.executable.ToUpper() -expected "$env:SystemRoot\System32\cmd.exe".ToUpper()
$test_name = "working directory override"
$actual = Run-Command -command "cmd.exe /c cd" -working_directory $env:SystemRoot
Assert-Equals -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "$env:SystemRoot`r`n"
Assert-Equals -actual $actual.stderr -expected ""
Assert-Equals -actual $actual.executable.ToUpper() -expected "$env:SystemRoot\System32\cmd.exe".ToUpper()
Assert-Equal -actual $actual.rc -expected 0
Assert-Equal -actual $actual.stdout -expected "$env:SystemRoot`r`n"
Assert-Equal -actual $actual.stderr -expected ""
Assert-Equal -actual $actual.executable.ToUpper() -expected "$env:SystemRoot\System32\cmd.exe".ToUpper()
$test_name = "working directory invalid path"
try {
$actual = Run-Command -command "doesn't matter" -working_directory "invalid path here"
Fail-Json -obj $result -message "Test $test_name failed`nCommand should have thrown an exception"
} catch {
Assert-Equals -actual $_.Exception.Message -expected "invalid working directory path 'invalid path here'"
}
catch {
Assert-Equal -actual $_.Exception.Message -expected "invalid working directory path 'invalid path here'"
}
$test_name = "invalid arguments"
$actual = Run-Command -command "ipconfig.exe /asdf"
Assert-Equals -actual $actual.rc -expected 1
Assert-Equal -actual $actual.rc -expected 1
$test_name = "test stdout and stderr streams"
$actual = Run-Command -command "cmd.exe /c echo stdout && echo stderr 1>&2"
Assert-Equals -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "stdout `r`n"
Assert-Equals -actual $actual.stderr -expected "stderr `r`n"
Assert-Equal -actual $actual.rc -expected 0
Assert-Equal -actual $actual.stdout -expected "stdout `r`n"
Assert-Equal -actual $actual.stderr -expected "stderr `r`n"
$test_name = "Test UTF8 output from stdout stream"
$actual = Run-Command -command "powershell.exe -ExecutionPolicy ByPass -Command `"Write-Host '💩'`""
Assert-Equals -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "💩`n"
Assert-Equals -actual $actual.stderr -expected ""
Assert-Equal -actual $actual.rc -expected 0
Assert-Equal -actual $actual.stdout -expected "💩`n"
Assert-Equal -actual $actual.stderr -expected ""
$test_name = "test default environment variable"
Set-Item -LiteralPath env:TESTENV -Value "test"
@ -129,7 +133,7 @@ begin {
"@
$encoded_wrapper = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($wrapper))
$actual = Run-Command -command "powershell.exe -ExecutionPolicy ByPass -EncodedCommand $encoded_wrapper" -stdin "Ansible"
Assert-Equals -actual $actual.stdout -expected "Ansible`n"
Assert-Equal -actual $actual.stdout -expected "Ansible`n"
$result.data = "success"
Exit-Json -obj $result

@ -9,10 +9,13 @@ $result = @{
changed = $false
}
Function Assert-Equals($actual, $expected) {
Function Assert-Equal($actual, $expected) {
if ($actual -cne $expected) {
$call_stack = (Get-PSCallStack)[1]
$error_msg = "AssertionError:`r`nActual: `"$actual`" != Expected: `"$expected`"`r`nLine: $($call_stack.ScriptLineNumber), Method: $($call_stack.Position.Text)"
$error_msg = -join @(
"AssertionError:`r`nActual: `"$actual`" != Expected: `"$expected`"`r`nLine: "
"$($call_stack.ScriptLineNumber), Method: $($call_stack.Position.Text)"
)
Fail-Json -obj $result -message $error_msg
}
}
@ -22,7 +25,8 @@ Function Get-PagefilePath() {
$cs = Get-CimInstance -ClassName Win32_ComputerSystem
if ($cs.AutomaticManagedPagefile) {
$pagefile = "$($env:SystemRoot.Substring(0, 1)):\pagefile.sys"
} else {
}
else {
$pf = Get-CimInstance -ClassName Win32_PageFileSetting
if ($null -ne $pf) {
$pagefile = $pf[0].Name
@ -35,74 +39,76 @@ $pagefile = Get-PagefilePath
if ($pagefile) {
# Test-AnsiblePath Hidden system file
$actual = Test-AnsiblePath -Path $pagefile
Assert-Equals -actual $actual -expected $true
Assert-Equal -actual $actual -expected $true
# Get-AnsibleItem file
$actual = Get-AnsibleItem -Path $pagefile
Assert-Equals -actual $actual.FullName -expected $pagefile
Assert-Equals -actual $actual.Attributes.HasFlag([System.IO.FileAttributes]::Directory) -expected $false
Assert-Equals -actual $actual.Exists -expected $true
Assert-Equal -actual $actual.FullName -expected $pagefile
Assert-Equal -actual $actual.Attributes.HasFlag([System.IO.FileAttributes]::Directory) -expected $false
Assert-Equal -actual $actual.Exists -expected $true
}
# Test-AnsiblePath File that doesn't exist
$actual = Test-AnsiblePath -Path C:\fakefile
Assert-Equals -actual $actual -expected $false
Assert-Equal -actual $actual -expected $false
# Test-AnsiblePath Directory that doesn't exist
$actual = Test-AnsiblePath -Path C:\fakedirectory
Assert-Equals -actual $actual -expected $false
Assert-Equal -actual $actual -expected $false
# Test-AnsiblePath file in non-existant directory
$actual = Test-AnsiblePath -Path C:\fakedirectory\fakefile.txt
Assert-Equals -actual $actual -expected $false
Assert-Equal -actual $actual -expected $false
# Test-AnsiblePath Normal directory
$actual = Test-AnsiblePath -Path C:\Windows
Assert-Equals -actual $actual -expected $true
Assert-Equal -actual $actual -expected $true
# Test-AnsiblePath Normal file
$actual = Test-AnsiblePath -Path C:\Windows\System32\kernel32.dll
Assert-Equals -actual $actual -expected $true
Assert-Equal -actual $actual -expected $true
# Test-AnsiblePath fails with wildcard
$failed = $false
try {
Test-AnsiblePath -Path C:\Windows\*.exe
} catch {
}
catch {
$failed = $true
Assert-Equals -actual $_.Exception.Message -expected "Exception calling `"GetAttributes`" with `"1`" argument(s): `"Illegal characters in path.`""
Assert-Equal -actual $_.Exception.Message -expected "Exception calling `"GetAttributes`" with `"1`" argument(s): `"Illegal characters in path.`""
}
Assert-Equals -actual $failed -expected $true
Assert-Equal -actual $failed -expected $true
# Test-AnsiblePath on non file PS Provider object
$actual = Test-AnsiblePath -Path Cert:\LocalMachine\My
Assert-Equals -actual $actual -expected $true
Assert-Equal -actual $actual -expected $true
# Test-AnsiblePath on environment variable
$actual = Test-AnsiblePath -Path env:SystemDrive
Assert-Equals -actual $actual -expected $true
Assert-Equal -actual $actual -expected $true
# Test-AnsiblePath on environment variable that does not exist
$actual = Test-AnsiblePath -Path env:FakeEnvValue
Assert-Equals -actual $actual -expected $false
Assert-Equal -actual $actual -expected $false
# Get-AnsibleItem doesn't exist with -ErrorAction SilentlyContinue param
$actual = Get-AnsibleItem -Path C:\fakefile -ErrorAction SilentlyContinue
Assert-Equals -actual $actual -expected $null
Assert-Equal -actual $actual -expected $null
# Get-AnsibleItem directory
$actual = Get-AnsibleItem -Path C:\Windows
Assert-Equals -actual $actual.FullName -expected C:\Windows
Assert-Equals -actual $actual.Attributes.HasFlag([System.IO.FileAttributes]::Directory) -expected $true
Assert-Equals -actual $actual.Exists -expected $true
Assert-Equal -actual $actual.FullName -expected C:\Windows
Assert-Equal -actual $actual.Attributes.HasFlag([System.IO.FileAttributes]::Directory) -expected $true
Assert-Equal -actual $actual.Exists -expected $true
# ensure Get-AnsibleItem doesn't fail in a try/catch and -ErrorAction SilentlyContinue - stop's a trap from trapping it
try {
$actual = Get-AnsibleItem -Path C:\fakepath -ErrorAction SilentlyContinue
} catch {
}
catch {
Fail-Json -obj $result -message "this should not fire"
}
Assert-Equals -actual $actual -expected $null
Assert-Equal -actual $actual -expected $null
$result.data = "success"
Exit-Json -obj $result

@ -6,4 +6,4 @@ $params = Parse-Args $args
$path = Get-AnsibleParam -Obj $params -Name path -Type path
Exit-Json @{ path=$path }
Exit-Json @{ path = $path }

@ -25,7 +25,7 @@ New-Item -Path $folder_target -ItemType Directory | Out-Null
New-Item -Path $file_target -ItemType File | Out-Null
Set-Content -LiteralPath $file_target -Value "a"
Function Assert-Equals($actual, $expected) {
Function Assert-Equal($actual, $expected) {
if ($actual -ne $expected) {
Fail-Json @{} "actual != expected`nActual: $actual`nExpected: $expected"
}
@ -48,105 +48,109 @@ Assert-True -expression ($null -eq $no_link_result) -message "did not return nul
try {
New-Link -link_path "$path\folder-hard" -link_target $folder_target -link_type "hard"
Assert-True -expression $false -message "creation of hard link should have failed if target was a directory"
} catch {
Assert-Equals -actual $_.Exception.Message -expected "cannot set the target for a hard link to a directory"
}
catch {
Assert-Equal -actual $_.Exception.Message -expected "cannot set the target for a hard link to a directory"
}
# fail to create a junction point pointed to a file
try {
New-Link -link_path "$path\junction-fail" -link_target $file_target -link_type "junction"
Assert-True -expression $false -message "creation of junction point should have failed if target was a file"
} catch {
Assert-Equals -actual $_.Exception.Message -expected "cannot set the target for a junction point to a file"
}
catch {
Assert-Equal -actual $_.Exception.Message -expected "cannot set the target for a junction point to a file"
}
# fail to create a symbolic link with non-existent target
try {
New-Link -link_path "$path\symlink-fail" -link_target "$path\fake-folder" -link_type "link"
Assert-True -expression $false -message "creation of symbolic link should have failed if target did not exist"
} catch {
Assert-Equals -actual $_.Exception.Message -expected "link_target '$path\fake-folder' does not exist, cannot create link"
}
catch {
Assert-Equal -actual $_.Exception.Message -expected "link_target '$path\fake-folder' does not exist, cannot create link"
}
# create recursive symlink
Run-Command -command "cmd.exe /c mklink /D symlink-rel folder" -working_directory $path | Out-Null
$rel_link_result = Get-Link -link_path "$path\symlink-rel"
Assert-Equals -actual $rel_link_result.Type -expected "SymbolicLink"
Assert-Equals -actual $rel_link_result.SubstituteName -expected "folder"
Assert-Equals -actual $rel_link_result.PrintName -expected "folder"
Assert-Equals -actual $rel_link_result.TargetPath -expected "folder"
Assert-Equals -actual $rel_link_result.AbsolutePath -expected $folder_target
Assert-Equals -actual $rel_link_result.HardTargets -expected $null
Assert-Equal -actual $rel_link_result.Type -expected "SymbolicLink"
Assert-Equal -actual $rel_link_result.SubstituteName -expected "folder"
Assert-Equal -actual $rel_link_result.PrintName -expected "folder"
Assert-Equal -actual $rel_link_result.TargetPath -expected "folder"
Assert-Equal -actual $rel_link_result.AbsolutePath -expected $folder_target
Assert-Equal -actual $rel_link_result.HardTargets -expected $null
# create a symbolic file test
New-Link -link_path $symlink_file_path -link_target $file_target -link_type "link"
$file_link_result = Get-Link -link_path $symlink_file_path
Assert-Equals -actual $file_link_result.Type -expected "SymbolicLink"
Assert-Equals -actual $file_link_result.SubstituteName -expected "\??\$file_target"
Assert-Equals -actual $file_link_result.PrintName -expected $file_target
Assert-Equals -actual $file_link_result.TargetPath -expected $file_target
Assert-Equals -actual $file_link_result.AbsolutePath -expected $file_target
Assert-Equals -actual $file_link_result.HardTargets -expected $null
Assert-Equal -actual $file_link_result.Type -expected "SymbolicLink"
Assert-Equal -actual $file_link_result.SubstituteName -expected "\??\$file_target"
Assert-Equal -actual $file_link_result.PrintName -expected $file_target
Assert-Equal -actual $file_link_result.TargetPath -expected $file_target
Assert-Equal -actual $file_link_result.AbsolutePath -expected $file_target
Assert-Equal -actual $file_link_result.HardTargets -expected $null
# create a symbolic link folder test
New-Link -link_path $symlink_folder_path -link_target $folder_target -link_type "link"
$folder_link_result = Get-Link -link_path $symlink_folder_path
Assert-Equals -actual $folder_link_result.Type -expected "SymbolicLink"
Assert-Equals -actual $folder_link_result.SubstituteName -expected "\??\$folder_target"
Assert-Equals -actual $folder_link_result.PrintName -expected $folder_target
Assert-Equals -actual $folder_link_result.TargetPath -expected $folder_target
Assert-Equals -actual $folder_link_result.AbsolutePath -expected $folder_target
Assert-Equals -actual $folder_link_result.HardTargets -expected $null
Assert-Equal -actual $folder_link_result.Type -expected "SymbolicLink"
Assert-Equal -actual $folder_link_result.SubstituteName -expected "\??\$folder_target"
Assert-Equal -actual $folder_link_result.PrintName -expected $folder_target
Assert-Equal -actual $folder_link_result.TargetPath -expected $folder_target
Assert-Equal -actual $folder_link_result.AbsolutePath -expected $folder_target
Assert-Equal -actual $folder_link_result.HardTargets -expected $null
# create a junction point test
New-Link -link_path $junction_point_path -link_target $folder_target -link_type "junction"
$junction_point_result = Get-Link -link_path $junction_point_path
Assert-Equals -actual $junction_point_result.Type -expected "JunctionPoint"
Assert-Equals -actual $junction_point_result.SubstituteName -expected "\??\$folder_target"
Assert-Equals -actual $junction_point_result.PrintName -expected $folder_target
Assert-Equals -actual $junction_point_result.TargetPath -expected $folder_target
Assert-Equals -actual $junction_point_result.AbsolutePath -expected $folder_target
Assert-Equals -actual $junction_point_result.HardTargets -expected $null
Assert-Equal -actual $junction_point_result.Type -expected "JunctionPoint"
Assert-Equal -actual $junction_point_result.SubstituteName -expected "\??\$folder_target"
Assert-Equal -actual $junction_point_result.PrintName -expected $folder_target
Assert-Equal -actual $junction_point_result.TargetPath -expected $folder_target
Assert-Equal -actual $junction_point_result.AbsolutePath -expected $folder_target
Assert-Equal -actual $junction_point_result.HardTargets -expected $null
# create a hard link test
New-Link -link_path $hardlink_path -link_target $file_target -link_type "hard"
$hardlink_result = Get-Link -link_path $hardlink_path
Assert-Equals -actual $hardlink_result.Type -expected "HardLink"
Assert-Equals -actual $hardlink_result.SubstituteName -expected $null
Assert-Equals -actual $hardlink_result.PrintName -expected $null
Assert-Equals -actual $hardlink_result.TargetPath -expected $null
Assert-Equals -actual $hardlink_result.AbsolutePath -expected $null
Assert-Equal -actual $hardlink_result.Type -expected "HardLink"
Assert-Equal -actual $hardlink_result.SubstituteName -expected $null
Assert-Equal -actual $hardlink_result.PrintName -expected $null
Assert-Equal -actual $hardlink_result.TargetPath -expected $null
Assert-Equal -actual $hardlink_result.AbsolutePath -expected $null
if ($hardlink_result.HardTargets[0] -ne $hardlink_path -and $hardlink_result.HardTargets[1] -ne $hardlink_path) {
Assert-True -expression $false -message "file $hardlink_path is not a target of the hard link"
}
if ($hardlink_result.HardTargets[0] -ne $file_target -and $hardlink_result.HardTargets[1] -ne $file_target) {
Assert-True -expression $false -message "file $file_target is not a target of the hard link"
}
Assert-equals -actual (Get-Content -LiteralPath $hardlink_path -Raw) -expected (Get-Content -LiteralPath $file_target -Raw)
Assert-Equal -actual (Get-Content -LiteralPath $hardlink_path -Raw) -expected (Get-Content -LiteralPath $file_target -Raw)
# create a new hard link and verify targets go to 3
New-Link -link_path $hardlink_path_2 -link_target $file_target -link_type "hard"
$hardlink_result_2 = Get-Link -link_path $hardlink_path
Assert-True -expression ($hardlink_result_2.HardTargets.Count -eq 3) -message "did not return 3 targets for the hard link, actual $($hardlink_result_2.Targets.Count)"
$expected = "did not return 3 targets for the hard link, actual $($hardlink_result_2.Targets.Count)"
Assert-True -expression ($hardlink_result_2.HardTargets.Count -eq 3) -message $expected
# check if broken symbolic link still works
Remove-Item -LiteralPath $folder_target -Force | Out-Null
$broken_link_result = Get-Link -link_path $symlink_folder_path
Assert-Equals -actual $broken_link_result.Type -expected "SymbolicLink"
Assert-Equals -actual $broken_link_result.SubstituteName -expected "\??\$folder_target"
Assert-Equals -actual $broken_link_result.PrintName -expected $folder_target
Assert-Equals -actual $broken_link_result.TargetPath -expected $folder_target
Assert-Equals -actual $broken_link_result.AbsolutePath -expected $folder_target
Assert-Equals -actual $broken_link_result.HardTargets -expected $null
Assert-Equal -actual $broken_link_result.Type -expected "SymbolicLink"
Assert-Equal -actual $broken_link_result.SubstituteName -expected "\??\$folder_target"
Assert-Equal -actual $broken_link_result.PrintName -expected $folder_target
Assert-Equal -actual $broken_link_result.TargetPath -expected $folder_target
Assert-Equal -actual $broken_link_result.AbsolutePath -expected $folder_target
Assert-Equal -actual $broken_link_result.HardTargets -expected $null
# check if broken junction point still works
$broken_junction_result = Get-Link -link_path $junction_point_path
Assert-Equals -actual $broken_junction_result.Type -expected "JunctionPoint"
Assert-Equals -actual $broken_junction_result.SubstituteName -expected "\??\$folder_target"
Assert-Equals -actual $broken_junction_result.PrintName -expected $folder_target
Assert-Equals -actual $broken_junction_result.TargetPath -expected $folder_target
Assert-Equals -actual $broken_junction_result.AbsolutePath -expected $folder_target
Assert-Equals -actual $broken_junction_result.HardTargets -expected $null
Assert-Equal -actual $broken_junction_result.Type -expected "JunctionPoint"
Assert-Equal -actual $broken_junction_result.SubstituteName -expected "\??\$folder_target"
Assert-Equal -actual $broken_junction_result.PrintName -expected $folder_target
Assert-Equal -actual $broken_junction_result.TargetPath -expected $folder_target
Assert-Equal -actual $broken_junction_result.AbsolutePath -expected $folder_target
Assert-Equal -actual $broken_junction_result.HardTargets -expected $null
# delete file symbolic link
Remove-Link -link_path $symlink_file_path

@ -5,7 +5,7 @@
$module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
Function Assert-Equals($actual, $expected) {
Function Assert-Equal($actual, $expected) {
if ($actual -cne $expected) {
$call_stack = (Get-PSCallStack)[1]
$module.Result.actual = $actual
@ -70,20 +70,21 @@ foreach ($privilege in $total_privileges) {
$expected = $actual_privileges.$privilege
}
$actual = Get-AnsiblePrivilege -Name $privilege
Assert-Equals -actual $actual -expected $expected
Assert-Equal -actual $actual -expected $expected
}
# test c# GetAllPrivilegeInfo
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
Assert-Equals -actual $actual.GetType().Name -expected 'Dictionary`2'
Assert-Equals -actual $actual.Count -expected $actual_privileges.Count
Assert-Equal -actual $actual.GetType().Name -expected 'Dictionary`2'
Assert-Equal -actual $actual.Count -expected $actual_privileges.Count
foreach ($privilege in $total_privileges) {
if ($actual_privileges.ContainsKey($privilege)) {
$actual_value = $actual.$privilege
if ($actual_privileges.$privilege) {
Assert-Equals -actual $actual_value.HasFlag([Ansible.Privilege.PrivilegeAttributes]::Enabled) -expected $true
} else {
Assert-Equals -actual $actual_value.HasFlag([Ansible.Privilege.PrivilegeAttributes]::Enabled) -expected $false
Assert-Equal -actual $actual_value.HasFlag([Ansible.Privilege.PrivilegeAttributes]::Enabled) -expected $true
}
else {
Assert-Equal -actual $actual_value.HasFlag([Ansible.Privilege.PrivilegeAttributes]::Enabled) -expected $false
}
}
}
@ -93,19 +94,19 @@ Set-AnsiblePrivilege -Name SeUndockPrivilege -Value $false # ensure we start wi
Set-AnsiblePrivilege -Name SeUndockPrivilege -Value $true -WhatIf
$actual = Get-AnsiblePrivilege -Name SeUndockPrivilege
Assert-Equals -actual $actual -expected $false
Assert-Equal -actual $actual -expected $false
Set-AnsiblePrivilege -Name SeUndockPrivilege -Value $true
$actual = Get-AnsiblePrivilege -Name SeUndockPrivilege
Assert-Equals -actual $actual -expected $true
Assert-Equal -actual $actual -expected $true
Set-AnsiblePrivilege -Name SeUndockPrivilege -Value $false -WhatIf
$actual = Get-AnsiblePrivilege -Name SeUndockPrivilege
Assert-Equals -actual $actual -expected $true
Assert-Equal -actual $actual -expected $true
Set-AnsiblePrivilege -Name SeUndockPrivilege -Value $false
$actual = Get-AnsiblePrivilege -Name SeUndockPrivilege
Assert-Equals -actual $actual -expected $false
Assert-Equal -actual $actual -expected $false
$module.Result.data = "success"
$module.ExitJson()

@ -6,7 +6,7 @@
$params = Parse-Args $args
$sid_account = Get-AnsibleParam -obj $params -name "sid_account" -type "str" -failifempty $true
Function Assert-Equals($actual, $expected) {
Function Assert-Equal($actual, $expected) {
if ($actual -ne $expected) {
Fail-Json @{} "actual != expected`nActual: $actual`nExpected: $expected"
}
@ -39,10 +39,18 @@ $tests = @(
@{ sid = "S-1-1-0"; full_name = "Everyone"; names = @("Everyone") },
@{ sid = "S-1-5-18"; full_name = "NT AUTHORITY\SYSTEM"; names = @("NT AUTHORITY\SYSTEM", "SYSTEM") },
@{ sid = "S-1-5-20"; full_name = "NT AUTHORITY\NETWORK SERVICE"; names = @("NT AUTHORITY\NETWORK SERVICE", "NETWORK SERVICE") },
@{ sid = "$($default_admin.SID)"; full_name = "$($default_admin.FullName)"; names = @("$env:COMPUTERNAME\$($default_admin.Name)", "$($default_admin.Name)", ".\$($default_admin.Name)") },
@{
sid = "$($default_admin.SID)"
full_name = "$($default_admin.FullName)"
names = @("$env:COMPUTERNAME\$($default_admin.Name)", "$($default_admin.Name)", ".\$($default_admin.Name)")
},
# Local Groups
@{ sid = "$($default_admin_group.SID)"; full_name = "BUILTIN\$($default_admin_group.Name)"; names = @("BUILTIN\$($default_admin_group.Name)", "$($default_admin_group.Name)", ".\$($default_admin_group.Name)") }
@{
sid = "$($default_admin_group.SID)"
full_name = "BUILTIN\$($default_admin_group.Name)"
names = @("BUILTIN\$($default_admin_group.Name)", "$($default_admin_group.Name)", ".\$($default_admin_group.Name)")
}
)
# Add domain tests if the domain name has been set
@ -70,12 +78,12 @@ foreach ($test in $tests) {
$actual_account_name = Convert-FromSID -sid $test.sid
# renamed admins may have an empty FullName; skip comparison in that case
if ($test.full_name) {
Assert-Equals -actual $actual_account_name -expected $test.full_name
Assert-Equal -actual $actual_account_name -expected $test.full_name
}
foreach ($test_name in $test.names) {
$actual_sid = Convert-ToSID -account_name $test_name
Assert-Equals -actual $actual_sid -expected $test.sid
Assert-Equal -actual $actual_sid -expected $test.sid
}
}
@ -83,11 +91,11 @@ foreach ($test in $tests) {
# in the normal test suite
# Calling Convert-ToSID with a string like a SID should return that SID back
$actual = Convert-ToSID -account_name $sid_account
Assert-Equals -actual $actual -expected $sid_account
Assert-Equal -actual $actual -expected $sid_account
# Calling COnvert-ToSID with a string prefixed with .\ should return the SID
# for a user that is called that SID and not the SID passed in
$actual = Convert-ToSID -account_name ".\$sid_account"
Assert-Equals -actual ($actual -ne $sid_account) -expected $true
Assert-Equal -actual ($actual -ne $sid_account) -expected $true
Exit-Json @{ data = "success" }

@ -13,22 +13,24 @@ $module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$httpbin_host = $module.Params.httpbin_host
Function Assert-Equals {
Function Assert-Equal {
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
[Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
)
process {
$matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array] -or $Actual -is [System.Collections.IList]) {
$Actual.Count | Assert-Equals -Expected $Expected.Count
$Actual.Count | Assert-Equal -Expected $Expected.Count
for ($i = 0; $i -lt $Actual.Count; $i++) {
$actualValue = $Actual[$i]
$expectedValue = $Expected[$i]
Assert-Equals -Actual $actualValue -Expected $expectedValue
Assert-Equal -Actual $actualValue -Expected $expectedValue
}
$matched = $true
} else {
}
else {
$matched = $Actual -ceq $Expected
}
@ -46,12 +48,13 @@ Function Assert-Equals {
$module.FailJson("AssertionError: actual != expected")
}
}
}
Function Convert-StreamToString {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[Parameter(Mandatory = $true)]
[System.IO.Stream]
$Stream
)
@ -60,7 +63,8 @@ Function Convert-StreamToString {
try {
$Stream.CopyTo($ms)
[System.Text.Encoding]::UTF8.GetString($ms.ToArray())
} finally {
}
finally {
$ms.Dispose()
}
}
@ -69,50 +73,50 @@ $tests = [Ordered]@{
'GET request over http' = {
$r = Get-AnsibleWebRequest -Uri "http://$httpbin_host/get"
$r.Method | Assert-Equals -Expected 'GET'
$r.Timeout | Assert-Equals -Expected 30000
$r.UseDefaultCredentials | Assert-Equals -Expected $false
$r.Credentials | Assert-Equals -Expected $null
$r.ClientCertificates.Count | Assert-Equals -Expected 0
$r.Proxy.Credentials | Assert-Equals -Expected $null
$r.UserAgent | Assert-Equals -Expected 'ansible-httpget'
$r.Method | Assert-Equal -Expected 'GET'
$r.Timeout | Assert-Equal -Expected 30000
$r.UseDefaultCredentials | Assert-Equal -Expected $false
$r.Credentials | Assert-Equal -Expected $null
$r.ClientCertificates.Count | Assert-Equal -Expected 0
$r.Proxy.Credentials | Assert-Equal -Expected $null
$r.UserAgent | Assert-Equal -Expected 'ansible-httpget'
$actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.StatusCode | Assert-Equals -Expected 200
$Response.StatusCode | Assert-Equal -Expected 200
Convert-StreamToString -Stream $Stream
} | ConvertFrom-Json
$actual.headers.'User-Agent' | Assert-Equals -Expected 'ansible-httpget'
$actual.headers.'Host' | Assert-Equals -Expected $httpbin_host
$actual.headers.'User-Agent' | Assert-Equal -Expected 'ansible-httpget'
$actual.headers.'Host' | Assert-Equal -Expected $httpbin_host
$module.Result.msg | Assert-Equals -Expected 'OK'
$module.Result.status_code | Assert-Equals -Expected 200
$module.Result.ContainsKey('elapsed') | Assert-Equals -Expected $true
$module.Result.msg | Assert-Equal -Expected 'OK'
$module.Result.status_code | Assert-Equal -Expected 200
$module.Result.ContainsKey('elapsed') | Assert-Equal -Expected $true
}
'GET request over https' = {
# url is an alias for the -Uri parameter.
$r = Get-AnsibleWebRequest -url "https://$httpbin_host/get"
$r.Method | Assert-Equals -Expected 'GET'
$r.Timeout | Assert-Equals -Expected 30000
$r.UseDefaultCredentials | Assert-Equals -Expected $false
$r.Credentials | Assert-Equals -Expected $null
$r.ClientCertificates.Count | Assert-Equals -Expected 0
$r.Proxy.Credentials | Assert-Equals -Expected $null
$r.UserAgent | Assert-Equals -Expected 'ansible-httpget'
$r.Method | Assert-Equal -Expected 'GET'
$r.Timeout | Assert-Equal -Expected 30000
$r.UseDefaultCredentials | Assert-Equal -Expected $false
$r.Credentials | Assert-Equal -Expected $null
$r.ClientCertificates.Count | Assert-Equal -Expected 0
$r.Proxy.Credentials | Assert-Equal -Expected $null
$r.UserAgent | Assert-Equal -Expected 'ansible-httpget'
$actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.StatusCode | Assert-Equals -Expected 200
$Response.StatusCode | Assert-Equal -Expected 200
Convert-StreamToString -Stream $Stream
} | ConvertFrom-Json
$actual.headers.'User-Agent' | Assert-Equals -Expected 'ansible-httpget'
$actual.headers.'Host' | Assert-Equals -Expected $httpbin_host
$actual.headers.'User-Agent' | Assert-Equal -Expected 'ansible-httpget'
$actual.headers.'Host' | Assert-Equal -Expected $httpbin_host
}
'POST request' = {
@ -125,14 +129,14 @@ $tests = [Ordered]@{
}
$r = Get-AnsibleWebRequest @getParams
$r.Method | Assert-Equals -Expected 'POST'
$r.Timeout | Assert-Equals -Expected 30000
$r.UseDefaultCredentials | Assert-Equals -Expected $false
$r.Credentials | Assert-Equals -Expected $null
$r.ClientCertificates.Count | Assert-Equals -Expected 0
$r.Proxy.Credentials | Assert-Equals -Expected $null
$r.ContentType | Assert-Equals -Expected 'application/json'
$r.UserAgent | Assert-Equals -Expected 'ansible-httpget'
$r.Method | Assert-Equal -Expected 'POST'
$r.Timeout | Assert-Equal -Expected 30000
$r.UseDefaultCredentials | Assert-Equal -Expected $false
$r.Credentials | Assert-Equal -Expected $null
$r.ClientCertificates.Count | Assert-Equal -Expected 0
$r.Proxy.Credentials | Assert-Equal -Expected $null
$r.ContentType | Assert-Equal -Expected 'application/json'
$r.UserAgent | Assert-Equal -Expected 'ansible-httpget'
$body = New-Object -TypeName System.IO.MemoryStream -ArgumentList @(,
([System.Text.Encoding]::UTF8.GetBytes('{"foo":"bar"}'))
@ -140,13 +144,13 @@ $tests = [Ordered]@{
$actual = Invoke-WithWebRequest -Module $module -Request $r -Body $body -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.StatusCode | Assert-Equals -Expected 200
$Response.StatusCode | Assert-Equal -Expected 200
Convert-StreamToString -Stream $Stream
} | ConvertFrom-Json
$actual.headers.'User-Agent' | Assert-Equals -Expected 'ansible-httpget'
$actual.headers.'Host' | Assert-Equals -Expected $httpbin_host
$actual.data | Assert-Equals -Expected '{"foo":"bar"}'
$actual.headers.'User-Agent' | Assert-Equal -Expected 'ansible-httpget'
$actual.headers.'Host' | Assert-Equal -Expected $httpbin_host
$actual.data | Assert-Equal -Expected '{"foo":"bar"}'
}
'Safe redirection of GET' = {
@ -155,8 +159,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equals -Expected 200
$Response.ResponseUri | Assert-Equal -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equal -Expected 200
}
}
@ -166,8 +170,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equals -Expected 200
$Response.ResponseUri | Assert-Equal -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equal -Expected 200
}
}
@ -181,8 +185,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected $r.RequestUri
$Response.StatusCode | Assert-Equals -Expected 302
$Response.ResponseUri | Assert-Equal -Expected $r.RequestUri
$Response.StatusCode | Assert-Equal -Expected 302
}
}
@ -196,8 +200,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected $r.RequestUri
$Response.StatusCode | Assert-Equals -Expected 302
$Response.ResponseUri | Assert-Equal -Expected $r.RequestUri
$Response.StatusCode | Assert-Equal -Expected 302
}
}
@ -212,8 +216,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected $r.RequestUri
$Response.StatusCode | Assert-Equals -Expected 302
$Response.ResponseUri | Assert-Equal -Expected $r.RequestUri
$Response.StatusCode | Assert-Equal -Expected 302
}
}
@ -228,8 +232,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected $r.RequestUri
$Response.StatusCode | Assert-Equals -Expected 302
$Response.ResponseUri | Assert-Equal -Expected $r.RequestUri
$Response.StatusCode | Assert-Equal -Expected 302
}
}
@ -243,8 +247,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equals -Expected 200
$Response.ResponseUri | Assert-Equal -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equal -Expected 200
}
}
@ -259,8 +263,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equals -Expected 200
$Response.ResponseUri | Assert-Equal -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equal -Expected 200
}
}
@ -275,8 +279,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "https://$httpbin_host/put"
$Response.StatusCode | Assert-Equals -Expected 200
$Response.ResponseUri | Assert-Equal -Expected "https://$httpbin_host/put"
$Response.StatusCode | Assert-Equal -Expected 200
}
}
@ -290,8 +294,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -IgnoreBadResponse -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "https://$httpbin_host/relative-redirect/1"
$Response.StatusCode | Assert-Equals -Expected 302
$Response.ResponseUri | Assert-Equal -Expected "https://$httpbin_host/relative-redirect/1"
$Response.StatusCode | Assert-Equal -Expected 302
}
}
@ -305,12 +309,13 @@ $tests = [Ordered]@{
$failed = $false
try {
$null = Invoke-WithWebRequest -Module $module -Request $r -Script {}
} catch {
$_.Exception.GetType().Name | Assert-Equals -Expected 'WebException'
$_.Exception.Message | Assert-Equals -Expected 'Too many automatic redirections were attempted.'
}
catch {
$_.Exception.GetType().Name | Assert-Equal -Expected 'WebException'
$_.Exception.Message | Assert-Equal -Expected 'Too many automatic redirections were attempted.'
$failed = $true
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
'Basic auth as Credential' = {
@ -324,7 +329,7 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -IgnoreBadResponse -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.StatusCode | Assert-Equals -Expected 200
$Response.StatusCode | Assert-Equal -Expected 200
}
}
@ -340,7 +345,7 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -IgnoreBadResponse -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.StatusCode | Assert-Equals -Expected 200
$Response.StatusCode | Assert-Equal -Expected 200
}
}
@ -359,13 +364,13 @@ $tests = [Ordered]@{
$actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.StatusCode | Assert-Equals -Expected 200
$Response.StatusCode | Assert-Equal -Expected 200
Convert-StreamToString -Stream $Stream
} | ConvertFrom-Json
$actual.headers.'Testheader' | Assert-Equals -Expected 'test-header'
$actual.headers.'testingheader' | Assert-Equals -Expected 'testing_header'
$actual.Headers.'User-Agent' | Assert-Equals -Expected 'test-agent'
$actual.headers.'Testheader' | Assert-Equal -Expected 'test-header'
$actual.headers.'testingheader' | Assert-Equal -Expected 'testing_header'
$actual.Headers.'User-Agent' | Assert-Equal -Expected 'test-agent'
}
'Request with timeout' = {
@ -378,12 +383,13 @@ $tests = [Ordered]@{
$failed = $false
try {
$null = Invoke-WithWebRequest -Module $module -Request $r -Script {}
} catch {
}
catch {
$failed = $true
$_.Exception.GetType().Name | Assert-Equals -Expected WebException
$_.Exception.Message | Assert-Equals -Expected 'The operation has timed out'
$_.Exception.GetType().Name | Assert-Equal -Expected WebException
$_.Exception.Message | Assert-Equal -Expected 'The operation has timed out'
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
'Request with file URI' = {
@ -395,12 +401,12 @@ $tests = [Ordered]@{
$actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ContentLength | Assert-Equals -Expected 6
$Response.ContentLength | Assert-Equal -Expected 6
Convert-StreamToString -Stream $Stream
}
$actual | Assert-Equals -Expected "test`r`n"
$module.Result.msg | Assert-Equals -Expected "OK"
$module.Result.status_code | Assert-Equals -Expected 200
$actual | Assert-Equal -Expected "test`r`n"
$module.Result.msg | Assert-Equal -Expected "OK"
$module.Result.status_code | Assert-Equal -Expected 200
}
'Web request based on module options' = {
@ -419,9 +425,9 @@ $tests = [Ordered]@{
$spec = @{
options = @{
url = @{ type = 'str'; required = $true }
test = @{ type = 'str'; choices = 'abc', 'def'}
test = @{ type = 'str'; choices = 'abc', 'def' }
}
mutually_exclusive = @(,@('url', 'test'))
mutually_exclusive = @(, @('url', 'test'))
}
$testModule = [Ansible.Basic.AnsibleModule]::Create(@(), $spec, @(Get-AnsibleWebRequestSpec))
@ -430,10 +436,10 @@ $tests = [Ordered]@{
$actual = Invoke-WithWebRequest -Module $testModule -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "https://$httpbin_host/get"
$Response.ResponseUri | Assert-Equal -Expected "https://$httpbin_host/get"
Convert-StreamToString -Stream $Stream
} | ConvertFrom-Json
$actual.headers.'User-Agent' | Assert-Equals -Expected 'actual-agent'
$actual.headers.'User-Agent' | Assert-Equal -Expected 'actual-agent'
}
'Web request with default proxy' = {
@ -442,7 +448,7 @@ $tests = [Ordered]@{
}
$r = Get-AnsibleWebRequest @params
$null -ne $r.Proxy | Assert-Equals -Expected $true
$null -ne $r.Proxy | Assert-Equal -Expected $true
}
'Web request with no proxy' = {
@ -452,7 +458,7 @@ $tests = [Ordered]@{
}
$r = Get-AnsibleWebRequest @params
$null -eq $r.Proxy | Assert-Equals -Expected $true
$null -eq $r.Proxy | Assert-Equal -Expected $true
}
}

@ -5,22 +5,24 @@
$module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
Function Assert-Equals {
Function Assert-Equal {
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
[Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
)
process {
$matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) {
$Actual.Count | Assert-Equals -Expected $Expected.Count
$Actual.Count | Assert-Equal -Expected $Expected.Count
for ($i = 0; $i -lt $Actual.Count; $i++) {
$actual_value = $Actual[$i]
$expected_value = $Expected[$i]
Assert-Equals -Actual $actual_value -Expected $expected_value
Assert-Equal -Actual $actual_value -Expected $expected_value
}
$matched = $true
} else {
}
else {
$matched = $Actual -ceq $Expected
}
@ -37,111 +39,48 @@ Function Assert-Equals {
$module.Result.method = $call_stack.Position.Text
$module.FailJson("AssertionError: actual != expected")
}
}
Function Assert-DictionaryEquals {
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
[Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
)
$actual_keys = $Actual.Keys
$expected_keys = $Expected.Keys
$actual_keys.Count | Assert-Equals -Expected $expected_keys.Count
foreach ($actual_entry in $Actual.GetEnumerator()) {
$actual_key = $actual_entry.Key
($actual_key -cin $expected_keys) | Assert-Equals -Expected $true
$actual_value = $actual_entry.Value
$expected_value = $Expected.$actual_key
if ($actual_value -is [System.Collections.IDictionary]) {
$actual_value | Assert-DictionaryEquals -Expected $expected_value
} elseif ($actual_value -is [System.Collections.ArrayList]) {
for ($i = 0; $i -lt $actual_value.Count; $i++) {
$actual_entry = $actual_value[$i]
$expected_entry = $expected_value[$i]
if ($actual_entry -is [System.Collections.IDictionary]) {
$actual_entry | Assert-DictionaryEquals -Expected $expected_entry
} else {
Assert-Equals -Actual $actual_entry -Expected $expected_entry
}
}
} else {
Assert-Equals -Actual $actual_value -Expected $expected_value
}
}
foreach ($expected_key in $expected_keys) {
($expected_key -cin $actual_keys) | Assert-Equals -Expected $true
}
}
Function Assert-Equals {
Function Assert-DictionaryEqual {
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
[Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
)
$matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) {
$Actual.Count | Assert-Equals -Expected $Expected.Count
for ($i = 0; $i -lt $Actual.Count; $i++) {
$actual_value = $Actual[$i]
$expected_value = $Expected[$i]
Assert-Equals -Actual $actual_value -Expected $expected_value
}
$matched = $true
} else {
$matched = $Actual -ceq $Expected
}
if (-not $matched) {
if ($Actual -is [PSObject]) {
$Actual = $Actual.ToString()
}
$call_stack = (Get-PSCallStack)[1]
$module.Result.test = $test
$module.Result.actual = $Actual
$module.Result.expected = $Expected
$module.Result.line = $call_stack.ScriptLineNumber
$module.Result.method = $call_stack.Position.Text
$module.FailJson("AssertionError: actual != expected")
}
}
Function Assert-DictionaryEquals {
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
[Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
)
process {
$actual_keys = $Actual.Keys
$expected_keys = $Expected.Keys
$actual_keys.Count | Assert-Equals -Expected $expected_keys.Count
$actual_keys.Count | Assert-Equal -Expected $expected_keys.Count
foreach ($actual_entry in $Actual.GetEnumerator()) {
$actual_key = $actual_entry.Key
($actual_key -cin $expected_keys) | Assert-Equals -Expected $true
($actual_key -cin $expected_keys) | Assert-Equal -Expected $true
$actual_value = $actual_entry.Value
$expected_value = $Expected.$actual_key
if ($actual_value -is [System.Collections.IDictionary]) {
$actual_value | Assert-DictionaryEquals -Expected $expected_value
} elseif ($actual_value -is [System.Collections.ArrayList]) {
$actual_value | Assert-DictionaryEqual -Expected $expected_value
}
elseif ($actual_value -is [System.Collections.ArrayList]) {
for ($i = 0; $i -lt $actual_value.Count; $i++) {
$actual_entry = $actual_value[$i]
$expected_entry = $expected_value[$i]
if ($actual_entry -is [System.Collections.IDictionary]) {
$actual_entry | Assert-DictionaryEquals -Expected $expected_entry
} else {
Assert-Equals -Actual $actual_entry -Expected $expected_entry
$actual_entry | Assert-DictionaryEqual -Expected $expected_entry
}
else {
Assert-Equal -Actual $actual_entry -Expected $expected_entry
}
}
} else {
Assert-Equals -Actual $actual_value -Expected $expected_value
}
else {
Assert-Equal -Actual $actual_value -Expected $expected_value
}
}
foreach ($expected_key in $expected_keys) {
($expected_key -cin $actual_keys) | Assert-Equals -Expected $true
($expected_key -cin $actual_keys) | Assert-Equal -Expected $true
}
}
}
@ -150,12 +89,12 @@ $process = [Ansible.Privilege.PrivilegeUtil]::GetCurrentProcess()
$tests = @{
"Check valid privilege name" = {
$actual = [Ansible.Privilege.PrivilegeUtil]::CheckPrivilegeName("SeTcbPrivilege")
$actual | Assert-Equals -Expected $true
$actual | Assert-Equal -Expected $true
}
"Check invalid privilege name" = {
$actual = [Ansible.Privilege.PrivilegeUtil]::CheckPrivilegeName("SeFake")
$actual | Assert-Equals -Expected $false
$actual | Assert-Equal -Expected $false
}
"Disable a privilege" = {
@ -163,14 +102,14 @@ $tests = @{
[Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege") > $null
$actual = [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege")
$actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2'
$actual.Count | Assert-Equals -Expected 1
$actual.SeTimeZonePrivilege | Assert-Equals -Expected $true
$actual.GetType().Name | Assert-Equal -Expected 'Dictionary`2'
$actual.Count | Assert-Equal -Expected 1
$actual.SeTimeZonePrivilege | Assert-Equal -Expected $true
# Disable again
$actual = [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege")
$actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2'
$actual.Count | Assert-Equals -Expected 0
$actual.GetType().Name | Assert-Equal -Expected 'Dictionary`2'
$actual.Count | Assert-Equal -Expected 0
}
"Enable a privilege" = {
@ -178,43 +117,43 @@ $tests = @{
[Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege") > $null
$actual = [Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege")
$actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2'
$actual.Count | Assert-Equals -Expected 1
$actual.SeTimeZonePrivilege | Assert-Equals -Expected $false
$actual.GetType().Name | Assert-Equal -Expected 'Dictionary`2'
$actual.Count | Assert-Equal -Expected 1
$actual.SeTimeZonePrivilege | Assert-Equal -Expected $false
# Disable again
$actual = [Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege")
$actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2'
$actual.Count | Assert-Equals -Expected 0
$actual.GetType().Name | Assert-Equal -Expected 'Dictionary`2'
$actual.Count | Assert-Equal -Expected 0
}
"Disable and revert privileges" = {
$current_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$previous_state = [Ansible.Privilege.PrivilegeUtil]::DisableAllPrivileges($process)
$previous_state.GetType().Name | Assert-Equals -Expected 'Dictionary`2'
$previous_state.GetType().Name | Assert-Equal -Expected 'Dictionary`2'
foreach ($previous_state_entry in $previous_state.GetEnumerator()) {
$previous_state_entry.Value | Assert-Equals -Expected $true
$previous_state_entry.Value | Assert-Equal -Expected $true
}
# Disable again
$previous_state2 = [Ansible.Privilege.PrivilegeUtil]::DisableAllPrivileges($process)
$previous_state2.Count | Assert-Equals -Expected 0
$previous_state2.Count | Assert-Equal -Expected 0
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
foreach ($actual_entry in $actual.GetEnumerator()) {
$actual_entry.Value -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$actual_entry.Value -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
}
[Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process, $previous_state) > $null
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual | Assert-DictionaryEquals -Expected $current_state
$actual | Assert-DictionaryEqual -Expected $current_state
}
"Remove a privilege" = {
[Ansible.Privilege.PrivilegeUtil]::RemovePrivilege($process, "SeUndockPrivilege") > $null
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.ContainsKey("SeUndockPrivilege") | Assert-Equals -Expected $false
$actual.ContainsKey("SeUndockPrivilege") | Assert-Equal -Expected $false
}
"Test Enabler" = {
@ -226,30 +165,34 @@ $tests = @{
}
[Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process, $new_state) > $null
$check_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$check_state.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$check_state.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$check_state.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$check_state.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
$check_state.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
$check_state.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
# Check that strict = false won't validate privileges not held but activates the ones we want
$enabler = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $false, "SeTimeZonePrivilege", "SeShutdownPrivilege", "SeTcbPrivilege"
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$actual.ContainsKey("SeTcbPrivilege") | Assert-Equals -Expected $false
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
Assert-Equal -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
Assert-Equal -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
$actual.ContainsKey("SeTcbPrivilege") | Assert-Equal -Expected $false
# Now verify a no-op enabler will not rever back to disabled
$enabler2 = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $false, "SeTimeZonePrivilege", "SeShutdownPrivilege", "SeTcbPrivilege"
$enabler2.Dispose()
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
Assert-Equal -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
Assert-Equal -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
# Verify that when disposing the object the privileges are reverted
$enabler.Dispose()
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
}
"Test Enabler strict" = {
@ -261,56 +204,67 @@ $tests = @{
}
[Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process, $new_state) > $null
$check_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$check_state.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$check_state.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$check_state.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$check_state.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
$check_state.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
$check_state.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
# Check that strict = false won't validate privileges not held but activates the ones we want
$enabler = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $true, "SeTimeZonePrivilege", "SeShutdownPrivilege"
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
Assert-Equal -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
Assert-Equal -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
# Now verify a no-op enabler will not rever back to disabled
$enabler2 = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $true, "SeTimeZonePrivilege", "SeShutdownPrivilege"
$enabler2.Dispose()
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
Assert-Equal -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
Assert-Equal -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
# Verify that when disposing the object the privileges are reverted
$enabler.Dispose()
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
}
"Test Enabler invalid privilege" = {
$failed = $false
try {
New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $false, "SeTimeZonePrivilege", "SeFake"
} catch {
}
catch {
$failed = $true
$_.Exception.InnerException.Message | Assert-Equals -Expected "Failed to enable privilege(s) SeTimeZonePrivilege, SeFake (A specified privilege does not exist, Win32ErrorCode 1313)"
$expected = "Failed to enable privilege(s) SeTimeZonePrivilege, SeFake (A specified privilege does not exist, Win32ErrorCode 1313)"
$_.Exception.InnerException.Message | Assert-Equal -Expected $expected
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
"Test Enabler strict failure" = {
# Start disabled
[Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege") > $null
$check_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$check_state.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0
$check_state.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
$failed = $false
try {
New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $true, "SeTimeZonePrivilege", "SeTcbPrivilege"
} catch {
}
catch {
$failed = $true
$_.Exception.InnerException.Message | Assert-Equals -Expected "Failed to enable privilege(s) SeTimeZonePrivilege, SeTcbPrivilege (Not all privileges or groups referenced are assigned to the caller, Win32ErrorCode 1300)"
$expected = -join @(
"Failed to enable privilege(s) SeTimeZonePrivilege, SeTcbPrivilege "
"(Not all privileges or groups referenced are assigned to the caller, Win32ErrorCode 1300)"
)
$_.Exception.InnerException.Message | Assert-Equal -Expected $expected
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
}

@ -5,22 +5,24 @@
$module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
Function Assert-Equals {
Function Assert-Equal {
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)][AllowNull()]$Actual,
[Parameter(Mandatory=$true, Position=0)][AllowNull()]$Expected
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
)
process {
$matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) {
$Actual.Count | Assert-Equals -Expected $Expected.Count
$Actual.Count | Assert-Equal -Expected $Expected.Count
for ($i = 0; $i -lt $Actual.Count; $i++) {
$actual_value = $Actual[$i]
$expected_value = $Expected[$i]
Assert-Equals -Actual $actual_value -Expected $expected_value
Assert-Equal -Actual $actual_value -Expected $expected_value
}
$matched = $true
} else {
}
else {
$matched = $Actual -ceq $Expected
}
@ -37,122 +39,125 @@ Function Assert-Equals {
$module.Result.method = $call_stack.Position.Text
$module.FailJson("AssertionError: actual != expected")
}
}
}
$tests = @{
"ParseCommandLine empty string" = {
$expected = @((Get-Process -Id $pid).Path)
$actual = [Ansible.Process.ProcessUtil]::ParseCommandLine("")
Assert-Equals -Actual $actual -Expected $expected
Assert-Equal -Actual $actual -Expected $expected
}
"ParseCommandLine single argument" = {
$expected = @("powershell.exe")
$actual = [Ansible.Process.ProcessUtil]::ParseCommandLine("powershell.exe")
Assert-Equals -Actual $actual -Expected $expected
Assert-Equal -Actual $actual -Expected $expected
}
"ParseCommandLine multiple arguments" = {
$expected = @("powershell.exe", "-File", "C:\temp\script.ps1")
$actual = [Ansible.Process.ProcessUtil]::ParseCommandLine("powershell.exe -File C:\temp\script.ps1")
Assert-Equals -Actual $actual -Expected $expected
Assert-Equal -Actual $actual -Expected $expected
}
"ParseCommandLine comples arguments" = {
$expected = @('abc', 'd', 'ef gh', 'i\j', 'k"l', 'm\n op', 'ADDLOCAL=qr, s', 'tuv\', 'w''x', 'yz')
$actual = [Ansible.Process.ProcessUtil]::ParseCommandLine('abc d "ef gh" i\j k\"l m\\"n op" ADDLOCAL="qr, s" tuv\ w''x yz')
Assert-Equals -Actual $actual -Expected $expected
Assert-Equal -Actual $actual -Expected $expected
}
"SearchPath normal" = {
$expected = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe"
$actual = [Ansible.Process.ProcessUtil]::SearchPath("powershell.exe")
$actual | Assert-Equals -Expected $expected
$actual | Assert-Equal -Expected $expected
}
"SearchPath missing" = {
$failed = $false
try {
[Ansible.Process.ProcessUtil]::SearchPath("fake.exe")
} catch {
}
catch {
$failed = $true
$_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "System.IO.FileNotFoundException"
$_.Exception.InnerException.GetType().FullName | Assert-Equal -Expected "System.IO.FileNotFoundException"
$expected = 'Exception calling "SearchPath" with "1" argument(s): "Could not find file ''fake.exe''."'
$_.Exception.Message | Assert-Equals -Expected $expected
$_.Exception.Message | Assert-Equal -Expected $expected
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
"CreateProcess basic" = {
$actual = [Ansible.Process.ProcessUtil]::CreateProcess("whoami.exe")
$actual.GetType().FullName | Assert-Equals -Expected "Ansible.Process.Result"
$actual.StandardOut | Assert-Equals -Expected "$(&whoami.exe)`r`n"
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.GetType().FullName | Assert-Equal -Expected "Ansible.Process.Result"
$actual.StandardOut | Assert-Equal -Expected "$(&whoami.exe)`r`n"
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess stderr" = {
$actual = [Ansible.Process.ProcessUtil]::CreateProcess("powershell.exe [System.Console]::Error.WriteLine('hi')")
$actual.StandardOut | Assert-Equals -Expected ""
$actual.StandardError | Assert-Equals -Expected "hi`r`n"
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected ""
$actual.StandardError | Assert-Equal -Expected "hi`r`n"
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess exit code" = {
$actual = [Ansible.Process.ProcessUtil]::CreateProcess("powershell.exe exit 10")
$actual.StandardOut | Assert-Equals -Expected ""
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 10
$actual.StandardOut | Assert-Equal -Expected ""
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 10
}
"CreateProcess bad executable" = {
$failed = $false
try {
[Ansible.Process.ProcessUtil]::CreateProcess("fake.exe")
} catch {
}
catch {
$failed = $true
$_.Exception.InnerException.GetType().FullName | Assert-Equals -Expected "Ansible.Process.Win32Exception"
$_.Exception.InnerException.GetType().FullName | Assert-Equal -Expected "Ansible.Process.Win32Exception"
$expected = 'Exception calling "CreateProcess" with "1" argument(s): "CreateProcessW() failed '
$expected += '(The system cannot find the file specified, Win32ErrorCode 2)"'
$_.Exception.Message | Assert-Equals -Expected $expected
$_.Exception.Message | Assert-Equal -Expected $expected
}
$failed | Assert-Equals -Expected $true
$failed | Assert-Equal -Expected $true
}
"CreateProcess with unicode" = {
$actual = [Ansible.Process.ProcessUtil]::CreateProcess("cmd.exe /c echo 💩 café")
$actual.StandardOut | Assert-Equals -Expected "💩 café`r`n"
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected "💩 café`r`n"
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, "cmd.exe /c echo 💩 café", $null, $null)
$actual.StandardOut | Assert-Equals -Expected "💩 café`r`n"
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected "💩 café`r`n"
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess without working dir" = {
$expected = $pwd.Path + "`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $pwd.Path', $null, $null)
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess with working dir" = {
$expected = "C:\Windows`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $pwd.Path', "C:\Windows", $null)
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess without environment" = {
$expected = "$($env:USERNAME)`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $env:TEST; $env:USERNAME', $null, $null)
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess with environment" = {
@ -161,69 +166,70 @@ $tests = @{
TEST2 = "Testing 2"
}
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'cmd.exe /c set', $null, $env_vars)
("TEST=tesTing" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
("TEST2=Testing 2" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
("USERNAME=$($env:USERNAME)" -cnotin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
("TEST=tesTing" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equal -Expected $true
("TEST2=Testing 2" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equal -Expected $true
("USERNAME=$($env:USERNAME)" -cnotin $actual.StandardOut.Split("`r`n")) | Assert-Equal -Expected $true
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess with string stdin" = {
$expected = "input value`r`n`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
$null, $null, "input value")
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess with string stdin and newline" = {
$expected = "input value`r`n`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
$null, $null, "input value`r`n")
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess with byte stdin" = {
$expected = "input value`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
$null, $null, [System.Text.Encoding]::UTF8.GetBytes("input value"))
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess with byte stdin and newline" = {
$expected = "input value`r`n`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
$null, $null, [System.Text.Encoding]::UTF8.GetBytes("input value`r`n"))
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess with lpApplicationName" = {
$expected = "abc`r`n"
$full_path = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($full_path, "Write-Output 'abc'", $null, $null)
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($full_path, "powershell.exe Write-Output 'abc'", $null, $null)
$actual.StandardOut | Assert-Equals -Expected $expected
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
"CreateProcess with unicode and us-ascii encoding" = {
$poop = [System.Char]::ConvertFromUtf32(0xE05A) # Coverage breaks due to script parsing encoding issues with unicode chars, just use the code point instead
# Coverage breaks due to script parsing encoding issues with unicode chars, just use the code point instead
$poop = [System.Char]::ConvertFromUtf32(0xE05A)
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, "cmd.exe /c echo $poop café", $null, $null, '', 'us-ascii')
$actual.StandardOut | Assert-Equals -Expected "??? caf??`r`n"
$actual.StandardError | Assert-Equals -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0
$actual.StandardOut | Assert-Equal -Expected "??? caf??`r`n"
$actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equal -Expected 0
}
}

@ -7,9 +7,9 @@
$parsed_args = Parse-Args $args
$sleep_delay_sec = Get-AnsibleParam -obj $parsed_args -name "sleep_delay_sec" -type "int" -default 0
$fail_mode = Get-AnsibleParam -obj $parsed_args -name "fail_mode" -type "str" -default "success" -validateset "success","graceful","exception"
$fail_mode = Get-AnsibleParam -obj $parsed_args -name "fail_mode" -type "str" -default "success" -validateset "success", "graceful", "exception"
If($fail_mode -isnot [array]) {
If ($fail_mode -isnot [array]) {
$fail_mode = @($fail_mode)
}
@ -19,30 +19,29 @@ $result = @{
module_tempdir = $PSScriptRoot
}
If($sleep_delay_sec -gt 0) {
If ($sleep_delay_sec -gt 0) {
Sleep -Seconds $sleep_delay_sec
$result["slept_sec"] = $sleep_delay_sec
}
If($fail_mode -contains "leading_junk") {
If ($fail_mode -contains "leading_junk") {
Write-Output "leading junk before module output"
}
If($fail_mode -contains "graceful") {
If ($fail_mode -contains "graceful") {
Fail-Json $result "failed gracefully"
}
Try {
If($fail_mode -contains "exception") {
If ($fail_mode -contains "exception") {
Throw "failing via exception"
}
Exit-Json $result
}
Finally
{
If($fail_mode -contains "trailing_junk") {
Finally {
If ($fail_mode -contains "trailing_junk") {
Write-Output "trailing junk after module output"
}
}

@ -4,10 +4,13 @@
$ErrorActionPreference = "Stop"
Function Assert-Equals($actual, $expected) {
Function Assert-Equal($actual, $expected) {
if ($actual -cne $expected) {
$call_stack = (Get-PSCallStack)[1]
$error_msg = "AssertionError:`r`nActual: `"$actual`" != Expected: `"$expected`"`r`nLine: $($call_stack.ScriptLineNumber), Method: $($call_stack.Position.Text)"
$error_msg = -join @(
"AssertionError:`r`nActual: `"$actual`" != Expected: `"$expected`"`r`nLine: "
"$($call_stack.ScriptLineNumber), Method: $($call_stack.Position.Text)"
)
Fail-Json -obj $result -message $error_msg
}
}
@ -19,21 +22,21 @@ $result = @{
#ConvertFrom-AnsibleJso
$input_json = '{"string":"string","float":3.1415926,"dict":{"string":"string","int":1},"list":["entry 1","entry 2"],"null":null,"int":1}'
$actual = ConvertFrom-AnsibleJson -InputObject $input_json
Assert-Equals -actual $actual.GetType() -expected ([Hashtable])
Assert-Equals -actual $actual.string.GetType() -expected ([String])
Assert-Equals -actual $actual.string -expected "string"
Assert-Equals -actual $actual.int.GetType() -expected ([Int32])
Assert-Equals -actual $actual.int -expected 1
Assert-Equals -actual $actual.null -expected $null
Assert-Equals -actual $actual.float.GetType() -expected ([Decimal])
Assert-Equals -actual $actual.float -expected 3.1415926
Assert-Equals -actual $actual.list.GetType() -expected ([Object[]])
Assert-Equals -actual $actual.list.Count -expected 2
Assert-Equals -actual $actual.list[0] -expected "entry 1"
Assert-Equals -actual $actual.list[1] -expected "entry 2"
Assert-Equals -actual $actual.GetType() -expected ([Hashtable])
Assert-Equals -actual $actual.dict.string -expected "string"
Assert-Equals -actual $actual.dict.int -expected 1
Assert-Equal -actual $actual.GetType() -expected ([Hashtable])
Assert-Equal -actual $actual.string.GetType() -expected ([String])
Assert-Equal -actual $actual.string -expected "string"
Assert-Equal -actual $actual.int.GetType() -expected ([Int32])
Assert-Equal -actual $actual.int -expected 1
Assert-Equal -actual $actual.null -expected $null
Assert-Equal -actual $actual.float.GetType() -expected ([Decimal])
Assert-Equal -actual $actual.float -expected 3.1415926
Assert-Equal -actual $actual.list.GetType() -expected ([Object[]])
Assert-Equal -actual $actual.list.Count -expected 2
Assert-Equal -actual $actual.list[0] -expected "entry 1"
Assert-Equal -actual $actual.list[1] -expected "entry 2"
Assert-Equal -actual $actual.GetType() -expected ([Hashtable])
Assert-Equal -actual $actual.dict.string -expected "string"
Assert-Equal -actual $actual.dict.int -expected 1
$result.msg = "good"
Exit-Json -obj $result

@ -31,24 +31,32 @@ Function Test-ThrowException {
if ($data -eq "normal") {
Exit-Json -obj $result
} elseif ($data -eq "fail") {
}
elseif ($data -eq "fail") {
Fail-Json -obj $result -message "fail message"
} elseif ($data -eq "throw") {
}
elseif ($data -eq "throw") {
throw [ArgumentException]"module is thrown"
} elseif ($data -eq "error") {
}
elseif ($data -eq "error") {
Write-Error -Message $data
} elseif ($data -eq "cmdlet_error") {
}
elseif ($data -eq "cmdlet_error") {
Get-Item -Path "fake:\path"
} elseif ($data -eq "dotnet_exception") {
}
elseif ($data -eq "dotnet_exception") {
[System.IO.Path]::GetFullPath($null)
} elseif ($data -eq "function_throw") {
}
elseif ($data -eq "function_throw") {
Test-ThrowException
} elseif ($data -eq "proc_exit_fine") {
}
elseif ($data -eq "proc_exit_fine") {
# verifies that if no error was actually fired and we have an output, we
# don't use the RC to validate if the module failed
&cmd.exe /c exit 2
Exit-Json -obj $result
} elseif ($data -eq "proc_exit_fail") {
}
elseif ($data -eq "proc_exit_fail") {
&cmd.exe /c exit 2
Fail-Json -obj $result -message "proc_exit_fail"
}

@ -2,4 +2,4 @@
#Requires -Module Ansible.ModuleUtils.Legacy
Exit-Json @{ data="success" }
Exit-Json @{ data = "success" }

@ -2,5 +2,5 @@
#Requires -Module Ansible.ModuleUtils.Legacy
Exit-Json @{ data="success" }
Exit-Json @{ data = "success" }

@ -2,4 +2,4 @@
# POWERSHELL_COMMON
Exit-Json @{ data="success" }
Exit-Json @{ data = "success" }

@ -1,4 +1,4 @@
#!powershell
# POWERSHELL_COMMON
Exit-Json @{ data="success" }
Exit-Json @{ data = "success" }

@ -3,4 +3,4 @@
# this should fail
#Requires -Module Ansible.ModuleUtils.BogusModule
Exit-Json @{ data="success" }
Exit-Json @{ data = "success" }

@ -6,4 +6,4 @@
$o = CustomFunction
Exit-Json @{data=$o}
Exit-Json @{data = $o }

@ -1,5 +1,5 @@
Param(
[bool]$boolvariable
[bool]$boolvariable
)
Write-Output $boolvariable.GetType().FullName

@ -1,3 +1,3 @@
# Test script to create a file.
echo $null > $args[0]
Write-Output $null > $args[0]

@ -1,7 +1,6 @@
# Test script to make sure the Ansible script module works when arguments are
# passed to the script.
foreach ($i in $args)
{
foreach ($i in $args) {
Write-Host $i;
}

@ -1,7 +1,6 @@
# Test script to make sure we handle non-zero exit codes.
trap
{
trap {
Write-Error -ErrorRecord $_
exit 1;
}

@ -8,7 +8,7 @@ A list of hosts entries, delimited by '|'.
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0)][String]$Hosts
[Parameter(Mandatory = $true, Position = 0)][String]$Hosts
)
$ProgressPreference = "SilentlyContinue"

@ -8,7 +8,7 @@ A list of hosts entries, delimited by '|'.
[CmdletBinding()]
param(
[Parameter(Mandatory=$true, Position=0)][String]$Hosts
[Parameter(Mandatory = $true, Position = 0)][String]$Hosts
)
$ProgressPreference = "SilentlyContinue"
@ -26,7 +26,8 @@ $new_lines = [System.Collections.ArrayList]@()
foreach ($host_line in $hosts_file_lines) {
if ($host_line -in $hosts_entries) {
$changed = $true
} else {
}
else {
$new_lines += $host_line
}
}

@ -3,7 +3,7 @@ param (
$IsContainer
)
#Requires -Version 6
#Requires -Version 7
Set-StrictMode -Version 2.0
$ErrorActionPreference = "Stop"
@ -12,11 +12,11 @@ $ProgressPreference = 'SilentlyContinue'
Function Install-PSModule {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[Parameter(Mandatory = $true)]
[String]
$Name,
[Parameter(Mandatory=$true)]
[Parameter(Mandatory = $true)]
[Version]
$RequiredVersion
)
@ -29,7 +29,7 @@ Function Install-PSModule {
}
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
Install-PSModule -Name PSScriptAnalyzer -RequiredVersion 1.18.0
Install-PSModule -Name PSScriptAnalyzer -RequiredVersion 1.20.0
if ($IsContainer) {
# PSScriptAnalyzer contain lots of json files for the UseCompatibleCommands check. We don't use this rule so by

@ -4,12 +4,6 @@
$ErrorActionPreference = "Stop"
$WarningPreference = "Stop"
# Until https://github.com/PowerShell/PSScriptAnalyzer/issues/1217 is fixed we need to import Pester if it's
# available.
if (Get-Module -Name Pester -ListAvailable -ErrorAction SilentlyContinue) {
Import-Module -Name Pester
}
$LiteralPathRule = Import-Module -Name PSSA-PSCustomUseLiteralPath -PassThru
$LiteralPathRulePath = Join-Path -Path $LiteralPathRule.ModuleBase -ChildPath $LiteralPathRule.RootModule
@ -19,7 +13,8 @@ $PSSAParams = @{
Setting = (Join-Path -Path $PSScriptRoot -ChildPath "settings.psd1")
}
$Results = @(ForEach ($Path in $Args) {
$Results = @(
ForEach ($Path in $Args) {
$Retries = 3
Do {
@ -34,7 +29,8 @@ $Results = @(ForEach ($Path in $Args) {
}
}
Until ($Retries -le 0)
})
}
)
# Since pwsh 7.1 results that exceed depth will produce a warning which fails the process.
# Ignore warnings only for this step.

@ -1,5 +1,41 @@
@{
ExcludeRules=@(
Rules = @{
PSAvoidLongLines = @{
Enable = $true
MaximumLineLength = 160
}
PSPlaceOpenBrace = @{
Enable = $true
OnSameLine = $true
IgnoreOneLineBlock = $true
NewLineAfter = $true
}
PSPlaceCloseBrace = @{
Enable = $true
IgnoreOneLineBlock = $true
NewLineAfter = $true
NoEmptyLineBefore = $false
}
PSUseConsistentIndentation = @{
Enable = $true
IndentationSize = 4
PipelineIndentation = 'IncreaseIndentationForFirstPipeline'
Kind = 'space'
}
PSUseConsistentWhitespace = @{
Enable = $true
CheckInnerBrace = $true
CheckOpenBrace = $true
CheckOpenParen = $true
CheckOperator = $true
CheckPipe = $true
CheckPipeForRedundantWhitespace = $false
CheckSeparator = $true
CheckParameter = $false
IgnoreAssignmentOperatorInsideHashTable = $false
}
}
ExcludeRules = @(
'PSUseOutputTypeCorrectly',
'PSUseShouldProcessForStateChangingFunctions',
# We send strings as plaintext so will always come across the 3 issues
@ -8,6 +44,9 @@
'PSAvoidUsingUserNameAndPassWordParams',
# We send the module as a base64 encoded string and a BOM will cause
# issues here
'PSUseBOMForUnicodeEncodedFile'
'PSUseBOMForUnicodeEncodedFile',
# Too many false positives, there are many cases where shared utils
# invoke user defined code but not all parameters are used.
'PSReviewUnusedParameter'
)
}

@ -14,7 +14,7 @@ Function Resolve-CircularReference {
#>
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[Parameter(Mandatory = $true)]
[System.Collections.IDictionary]
$Hash
)
@ -23,22 +23,26 @@ Function Resolve-CircularReference {
$value = $Hash[$key]
if ($value -is [System.Collections.IDictionary]) {
Resolve-CircularReference -Hash $value
} elseif ($value -is [Array] -or $value -is [System.Collections.IList]) {
}
elseif ($value -is [Array] -or $value -is [System.Collections.IList]) {
$values = @(foreach ($v in $value) {
if ($v -is [System.Collections.IDictionary]) {
Resolve-CircularReference -Hash $v
}
,$v
, $v
})
$Hash[$key] = $values
} elseif ($value -is [DateTime]) {
}
elseif ($value -is [DateTime]) {
$Hash[$key] = $value.ToString("yyyy-MM-dd")
} elseif ($value -is [delegate]) {
}
elseif ($value -is [delegate]) {
# Type can be set to a delegate function which defines it's own type. For the documentation we just
# reflection that as raw
if ($key -eq 'type') {
$Hash[$key] = 'raw'
} else {
}
else {
$Hash[$key] = $value.ToString() # Shouldn't ever happen but just in case.
}
}

@ -9,7 +9,8 @@ param (
$Path
)
$stubInfo = @(foreach ($sourcePath in $Path) {
$stubInfo = @(
foreach ($sourcePath in $Path) {
# Default is to just no lines for missing files
[Collections.Generic.HashSet[int]]$lines = @()
@ -33,6 +34,7 @@ $stubInfo = @(foreach ($sourcePath in $Path) {
Path = $sourcePath
Lines = $lines
}
})
}
)
ConvertTo-Json -InputObject $stubInfo -Depth 2 -Compress

@ -66,28 +66,24 @@ Param (
[switch]$EnableCredSSP
)
Function Write-Log
{
Function Write-ProgressLog {
$Message = $args[0]
Write-EventLog -LogName Application -Source $EventSource -EntryType Information -EventId 1 -Message $Message
}
Function Write-VerboseLog
{
Function Write-VerboseLog {
$Message = $args[0]
Write-Verbose $Message
Write-Log $Message
Write-ProgressLog $Message
}
Function Write-HostLog
{
Function Write-HostLog {
$Message = $args[0]
Write-Output $Message
Write-Log $Message
Write-ProgressLog $Message
}
Function New-LegacySelfSignedCert
{
Function New-LegacySelfSignedCert {
Param (
[string]$SubjectName,
[int]$ValidDays = 1095
@ -129,10 +125,9 @@ Function New-LegacySelfSignedCert
$AlternativeName += $hostFQDN
$IAlternativeNames = New-Object -ComObject X509Enrollment.CAlternativeNames
foreach ($AN in $AlternativeName)
{
foreach ($AN in $AlternativeName) {
$AltName = New-Object -ComObject X509Enrollment.CAlternativeName
$AltName.InitializeFromString(0x3,$AN)
$AltName.InitializeFromString(0x3, $AN)
$IAlternativeNames.Add($AltName)
}
@ -162,8 +157,7 @@ Function New-LegacySelfSignedCert
return $parsed_cert.Thumbprint
}
Function Enable-GlobalHttpFirewallAccess
{
Function Enable-GlobalHttpFirewallAccess {
Write-Verbose "Forcing global HTTP firewall access"
# this is a fairly naive implementation; could be more sophisticated about rule matching/collapsing
$fw = New-Object -ComObject HNetCfg.FWPolicy2
@ -217,80 +211,71 @@ Function Enable-GlobalHttpFirewallAccess
}
# Setup error handling.
Trap
{
Trap {
$_
Exit 1
}
$ErrorActionPreference = "Stop"
# Get the ID and security principal of the current user account
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal = new-object System.Security.Principal.WindowsPrincipal($myWindowsID)
# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
# Check to see if we are currently running "as Administrator"
if (-Not $myWindowsPrincipal.IsInRole($adminRole))
{
if (-Not $myWindowsPrincipal.IsInRole($adminRole)) {
Write-Output "ERROR: You need elevated Administrator privileges in order to run this script."
Write-Output " Start Windows PowerShell by using the Run as Administrator option."
Exit 2
}
$EventSource = $MyInvocation.MyCommand.Name
If (-Not $EventSource)
{
If (-Not $EventSource) {
$EventSource = "Powershell CLI"
}
If ([System.Diagnostics.EventLog]::Exists('Application') -eq $False -or [System.Diagnostics.EventLog]::SourceExists($EventSource) -eq $False)
{
If ([System.Diagnostics.EventLog]::Exists('Application') -eq $False -or [System.Diagnostics.EventLog]::SourceExists($EventSource) -eq $False) {
New-EventLog -LogName Application -Source $EventSource
}
# Detect PowerShell version.
If ($PSVersionTable.PSVersion.Major -lt 3)
{
Write-Log "PowerShell version 3 or higher is required."
If ($PSVersionTable.PSVersion.Major -lt 3) {
Write-ProgressLog "PowerShell version 3 or higher is required."
Throw "PowerShell version 3 or higher is required."
}
# Find and start the WinRM service.
Write-Verbose "Verifying WinRM service."
If (!(Get-Service "WinRM"))
{
Write-Log "Unable to find the WinRM service."
If (!(Get-Service "WinRM")) {
Write-ProgressLog "Unable to find the WinRM service."
Throw "Unable to find the WinRM service."
}
ElseIf ((Get-Service "WinRM").Status -ne "Running")
{
ElseIf ((Get-Service "WinRM").Status -ne "Running") {
Write-Verbose "Setting WinRM service to start automatically on boot."
Set-Service -Name "WinRM" -StartupType Automatic
Write-Log "Set WinRM service to start automatically on boot."
Write-ProgressLog "Set WinRM service to start automatically on boot."
Write-Verbose "Starting WinRM service."
Start-Service -Name "WinRM" -ErrorAction Stop
Write-Log "Started WinRM service."
Write-ProgressLog "Started WinRM service."
}
# WinRM should be running; check that we have a PS session config.
If (!(Get-PSSessionConfiguration -Verbose:$false) -or (!(Get-ChildItem WSMan:\localhost\Listener)))
{
If (!(Get-PSSessionConfiguration -Verbose:$false) -or (!(Get-ChildItem WSMan:\localhost\Listener))) {
If ($SkipNetworkProfileCheck) {
Write-Verbose "Enabling PS Remoting without checking Network profile."
Enable-PSRemoting -SkipNetworkProfileCheck -Force -ErrorAction Stop
Write-Log "Enabled PS Remoting without checking Network profile."
Write-ProgressLog "Enabled PS Remoting without checking Network profile."
}
Else {
Write-Verbose "Enabling PS Remoting."
Enable-PSRemoting -Force -ErrorAction Stop
Write-Log "Enabled PS Remoting."
Write-ProgressLog "Enabled PS Remoting."
}
}
Else
{
Else {
Write-Verbose "PS Remoting is already enabled."
}
@ -310,8 +295,7 @@ if ($token_value -ne 1) {
# Make sure there is a SSL listener.
$listeners = Get-ChildItem WSMan:\localhost\Listener
If (!($listeners | Where-Object {$_.Keys -like "TRANSPORT=HTTPS"}))
{
If (!($listeners | Where-Object { $_.Keys -like "TRANSPORT=HTTPS" })) {
# We cannot use New-SelfSignedCertificate on 2012R2 and earlier
$thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays
Write-HostLog "Self-signed SSL certificate generated; thumbprint: $thumbprint"
@ -329,15 +313,13 @@ If (!($listeners | Where-Object {$_.Keys -like "TRANSPORT=HTTPS"}))
Write-Verbose "Enabling SSL listener."
New-WSManInstance -ResourceURI 'winrm/config/Listener' -SelectorSet $selectorset -ValueSet $valueset
Write-Log "Enabled SSL listener."
Write-ProgressLog "Enabled SSL listener."
}
Else
{
Else {
Write-Verbose "SSL listener is already active."
# Force a new SSL cert on Listener if the $ForceNewSSLCert
If ($ForceNewSSLCert)
{
If ($ForceNewSSLCert) {
# We cannot use New-SelfSignedCertificate on 2012R2 and earlier
$thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays
@ -361,45 +343,37 @@ Else
}
# Check for basic authentication.
$basicAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object {$_.Name -eq "Basic"}
$basicAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object { $_.Name -eq "Basic" }
If ($DisableBasicAuth)
{
If (($basicAuthSetting.Value) -eq $true)
{
If ($DisableBasicAuth) {
If (($basicAuthSetting.Value) -eq $true) {
Write-Verbose "Disabling basic auth support."
Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $false
Write-Log "Disabled basic auth support."
Write-ProgressLog "Disabled basic auth support."
}
Else
{
Else {
Write-Verbose "Basic auth is already disabled."
}
}
Else
{
If (($basicAuthSetting.Value) -eq $false)
{
Else {
If (($basicAuthSetting.Value) -eq $false) {
Write-Verbose "Enabling basic auth support."
Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $true
Write-Log "Enabled basic auth support."
Write-ProgressLog "Enabled basic auth support."
}
Else
{
Else {
Write-Verbose "Basic auth is already enabled."
}
}
# If EnableCredSSP if set to true
If ($EnableCredSSP)
{
If ($EnableCredSSP) {
# Check for CredSSP authentication
$credsspAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object {$_.Name -eq "CredSSP"}
If (($credsspAuthSetting.Value) -eq $false)
{
$credsspAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object { $_.Name -eq "CredSSP" }
If (($credsspAuthSetting.Value) -eq $false) {
Write-Verbose "Enabling CredSSP auth support."
Enable-WSManCredSSP -role server -Force
Write-Log "Enabled CredSSP auth support."
Write-ProgressLog "Enabled CredSSP auth support."
}
}
@ -410,44 +384,37 @@ If ($GlobalHttpFirewallAccess) {
# Configure firewall to allow WinRM HTTPS connections.
$fwtest1 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS"
$fwtest2 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS" profile=any
If ($fwtest1.count -lt 5)
{
If ($fwtest1.count -lt 5) {
Write-Verbose "Adding firewall rule to allow WinRM HTTPS."
netsh advfirewall firewall add rule profile=any name="Allow WinRM HTTPS" dir=in localport=5986 protocol=TCP action=allow
Write-Log "Added firewall rule to allow WinRM HTTPS."
Write-ProgressLog "Added firewall rule to allow WinRM HTTPS."
}
ElseIf (($fwtest1.count -ge 5) -and ($fwtest2.count -lt 5))
{
ElseIf (($fwtest1.count -ge 5) -and ($fwtest2.count -lt 5)) {
Write-Verbose "Updating firewall rule to allow WinRM HTTPS for any profile."
netsh advfirewall firewall set rule name="Allow WinRM HTTPS" new profile=any
Write-Log "Updated firewall rule to allow WinRM HTTPS for any profile."
Write-ProgressLog "Updated firewall rule to allow WinRM HTTPS for any profile."
}
Else
{
Else {
Write-Verbose "Firewall rule already exists to allow WinRM HTTPS."
}
# Test a remoting connection to localhost, which should work.
$httpResult = Invoke-Command -ComputerName "localhost" -ScriptBlock {$env:COMPUTERNAME} -ErrorVariable httpError -ErrorAction SilentlyContinue
$httpResult = Invoke-Command -ComputerName "localhost" -ScriptBlock { $using:env:COMPUTERNAME } -ErrorVariable httpError -ErrorAction SilentlyContinue
$httpsOptions = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$httpsResult = New-PSSession -UseSSL -ComputerName "localhost" -SessionOption $httpsOptions -ErrorVariable httpsError -ErrorAction SilentlyContinue
If ($httpResult -and $httpsResult)
{
If ($httpResult -and $httpsResult) {
Write-Verbose "HTTP: Enabled | HTTPS: Enabled"
}
ElseIf ($httpsResult -and !$httpResult)
{
ElseIf ($httpsResult -and !$httpResult) {
Write-Verbose "HTTP: Disabled | HTTPS: Enabled"
}
ElseIf ($httpResult -and !$httpsResult)
{
ElseIf ($httpResult -and !$httpsResult) {
Write-Verbose "HTTP: Enabled | HTTPS: Disabled"
}
Else
{
Write-Log "Unable to establish an HTTP or HTTPS remoting session."
Else {
Write-ProgressLog "Unable to establish an HTTP or HTTPS remoting session."
Throw "Unable to establish an HTTP or HTTPS remoting session."
}
Write-VerboseLog "PS Remoting has been successfully configured for Ansible."

@ -165,7 +165,6 @@ test/integration/targets/win_exec_wrapper/tasks/main.yml no-smart-quotes # We a
test/integration/targets/win_fetch/tasks/main.yml no-smart-quotes # We are explictly testing smart quotes in the file name to fetch
test/integration/targets/win_module_utils/library/legacy_only_new_way_win_line_ending.ps1 line-endings # Explicitly tests that we still work with Windows line endings
test/integration/targets/win_module_utils/library/legacy_only_old_way_win_line_ending.ps1 line-endings # Explicitly tests that we still work with Windows line endings
test/integration/targets/win_script/files/test_script_creates_file.ps1 pslint:PSAvoidUsingCmdletAliases
test/integration/targets/win_script/files/test_script.ps1 pslint:PSAvoidUsingWriteHost # Keep
test/integration/targets/win_script/files/test_script_removes_file.ps1 pslint:PSCustomUseLiteralPath
test/integration/targets/win_script/files/test_script_with_args.ps1 pslint:PSAvoidUsingWriteHost # Keep
@ -196,16 +195,26 @@ test/support/network-integration/collections/ansible_collections/cisco/ios/plugi
test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/cliconf/vyos.py pylint:arguments-renamed
test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_command.py pep8:E231
test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_command.py pylint:disallowed-name
test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/module_utils/WebRequest.psm1 pslint!skip
test/support/windows-integration/collections/ansible_collections/ansible/windows/plugins/modules/win_uri.ps1 pslint!skip
test/support/windows-integration/plugins/modules/async_status.ps1 pslint!skip
test/support/windows-integration/plugins/modules/setup.ps1 pslint!skip
test/support/windows-integration/plugins/modules/slurp.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_acl.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_certificate_store.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_command.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_copy.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_dsc.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_feature.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_find.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_file.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_get_url.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_lineinfile.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_regedit.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_shell.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_stat.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_tempfile.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_user_right.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_user.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_wait_for.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_whoami.ps1 pslint!skip
test/units/executor/test_play_iterator.py pylint:disallowed-name
test/units/modules/test_apt.py pylint:disallowed-name
test/units/module_utils/basic/test_deprecate_warn.py pylint:ansible-deprecated-no-version

@ -1,129 +0,0 @@
#!powershell
# Copyright: 2019, rnsc(@rnsc) <github@rnsc.be>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt
#AnsibleRequires -CSharpUtil Ansible.Basic
#AnsibleRequires -OSVersion 6.3
$spec = @{
options = @{
drive_letter = @{ type = "str"; required = $true }
state = @{ type = "str"; choices = "absent", "present"; default = "present"; }
settings = @{
type = "dict"
required = $false
options = @{
minimum_file_size = @{ type = "int"; default = 32768 }
minimum_file_age_days = @{ type = "int"; default = 2 }
no_compress = @{ type = "bool"; required = $false; default = $false }
optimize_in_use_files = @{ type = "bool"; required = $false; default = $false }
verify = @{ type = "bool"; required = $false; default = $false }
}
}
}
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$drive_letter = $module.Params.drive_letter
$state = $module.Params.state
$settings = $module.Params.settings
$module.Result.changed = $false
$module.Result.reboot_required = $false
$module.Result.msg = ""
function Set-DataDeduplication($volume, $state, $settings, $dedup_job) {
$current_state = 'absent'
try {
$dedup_info = Get-DedupVolume -Volume "$($volume.DriveLetter):"
} catch {
$dedup_info = $null
}
if ($dedup_info.Enabled) {
$current_state = 'present'
}
if ( $state -ne $current_state ) {
if( -not $module.CheckMode) {
if($state -eq 'present') {
# Enable-DedupVolume -Volume <String>
Enable-DedupVolume -Volume "$($volume.DriveLetter):"
} elseif ($state -eq 'absent') {
Disable-DedupVolume -Volume "$($volume.DriveLetter):"
}
}
$module.Result.changed = $true
}
if ($state -eq 'present') {
if ($null -ne $settings) {
Set-DataDedupJobSettings -volume $volume -settings $settings
}
}
}
function Set-DataDedupJobSettings ($volume, $settings) {
try {
$dedup_info = Get-DedupVolume -Volume "$($volume.DriveLetter):"
} catch {
$dedup_info = $null
}
ForEach ($key in $settings.keys) {
# See Microsoft documentation:
# https://docs.microsoft.com/en-us/powershell/module/deduplication/set-dedupvolume?view=win10-ps
$update_key = $key
$update_value = $settings.$($key)
# Transform Ansible style options to Powershell params
$update_key = $update_key -replace('_', '')
if ($update_key -eq "MinimumFileSize" -and $update_value -lt 32768) {
$update_value = 32768
}
$current_value = ($dedup_info | Select-Object -ExpandProperty $update_key)
if ($update_value -ne $current_value) {
$command_param = @{
$($update_key) = $update_value
}
# Set-DedupVolume -Volume <String>`
# -NoCompress <bool> `
# -MinimumFileAgeDays <UInt32> `
# -MinimumFileSize <UInt32> (minimum 32768)
if( -not $module.CheckMode ) {
Set-DedupVolume -Volume "$($volume.DriveLetter):" @command_param
}
$module.Result.changed = $true
}
}
}
# Install required feature
$feature_name = "FS-Data-Deduplication"
if( -not $module.CheckMode) {
$feature = Install-WindowsFeature -Name $feature_name
if ($feature.RestartNeeded -eq 'Yes') {
$module.Result.reboot_required = $true
$module.FailJson("$feature_name was installed but requires Windows to be rebooted to work.")
}
}
$volume = Get-Volume -DriveLetter $drive_letter
Set-DataDeduplication -volume $volume -state $state -settings $settings -dedup_job $dedup_job
$module.ExitJson()

@ -1,87 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: 2019, rnsc(@rnsc) <github@rnsc.be>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: win_data_deduplication
version_added: "2.10"
short_description: Module to enable Data Deduplication on a volume.
description:
- This module can be used to enable Data Deduplication on a Windows volume.
- The module will install the FS-Data-Deduplication feature (a reboot will be necessary).
options:
drive_letter:
description:
- Windows drive letter on which to enable data deduplication.
required: yes
type: str
state:
description:
- Wether to enable or disable data deduplication on the selected volume.
default: present
type: str
choices: [ present, absent ]
settings:
description:
- Dictionary of settings to pass to the Set-DedupVolume powershell command.
type: dict
suboptions:
minimum_file_size:
description:
- Minimum file size you want to target for deduplication.
- It will default to 32768 if not defined or if the value is less than 32768.
type: int
default: 32768
minimum_file_age_days:
description:
- Minimum file age you want to target for deduplication.
type: int
default: 2
no_compress:
description:
- Wether you want to enabled filesystem compression or not.
type: bool
default: no
optimize_in_use_files:
description:
- Indicates that the server attempts to optimize currently open files.
type: bool
default: no
verify:
description:
- Indicates whether the deduplication engine performs a byte-for-byte verification for each duplicate chunk
that optimization creates, rather than relying on a cryptographically strong hash.
- This option is not recommend.
- Setting this parameter to True can degrade optimization performance.
type: bool
default: no
author:
- rnsc (@rnsc)
'''
EXAMPLES = r'''
- name: Enable Data Deduplication on D
win_data_deduplication:
drive_letter: 'D'
state: present
- name: Enable Data Deduplication on D
win_data_deduplication:
drive_letter: 'D'
state: present
settings:
no_compress: true
minimum_file_age_days: 1
minimum_file_size: 0
'''
RETURN = r'''
#
'''

@ -1,398 +0,0 @@
#!powershell
# Copyright: (c) 2015, Trond Hindenes <trond@hindenes.com>, and others
# Copyright: (c) 2017, 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 -Version 5
Function ConvertTo-ArgSpecType {
<#
.SYNOPSIS
Converts the DSC parameter type to the arg spec type required for Ansible.
#>
param(
[Parameter(Mandatory=$true)][String]$CimType
)
$arg_type = switch($CimType) {
Boolean { "bool" }
Char16 { [Func[[Object], [Char]]]{ [System.Char]::Parse($args[0].ToString()) } }
DateTime { [Func[[Object], [DateTime]]]{ [System.DateTime]($args[0].ToString()) } }
Instance { "dict" }
Real32 { "float" }
Real64 { [Func[[Object], [Double]]]{ [System.Double]::Parse($args[0].ToString()) } }
Reference { "dict" }
SInt16 { [Func[[Object], [Int16]]]{ [System.Int16]::Parse($args[0].ToString()) } }
SInt32 { "int" }
SInt64 { [Func[[Object], [Int64]]]{ [System.Int64]::Parse($args[0].ToString()) } }
SInt8 { [Func[[Object], [SByte]]]{ [System.SByte]::Parse($args[0].ToString()) } }
String { "str" }
UInt16 { [Func[[Object], [UInt16]]]{ [System.UInt16]::Parse($args[0].ToString()) } }
UInt32 { [Func[[Object], [UInt32]]]{ [System.UInt32]::Parse($args[0].ToString()) } }
UInt64 { [Func[[Object], [UInt64]]]{ [System.UInt64]::Parse($args[0].ToString()) } }
UInt8 { [Func[[Object], [Byte]]]{ [System.Byte]::Parse($args[0].ToString()) } }
Unknown { "raw" }
default { "raw" }
}
return $arg_type
}
Function Get-DscCimClassProperties {
<#
.SYNOPSIS
Get's a list of CimProperties of a CIM Class. It filters out any magic or
read only properties that we don't need to know about.
#>
param([Parameter(Mandatory=$true)][String]$ClassName)
$resource = Get-CimClass -ClassName $ClassName -Namespace root\Microsoft\Windows\DesiredStateConfiguration
# Filter out any magic properties that are used internally on an OMI_BaseResource
# https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/DscSupport/CimDSCParser.cs#L1203
$magic_properties = @("ResourceId", "SourceInfo", "ModuleName", "ModuleVersion", "ConfigurationName")
$properties = $resource.CimClassProperties | Where-Object {
($resource.CimSuperClassName -ne "OMI_BaseResource" -or $_.Name -notin $magic_properties) -and
-not $_.Flags.HasFlag([Microsoft.Management.Infrastructure.CimFlags]::ReadOnly)
}
return ,$properties
}
Function Add-PropertyOption {
<#
.SYNOPSIS
Adds the spec for the property type to the existing module specification.
#>
param(
[Parameter(Mandatory=$true)][Hashtable]$Spec,
[Parameter(Mandatory=$true)]
[Microsoft.Management.Infrastructure.CimPropertyDeclaration]$Property
)
$option = @{
required = $false
}
$property_name = $Property.Name
$property_type = $Property.CimType.ToString()
if ($Property.Flags.HasFlag([Microsoft.Management.Infrastructure.CimFlags]::Key) -or
$Property.Flags.HasFlag([Microsoft.Management.Infrastructure.CimFlags]::Required)) {
$option.required = $true
}
if ($null -ne $Property.Qualifiers['Values']) {
$option.choices = [System.Collections.Generic.List`1[Object]]$Property.Qualifiers['Values'].Value
}
if ($property_name -eq "Name") {
# For backwards compatibility we support specifying the Name DSC property as item_name
$option.aliases = @("item_name")
} elseif ($property_name -ceq "key") {
# There seems to be a bug in the CIM property parsing when the property name is 'Key'. The CIM instance will
# think the name is 'key' when the MOF actually defines it as 'Key'. We set the proper casing so the module arg
# validator won't fire a case sensitive warning
$property_name = "Key"
}
if ($Property.ReferenceClassName -eq "MSFT_Credential") {
# Special handling for the MSFT_Credential type (PSCredential), we handle this with having 2 options that
# have the suffix _username and _password.
$option_spec_pass = @{
type = "str"
required = $option.required
no_log = $true
}
$Spec.options."$($property_name)_password" = $option_spec_pass
$Spec.required_together.Add(@("$($property_name)_username", "$($property_name)_password")) > $null
$property_name = "$($property_name)_username"
$option.type = "str"
} elseif ($Property.ReferenceClassName -eq "MSFT_KeyValuePair") {
$option.type = "dict"
} elseif ($property_type.EndsWith("Array")) {
$option.type = "list"
$option.elements = ConvertTo-ArgSpecType -CimType $property_type.Substring(0, $property_type.Length - 5)
} else {
$option.type = ConvertTo-ArgSpecType -CimType $property_type
}
if (($option.type -eq "dict" -or ($option.type -eq "list" -and $option.elements -eq "dict")) -and
$Property.ReferenceClassName -ne "MSFT_KeyValuePair") {
# Get the sub spec if the type is a Instance (CimInstance/dict)
$sub_option_spec = Get-OptionSpec -ClassName $Property.ReferenceClassName
$option += $sub_option_spec
}
$Spec.options.$property_name = $option
}
Function Get-OptionSpec {
<#
.SYNOPSIS
Generates the specifiec used in AnsibleModule for a CIM MOF resource name.
.NOTES
This won't be able to retrieve the default values for an option as that is not defined in the MOF for a resource.
Default values are still preserved in the DSC engine if we don't pass in the property at all, we just can't report
on what they are automatically.
#>
param(
[Parameter(Mandatory=$true)][String]$ClassName
)
$spec = @{
options = @{}
required_together = [System.Collections.ArrayList]@()
}
$properties = Get-DscCimClassProperties -ClassName $ClassName
foreach ($property in $properties) {
Add-PropertyOption -Spec $spec -Property $property
}
return $spec
}
Function ConvertTo-CimInstance {
<#
.SYNOPSIS
Converts a dict to a CimInstance of the specified Class. Also provides a
better error message if this fails that contains the option name that failed.
#>
param(
[Parameter(Mandatory=$true)][String]$Name,
[Parameter(Mandatory=$true)][String]$ClassName,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Value,
[Parameter(Mandatory=$true)][Ansible.Basic.AnsibleModule]$Module,
[Switch]$Recurse
)
$properties = @{}
foreach ($value_info in $Value.GetEnumerator()) {
# Need to remove all null values from existing dict so the conversion works
if ($null -eq $value_info.Value) {
continue
}
$properties.($value_info.Key) = $value_info.Value
}
if ($Recurse) {
# We want to validate and convert and values to what's required by DSC
$properties = ConvertTo-DscProperty -ClassName $ClassName -Params $properties -Module $Module
}
try {
return (New-CimInstance -ClassName $ClassName -Property $properties -ClientOnly)
} catch {
# New-CimInstance raises a poor error message, make sure we mention what option it is for
$Module.FailJson("Failed to cast dict value for option '$Name' to a CimInstance: $($_.Exception.Message)", $_)
}
}
Function ConvertTo-DscProperty {
<#
.SYNOPSIS
Converts the input module parameters that have been validated and casted
into the types expected by the DSC engine. This is mostly done to deal with
types like PSCredential and Dictionaries.
#>
param(
[Parameter(Mandatory=$true)][String]$ClassName,
[Parameter(Mandatory=$true)][System.Collections.IDictionary]$Params,
[Parameter(Mandatory=$true)][Ansible.Basic.AnsibleModule]$Module
)
$properties = Get-DscCimClassProperties -ClassName $ClassName
$dsc_properties = @{}
foreach ($property in $properties) {
$property_name = $property.Name
$property_type = $property.CimType.ToString()
if ($property.ReferenceClassName -eq "MSFT_Credential") {
$username = $Params."$($property_name)_username"
$password = $Params."$($property_name)_password"
# No user set == No option set in playbook, skip this property
if ($null -eq $username) {
continue
}
$sec_password = ConvertTo-SecureString -String $password -AsPlainText -Force
$value = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $sec_password
} else {
$value = $Params.$property_name
# The actual value wasn't set, skip adding this property
if ($null -eq $value) {
continue
}
if ($property.ReferenceClassName -eq "MSFT_KeyValuePair") {
$key_value_pairs = [System.Collections.Generic.List`1[CimInstance]]@()
foreach ($value_info in $value.GetEnumerator()) {
$kvp = @{Key = $value_info.Key; Value = $value_info.Value.ToString()}
$cim_instance = ConvertTo-CimInstance -Name $property_name -ClassName MSFT_KeyValuePair `
-Value $kvp -Module $Module
$key_value_pairs.Add($cim_instance) > $null
}
$value = $key_value_pairs.ToArray()
} elseif ($null -ne $property.ReferenceClassName) {
# Convert the dict to a CimInstance (or list of CimInstances)
$convert_args = @{
ClassName = $property.ReferenceClassName
Module = $Module
Name = $property_name
Recurse = $true
}
if ($property_type.EndsWith("Array")) {
$value = [System.Collections.Generic.List`1[CimInstance]]@()
foreach ($raw in $Params.$property_name.GetEnumerator()) {
$cim_instance = ConvertTo-CimInstance -Value $raw @convert_args
$value.Add($cim_instance) > $null
}
$value = $value.ToArray() # Need to make sure we are dealing with an Array not a List
} else {
$value = ConvertTo-CimInstance -Value $value @convert_args
}
}
}
$dsc_properties.$property_name = $value
}
return $dsc_properties
}
Function Invoke-DscMethod {
<#
.SYNOPSIS
Invokes the DSC Resource Method specified in another PS pipeline. This is
done so we can retrieve the Verbose stream and return it back to the user
for futher debugging.
#>
param(
[Parameter(Mandatory=$true)][Ansible.Basic.AnsibleModule]$Module,
[Parameter(Mandatory=$true)][String]$Method,
[Parameter(Mandatory=$true)][Hashtable]$Arguments
)
# Invoke the DSC resource in a separate runspace so we can capture the Verbose output
$ps = [PowerShell]::Create()
$ps.AddCommand("Invoke-DscResource").AddParameter("Method", $Method) > $null
$ps.AddParameters($Arguments) > $null
$result = $ps.Invoke()
# Pass the warnings through to the AnsibleModule return result
foreach ($warning in $ps.Streams.Warning) {
$Module.Warn($warning.Message)
}
# If running at a high enough verbosity, add the verbose output to the AnsibleModule return result
if ($Module.Verbosity -ge 3) {
$verbose_logs = [System.Collections.Generic.List`1[String]]@()
foreach ($verbosity in $ps.Streams.Verbose) {
$verbose_logs.Add($verbosity.Message) > $null
}
$Module.Result."verbose_$($Method.ToLower())" = $verbose_logs
}
if ($ps.HadErrors) {
# Cannot pass in the ErrorRecord as it's a RemotingErrorRecord and doesn't contain the ScriptStackTrace
# or other info that would be useful
$Module.FailJson("Failed to invoke DSC $Method method: $($ps.Streams.Error[0].Exception.Message)")
}
return $result
}
# win_dsc is unique in that is builds the arg spec based on DSC Resource input. To get this info
# we need to read the resource_name and module_version value which is done outside of Ansible.Basic
if ($args.Length -gt 0) {
$params = Get-Content -Path $args[0] | ConvertFrom-Json
} else {
$params = $complex_args
}
if (-not $params.ContainsKey("resource_name")) {
$res = @{
msg = "missing required argument: resource_name"
failed = $true
}
Write-Output -InputObject (ConvertTo-Json -Compress -InputObject $res)
exit 1
}
$resource_name = $params.resource_name
if ($params.ContainsKey("module_version")) {
$module_version = $params.module_version
} else {
$module_version = "latest"
}
$module_versions = (Get-DscResource -Name $resource_name -ErrorAction SilentlyContinue | Sort-Object -Property Version)
$resource = $null
if ($module_version -eq "latest" -and $null -ne $module_versions) {
$resource = $module_versions[-1]
} elseif ($module_version -ne "latest") {
$resource = $module_versions | Where-Object { $_.Version -eq $module_version }
}
if (-not $resource) {
if ($module_version -eq "latest") {
$msg = "Resource '$resource_name' not found."
} else {
$msg = "Resource '$resource_name' with version '$module_version' not found."
$msg += " Versions installed: '$($module_versions.Version -join "', '")'."
}
Write-Output -InputObject (ConvertTo-Json -Compress -InputObject @{ failed = $true; msg = $msg })
exit 1
}
# Build the base args for the DSC Invocation based on the resource selected
$dsc_args = @{
Name = $resource.Name
}
# Binary resources are not working very well with that approach - need to guesstimate module name/version
$module_version = $null
if ($resource.Module) {
$dsc_args.ModuleName = @{
ModuleName = $resource.Module.Name
ModuleVersion = $resource.Module.Version
}
$module_version = $resource.Module.Version.ToString()
} else {
$dsc_args.ModuleName = "PSDesiredStateConfiguration"
}
# To ensure the class registered with CIM is the one based on our version, we want to run the Get method so the DSC
# engine updates the metadata propery. We don't care about any errors here
try {
Invoke-DscResource -Method Get -Property @{Fake="Fake"} @dsc_args > $null
} catch {}
# Dynamically build the option spec based on the resource_name specified and create the module object
$spec = Get-OptionSpec -ClassName $resource.ResourceType
$spec.supports_check_mode = $true
$spec.options.module_version = @{ type = "str"; default = "latest" }
$spec.options.resource_name = @{ type = "str"; required = $true }
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$module.Result.reboot_required = $false
$module.Result.module_version = $module_version
# Build the DSC invocation arguments and invoke the resource
$dsc_args.Property = ConvertTo-DscProperty -ClassName $resource.ResourceType -Module $module -Params $Module.Params
$dsc_args.Verbose = $true
$test_result = Invoke-DscMethod -Module $module -Method Test -Arguments $dsc_args
if ($test_result.InDesiredState -ne $true) {
if (-not $module.CheckMode) {
$result = Invoke-DscMethod -Module $module -Method Set -Arguments $dsc_args
$module.Result.reboot_required = $result.RebootRequired
}
$module.Result.changed = $true
}
$module.ExitJson()

@ -1,183 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2015, Trond Hindenes <trond@hindenes.com>, and others
# Copyright: (c) 2017, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: win_dsc
version_added: "2.4"
short_description: Invokes a PowerShell DSC configuration
description:
- Configures a resource using PowerShell DSC.
- Requires PowerShell version 5.0 or newer.
- Most of the options for this module are dynamic and will vary depending on
the DSC Resource specified in I(resource_name).
- See :doc:`/user_guide/windows_dsc` for more information on how to use this module.
options:
resource_name:
description:
- The name of the DSC Resource to use.
- Must be accessible to PowerShell using any of the default paths.
type: str
required: yes
module_version:
description:
- Can be used to configure the exact version of the DSC resource to be
invoked.
- Useful if the target node has multiple versions installed of the module
containing the DSC resource.
- If not specified, the module will follow standard PowerShell convention
and use the highest version available.
type: str
default: latest
free_form:
description:
- The M(win_dsc) module takes in multiple free form options based on the
DSC resource being invoked by I(resource_name).
- There is no option actually named C(free_form) so see the examples.
- This module will try and convert the option to the correct type required
by the DSC resource and throw a warning if it fails.
- If the type of the DSC resource option is a C(CimInstance) or
C(CimInstance[]), this means the value should be a dictionary or list
of dictionaries based on the values required by that option.
- If the type of the DSC resource option is a C(PSCredential) then there
needs to be 2 options set in the Ansible task definition suffixed with
C(_username) and C(_password).
- If the type of the DSC resource option is an array, then a list should be
provided but a comma separated string also work. Use a list where
possible as no escaping is required and it works with more complex types
list C(CimInstance[]).
- If the type of the DSC resource option is a C(DateTime), you should use
a string in the form of an ISO 8901 string to ensure the exact date is
used.
- Since Ansible 2.8, Ansible will now validate the input fields against the
DSC resource definition automatically. Older versions will silently
ignore invalid fields.
type: str
required: true
notes:
- By default there are a few builtin resources that come with PowerShell 5.0,
see U(https://docs.microsoft.com/en-us/powershell/scripting/dsc/resources/resources) for
more information on these resources.
- Custom DSC resources can be installed with M(win_psmodule) using the I(name)
option.
- The DSC engine run's each task as the SYSTEM account, any resources that need
to be accessed with a different account need to have C(PsDscRunAsCredential)
set.
- To see the valid options for a DSC resource, run the module with C(-vvv) to
show the possible module invocation. Default values are not shown in this
output but are applied within the DSC engine.
author:
- Trond Hindenes (@trondhindenes)
'''
EXAMPLES = r'''
- name: Extract zip file
win_dsc:
resource_name: Archive
Ensure: Present
Path: C:\Temp\zipfile.zip
Destination: C:\Temp\Temp2
- name: Install a Windows feature with the WindowsFeature resource
win_dsc:
resource_name: WindowsFeature
Name: telnet-client
- name: Edit HKCU reg key under specific user
win_dsc:
resource_name: Registry
Ensure: Present
Key: HKEY_CURRENT_USER\ExampleKey
ValueName: TestValue
ValueData: TestData
PsDscRunAsCredential_username: '{{ansible_user}}'
PsDscRunAsCredential_password: '{{ansible_password}}'
no_log: true
- name: Create file with multiple attributes
win_dsc:
resource_name: File
DestinationPath: C:\ansible\dsc
Attributes: # can also be a comma separated string, e.g. 'Hidden, System'
- Hidden
- System
Ensure: Present
Type: Directory
- name: Call DSC resource with DateTime option
win_dsc:
resource_name: DateTimeResource
DateTimeOption: '2019-02-22T13:57:31.2311892+00:00'
# more complex example using custom DSC resource and dict values
- name: Setup the xWebAdministration module
win_psmodule:
name: xWebAdministration
state: present
- name: Create IIS Website with Binding and Authentication options
win_dsc:
resource_name: xWebsite
Ensure: Present
Name: DSC Website
State: Started
PhysicalPath: C:\inetpub\wwwroot
BindingInfo: # Example of a CimInstance[] DSC parameter (list of dicts)
- Protocol: https
Port: 1234
CertificateStoreName: MY
CertificateThumbprint: C676A89018C4D5902353545343634F35E6B3A659
HostName: DSCTest
IPAddress: '*'
SSLFlags: '1'
- Protocol: http
Port: 4321
IPAddress: '*'
AuthenticationInfo: # Example of a CimInstance DSC parameter (dict)
Anonymous: no
Basic: true
Digest: false
Windows: yes
'''
RETURN = r'''
module_version:
description: The version of the dsc resource/module used.
returned: always
type: str
sample: "1.0.1"
reboot_required:
description: Flag returned from the DSC engine indicating whether or not
the machine requires a reboot for the invoked changes to take effect.
returned: always
type: bool
sample: true
verbose_test:
description: The verbose output as a list from executing the DSC test
method.
returned: Ansible verbosity is -vvv or greater
type: list
sample: [
"Perform operation 'Invoke CimMethod' with the following parameters, ",
"[SERVER]: LCM: [Start Test ] [[File]DirectResourceAccess]",
"Operation 'Invoke CimMethod' complete."
]
verbose_set:
description: The verbose output as a list from executing the DSC Set
method.
returned: Ansible verbosity is -vvv or greater and a change occurred
type: list
sample: [
"Perform operation 'Invoke CimMethod' with the following parameters, ",
"[SERVER]: LCM: [Start Set ] [[File]DirectResourceAccess]",
"Operation 'Invoke CimMethod' complete."
]
'''

@ -1,111 +0,0 @@
#!powershell
# Copyright: (c) 2014, Paul Durivage <paul.durivage@rackspace.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#Requires -Module Ansible.ModuleUtils.Legacy
Import-Module -Name ServerManager
$result = @{
changed = $false
}
$params = Parse-Args $args -supports_check_mode $true
$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
$name = Get-AnsibleParam -obj $params -name "name" -type "list" -failifempty $true
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent"
$include_sub_features = Get-AnsibleParam -obj $params -name "include_sub_features" -type "bool" -default $false
$include_management_tools = Get-AnsibleParam -obj $params -name "include_management_tools" -type "bool" -default $false
$source = Get-AnsibleParam -obj $params -name "source" -type "str"
$install_cmdlet = $false
if (Get-Command -Name Install-WindowsFeature -ErrorAction SilentlyContinue) {
Set-Alias -Name Install-AnsibleWindowsFeature -Value Install-WindowsFeature
Set-Alias -Name Uninstall-AnsibleWindowsFeature -Value Uninstall-WindowsFeature
$install_cmdlet = $true
} elseif (Get-Command -Name Add-WindowsFeature -ErrorAction SilentlyContinue) {
Set-Alias -Name Install-AnsibleWindowsFeature -Value Add-WindowsFeature
Set-Alias -Name Uninstall-AnsibleWindowsFeature -Value Remove-WindowsFeature
} else {
Fail-Json -obj $result -message "This version of Windows does not support the cmdlets Install-WindowsFeature or Add-WindowsFeature"
}
if ($state -eq "present") {
$install_args = @{
Name = $name
IncludeAllSubFeature = $include_sub_features
Restart = $false
WhatIf = $check_mode
ErrorAction = "Stop"
}
if ($install_cmdlet) {
$install_args.IncludeManagementTools = $include_management_tools
$install_args.Confirm = $false
if ($source) {
if (-not (Test-Path -Path $source)) {
Fail-Json -obj $result -message "Failed to find source path $source for feature install"
}
$install_args.Source = $source
}
}
try {
$action_results = Install-AnsibleWindowsFeature @install_args
} catch {
Fail-Json -obj $result -message "Failed to install Windows Feature: $($_.Exception.Message)"
}
} else {
$uninstall_args = @{
Name = $name
Restart = $false
WhatIf = $check_mode
ErrorAction = "Stop"
}
if ($install_cmdlet) {
$uninstall_args.IncludeManagementTools = $include_management_tools
}
try {
$action_results = Uninstall-AnsibleWindowsFeature @uninstall_args
} catch {
Fail-Json -obj $result -message "Failed to uninstall Windows Feature: $($_.Exception.Message)"
}
}
# Loop through results and create a hash containing details about
# each role/feature that is installed/removed
# $action_results.FeatureResult is not empty if anything was changed
$feature_results = @()
foreach ($action_result in $action_results.FeatureResult) {
$message = @()
foreach ($msg in $action_result.Message) {
$message += @{
message_type = $msg.MessageType.ToString()
error_code = $msg.ErrorCode
text = $msg.Text
}
}
$feature_results += @{
id = $action_result.Id
display_name = $action_result.DisplayName
message = $message
reboot_required = ConvertTo-Bool -obj $action_result.RestartNeeded
skip_reason = $action_result.SkipReason.ToString()
success = ConvertTo-Bool -obj $action_result.Success
restart_needed = ConvertTo-Bool -obj $action_result.RestartNeeded
}
$result.changed = $true
}
$result.feature_result = $feature_results
$result.success = ConvertTo-Bool -obj $action_results.Success
$result.exitcode = $action_results.ExitCode.ToString()
$result.reboot_required = ConvertTo-Bool -obj $action_results.RestartNeeded
# controls whether Ansible will fail or not
$result.failed = (-not $action_results.Success)
Exit-Json -obj $result

@ -1,149 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2014, Paul Durivage <paul.durivage@rackspace.com>
# Copyright: (c) 2014, Trond Hindenes <trond@hindenes.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# this is a windows documentation stub. actual code lives in the .ps1
# file of the same name
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: win_feature
version_added: "1.7"
short_description: Installs and uninstalls Windows Features on Windows Server
description:
- Installs or uninstalls Windows Roles or Features on Windows Server.
- This module uses the Add/Remove-WindowsFeature Cmdlets on Windows 2008 R2
and Install/Uninstall-WindowsFeature Cmdlets on Windows 2012, which are not available on client os machines.
options:
name:
description:
- Names of roles or features to install as a single feature or a comma-separated list of features.
- To list all available features use the PowerShell command C(Get-WindowsFeature).
type: list
required: yes
state:
description:
- State of the features or roles on the system.
type: str
choices: [ absent, present ]
default: present
include_sub_features:
description:
- Adds all subfeatures of the specified feature.
type: bool
default: no
include_management_tools:
description:
- Adds the corresponding management tools to the specified feature.
- Not supported in Windows 2008 R2 and will be ignored.
type: bool
default: no
source:
description:
- Specify a source to install the feature from.
- Not supported in Windows 2008 R2 and will be ignored.
- Can either be C({driveletter}:\sources\sxs) or C(\\{IP}\share\sources\sxs).
type: str
version_added: "2.1"
seealso:
- module: win_chocolatey
- module: win_package
author:
- Paul Durivage (@angstwad)
- Trond Hindenes (@trondhindenes)
'''
EXAMPLES = r'''
- name: Install IIS (Web-Server only)
win_feature:
name: Web-Server
state: present
- name: Install IIS (Web-Server and Web-Common-Http)
win_feature:
name:
- Web-Server
- Web-Common-Http
state: present
- name: Install NET-Framework-Core from file
win_feature:
name: NET-Framework-Core
source: C:\Temp\iso\sources\sxs
state: present
- name: Install IIS Web-Server with sub features and management tools
win_feature:
name: Web-Server
state: present
include_sub_features: yes
include_management_tools: yes
register: win_feature
- name: Reboot if installing Web-Server feature requires it
win_reboot:
when: win_feature.reboot_required
'''
RETURN = r'''
exitcode:
description: The stringified exit code from the feature installation/removal command.
returned: always
type: str
sample: Success
feature_result:
description: List of features that were installed or removed.
returned: success
type: complex
sample:
contains:
display_name:
description: Feature display name.
returned: always
type: str
sample: "Telnet Client"
id:
description: A list of KB article IDs that apply to the update.
returned: always
type: int
sample: 44
message:
description: Any messages returned from the feature subsystem that occurred during installation or removal of this feature.
returned: always
type: list
elements: str
sample: []
reboot_required:
description: True when the target server requires a reboot as a result of installing or removing this feature.
returned: always
type: bool
sample: true
restart_needed:
description: DEPRECATED in Ansible 2.4 (refer to C(reboot_required) instead). True when the target server requires a reboot as a
result of installing or removing this feature.
returned: always
type: bool
sample: true
skip_reason:
description: The reason a feature installation or removal was skipped.
returned: always
type: str
sample: NotSkipped
success:
description: If the feature installation or removal was successful.
returned: always
type: bool
sample: true
reboot_required:
description: True when the target server requires a reboot to complete updates (no further updates can be installed until after a reboot).
returned: success
type: bool
sample: true
'''

@ -1,416 +0,0 @@
#!powershell
# Copyright: (c) 2016, 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.LinkUtil
$spec = @{
options = @{
paths = @{ type = "list"; elements = "str"; required = $true }
age = @{ type = "str" }
age_stamp = @{ type = "str"; default = "mtime"; choices = "mtime", "ctime", "atime" }
file_type = @{ type = "str"; default = "file"; choices = "file", "directory" }
follow = @{ type = "bool"; default = $false }
hidden = @{ type = "bool"; default = $false }
patterns = @{ type = "list"; elements = "str"; aliases = "regex", "regexp" }
recurse = @{ type = "bool"; default = $false }
size = @{ type = "str" }
use_regex = @{ type = "bool"; default = $false }
get_checksum = @{ type = "bool"; default = $true }
checksum_algorithm = @{ type = "str"; default = "sha1"; choices = "md5", "sha1", "sha256", "sha384", "sha512" }
}
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$paths = $module.Params.paths
$age = $module.Params.age
$age_stamp = $module.Params.age_stamp
$file_type = $module.Params.file_type
$follow = $module.Params.follow
$hidden = $module.Params.hidden
$patterns = $module.Params.patterns
$recurse = $module.Params.recurse
$size = $module.Params.size
$use_regex = $module.Params.use_regex
$get_checksum = $module.Params.get_checksum
$checksum_algorithm = $module.Params.checksum_algorithm
$module.Result.examined = 0
$module.Result.files = @()
$module.Result.matched = 0
Load-LinkUtils
Function Assert-Age {
Param (
[System.IO.FileSystemInfo]$File,
[System.Int64]$Age,
[System.String]$AgeStamp
)
$actual_age = switch ($AgeStamp) {
mtime { $File.LastWriteTime.Ticks }
ctime { $File.CreationTime.Ticks }
atime { $File.LastAccessTime.Ticks }
}
if ($Age -ge 0) {
return $Age -ge $actual_age
} else {
return ($Age * -1) -le $actual_age
}
}
Function Assert-FileType {
Param (
[System.IO.FileSystemInfo]$File,
[System.String]$FileType
)
$is_dir = $File.Attributes.HasFlag([System.IO.FileAttributes]::Directory)
return ($FileType -eq 'directory' -and $is_dir) -or ($FileType -eq 'file' -and -not $is_dir)
}
Function Assert-FileHidden {
Param (
[System.IO.FileSystemInfo]$File,
[Switch]$IsHidden
)
$file_is_hidden = $File.Attributes.HasFlag([System.IO.FileAttributes]::Hidden)
return $IsHidden.IsPresent -eq $file_is_hidden
}
Function Assert-FileNamePattern {
Param (
[System.IO.FileSystemInfo]$File,
[System.String[]]$Patterns,
[Switch]$UseRegex
)
$valid_match = $false
foreach ($pattern in $Patterns) {
if ($UseRegex) {
if ($File.Name -match $pattern) {
$valid_match = $true
break
}
} else {
if ($File.Name -like $pattern) {
$valid_match = $true
break
}
}
}
return $valid_match
}
Function Assert-FileSize {
Param (
[System.IO.FileSystemInfo]$File,
[System.Int64]$Size
)
if ($Size -ge 0) {
return $File.Length -ge $Size
} else {
return $File.Length -le ($Size * -1)
}
}
Function Get-FileChecksum {
Param (
[System.String]$Path,
[System.String]$Algorithm
)
$sp = switch ($algorithm) {
'md5' { New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider }
'sha1' { New-Object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider }
'sha256' { New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider }
'sha384' { New-Object -TypeName System.Security.Cryptography.SHA384CryptoServiceProvider }
'sha512' { New-Object -TypeName System.Security.Cryptography.SHA512CryptoServiceProvider }
}
$fp = [System.IO.File]::Open($Path, [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite)
try {
$hash = [System.BitConverter]::ToString($sp.ComputeHash($fp)).Replace("-", "").ToLower()
} finally {
$fp.Dispose()
}
return $hash
}
Function Search-Path {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true)]
[System.String]
$Path,
[Parameter(Mandatory=$true)]
[AllowEmptyCollection()]
[System.Collections.Generic.HashSet`1[System.String]]
$CheckedPaths,
[Parameter(Mandatory=$true)]
[Object]
$Module,
[System.Int64]
$Age,
[System.String]
$AgeStamp,
[System.String]
$FileType,
[Switch]
$Follow,
[Switch]
$GetChecksum,
[Switch]
$IsHidden,
[System.String[]]
$Patterns,
[Switch]
$Recurse,
[System.Int64]
$Size,
[Switch]
$UseRegex
)
$dir_obj = New-Object -TypeName System.IO.DirectoryInfo -ArgumentList $Path
if ([Int32]$dir_obj.Attributes -eq -1) {
$Module.Warn("Argument path '$Path' does not exist, skipping")
return
} elseif (-not $dir_obj.Attributes.HasFlag([System.IO.FileAttributes]::Directory)) {
$Module.Warn("Argument path '$Path' is a file not a directory, skipping")
return
}
$dir_files = @()
try {
$dir_files = $dir_obj.EnumerateFileSystemInfos("*", [System.IO.SearchOption]::TopDirectoryOnly)
} catch [System.IO.DirectoryNotFoundException] { # Broken ReparsePoint/Symlink, cannot enumerate
} catch [System.UnauthorizedAccessException] {} # No ListDirectory permissions, Get-ChildItem ignored this
foreach ($dir_child in $dir_files) {
if ($dir_child.Attributes.HasFlag([System.IO.FileAttributes]::Directory) -and $Recurse) {
if ($Follow -or -not $dir_child.Attributes.HasFlag([System.IO.FileAttributes]::ReparsePoint)) {
$PSBoundParameters.Remove('Path') > $null
Search-Path -Path $dir_child.FullName @PSBoundParameters
}
}
# Check to see if we've already encountered this path and skip if we have.
if (-not $CheckedPaths.Add($dir_child.FullName.ToLowerInvariant())) {
continue
}
$Module.Result.examined++
if ($PSBoundParameters.ContainsKey('Age')) {
$age_match = Assert-Age -File $dir_child -Age $Age -AgeStamp $AgeStamp
} else {
$age_match = $true
}
$file_type_match = Assert-FileType -File $dir_child -FileType $FileType
$hidden_match = Assert-FileHidden -File $dir_child -IsHidden:$IsHidden
if ($PSBoundParameters.ContainsKey('Patterns')) {
$pattern_match = Assert-FileNamePattern -File $dir_child -Patterns $Patterns -UseRegex:$UseRegex.IsPresent
} else {
$pattern_match = $true
}
if ($PSBoundParameters.ContainsKey('Size')) {
$size_match = Assert-FileSize -File $dir_child -Size $Size
} else {
$size_match = $true
}
if (-not ($age_match -and $file_type_match -and $hidden_match -and $pattern_match -and $size_match)) {
continue
}
# It passed all our filters so add it
$module.Result.matched++
# TODO: Make this generic so it can be shared with win_find and win_stat.
$epoch = New-Object -Type System.DateTime -ArgumentList 1970, 1, 1, 0, 0, 0, 0
$file_info = @{
attributes = $dir_child.Attributes.ToString()
checksum = $null
creationtime = (New-TimeSpan -Start $epoch -End $dir_child.CreationTime).TotalSeconds
exists = $true
extension = $null
filename = $dir_child.Name
isarchive = $dir_child.Attributes.HasFlag([System.IO.FileAttributes]::Archive)
isdir = $dir_child.Attributes.HasFlag([System.IO.FileAttributes]::Directory)
ishidden = $dir_child.Attributes.HasFlag([System.IO.FileAttributes]::Hidden)
isreadonly = $dir_child.Attributes.HasFlag([System.IO.FileAttributes]::ReadOnly)
isreg = $false
isshared = $false
lastaccesstime = (New-TimeSpan -Start $epoch -End $dir_child.LastAccessTime).TotalSeconds
lastwritetime = (New-TimeSpan -Start $epoch -End $dir_child.LastWriteTime).TotalSeconds
owner = $null
path = $dir_child.FullName
sharename = $null
size = $null
}
try {
$file_info.owner = $dir_child.GetAccessControl().Owner
} catch {} # May not have rights to get the Owner, historical behaviour is to ignore.
if ($dir_child.Attributes.HasFlag([System.IO.FileAttributes]::Directory)) {
$share_info = Get-CimInstance -ClassName Win32_Share -Filter "Path='$($dir_child.FullName -replace '\\', '\\')'"
if ($null -ne $share_info) {
$file_info.isshared = $true
$file_info.sharename = $share_info.Name
}
} else {
$file_info.extension = $dir_child.Extension
$file_info.isreg = $true
$file_info.size = $dir_child.Length
if ($GetChecksum) {
try {
$file_info.checksum = Get-FileChecksum -Path $dir_child.FullName -Algorithm $checksum_algorithm
} catch {} # Just keep the checksum as $null in the case of a failure.
}
}
# Append the link information if the path is a link
$link_info = @{
isjunction = $false
islnk = $false
nlink = 1
lnk_source = $null
lnk_target = $null
hlnk_targets = @()
}
$link_stat = Get-Link -link_path $dir_child.FullName
if ($null -ne $link_stat) {
switch ($link_stat.Type) {
"SymbolicLink" {
$link_info.islnk = $true
$link_info.isreg = $false
$link_info.lnk_source = $link_stat.AbsolutePath
$link_info.lnk_target = $link_stat.TargetPath
break
}
"JunctionPoint" {
$link_info.isjunction = $true
$link_info.isreg = $false
$link_info.lnk_source = $link_stat.AbsolutePath
$link_info.lnk_target = $link_stat.TargetPath
break
}
"HardLink" {
$link_info.nlink = $link_stat.HardTargets.Count
# remove current path from the targets
$hlnk_targets = $link_info.HardTargets | Where-Object { $_ -ne $dir_child.FullName }
$link_info.hlnk_targets = @($hlnk_targets)
break
}
}
}
foreach ($kv in $link_info.GetEnumerator()) {
$file_info.$($kv.Key) = $kv.Value
}
# Output the file_info object
$file_info
}
}
$search_params = @{
CheckedPaths = [System.Collections.Generic.HashSet`1[System.String]]@()
GetChecksum = $get_checksum
Module = $module
FileType = $file_type
Follow = $follow
IsHidden = $hidden
Recurse = $recurse
}
if ($null -ne $age) {
$seconds_per_unit = @{'s'=1; 'm'=60; 'h'=3600; 'd'=86400; 'w'=604800}
$seconds_pattern = '^(-?\d+)(s|m|h|d|w)?$'
$match = $age -match $seconds_pattern
if ($Match) {
$specified_seconds = [Int64]$Matches[1]
if ($null -eq $Matches[2]) {
$chosen_unit = 's'
} else {
$chosen_unit = $Matches[2]
}
$total_seconds = $specified_seconds * ($seconds_per_unit.$chosen_unit)
if ($total_seconds -ge 0) {
$search_params.Age = (Get-Date).AddSeconds($total_seconds * -1).Ticks
} else {
# Make sure we add the positive value of seconds to current time then make it negative for later comparisons.
$age = (Get-Date).AddSeconds($total_seconds).Ticks
$search_params.Age = $age * -1
}
$search_params.AgeStamp = $age_stamp
} else {
$module.FailJson("Invalid age pattern specified")
}
}
if ($null -ne $patterns) {
$search_params.Patterns = $patterns
$search_params.UseRegex = $use_regex
}
if ($null -ne $size) {
$bytes_per_unit = @{'b'=1; 'k'=1KB; 'm'=1MB; 'g'=1GB;'t'=1TB}
$size_pattern = '^(-?\d+)(b|k|m|g|t)?$'
$match = $size -match $size_pattern
if ($Match) {
$specified_size = [Int64]$Matches[1]
if ($null -eq $Matches[2]) {
$chosen_byte = 'b'
} else {
$chosen_byte = $Matches[2]
}
$search_params.Size = $specified_size * ($bytes_per_unit.$chosen_byte)
} else {
$module.FailJson("Invalid size pattern specified")
}
}
$matched_files = foreach ($path in $paths) {
# Ensure we pass in an absolute path. We use the ExecutionContext as this is based on the PSProvider path not the
# process location which can be different.
$abs_path = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path)
Search-Path -Path $abs_path @search_params
}
# Make sure we sort the files in alphabetical order.
$module.Result.files = @() + ($matched_files | Sort-Object -Property {$_.path})
$module.ExitJson()

@ -1,345 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2016, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# this is a windows documentation stub. actual code lives in the .ps1
# file of the same name
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: win_find
version_added: "2.3"
short_description: Return a list of files based on specific criteria
description:
- Return a list of files based on specified criteria.
- Multiple criteria are AND'd together.
- For non-Windows targets, use the M(find) module instead.
options:
age:
description:
- Select files or folders whose age is equal to or greater than
the specified time.
- Use a negative age to find files equal to or less than
the specified time.
- You can choose seconds, minutes, hours, days or weeks
by specifying the first letter of an of
those words (e.g., "2s", "10d", 1w").
type: str
age_stamp:
description:
- Choose the file property against which we compare C(age).
- The default attribute we compare with is the last modification time.
type: str
choices: [ atime, ctime, mtime ]
default: mtime
checksum_algorithm:
description:
- Algorithm to determine the checksum of a file.
- Will throw an error if the host is unable to use specified algorithm.
type: str
choices: [ md5, sha1, sha256, sha384, sha512 ]
default: sha1
file_type:
description: Type of file to search for.
type: str
choices: [ directory, file ]
default: file
follow:
description:
- Set this to C(yes) to follow symlinks in the path.
- This needs to be used in conjunction with C(recurse).
type: bool
default: no
get_checksum:
description:
- Whether to return a checksum of the file in the return info (default sha1),
use C(checksum_algorithm) to change from the default.
type: bool
default: yes
hidden:
description: Set this to include hidden files or folders.
type: bool
default: no
paths:
description:
- List of paths of directories to search for files or folders in.
- This can be supplied as a single path or a list of paths.
type: list
required: yes
patterns:
description:
- One or more (powershell or regex) patterns to compare filenames with.
- The type of pattern matching is controlled by C(use_regex) option.
- The patterns restrict the list of files or folders to be returned based on the filenames.
- For a file to be matched it only has to match with one pattern in a list provided.
type: list
aliases: [ "regex", "regexp" ]
recurse:
description:
- Will recursively descend into the directory looking for files or folders.
type: bool
default: no
size:
description:
- Select files or folders whose size is equal to or greater than the specified size.
- Use a negative value to find files equal to or less than the specified size.
- You can specify the size with a suffix of the byte type i.e. kilo = k, mega = m...
- Size is not evaluated for symbolic links.
type: str
use_regex:
description:
- Will set patterns to run as a regex check if set to C(yes).
type: bool
default: no
author:
- Jordan Borean (@jborean93)
'''
EXAMPLES = r'''
- name: Find files in path
win_find:
paths: D:\Temp
- name: Find hidden files in path
win_find:
paths: D:\Temp
hidden: yes
- name: Find files in multiple paths
win_find:
paths:
- C:\Temp
- D:\Temp
- name: Find files in directory while searching recursively
win_find:
paths: D:\Temp
recurse: yes
- name: Find files in directory while following symlinks
win_find:
paths: D:\Temp
recurse: yes
follow: yes
- name: Find files with .log and .out extension using powershell wildcards
win_find:
paths: D:\Temp
patterns: [ '*.log', '*.out' ]
- name: Find files in path based on regex pattern
win_find:
paths: D:\Temp
patterns: out_\d{8}-\d{6}.log
- name: Find files older than 1 day
win_find:
paths: D:\Temp
age: 86400
- name: Find files older than 1 day based on create time
win_find:
paths: D:\Temp
age: 86400
age_stamp: ctime
- name: Find files older than 1 day with unit syntax
win_find:
paths: D:\Temp
age: 1d
- name: Find files newer than 1 hour
win_find:
paths: D:\Temp
age: -3600
- name: Find files newer than 1 hour with unit syntax
win_find:
paths: D:\Temp
age: -1h
- name: Find files larger than 1MB
win_find:
paths: D:\Temp
size: 1048576
- name: Find files larger than 1GB with unit syntax
win_find:
paths: D:\Temp
size: 1g
- name: Find files smaller than 1MB
win_find:
paths: D:\Temp
size: -1048576
- name: Find files smaller than 1GB with unit syntax
win_find:
paths: D:\Temp
size: -1g
- name: Find folders/symlinks in multiple paths
win_find:
paths:
- C:\Temp
- D:\Temp
file_type: directory
- name: Find files and return SHA256 checksum of files found
win_find:
paths: C:\Temp
get_checksum: yes
checksum_algorithm: sha256
- name: Find files and do not return the checksum
win_find:
paths: C:\Temp
get_checksum: no
'''
RETURN = r'''
examined:
description: The number of files/folders that was checked.
returned: always
type: int
sample: 10
matched:
description: The number of files/folders that match the criteria.
returned: always
type: int
sample: 2
files:
description: Information on the files/folders that match the criteria returned as a list of dictionary elements
for each file matched. The entries are sorted by the path value alphabetically.
returned: success
type: complex
contains:
attributes:
description: attributes of the file at path in raw form.
returned: success, path exists
type: str
sample: "Archive, Hidden"
checksum:
description: The checksum of a file based on checksum_algorithm specified.
returned: success, path exists, path is a file, get_checksum == True
type: str
sample: 09cb79e8fc7453c84a07f644e441fd81623b7f98
creationtime:
description: The create time of the file represented in seconds since epoch.
returned: success, path exists
type: float
sample: 1477984205.15
exists:
description: Whether the file exists, will always be true for M(win_find).
returned: success, path exists
type: bool
sample: true
extension:
description: The extension of the file at path.
returned: success, path exists, path is a file
type: str
sample: ".ps1"
filename:
description: The name of the file.
returned: success, path exists
type: str
sample: temp
hlnk_targets:
description: List of other files pointing to the same file (hard links), excludes the current file.
returned: success, path exists
type: list
sample:
- C:\temp\file.txt
- C:\Windows\update.log
isarchive:
description: If the path is ready for archiving or not.
returned: success, path exists
type: bool
sample: true
isdir:
description: If the path is a directory or not.
returned: success, path exists
type: bool
sample: true
ishidden:
description: If the path is hidden or not.
returned: success, path exists
type: bool
sample: true
isjunction:
description: If the path is a junction point.
returned: success, path exists
type: bool
sample: true
islnk:
description: If the path is a symbolic link.
returned: success, path exists
type: bool
sample: true
isreadonly:
description: If the path is read only or not.
returned: success, path exists
type: bool
sample: true
isreg:
description: If the path is a regular file or not.
returned: success, path exists
type: bool
sample: true
isshared:
description: If the path is shared or not.
returned: success, path exists
type: bool
sample: true
lastaccesstime:
description: The last access time of the file represented in seconds since epoch.
returned: success, path exists
type: float
sample: 1477984205.15
lastwritetime:
description: The last modification time of the file represented in seconds since epoch.
returned: success, path exists
type: float
sample: 1477984205.15
lnk_source:
description: The target of the symlink normalized for the remote filesystem.
returned: success, path exists, path is a symbolic link or junction point
type: str
sample: C:\temp
lnk_target:
description: The target of the symlink. Note that relative paths remain relative, will return null if not a link.
returned: success, path exists, path is a symbolic link or junction point
type: str
sample: temp
nlink:
description: Number of links to the file (hard links)
returned: success, path exists
type: int
sample: 1
owner:
description: The owner of the file.
returned: success, path exists
type: str
sample: BUILTIN\Administrators
path:
description: The full absolute path to the file.
returned: success, path exists
type: str
sample: BUILTIN\Administrators
sharename:
description: The name of share if folder is shared.
returned: success, path exists, path is a directory and isshared == True
type: str
sample: file-share
size:
description: The size in bytes of the file.
returned: success, path exists, path is a file
type: int
sample: 1024
'''

@ -1,200 +0,0 @@
#!powershell
# Copyright: (c) 2019, Varun Chopra (@chopraaa) <v@chopraaa.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.Basic
#AnsibleRequires -OSVersion 6.2
Set-StrictMode -Version 2
$ErrorActionPreference = "Stop"
$spec = @{
options = @{
drive_letter = @{ type = "str" }
path = @{ type = "str" }
label = @{ type = "str" }
new_label = @{ type = "str" }
file_system = @{ type = "str"; choices = "ntfs", "refs", "exfat", "fat32", "fat" }
allocation_unit_size = @{ type = "int" }
large_frs = @{ type = "bool" }
full = @{ type = "bool"; default = $false }
compress = @{ type = "bool" }
integrity_streams = @{ type = "bool" }
force = @{ type = "bool"; default = $false }
}
mutually_exclusive = @(
,@('drive_letter', 'path', 'label')
)
required_one_of = @(
,@('drive_letter', 'path', 'label')
)
supports_check_mode = $true
}
$module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$drive_letter = $module.Params.drive_letter
$path = $module.Params.path
$label = $module.Params.label
$new_label = $module.Params.new_label
$file_system = $module.Params.file_system
$allocation_unit_size = $module.Params.allocation_unit_size
$large_frs = $module.Params.large_frs
$full_format = $module.Params.full
$compress_volume = $module.Params.compress
$integrity_streams = $module.Params.integrity_streams
$force_format = $module.Params.force
# Some pre-checks
if ($null -ne $drive_letter -and $drive_letter -notmatch "^[a-zA-Z]$") {
$module.FailJson("The parameter drive_letter should be a single character A-Z")
}
if ($integrity_streams -eq $true -and $file_system -ne "refs") {
$module.FailJson("Integrity streams can be enabled only on ReFS volumes. You specified: $($file_system)")
}
if ($compress_volume -eq $true) {
if ($file_system -eq "ntfs") {
if ($null -ne $allocation_unit_size -and $allocation_unit_size -gt 4096) {
$module.FailJson("NTFS compression is not supported for allocation unit sizes above 4096")
}
}
else {
$module.FailJson("Compression can be enabled only on NTFS volumes. You specified: $($file_system)")
}
}
function Get-AnsibleVolume {
param(
$DriveLetter,
$Path,
$Label
)
if ($null -ne $DriveLetter) {
try {
$volume = Get-Volume -DriveLetter $DriveLetter
} catch {
$module.FailJson("There was an error retrieving the volume using drive_letter $($DriveLetter): $($_.Exception.Message)", $_)
}
}
elseif ($null -ne $Path) {
try {
$volume = Get-Volume -Path $Path
} catch {
$module.FailJson("There was an error retrieving the volume using path $($Path): $($_.Exception.Message)", $_)
}
}
elseif ($null -ne $Label) {
try {
$volume = Get-Volume -FileSystemLabel $Label
} catch {
$module.FailJson("There was an error retrieving the volume using label $($Label): $($_.Exception.Message)", $_)
}
}
else {
$module.FailJson("Unable to locate volume: drive_letter, path and label were not specified")
}
return $volume
}
function Format-AnsibleVolume {
param(
$Path,
$Label,
$FileSystem,
$Full,
$UseLargeFRS,
$Compress,
$SetIntegrityStreams,
$AllocationUnitSize
)
$parameters = @{
Path = $Path
Full = $Full
}
if ($null -ne $UseLargeFRS) {
$parameters.Add("UseLargeFRS", $UseLargeFRS)
}
if ($null -ne $SetIntegrityStreams) {
$parameters.Add("SetIntegrityStreams", $SetIntegrityStreams)
}
if ($null -ne $Compress){
$parameters.Add("Compress", $Compress)
}
if ($null -ne $Label) {
$parameters.Add("NewFileSystemLabel", $Label)
}
if ($null -ne $FileSystem) {
$parameters.Add("FileSystem", $FileSystem)
}
if ($null -ne $AllocationUnitSize) {
$parameters.Add("AllocationUnitSize", $AllocationUnitSize)
}
Format-Volume @parameters -Confirm:$false | Out-Null
}
$ansible_volume = Get-AnsibleVolume -DriveLetter $drive_letter -Path $path -Label $label
$ansible_file_system = $ansible_volume.FileSystem
$ansible_volume_size = $ansible_volume.Size
$ansible_volume_alu = (Get-CimInstance -ClassName Win32_Volume -Filter "DeviceId = '$($ansible_volume.path.replace('\','\\'))'" -Property BlockSize).BlockSize
$ansible_partition = Get-Partition -Volume $ansible_volume
if (-not $force_format -and $null -ne $allocation_unit_size -and $ansible_volume_alu -ne 0 -and $null -ne $ansible_volume_alu -and $allocation_unit_size -ne $ansible_volume_alu) {
$module.FailJson("Force format must be specified since target allocation unit size: $($allocation_unit_size) is different from the current allocation unit size of the volume: $($ansible_volume_alu)")
}
foreach ($access_path in $ansible_partition.AccessPaths) {
if ($access_path -ne $Path) {
if ($null -ne $file_system -and
-not [string]::IsNullOrEmpty($ansible_file_system) -and
$file_system -ne $ansible_file_system)
{
if (-not $force_format)
{
$no_files_in_volume = (Get-ChildItem -LiteralPath $access_path -ErrorAction SilentlyContinue | Measure-Object).Count -eq 0
if($no_files_in_volume)
{
$module.FailJson("Force format must be specified since target file system: $($file_system) is different from the current file system of the volume: $($ansible_file_system.ToLower())")
}
else
{
$module.FailJson("Force format must be specified to format non-pristine volumes")
}
}
}
else
{
$pristine = -not $force_format
}
}
}
if ($force_format) {
if (-not $module.CheckMode) {
Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume -AllocationUnitSize $allocation_unit_size
}
$module.Result.changed = $true
}
else {
if ($pristine) {
if ($null -eq $new_label) {
$new_label = $ansible_volume.FileSystemLabel
}
# Conditions for formatting
if ($ansible_volume_size -eq 0 -or
$ansible_volume.FileSystemLabel -ne $new_label) {
if (-not $module.CheckMode) {
Format-AnsibleVolume -Path $ansible_volume.Path -Full $full_format -Label $new_label -FileSystem $file_system -SetIntegrityStreams $integrity_streams -UseLargeFRS $large_frs -Compress $compress_volume -AllocationUnitSize $allocation_unit_size
}
$module.Result.changed = $true
}
}
}
$module.ExitJson()

@ -1,103 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2019, Varun Chopra (@chopraaa) <v@chopraaa.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {
'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = r'''
module: win_format
version_added: '2.8'
short_description: Formats an existing volume or a new volume on an existing partition on Windows
description:
- The M(win_format) module formats an existing volume or a new volume on an existing partition on Windows
options:
drive_letter:
description:
- Used to specify the drive letter of the volume to be formatted.
type: str
path:
description:
- Used to specify the path to the volume to be formatted.
type: str
label:
description:
- Used to specify the label of the volume to be formatted.
type: str
new_label:
description:
- Used to specify the new file system label of the formatted volume.
type: str
file_system:
description:
- Used to specify the file system to be used when formatting the target volume.
type: str
choices: [ ntfs, refs, exfat, fat32, fat ]
allocation_unit_size:
description:
- Specifies the cluster size to use when formatting the volume.
- If no cluster size is specified when you format a partition, defaults are selected based on
the size of the partition.
- This value must be a multiple of the physical sector size of the disk.
type: int
large_frs:
description:
- Specifies that large File Record System (FRS) should be used.
type: bool
compress:
description:
- Enable compression on the resulting NTFS volume.
- NTFS compression is not supported where I(allocation_unit_size) is more than 4096.
type: bool
integrity_streams:
description:
- Enable integrity streams on the resulting ReFS volume.
type: bool
full:
description:
- A full format writes to every sector of the disk, takes much longer to perform than the
default (quick) format, and is not recommended on storage that is thinly provisioned.
- Specify C(true) for full format.
type: bool
force:
description:
- Specify if formatting should be forced for volumes that are not created from new partitions
or if the source and target file system are different.
type: bool
notes:
- Microsoft Windows Server 2012 or Microsoft Windows 8 or newer is required to use this module. To check if your system is compatible, see
U(https://docs.microsoft.com/en-us/windows/desktop/sysinfo/operating-system-version).
- One of three parameters (I(drive_letter), I(path) and I(label)) are mandatory to identify the target
volume but more than one cannot be specified at the same time.
- This module is idempotent if I(force) is not specified and file system labels remain preserved.
- For more information, see U(https://docs.microsoft.com/en-us/previous-versions/windows/desktop/stormgmt/format-msft-volume)
seealso:
- module: win_disk_facts
- module: win_partition
author:
- Varun Chopra (@chopraaa) <v@chopraaa.com>
'''
EXAMPLES = r'''
- name: Create a partition with drive letter D and size 5 GiB
win_partition:
drive_letter: D
partition_size: 5 GiB
disk_number: 1
- name: Full format the newly created partition as NTFS and label it
win_format:
drive_letter: D
file_system: NTFS
new_label: Formatted
full: True
'''
RETURN = r'''
#
'''

@ -1,145 +0,0 @@
#!powershell
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#Requires -Module Ansible.ModuleUtils.Legacy
Set-StrictMode -Version 2
$ErrorActionPreference = "Stop"
$system_path = "System\CurrentControlSet\Control\Session Manager\Environment"
$user_path = "Environment"
# list/arraylist methods don't allow IEqualityComparer override for case/backslash/quote-insensitivity, roll our own search
Function Get-IndexOfPathElement ($list, [string]$value) {
$idx = 0
$value = $value.Trim('"').Trim('\')
ForEach($el in $list) {
If ([string]$el.Trim('"').Trim('\') -ieq $value) {
return $idx
}
$idx++
}
return -1
}
# alters list in place, returns true if at least one element was added
Function Add-Elements ($existing_elements, $elements_to_add) {
$last_idx = -1
$changed = $false
ForEach($el in $elements_to_add) {
$idx = Get-IndexOfPathElement $existing_elements $el
# add missing elements at the end
If ($idx -eq -1) {
$last_idx = $existing_elements.Add($el)
$changed = $true
}
ElseIf ($idx -lt $last_idx) {
$existing_elements.RemoveAt($idx) | Out-Null
$existing_elements.Add($el) | Out-Null
$last_idx = $existing_elements.Count - 1
$changed = $true
}
Else {
$last_idx = $idx
}
}
return $changed
}
# alters list in place, returns true if at least one element was removed
Function Remove-Elements ($existing_elements, $elements_to_remove) {
$count = $existing_elements.Count
ForEach($el in $elements_to_remove) {
$idx = Get-IndexOfPathElement $existing_elements $el
$result.removed_idx = $idx
If ($idx -gt -1) {
$existing_elements.RemoveAt($idx)
}
}
return $count -ne $existing_elements.Count
}
# PS registry provider doesn't allow access to unexpanded REG_EXPAND_SZ; fall back to .NET
Function Get-RawPathVar ($scope) {
If ($scope -eq "user") {
$env_key = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey($user_path)
}
ElseIf ($scope -eq "machine") {
$env_key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey($system_path)
}
return $env_key.GetValue($var_name, "", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
}
Function Set-RawPathVar($path_value, $scope) {
If ($scope -eq "user") {
$var_path = "HKCU:\" + $user_path
}
ElseIf ($scope -eq "machine") {
$var_path = "HKLM:\" + $system_path
}
Set-ItemProperty $var_path -Name $var_name -Value $path_value -Type ExpandString | Out-Null
return $path_value
}
$parsed_args = Parse-Args $args -supports_check_mode $true
$result = @{changed=$false}
$var_name = Get-AnsibleParam $parsed_args "name" -Default "PATH"
$elements = Get-AnsibleParam $parsed_args "elements" -FailIfEmpty $result
$state = Get-AnsibleParam $parsed_args "state" -Default "present" -ValidateSet "present","absent"
$scope = Get-AnsibleParam $parsed_args "scope" -Default "machine" -ValidateSet "machine","user"
$check_mode = Get-AnsibleParam $parsed_args "_ansible_check_mode" -Default $false
If ($elements -is [string]) {
$elements = @($elements)
}
If ($elements -isnot [Array]) {
Fail-Json $result "elements must be a string or list of path strings"
}
$current_value = Get-RawPathVar $scope
$result.path_value = $current_value
# TODO: test case-canonicalization on wacky unicode values (eg turkish i)
# TODO: detect and warn/fail on unparseable path? (eg, unbalanced quotes, invalid path chars)
# TODO: detect and warn/fail if system path and Powershell isn't on it?
$existing_elements = New-Object System.Collections.ArrayList
# split on semicolons, accounting for quoted values with embedded semicolons (which may or may not be wrapped in whitespace)
$pathsplit_re = [regex] '((?<q>\s*"[^"]+"\s*)|(?<q>[^;]+))(;$|$|;)'
ForEach ($m in $pathsplit_re.Matches($current_value)) {
$existing_elements.Add($m.Groups['q'].Value) | Out-Null
}
If ($state -eq "absent") {
$result.changed = Remove-Elements $existing_elements $elements
}
ElseIf ($state -eq "present") {
$result.changed = Add-Elements $existing_elements $elements
}
# calculate the new path value from the existing elements
$path_value = [String]::Join(";", $existing_elements.ToArray())
$result.path_value = $path_value
If ($result.changed -and -not $check_mode) {
Set-RawPathVar $path_value $scope | Out-Null
}
Exit-Json $result

@ -1,79 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2016, Red Hat | Ansible
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This is a windows documentation stub. Actual code lives in the .ps1
# file of the same name
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'core'}
DOCUMENTATION = r'''
---
module: win_path
version_added: "2.3"
short_description: Manage Windows path environment variables
description:
- Allows element-based ordering, addition, and removal of Windows path environment variables.
options:
name:
description:
- Target path environment variable name.
type: str
default: PATH
elements:
description:
- A single path element, or a list of path elements (ie, directories) to add or remove.
- When multiple elements are included in the list (and C(state) is C(present)), the elements are guaranteed to appear in the same relative order
in the resultant path value.
- Variable expansions (eg, C(%VARNAME%)) are allowed, and are stored unexpanded in the target path element.
- Any existing path elements not mentioned in C(elements) are always preserved in their current order.
- New path elements are appended to the path, and existing path elements may be moved closer to the end to satisfy the requested ordering.
- Paths are compared in a case-insensitive fashion, and trailing backslashes are ignored for comparison purposes. However, note that trailing
backslashes in YAML require quotes.
type: list
required: yes
state:
description:
- Whether the path elements specified in C(elements) should be present or absent.
type: str
choices: [ absent, present ]
scope:
description:
- The level at which the environment variable specified by C(name) should be managed (either for the current user or global machine scope).
type: str
choices: [ machine, user ]
default: machine
notes:
- This module is for modifying individual elements of path-like
environment variables. For general-purpose management of other
environment vars, use the M(win_environment) module.
- This module does not broadcast change events.
This means that the minority of windows applications which can have
their environment changed without restarting will not be notified and
therefore will need restarting to pick up new environment settings.
- User level environment variables will require an interactive user to
log out and in again before they become available.
seealso:
- module: win_environment
author:
- Matt Davis (@nitzmahone)
'''
EXAMPLES = r'''
- name: Ensure that system32 and Powershell are present on the global system path, and in the specified order
win_path:
elements:
- '%SystemRoot%\system32'
- '%SystemRoot%\system32\WindowsPowerShell\v1.0'
- name: Ensure that C:\Program Files\MyJavaThing is not on the current user's CLASSPATH
win_path:
name: CLASSPATH
elements: C:\Program Files\MyJavaThing
scope: user
state: absent
'''

@ -1,67 +0,0 @@
#!/usr/bin/python
# coding: utf-8 -*-
# Copyright: (c) 2017, Dag Wieers <dag@wieers.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: win_tempfile
version_added: "2.3"
short_description: Creates temporary files and directories
description:
- Creates temporary files and directories.
- For non-Windows targets, please use the M(tempfile) module instead.
options:
state:
description:
- Whether to create file or directory.
type: str
choices: [ directory, file ]
default: file
path:
description:
- Location where temporary file or directory should be created.
- If path is not specified default system temporary directory (%TEMP%) will be used.
type: path
default: '%TEMP%'
aliases: [ dest ]
prefix:
description:
- Prefix of file/directory name created by module.
type: str
default: ansible.
suffix:
description:
- Suffix of file/directory name created by module.
type: str
default: ''
seealso:
- module: tempfile
author:
- Dag Wieers (@dagwieers)
'''
EXAMPLES = r"""
- name: Create temporary build directory
win_tempfile:
state: directory
suffix: build
- name: Create temporary file
win_tempfile:
state: file
suffix: temp
"""
RETURN = r'''
path:
description: The absolute path to the created file or directory.
returned: success
type: str
sample: C:\Users\Administrator\AppData\Local\Temp\ansible.bMlvdk
'''

@ -1,66 +0,0 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# this is a virtual module that is entirely implemented server side
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['stableinterface'],
'supported_by': 'core'}
DOCUMENTATION = r'''
---
module: win_template
version_added: "1.9.2"
short_description: Template a file out to a remote server
options:
backup:
description:
- Determine whether a backup should be created.
- When set to C(yes), create a backup file including the timestamp information
so you can get the original file back if you somehow clobbered it incorrectly.
type: bool
default: no
version_added: '2.8'
newline_sequence:
default: '\r\n'
force:
version_added: '2.4'
notes:
- Beware fetching files from windows machines when creating templates because certain tools, such as Powershell ISE,
and regedit's export facility add a Byte Order Mark as the first character of the file, which can cause tracebacks.
- You can use the M(win_copy) module with the C(content:) option if you prefer the template inline, as part of the
playbook.
- For Linux you can use M(template) which uses '\\n' as C(newline_sequence) by default.
seealso:
- module: win_copy
- module: copy
- module: template
author:
- Jon Hawkesworth (@jhawkesworth)
extends_documentation_fragment:
- template_common
'''
EXAMPLES = r'''
- name: Create a file from a Jinja2 template
win_template:
src: /mytemplates/file.conf.j2
dest: C:\Temp\file.conf
- name: Create a Unix-style file from a Jinja2 template
win_template:
src: unix/config.conf.j2
dest: C:\share\unix\config.conf
newline_sequence: '\n'
backup: yes
'''
RETURN = r'''
backup_file:
description: Name of the backup file that was created.
returned: if backup=yes
type: str
sample: C:\Path\To\File.txt.11540.20150212-220915.bak
'''
Loading…
Cancel
Save