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 [switch]$EnableCredSSP
) )
Function Write-Log Function Write-ProgressLog {
{
$Message = $args[0] $Message = $args[0]
Write-EventLog -LogName Application -Source $EventSource -EntryType Information -EventId 1 -Message $Message Write-EventLog -LogName Application -Source $EventSource -EntryType Information -EventId 1 -Message $Message
} }
Function Write-VerboseLog Function Write-VerboseLog {
{
$Message = $args[0] $Message = $args[0]
Write-Verbose $Message Write-Verbose $Message
Write-Log $Message Write-ProgressLog $Message
} }
Function Write-HostLog Function Write-HostLog {
{
$Message = $args[0] $Message = $args[0]
Write-Output $Message Write-Output $Message
Write-Log $Message Write-ProgressLog $Message
} }
Function New-LegacySelfSignedCert Function New-LegacySelfSignedCert {
{
Param ( Param (
[string]$SubjectName, [string]$SubjectName,
[int]$ValidDays = 1095 [int]$ValidDays = 1095
@ -129,8 +125,7 @@ Function New-LegacySelfSignedCert
$AlternativeName += $hostFQDN $AlternativeName += $hostFQDN
$IAlternativeNames = New-Object -ComObject X509Enrollment.CAlternativeNames $IAlternativeNames = New-Object -ComObject X509Enrollment.CAlternativeNames
foreach ($AN in $AlternativeName) foreach ($AN in $AlternativeName) {
{
$AltName = New-Object -ComObject X509Enrollment.CAlternativeName $AltName = New-Object -ComObject X509Enrollment.CAlternativeName
$AltName.InitializeFromString(0x3, $AN) $AltName.InitializeFromString(0x3, $AN)
$IAlternativeNames.Add($AltName) $IAlternativeNames.Add($AltName)
@ -162,8 +157,7 @@ Function New-LegacySelfSignedCert
return $parsed_cert.Thumbprint return $parsed_cert.Thumbprint
} }
Function Enable-GlobalHttpFirewallAccess Function Enable-GlobalHttpFirewallAccess {
{
Write-Verbose "Forcing global HTTP firewall access" Write-Verbose "Forcing global HTTP firewall access"
# this is a fairly naive implementation; could be more sophisticated about rule matching/collapsing # this is a fairly naive implementation; could be more sophisticated about rule matching/collapsing
$fw = New-Object -ComObject HNetCfg.FWPolicy2 $fw = New-Object -ComObject HNetCfg.FWPolicy2
@ -217,8 +211,7 @@ Function Enable-GlobalHttpFirewallAccess
} }
# Setup error handling. # Setup error handling.
Trap Trap {
{
$_ $_
Exit 1 Exit 1
} }
@ -232,65 +225,57 @@ $myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWin
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator $adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
# Check to see if we are currently running "as 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 "ERROR: You need elevated Administrator privileges in order to run this script."
Write-Output " Start Windows PowerShell by using the Run as Administrator option." Write-Output " Start Windows PowerShell by using the Run as Administrator option."
Exit 2 Exit 2
} }
$EventSource = $MyInvocation.MyCommand.Name $EventSource = $MyInvocation.MyCommand.Name
If (-Not $EventSource) If (-Not $EventSource) {
{
$EventSource = "Powershell CLI" $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 New-EventLog -LogName Application -Source $EventSource
} }
# Detect PowerShell version. # Detect PowerShell version.
If ($PSVersionTable.PSVersion.Major -lt 3) If ($PSVersionTable.PSVersion.Major -lt 3) {
{ Write-ProgressLog "PowerShell version 3 or higher is required."
Write-Log "PowerShell version 3 or higher is required."
Throw "PowerShell version 3 or higher is required." Throw "PowerShell version 3 or higher is required."
} }
# Find and start the WinRM service. # Find and start the WinRM service.
Write-Verbose "Verifying WinRM service." Write-Verbose "Verifying WinRM service."
If (!(Get-Service "WinRM")) If (!(Get-Service "WinRM")) {
{ Write-ProgressLog "Unable to find the WinRM service."
Write-Log "Unable to find the WinRM service."
Throw "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." Write-Verbose "Setting WinRM service to start automatically on boot."
Set-Service -Name "WinRM" -StartupType Automatic 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." Write-Verbose "Starting WinRM service."
Start-Service -Name "WinRM" -ErrorAction Stop 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. # 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) { If ($SkipNetworkProfileCheck) {
Write-Verbose "Enabling PS Remoting without checking Network profile." Write-Verbose "Enabling PS Remoting without checking Network profile."
Enable-PSRemoting -SkipNetworkProfileCheck -Force -ErrorAction Stop 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 { Else {
Write-Verbose "Enabling PS Remoting." Write-Verbose "Enabling PS Remoting."
Enable-PSRemoting -Force -ErrorAction Stop Enable-PSRemoting -Force -ErrorAction Stop
Write-Log "Enabled PS Remoting." Write-ProgressLog "Enabled PS Remoting."
} }
} }
Else Else {
{
Write-Verbose "PS Remoting is already enabled." Write-Verbose "PS Remoting is already enabled."
} }
@ -310,8 +295,7 @@ if ($token_value -ne 1) {
# Make sure there is a SSL listener. # Make sure there is a SSL listener.
$listeners = Get-ChildItem WSMan:\localhost\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 # We cannot use New-SelfSignedCertificate on 2012R2 and earlier
$thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays $thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays
Write-HostLog "Self-signed SSL certificate generated; thumbprint: $thumbprint" 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." Write-Verbose "Enabling SSL listener."
New-WSManInstance -ResourceURI 'winrm/config/Listener' -SelectorSet $selectorset -ValueSet $valueset 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." Write-Verbose "SSL listener is already active."
# Force a new SSL cert on Listener if the $ForceNewSSLCert # Force a new SSL cert on Listener if the $ForceNewSSLCert
If ($ForceNewSSLCert) If ($ForceNewSSLCert) {
{
# We cannot use New-SelfSignedCertificate on 2012R2 and earlier # We cannot use New-SelfSignedCertificate on 2012R2 and earlier
$thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays $thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays
@ -363,43 +345,35 @@ Else
# Check for basic authentication. # 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 ($DisableBasicAuth) {
{ If (($basicAuthSetting.Value) -eq $true) {
If (($basicAuthSetting.Value) -eq $true)
{
Write-Verbose "Disabling basic auth support." Write-Verbose "Disabling basic auth support."
Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $false 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." Write-Verbose "Basic auth is already disabled."
} }
} }
Else Else {
{ If (($basicAuthSetting.Value) -eq $false) {
If (($basicAuthSetting.Value) -eq $false)
{
Write-Verbose "Enabling basic auth support." Write-Verbose "Enabling basic auth support."
Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $true 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." Write-Verbose "Basic auth is already enabled."
} }
} }
# If EnableCredSSP if set to true # If EnableCredSSP if set to true
If ($EnableCredSSP) If ($EnableCredSSP) {
{
# Check for CredSSP authentication # Check for CredSSP authentication
$credsspAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object { $_.Name -eq "CredSSP" } $credsspAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object { $_.Name -eq "CredSSP" }
If (($credsspAuthSetting.Value) -eq $false) If (($credsspAuthSetting.Value) -eq $false) {
{
Write-Verbose "Enabling CredSSP auth support." Write-Verbose "Enabling CredSSP auth support."
Enable-WSManCredSSP -role server -Force 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. # Configure firewall to allow WinRM HTTPS connections.
$fwtest1 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS" $fwtest1 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS"
$fwtest2 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS" profile=any $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." 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 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." Write-Verbose "Updating firewall rule to allow WinRM HTTPS for any profile."
netsh advfirewall firewall set rule name="Allow WinRM HTTPS" new profile=any 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." Write-Verbose "Firewall rule already exists to allow WinRM HTTPS."
} }
# Test a remoting connection to localhost, which should work. # 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 $httpsOptions = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$httpsResult = New-PSSession -UseSSL -ComputerName "localhost" -SessionOption $httpsOptions -ErrorVariable httpsError -ErrorAction SilentlyContinue $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" Write-Verbose "HTTP: Enabled | HTTPS: Enabled"
} }
ElseIf ($httpsResult -and !$httpResult) ElseIf ($httpsResult -and !$httpResult) {
{
Write-Verbose "HTTP: Disabled | HTTPS: Enabled" Write-Verbose "HTTP: Disabled | HTTPS: Enabled"
} }
ElseIf ($httpResult -and !$httpsResult) ElseIf ($httpResult -and !$httpsResult) {
{
Write-Verbose "HTTP: Enabled | HTTPS: Disabled" Write-Verbose "HTTP: Enabled | HTTPS: Disabled"
} }
Else Else {
{ Write-ProgressLog "Unable to establish an HTTP or HTTPS remoting session."
Write-Log "Unable to establish an HTTP or HTTPS remoting session."
Throw "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." Write-VerboseLog "PS Remoting has been successfully configured for Ansible."

@ -18,24 +18,21 @@
# 6.3 is 2012 R2 # 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" Write-Output "Powershell 3 Installed already; You don't need this"
Exit Exit
} }
$powershellpath = "C:\powershell" $powershellpath = "C:\powershell"
function download-file function download-file {
{
param ([string]$path, [string]$local) param ([string]$path, [string]$local)
$client = new-object system.net.WebClient $client = new-object system.net.WebClient
$client.Headers.Add("user-agent", "PowerShell") $client.Headers.Add("user-agent", "PowerShell")
$client.downloadfile($path, $local) $client.downloadfile($path, $local)
} }
if (!(test-path $powershellpath)) if (!(test-path $powershellpath)) {
{
New-Item -ItemType directory -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. #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 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." Write-Output "OS is new; upgrade not needed."
Exit Exit
} }
@ -64,25 +60,20 @@ $osminor = [environment]::OSVersion.Version.Minor
$architecture = $ENV:PROCESSOR_ARCHITECTURE $architecture = $ENV:PROCESSOR_ARCHITECTURE
if ($architecture -eq "AMD64") if ($architecture -eq "AMD64") {
{
$architecture = "x64" $architecture = "x64"
} }
else else {
{
$architecture = "x86" $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" $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" $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. # Nothing to do; In theory this point will never be reached.
Exit Exit
} }

@ -49,7 +49,12 @@ $ps.Runspace = $rs
Write-AnsibleLog "INFO - adding global functions to PowerShell pipeline script" "async_watchdog" Write-AnsibleLog "INFO - adding global functions to PowerShell pipeline script" "async_watchdog"
$ps.AddScript($script:common_functions).AddStatement() > $null $ps.AddScript($script:common_functions).AddStatement() > $null
$ps.AddScript($script:wrapper_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" Write-AnsibleLog "INFO - adding $($actions[0]) to PowerShell pipeline script" "async_watchdog"
$ps.AddScript($entrypoint).AddArgument($payload) > $null $ps.AddScript($entrypoint).AddArgument($payload) > $null
@ -79,7 +84,8 @@ if ($job_async_result.IsCompleted) {
$module_result = ConvertFrom-AnsibleJson -InputObject $job_output $module_result = ConvertFrom-AnsibleJson -InputObject $job_output
# TODO: check for conflicting keys # TODO: check for conflicting keys
$result = $result + $module_result $result = $result + $module_result
} catch { }
catch {
$result.failed = $true $result.failed = $true
$result.msg = "failed to parse module output: $($_.Exception.Message)" $result.msg = "failed to parse module output: $($_.Exception.Message)"
# return output back to Ansible to help with debugging errors # 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 Set-Content -Path $resultfile_path -Value $result_json
Write-AnsibleLog "INFO - wrote output to $resultfile_path" "async_watchdog" Write-AnsibleLog "INFO - wrote output to $resultfile_path" "async_watchdog"
} else { }
else {
Write-AnsibleLog "ERROR - reached timeout on async job, stopping job" "async_watchdog" Write-AnsibleLog "ERROR - reached timeout on async job, stopping job" "async_watchdog"
$ps.BeginStop($null, $null) > $null # best effort stop $ps.BeginStop($null, $null) > $null # best effort stop

@ -74,7 +74,8 @@ $bootstrap_wrapper = {
try { try {
$pipe.Connect() $pipe.Connect()
$pipe.Read($input_bytes, 0, $bytes_length) > $null $pipe.Read($input_bytes, 0, $bytes_length) > $null
} finally { }
finally {
$pipe.Close() $pipe.Close()
} }
$exec = [System.Text.Encoding]::UTF8.GetString($input_bytes) $exec = [System.Text.Encoding]::UTF8.GetString($input_bytes)
@ -163,7 +164,8 @@ try {
$pipe.Write($payload_bytes, 0, $payload_bytes.Count) $pipe.Write($payload_bytes, 0, $payload_bytes.Count)
$pipe.Flush() $pipe.Flush()
$pipe.WaitForPipeDrain() $pipe.WaitForPipeDrain()
} finally { }
finally {
$pipe.Close() $pipe.Close()
} }

@ -17,7 +17,8 @@ Function Get-EnumValue($enum, $flag_type, $value) {
$raw_enum_value = $value.Replace('_', '') $raw_enum_value = $value.Replace('_', '')
try { try {
$enum_value = [Enum]::Parse($enum, $raw_enum_value, $true) $enum_value = [Enum]::Parse($enum, $raw_enum_value, $true)
} catch [System.ArgumentException] { }
catch [System.ArgumentException] {
$valid_options = [Enum]::GetNames($enum) | ForEach-Object -Process { $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() (($_ -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 return $enum_value
} }
Function Get-BecomeFlags($flags) { Function Get-BecomeFlag($flags) {
$logon_type = [Ansible.AccessToken.LogonType]::Interactive $logon_type = [Ansible.AccessToken.LogonType]::Interactive
$logon_flags = [Ansible.Become.LogonFlags]::WithProfile $logon_flags = [Ansible.Become.LogonFlags]::WithProfile
if ($null -eq $flags -or $flags -eq "") { if ($null -eq $flags -or $flags -eq "") {
$flag_split = @() $flag_split = @()
} elseif ($flags -is [string]) { }
elseif ($flags -is [string]) {
$flag_split = $flags.Split(" ") $flag_split = $flags.Split(" ")
} else { }
else {
throw "become_flags must be a string, was $($flags.GetType())" throw "become_flags must be a string, was $($flags.GetType())"
} }
@ -52,7 +55,8 @@ Function Get-BecomeFlags($flags) {
value = $flag_value value = $flag_value
} }
$logon_type = Get-EnumValue @enum_details $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_flag_values = $flag_value.Split(",")
$logon_flags = 0 -as [Ansible.Become.LogonFlags] $logon_flags = 0 -as [Ansible.Become.LogonFlags]
foreach ($logon_flag_value in $logon_flag_values) { foreach ($logon_flag_value in $logon_flag_values) {
@ -67,7 +71,8 @@ Function Get-BecomeFlags($flags) {
$logon_flag = Get-EnumValue @enum_details $logon_flag = Get-EnumValue @enum_details
$logon_flags = $logon_flags -bor $logon_flag $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'" 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 { try {
$logon_type, $logon_flags = Get-BecomeFlags -flags $Payload.become_flags $logon_type, $logon_flags = Get-BecomeFlag -flags $Payload.become_flags
} catch { }
catch {
Write-AnsibleError -Message "internal error: failed to parse become_flags '$($Payload.become_flags)'" -ErrorRecord $_ Write-AnsibleError -Message "internal error: failed to parse become_flags '$($Payload.become_flags)'" -ErrorRecord $_
$host.SetShouldExit(1) $host.SetShouldExit(1)
return return
@ -139,7 +145,8 @@ try {
$stdout = $result.StandardOut $stdout = $result.StandardOut
try { try {
$stdout = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($stdout)) $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 # 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" Write-AnsibleLog "WARN - become process stdout was not base64 encoded as expected: $stdout"
} }
@ -147,7 +154,8 @@ try {
$host.UI.WriteLine($stdout) $host.UI.WriteLine($stdout)
$host.UI.WriteErrorLine($result.StandardError.Trim()) $host.UI.WriteErrorLine($result.StandardError.Trim())
$host.SetShouldExit($result.ExitCode) $host.SetShouldExit($result.ExitCode)
} catch { }
catch {
Write-AnsibleError -Message "internal error: failed to become user '$username'" -ErrorRecord $_ Write-AnsibleError -Message "internal error: failed to become user '$username'" -ErrorRecord $_
$host.SetShouldExit(1) $host.SetShouldExit(1)
} }

@ -159,7 +159,8 @@ try {
try { try {
&$entrypoint @params &$entrypoint @params
} finally { }
finally {
# Processing here is kept to an absolute minimum to make sure each task runtime is kept as small as # 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 # possible. Once all the tests have been run ansible-test will collect this info and process it locally in
# one go. # one go.
@ -180,14 +181,16 @@ try {
$utf8_no_bom = New-Object -TypeName System.Text.UTF8Encoding -ArgumentList $false $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)) [System.IO.File]::WriteAllbytes($coverage_output_path, $utf8_no_bom.GetBytes($code_cov_json))
} }
} finally { }
finally {
try { try {
if ($breakpoint_info) { if ($breakpoint_info) {
foreach ($b in $breakpoint_info.Breakpoints) { foreach ($b in $breakpoint_info.Breakpoints) {
Remove-PSBreakpoint -Breakpoint $b Remove-PSBreakpoint -Breakpoint $b
} }
} }
} finally { }
finally {
Write-AnsibleLog "INFO - Remove temp coverage folder '$temp_path'" "coverage_wrapper" Write-AnsibleLog "INFO - Remove temp coverage folder '$temp_path'" "coverage_wrapper"
Remove-Item -LiteralPath $temp_path -Force -Recurse Remove-Item -LiteralPath $temp_path -Force -Recurse
} }

@ -31,7 +31,8 @@ begin {
$cmdlet = Get-Command -Name ConvertFrom-Json -CommandType Cmdlet $cmdlet = Get-Command -Name ConvertFrom-Json -CommandType Cmdlet
if ("AsHashtable" -in $cmdlet.Parameters.Keys) { if ("AsHashtable" -in $cmdlet.Parameters.Keys) {
return , (ConvertFrom-Json -InputObject $InputObject -AsHashtable) return , (ConvertFrom-Json -InputObject $InputObject -AsHashtable)
} else { }
else {
# get the PSCustomObject and then manually convert from there # get the PSCustomObject and then manually convert from there
$raw_obj = ConvertFrom-Json -InputObject $InputObject $raw_obj = ConvertFrom-Json -InputObject $InputObject
@ -48,13 +49,15 @@ begin {
$new_value.($prop.Name) = (ConvertTo-Hashtable -InputObject $prop.Value) $new_value.($prop.Name) = (ConvertTo-Hashtable -InputObject $prop.Value)
} }
return , $new_value return , $new_value
} elseif ($InputObject -is [Array]) { }
elseif ($InputObject -is [Array]) {
$new_value = [System.Collections.ArrayList]@() $new_value = [System.Collections.ArrayList]@()
foreach ($val in $InputObject) { foreach ($val in $InputObject) {
$new_value.Add((ConvertTo-Hashtable -InputObject $val)) > $null $new_value.Add((ConvertTo-Hashtable -InputObject $val)) > $null
} }
return , $new_value.ToArray() return , $new_value.ToArray()
} else { }
else {
return , $InputObject return , $InputObject
} }
} }
@ -150,7 +153,8 @@ $($ErrorRecord.InvocationInfo.PositionMessage)
[System.IO.FileAccess]::Write, [System.IO.FileShare]::ReadWrite) [System.IO.FileAccess]::Write, [System.IO.FileShare]::ReadWrite)
try { try {
$fs.Write($msg_bytes, 0, $msg_bytes.Length) $fs.Write($msg_bytes, 0, $msg_bytes.Length)
} finally { }
finally {
$fs.Close() $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" 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) { 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 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" 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) { 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 exit 1
} }
} }
@ -218,10 +224,12 @@ $($ErrorRecord.InvocationInfo.PositionMessage)
if ($encoded_output -and $null -ne $output) { if ($encoded_output -and $null -ne $output) {
$b64_output = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($output)) $b64_output = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($output))
Write-Output -InputObject $b64_output Write-Output -InputObject $b64_output
} else { }
else {
$output $output
} }
} catch { }
catch {
Write-AnsibleError -Message "internal error: failed to run exec_wrapper action $action" -ErrorRecord $_ Write-AnsibleError -Message "internal error: failed to run exec_wrapper action $action" -ErrorRecord $_
exit 1 exit 1
} }

@ -38,7 +38,8 @@ if ($Payload.ContainsKey("coverage") -and $null -ne $host.Runspace -and $null -n
$params = @{ $params = @{
Payload = $Payload Payload = $Payload
} }
} else { }
else {
# get the common module_wrapper code and invoke that to run the module # 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)) $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" }) $variables = [System.Collections.ArrayList]@(@{ Name = "complex_args"; Value = $Payload.module_args; Scope = "Global" })
@ -58,7 +59,8 @@ $entrypoint = [ScriptBlock]::Create($entrypoint)
try { try {
&$entrypoint @params &$entrypoint @params
} catch { }
catch {
# failed to invoke the PowerShell module, capture the exception and # failed to invoke the PowerShell module, capture the exception and
# output a pretty error for Ansible to parse # output a pretty error for Ansible to parse
$result = @{ $result = @{

@ -110,7 +110,8 @@ if ($Breakpoints.Count -gt 0) {
foreach ($b in $Breakpoints) { 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) $ps.Runspace.Debugger.SetBreakpoints($Breakpoints)
} }
} }
@ -126,7 +127,8 @@ $new_out = New-Object -TypeName System.IO.StringWriter -ArgumentList $sb
try { try {
[System.Console]::SetOut($new_out) [System.Console]::SetOut($new_out)
$module_output = $ps.Invoke() $module_output = $ps.Invoke()
} catch { }
catch {
# uncaught exception while executing module, present a prettier error for # uncaught exception while executing module, present a prettier error for
# Ansible to parse # Ansible to parse
$error_params = @{ $error_params = @{
@ -147,7 +149,8 @@ try {
Write-AnsibleError @error_params Write-AnsibleError @error_params
$host.SetShouldExit(1) $host.SetShouldExit(1)
return return
} finally { }
finally {
[System.Console]::SetOut($orig_out) [System.Console]::SetOut($orig_out)
$new_out.Dispose() $new_out.Dispose()
} }
@ -166,9 +169,11 @@ if ($ps.InvocationStateInfo.State -eq "Failed" -and $ModuleName -ne "script") {
# options. # options.
if ($null -eq $reason) { if ($null -eq $reason) {
$error_params.Message += ": Unknown error" $error_params.Message += ": Unknown error"
} elseif ($reason.PSObject.Properties.Name -contains "ErrorRecord") { }
elseif ($reason.PSObject.Properties.Name -contains "ErrorRecord") {
$error_params.ErrorRecord = $reason.ErrorRecord $error_params.ErrorRecord = $reason.ErrorRecord
} else { }
else {
$error_params.Message += ": $($reason.ToString())" $error_params.Message += ": $($reason.ToString())"
} }

@ -86,7 +86,8 @@ Function Add-CSharpType {
if ([System.IntPtr]::Size -eq 4) { if ([System.IntPtr]::Size -eq 4) {
$defined_symbols.Add('X86') > $null $defined_symbols.Add('X86') > $null
} else { }
else {
$defined_symbols.Add('AMD64') > $null $defined_symbols.Add('AMD64') > $null
} }
@ -100,10 +101,12 @@ Function Add-CSharpType {
if ($null -ne $is_windows) { if ($null -ne $is_windows) {
if ($is_windows.Value) { if ($is_windows.Value) {
$defined_symbols.Add("WINDOWS") > $null $defined_symbols.Add("WINDOWS") > $null
} else { }
else {
$defined_symbols.Add("UNIX") > $null $defined_symbols.Add("UNIX") > $null
} }
} else { }
else {
$defined_symbols.Add("WINDOWS") > $null $defined_symbols.Add("WINDOWS") > $null
} }
@ -155,7 +158,8 @@ Function Add-CSharpType {
$assembly_path = $match.Groups["Name"].Value $assembly_path = $match.Groups["Name"].Value
if ($parameter_type -eq "Type") { if ($parameter_type -eq "Type") {
$assembly_path = ([Type]$assembly_path).Assembly.Location $assembly_path = ([Type]$assembly_path).Assembly.Location
} else { }
else {
if (-not ([System.IO.Path]::IsPathRooted($assembly_path))) { if (-not ([System.IO.Path]::IsPathRooted($assembly_path))) {
$assembly_path = Join-Path -Path $lib_assembly_location -ChildPath $assembly_path $assembly_path = Join-Path -Path $lib_assembly_location -ChildPath $assembly_path
} }
@ -246,18 +250,21 @@ Function Add-CSharpType {
$code_ms.Seek(0, [System.IO.SeekOrigin]::Begin) > $null $code_ms.Seek(0, [System.IO.SeekOrigin]::Begin) > $null
$pdb_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) $compiled_assembly = [System.Runtime.Loader.AssemblyLoadContext]::Default.LoadFromStream($code_ms, $pdb_ms)
} finally { }
finally {
$code_ms.Close() $code_ms.Close()
$pdb_ms.Close() $pdb_ms.Close()
} }
} else { }
else {
# compile the code using CodeDom on PSDesktop # compile the code using CodeDom on PSDesktop
# configure compile options based on input # configure compile options based on input
if ($PSCmdlet.ParameterSetName -eq "Module") { if ($PSCmdlet.ParameterSetName -eq "Module") {
$temp_path = $AnsibleModule.Tmpdir $temp_path = $AnsibleModule.Tmpdir
$include_debug = $AnsibleModule.Verbosity -ge 3 $include_debug = $AnsibleModule.Verbosity -ge 3
} else { }
else {
$temp_path = $TempPath $temp_path = $TempPath
$include_debug = $IncludeDebugInfo.IsPresent $include_debug = $IncludeDebugInfo.IsPresent
} }

@ -14,7 +14,8 @@ Function Escape-Argument($argument, $force_quote=$false) {
# argument does not need escaping (and we don't want to force it), # argument does not need escaping (and we don't want to force it),
# return as is # return as is
return $argument return $argument
} else { }
else {
# we need to quote the arg so start with " # we need to quote the arg so start with "
$new_argument = '"' $new_argument = '"'
@ -32,12 +33,14 @@ Function Escape-Argument($argument, $force_quote=$false) {
# We are at the end of the string so we need to add the same \ # We are at the end of the string so we need to add the same \
# * 2 as the end char would be a " # * 2 as the end char would be a "
$new_argument += ("\" * ($num_backslashes + 1) * 2) $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 # we have a inline ", we need to add the existing \ but * by 2
# plus another 1 # plus another 1
$new_argument += ("\" * (($num_backslashes * 2) + 1)) $new_argument += ("\" * (($num_backslashes * 2) + 1))
$new_argument += $current_char $new_argument += $current_char
} else { }
else {
# normal character so no need to escape the \ we have counted # normal character so no need to escape the \ we have counted
$new_argument += ("\" * $num_backslashes) $new_argument += ("\" * $num_backslashes)
$new_argument += $current_char $new_argument += $current_char

@ -21,7 +21,8 @@ Function Backup-File {
$backup_path = "$path.$pid." + [DateTime]::Now.ToString("yyyyMMdd-HHmmss") + ".bak"; $backup_path = "$path.$pid." + [DateTime]::Now.ToString("yyyyMMdd-HHmmss") + ".bak";
Try { Try {
Copy-Item -LiteralPath $path -Destination $backup_path Copy-Item -LiteralPath $path -Destination $backup_path
} Catch { }
Catch {
throw "Failed to create backup file '$backup_path' from '$path'. ($($_.Exception.Message))" throw "Failed to create backup file '$backup_path' from '$path'. ($($_.Exception.Message))"
} }
} }

@ -28,9 +28,11 @@ Function Convert-ListToSnakeCase($list) {
foreach ($value in $list) { foreach ($value in $list) {
if ($value -is [Hashtable]) { if ($value -is [Hashtable]) {
$new_value = Convert-DictToSnakeCase -dict $value $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 $new_value = Convert-ListToSnakeCase -list $value
} else { }
else {
$new_value = $value $new_value = $value
} }
[void]$snake_list.Add($new_value) [void]$snake_list.Add($new_value)
@ -51,9 +53,11 @@ Function Convert-DictToSnakeCase($dict) {
$value = $dict_entry.Value $value = $dict_entry.Value
if ($value -is [Hashtable]) { if ($value -is [Hashtable]) {
$snake_dict.$snake_key = Convert-DictToSnakeCase -dict $value $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 $snake_dict.$snake_key = Convert-ListToSnakeCase -list $value
} else { }
else {
$snake_dict.$snake_key = $value $snake_dict.$snake_key = $value
} }
} }

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

@ -17,9 +17,11 @@ Function Test-AnsiblePath {
# Replacement for Test-Path # Replacement for Test-Path
try { try {
$file_attributes = [System.IO.File]::GetAttributes($Path) $file_attributes = [System.IO.File]::GetAttributes($Path)
} catch [System.IO.FileNotFoundException], [System.IO.DirectoryNotFoundException] { }
catch [System.IO.FileNotFoundException], [System.IO.DirectoryNotFoundException] {
return $false return $false
} catch [NotSupportedException] { }
catch [NotSupportedException] {
# When testing a path like Cert:\LocalMachine\My, System.IO.File will # When testing a path like Cert:\LocalMachine\My, System.IO.File will
# not work, we just revert back to using Test-Path for this # not work, we just revert back to using Test-Path for this
return Test-Path -Path $Path return Test-Path -Path $Path
@ -27,7 +29,8 @@ Function Test-AnsiblePath {
if ([Int32]$file_attributes -eq -1) { if ([Int32]$file_attributes -eq -1) {
return $false return $false
} else { }
else {
return $true return $true
} }
} }
@ -40,7 +43,8 @@ Function Get-AnsibleItem {
# Replacement for Get-Item # Replacement for Get-Item
try { try {
$file_attributes = [System.IO.File]::GetAttributes($Path) $file_attributes = [System.IO.File]::GetAttributes($Path)
} catch { }
catch {
# if -ErrorAction SilentlyCotinue is set on the cmdlet and we failed to # if -ErrorAction SilentlyCotinue is set on the cmdlet and we failed to
# get the attributes, just return $null, otherwise throw the error # get the attributes, just return $null, otherwise throw the error
if ($ErrorActionPreference -ne "SilentlyContinue") { if ($ErrorActionPreference -ne "SilentlyContinue") {
@ -50,9 +54,11 @@ Function Get-AnsibleItem {
} }
if ([Int32]$file_attributes -eq -1) { if ([Int32]$file_attributes -eq -1) {
throw New-Object -TypeName System.Management.Automation.ItemNotFoundException -ArgumentList "Cannot find path '$Path' because it does not exist." 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 return New-Object -TypeName System.IO.DirectoryInfo -ArgumentList $Path
} else { }
else {
return New-Object -TypeName System.IO.FileInfo -ArgumentList $Path return New-Object -TypeName System.IO.FileInfo -ArgumentList $Path
} }
} }

@ -4,8 +4,7 @@
Set-StrictMode -Version 2.0 Set-StrictMode -Version 2.0
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
Function Set-Attr($obj, $name, $value) Function Set-Attr($obj, $name, $value) {
{
<# <#
.SYNOPSIS .SYNOPSIS
Helper function to set an "attribute" on a psobject instance in PowerShell. Helper function to set an "attribute" on a psobject instance in PowerShell.
@ -16,23 +15,19 @@ Function Set-Attr($obj, $name, $value)
#> #>
# If the provided $obj is undefined, define one to be nice # If the provided $obj is undefined, define one to be nice
If (-not $obj.GetType) If (-not $obj.GetType) {
{
$obj = @{ } $obj = @{ }
} }
Try Try {
{
$obj.$name = $value $obj.$name = $value
} }
Catch Catch {
{
$obj | Add-Member -Force -MemberType NoteProperty -Name $name -Value $value $obj | Add-Member -Force -MemberType NoteProperty -Name $name -Value $value
} }
} }
Function Exit-Json($obj) Function Exit-Json($obj) {
{
<# <#
.SYNOPSIS .SYNOPSIS
Helper function to convert a PowerShell object to JSON and output it, exiting Helper function to convert a PowerShell object to JSON and output it, exiting
@ -42,8 +37,7 @@ Function Exit-Json($obj)
#> #>
# If the provided $obj is undefined, define one to be nice # If the provided $obj is undefined, define one to be nice
If (-not $obj.GetType) If (-not $obj.GetType) {
{
$obj = @{ } $obj = @{ }
} }
@ -55,8 +49,7 @@ Function Exit-Json($obj)
Exit Exit
} }
Function Fail-Json($obj, $message = $null) Function Fail-Json($obj, $message = $null) {
{
<# <#
.SYNOPSIS .SYNOPSIS
Helper function to add the "msg" property and "failed" property, convert the Helper function to add the "msg" property and "failed" property, convert the
@ -67,12 +60,14 @@ Function Fail-Json($obj, $message = $null)
if ($obj -is [hashtable] -or $obj -is [psobject]) { if ($obj -is [hashtable] -or $obj -is [psobject]) {
# Nothing to do # 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, # 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 # create a new Hashtable and use the arg as the failure message
$message = $obj $message = $obj
$obj = @{ } $obj = @{ }
} else { }
else {
# If the first argument is undefined or a different type, # If the first argument is undefined or a different type,
# make it a Hashtable # make it a Hashtable
$obj = @{ } $obj = @{ }
@ -90,8 +85,7 @@ Function Fail-Json($obj, $message = $null)
Exit 1 Exit 1
} }
Function Add-Warning($obj, $message) Function Add-Warning($obj, $message) {
{
<# <#
.SYNOPSIS .SYNOPSIS
Helper function to add warnings, even if the warnings attribute was Helper function to add warnings, even if the warnings attribute was
@ -101,15 +95,15 @@ Function Add-Warning($obj, $message)
if (-not $obj.ContainsKey("warnings")) { if (-not $obj.ContainsKey("warnings")) {
$obj.warnings = @() $obj.warnings = @()
} elseif ($obj.warnings -isnot [array]) { }
elseif ($obj.warnings -isnot [array]) {
throw "Add-Warning: warnings attribute is not an array" throw "Add-Warning: warnings attribute is not an array"
} }
$obj.warnings += $message $obj.warnings += $message
} }
Function Add-DeprecationWarning($obj, $message, $version = $null) Function Add-DeprecationWarning($obj, $message, $version = $null) {
{
<# <#
.SYNOPSIS .SYNOPSIS
Helper function to add deprecations, even if the deprecations attribute was Helper function to add deprecations, even if the deprecations attribute was
@ -118,7 +112,8 @@ Function Add-DeprecationWarning($obj, $message, $version = $null)
#> #>
if (-not $obj.ContainsKey("deprecations")) { if (-not $obj.ContainsKey("deprecations")) {
$obj.deprecations = @() $obj.deprecations = @()
} elseif ($obj.deprecations -isnot [array]) { }
elseif ($obj.deprecations -isnot [array]) {
throw "Add-DeprecationWarning: deprecations attribute is not a list" throw "Add-DeprecationWarning: deprecations attribute is not a list"
} }
@ -128,8 +123,7 @@ Function Add-DeprecationWarning($obj, $message, $version = $null)
} }
} }
Function Expand-Environment($value) Function Expand-Environment($value) {
{
<# <#
.SYNOPSIS .SYNOPSIS
Helper function to expand environment variables in values. By default Helper function to expand environment variables in values. By default
@ -137,13 +131,13 @@ Function Expand-Environment($value)
#> #>
if ($null -ne $value) { if ($null -ne $value) {
[System.Environment]::ExpandEnvironmentVariables($value) [System.Environment]::ExpandEnvironmentVariables($value)
} else { }
else {
$value $value
} }
} }
Function Get-AnsibleParam($obj, $name, $default = $null, $resultobj = @{}, $failifempty = $false, $emptyattributefailmessage, $ValidateSet, $ValidateSetErrorMessage, $type = $null, $aliases = @()) Function Get-AnsibleParam {
{
<# <#
.SYNOPSIS .SYNOPSIS
Helper function to get an "attribute" from a psobject instance in PowerShell. Helper function to get an "attribute" from a psobject instance in PowerShell.
@ -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 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. 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. # Check if the provided Member $name or aliases exist in $obj and return it or the default.
try { try {
@ -180,20 +186,24 @@ Function Get-AnsibleParam($obj, $name, $default = $null, $resultobj = @{}, $fail
if ($ValidateSet -contains ($obj.$name)) { if ($ValidateSet -contains ($obj.$name)) {
$value = $obj.$name $value = $obj.$name
} else { }
else {
if ($null -eq $ValidateSetErrorMessage) { if ($null -eq $ValidateSetErrorMessage) {
#Auto-generated error should be sufficient in most use cases #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)." $ValidateSetErrorMessage = "Get-AnsibleParam: Argument $name needs to be one of $($ValidateSet -join ",") but was $($obj.$name)."
} }
Fail-Json -obj $resultobj -message $ValidateSetErrorMessage Fail-Json -obj $resultobj -message $ValidateSetErrorMessage
} }
} else { }
else {
$value = $obj.$name $value = $obj.$name
} }
} catch { }
catch {
if ($failifempty -eq $false) { if ($failifempty -eq $false) {
$value = $default $value = $default
} else { }
else {
if (-not $emptyattributefailmessage) { if (-not $emptyattributefailmessage) {
$emptyattributefailmessage = "Get-AnsibleParam: Missing required argument: $name" $emptyattributefailmessage = "Get-AnsibleParam: Missing required argument: $name"
} }
@ -224,27 +234,35 @@ Function Get-AnsibleParam($obj, $name, $default = $null, $resultobj = @{}, $fail
Fail-Json -obj $resultobj -message "Get-AnsibleParam: Parameter '$name' has an invalid path '$value' specified." 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 # Convert str types to real Powershell strings
$value = $value.ToString() $value = $value.ToString()
} elseif ($type -eq "bool") { }
elseif ($type -eq "bool") {
# Convert boolean types to real Powershell booleans # Convert boolean types to real Powershell booleans
$value = $value | ConvertTo-Bool $value = $value | ConvertTo-Bool
} elseif ($type -eq "int") { }
elseif ($type -eq "int") {
# Convert int types to real Powershell integers # Convert int types to real Powershell integers
$value = $value -as [int] $value = $value -as [int]
} elseif ($type -eq "float") { }
elseif ($type -eq "float") {
# Convert float types to real Powershell floats # Convert float types to real Powershell floats
$value = $value -as [float] $value = $value -as [float]
} elseif ($type -eq "list") { }
elseif ($type -eq "list") {
if ($value -is [array]) { if ($value -is [array]) {
# Nothing to do # Nothing to do
} elseif ($value -is [string]) { }
elseif ($value -is [string]) {
# Convert string type to real Powershell array # Convert string type to real Powershell array
$value = $value.Split(",").Trim() $value = $value.Split(",").Trim()
} elseif ($value -is [int]) { }
elseif ($value -is [int]) {
$value = @($value) $value = @($value)
} else { }
else {
Fail-Json -obj $resultobj -message "Get-AnsibleParam: Parameter '$name' is not a YAML list." 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 # , is not a typo, forces it to return as a list when it is empty or only has 1 entry
@ -255,13 +273,11 @@ Function Get-AnsibleParam($obj, $name, $default = $null, $resultobj = @{}, $fail
} }
#Alias Get-attr-->Get-AnsibleParam for backwards compat. Only add when needed to ease debugging of scripts #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 New-Alias -Name Get-attr -Value Get-AnsibleParam
} }
Function ConvertTo-Bool Function ConvertTo-Bool {
{
<# <#
.SYNOPSIS .SYNOPSIS
Helper filter/pipeline function to convert a value to boolean following current Helper filter/pipeline function to convert a value to boolean following current
@ -274,18 +290,20 @@ Function ConvertTo-Bool
$obj $obj
) )
process {
$boolean_strings = "yes", "on", "1", "true", 1 $boolean_strings = "yes", "on", "1", "true", 1
$obj_string = [string]$obj $obj_string = [string]$obj
if (($obj -is [boolean] -and $obj) -or $boolean_strings -contains $obj_string.ToLower()) { if (($obj -is [boolean] -and $obj) -or $boolean_strings -contains $obj_string.ToLower()) {
return $true return $true
} else { }
else {
return $false return $false
} }
} }
}
Function Parse-Args($arguments, $supports_check_mode = $false) Function Parse-Args {
{
<# <#
.SYNOPSIS .SYNOPSIS
Helper function to parse Ansible JSON arguments from a "file" passed as Helper function to parse Ansible JSON arguments from a "file" passed as
@ -293,17 +311,18 @@ Function Parse-Args($arguments, $supports_check_mode = $false)
.EXAMPLE .EXAMPLE
$params = Parse-Args $args $params = Parse-Args $args
#> #>
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Cannot change the name now")]
param ($arguments, $supports_check_mode = $false)
$params = New-Object psobject $params = New-Object psobject
If ($arguments.Length -gt 0) If ($arguments.Length -gt 0) {
{
$params = Get-Content $arguments[0] | ConvertFrom-Json $params = Get-Content $arguments[0] | ConvertFrom-Json
} }
Else { Else {
$params = $complex_args $params = $complex_args
} }
$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false $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 @{ Exit-Json @{
skipped = $true skipped = $true
changed = $false 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 .SYNOPSIS
Helper function to calculate a hash of a file in a way which PowerShell 3 Helper function to calculate a hash of a file in a way which PowerShell 3
and above can handle and above can handle
#> #>
If (Test-Path -LiteralPath $path -PathType Leaf) If (Test-Path -LiteralPath $path -PathType Leaf) {
{ switch ($algorithm) {
switch ($algorithm)
{
'md5' { $sp = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider } 'md5' { $sp = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider }
'sha1' { $sp = New-Object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider } 'sha1' { $sp = New-Object -TypeName System.Security.Cryptography.SHA1CryptoServiceProvider }
'sha256' { $sp = New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider } 'sha256' { $sp = New-Object -TypeName System.Security.Cryptography.SHA256CryptoServiceProvider }
@ -336,25 +352,23 @@ Function Get-FileChecksum($path, $algorithm = 'sha1')
If ($PSVersionTable.PSVersion.Major -ge 4) { If ($PSVersionTable.PSVersion.Major -ge 4) {
$raw_hash = Get-FileHash -LiteralPath $path -Algorithm $algorithm $raw_hash = Get-FileHash -LiteralPath $path -Algorithm $algorithm
$hash = $raw_hash.Hash.ToLower() $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); $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(); $hash = [System.BitConverter]::ToString($sp.ComputeHash($fp)).Replace("-", "").ToLower();
$fp.Dispose(); $fp.Dispose();
} }
} }
ElseIf (Test-Path -LiteralPath $path -PathType Container) ElseIf (Test-Path -LiteralPath $path -PathType Container) {
{
$hash = "3"; $hash = "3";
} }
Else Else {
{
$hash = "1"; $hash = "1";
} }
return $hash return $hash
} }
Function Get-PendingRebootStatus Function Get-PendingRebootStatus {
{
<# <#
.SYNOPSIS .SYNOPSIS
Check if reboot is required, if so notify CA. Check if reboot is required, if so notify CA.
@ -362,13 +376,12 @@ Function Get-PendingRebootStatus
#> #>
$featureData = Invoke-CimMethod -EA Ignore -Name GetServerFeature -Namespace root\microsoft\windows\servermanager -Class MSFT_ServerManagerTasks $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 $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"} $CBSRebootStatus = Get-ChildItem "HKLM:\\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing" -ErrorAction SilentlyContinue |
if(($featureData -and $featureData.RequiresReboot) -or $regData -or $CBSRebootStatus) Where-Object { $_.PSChildName -eq "RebootPending" }
{ if (($featureData -and $featureData.RequiresReboot) -or $regData -or $CBSRebootStatus) {
return $True return $True
} }
else else {
{
return $False return $False
} }
} }

@ -3,7 +3,10 @@
#Requires -Module Ansible.ModuleUtils.PrivilegeUtil #Requires -Module Ansible.ModuleUtils.PrivilegeUtil
Function Load-LinkUtils() { Function Load-LinkUtils {
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification = "Cannot change the name now")]
param ()
$link_util = @' $link_util = @'
using Microsoft.Win32.SafeHandles; using Microsoft.Win32.SafeHandles;
using System; using System;
@ -314,8 +317,12 @@ namespace Ansible
throw new Exception(errorMessage); throw new Exception(errorMessage);
} }
string printName = new string(buffer.PathBuffer, (int)(buffer.PrintNameOffset / SIZE_OF_WCHAR) + pathOffset, (int)(buffer.PrintNameLength / SIZE_OF_WCHAR)); string printName = new string(buffer.PathBuffer,
string substituteName = new string(buffer.PathBuffer, (int)(buffer.SubstituteNameOffset / SIZE_OF_WCHAR) + pathOffset, (int)(buffer.SubstituteNameLength / SIZE_OF_WCHAR)); (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 // 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 // Remove the leading Windows object directory \?\ from the path if present

@ -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" $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)) { 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 Add-DeprecationWarning -obj $result.Value -message $msg -version 2.12
} else { }
else {
$module = Get-Variable -Name module -ErrorAction SilentlyContinue $module = Get-Variable -Name module -ErrorAction SilentlyContinue
if ($null -ne $module -and $module.Value.GetType().FullName -eq "Ansible.Basic.AnsibleModule") { if ($null -ne $module -and $module.Value.GetType().FullName -eq "Ansible.Basic.AnsibleModule") {
$module.Value.Deprecate($msg, "2.12") $module.Value.Deprecate($msg, "2.12")
@ -49,7 +50,8 @@ Function Get-AnsiblePrivilege {
if ($privilege_info.ContainsKey($Name)) { if ($privilege_info.ContainsKey($Name)) {
$status = $privilege_info.$Name $status = $privilege_info.$Name
return $status.HasFlag([Ansible.Privilege.PrivilegeAttributes]::Enabled) return $status.HasFlag([Ansible.Privilege.PrivilegeAttributes]::Enabled)
} else { }
else {
return $null return $null
} }
} }
@ -82,7 +84,8 @@ Function Set-AnsiblePrivilege {
$current_state = Get-AnsiblePrivilege -Name $Name $current_state = Get-AnsiblePrivilege -Name $Name
if ($current_state -eq $Value) { if ($current_state -eq $Value) {
return # no change needs to occur 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 # 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" 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) $account_object = New-Object System.Security.Principal.SecurityIdentifier($sid)
try { try {
$nt_account = $account_object.Translate([System.Security.Principal.NTAccount]) $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)" 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 { 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) param($account_name)
# Converts an account name to a SID, it can take in the following forms # 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 # SID: Will just return the SID value that was passed in
@ -34,21 +36,25 @@ Function Convert-ToSID {
try { try {
$sid = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $account_name $sid = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $account_name
return $sid.Value return $sid.Value
} catch {} }
catch {}
if ($account_name -like "*\*") { if ($account_name -like "*\*") {
$account_name_split = $account_name -split "\\" $account_name_split = $account_name -split "\\"
if ($account_name_split[0] -eq ".") { if ($account_name_split[0] -eq ".") {
$domain = $env:COMPUTERNAME $domain = $env:COMPUTERNAME
} else { }
else {
$domain = $account_name_split[0] $domain = $account_name_split[0]
} }
$username = $account_name_split[1] $username = $account_name_split[1]
} elseif ($account_name -like "*@*") { }
elseif ($account_name -like "*@*") {
$account_name_split = $account_name -split "@" $account_name_split = $account_name -split "@"
$domain = $account_name_split[1] $domain = $account_name_split[1]
$username = $account_name_split[0] $username = $account_name_split[0]
} else { }
else {
$domain = $null $domain = $null
$username = $account_name $username = $account_name
} }
@ -59,15 +65,18 @@ Function Convert-ToSID {
if ($domain -eq $env:COMPUTERNAME) { if ($domain -eq $env:COMPUTERNAME) {
$adsi = [ADSI]("WinNT://$env:COMPUTERNAME,computer") $adsi = [ADSI]("WinNT://$env:COMPUTERNAME,computer")
$group = $adsi.psbase.children | Where-Object { $_.schemaClassName -eq "group" -and $_.Name -eq $username } $group = $adsi.psbase.children | Where-Object { $_.schemaClassName -eq "group" -and $_.Name -eq $username }
} else { }
else {
$group = $null $group = $null
} }
if ($group) { if ($group) {
$account = New-Object System.Security.Principal.NTAccount($username) $account = New-Object System.Security.Principal.NTAccount($username)
} else { }
else {
$account = New-Object System.Security.Principal.NTAccount($domain, $username) $account = New-Object System.Security.Principal.NTAccount($domain, $username)
} }
} else { }
else {
# when in a domain NTAccount(String) will favour domain lookups check # when in a domain NTAccount(String) will favour domain lookups check
# if username is a local user and explictly search on the localhost for # if username is a local user and explictly search on the localhost for
# that account # that account
@ -75,14 +84,16 @@ Function Convert-ToSID {
$user = $adsi.psbase.children | Where-Object { $_.schemaClassName -eq "user" -and $_.Name -eq $username } $user = $adsi.psbase.children | Where-Object { $_.schemaClassName -eq "user" -and $_.Name -eq $username }
if ($user) { if ($user) {
$account = New-Object System.Security.Principal.NTAccount($env:COMPUTERNAME, $username) $account = New-Object System.Security.Principal.NTAccount($env:COMPUTERNAME, $username)
} else { }
else {
$account = New-Object System.Security.Principal.NTAccount($username) $account = New-Object System.Security.Principal.NTAccount($username)
} }
} }
try { try {
$account_sid = $account.Translate([System.Security.Principal.SecurityIdentifier]) $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)" 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]) { if ($UseDefaultCredential -and $web_request -is [System.Net.HttpWebRequest]) {
$web_request.UseDefaultCredentials = $true $web_request.UseDefaultCredentials = $true
} elseif ($UrlUsername) { }
elseif ($UrlUsername) {
if ($ForceBasicAuth) { if ($ForceBasicAuth) {
$auth_value = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $UrlUsername, $UrlPassword))) $auth_value = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $UrlUsername, $UrlPassword)))
$web_request.Headers.Add("Authorization", "Basic $auth_value") $web_request.Headers.Add("Authorization", "Basic $auth_value")
} else { }
else {
$credential = New-Object -TypeName System.Net.NetworkCredential -ArgumentList $UrlUsername, $UrlPassword $credential = New-Object -TypeName System.Net.NetworkCredential -ArgumentList $UrlUsername, $UrlPassword
$web_request.Credentials = $credential $web_request.Credentials = $credential
} }
@ -238,7 +240,8 @@ Function Get-AnsibleWebRequest {
$cert = New-Object -TypeName "$crypto_ns.X509Certificate2" -ArgumentList @( $cert = New-Object -TypeName "$crypto_ns.X509Certificate2" -ArgumentList @(
$ClientCert, $ClientCertPassword $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 Write-Error -Message "Failed to read client certificate at '$ClientCert'" -Exception $_.Exception -Category SecurityError
return return
} }
@ -250,9 +253,11 @@ Function Get-AnsibleWebRequest {
if (-not $UseProxy) { if (-not $UseProxy) {
$proxy = $null $proxy = $null
} elseif ($ProxyUrl) { }
elseif ($ProxyUrl) {
$proxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $ProxyUrl, $true $proxy = New-Object -TypeName System.Net.WebProxy -ArgumentList $ProxyUrl, $true
} else { }
else {
$proxy = $web_request.Proxy $proxy = $web_request.Proxy
} }
@ -264,11 +269,13 @@ Function Get-AnsibleWebRequest {
# property. We cannot set UseDefaultCredentials so we just set the Credentials to the # property. We cannot set UseDefaultCredentials so we just set the Credentials to the
# DefaultCredentials in the CredentialCache which does the same thing. # DefaultCredentials in the CredentialCache which does the same thing.
$proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials
} elseif ($ProxyUsername) { }
elseif ($ProxyUsername) {
$proxy.Credentials = New-Object -TypeName System.Net.NetworkCredential -ArgumentList @( $proxy.Credentials = New-Object -TypeName System.Net.NetworkCredential -ArgumentList @(
$ProxyUsername, $ProxyPassword $ProxyUsername, $ProxyPassword
) )
} else { }
else {
$proxy.Credentials = $null $proxy.Credentials = $null
} }
} }
@ -305,7 +312,8 @@ Function Get-AnsibleWebRequest {
if ($Headers -and $Headers.ContainsKey("User-Agent")) { if ($Headers -and $Headers.ContainsKey("User-Agent")) {
if ($HttpAgent -eq $ansible_web_request_options.http_agent.default) { if ($HttpAgent -eq $ansible_web_request_options.http_agent.default) {
$HttpAgent = $Headers['User-Agent'] $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") $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 { safe {
if ($web_request.Method -in @("GET", "HEAD")) { if ($web_request.Method -in @("GET", "HEAD")) {
$web_request.AllowAutoRedirect = $true $web_request.AllowAutoRedirect = $true
} else { }
else {
$web_request.AllowAutoRedirect = $false $web_request.AllowAutoRedirect = $false
} }
} }
@ -325,7 +334,8 @@ Function Get-AnsibleWebRequest {
if ($MaximumRedirection -eq 0) { if ($MaximumRedirection -eq 0) {
$web_request.AllowAutoRedirect = $false $web_request.AllowAutoRedirect = $false
} else { }
else {
$web_request.MaximumAutomaticRedirections = $MaximumRedirection $web_request.MaximumAutomaticRedirections = $MaximumRedirection
} }
} }
@ -415,7 +425,8 @@ Function Invoke-WithWebRequest {
try { try {
$Body.CopyTo($request_st) $Body.CopyTo($request_st)
$request_st.Flush() $request_st.Flush()
} finally { }
finally {
$request_st.Close() $request_st.Close()
} }
} }
@ -423,7 +434,8 @@ Function Invoke-WithWebRequest {
try { try {
try { try {
$web_response = $Request.GetResponse() $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 # 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 # 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 # 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 $Module.Result.status_code = $_.Exception.Response.StatusCode
throw $_ throw $_
} }
} else { }
else {
throw $_ throw $_
} }
} }
@ -445,7 +458,8 @@ Function Invoke-WithWebRequest {
# A FileWebResponse won't have these properties set # A FileWebResponse won't have these properties set
$Module.Result.msg = "OK" $Module.Result.msg = "OK"
$Module.Result.status_code = 200 $Module.Result.status_code = 200
} else { }
else {
$Module.Result.msg = $web_response.StatusDescription $Module.Result.msg = $web_response.StatusDescription
$Module.Result.status_code = $web_response.StatusCode $Module.Result.status_code = $web_response.StatusCode
} }
@ -454,10 +468,12 @@ Function Invoke-WithWebRequest {
try { try {
# Invoke the ScriptBlock and pass in WebResponse and ResponseStream # Invoke the ScriptBlock and pass in WebResponse and ResponseStream
&$Script -Response $web_response -Stream $response_stream &$Script -Response $web_response -Stream $response_stream
} finally { }
finally {
$response_stream.Dispose() $response_stream.Dispose()
} }
} finally { }
finally {
if ($web_response) { if ($web_response) {
$web_response.Close() $web_response.Close()
} }

@ -15,22 +15,24 @@ $module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$test_username = $module.Params.test_username $test_username = $module.Params.test_username
$test_password = $module.Params.test_password $test_password = $module.Params.test_password
Function Assert-Equals { Function Assert-Equal {
param( param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual, [Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected [Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
) )
process {
$matched = $false $matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) { 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++) { for ($i = 0; $i -lt $Actual.Count; $i++) {
$actual_value = $Actual[$i] $actual_value = $Actual[$i]
$expected_value = $Expected[$i] $expected_value = $Expected[$i]
Assert-Equals -Actual $actual_value -Expected $expected_value Assert-Equal -Actual $actual_value -Expected $expected_value
} }
$matched = $true $matched = $true
} else { }
else {
$matched = $Actual -ceq $Expected $matched = $Actual -ceq $Expected
} }
@ -49,6 +51,7 @@ Function Assert-Equals {
$module.FailJson("AssertionError: actual != expected") $module.FailJson("AssertionError: actual != expected")
} }
} }
}
$current_user = [System.Security.Principal.WindowsIdentity]::GetCurrent().User $current_user = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
@ -58,15 +61,16 @@ $tests = [Ordered]@{
$h_token = [Ansible.AccessToken.TokenUtil]::OpenProcessToken($h_process, "Query") $h_token = [Ansible.AccessToken.TokenUtil]::OpenProcessToken($h_process, "Query")
try { try {
$h_token.IsClosed | Assert-Equals -Expected $false $h_token.IsClosed | Assert-Equal -Expected $false
$h_token.IsInvalid | Assert-Equals -Expected $false $h_token.IsInvalid | Assert-Equal -Expected $false
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token) $actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token)
$actual_user | Assert-Equals -Expected $current_user $actual_user | Assert-Equal -Expected $current_user
} finally { }
finally {
$h_token.Dispose() $h_token.Dispose()
} }
$h_token.IsClosed | Assert-Equals -Expected $true $h_token.IsClosed | Assert-Equal -Expected $true
} }
"Open process token of another process" = { "Open process token of another process" = {
@ -74,21 +78,24 @@ $tests = [Ordered]@{
try { try {
$h_process = [Ansible.AccessToken.TokenUtil]::OpenProcess($proc_info.Id, "QueryInformation", $false) $h_process = [Ansible.AccessToken.TokenUtil]::OpenProcess($proc_info.Id, "QueryInformation", $false)
try { try {
$h_process.IsClosed | Assert-Equals -Expected $false $h_process.IsClosed | Assert-Equal -Expected $false
$h_process.IsInvalid | Assert-Equals -Expected $false $h_process.IsInvalid | Assert-Equal -Expected $false
$h_token = [Ansible.AccessToken.TokenUtil]::OpenProcessToken($h_process, "Query") $h_token = [Ansible.AccessToken.TokenUtil]::OpenProcessToken($h_process, "Query")
try { try {
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token) $actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token)
$actual_user | Assert-Equals -Expected $current_user $actual_user | Assert-Equal -Expected $current_user
} finally { }
finally {
$h_token.Dispose() $h_token.Dispose()
} }
} finally { }
finally {
$h_process.Dispose() $h_process.Dispose()
} }
$h_process.IsClosed | Assert-Equals -Expected $true $h_process.IsClosed | Assert-Equal -Expected $true
} finally { }
finally {
$proc_info | Stop-Process $proc_info | Stop-Process
} }
} }
@ -98,11 +105,13 @@ $tests = [Ordered]@{
try { try {
$h_process = [Ansible.AccessToken.TokenUtil]::OpenProcess(4, "QueryInformation", $false) $h_process = [Ansible.AccessToken.TokenUtil]::OpenProcess(4, "QueryInformation", $false)
$h_process.Dispose() # Incase this doesn't fail, make sure we still dispose of it $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 $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" = { "Duplicate access token primary" = {
@ -111,22 +120,24 @@ $tests = [Ordered]@{
try { try {
$dup_token = [Ansible.AccessToken.TokenUtil]::DuplicateToken($h_token, "Query", "Anonymous", "Primary") $dup_token = [Ansible.AccessToken.TokenUtil]::DuplicateToken($h_token, "Query", "Anonymous", "Primary")
try { try {
$dup_token.IsClosed | Assert-Equals -Expected $false $dup_token.IsClosed | Assert-Equal -Expected $false
$dup_token.IsInvalid | Assert-Equals -Expected $false $dup_token.IsInvalid | Assert-Equal -Expected $false
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($dup_token) $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 = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($dup_token)
$actual_stat.TokenType | Assert-Equals -Expected ([Ansible.AccessToken.TokenType]::Primary) $actual_stat.TokenType | Assert-Equal -Expected ([Ansible.AccessToken.TokenType]::Primary)
$actual_stat.ImpersonationLevel | Assert-Equals -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]::Anonymous) $actual_stat.ImpersonationLevel | Assert-Equal -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]::Anonymous)
} finally { }
finally {
$dup_token.Dispose() $dup_token.Dispose()
} }
$dup_token.IsClosed | Assert-Equals -Expected $true $dup_token.IsClosed | Assert-Equal -Expected $true
} finally { }
finally {
$h_token.Dispose() $h_token.Dispose()
} }
} }
@ -140,16 +151,18 @@ $tests = [Ordered]@{
try { try {
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($dup_token) $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 = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($dup_token)
$actual_stat.TokenType | Assert-Equals -Expected ([Ansible.AccessToken.TokenType]::Impersonation) $actual_stat.TokenType | Assert-Equal -Expected ([Ansible.AccessToken.TokenType]::Impersonation)
$actual_stat.ImpersonationLevel | Assert-Equals -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]"$_") $actual_stat.ImpersonationLevel | Assert-Equal -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]"$_")
} finally { }
finally {
$dup_token.Dispose() $dup_token.Dispose()
} }
} }
} finally { }
finally {
$h_token.Dispose() $h_token.Dispose()
} }
} }
@ -162,25 +175,26 @@ $tests = [Ordered]@{
$tested = $false $tested = $false
foreach ($h_token in [Ansible.AccessToken.TokenUtil]::EnumerateUserTokens($system_sid, "Duplicate, Impersonate, Query")) { foreach ($h_token in [Ansible.AccessToken.TokenUtil]::EnumerateUserTokens($system_sid, "Duplicate, Impersonate, Query")) {
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token) $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) [Ansible.AccessToken.TokenUtil]::ImpersonateToken($h_token)
try { try {
$current_sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User $current_sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
$current_sid | Assert-Equals -Expected $system_sid $current_sid | Assert-Equal -Expected $system_sid
} finally { }
finally {
[Ansible.AccessToken.TokenUtil]::RevertToSelf() [Ansible.AccessToken.TokenUtil]::RevertToSelf()
} }
$current_sid = [System.Security.Principal.WindowsIdentity]::GetCurrent().User $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 # Will keep on looping for each SYSTEM token it can retrieve, we only want to test 1
$tested = $true $tested = $true
break break
} }
$tested | Assert-Equals -Expected $true $tested | Assert-Equal -Expected $true
} }
"Get token privileges" = { "Get token privileges" = {
@ -191,8 +205,8 @@ $tests = [Ordered]@{
$actual_privs = [Ansible.AccessToken.Tokenutil]::GetTokenPrivileges($h_token) $actual_privs = [Ansible.AccessToken.Tokenutil]::GetTokenPrivileges($h_token)
$actual_stat = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($h_token) $actual_stat = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($h_token)
$actual_privs.Count | Assert-Equals -Expected $priv_info.Count $actual_privs.Count | Assert-Equal -Expected $priv_info.Count
$actual_privs.Count | Assert-Equals -Expected $actual_stat.PrivilegeCount $actual_privs.Count | Assert-Equal -Expected $actual_stat.PrivilegeCount
foreach ($info in $priv_info) { foreach ($info in $priv_info) {
$info_split = $info.Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries) $info_split = $info.Split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)
@ -200,14 +214,16 @@ $tests = [Ordered]@{
$priv_enabled = $info_split[-1] -eq "Enabled" $priv_enabled = $info_split[-1] -eq "Enabled"
$actual_priv = $actual_privs | Where-Object { $_.Name -eq $priv_name } $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) { if ($priv_enabled) {
$actual_priv.Attributes.HasFlag([Ansible.AccessToken.PrivilegeAttributes]::Enabled) | 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-Equals -Expected $true else {
$actual_priv.Attributes.HasFlag([Ansible.AccessToken.PrivilegeAttributes]::Disabled) | Assert-Equal -Expected $true
}
} }
} }
} finally { finally {
$h_token.Dispose() $h_token.Dispose()
} }
} }
@ -219,25 +235,27 @@ $tests = [Ordered]@{
$actual_priv = [Ansible.AccessToken.Tokenutil]::GetTokenPrivileges($h_token) $actual_priv = [Ansible.AccessToken.Tokenutil]::GetTokenPrivileges($h_token)
$actual_stat = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($h_token) $actual_stat = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($h_token)
$actual_stat.TokenId.GetType().FullName | Assert-Equals -Expected "Ansible.AccessToken.Luid" $actual_stat.TokenId.GetType().FullName | Assert-Equal -Expected "Ansible.AccessToken.Luid"
$actual_stat.AuthenticationId.GetType().FullName | Assert-Equals -Expected "Ansible.AccessToken.Luid" $actual_stat.AuthenticationId.GetType().FullName | Assert-Equal -Expected "Ansible.AccessToken.Luid"
$actual_stat.ExpirationTime.GetType().FullName | Assert-Equals -Expected "System.Int64" $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 $os_version = [Version](Get-Item -LiteralPath $env:SystemRoot\System32\kernel32.dll).VersionInfo.ProductVersion
if ($os_version -lt [Version]"6.1") { 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 # 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) $actual_stat.ImpersonationLevel | Assert-Equal -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]::Impersonation)
} else { }
$actual_stat.ImpersonationLevel | Assert-Equals -Expected ([Ansible.AccessToken.SecurityImpersonationLevel]::Anonymous) else {
} $actual_stat.ImpersonationLevel | Assert-Equal -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.DynamicCharged.GetType().FullName | Assert-Equal -Expected "System.UInt32"
$actual_stat.GroupCount.GetType().FullName | Assert-Equals -Expected "System.UInt32" $actual_stat.DynamicAvailable.GetType().FullName | Assert-Equal -Expected "System.UInt32"
$actual_stat.PrivilegeCount | Assert-Equals -Expected $actual_priv.Count $actual_stat.GroupCount.GetType().FullName | Assert-Equal -Expected "System.UInt32"
$actual_stat.ModifiedId.GetType().FullName | Assert-Equals -Expected "Ansible.AccessToken.Luid" $actual_stat.PrivilegeCount | Assert-Equal -Expected $actual_priv.Count
} finally { $actual_stat.ModifiedId.GetType().FullName | Assert-Equal -Expected "Ansible.AccessToken.Luid"
}
finally {
$h_token.Dispose() $h_token.Dispose()
} }
} }
@ -246,23 +264,25 @@ $tests = [Ordered]@{
$h_token = [Ansible.AccessToken.TokenUtil]::LogonUser($test_username, $null, $test_password, "Interactive", "Default") $h_token = [Ansible.AccessToken.TokenUtil]::LogonUser($test_username, $null, $test_password, "Interactive", "Default")
try { try {
$actual_elevation_type = [Ansible.AccessToken.TokenUtil]::GetTokenElevationType($h_token) $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) $actual_linked = [Ansible.AccessToken.TokenUtil]::GetTokenLinkedToken($h_token)
try { try {
$actual_linked.IsClosed | Assert-Equals -Expected $false $actual_linked.IsClosed | Assert-Equal -Expected $false
$actual_linked.IsInvalid | Assert-Equals -Expected $false $actual_linked.IsInvalid | Assert-Equal -Expected $false
$actual_elevation_type = [Ansible.AccessToken.TokenUtil]::GetTokenElevationType($actual_linked) $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 = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($actual_linked)
$actual_stat.TokenType | Assert-Equals -Expected ([Ansible.AccessToken.TokenType]::Impersonation) $actual_stat.TokenType | Assert-Equal -Expected ([Ansible.AccessToken.TokenType]::Impersonation)
} finally { }
finally {
$actual_linked.Dispose() $actual_linked.Dispose()
} }
$actual_linked.IsClosed | Assert-Equals -Expected $true $actual_linked.IsClosed | Assert-Equal -Expected $true
} finally { }
finally {
$h_token.Dispose() $h_token.Dispose()
} }
} }
@ -286,29 +306,32 @@ $tests = [Ordered]@{
try { try {
$actual_linked = [Ansible.AccessToken.TokenUtil]::GetTokenLinkedToken($h_token) $actual_linked = [Ansible.AccessToken.TokenUtil]::GetTokenLinkedToken($h_token)
try { try {
$actual_linked.IsClosed | Assert-Equals -Expected $false $actual_linked.IsClosed | Assert-Equal -Expected $false
$actual_linked.IsInvalid | Assert-Equals -Expected $false $actual_linked.IsInvalid | Assert-Equal -Expected $false
$actual_elevation_type = [Ansible.AccessToken.TokenUtil]::GetTokenElevationType($actual_linked) $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 = [Ansible.AccessToken.TokenUtil]::GetTokenStatistics($actual_linked)
$actual_stat.TokenType | Assert-Equals -Expected ([Ansible.AccessToken.TokenType]::Primary) $actual_stat.TokenType | Assert-Equal -Expected ([Ansible.AccessToken.TokenType]::Primary)
} finally { }
finally {
$actual_linked.Dispose() $actual_linked.Dispose()
} }
$actual_linked.IsClosed | Assert-Equals -Expected $true $actual_linked.IsClosed | Assert-Equal -Expected $true
} finally { }
finally {
[Ansible.AccessToken.TokenUtil]::RevertToSelf() [Ansible.AccessToken.TokenUtil]::RevertToSelf()
} }
} finally { }
finally {
$h_token.Dispose() $h_token.Dispose()
} }
$tested = $true $tested = $true
break break
} }
$tested | Assert-Equals -Expected $true $tested | Assert-Equal -Expected $true
} }
"Failed to get token information" = { "Failed to get token information" = {
@ -318,13 +341,16 @@ $tests = [Ordered]@{
$failed = $false $failed = $false
try { try {
[Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token) [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token)
} catch [Ansible.AccessToken.Win32Exception] { }
catch [Ansible.AccessToken.Win32Exception] {
$failed = $true $failed = $true
$_.Exception.Message | Assert-Equals -Expected "GetTokenInformation(TokenUser) failed to get buffer length (Access is denied, Win32ErrorCode 5 - 0x00000005)" $msg = "GetTokenInformation(TokenUser) failed to get buffer length (Access is denied, Win32ErrorCode 5 - 0x00000005)"
} finally { $_.Exception.Message | Assert-Equal -Expected $msg
}
finally {
$h_token.Dispose() $h_token.Dispose()
} }
$failed | Assert-Equals -Expected $true $failed | Assert-Equal -Expected $true
} }
"Logon with valid credentials" = { "Logon with valid credentials" = {
@ -333,39 +359,42 @@ $tests = [Ordered]@{
$h_token = [Ansible.AccessToken.TokenUtil]::LogonUser($test_username, $null, $test_password, "Network", "Default") $h_token = [Ansible.AccessToken.TokenUtil]::LogonUser($test_username, $null, $test_password, "Network", "Default")
try { try {
$h_token.IsClosed | Assert-Equals -Expected $false $h_token.IsClosed | Assert-Equal -Expected $false
$h_token.IsInvalid | Assert-Equals -Expected $false $h_token.IsInvalid | Assert-Equal -Expected $false
$actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token) $actual_user = [Ansible.AccessToken.TokenUtil]::GetTokenUser($h_token)
$actual_user | Assert-Equals -Expected $expected_sid $actual_user | Assert-Equal -Expected $expected_sid
} finally { }
finally {
$h_token.Dispose() $h_token.Dispose()
} }
$h_token.IsClosed | Assert-Equals -Expected $true $h_token.IsClosed | Assert-Equal -Expected $true
} }
"Logon with invalid credentials" = { "Logon with invalid credentials" = {
$failed = $false $failed = $false
try { try {
[Ansible.AccessToken.TokenUtil]::LogonUser("fake-user", $null, "fake-pass", "Network", "Default") [Ansible.AccessToken.TokenUtil]::LogonUser("fake-user", $null, "fake-pass", "Network", "Default")
} catch [Ansible.AccessToken.Win32Exception] { }
catch [Ansible.AccessToken.Win32Exception] {
$failed = $true $failed = $true
$_.Exception.Message.Contains("Failed to logon fake-user") | Assert-Equals -Expected $true $_.Exception.Message.Contains("Failed to logon fake-user") | Assert-Equal -Expected $true
$_.Exception.Message.Contains("Win32ErrorCode 1326 - 0x0000052E)") | Assert-Equals -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" = { "Logon with invalid credential with domain account" = {
$failed = $false $failed = $false
try { try {
[Ansible.AccessToken.TokenUtil]::LogonUser("fake-user", "fake-domain", "fake-pass", "Network", "Default") [Ansible.AccessToken.TokenUtil]::LogonUser("fake-user", "fake-domain", "fake-pass", "Network", "Default")
} catch [Ansible.AccessToken.Win32Exception] { }
catch [Ansible.AccessToken.Win32Exception] {
$failed = $true $failed = $true
$_.Exception.Message.Contains("Failed to logon fake-domain\fake-user") | 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-Equals -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, @{}) $module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
Function Assert-Equals { Function Assert-Equal {
param( param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual, [Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected [Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
) )
process {
$matched = $false $matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) { 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++) { for ($i = 0; $i -lt $Actual.Count; $i++) {
$actual_value = $Actual[$i] $actual_value = $Actual[$i]
$expected_value = $Expected[$i] $expected_value = $Expected[$i]
Assert-Equals -Actual $actual_value -Expected $expected_value Assert-Equal -Actual $actual_value -Expected $expected_value
} }
$matched = $true $matched = $true
} else { }
else {
$matched = $Actual -ceq $Expected $matched = $Actual -ceq $Expected
} }
@ -38,6 +40,7 @@ Function Assert-Equals {
$module.FailJson("AssertionError: actual != expected") $module.FailJson("AssertionError: actual != expected")
} }
} }
}
# Would be great to move win_whomai out into it's own module util and share the # Would be great to move win_whomai out into it's own module util and share the
# code here, for now just rely on a cut down version # code here, for now just rely on a cut down version
@ -437,101 +440,101 @@ $tests = @{
"Runas standard user" = { "Runas standard user" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script") "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive" $stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
} }
"Runas admin user" = { "Runas admin user" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script") "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive" $stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
} }
"Runas SYSTEM" = { "Runas SYSTEM" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script") "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "System" $stdout.LogonType | Assert-Equal -Expected "System"
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected "S-1-5-18" $stdout.UserSid.Value | Assert-Equal -Expected "S-1-5-18"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $system_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $system_integrity_sid
$with_domain = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NT AUTHORITY\System", $null, "whoami.exe") $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" = { "Runas LocalService" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("LocalService", $null, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("LocalService", $null,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script") "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Service" $stdout.LogonType | Assert-Equal -Expected "Service"
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected "S-1-5-19" $stdout.UserSid.Value | Assert-Equal -Expected "S-1-5-19"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $system_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $system_integrity_sid
$with_domain = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NT AUTHORITY\LocalService", $null, "whoami.exe") $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" = { "Runas NetworkService" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NetworkService", $null, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NetworkService", $null,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script") "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Service" $stdout.LogonType | Assert-Equal -Expected "Service"
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected "S-1-5-20" $stdout.UserSid.Value | Assert-Equal -Expected "S-1-5-20"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $system_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $system_integrity_sid
$with_domain = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("NT AUTHORITY\NetworkService", $null, "whoami.exe") $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" = { "Runas without working dir set" = {
$expected = "$env:SystemRoot\system32`r`n" $expected = "$env:SystemRoot\system32`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null,
'powershell.exe $pwd.Path', $null, $null, "") 'powershell.exe $pwd.Path', $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"Runas with working dir set" = { "Runas with working dir set" = {
$expected = "$env:SystemRoot`r`n" $expected = "$env:SystemRoot`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null,
'powershell.exe $pwd.Path', $env:SystemRoot, $null, "") 'powershell.exe $pwd.Path', $env:SystemRoot, $null, "")
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"Runas without environment set" = { "Runas without environment set" = {
$expected = "Windows_NT`r`n" $expected = "Windows_NT`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, 0, "Interactive", $null,
'powershell.exe $env:TEST; $env:OS', $null, $null, "") 'powershell.exe $env:TEST; $env:OS', $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"Runas with environment set" = { "Runas with environment set" = {
@ -541,52 +544,53 @@ $tests = @{
} }
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null,
'cmd.exe /c set', $null, $env_vars, "") 'cmd.exe /c set', $null, $env_vars, "")
("TEST=tesTing" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equals -Expected $true ("TEST=tesTing" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equal -Expected $true
("TEST2=Testing 2" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equals -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-Equals -Expected $true ("OS=Windows_NT" -cnotin $actual.StandardOut.Split("`r`n")) | Assert-Equal -Expected $true
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"Runas with string stdin" = { "Runas with string stdin" = {
$expected = "input value`r`n`r`n" $expected = "input value`r`n`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null,
'powershell.exe [System.Console]::In.ReadToEnd()', $null, $null, "input value") 'powershell.exe [System.Console]::In.ReadToEnd()', $null, $null, "input value")
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"Runas with string stdin and newline" = { "Runas with string stdin and newline" = {
$expected = "input value`r`n`r`n" $expected = "input value`r`n`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null,
'powershell.exe [System.Console]::In.ReadToEnd()', $null, $null, "input value`r`n") 'powershell.exe [System.Console]::In.ReadToEnd()', $null, $null, "input value`r`n")
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"Runas with byte stdin" = { "Runas with byte stdin" = {
$expected = "input value`r`n" $expected = "input value`r`n"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, "Interactive", $null, $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")) 'powershell.exe [System.Console]::In.ReadToEnd()', $null, $null, [System.Text.Encoding]::UTF8.GetBytes("input value"))
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"Missing executable" = { "Missing executable" = {
$failed = $false $failed = $false
try { try {
[Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, "fake.exe") [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, "fake.exe")
} catch { }
catch {
$failed = $true $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 = 'Exception calling "CreateProcessAsUser" with "3" argument(s): "CreateProcessWithTokenW() failed '
$expected += '(The system cannot find the file specified, Win32ErrorCode 2)"' $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" = { "CreateProcessAsUser with lpApplicationName" = {
@ -594,112 +598,114 @@ $tests = @{
$full_path = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe" $full_path = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe"
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $full_path, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $full_path,
"Write-Output 'abc'", $null, $null, "") "Write-Output 'abc'", $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $full_path, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $full_path,
"powershell.exe Write-Output 'abc'", $null, $null, "") "powershell.exe Write-Output 'abc'", $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcessAsUser with stderr" = { "CreateProcessAsUser with stderr" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $null, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $null,
"powershell.exe [System.Console]::Error.WriteLine('hi')", $null, $null, "") "powershell.exe [System.Console]::Error.WriteLine('hi')", $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected "" $actual.StandardOut | Assert-Equal -Expected ""
$actual.StandardError | Assert-Equals -Expected "hi`r`n" $actual.StandardError | Assert-Equal -Expected "hi`r`n"
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcessAsUser with exit code" = { "CreateProcessAsUser with exit code" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $null, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("SYSTEM", $null, 0, "Interactive", $null,
"powershell.exe exit 10", $null, $null, "") "powershell.exe exit 10", $null, $null, "")
$actual.StandardOut | Assert-Equals -Expected "" $actual.StandardOut | Assert-Equal -Expected ""
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 10 $actual.ExitCode | Assert-Equal -Expected 10
} }
"Local account with computer name" = { "Local account with computer name" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("$env:COMPUTERNAME\$standard_user", $become_pass, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("$env:COMPUTERNAME\$standard_user", $become_pass,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script") "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive" $stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
} }
"Local account with computer as period" = { "Local account with computer as period" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser(".\$standard_user", $become_pass, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser(".\$standard_user", $become_pass,
"powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script") "powershell.exe -NoProfile -ExecutionPolicy ByPass -File $tmp_script")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive" $stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
} }
"Local account with invalid password" = { "Local account with invalid password" = {
$failed = $false $failed = $false
try { try {
[Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, "incorrect", "powershell.exe Write-Output abc") [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, "incorrect", "powershell.exe Write-Output abc")
} catch { }
catch {
$failed = $true $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 # 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" = { "Invalid account" = {
$failed = $false $failed = $false
try { try {
[Ansible.Become.BecomeUtil]::CreateProcessAsUser("incorrect", "incorrect", "powershell.exe Write-Output abc") [Ansible.Become.BecomeUtil]::CreateProcessAsUser("incorrect", "incorrect", "powershell.exe Write-Output abc")
} catch { }
catch {
$failed = $true $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 = 'Exception calling "CreateProcessAsUser" with "3" argument(s): "Some or all '
$expected += 'identity references could not be translated."' $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" = { "Interactive logon with standard" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive" $stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi" $stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
} }
"Batch logon with standard" = { "Batch logon with standard" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
"Batch", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Batch", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Batch" $stdout.LogonType | Assert-Equal -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi" $stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
} }
"Network logon with standard" = { "Network logon with standard" = {
@ -709,15 +715,15 @@ $tests = @{
} }
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
"Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Network" $stdout.LogonType | Assert-Equal -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi" $stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
} }
"Network with cleartext logon with standard" = { "Network with cleartext logon with standard" = {
@ -727,31 +733,31 @@ $tests = @{
} }
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, $become_pass, "WithProfile",
"NetworkCleartext", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "NetworkCleartext", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "NetworkCleartext" $stdout.LogonType | Assert-Equal -Expected "NetworkCleartext"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi" $stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
} }
"Logon without password with standard" = { "Logon without password with standard" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, [NullString]::Value, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, [NullString]::Value, "WithProfile",
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
# Too unstable, there might be another process still lingering which causes become to steal instead of using # 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 # S4U. Just don't check the type and source to verify we can become without a password
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
# $stdout.LogonType | Assert-Equals -Expected "Batch" # $stdout.LogonType | Assert-Equal -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
# $stdout.SourceName | Assert-Equals -Expected "ansible" # $stdout.SourceName | Assert-Equal -Expected "ansible"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
} }
"Logon without password and network type with standard" = { "Logon without password and network type with standard" = {
@ -761,45 +767,45 @@ $tests = @{
} }
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, [NullString]::Value, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($standard_user, [NullString]::Value, "WithProfile",
"Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
# Too unstable, there might be another process still lingering which causes become to steal instead of using # 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 # S4U. Just don't check the type and source to verify we can become without a password
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
# $stdout.LogonType | Assert-Equals -Expected "Network" # $stdout.LogonType | Assert-Equal -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $medium_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $medium_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
# $stdout.SourceName | Assert-Equals -Expected "ansible" # $stdout.SourceName | Assert-Equal -Expected "ansible"
$stdout.UserSid.Value | Assert-Equals -Expected $standard_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $standard_user_sid
} }
"Interactive logon with admin" = { "Interactive logon with admin" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive" $stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi" $stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
} }
"Batch logon with admin" = { "Batch logon with admin" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
"Batch", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Batch", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Batch" $stdout.LogonType | Assert-Equal -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi" $stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
} }
"Network logon with admin" = { "Network logon with admin" = {
@ -809,15 +815,15 @@ $tests = @{
} }
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
"Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Network" $stdout.LogonType | Assert-Equal -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi" $stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
} }
"Network with cleartext logon with admin" = { "Network with cleartext logon with admin" = {
@ -827,15 +833,15 @@ $tests = @{
} }
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, "WithProfile",
"NetworkCleartext", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "NetworkCleartext", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "NetworkCleartext" $stdout.LogonType | Assert-Equal -Expected "NetworkCleartext"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
$stdout.SourceName | Assert-Equals -Expected "Advapi" $stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
} }
"Fail to logon with null or empty password" = { "Fail to logon with null or empty password" = {
@ -847,29 +853,30 @@ $tests = @{
# string won't go the S4U route. # string won't go the S4U route.
[Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $null, "WithProfile", [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $null, "WithProfile",
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
} catch { }
catch {
$failed = $true $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 # 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" = { "Logon without password with admin" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, [NullString]::Value, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, [NullString]::Value, "WithProfile",
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
# Too unstable, there might be another process still lingering which causes become to steal instead of using # 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 # S4U. Just don't check the type and source to verify we can become without a password
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
# $stdout.LogonType | Assert-Equals -Expected "Batch" # $stdout.LogonType | Assert-Equal -Expected "Batch"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
# $stdout.SourceName | Assert-Equals -Expected "ansible" # $stdout.SourceName | Assert-Equal -Expected "ansible"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
} }
"Logon without password and network type with admin" = { "Logon without password and network type with admin" = {
@ -879,17 +886,17 @@ $tests = @{
} }
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, [NullString]::Value, "WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, [NullString]::Value, "WithProfile",
"Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Network", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
# Too unstable, there might be another process still lingering which causes become to steal instead of using # 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 # S4U. Just don't check the type and source to verify we can become without a password
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
# $stdout.LogonType | Assert-Equals -Expected "Network" # $stdout.LogonType | Assert-Equal -Expected "Network"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $true $stdout.ProfileLoaded | Assert-Equal -Expected $true
# $stdout.SourceName | Assert-Equals -Expected "ansible" # $stdout.SourceName | Assert-Equal -Expected "ansible"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
} }
"Logon without profile with admin" = { "Logon without profile with admin" = {
@ -900,45 +907,45 @@ $tests = @{
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0, $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser($admin_user, $become_pass, 0,
"Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "Interactive", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "Interactive" $stdout.LogonType | Assert-Equal -Expected "Interactive"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $high_integrity_sid $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $high_integrity_sid
$stdout.ProfileLoaded | Assert-Equals -Expected $false $stdout.ProfileLoaded | Assert-Equal -Expected $false
$stdout.SourceName | Assert-Equals -Expected "Advapi" $stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $admin_user_sid $stdout.UserSid.Value | Assert-Equal -Expected $admin_user_sid
} }
"Logon with network credentials and no profile" = { "Logon with network credentials and no profile" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("fakeuser", "fakepassword", "NetcredentialsOnly", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("fakeuser", "fakepassword", "NetcredentialsOnly",
"NewCredentials", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "NewCredentials", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "NewCredentials" $stdout.LogonType | Assert-Equal -Expected "NewCredentials"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $current_user.MandatoryLabelSid.Value $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 # while we didn't set WithProfile, the new process is based on the current process
$stdout.ProfileLoaded | Assert-Equals -Expected $current_user.ProfileLoaded $stdout.ProfileLoaded | Assert-Equal -Expected $current_user.ProfileLoaded
$stdout.SourceName | Assert-Equals -Expected "Advapi" $stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $current_user.UserSid.Value $stdout.UserSid.Value | Assert-Equal -Expected $current_user.UserSid.Value
} }
"Logon with network credentials and with profile" = { "Logon with network credentials and with profile" = {
$actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("fakeuser", "fakepassword", "NetcredentialsOnly, WithProfile", $actual = [Ansible.Become.BecomeUtil]::CreateProcessAsUser("fakeuser", "fakepassword", "NetcredentialsOnly, WithProfile",
"NewCredentials", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n") "NewCredentials", $null, "powershell.exe -NoProfile -", $tmp_dir, $null, $test_whoami + "`r`n")
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$stdout = ConvertFrom-Json -InputObject $actual.StandardOut $stdout = ConvertFrom-Json -InputObject $actual.StandardOut
$stdout.LogonType | Assert-Equals -Expected "NewCredentials" $stdout.LogonType | Assert-Equal -Expected "NewCredentials"
$stdout.MandatoryLabelSid.Value | Assert-Equals -Expected $current_user.MandatoryLabelSid.Value $stdout.MandatoryLabelSid.Value | Assert-Equal -Expected $current_user.MandatoryLabelSid.Value
$stdout.ProfileLoaded | Assert-Equals -Expected $current_user.ProfileLoaded $stdout.ProfileLoaded | Assert-Equal -Expected $current_user.ProfileLoaded
$stdout.SourceName | Assert-Equals -Expected "Advapi" $stdout.SourceName | Assert-Equal -Expected "Advapi"
$stdout.UserSid.Value | Assert-Equals -Expected $current_user.UserSid.Value $stdout.UserSid.Value | Assert-Equal -Expected $current_user.UserSid.Value
} }
} }
@ -965,7 +972,8 @@ try {
$user_obj = $adsi.Create("User", $user) $user_obj = $adsi.Create("User", $user)
$user_obj.SetPassword($become_pass) $user_obj.SetPassword($become_pass)
$user_obj.SetInfo() $user_obj.SetInfo()
} else { }
else {
$user_obj.SetPassword($become_pass) $user_obj.SetPassword($become_pass)
} }
$user_obj.RefreshCache() $user_obj.RefreshCache()
@ -973,13 +981,17 @@ try {
if ($user -eq $standard_user) { if ($user -eq $standard_user) {
$standard_user_sid = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList @($user_obj.ObjectSid.Value, 0)).Value $standard_user_sid = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList @($user_obj.ObjectSid.Value, 0)).Value
$group = [System.Security.Principal.WellKnownSidType]::BuiltinUsersSid $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 $admin_user_sid = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList @($user_obj.ObjectSid.Value, 0)).Value
$group = [System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid $group = [System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid
} }
$group = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $group, $null).Value $group = (New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $group, $null).Value
[string[]]$current_groups = $user_obj.Groups() | ForEach-Object { [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) { if ($current_groups -notcontains $group) {
$group_obj = $adsi.Children | Where-Object { $group_obj = $adsi.Children | Where-Object {
@ -995,7 +1007,8 @@ try {
$test = $test_impl.Key $test = $test_impl.Key
&$test_impl.Value &$test_impl.Value
} }
} finally { }
finally {
Remove-Item -LiteralPath $tmp_dir -Force -Recurse Remove-Item -LiteralPath $tmp_dir -Force -Recurse
foreach ($user in $standard_user, $admin_user) { foreach ($user in $standard_user, $admin_user) {
$user_obj = $adsi.Children | Where-Object { $_.SchemaClassName -eq "User" -and $_.Name -eq $user } $user_obj = $adsi.Children | Where-Object { $_.SchemaClassName -eq "User" -and $_.Name -eq $user }

@ -9,10 +9,13 @@ $result = @{
changed = $false changed = $false
} }
Function Assert-Equals($actual, $expected) { Function Assert-Equal($actual, $expected) {
if ($actual -cne $expected) { if ($actual -cne $expected) {
$call_stack = (Get-PSCallStack)[1] $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 Fail-Json -obj $result -message $error_msg
} }
} }
@ -34,15 +37,16 @@ namespace Namespace1
} }
'@ '@
$res = Add-CSharpType -References $code $res = Add-CSharpType -References $code
Assert-Equals -actual $res -expected $null Assert-Equal -actual $res -expected $null
$actual = [Namespace1.Class1]::GetString($false) $actual = [Namespace1.Class1]::GetString($false)
Assert-Equals $actual -expected "Hello World" Assert-Equal $actual -expected "Hello World"
try { try {
[Namespace1.Class1]::GetString($true) [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 = @' $code_debug = @'
@ -62,17 +66,18 @@ namespace Namespace2
} }
'@ '@
$res = Add-CSharpType -References $code_debug -IncludeDebugInfo $res = Add-CSharpType -References $code_debug -IncludeDebugInfo
Assert-Equals -actual $res -expected $null Assert-Equal -actual $res -expected $null
$actual = [Namespace2.Class2]::GetString($false) $actual = [Namespace2.Class2]::GetString($false)
Assert-Equals $actual -expected "Hello World" Assert-Equal $actual -expected "Hello World"
try { try {
[Namespace2.Class2]::GetString($true) [Namespace2.Class2]::GetString($true)
} catch { }
catch {
$tmp_path = [System.IO.Path]::GetFullPath($env:TMP).ToLower() $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-Equal ($_.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().Contains(".cs:line 10")) -expected $true
} }
$code_tmp = @' $code_tmp = @'
@ -93,19 +98,21 @@ namespace Namespace3
'@ '@
$tmp_path = $env:USERPROFILE $tmp_path = $env:USERPROFILE
$res = Add-CSharpType -References $code_tmp -IncludeDebugInfo -TempPath $tmp_path -PassThru $res = Add-CSharpType -References $code_tmp -IncludeDebugInfo -TempPath $tmp_path -PassThru
Assert-Equals -actual $res.GetType().Name -expected "RuntimeAssembly" Assert-Equal -actual $res.GetType().Name -expected "RuntimeAssembly"
Assert-Equals -actual $res.Location -expected "" Assert-Equal -actual $res.Location -expected ""
Assert-Equals -actual $res.GetTypes().Length -expected 1 Assert-Equal -actual $res.GetTypes().Length -expected 1
Assert-Equals -actual $res.GetTypes()[0].Name -expected "Class3" Assert-Equal -actual $res.GetTypes()[0].Name -expected "Class3"
$actual = [Namespace3.Class3]::GetString($false) $actual = [Namespace3.Class3]::GetString($false)
Assert-Equals $actual -expected "Hello World" Assert-Equal $actual -expected "Hello World"
try { try {
[Namespace3.Class3]::GetString($true) [Namespace3.Class3]::GetString($true)
} catch { }
Assert-Equals ($_.Exception.ToString().ToLower().Contains("at namespace3.class3.getstring(boolean error) in $($tmp_path.ToLower())")) -expected $true catch {
Assert-Equals ($_.Exception.ToString().Contains(".cs:line 10")) -expected $true $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 = @' $warning_code = @'
@ -130,15 +137,17 @@ namespace Namespace4
$failed = $false $failed = $false
try { try {
Add-CSharpType -References $warning_code Add-CSharpType -References $warning_code
} catch { }
catch {
$failed = $true $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 Add-CSharpType -References $warning_code -IgnoreWarnings
$actual = [Namespace4.Class4]::GetString($true) $actual = [Namespace4.Class4]::GetString($true)
Assert-Equals -actual $actual -expected "Hello World" Assert-Equal -actual $actual -expected "Hello World"
$reference_1 = @' $reference_1 = @'
using System; using System;
@ -181,7 +190,7 @@ namespace Namespace6
Add-CSharpType -References $reference_1, $reference_2 Add-CSharpType -References $reference_1, $reference_2
$actual = [Namespace6.Class6]::GetString() $actual = [Namespace6.Class6]::GetString()
Assert-Equals -actual $actual -expected "Hello World" Assert-Equal -actual $actual -expected "Hello World"
$ignored_warning = @' $ignored_warning = @'
using System; using System;
@ -202,7 +211,7 @@ namespace Namespace7
'@ '@
Add-CSharpType -References $ignored_warning Add-CSharpType -References $ignored_warning
$actual = [Namespace7.Class7]::GetString() $actual = [Namespace7.Class7]::GetString()
Assert-Equals -actual $actual -expected "abc" Assert-Equal -actual $actual -expected "abc"
$defined_symbol = @' $defined_symbol = @'
using System; using System;
@ -225,7 +234,7 @@ namespace Namespace8
'@ '@
Add-CSharpType -References $defined_symbol -CompileSymbols "SYMBOL1" Add-CSharpType -References $defined_symbol -CompileSymbols "SYMBOL1"
$actual = [Namespace8.Class8]::GetString() $actual = [Namespace8.Class8]::GetString()
Assert-Equals -actual $actual -expected "symbol" Assert-Equal -actual $actual -expected "symbol"
$type_accelerator = @' $type_accelerator = @'
using System; using System;
@ -245,7 +254,7 @@ namespace Namespace9
'@ '@
Add-CSharpType -Reference $type_accelerator Add-CSharpType -Reference $type_accelerator
$actual = [AnsibleType]::GetString() $actual = [AnsibleType]::GetString()
Assert-Equals -actual $actual -expected "a" Assert-Equal -actual $actual -expected "a"
$missing_type_class = @' $missing_type_class = @'
using System; using System;
@ -266,11 +275,12 @@ namespace Namespace10
$failed = $false $failed = $false
try { try {
Add-CSharpType -Reference $missing_type_class Add-CSharpType -Reference $missing_type_class
} catch { }
catch {
$failed = $true $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 = @' $arch_class = @'
using System; using System;
@ -293,7 +303,7 @@ namespace Namespace11
} }
'@ '@
Add-CSharpType -Reference $arch_class 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 = @' $lib_set = @'
using System; using System;
@ -316,7 +326,7 @@ try {
finally { finally {
Remove-Item -LiteralPath env:\LIB Remove-Item -LiteralPath env:\LIB
} }
Assert-Equals -actual ([Namespace12.Class12]::GetString()) -expected "b" Assert-Equal -actual ([Namespace12.Class12]::GetString()) -expected "b"
$result.res = "success" $result.res = "success"
Exit-Json -obj $result Exit-Json -obj $result

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

@ -5,7 +5,7 @@
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
Function Assert-Equals($actual, $expected) { Function Assert-Equal($actual, $expected) {
if ($actual -cne $expected) { if ($actual -cne $expected) {
Fail-Json @{} "actual != expected`nActual: $actual`nExpected: $expected" Fail-Json @{} "actual != expected`nActual: $actual`nExpected: $expected"
} }
@ -41,33 +41,40 @@ foreach ($entry in $output_dict.GetEnumerator()) {
$value = $entry.Value $value = $entry.Value
if ($value -is [Hashtable]) { 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()) { 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") { if ($key -eq "list_dict") {
foreach ($inner_list in $value) { foreach ($inner_list in $value) {
if ($inner_list -is [Hashtable]) { if ($inner_list -is [Hashtable]) {
foreach ($inner_list_hash in $inner_list.GetEnumerator()) { 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 # this is not a string key so we need to keep it the same
Assert-Equals -actual $inner_list -expected "stringTwo" Assert-Equal -actual $inner_list -expected "stringTwo"
} else { }
Assert-Equals -actual $inner_list -expected 0 else {
Assert-Equal -actual $inner_list -expected 0
}
} }
} }
} elseif ($key -eq "empty_list") { elseif ($key -eq "empty_list") {
Assert-Equals -actual $value.Count -expected 0 Assert-Equal -actual $value.Count -expected 0
} elseif ($key -eq "single_list") { }
Assert-Equals -actual $value.Count -expected 1 elseif ($key -eq "single_list") {
} else { Assert-Equal -actual $value.Count -expected 1
}
else {
Fail-Json -obj $result -message "invalid key found for list $key" 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 $exe_filename = Split-Path -Path $exe -Leaf
$test_name = $null $test_name = $null
Function Assert-Equals($actual, $expected) { Function Assert-Equal($actual, $expected) {
if ($actual -cne $expected) { if ($actual -cne $expected) {
Fail-Json -obj $result -message "Test $test_name failed`nActual: '$actual' != Expected: '$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" $test_name = "full exe path"
$actual = Run-Command -command "`"$exe`" arg1 arg2 `"arg 3`"" $actual = Run-Command -command "`"$exe`" arg1 arg2 `"arg 3`""
Assert-Equals -actual $actual.rc -expected 0 Assert-Equal -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "arg1`r`narg2`r`narg 3`r`n" Assert-Equal -actual $actual.stdout -expected "arg1`r`narg2`r`narg 3`r`n"
Assert-Equals -actual $actual.stderr -expected "" Assert-Equal -actual $actual.stderr -expected ""
Assert-Equals -actual $actual.executable -expected $exe Assert-Equal -actual $actual.executable -expected $exe
$test_name = "exe in special char dir" $test_name = "exe in special char dir"
$tmp_dir = Join-Path -Path $env:TEMP -ChildPath "ansible .ÅÑŚÌβŁÈ [$!@^&test(;)]" $tmp_dir = Join-Path -Path $env:TEMP -ChildPath "ansible .ÅÑŚÌβŁÈ [$!@^&test(;)]"
@ -36,66 +36,70 @@ try {
$exe_special = Join-Path $tmp_dir -ChildPath "PrintArgv.exe" $exe_special = Join-Path $tmp_dir -ChildPath "PrintArgv.exe"
Copy-Item -LiteralPath $exe -Destination $exe_special Copy-Item -LiteralPath $exe -Destination $exe_special
$actual = Run-Command -command "`"$exe_special`" arg1 arg2 `"arg 3`"" $actual = Run-Command -command "`"$exe_special`" arg1 arg2 `"arg 3`""
} finally { }
finally {
Remove-Item -LiteralPath $tmp_dir -Force -Recurse Remove-Item -LiteralPath $tmp_dir -Force -Recurse
} }
Assert-Equals -actual $actual.rc -expected 0 Assert-Equal -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "arg1`r`narg2`r`narg 3`r`n" Assert-Equal -actual $actual.stdout -expected "arg1`r`narg2`r`narg 3`r`n"
Assert-Equals -actual $actual.stderr -expected "" Assert-Equal -actual $actual.stderr -expected ""
Assert-Equals -actual $actual.executable -expected $exe_special Assert-Equal -actual $actual.executable -expected $exe_special
$test_name = "invalid exe path" $test_name = "invalid exe path"
try { try {
$actual = Run-Command -command "C:\fakepath\$exe_filename arg1" $actual = Run-Command -command "C:\fakepath\$exe_filename arg1"
Fail-Json -obj $result -message "Test $test_name failed`nCommand should have thrown an exception" 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" $test_name = "exe in current folder"
$actual = Run-Command -command "$exe_filename arg1" -working_directory $exe_directory $actual = Run-Command -command "$exe_filename arg1" -working_directory $exe_directory
Assert-Equals -actual $actual.rc -expected 0 Assert-Equal -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "arg1`r`n" Assert-Equal -actual $actual.stdout -expected "arg1`r`n"
Assert-Equals -actual $actual.stderr -expected "" Assert-Equal -actual $actual.stderr -expected ""
Assert-Equals -actual $actual.executable -expected $exe Assert-Equal -actual $actual.executable -expected $exe
$test_name = "no working directory set" $test_name = "no working directory set"
$actual = Run-Command -command "cmd.exe /c cd" $actual = Run-Command -command "cmd.exe /c cd"
Assert-Equals -actual $actual.rc -expected 0 Assert-Equal -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "$($pwd.Path)`r`n" Assert-Equal -actual $actual.stdout -expected "$($pwd.Path)`r`n"
Assert-Equals -actual $actual.stderr -expected "" Assert-Equal -actual $actual.stderr -expected ""
Assert-Equals -actual $actual.executable.ToUpper() -expected "$env:SystemRoot\System32\cmd.exe".ToUpper() Assert-Equal -actual $actual.executable.ToUpper() -expected "$env:SystemRoot\System32\cmd.exe".ToUpper()
$test_name = "working directory override" $test_name = "working directory override"
$actual = Run-Command -command "cmd.exe /c cd" -working_directory $env:SystemRoot $actual = Run-Command -command "cmd.exe /c cd" -working_directory $env:SystemRoot
Assert-Equals -actual $actual.rc -expected 0 Assert-Equal -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "$env:SystemRoot`r`n" Assert-Equal -actual $actual.stdout -expected "$env:SystemRoot`r`n"
Assert-Equals -actual $actual.stderr -expected "" Assert-Equal -actual $actual.stderr -expected ""
Assert-Equals -actual $actual.executable.ToUpper() -expected "$env:SystemRoot\System32\cmd.exe".ToUpper() Assert-Equal -actual $actual.executable.ToUpper() -expected "$env:SystemRoot\System32\cmd.exe".ToUpper()
$test_name = "working directory invalid path" $test_name = "working directory invalid path"
try { try {
$actual = Run-Command -command "doesn't matter" -working_directory "invalid path here" $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" 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" $test_name = "invalid arguments"
$actual = Run-Command -command "ipconfig.exe /asdf" $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" $test_name = "test stdout and stderr streams"
$actual = Run-Command -command "cmd.exe /c echo stdout && echo stderr 1>&2" $actual = Run-Command -command "cmd.exe /c echo stdout && echo stderr 1>&2"
Assert-Equals -actual $actual.rc -expected 0 Assert-Equal -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "stdout `r`n" Assert-Equal -actual $actual.stdout -expected "stdout `r`n"
Assert-Equals -actual $actual.stderr -expected "stderr `r`n" Assert-Equal -actual $actual.stderr -expected "stderr `r`n"
$test_name = "Test UTF8 output from stdout stream" $test_name = "Test UTF8 output from stdout stream"
$actual = Run-Command -command "powershell.exe -ExecutionPolicy ByPass -Command `"Write-Host '💩'`"" $actual = Run-Command -command "powershell.exe -ExecutionPolicy ByPass -Command `"Write-Host '💩'`""
Assert-Equals -actual $actual.rc -expected 0 Assert-Equal -actual $actual.rc -expected 0
Assert-Equals -actual $actual.stdout -expected "💩`n" Assert-Equal -actual $actual.stdout -expected "💩`n"
Assert-Equals -actual $actual.stderr -expected "" Assert-Equal -actual $actual.stderr -expected ""
$test_name = "test default environment variable" $test_name = "test default environment variable"
Set-Item -LiteralPath env:TESTENV -Value "test" Set-Item -LiteralPath env:TESTENV -Value "test"
@ -129,7 +133,7 @@ begin {
"@ "@
$encoded_wrapper = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($wrapper)) $encoded_wrapper = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($wrapper))
$actual = Run-Command -command "powershell.exe -ExecutionPolicy ByPass -EncodedCommand $encoded_wrapper" -stdin "Ansible" $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" $result.data = "success"
Exit-Json -obj $result Exit-Json -obj $result

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

@ -25,7 +25,7 @@ New-Item -Path $folder_target -ItemType Directory | Out-Null
New-Item -Path $file_target -ItemType File | Out-Null New-Item -Path $file_target -ItemType File | Out-Null
Set-Content -LiteralPath $file_target -Value "a" Set-Content -LiteralPath $file_target -Value "a"
Function Assert-Equals($actual, $expected) { Function Assert-Equal($actual, $expected) {
if ($actual -ne $expected) { if ($actual -ne $expected) {
Fail-Json @{} "actual != expected`nActual: $actual`nExpected: $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 { try {
New-Link -link_path "$path\folder-hard" -link_target $folder_target -link_type "hard" 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" 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 # fail to create a junction point pointed to a file
try { try {
New-Link -link_path "$path\junction-fail" -link_target $file_target -link_type "junction" 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" 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 # fail to create a symbolic link with non-existent target
try { try {
New-Link -link_path "$path\symlink-fail" -link_target "$path\fake-folder" -link_type "link" 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" 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 # create recursive symlink
Run-Command -command "cmd.exe /c mklink /D symlink-rel folder" -working_directory $path | Out-Null 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" $rel_link_result = Get-Link -link_path "$path\symlink-rel"
Assert-Equals -actual $rel_link_result.Type -expected "SymbolicLink" Assert-Equal -actual $rel_link_result.Type -expected "SymbolicLink"
Assert-Equals -actual $rel_link_result.SubstituteName -expected "folder" Assert-Equal -actual $rel_link_result.SubstituteName -expected "folder"
Assert-Equals -actual $rel_link_result.PrintName -expected "folder" Assert-Equal -actual $rel_link_result.PrintName -expected "folder"
Assert-Equals -actual $rel_link_result.TargetPath -expected "folder" Assert-Equal -actual $rel_link_result.TargetPath -expected "folder"
Assert-Equals -actual $rel_link_result.AbsolutePath -expected $folder_target Assert-Equal -actual $rel_link_result.AbsolutePath -expected $folder_target
Assert-Equals -actual $rel_link_result.HardTargets -expected $null Assert-Equal -actual $rel_link_result.HardTargets -expected $null
# create a symbolic file test # create a symbolic file test
New-Link -link_path $symlink_file_path -link_target $file_target -link_type "link" New-Link -link_path $symlink_file_path -link_target $file_target -link_type "link"
$file_link_result = Get-Link -link_path $symlink_file_path $file_link_result = Get-Link -link_path $symlink_file_path
Assert-Equals -actual $file_link_result.Type -expected "SymbolicLink" Assert-Equal -actual $file_link_result.Type -expected "SymbolicLink"
Assert-Equals -actual $file_link_result.SubstituteName -expected "\??\$file_target" Assert-Equal -actual $file_link_result.SubstituteName -expected "\??\$file_target"
Assert-Equals -actual $file_link_result.PrintName -expected $file_target Assert-Equal -actual $file_link_result.PrintName -expected $file_target
Assert-Equals -actual $file_link_result.TargetPath -expected $file_target Assert-Equal -actual $file_link_result.TargetPath -expected $file_target
Assert-Equals -actual $file_link_result.AbsolutePath -expected $file_target Assert-Equal -actual $file_link_result.AbsolutePath -expected $file_target
Assert-Equals -actual $file_link_result.HardTargets -expected $null Assert-Equal -actual $file_link_result.HardTargets -expected $null
# create a symbolic link folder test # create a symbolic link folder test
New-Link -link_path $symlink_folder_path -link_target $folder_target -link_type "link" New-Link -link_path $symlink_folder_path -link_target $folder_target -link_type "link"
$folder_link_result = Get-Link -link_path $symlink_folder_path $folder_link_result = Get-Link -link_path $symlink_folder_path
Assert-Equals -actual $folder_link_result.Type -expected "SymbolicLink" Assert-Equal -actual $folder_link_result.Type -expected "SymbolicLink"
Assert-Equals -actual $folder_link_result.SubstituteName -expected "\??\$folder_target" Assert-Equal -actual $folder_link_result.SubstituteName -expected "\??\$folder_target"
Assert-Equals -actual $folder_link_result.PrintName -expected $folder_target Assert-Equal -actual $folder_link_result.PrintName -expected $folder_target
Assert-Equals -actual $folder_link_result.TargetPath -expected $folder_target Assert-Equal -actual $folder_link_result.TargetPath -expected $folder_target
Assert-Equals -actual $folder_link_result.AbsolutePath -expected $folder_target Assert-Equal -actual $folder_link_result.AbsolutePath -expected $folder_target
Assert-Equals -actual $folder_link_result.HardTargets -expected $null Assert-Equal -actual $folder_link_result.HardTargets -expected $null
# create a junction point test # create a junction point test
New-Link -link_path $junction_point_path -link_target $folder_target -link_type "junction" New-Link -link_path $junction_point_path -link_target $folder_target -link_type "junction"
$junction_point_result = Get-Link -link_path $junction_point_path $junction_point_result = Get-Link -link_path $junction_point_path
Assert-Equals -actual $junction_point_result.Type -expected "JunctionPoint" Assert-Equal -actual $junction_point_result.Type -expected "JunctionPoint"
Assert-Equals -actual $junction_point_result.SubstituteName -expected "\??\$folder_target" Assert-Equal -actual $junction_point_result.SubstituteName -expected "\??\$folder_target"
Assert-Equals -actual $junction_point_result.PrintName -expected $folder_target Assert-Equal -actual $junction_point_result.PrintName -expected $folder_target
Assert-Equals -actual $junction_point_result.TargetPath -expected $folder_target Assert-Equal -actual $junction_point_result.TargetPath -expected $folder_target
Assert-Equals -actual $junction_point_result.AbsolutePath -expected $folder_target Assert-Equal -actual $junction_point_result.AbsolutePath -expected $folder_target
Assert-Equals -actual $junction_point_result.HardTargets -expected $null Assert-Equal -actual $junction_point_result.HardTargets -expected $null
# create a hard link test # create a hard link test
New-Link -link_path $hardlink_path -link_target $file_target -link_type "hard" New-Link -link_path $hardlink_path -link_target $file_target -link_type "hard"
$hardlink_result = Get-Link -link_path $hardlink_path $hardlink_result = Get-Link -link_path $hardlink_path
Assert-Equals -actual $hardlink_result.Type -expected "HardLink" Assert-Equal -actual $hardlink_result.Type -expected "HardLink"
Assert-Equals -actual $hardlink_result.SubstituteName -expected $null Assert-Equal -actual $hardlink_result.SubstituteName -expected $null
Assert-Equals -actual $hardlink_result.PrintName -expected $null Assert-Equal -actual $hardlink_result.PrintName -expected $null
Assert-Equals -actual $hardlink_result.TargetPath -expected $null Assert-Equal -actual $hardlink_result.TargetPath -expected $null
Assert-Equals -actual $hardlink_result.AbsolutePath -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) { 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" 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) { 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-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 # 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" New-Link -link_path $hardlink_path_2 -link_target $file_target -link_type "hard"
$hardlink_result_2 = Get-Link -link_path $hardlink_path $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 # check if broken symbolic link still works
Remove-Item -LiteralPath $folder_target -Force | Out-Null Remove-Item -LiteralPath $folder_target -Force | Out-Null
$broken_link_result = Get-Link -link_path $symlink_folder_path $broken_link_result = Get-Link -link_path $symlink_folder_path
Assert-Equals -actual $broken_link_result.Type -expected "SymbolicLink" Assert-Equal -actual $broken_link_result.Type -expected "SymbolicLink"
Assert-Equals -actual $broken_link_result.SubstituteName -expected "\??\$folder_target" Assert-Equal -actual $broken_link_result.SubstituteName -expected "\??\$folder_target"
Assert-Equals -actual $broken_link_result.PrintName -expected $folder_target Assert-Equal -actual $broken_link_result.PrintName -expected $folder_target
Assert-Equals -actual $broken_link_result.TargetPath -expected $folder_target Assert-Equal -actual $broken_link_result.TargetPath -expected $folder_target
Assert-Equals -actual $broken_link_result.AbsolutePath -expected $folder_target Assert-Equal -actual $broken_link_result.AbsolutePath -expected $folder_target
Assert-Equals -actual $broken_link_result.HardTargets -expected $null Assert-Equal -actual $broken_link_result.HardTargets -expected $null
# check if broken junction point still works # check if broken junction point still works
$broken_junction_result = Get-Link -link_path $junction_point_path $broken_junction_result = Get-Link -link_path $junction_point_path
Assert-Equals -actual $broken_junction_result.Type -expected "JunctionPoint" Assert-Equal -actual $broken_junction_result.Type -expected "JunctionPoint"
Assert-Equals -actual $broken_junction_result.SubstituteName -expected "\??\$folder_target" Assert-Equal -actual $broken_junction_result.SubstituteName -expected "\??\$folder_target"
Assert-Equals -actual $broken_junction_result.PrintName -expected $folder_target Assert-Equal -actual $broken_junction_result.PrintName -expected $folder_target
Assert-Equals -actual $broken_junction_result.TargetPath -expected $folder_target Assert-Equal -actual $broken_junction_result.TargetPath -expected $folder_target
Assert-Equals -actual $broken_junction_result.AbsolutePath -expected $folder_target Assert-Equal -actual $broken_junction_result.AbsolutePath -expected $folder_target
Assert-Equals -actual $broken_junction_result.HardTargets -expected $null Assert-Equal -actual $broken_junction_result.HardTargets -expected $null
# delete file symbolic link # delete file symbolic link
Remove-Link -link_path $symlink_file_path Remove-Link -link_path $symlink_file_path

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

@ -6,7 +6,7 @@
$params = Parse-Args $args $params = Parse-Args $args
$sid_account = Get-AnsibleParam -obj $params -name "sid_account" -type "str" -failifempty $true $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) { if ($actual -ne $expected) {
Fail-Json @{} "actual != expected`nActual: $actual`nExpected: $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-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-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 = "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 # 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 # 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 $actual_account_name = Convert-FromSID -sid $test.sid
# renamed admins may have an empty FullName; skip comparison in that case # renamed admins may have an empty FullName; skip comparison in that case
if ($test.full_name) { 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) { foreach ($test_name in $test.names) {
$actual_sid = Convert-ToSID -account_name $test_name $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 # in the normal test suite
# Calling Convert-ToSID with a string like a SID should return that SID back # Calling Convert-ToSID with a string like a SID should return that SID back
$actual = Convert-ToSID -account_name $sid_account $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 # 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 # for a user that is called that SID and not the SID passed in
$actual = Convert-ToSID -account_name ".\$sid_account" $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" } Exit-Json @{ data = "success" }

@ -13,22 +13,24 @@ $module = [Ansible.Basic.AnsibleModule]::Create($args, $spec)
$httpbin_host = $module.Params.httpbin_host $httpbin_host = $module.Params.httpbin_host
Function Assert-Equals { Function Assert-Equal {
param( param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual, [Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected [Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
) )
process {
$matched = $false $matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array] -or $Actual -is [System.Collections.IList]) { 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++) { for ($i = 0; $i -lt $Actual.Count; $i++) {
$actualValue = $Actual[$i] $actualValue = $Actual[$i]
$expectedValue = $Expected[$i] $expectedValue = $Expected[$i]
Assert-Equals -Actual $actualValue -Expected $expectedValue Assert-Equal -Actual $actualValue -Expected $expectedValue
} }
$matched = $true $matched = $true
} else { }
else {
$matched = $Actual -ceq $Expected $matched = $Actual -ceq $Expected
} }
@ -47,6 +49,7 @@ Function Assert-Equals {
$module.FailJson("AssertionError: actual != expected") $module.FailJson("AssertionError: actual != expected")
} }
} }
}
Function Convert-StreamToString { Function Convert-StreamToString {
[CmdletBinding()] [CmdletBinding()]
@ -60,7 +63,8 @@ Function Convert-StreamToString {
try { try {
$Stream.CopyTo($ms) $Stream.CopyTo($ms)
[System.Text.Encoding]::UTF8.GetString($ms.ToArray()) [System.Text.Encoding]::UTF8.GetString($ms.ToArray())
} finally { }
finally {
$ms.Dispose() $ms.Dispose()
} }
} }
@ -69,50 +73,50 @@ $tests = [Ordered]@{
'GET request over http' = { 'GET request over http' = {
$r = Get-AnsibleWebRequest -Uri "http://$httpbin_host/get" $r = Get-AnsibleWebRequest -Uri "http://$httpbin_host/get"
$r.Method | Assert-Equals -Expected 'GET' $r.Method | Assert-Equal -Expected 'GET'
$r.Timeout | Assert-Equals -Expected 30000 $r.Timeout | Assert-Equal -Expected 30000
$r.UseDefaultCredentials | Assert-Equals -Expected $false $r.UseDefaultCredentials | Assert-Equal -Expected $false
$r.Credentials | Assert-Equals -Expected $null $r.Credentials | Assert-Equal -Expected $null
$r.ClientCertificates.Count | Assert-Equals -Expected 0 $r.ClientCertificates.Count | Assert-Equal -Expected 0
$r.Proxy.Credentials | Assert-Equals -Expected $null $r.Proxy.Credentials | Assert-Equal -Expected $null
$r.UserAgent | Assert-Equals -Expected 'ansible-httpget' $r.UserAgent | Assert-Equal -Expected 'ansible-httpget'
$actual = Invoke-WithWebRequest -Module $module -Request $r -Script { $actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) 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 Convert-StreamToString -Stream $Stream
} | ConvertFrom-Json } | ConvertFrom-Json
$actual.headers.'User-Agent' | Assert-Equals -Expected 'ansible-httpget' $actual.headers.'User-Agent' | Assert-Equal -Expected 'ansible-httpget'
$actual.headers.'Host' | Assert-Equals -Expected $httpbin_host $actual.headers.'Host' | Assert-Equal -Expected $httpbin_host
$module.Result.msg | Assert-Equals -Expected 'OK' $module.Result.msg | Assert-Equal -Expected 'OK'
$module.Result.status_code | Assert-Equals -Expected 200 $module.Result.status_code | Assert-Equal -Expected 200
$module.Result.ContainsKey('elapsed') | Assert-Equals -Expected $true $module.Result.ContainsKey('elapsed') | Assert-Equal -Expected $true
} }
'GET request over https' = { 'GET request over https' = {
# url is an alias for the -Uri parameter. # url is an alias for the -Uri parameter.
$r = Get-AnsibleWebRequest -url "https://$httpbin_host/get" $r = Get-AnsibleWebRequest -url "https://$httpbin_host/get"
$r.Method | Assert-Equals -Expected 'GET' $r.Method | Assert-Equal -Expected 'GET'
$r.Timeout | Assert-Equals -Expected 30000 $r.Timeout | Assert-Equal -Expected 30000
$r.UseDefaultCredentials | Assert-Equals -Expected $false $r.UseDefaultCredentials | Assert-Equal -Expected $false
$r.Credentials | Assert-Equals -Expected $null $r.Credentials | Assert-Equal -Expected $null
$r.ClientCertificates.Count | Assert-Equals -Expected 0 $r.ClientCertificates.Count | Assert-Equal -Expected 0
$r.Proxy.Credentials | Assert-Equals -Expected $null $r.Proxy.Credentials | Assert-Equal -Expected $null
$r.UserAgent | Assert-Equals -Expected 'ansible-httpget' $r.UserAgent | Assert-Equal -Expected 'ansible-httpget'
$actual = Invoke-WithWebRequest -Module $module -Request $r -Script { $actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) 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 Convert-StreamToString -Stream $Stream
} | ConvertFrom-Json } | ConvertFrom-Json
$actual.headers.'User-Agent' | Assert-Equals -Expected 'ansible-httpget' $actual.headers.'User-Agent' | Assert-Equal -Expected 'ansible-httpget'
$actual.headers.'Host' | Assert-Equals -Expected $httpbin_host $actual.headers.'Host' | Assert-Equal -Expected $httpbin_host
} }
'POST request' = { 'POST request' = {
@ -125,14 +129,14 @@ $tests = [Ordered]@{
} }
$r = Get-AnsibleWebRequest @getParams $r = Get-AnsibleWebRequest @getParams
$r.Method | Assert-Equals -Expected 'POST' $r.Method | Assert-Equal -Expected 'POST'
$r.Timeout | Assert-Equals -Expected 30000 $r.Timeout | Assert-Equal -Expected 30000
$r.UseDefaultCredentials | Assert-Equals -Expected $false $r.UseDefaultCredentials | Assert-Equal -Expected $false
$r.Credentials | Assert-Equals -Expected $null $r.Credentials | Assert-Equal -Expected $null
$r.ClientCertificates.Count | Assert-Equals -Expected 0 $r.ClientCertificates.Count | Assert-Equal -Expected 0
$r.Proxy.Credentials | Assert-Equals -Expected $null $r.Proxy.Credentials | Assert-Equal -Expected $null
$r.ContentType | Assert-Equals -Expected 'application/json' $r.ContentType | Assert-Equal -Expected 'application/json'
$r.UserAgent | Assert-Equals -Expected 'ansible-httpget' $r.UserAgent | Assert-Equal -Expected 'ansible-httpget'
$body = New-Object -TypeName System.IO.MemoryStream -ArgumentList @(, $body = New-Object -TypeName System.IO.MemoryStream -ArgumentList @(,
([System.Text.Encoding]::UTF8.GetBytes('{"foo":"bar"}')) ([System.Text.Encoding]::UTF8.GetBytes('{"foo":"bar"}'))
@ -140,13 +144,13 @@ $tests = [Ordered]@{
$actual = Invoke-WithWebRequest -Module $module -Request $r -Body $body -Script { $actual = Invoke-WithWebRequest -Module $module -Request $r -Body $body -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) 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 Convert-StreamToString -Stream $Stream
} | ConvertFrom-Json } | ConvertFrom-Json
$actual.headers.'User-Agent' | Assert-Equals -Expected 'ansible-httpget' $actual.headers.'User-Agent' | Assert-Equal -Expected 'ansible-httpget'
$actual.headers.'Host' | Assert-Equals -Expected $httpbin_host $actual.headers.'Host' | Assert-Equal -Expected $httpbin_host
$actual.data | Assert-Equals -Expected '{"foo":"bar"}' $actual.data | Assert-Equal -Expected '{"foo":"bar"}'
} }
'Safe redirection of GET' = { 'Safe redirection of GET' = {
@ -155,8 +159,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script { Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get" $Response.ResponseUri | Assert-Equal -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equals -Expected 200 $Response.StatusCode | Assert-Equal -Expected 200
} }
} }
@ -166,8 +170,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script { Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get" $Response.ResponseUri | Assert-Equal -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equals -Expected 200 $Response.StatusCode | Assert-Equal -Expected 200
} }
} }
@ -181,8 +185,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script { Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected $r.RequestUri $Response.ResponseUri | Assert-Equal -Expected $r.RequestUri
$Response.StatusCode | Assert-Equals -Expected 302 $Response.StatusCode | Assert-Equal -Expected 302
} }
} }
@ -196,8 +200,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script { Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected $r.RequestUri $Response.ResponseUri | Assert-Equal -Expected $r.RequestUri
$Response.StatusCode | Assert-Equals -Expected 302 $Response.StatusCode | Assert-Equal -Expected 302
} }
} }
@ -212,8 +216,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script { Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected $r.RequestUri $Response.ResponseUri | Assert-Equal -Expected $r.RequestUri
$Response.StatusCode | Assert-Equals -Expected 302 $Response.StatusCode | Assert-Equal -Expected 302
} }
} }
@ -228,8 +232,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script { Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected $r.RequestUri $Response.ResponseUri | Assert-Equal -Expected $r.RequestUri
$Response.StatusCode | Assert-Equals -Expected 302 $Response.StatusCode | Assert-Equal -Expected 302
} }
} }
@ -243,8 +247,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script { Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get" $Response.ResponseUri | Assert-Equal -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equals -Expected 200 $Response.StatusCode | Assert-Equal -Expected 200
} }
} }
@ -259,8 +263,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script { Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "http://$httpbin_host/get" $Response.ResponseUri | Assert-Equal -Expected "http://$httpbin_host/get"
$Response.StatusCode | Assert-Equals -Expected 200 $Response.StatusCode | Assert-Equal -Expected 200
} }
} }
@ -275,8 +279,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -Script { Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "https://$httpbin_host/put" $Response.ResponseUri | Assert-Equal -Expected "https://$httpbin_host/put"
$Response.StatusCode | Assert-Equals -Expected 200 $Response.StatusCode | Assert-Equal -Expected 200
} }
} }
@ -290,8 +294,8 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -IgnoreBadResponse -Script { Invoke-WithWebRequest -Module $module -Request $r -IgnoreBadResponse -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream)
$Response.ResponseUri | Assert-Equals -Expected "https://$httpbin_host/relative-redirect/1" $Response.ResponseUri | Assert-Equal -Expected "https://$httpbin_host/relative-redirect/1"
$Response.StatusCode | Assert-Equals -Expected 302 $Response.StatusCode | Assert-Equal -Expected 302
} }
} }
@ -305,12 +309,13 @@ $tests = [Ordered]@{
$failed = $false $failed = $false
try { try {
$null = Invoke-WithWebRequest -Module $module -Request $r -Script {} $null = Invoke-WithWebRequest -Module $module -Request $r -Script {}
} catch { }
$_.Exception.GetType().Name | Assert-Equals -Expected 'WebException' catch {
$_.Exception.Message | Assert-Equals -Expected 'Too many automatic redirections were attempted.' $_.Exception.GetType().Name | Assert-Equal -Expected 'WebException'
$_.Exception.Message | Assert-Equal -Expected 'Too many automatic redirections were attempted.'
$failed = $true $failed = $true
} }
$failed | Assert-Equals -Expected $true $failed | Assert-Equal -Expected $true
} }
'Basic auth as Credential' = { 'Basic auth as Credential' = {
@ -324,7 +329,7 @@ $tests = [Ordered]@{
Invoke-WithWebRequest -Module $module -Request $r -IgnoreBadResponse -Script { Invoke-WithWebRequest -Module $module -Request $r -IgnoreBadResponse -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) 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 { Invoke-WithWebRequest -Module $module -Request $r -IgnoreBadResponse -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) 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 { $actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) 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 Convert-StreamToString -Stream $Stream
} | ConvertFrom-Json } | ConvertFrom-Json
$actual.headers.'Testheader' | Assert-Equals -Expected 'test-header' $actual.headers.'Testheader' | Assert-Equal -Expected 'test-header'
$actual.headers.'testingheader' | Assert-Equals -Expected 'testing_header' $actual.headers.'testingheader' | Assert-Equal -Expected 'testing_header'
$actual.Headers.'User-Agent' | Assert-Equals -Expected 'test-agent' $actual.Headers.'User-Agent' | Assert-Equal -Expected 'test-agent'
} }
'Request with timeout' = { 'Request with timeout' = {
@ -378,12 +383,13 @@ $tests = [Ordered]@{
$failed = $false $failed = $false
try { try {
$null = Invoke-WithWebRequest -Module $module -Request $r -Script {} $null = Invoke-WithWebRequest -Module $module -Request $r -Script {}
} catch { }
catch {
$failed = $true $failed = $true
$_.Exception.GetType().Name | Assert-Equals -Expected WebException $_.Exception.GetType().Name | Assert-Equal -Expected WebException
$_.Exception.Message | Assert-Equals -Expected 'The operation has timed out' $_.Exception.Message | Assert-Equal -Expected 'The operation has timed out'
} }
$failed | Assert-Equals -Expected $true $failed | Assert-Equal -Expected $true
} }
'Request with file URI' = { 'Request with file URI' = {
@ -395,12 +401,12 @@ $tests = [Ordered]@{
$actual = Invoke-WithWebRequest -Module $module -Request $r -Script { $actual = Invoke-WithWebRequest -Module $module -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) 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 Convert-StreamToString -Stream $Stream
} }
$actual | Assert-Equals -Expected "test`r`n" $actual | Assert-Equal -Expected "test`r`n"
$module.Result.msg | Assert-Equals -Expected "OK" $module.Result.msg | Assert-Equal -Expected "OK"
$module.Result.status_code | Assert-Equals -Expected 200 $module.Result.status_code | Assert-Equal -Expected 200
} }
'Web request based on module options' = { 'Web request based on module options' = {
@ -430,10 +436,10 @@ $tests = [Ordered]@{
$actual = Invoke-WithWebRequest -Module $testModule -Request $r -Script { $actual = Invoke-WithWebRequest -Module $testModule -Request $r -Script {
Param ([System.Net.WebResponse]$Response, [System.IO.Stream]$Stream) 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 Convert-StreamToString -Stream $Stream
} | ConvertFrom-Json } | 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' = { 'Web request with default proxy' = {
@ -442,7 +448,7 @@ $tests = [Ordered]@{
} }
$r = Get-AnsibleWebRequest @params $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' = { 'Web request with no proxy' = {
@ -452,7 +458,7 @@ $tests = [Ordered]@{
} }
$r = Get-AnsibleWebRequest @params $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, @{}) $module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
Function Assert-Equals { Function Assert-Equal {
param( param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual, [Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected [Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
) )
process {
$matched = $false $matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) { 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++) { for ($i = 0; $i -lt $Actual.Count; $i++) {
$actual_value = $Actual[$i] $actual_value = $Actual[$i]
$expected_value = $Expected[$i] $expected_value = $Expected[$i]
Assert-Equals -Actual $actual_value -Expected $expected_value Assert-Equal -Actual $actual_value -Expected $expected_value
} }
$matched = $true $matched = $true
} else { }
else {
$matched = $Actual -ceq $Expected $matched = $Actual -ceq $Expected
} }
@ -38,110 +40,47 @@ Function Assert-Equals {
$module.FailJson("AssertionError: actual != expected") $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( param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual, [Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected [Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
) )
$matched = $false process {
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
)
$actual_keys = $Actual.Keys $actual_keys = $Actual.Keys
$expected_keys = $Expected.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()) { foreach ($actual_entry in $Actual.GetEnumerator()) {
$actual_key = $actual_entry.Key $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 $actual_value = $actual_entry.Value
$expected_value = $Expected.$actual_key $expected_value = $Expected.$actual_key
if ($actual_value -is [System.Collections.IDictionary]) { if ($actual_value -is [System.Collections.IDictionary]) {
$actual_value | Assert-DictionaryEquals -Expected $expected_value $actual_value | Assert-DictionaryEqual -Expected $expected_value
} elseif ($actual_value -is [System.Collections.ArrayList]) { }
elseif ($actual_value -is [System.Collections.ArrayList]) {
for ($i = 0; $i -lt $actual_value.Count; $i++) { for ($i = 0; $i -lt $actual_value.Count; $i++) {
$actual_entry = $actual_value[$i] $actual_entry = $actual_value[$i]
$expected_entry = $expected_value[$i] $expected_entry = $expected_value[$i]
if ($actual_entry -is [System.Collections.IDictionary]) { if ($actual_entry -is [System.Collections.IDictionary]) {
$actual_entry | Assert-DictionaryEquals -Expected $expected_entry $actual_entry | Assert-DictionaryEqual -Expected $expected_entry
} else { }
Assert-Equals -Actual $actual_entry -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) { 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 = @{ $tests = @{
"Check valid privilege name" = { "Check valid privilege name" = {
$actual = [Ansible.Privilege.PrivilegeUtil]::CheckPrivilegeName("SeTcbPrivilege") $actual = [Ansible.Privilege.PrivilegeUtil]::CheckPrivilegeName("SeTcbPrivilege")
$actual | Assert-Equals -Expected $true $actual | Assert-Equal -Expected $true
} }
"Check invalid privilege name" = { "Check invalid privilege name" = {
$actual = [Ansible.Privilege.PrivilegeUtil]::CheckPrivilegeName("SeFake") $actual = [Ansible.Privilege.PrivilegeUtil]::CheckPrivilegeName("SeFake")
$actual | Assert-Equals -Expected $false $actual | Assert-Equal -Expected $false
} }
"Disable a privilege" = { "Disable a privilege" = {
@ -163,14 +102,14 @@ $tests = @{
[Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege") > $null [Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege") > $null
$actual = [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege") $actual = [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege")
$actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2' $actual.GetType().Name | Assert-Equal -Expected 'Dictionary`2'
$actual.Count | Assert-Equals -Expected 1 $actual.Count | Assert-Equal -Expected 1
$actual.SeTimeZonePrivilege | Assert-Equals -Expected $true $actual.SeTimeZonePrivilege | Assert-Equal -Expected $true
# Disable again # Disable again
$actual = [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege") $actual = [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege")
$actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2' $actual.GetType().Name | Assert-Equal -Expected 'Dictionary`2'
$actual.Count | Assert-Equals -Expected 0 $actual.Count | Assert-Equal -Expected 0
} }
"Enable a privilege" = { "Enable a privilege" = {
@ -178,43 +117,43 @@ $tests = @{
[Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege") > $null [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege") > $null
$actual = [Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege") $actual = [Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege")
$actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2' $actual.GetType().Name | Assert-Equal -Expected 'Dictionary`2'
$actual.Count | Assert-Equals -Expected 1 $actual.Count | Assert-Equal -Expected 1
$actual.SeTimeZonePrivilege | Assert-Equals -Expected $false $actual.SeTimeZonePrivilege | Assert-Equal -Expected $false
# Disable again # Disable again
$actual = [Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege") $actual = [Ansible.Privilege.PrivilegeUtil]::EnablePrivilege($process, "SeTimeZonePrivilege")
$actual.GetType().Name | Assert-Equals -Expected 'Dictionary`2' $actual.GetType().Name | Assert-Equal -Expected 'Dictionary`2'
$actual.Count | Assert-Equals -Expected 0 $actual.Count | Assert-Equal -Expected 0
} }
"Disable and revert privileges" = { "Disable and revert privileges" = {
$current_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $current_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$previous_state = [Ansible.Privilege.PrivilegeUtil]::DisableAllPrivileges($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()) { 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 # Disable again
$previous_state2 = [Ansible.Privilege.PrivilegeUtil]::DisableAllPrivileges($process) $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) $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
foreach ($actual_entry in $actual.GetEnumerator()) { 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 [Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process, $previous_state) > $null
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual | Assert-DictionaryEquals -Expected $current_state $actual | Assert-DictionaryEqual -Expected $current_state
} }
"Remove a privilege" = { "Remove a privilege" = {
[Ansible.Privilege.PrivilegeUtil]::RemovePrivilege($process, "SeUndockPrivilege") > $null [Ansible.Privilege.PrivilegeUtil]::RemovePrivilege($process, "SeUndockPrivilege") > $null
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.ContainsKey("SeUndockPrivilege") | Assert-Equals -Expected $false $actual.ContainsKey("SeUndockPrivilege") | Assert-Equal -Expected $false
} }
"Test Enabler" = { "Test Enabler" = {
@ -226,30 +165,34 @@ $tests = @{
} }
[Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process, $new_state) > $null [Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process, $new_state) > $null
$check_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $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
$check_state.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0 $check_state.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
$check_state.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -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 # 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" $enabler = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $false, "SeTimeZonePrivilege", "SeShutdownPrivilege", "SeTcbPrivilege"
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled) $actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled) Assert-Equal -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0 $actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
$actual.ContainsKey("SeTcbPrivilege") | Assert-Equals -Expected $false 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 # 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 = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $false, "SeTimeZonePrivilege", "SeShutdownPrivilege", "SeTcbPrivilege"
$enabler2.Dispose() $enabler2.Dispose()
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled) $actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([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 # Verify that when disposing the object the privileges are reverted
$enabler.Dispose() $enabler.Dispose()
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -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-Equals -Expected 0 $actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
} }
"Test Enabler strict" = { "Test Enabler strict" = {
@ -261,56 +204,67 @@ $tests = @{
} }
[Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process, $new_state) > $null [Ansible.Privilege.PrivilegeUtil]::SetTokenPrivileges($process, $new_state) > $null
$check_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $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
$check_state.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0 $check_state.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
$check_state.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -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 # 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" $enabler = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $true, "SeTimeZonePrivilege", "SeShutdownPrivilege"
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled) $actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled) Assert-Equal -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled)
$actual.SeIncreaseWorkingSetPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected 0 $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 # Now verify a no-op enabler will not rever back to disabled
$enabler2 = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $true, "SeTimeZonePrivilege", "SeShutdownPrivilege" $enabler2 = New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $true, "SeTimeZonePrivilege", "SeShutdownPrivilege"
$enabler2.Dispose() $enabler2.Dispose()
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([Ansible.Privilege.PrivilegeAttributes]::Enabled) $actual.SeTimeZonePrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled |
$actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equals -Expected ([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 # Verify that when disposing the object the privileges are reverted
$enabler.Dispose() $enabler.Dispose()
$actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $actual = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process)
$actual.SeTimeZonePrivilege -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-Equals -Expected 0 $actual.SeShutdownPrivilege -band [Ansible.Privilege.PrivilegeAttributes]::Enabled | Assert-Equal -Expected 0
} }
"Test Enabler invalid privilege" = { "Test Enabler invalid privilege" = {
$failed = $false $failed = $false
try { try {
New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $false, "SeTimeZonePrivilege", "SeFake" New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $false, "SeTimeZonePrivilege", "SeFake"
} catch { }
catch {
$failed = $true $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" = { "Test Enabler strict failure" = {
# Start disabled # Start disabled
[Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege") > $null [Ansible.Privilege.PrivilegeUtil]::DisablePrivilege($process, "SeTimeZonePrivilege") > $null
$check_state = [Ansible.Privilege.PrivilegeUtil]::GetAllPrivilegeInfo($process) $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 $failed = $false
try { try {
New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $true, "SeTimeZonePrivilege", "SeTcbPrivilege" New-Object -TypeName Ansible.Privilege.PrivilegeEnabler -ArgumentList $true, "SeTimeZonePrivilege", "SeTcbPrivilege"
} catch { }
catch {
$failed = $true $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, @{}) $module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
Function Assert-Equals { Function Assert-Equal {
param( param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual, [Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected [Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
) )
process {
$matched = $false $matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array]) { 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++) { for ($i = 0; $i -lt $Actual.Count; $i++) {
$actual_value = $Actual[$i] $actual_value = $Actual[$i]
$expected_value = $Expected[$i] $expected_value = $Expected[$i]
Assert-Equals -Actual $actual_value -Expected $expected_value Assert-Equal -Actual $actual_value -Expected $expected_value
} }
$matched = $true $matched = $true
} else { }
else {
$matched = $Actual -ceq $Expected $matched = $Actual -ceq $Expected
} }
@ -38,121 +40,124 @@ Function Assert-Equals {
$module.FailJson("AssertionError: actual != expected") $module.FailJson("AssertionError: actual != expected")
} }
} }
}
$tests = @{ $tests = @{
"ParseCommandLine empty string" = { "ParseCommandLine empty string" = {
$expected = @((Get-Process -Id $pid).Path) $expected = @((Get-Process -Id $pid).Path)
$actual = [Ansible.Process.ProcessUtil]::ParseCommandLine("") $actual = [Ansible.Process.ProcessUtil]::ParseCommandLine("")
Assert-Equals -Actual $actual -Expected $expected Assert-Equal -Actual $actual -Expected $expected
} }
"ParseCommandLine single argument" = { "ParseCommandLine single argument" = {
$expected = @("powershell.exe") $expected = @("powershell.exe")
$actual = [Ansible.Process.ProcessUtil]::ParseCommandLine("powershell.exe") $actual = [Ansible.Process.ProcessUtil]::ParseCommandLine("powershell.exe")
Assert-Equals -Actual $actual -Expected $expected Assert-Equal -Actual $actual -Expected $expected
} }
"ParseCommandLine multiple arguments" = { "ParseCommandLine multiple arguments" = {
$expected = @("powershell.exe", "-File", "C:\temp\script.ps1") $expected = @("powershell.exe", "-File", "C:\temp\script.ps1")
$actual = [Ansible.Process.ProcessUtil]::ParseCommandLine("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" = { "ParseCommandLine comples arguments" = {
$expected = @('abc', 'd', 'ef gh', 'i\j', 'k"l', 'm\n op', 'ADDLOCAL=qr, s', 'tuv\', 'w''x', 'yz') $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') $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" = { "SearchPath normal" = {
$expected = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe" $expected = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe"
$actual = [Ansible.Process.ProcessUtil]::SearchPath("powershell.exe") $actual = [Ansible.Process.ProcessUtil]::SearchPath("powershell.exe")
$actual | Assert-Equals -Expected $expected $actual | Assert-Equal -Expected $expected
} }
"SearchPath missing" = { "SearchPath missing" = {
$failed = $false $failed = $false
try { try {
[Ansible.Process.ProcessUtil]::SearchPath("fake.exe") [Ansible.Process.ProcessUtil]::SearchPath("fake.exe")
} catch { }
catch {
$failed = $true $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''."' $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" = { "CreateProcess basic" = {
$actual = [Ansible.Process.ProcessUtil]::CreateProcess("whoami.exe") $actual = [Ansible.Process.ProcessUtil]::CreateProcess("whoami.exe")
$actual.GetType().FullName | Assert-Equals -Expected "Ansible.Process.Result" $actual.GetType().FullName | Assert-Equal -Expected "Ansible.Process.Result"
$actual.StandardOut | Assert-Equals -Expected "$(&whoami.exe)`r`n" $actual.StandardOut | Assert-Equal -Expected "$(&whoami.exe)`r`n"
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess stderr" = { "CreateProcess stderr" = {
$actual = [Ansible.Process.ProcessUtil]::CreateProcess("powershell.exe [System.Console]::Error.WriteLine('hi')") $actual = [Ansible.Process.ProcessUtil]::CreateProcess("powershell.exe [System.Console]::Error.WriteLine('hi')")
$actual.StandardOut | Assert-Equals -Expected "" $actual.StandardOut | Assert-Equal -Expected ""
$actual.StandardError | Assert-Equals -Expected "hi`r`n" $actual.StandardError | Assert-Equal -Expected "hi`r`n"
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess exit code" = { "CreateProcess exit code" = {
$actual = [Ansible.Process.ProcessUtil]::CreateProcess("powershell.exe exit 10") $actual = [Ansible.Process.ProcessUtil]::CreateProcess("powershell.exe exit 10")
$actual.StandardOut | Assert-Equals -Expected "" $actual.StandardOut | Assert-Equal -Expected ""
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 10 $actual.ExitCode | Assert-Equal -Expected 10
} }
"CreateProcess bad executable" = { "CreateProcess bad executable" = {
$failed = $false $failed = $false
try { try {
[Ansible.Process.ProcessUtil]::CreateProcess("fake.exe") [Ansible.Process.ProcessUtil]::CreateProcess("fake.exe")
} catch { }
catch {
$failed = $true $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 = 'Exception calling "CreateProcess" with "1" argument(s): "CreateProcessW() failed '
$expected += '(The system cannot find the file specified, Win32ErrorCode 2)"' $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" = { "CreateProcess with unicode" = {
$actual = [Ansible.Process.ProcessUtil]::CreateProcess("cmd.exe /c echo 💩 café") $actual = [Ansible.Process.ProcessUtil]::CreateProcess("cmd.exe /c echo 💩 café")
$actual.StandardOut | Assert-Equals -Expected "💩 café`r`n" $actual.StandardOut | Assert-Equal -Expected "💩 café`r`n"
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, "cmd.exe /c echo 💩 café", $null, $null) $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, "cmd.exe /c echo 💩 café", $null, $null)
$actual.StandardOut | Assert-Equals -Expected "💩 café`r`n" $actual.StandardOut | Assert-Equal -Expected "💩 café`r`n"
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess without working dir" = { "CreateProcess without working dir" = {
$expected = $pwd.Path + "`r`n" $expected = $pwd.Path + "`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $pwd.Path', $null, $null) $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $pwd.Path', $null, $null)
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess with working dir" = { "CreateProcess with working dir" = {
$expected = "C:\Windows`r`n" $expected = "C:\Windows`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $pwd.Path', "C:\Windows", $null) $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $pwd.Path', "C:\Windows", $null)
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess without environment" = { "CreateProcess without environment" = {
$expected = "$($env:USERNAME)`r`n" $expected = "$($env:USERNAME)`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $env:TEST; $env:USERNAME', $null, $null) $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe $env:TEST; $env:USERNAME', $null, $null)
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess with environment" = { "CreateProcess with environment" = {
@ -161,69 +166,70 @@ $tests = @{
TEST2 = "Testing 2" TEST2 = "Testing 2"
} }
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'cmd.exe /c set', $null, $env_vars) $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 ("TEST=tesTing" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equal -Expected $true
("TEST2=Testing 2" -cin $actual.StandardOut.Split("`r`n")) | Assert-Equals -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-Equals -Expected $true ("USERNAME=$($env:USERNAME)" -cnotin $actual.StandardOut.Split("`r`n")) | Assert-Equal -Expected $true
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess with string stdin" = { "CreateProcess with string stdin" = {
$expected = "input value`r`n`r`n" $expected = "input value`r`n`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()', $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
$null, $null, "input value") $null, $null, "input value")
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess with string stdin and newline" = { "CreateProcess with string stdin and newline" = {
$expected = "input value`r`n`r`n" $expected = "input value`r`n`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()', $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
$null, $null, "input value`r`n") $null, $null, "input value`r`n")
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess with byte stdin" = { "CreateProcess with byte stdin" = {
$expected = "input value`r`n" $expected = "input value`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()', $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
$null, $null, [System.Text.Encoding]::UTF8.GetBytes("input value")) $null, $null, [System.Text.Encoding]::UTF8.GetBytes("input value"))
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess with byte stdin and newline" = { "CreateProcess with byte stdin and newline" = {
$expected = "input value`r`n`r`n" $expected = "input value`r`n`r`n"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()', $actual = [Ansible.Process.ProcessUtil]::CreateProcess($null, 'powershell.exe [System.Console]::In.ReadToEnd()',
$null, $null, [System.Text.Encoding]::UTF8.GetBytes("input value`r`n")) $null, $null, [System.Text.Encoding]::UTF8.GetBytes("input value`r`n"))
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess with lpApplicationName" = { "CreateProcess with lpApplicationName" = {
$expected = "abc`r`n" $expected = "abc`r`n"
$full_path = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe" $full_path = "$($env:SystemRoot)\System32\WindowsPowerShell\v1.0\powershell.exe"
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($full_path, "Write-Output 'abc'", $null, $null) $actual = [Ansible.Process.ProcessUtil]::CreateProcess($full_path, "Write-Output 'abc'", $null, $null)
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
$actual = [Ansible.Process.ProcessUtil]::CreateProcess($full_path, "powershell.exe Write-Output 'abc'", $null, $null) $actual = [Ansible.Process.ProcessUtil]::CreateProcess($full_path, "powershell.exe Write-Output 'abc'", $null, $null)
$actual.StandardOut | Assert-Equals -Expected $expected $actual.StandardOut | Assert-Equal -Expected $expected
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
"CreateProcess with unicode and us-ascii encoding" = { "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 = [Ansible.Process.ProcessUtil]::CreateProcess($null, "cmd.exe /c echo $poop café", $null, $null, '', 'us-ascii')
$actual.StandardOut | Assert-Equals -Expected "??? caf??`r`n" $actual.StandardOut | Assert-Equal -Expected "??? caf??`r`n"
$actual.StandardError | Assert-Equals -Expected "" $actual.StandardError | Assert-Equal -Expected ""
$actual.ExitCode | Assert-Equals -Expected 0 $actual.ExitCode | Assert-Equal -Expected 0
} }
} }

@ -9,22 +9,24 @@ $module = [Ansible.Basic.AnsibleModule]::Create($args, @{})
$path = "$env:SystemRoot\System32\svchost.exe" $path = "$env:SystemRoot\System32\svchost.exe"
Function Assert-Equals { Function Assert-Equal {
param( param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual, [Parameter(Mandatory = $true, ValueFromPipeline = $true)][AllowNull()]$Actual,
[Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected [Parameter(Mandatory = $true, Position = 0)][AllowNull()]$Expected
) )
process {
$matched = $false $matched = $false
if ($Actual -is [System.Collections.ArrayList] -or $Actual -is [Array] -or $Actual -is [System.Collections.IList]) { 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++) { for ($i = 0; $i -lt $Actual.Count; $i++) {
$actualValue = $Actual[$i] $actualValue = $Actual[$i]
$expectedValue = $Expected[$i] $expectedValue = $Expected[$i]
Assert-Equals -Actual $actualValue -Expected $expectedValue Assert-Equal -Actual $actualValue -Expected $expectedValue
} }
$matched = $true $matched = $true
} else { }
else {
$matched = $Actual -ceq $Expected $matched = $Actual -ceq $Expected
} }
@ -43,6 +45,7 @@ Function Assert-Equals {
$module.FailJson("AssertionError: actual != expected") $module.FailJson("AssertionError: actual != expected")
} }
} }
}
Function Invoke-Sc { Function Invoke-Sc {
[CmdletBinding()] [CmdletBinding()]
@ -66,7 +69,8 @@ Function Invoke-Sc {
$commandArgs.Add("$($arg.Key)=") $commandArgs.Add("$($arg.Key)=")
$commandArgs.Add($arg.Value) $commandArgs.Add($arg.Value)
} }
} else { }
else {
foreach ($arg in $Arguments) { foreach ($arg in $Arguments) {
$commandArgs.Add($arg) $commandArgs.Add($arg)
} }
@ -118,7 +122,8 @@ Function Invoke-Sc {
} }
$v = $lineSplit[1].Trim() $v = $lineSplit[1].Trim()
} else { }
else {
$k = $currentKey $k = $currentKey
$v = $line $v = $line
} }
@ -126,18 +131,21 @@ Function Invoke-Sc {
if ($qtriggerSection.Count -gt 0) { if ($qtriggerSection.Count -gt 0) {
if ($k -eq 'DATA') { if ($k -eq 'DATA') {
$qtriggerSection.Data.Add($v) $qtriggerSection.Data.Add($v)
} else { }
else {
$qtriggerSection.Type = $k $qtriggerSection.Type = $k
$qtriggerSection.SubType = $v $qtriggerSection.SubType = $v
$qtriggerSection.Data = [System.Collections.Generic.List[String]]@() $qtriggerSection.Data = [System.Collections.Generic.List[String]]@()
} }
} else { }
else {
if ($info.ContainsKey($k)) { if ($info.ContainsKey($k)) {
if ($info[$k] -isnot [System.Collections.Generic.List[String]]) { if ($info[$k] -isnot [System.Collections.Generic.List[String]]) {
$info[$k] = [System.Collections.Generic.List[String]]@($info[$k]) $info[$k] = [System.Collections.Generic.List[String]]@($info[$k])
} }
$info[$k].Add($v) $info[$k].Add($v)
} else { }
else {
$currentKey = $k $currentKey = $k
$info[$k] = $v $info[$k] = $v
} }
@ -155,42 +163,43 @@ $tests = [Ordered]@{
"Props on service created by New-Service" = { "Props on service created by New-Service" = {
$actual = New-Object -TypeName Ansible.Service.Service -ArgumentList $serviceName $actual = New-Object -TypeName Ansible.Service.Service -ArgumentList $serviceName
$actual.ServiceName | Assert-Equals -Expected $serviceName $actual.ServiceName | Assert-Equal -Expected $serviceName
$actual.ServiceType | Assert-Equals -Expected ([Ansible.Service.ServiceType]::Win32OwnProcess) $actual.ServiceType | Assert-Equal -Expected ([Ansible.Service.ServiceType]::Win32OwnProcess)
$actual.StartType | Assert-Equals -Expected ([Ansible.Service.ServiceStartType]::DemandStart) $actual.StartType | Assert-Equal -Expected ([Ansible.Service.ServiceStartType]::DemandStart)
$actual.ErrorControl | Assert-Equals -Expected ([Ansible.Service.ErrorControl]::Normal) $actual.ErrorControl | Assert-Equal -Expected ([Ansible.Service.ErrorControl]::Normal)
$actual.Path | Assert-Equals -Expected ('"{0}"' -f $path) $actual.Path | Assert-Equal -Expected ('"{0}"' -f $path)
$actual.LoadOrderGroup | Assert-Equals -Expected "" $actual.LoadOrderGroup | Assert-Equal -Expected ""
$actual.DependentOn.Count | Assert-Equals -Expected 0 $actual.DependentOn.Count | Assert-Equal -Expected 0
$actual.Account | Assert-Equals -Expected ( $actual.Account | Assert-Equal -Expected (
[System.Security.Principal.SecurityIdentifier]'S-1-5-18').Translate([System.Security.Principal.NTAccount] [System.Security.Principal.SecurityIdentifier]'S-1-5-18').Translate([System.Security.Principal.NTAccount]
) )
$actual.DisplayName | Assert-Equals -Expected $serviceName $actual.DisplayName | Assert-Equal -Expected $serviceName
$actual.Description | Assert-Equals -Expected $null $actual.Description | Assert-Equal -Expected $null
$actual.FailureActions.ResetPeriod | Assert-Equals -Expected 0 $actual.FailureActions.ResetPeriod | Assert-Equal -Expected 0
$actual.FailureActions.RebootMsg | Assert-Equals -Expected $null $actual.FailureActions.RebootMsg | Assert-Equal -Expected $null
$actual.FailureActions.Command | Assert-Equals -Expected $null $actual.FailureActions.Command | Assert-Equal -Expected $null
$actual.FailureActions.Actions.Count | Assert-Equals -Expected 0 $actual.FailureActions.Actions.Count | Assert-Equal -Expected 0
$actual.FailureActionsOnNonCrashFailures | Assert-Equals -Expected $false $actual.FailureActionsOnNonCrashFailures | Assert-Equal -Expected $false
$actual.ServiceSidInfo | Assert-Equals -Expected ([Ansible.Service.ServiceSidInfo]::None) $actual.ServiceSidInfo | Assert-Equal -Expected ([Ansible.Service.ServiceSidInfo]::None)
$actual.RequiredPrivileges.Count | Assert-Equals -Expected 0 $actual.RequiredPrivileges.Count | Assert-Equal -Expected 0
# Cannot test default values as it differs per OS version # Cannot test default values as it differs per OS version
$null -ne $actual.PreShutdownTimeout | Assert-Equals -Expected $true $null -ne $actual.PreShutdownTimeout | Assert-Equal -Expected $true
$actual.Triggers.Count | Assert-Equals -Expected 0 $actual.Triggers.Count | Assert-Equal -Expected 0
$actual.PreferredNode | Assert-Equals -Expected $null $actual.PreferredNode | Assert-Equal -Expected $null
if ([Environment]::OSVersion.Version -ge [Version]'6.3') { if ([Environment]::OSVersion.Version -ge [Version]'6.3') {
$actual.LaunchProtection | Assert-Equals -Expected ([Ansible.Service.LaunchProtection]::None) $actual.LaunchProtection | Assert-Equal -Expected ([Ansible.Service.LaunchProtection]::None)
} else { }
$actual.LaunchProtection | Assert-Equals -Expected $null else {
$actual.LaunchProtection | Assert-Equal -Expected $null
} }
$actual.State | Assert-Equals -Expected ([Ansible.Service.ServiceStatus]::Stopped) $actual.State | Assert-Equal -Expected ([Ansible.Service.ServiceStatus]::Stopped)
$actual.Win32ExitCode | Assert-Equals -Expected 1077 # ERROR_SERVICE_NEVER_STARTED $actual.Win32ExitCode | Assert-Equal -Expected 1077 # ERROR_SERVICE_NEVER_STARTED
$actual.ServiceExitCode | Assert-Equals -Expected 0 $actual.ServiceExitCode | Assert-Equal -Expected 0
$actual.Checkpoint | Assert-Equals -Expected 0 $actual.Checkpoint | Assert-Equal -Expected 0
$actual.WaitHint | Assert-Equals -Expected 0 $actual.WaitHint | Assert-Equal -Expected 0
$actual.ProcessId | Assert-Equals -Expected 0 $actual.ProcessId | Assert-Equal -Expected 0
$actual.ServiceFlags | Assert-Equals -Expected ([Ansible.Service.ServiceFlags]::None) $actual.ServiceFlags | Assert-Equal -Expected ([Ansible.Service.ServiceFlags]::None)
$actual.DependedBy.Count | Assert-Equals 0 $actual.DependedBy.Count | Assert-Equal 0
} }
"Service creation through util" = { "Service creation through util" = {
@ -199,44 +208,46 @@ $tests = [Ordered]@{
try { try {
$cmdletService = Get-Service -Name $testName -ErrorAction SilentlyContinue $cmdletService = Get-Service -Name $testName -ErrorAction SilentlyContinue
$null -ne $cmdletService | Assert-Equals -Expected $true $null -ne $cmdletService | Assert-Equal -Expected $true
$actual.ServiceName | Assert-Equals -Expected $testName $actual.ServiceName | Assert-Equal -Expected $testName
$actual.ServiceType | Assert-Equals -Expected ([Ansible.Service.ServiceType]::Win32OwnProcess) $actual.ServiceType | Assert-Equal -Expected ([Ansible.Service.ServiceType]::Win32OwnProcess)
$actual.StartType | Assert-Equals -Expected ([Ansible.Service.ServiceStartType]::DemandStart) $actual.StartType | Assert-Equal -Expected ([Ansible.Service.ServiceStartType]::DemandStart)
$actual.ErrorControl | Assert-Equals -Expected ([Ansible.Service.ErrorControl]::Normal) $actual.ErrorControl | Assert-Equal -Expected ([Ansible.Service.ErrorControl]::Normal)
$actual.Path | Assert-Equals -Expected ('"{0}"' -f $path) $actual.Path | Assert-Equal -Expected ('"{0}"' -f $path)
$actual.LoadOrderGroup | Assert-Equals -Expected "" $actual.LoadOrderGroup | Assert-Equal -Expected ""
$actual.DependentOn.Count | Assert-Equals -Expected 0 $actual.DependentOn.Count | Assert-Equal -Expected 0
$actual.Account | Assert-Equals -Expected ( $actual.Account | Assert-Equal -Expected (
[System.Security.Principal.SecurityIdentifier]'S-1-5-18').Translate([System.Security.Principal.NTAccount] [System.Security.Principal.SecurityIdentifier]'S-1-5-18').Translate([System.Security.Principal.NTAccount]
) )
$actual.DisplayName | Assert-Equals -Expected $testName $actual.DisplayName | Assert-Equal -Expected $testName
$actual.Description | Assert-Equals -Expected $null $actual.Description | Assert-Equal -Expected $null
$actual.FailureActions.ResetPeriod | Assert-Equals -Expected 0 $actual.FailureActions.ResetPeriod | Assert-Equal -Expected 0
$actual.FailureActions.RebootMsg | Assert-Equals -Expected $null $actual.FailureActions.RebootMsg | Assert-Equal -Expected $null
$actual.FailureActions.Command | Assert-Equals -Expected $null $actual.FailureActions.Command | Assert-Equal -Expected $null
$actual.FailureActions.Actions.Count | Assert-Equals -Expected 0 $actual.FailureActions.Actions.Count | Assert-Equal -Expected 0
$actual.FailureActionsOnNonCrashFailures | Assert-Equals -Expected $false $actual.FailureActionsOnNonCrashFailures | Assert-Equal -Expected $false
$actual.ServiceSidInfo | Assert-Equals -Expected ([Ansible.Service.ServiceSidInfo]::None) $actual.ServiceSidInfo | Assert-Equal -Expected ([Ansible.Service.ServiceSidInfo]::None)
$actual.RequiredPrivileges.Count | Assert-Equals -Expected 0 $actual.RequiredPrivileges.Count | Assert-Equal -Expected 0
$null -ne $actual.PreShutdownTimeout | Assert-Equals -Expected $true $null -ne $actual.PreShutdownTimeout | Assert-Equal -Expected $true
$actual.Triggers.Count | Assert-Equals -Expected 0 $actual.Triggers.Count | Assert-Equal -Expected 0
$actual.PreferredNode | Assert-Equals -Expected $null $actual.PreferredNode | Assert-Equal -Expected $null
if ([Environment]::OSVersion.Version -ge [Version]'6.3') { if ([Environment]::OSVersion.Version -ge [Version]'6.3') {
$actual.LaunchProtection | Assert-Equals -Expected ([Ansible.Service.LaunchProtection]::None) $actual.LaunchProtection | Assert-Equal -Expected ([Ansible.Service.LaunchProtection]::None)
} else { }
$actual.LaunchProtection | Assert-Equals -Expected $null else {
} $actual.LaunchProtection | Assert-Equal -Expected $null
$actual.State | Assert-Equals -Expected ([Ansible.Service.ServiceStatus]::Stopped) }
$actual.Win32ExitCode | Assert-Equals -Expected 1077 # ERROR_SERVICE_NEVER_STARTED $actual.State | Assert-Equal -Expected ([Ansible.Service.ServiceStatus]::Stopped)
$actual.ServiceExitCode | Assert-Equals -Expected 0 $actual.Win32ExitCode | Assert-Equal -Expected 1077 # ERROR_SERVICE_NEVER_STARTED
$actual.Checkpoint | Assert-Equals -Expected 0 $actual.ServiceExitCode | Assert-Equal -Expected 0
$actual.WaitHint | Assert-Equals -Expected 0 $actual.Checkpoint | Assert-Equal -Expected 0
$actual.ProcessId | Assert-Equals -Expected 0 $actual.WaitHint | Assert-Equal -Expected 0
$actual.ServiceFlags | Assert-Equals -Expected ([Ansible.Service.ServiceFlags]::None) $actual.ProcessId | Assert-Equal -Expected 0
$actual.DependedBy.Count | Assert-Equals 0 $actual.ServiceFlags | Assert-Equal -Expected ([Ansible.Service.ServiceFlags]::None)
} finally { $actual.DependedBy.Count | Assert-Equal 0
}
finally {
$actual.Delete() $actual.Delete()
} }
} }
@ -245,13 +256,14 @@ $tests = [Ordered]@{
$failed = $false $failed = $false
try { try {
$null = New-Object -TypeName Ansible.Service.Service -ArgumentList 'fake_service' $null = New-Object -TypeName Ansible.Service.Service -ArgumentList 'fake_service'
} catch [Ansible.Service.ServiceManagerException] { }
catch [Ansible.Service.ServiceManagerException] {
# 1060 == ERROR_SERVICE_DOES_NOT_EXIST # 1060 == ERROR_SERVICE_DOES_NOT_EXIST
$_.Exception.Message -like '*Win32ErrorCode 1060 - 0x00000424*' | Assert-Equals -Expected $true $_.Exception.Message -like '*Win32ErrorCode 1060 - 0x00000424*' | Assert-Equal -Expected $true
$failed = $true $failed = $true
} }
$failed | Assert-Equals -Expected $true $failed | Assert-Equal -Expected $true
} }
"Open with specific access rights" = { "Open with specific access rights" = {
@ -260,19 +272,20 @@ $tests = [Ordered]@{
) )
# QueryStatus can get the status # QueryStatus can get the status
$service.State | Assert-Equals -Expected ([Ansible.Service.ServiceStatus]::Stopped) $service.State | Assert-Equal -Expected ([Ansible.Service.ServiceStatus]::Stopped)
# Should fail to get the config because we did not request that right # Should fail to get the config because we did not request that right
$failed = $false $failed = $false
try { try {
$service.Path = 'fail' $service.Path = 'fail'
} catch [Ansible.Service.ServiceManagerException] { }
catch [Ansible.Service.ServiceManagerException] {
# 5 == ERROR_ACCESS_DENIED # 5 == ERROR_ACCESS_DENIED
$_.Exception.Message -like '*Win32ErrorCode 5 - 0x00000005*' | Assert-Equals -Expected $true $_.Exception.Message -like '*Win32ErrorCode 5 - 0x00000005*' | Assert-Equal -Expected $true
$failed = $true $failed = $true
} }
$failed | Assert-Equals -Expected $true $failed | Assert-Equal -Expected $true
} }
@ -281,12 +294,12 @@ $tests = [Ordered]@{
$service.ServiceType = [Ansible.Service.ServiceType]::Win32ShareProcess $service.ServiceType = [Ansible.Service.ServiceType]::Win32ShareProcess
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.ServiceType | Assert-Equals -Expected ([Ansible.Service.ServiceType]::Win32ShareProcess) $service.ServiceType | Assert-Equal -Expected ([Ansible.Service.ServiceType]::Win32ShareProcess)
$actual.TYPE | Assert-Equals -Expected "20 WIN32_SHARE_PROCESS" $actual.TYPE | Assert-Equal -Expected "20 WIN32_SHARE_PROCESS"
$null = Invoke-Sc -Action config -Name $serviceName -Arguments @{type = "own" } $null = Invoke-Sc -Action config -Name $serviceName -Arguments @{type = "own" }
$service.Refresh() $service.Refresh()
$service.ServiceType | Assert-Equals -Expected ([Ansible.Service.ServiceType]::Win32OwnProcess) $service.ServiceType | Assert-Equal -Expected ([Ansible.Service.ServiceType]::Win32OwnProcess)
} }
"Create desktop interactive service" = { "Create desktop interactive service" = {
@ -294,29 +307,30 @@ $tests = [Ordered]@{
$service.ServiceType = [Ansible.Service.ServiceType]'Win32OwnProcess, InteractiveProcess' $service.ServiceType = [Ansible.Service.ServiceType]'Win32OwnProcess, InteractiveProcess'
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$actual.TYPE | Assert-Equals -Expected "110 WIN32_OWN_PROCESS (interactive)" $actual.TYPE | Assert-Equal -Expected "110 WIN32_OWN_PROCESS (interactive)"
$service.ServiceType | Assert-Equals -Expected ([Ansible.Service.ServiceType]'Win32OwnProcess, InteractiveProcess') $service.ServiceType | Assert-Equal -Expected ([Ansible.Service.ServiceType]'Win32OwnProcess, InteractiveProcess')
# Change back from interactive process # Change back from interactive process
$service.ServiceType = [Ansible.Service.ServiceType]::Win32OwnProcess $service.ServiceType = [Ansible.Service.ServiceType]::Win32OwnProcess
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$actual.TYPE | Assert-Equals -Expected "10 WIN32_OWN_PROCESS" $actual.TYPE | Assert-Equal -Expected "10 WIN32_OWN_PROCESS"
$service.ServiceType | Assert-Equals -Expected ([Ansible.Service.ServiceType]::Win32OwnProcess) $service.ServiceType | Assert-Equal -Expected ([Ansible.Service.ServiceType]::Win32OwnProcess)
$service.Account = [System.Security.Principal.SecurityIdentifier]'S-1-5-20' $service.Account = [System.Security.Principal.SecurityIdentifier]'S-1-5-20'
$failed = $false $failed = $false
try { try {
$service.ServiceType = [Ansible.Service.ServiceType]'Win32OwnProcess, InteractiveProcess' $service.ServiceType = [Ansible.Service.ServiceType]'Win32OwnProcess, InteractiveProcess'
} catch [Ansible.Service.ServiceManagerException] { }
catch [Ansible.Service.ServiceManagerException] {
$failed = $true $failed = $true
$_.Exception.NativeErrorCode | Assert-Equals -Expected 87 # ERROR_INVALID_PARAMETER $_.Exception.NativeErrorCode | Assert-Equal -Expected 87 # ERROR_INVALID_PARAMETER
} }
$failed | Assert-Equals -Expected $true $failed | Assert-Equal -Expected $true
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$actual.TYPE | Assert-Equals -Expected "10 WIN32_OWN_PROCESS" $actual.TYPE | Assert-Equal -Expected "10 WIN32_OWN_PROCESS"
} }
"Modify StartType" = { "Modify StartType" = {
@ -324,12 +338,12 @@ $tests = [Ordered]@{
$service.StartType = [Ansible.Service.ServiceStartType]::Disabled $service.StartType = [Ansible.Service.ServiceStartType]::Disabled
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.StartType | Assert-Equals -Expected ([Ansible.Service.ServiceStartType]::Disabled) $service.StartType | Assert-Equal -Expected ([Ansible.Service.ServiceStartType]::Disabled)
$actual.START_TYPE | Assert-Equals -Expected "4 DISABLED" $actual.START_TYPE | Assert-Equal -Expected "4 DISABLED"
$null = Invoke-Sc -Action config -Name $serviceName -Arguments @{start = "demand" } $null = Invoke-Sc -Action config -Name $serviceName -Arguments @{start = "demand" }
$service.Refresh() $service.Refresh()
$service.StartType | Assert-Equals -Expected ([Ansible.Service.ServiceStartType]::DemandStart) $service.StartType | Assert-Equal -Expected ([Ansible.Service.ServiceStartType]::DemandStart)
} }
"Modify StartType auto delayed" = { "Modify StartType auto delayed" = {
@ -342,29 +356,29 @@ $tests = [Ordered]@{
$service.StartType = [Ansible.Service.ServiceStartType]::AutoStartDelayed $service.StartType = [Ansible.Service.ServiceStartType]::AutoStartDelayed
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.StartType | Assert-Equals -Expected ([Ansible.Service.ServiceStartType]::AutoStartDelayed) $service.StartType | Assert-Equal -Expected ([Ansible.Service.ServiceStartType]::AutoStartDelayed)
$actual.START_TYPE | Assert-Equals -Expected "2 AUTO_START (DELAYED)" $actual.START_TYPE | Assert-Equal -Expected "2 AUTO_START (DELAYED)"
# Auto Start Delayed -> Auto Start # Auto Start Delayed -> Auto Start
$service.StartType = [Ansible.Service.ServiceStartType]::AutoStart $service.StartType = [Ansible.Service.ServiceStartType]::AutoStart
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.StartType | Assert-Equals -Expected ([Ansible.Service.ServiceStartType]::AutoStart) $service.StartType | Assert-Equal -Expected ([Ansible.Service.ServiceStartType]::AutoStart)
$actual.START_TYPE | Assert-Equals -Expected "2 AUTO_START" $actual.START_TYPE | Assert-Equal -Expected "2 AUTO_START"
# Auto Start -> Auto Start Delayed # Auto Start -> Auto Start Delayed
$service.StartType = [Ansible.Service.ServiceStartType]::AutoStartDelayed $service.StartType = [Ansible.Service.ServiceStartType]::AutoStartDelayed
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.StartType | Assert-Equals -Expected ([Ansible.Service.ServiceStartType]::AutoStartDelayed) $service.StartType | Assert-Equal -Expected ([Ansible.Service.ServiceStartType]::AutoStartDelayed)
$actual.START_TYPE | Assert-Equals -Expected "2 AUTO_START (DELAYED)" $actual.START_TYPE | Assert-Equal -Expected "2 AUTO_START (DELAYED)"
# Auto Start Delayed -> Manual # Auto Start Delayed -> Manual
$service.StartType = [Ansible.Service.ServiceStartType]::DemandStart $service.StartType = [Ansible.Service.ServiceStartType]::DemandStart
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.StartType | Assert-Equals -Expected ([Ansible.Service.ServiceStartType]::DemandStart) $service.StartType | Assert-Equal -Expected ([Ansible.Service.ServiceStartType]::DemandStart)
$actual.START_TYPE | Assert-Equals -Expected "3 DEMAND_START" $actual.START_TYPE | Assert-Equal -Expected "3 DEMAND_START"
} }
"Modify ErrorControl" = { "Modify ErrorControl" = {
@ -372,12 +386,12 @@ $tests = [Ordered]@{
$service.ErrorControl = [Ansible.Service.ErrorControl]::Severe $service.ErrorControl = [Ansible.Service.ErrorControl]::Severe
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.ErrorControl | Assert-Equals -Expected ([Ansible.Service.ErrorControl]::Severe) $service.ErrorControl | Assert-Equal -Expected ([Ansible.Service.ErrorControl]::Severe)
$actual.ERROR_CONTROL | Assert-Equals -Expected "2 SEVERE" $actual.ERROR_CONTROL | Assert-Equal -Expected "2 SEVERE"
$null = Invoke-Sc -Action config -Name $serviceName -Arguments @{error = "ignore" } $null = Invoke-Sc -Action config -Name $serviceName -Arguments @{error = "ignore" }
$service.Refresh() $service.Refresh()
$service.ErrorControl | Assert-Equals -Expected ([Ansible.Service.ErrorControl]::Ignore) $service.ErrorControl | Assert-Equal -Expected ([Ansible.Service.ErrorControl]::Ignore)
} }
"Modify Path" = { "Modify Path" = {
@ -385,12 +399,12 @@ $tests = [Ordered]@{
$service.Path = "Fake path" $service.Path = "Fake path"
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.Path | Assert-Equals -Expected "Fake path" $service.Path | Assert-Equal -Expected "Fake path"
$actual.BINARY_PATH_NAME | Assert-Equals -Expected "Fake path" $actual.BINARY_PATH_NAME | Assert-Equal -Expected "Fake path"
$null = Invoke-Sc -Action config -Name $serviceName -Arguments @{binpath = "other fake path" } $null = Invoke-Sc -Action config -Name $serviceName -Arguments @{binpath = "other fake path" }
$service.Refresh() $service.Refresh()
$service.Path | Assert-Equals -Expected "other fake path" $service.Path | Assert-Equal -Expected "other fake path"
} }
"Modify LoadOrderGroup" = { "Modify LoadOrderGroup" = {
@ -398,12 +412,12 @@ $tests = [Ordered]@{
$service.LoadOrderGroup = "my group" $service.LoadOrderGroup = "my group"
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.LoadOrderGroup | Assert-Equals -Expected "my group" $service.LoadOrderGroup | Assert-Equal -Expected "my group"
$actual.LOAD_ORDER_GROUP | Assert-Equals -Expected "my group" $actual.LOAD_ORDER_GROUP | Assert-Equal -Expected "my group"
$null = Invoke-Sc -Action config -Name $serviceName -Arguments @{group = "" } $null = Invoke-Sc -Action config -Name $serviceName -Arguments @{group = "" }
$service.Refresh() $service.Refresh()
$service.LoadOrderGroup | Assert-Equals -Expected "" $service.LoadOrderGroup | Assert-Equal -Expected ""
} }
"Modify DependentOn" = { "Modify DependentOn" = {
@ -411,12 +425,12 @@ $tests = [Ordered]@{
$service.DependentOn = @("HTTP", "WinRM") $service.DependentOn = @("HTTP", "WinRM")
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
@(,$service.DependentOn) | Assert-Equals -Expected @("HTTP", "WinRM") @(, $service.DependentOn) | Assert-Equal -Expected @("HTTP", "WinRM")
@(,$actual.DEPENDENCIES) | Assert-Equals -Expected @("HTTP", "WinRM") @(, $actual.DEPENDENCIES) | Assert-Equal -Expected @("HTTP", "WinRM")
$null = Invoke-Sc -Action config -Name $serviceName -Arguments @{depend = "" } $null = Invoke-Sc -Action config -Name $serviceName -Arguments @{depend = "" }
$service.Refresh() $service.Refresh()
$service.DependentOn.Count | Assert-Equals -Expected 0 $service.DependentOn.Count | Assert-Equal -Expected 0
} }
"Modify Account - service account" = { "Modify Account - service account" = {
@ -431,17 +445,17 @@ $tests = [Ordered]@{
$service.Account = $networkSid $service.Account = $networkSid
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.Account | Assert-Equals -Expected $networkName $service.Account | Assert-Equal -Expected $networkName
$actual.SERVICE_START_NAME | Assert-Equals -Expected $networkName.Value $actual.SERVICE_START_NAME | Assert-Equal -Expected $networkName.Value
$null = Invoke-Sc -Action config -Name $serviceName -Arguments @{obj = $localName.Value } $null = Invoke-Sc -Action config -Name $serviceName -Arguments @{obj = $localName.Value }
$service.Refresh() $service.Refresh()
$service.Account | Assert-Equals -Expected $localName $service.Account | Assert-Equal -Expected $localName
$service.Account = $systemSid $service.Account = $systemSid
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.Account | Assert-Equals -Expected $systemName $service.Account | Assert-Equal -Expected $systemName
$actual.SERVICE_START_NAME | Assert-Equals -Expected "LocalSystem" $actual.SERVICE_START_NAME | Assert-Equal -Expected "LocalSystem"
} }
"Modify Account - user" = { "Modify Account - user" = {
@ -459,19 +473,20 @@ $tests = [Ordered]@{
$actualSid = ([System.Security.Principal.NTAccount]"$env:COMPUTERNAME\$username").Translate( $actualSid = ([System.Security.Principal.NTAccount]"$env:COMPUTERNAME\$username").Translate(
[System.Security.Principal.SecurityIdentifier] [System.Security.Principal.SecurityIdentifier]
) )
} else { }
else {
$actualSid = $service.Account.Translate([System.Security.Principal.SecurityIdentifier]) $actualSid = $service.Account.Translate([System.Security.Principal.SecurityIdentifier])
} }
$actualSid.Value | Assert-Equals -Expected $currentSid.Value $actualSid.Value | Assert-Equal -Expected $currentSid.Value
$actual.SERVICE_START_NAME | Assert-Equals -Expected $service.Account.Value $actual.SERVICE_START_NAME | Assert-Equal -Expected $service.Account.Value
# Go back to SYSTEM from account # Go back to SYSTEM from account
$systemSid = [System.Security.Principal.SecurityIdentifier]'S-1-5-18' $systemSid = [System.Security.Principal.SecurityIdentifier]'S-1-5-18'
$service.Account = $systemSid $service.Account = $systemSid
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.Account | Assert-Equals -Expected $systemSid.Translate([System.Security.Principal.NTAccount]) $service.Account | Assert-Equal -Expected $systemSid.Translate([System.Security.Principal.NTAccount])
$actual.SERVICE_START_NAME | Assert-Equals -Expected "LocalSystem" $actual.SERVICE_START_NAME | Assert-Equal -Expected "LocalSystem"
} }
"Modify Account - virtual account" = { "Modify Account - virtual account" = {
@ -481,8 +496,8 @@ $tests = [Ordered]@{
$service.Account = $account $service.Account = $account
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.Account | Assert-Equals -Expected $account $service.Account | Assert-Equal -Expected $account
$actual.SERVICE_START_NAME | Assert-Equals -Expected $account.Value $actual.SERVICE_START_NAME | Assert-Equal -Expected $account.Value
} }
"Modify Account - gMSA" = { "Modify Account - gMSA" = {
@ -497,8 +512,8 @@ $tests = [Ordered]@{
$service.Account = $gmsaName $service.Account = $gmsaName
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.Account | Assert-Equals -Expected $gmsaName $service.Account | Assert-Equal -Expected $gmsaName
$actual.SERVICE_START_NAME | Assert-Equals -Expected $gmsaName $actual.SERVICE_START_NAME | Assert-Equal -Expected $gmsaName
# Go from gMSA to account and back to verify the Password doesn't matter. # Go from gMSA to account and back to verify the Password doesn't matter.
$currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().User $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().User
@ -510,8 +525,8 @@ $tests = [Ordered]@{
$service.Account = $gmsaSid $service.Account = $gmsaSid
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.Account | Assert-Equals -Expected $gmsaNetlogon $service.Account | Assert-Equal -Expected $gmsaNetlogon
$actual.SERVICE_START_NAME | Assert-Equals -Expected $gmsaNetlogon.Value $actual.SERVICE_START_NAME | Assert-Equal -Expected $gmsaNetlogon.Value
} }
"Modify DisplayName" = { "Modify DisplayName" = {
@ -519,12 +534,12 @@ $tests = [Ordered]@{
$service.DisplayName = "Custom Service Name" $service.DisplayName = "Custom Service Name"
$actual = Invoke-Sc -Action qc -Name $serviceName $actual = Invoke-Sc -Action qc -Name $serviceName
$service.DisplayName | Assert-Equals -Expected "Custom Service Name" $service.DisplayName | Assert-Equal -Expected "Custom Service Name"
$actual.DISPLAY_NAME | Assert-Equals -Expected "Custom Service Name" $actual.DISPLAY_NAME | Assert-Equal -Expected "Custom Service Name"
$null = Invoke-Sc -Action config -Name $serviceName -Arguments @{displayname = "New Service Name" } $null = Invoke-Sc -Action config -Name $serviceName -Arguments @{displayname = "New Service Name" }
$service.Refresh() $service.Refresh()
$service.DisplayName | Assert-Equals -Expected "New Service Name" $service.DisplayName | Assert-Equal -Expected "New Service Name"
} }
"Modify Description" = { "Modify Description" = {
@ -532,17 +547,17 @@ $tests = [Ordered]@{
$service.Description = "My custom service description" $service.Description = "My custom service description"
$actual = Invoke-Sc -Action qdescription -Name $serviceName $actual = Invoke-Sc -Action qdescription -Name $serviceName
$service.Description | Assert-Equals -Expected "My custom service description" $service.Description | Assert-Equal -Expected "My custom service description"
$actual.DESCRIPTION | Assert-Equals -Expected "My custom service description" $actual.DESCRIPTION | Assert-Equal -Expected "My custom service description"
$null = Invoke-Sc -Action description -Name $serviceName -Arguments @(, "new description") $null = Invoke-Sc -Action description -Name $serviceName -Arguments @(, "new description")
$service.Description | Assert-Equals -Expected "new description" $service.Description | Assert-Equal -Expected "new description"
$service.Description = $null $service.Description = $null
$actual = Invoke-Sc -Action qdescription -Name $serviceName $actual = Invoke-Sc -Action qdescription -Name $serviceName
$service.Description | Assert-Equals -Expected $null $service.Description | Assert-Equal -Expected $null
$actual.DESCRIPTION | Assert-Equals -Expected "" $actual.DESCRIPTION | Assert-Equal -Expected ""
} }
"Modify FailureActions" = { "Modify FailureActions" = {
@ -561,43 +576,43 @@ $tests = [Ordered]@{
$service.FailureActions = $newAction $service.FailureActions = $newAction
$actual = Invoke-Sc -Action qfailure -Name $serviceName $actual = Invoke-Sc -Action qfailure -Name $serviceName
$actual.'RESET_PERIOD (in seconds)' | Assert-Equals -Expected 86400 $actual.'RESET_PERIOD (in seconds)' | Assert-Equal -Expected 86400
$actual.REBOOT_MESSAGE | Assert-Equals -Expected 'Reboot msg' $actual.REBOOT_MESSAGE | Assert-Equal -Expected 'Reboot msg'
$actual.COMMAND_LINE | Assert-Equals -Expected 'Command line' $actual.COMMAND_LINE | Assert-Equal -Expected 'Command line'
$actual.FAILURE_ACTIONS.Count | Assert-Equals -Expected 4 $actual.FAILURE_ACTIONS.Count | Assert-Equal -Expected 4
$actual.FAILURE_ACTIONS[0] | Assert-Equals -Expected "RUN PROCESS -- Delay = 1000 milliseconds." $actual.FAILURE_ACTIONS[0] | Assert-Equal -Expected "RUN PROCESS -- Delay = 1000 milliseconds."
$actual.FAILURE_ACTIONS[1] | Assert-Equals -Expected "RUN PROCESS -- Delay = 2000 milliseconds." $actual.FAILURE_ACTIONS[1] | Assert-Equal -Expected "RUN PROCESS -- Delay = 2000 milliseconds."
$actual.FAILURE_ACTIONS[2] | Assert-Equals -Expected "RESTART -- Delay = 1000 milliseconds." $actual.FAILURE_ACTIONS[2] | Assert-Equal -Expected "RESTART -- Delay = 1000 milliseconds."
$actual.FAILURE_ACTIONS[3] | Assert-Equals -Expected "REBOOT -- Delay = 1000 milliseconds." $actual.FAILURE_ACTIONS[3] | Assert-Equal -Expected "REBOOT -- Delay = 1000 milliseconds."
$service.FailureActions.Actions.Count | Assert-Equals -Expected 4 $service.FailureActions.Actions.Count | Assert-Equal -Expected 4
# Test that we can change individual settings and it doesn't change all # Test that we can change individual settings and it doesn't change all
$service.FailureActions = [Ansible.Service.FailureActions]@{ResetPeriod = 172800 } $service.FailureActions = [Ansible.Service.FailureActions]@{ResetPeriod = 172800 }
$actual = Invoke-Sc -Action qfailure -Name $serviceName $actual = Invoke-Sc -Action qfailure -Name $serviceName
$actual.'RESET_PERIOD (in seconds)' | Assert-Equals -Expected 172800 $actual.'RESET_PERIOD (in seconds)' | Assert-Equal -Expected 172800
$actual.REBOOT_MESSAGE | Assert-Equals -Expected 'Reboot msg' $actual.REBOOT_MESSAGE | Assert-Equal -Expected 'Reboot msg'
$actual.COMMAND_LINE | Assert-Equals -Expected 'Command line' $actual.COMMAND_LINE | Assert-Equal -Expected 'Command line'
$actual.FAILURE_ACTIONS.Count | Assert-Equals -Expected 4 $actual.FAILURE_ACTIONS.Count | Assert-Equal -Expected 4
$service.FailureActions.Actions.Count | Assert-Equals -Expected 4 $service.FailureActions.Actions.Count | Assert-Equal -Expected 4
$service.FailureActions = [Ansible.Service.FailureActions]@{RebootMsg = "New reboot msg" } $service.FailureActions = [Ansible.Service.FailureActions]@{RebootMsg = "New reboot msg" }
$actual = Invoke-Sc -Action qfailure -Name $serviceName $actual = Invoke-Sc -Action qfailure -Name $serviceName
$actual.'RESET_PERIOD (in seconds)' | Assert-Equals -Expected 172800 $actual.'RESET_PERIOD (in seconds)' | Assert-Equal -Expected 172800
$actual.REBOOT_MESSAGE | Assert-Equals -Expected 'New reboot msg' $actual.REBOOT_MESSAGE | Assert-Equal -Expected 'New reboot msg'
$actual.COMMAND_LINE | Assert-Equals -Expected 'Command line' $actual.COMMAND_LINE | Assert-Equal -Expected 'Command line'
$actual.FAILURE_ACTIONS.Count | Assert-Equals -Expected 4 $actual.FAILURE_ACTIONS.Count | Assert-Equal -Expected 4
$service.FailureActions.Actions.Count | Assert-Equals -Expected 4 $service.FailureActions.Actions.Count | Assert-Equal -Expected 4
$service.FailureActions = [Ansible.Service.FailureActions]@{Command = "New command line" } $service.FailureActions = [Ansible.Service.FailureActions]@{Command = "New command line" }
$actual = Invoke-Sc -Action qfailure -Name $serviceName $actual = Invoke-Sc -Action qfailure -Name $serviceName
$actual.'RESET_PERIOD (in seconds)' | Assert-Equals -Expected 172800 $actual.'RESET_PERIOD (in seconds)' | Assert-Equal -Expected 172800
$actual.REBOOT_MESSAGE | Assert-Equals -Expected 'New reboot msg' $actual.REBOOT_MESSAGE | Assert-Equal -Expected 'New reboot msg'
$actual.COMMAND_LINE | Assert-Equals -Expected 'New command line' $actual.COMMAND_LINE | Assert-Equal -Expected 'New command line'
$actual.FAILURE_ACTIONS.Count | Assert-Equals -Expected 4 $actual.FAILURE_ACTIONS.Count | Assert-Equal -Expected 4
$service.FailureActions.Actions.Count | Assert-Equals -Expected 4 $service.FailureActions.Actions.Count | Assert-Equal -Expected 4
# Test setting both ResetPeriod and Actions together # Test setting both ResetPeriod and Actions together
$service.FailureActions = [Ansible.Service.FailureActions]@{ $service.FailureActions = [Ansible.Service.FailureActions]@{
@ -609,35 +624,35 @@ $tests = [Ordered]@{
} }
$actual = Invoke-Sc -Action qfailure -Name $serviceName $actual = Invoke-Sc -Action qfailure -Name $serviceName
$actual.'RESET_PERIOD (in seconds)' | Assert-Equals -Expected 86400 $actual.'RESET_PERIOD (in seconds)' | Assert-Equal -Expected 86400
$actual.REBOOT_MESSAGE | Assert-Equals -Expected 'New reboot msg' $actual.REBOOT_MESSAGE | Assert-Equal -Expected 'New reboot msg'
$actual.COMMAND_LINE | Assert-Equals -Expected 'New command line' $actual.COMMAND_LINE | Assert-Equal -Expected 'New command line'
# sc.exe does not show the None action it just ends the list, so we verify from get_FailureActions # sc.exe does not show the None action it just ends the list, so we verify from get_FailureActions
$actual.FAILURE_ACTIONS | Assert-Equals -Expected "RUN PROCESS -- Delay = 5000 milliseconds." $actual.FAILURE_ACTIONS | Assert-Equal -Expected "RUN PROCESS -- Delay = 5000 milliseconds."
$service.FailureActions.Actions.Count | Assert-Equals -Expected 2 $service.FailureActions.Actions.Count | Assert-Equal -Expected 2
$service.FailureActions.Actions[1].Type | Assert-Equals -Expected ([Ansible.Service.FailureAction]::None) $service.FailureActions.Actions[1].Type | Assert-Equal -Expected ([Ansible.Service.FailureAction]::None)
# Test setting just Actions without ResetPeriod # Test setting just Actions without ResetPeriod
$service.FailureActions = [Ansible.Service.FailureActions]@{ $service.FailureActions = [Ansible.Service.FailureActions]@{
Actions = [Ansible.Service.Action]@{Type = [Ansible.Service.FailureAction]::RunCommand; Delay = 10000 } Actions = [Ansible.Service.Action]@{Type = [Ansible.Service.FailureAction]::RunCommand; Delay = 10000 }
} }
$actual = Invoke-Sc -Action qfailure -Name $serviceName $actual = Invoke-Sc -Action qfailure -Name $serviceName
$actual.'RESET_PERIOD (in seconds)' | Assert-Equals -Expected 86400 $actual.'RESET_PERIOD (in seconds)' | Assert-Equal -Expected 86400
$actual.REBOOT_MESSAGE | Assert-Equals -Expected 'New reboot msg' $actual.REBOOT_MESSAGE | Assert-Equal -Expected 'New reboot msg'
$actual.COMMAND_LINE | Assert-Equals -Expected 'New command line' $actual.COMMAND_LINE | Assert-Equal -Expected 'New command line'
$actual.FAILURE_ACTIONS | Assert-Equals -Expected "RUN PROCESS -- Delay = 10000 milliseconds." $actual.FAILURE_ACTIONS | Assert-Equal -Expected "RUN PROCESS -- Delay = 10000 milliseconds."
$service.FailureActions.Actions.Count | Assert-Equals -Expected 1 $service.FailureActions.Actions.Count | Assert-Equal -Expected 1
# Test removing all actions # Test removing all actions
$service.FailureActions = [Ansible.Service.FailureActions]@{ $service.FailureActions = [Ansible.Service.FailureActions]@{
Actions = @() Actions = @()
} }
$actual = Invoke-Sc -Action qfailure -Name $serviceName $actual = Invoke-Sc -Action qfailure -Name $serviceName
$actual.'RESET_PERIOD (in seconds)' | Assert-Equals -Expected 0 # ChangeServiceConfig2W resets this back to 0. $actual.'RESET_PERIOD (in seconds)' | Assert-Equal -Expected 0 # ChangeServiceConfig2W resets this back to 0.
$actual.REBOOT_MESSAGE | Assert-Equals -Expected 'New reboot msg' $actual.REBOOT_MESSAGE | Assert-Equal -Expected 'New reboot msg'
$actual.COMMAND_LINE | Assert-Equals -Expected 'New command line' $actual.COMMAND_LINE | Assert-Equal -Expected 'New command line'
$actual.PSObject.Properties.Name.Contains('FAILURE_ACTIONS') | Assert-Equals -Expected $false $actual.PSObject.Properties.Name.Contains('FAILURE_ACTIONS') | Assert-Equal -Expected $false
$service.FailureActions.Actions.Count | Assert-Equals -Expected 0 $service.FailureActions.Actions.Count | Assert-Equal -Expected 0
# Test that we are reading the right values # Test that we are reading the right values
$null = Invoke-Sc -Action failure -Name $serviceName -Arguments @{ $null = Invoke-Sc -Action failure -Name $serviceName -Arguments @{
@ -648,14 +663,14 @@ $tests = [Ordered]@{
} }
$actual = $service.FailureActions $actual = $service.FailureActions
$actual.ResetPeriod | Assert-Equals -Expected 172800 $actual.ResetPeriod | Assert-Equal -Expected 172800
$actual.RebootMsg | Assert-Equals -Expected "sc reboot msg" $actual.RebootMsg | Assert-Equal -Expected "sc reboot msg"
$actual.Command | Assert-Equals -Expected "sc command line" $actual.Command | Assert-Equal -Expected "sc command line"
$actual.Actions.Count | Assert-Equals -Expected 2 $actual.Actions.Count | Assert-Equal -Expected 2
$actual.Actions[0].Type | Assert-Equals -Expected ([Ansible.Service.FailureAction]::RunCommand) $actual.Actions[0].Type | Assert-Equal -Expected ([Ansible.Service.FailureAction]::RunCommand)
$actual.Actions[0].Delay | Assert-Equals -Expected 5000 $actual.Actions[0].Delay | Assert-Equal -Expected 5000
$actual.Actions[1].Type | Assert-Equals -Expected ([Ansible.Service.FailureAction]::Reboot) $actual.Actions[1].Type | Assert-Equal -Expected ([Ansible.Service.FailureAction]::Reboot)
$actual.Actions[1].Delay | Assert-Equals -Expected 800 $actual.Actions[1].Delay | Assert-Equal -Expected 800
} }
"Modify FailureActionsOnNonCrashFailures" = { "Modify FailureActionsOnNonCrashFailures" = {
@ -663,11 +678,11 @@ $tests = [Ordered]@{
$service.FailureActionsOnNonCrashFailures = $true $service.FailureActionsOnNonCrashFailures = $true
$actual = Invoke-Sc -Action qfailureflag -Name $serviceName $actual = Invoke-Sc -Action qfailureflag -Name $serviceName
$service.FailureActionsOnNonCrashFailures | Assert-Equals -Expected $true $service.FailureActionsOnNonCrashFailures | Assert-Equal -Expected $true
$actual.FAILURE_ACTIONS_ON_NONCRASH_FAILURES | Assert-Equals -Expected "TRUE" $actual.FAILURE_ACTIONS_ON_NONCRASH_FAILURES | Assert-Equal -Expected "TRUE"
$null = Invoke-Sc -Action failureflag -Name $serviceName -Arguments @(, 0) $null = Invoke-Sc -Action failureflag -Name $serviceName -Arguments @(, 0)
$service.FailureActionsOnNonCrashFailures | Assert-Equals -Expected $false $service.FailureActionsOnNonCrashFailures | Assert-Equal -Expected $false
} }
"Modify ServiceSidInfo" = { "Modify ServiceSidInfo" = {
@ -675,17 +690,17 @@ $tests = [Ordered]@{
$service.ServiceSidInfo = [Ansible.Service.ServiceSidInfo]::None $service.ServiceSidInfo = [Ansible.Service.ServiceSidInfo]::None
$actual = Invoke-Sc -Action qsidtype -Name $serviceName $actual = Invoke-Sc -Action qsidtype -Name $serviceName
$service.ServiceSidInfo | Assert-Equals -Expected ([Ansible.Service.ServiceSidInfo]::None) $service.ServiceSidInfo | Assert-Equal -Expected ([Ansible.Service.ServiceSidInfo]::None)
$actual.SERVICE_SID_TYPE | Assert-Equals -Expected 'NONE' $actual.SERVICE_SID_TYPE | Assert-Equal -Expected 'NONE'
$null = Invoke-Sc -Action sidtype -Name $serviceName -Arguments @(, 'unrestricted') $null = Invoke-Sc -Action sidtype -Name $serviceName -Arguments @(, 'unrestricted')
$service.ServiceSidInfo | Assert-Equals -Expected ([Ansible.Service.ServiceSidInfo]::Unrestricted) $service.ServiceSidInfo | Assert-Equal -Expected ([Ansible.Service.ServiceSidInfo]::Unrestricted)
$service.ServiceSidInfo = [Ansible.Service.ServiceSidInfo]::Restricted $service.ServiceSidInfo = [Ansible.Service.ServiceSidInfo]::Restricted
$actual = Invoke-Sc -Action qsidtype -Name $serviceName $actual = Invoke-Sc -Action qsidtype -Name $serviceName
$service.ServiceSidInfo | Assert-Equals -Expected ([Ansible.Service.ServiceSidInfo]::Restricted) $service.ServiceSidInfo | Assert-Equal -Expected ([Ansible.Service.ServiceSidInfo]::Restricted)
$actual.SERVICE_SID_TYPE | Assert-Equals -Expected 'RESTRICTED' $actual.SERVICE_SID_TYPE | Assert-Equal -Expected 'RESTRICTED'
} }
"Modify RequiredPrivileges" = { "Modify RequiredPrivileges" = {
@ -693,25 +708,25 @@ $tests = [Ordered]@{
$service.RequiredPrivileges = @("SeBackupPrivilege", "SeTcbPrivilege") $service.RequiredPrivileges = @("SeBackupPrivilege", "SeTcbPrivilege")
$actual = Invoke-Sc -Action qprivs -Name $serviceName $actual = Invoke-Sc -Action qprivs -Name $serviceName
,$service.RequiredPrivileges | Assert-Equals -Expected @("SeBackupPrivilege", "SeTcbPrivilege") , $service.RequiredPrivileges | Assert-Equal -Expected @("SeBackupPrivilege", "SeTcbPrivilege")
,$actual.PRIVILEGES | Assert-Equals -Expected @("SeBackupPrivilege", "SeTcbPrivilege") , $actual.PRIVILEGES | Assert-Equal -Expected @("SeBackupPrivilege", "SeTcbPrivilege")
# Ensure setting to $null is the same as an empty array # Ensure setting to $null is the same as an empty array
$service.RequiredPrivileges = $null $service.RequiredPrivileges = $null
$actual = Invoke-Sc -Action qprivs -Name $serviceName $actual = Invoke-Sc -Action qprivs -Name $serviceName
,$service.RequiredPrivileges | Assert-Equals -Expected @() , $service.RequiredPrivileges | Assert-Equal -Expected @()
,$actual.PRIVILEGES | Assert-Equals -Expected @() , $actual.PRIVILEGES | Assert-Equal -Expected @()
$service.RequiredPrivileges = @("SeBackupPrivilege", "SeTcbPrivilege") $service.RequiredPrivileges = @("SeBackupPrivilege", "SeTcbPrivilege")
$service.RequiredPrivileges = @() $service.RequiredPrivileges = @()
$actual = Invoke-Sc -Action qprivs -Name $serviceName $actual = Invoke-Sc -Action qprivs -Name $serviceName
,$service.RequiredPrivileges | Assert-Equals -Expected @() , $service.RequiredPrivileges | Assert-Equal -Expected @()
,$actual.PRIVILEGES | Assert-Equals -Expected @() , $actual.PRIVILEGES | Assert-Equal -Expected @()
$null = Invoke-Sc -Action privs -Name $serviceName -Arguments @(, "SeCreateTokenPrivilege/SeRestorePrivilege") $null = Invoke-Sc -Action privs -Name $serviceName -Arguments @(, "SeCreateTokenPrivilege/SeRestorePrivilege")
,$service.RequiredPrivileges | Assert-Equals -Expected @("SeCreateTokenPrivilege", "SeRestorePrivilege") , $service.RequiredPrivileges | Assert-Equal -Expected @("SeCreateTokenPrivilege", "SeRestorePrivilege")
} }
"Modify PreShutdownTimeout" = { "Modify PreShutdownTimeout" = {
@ -722,7 +737,7 @@ $tests = [Ordered]@{
$actual = ( $actual = (
Get-ItemProperty -LiteralPath "HKLM:\SYSTEM\CurrentControlSet\Services\$serviceName" -Name PreshutdownTimeout Get-ItemProperty -LiteralPath "HKLM:\SYSTEM\CurrentControlSet\Services\$serviceName" -Name PreshutdownTimeout
).PreshutdownTimeout ).PreshutdownTimeout
$actual | Assert-Equals -Expected 60000 $actual | Assert-Equal -Expected 60000
} }
"Modify Triggers" = { "Modify Triggers" = {
@ -794,49 +809,49 @@ $tests = [Ordered]@{
$actual = Invoke-Sc -Action qtriggerinfo -Name $serviceName $actual = Invoke-Sc -Action qtriggerinfo -Name $serviceName
$actual.Triggers.Count | Assert-Equals -Expected 6 $actual.Triggers.Count | Assert-Equal -Expected 6
$actual.Triggers[0].Type | Assert-Equals -Expected 'DOMAIN JOINED STATUS' $actual.Triggers[0].Type | Assert-Equal -Expected 'DOMAIN JOINED STATUS'
$actual.Triggers[0].Action | Assert-Equals -Expected 'STOP SERVICE' $actual.Triggers[0].Action | Assert-Equal -Expected 'STOP SERVICE'
$actual.Triggers[0].SubType | Assert-Equals -Expected "$([Ansible.Service.Trigger]::DOMAIN_JOIN_GUID) [DOMAIN JOINED]" $actual.Triggers[0].SubType | Assert-Equal -Expected "$([Ansible.Service.Trigger]::DOMAIN_JOIN_GUID) [DOMAIN JOINED]"
$actual.Triggers[0].Data.Count | Assert-Equals -Expected 0 $actual.Triggers[0].Data.Count | Assert-Equal -Expected 0
$actual.Triggers[1].Type | Assert-Equals -Expected 'NETWORK EVENT' $actual.Triggers[1].Type | Assert-Equal -Expected 'NETWORK EVENT'
$actual.Triggers[1].Action | Assert-Equals -Expected 'START SERVICE' $actual.Triggers[1].Action | Assert-Equal -Expected 'START SERVICE'
$actual.Triggers[1].SubType | Assert-Equals -Expected "$([Ansible.Service.Trigger]::NAMED_PIPE_EVENT_GUID) [NAMED PIPE EVENT]" $actual.Triggers[1].SubType | Assert-Equal -Expected "$([Ansible.Service.Trigger]::NAMED_PIPE_EVENT_GUID) [NAMED PIPE EVENT]"
$actual.Triggers[1].Data.Count | Assert-Equals -Expected 1 $actual.Triggers[1].Data.Count | Assert-Equal -Expected 1
$actual.Triggers[1].Data[0] | Assert-Equals -Expected 'my named pipe' $actual.Triggers[1].Data[0] | Assert-Equal -Expected 'my named pipe'
$actual.Triggers[2].Type | Assert-Equals -Expected 'NETWORK EVENT' $actual.Triggers[2].Type | Assert-Equal -Expected 'NETWORK EVENT'
$actual.Triggers[2].Action | Assert-Equals -Expected 'START SERVICE' $actual.Triggers[2].Action | Assert-Equal -Expected 'START SERVICE'
$actual.Triggers[2].SubType | Assert-Equals -Expected "$([Ansible.Service.Trigger]::NAMED_PIPE_EVENT_GUID) [NAMED PIPE EVENT]" $actual.Triggers[2].SubType | Assert-Equal -Expected "$([Ansible.Service.Trigger]::NAMED_PIPE_EVENT_GUID) [NAMED PIPE EVENT]"
$actual.Triggers[2].Data.Count | Assert-Equals -Expected 1 $actual.Triggers[2].Data.Count | Assert-Equal -Expected 1
$actual.Triggers[2].Data[0] | Assert-Equals -Expected 'my named pipe 2' $actual.Triggers[2].Data[0] | Assert-Equal -Expected 'my named pipe 2'
$actual.Triggers[3].Type | Assert-Equals -Expected 'CUSTOM' $actual.Triggers[3].Type | Assert-Equal -Expected 'CUSTOM'
$actual.Triggers[3].Action | Assert-Equals -Expected 'START SERVICE' $actual.Triggers[3].Action | Assert-Equal -Expected 'START SERVICE'
$actual.Triggers[3].SubType | Assert-Equals -Expected '9bf04e57-05dc-4914-9ed9-84bf992db88c [ETW PROVIDER UUID]' $actual.Triggers[3].SubType | Assert-Equal -Expected '9bf04e57-05dc-4914-9ed9-84bf992db88c [ETW PROVIDER UUID]'
$actual.Triggers[3].Data.Count | Assert-Equals -Expected 2 $actual.Triggers[3].Data.Count | Assert-Equal -Expected 2
$actual.Triggers[3].Data[0] | Assert-Equals -Expected '01 02 03 04' $actual.Triggers[3].Data[0] | Assert-Equal -Expected '01 02 03 04'
$actual.Triggers[3].Data[1] | Assert-Equals -Expected '05 06 07 08 09' $actual.Triggers[3].Data[1] | Assert-Equal -Expected '05 06 07 08 09'
$actual.Triggers[4].Type | Assert-Equals -Expected 'CUSTOM' $actual.Triggers[4].Type | Assert-Equal -Expected 'CUSTOM'
$actual.Triggers[4].Action | Assert-Equals -Expected 'START SERVICE' $actual.Triggers[4].Action | Assert-Equal -Expected 'START SERVICE'
$actual.Triggers[4].SubType | Assert-Equals -Expected '9fbcfc7e-7581-4d46-913b-53bb15c80c51 [ETW PROVIDER UUID]' $actual.Triggers[4].SubType | Assert-Equal -Expected '9fbcfc7e-7581-4d46-913b-53bb15c80c51 [ETW PROVIDER UUID]'
$actual.Triggers[4].Data.Count | Assert-Equals -Expected 2 $actual.Triggers[4].Data.Count | Assert-Equal -Expected 2
$actual.Triggers[4].Data[0] | Assert-Equals -Expected "entry 1" $actual.Triggers[4].Data[0] | Assert-Equal -Expected "entry 1"
$actual.Triggers[4].Data[1] | Assert-Equals -Expected "entry 2" $actual.Triggers[4].Data[1] | Assert-Equal -Expected "entry 2"
$actual.Triggers[5].Type | Assert-Equals -Expected 'FIREWALL PORT EVENT' $actual.Triggers[5].Type | Assert-Equal -Expected 'FIREWALL PORT EVENT'
$actual.Triggers[5].Action | Assert-Equals -Expected 'STOP SERVICE' $actual.Triggers[5].Action | Assert-Equal -Expected 'STOP SERVICE'
$actual.Triggers[5].SubType | Assert-Equals -Expected "$([Ansible.Service.Trigger]::FIREWALL_PORT_CLOSE_GUID) [PORT CLOSE]" $actual.Triggers[5].SubType | Assert-Equal -Expected "$([Ansible.Service.Trigger]::FIREWALL_PORT_CLOSE_GUID) [PORT CLOSE]"
$actual.Triggers[5].Data.Count | Assert-Equals -Expected 1 $actual.Triggers[5].Data.Count | Assert-Equal -Expected 1
$actual.Triggers[5].Data[0] | Assert-Equals -Expected '1234;tcp;imagepath;servicename' $actual.Triggers[5].Data[0] | Assert-Equal -Expected '1234;tcp;imagepath;servicename'
# Remove trigger with $null # Remove trigger with $null
$service.Triggers = $null $service.Triggers = $null
$actual = Invoke-Sc -Action qtriggerinfo -Name $serviceName $actual = Invoke-Sc -Action qtriggerinfo -Name $serviceName
$actual.Triggers.Count | Assert-Equals -Expected 0 $actual.Triggers.Count | Assert-Equal -Expected 0
# Add a single trigger # Add a single trigger
$service.Triggers = [Ansible.Service.Trigger]@{ $service.Triggers = [Ansible.Service.Trigger]@{
@ -846,17 +861,17 @@ $tests = [Ordered]@{
} }
$actual = Invoke-Sc -Action qtriggerinfo -Name $serviceName $actual = Invoke-Sc -Action qtriggerinfo -Name $serviceName
$actual.Triggers.Count | Assert-Equals -Expected 1 $actual.Triggers.Count | Assert-Equal -Expected 1
$actual.Triggers[0].Type | Assert-Equals -Expected 'GROUP POLICY' $actual.Triggers[0].Type | Assert-Equal -Expected 'GROUP POLICY'
$actual.Triggers[0].Action | Assert-Equals -Expected 'START SERVICE' $actual.Triggers[0].Action | Assert-Equal -Expected 'START SERVICE'
$actual.Triggers[0].SubType | Assert-Equals -Expected "$([Ansible.Service.Trigger]::MACHINE_POLICY_PRESENT_GUID) [MACHINE POLICY PRESENT]" $actual.Triggers[0].SubType | Assert-Equal -Expected "$([Ansible.Service.Trigger]::MACHINE_POLICY_PRESENT_GUID) [MACHINE POLICY PRESENT]"
$actual.Triggers[0].Data.Count | Assert-Equals -Expected 0 $actual.Triggers[0].Data.Count | Assert-Equal -Expected 0
# Remove trigger with empty list # Remove trigger with empty list
$service.Triggers = @() $service.Triggers = @()
$actual = Invoke-Sc -Action qtriggerinfo -Name $serviceName $actual = Invoke-Sc -Action qtriggerinfo -Name $serviceName
$actual.Triggers.Count | Assert-Equals -Expected 0 $actual.Triggers.Count | Assert-Equal -Expected 0
# Add triggers through sc and check we get the values correctly # Add triggers through sc and check we get the values correctly
$null = Invoke-Sc -Action triggerinfo -Name $serviceName -Arguments @( $null = Invoke-Sc -Action triggerinfo -Name $serviceName -Arguments @(
@ -869,51 +884,51 @@ $tests = [Ordered]@{
) )
$actual = $service.Triggers $actual = $service.Triggers
$actual.Count | Assert-Equals -Expected 6 $actual.Count | Assert-Equal -Expected 6
$actual[0].Type | Assert-Equals -Expected ([Ansible.Service.TriggerType]::NetworkEndpoint) $actual[0].Type | Assert-Equal -Expected ([Ansible.Service.TriggerType]::NetworkEndpoint)
$actual[0].Action | Assert-Equals -Expected ([Ansible.Service.TriggerAction]::ServiceStart) $actual[0].Action | Assert-Equal -Expected ([Ansible.Service.TriggerAction]::ServiceStart)
$actual[0].SubType = [Guid][Ansible.Service.Trigger]::NAMED_PIPE_EVENT_GUID $actual[0].SubType = [Guid][Ansible.Service.Trigger]::NAMED_PIPE_EVENT_GUID
$actual[0].DataItems.Count | Assert-Equals -Expected 1 $actual[0].DataItems.Count | Assert-Equal -Expected 1
$actual[0].DataItems[0].Type | Assert-Equals -Expected ([Ansible.Service.TriggerDataType]::String) $actual[0].DataItems[0].Type | Assert-Equal -Expected ([Ansible.Service.TriggerDataType]::String)
$actual[0].DataItems[0].Data | Assert-Equals -Expected 'abc' $actual[0].DataItems[0].Data | Assert-Equal -Expected 'abc'
$actual[1].Type | Assert-Equals -Expected ([Ansible.Service.TriggerType]::NetworkEndpoint) $actual[1].Type | Assert-Equal -Expected ([Ansible.Service.TriggerType]::NetworkEndpoint)
$actual[1].Action | Assert-Equals -Expected ([Ansible.Service.TriggerAction]::ServiceStart) $actual[1].Action | Assert-Equal -Expected ([Ansible.Service.TriggerAction]::ServiceStart)
$actual[1].SubType = [Guid][Ansible.Service.Trigger]::NAMED_PIPE_EVENT_GUID $actual[1].SubType = [Guid][Ansible.Service.Trigger]::NAMED_PIPE_EVENT_GUID
$actual[1].DataItems.Count | Assert-Equals -Expected 1 $actual[1].DataItems.Count | Assert-Equal -Expected 1
$actual[1].DataItems[0].Type | Assert-Equals -Expected ([Ansible.Service.TriggerDataType]::String) $actual[1].DataItems[0].Type | Assert-Equal -Expected ([Ansible.Service.TriggerDataType]::String)
$actual[1].DataItems[0].Data | Assert-Equals -Expected 'def' $actual[1].DataItems[0].Data | Assert-Equal -Expected 'def'
$actual[2].Type | Assert-Equals -Expected ([Ansible.Service.TriggerType]::Custom) $actual[2].Type | Assert-Equal -Expected ([Ansible.Service.TriggerType]::Custom)
$actual[2].Action | Assert-Equals -Expected ([Ansible.Service.TriggerAction]::ServiceStart) $actual[2].Action | Assert-Equal -Expected ([Ansible.Service.TriggerAction]::ServiceStart)
$actual[2].SubType = [Guid]'d4497e12-ac36-4823-af61-92db0dbd4a76' $actual[2].SubType = [Guid]'d4497e12-ac36-4823-af61-92db0dbd4a76'
$actual[2].DataItems.Count | Assert-Equals -Expected 2 $actual[2].DataItems.Count | Assert-Equal -Expected 2
$actual[2].DataItems[0].Type | Assert-Equals -Expected ([Ansible.Service.TriggerDataType]::Binary) $actual[2].DataItems[0].Type | Assert-Equal -Expected ([Ansible.Service.TriggerDataType]::Binary)
,$actual[2].DataItems[0].Data | Assert-Equals -Expected ([byte[]]@(17, 34, 51, 68)) , $actual[2].DataItems[0].Data | Assert-Equal -Expected ([byte[]]@(17, 34, 51, 68))
$actual[2].DataItems[1].Type | Assert-Equals -Expected ([Ansible.Service.TriggerDataType]::Binary) $actual[2].DataItems[1].Type | Assert-Equal -Expected ([Ansible.Service.TriggerDataType]::Binary)
,$actual[2].DataItems[1].Data | Assert-Equals -Expected ([byte[]]@(170, 187, 204, 221)) , $actual[2].DataItems[1].Data | Assert-Equal -Expected ([byte[]]@(170, 187, 204, 221))
$actual[3].Type | Assert-Equals -Expected ([Ansible.Service.TriggerType]::Custom) $actual[3].Type | Assert-Equal -Expected ([Ansible.Service.TriggerType]::Custom)
$actual[3].Action | Assert-Equals -Expected ([Ansible.Service.TriggerAction]::ServiceStart) $actual[3].Action | Assert-Equal -Expected ([Ansible.Service.TriggerAction]::ServiceStart)
$actual[3].SubType = [Guid]'435a1742-22c5-4234-9db3-e32dafde695c' $actual[3].SubType = [Guid]'435a1742-22c5-4234-9db3-e32dafde695c'
$actual[3].DataItems.Count | Assert-Equals -Expected 2 $actual[3].DataItems.Count | Assert-Equal -Expected 2
$actual[3].DataItems[0].Type | Assert-Equals -Expected ([Ansible.Service.TriggerDataType]::String) $actual[3].DataItems[0].Type | Assert-Equal -Expected ([Ansible.Service.TriggerDataType]::String)
$actual[3].DataItems[0].Data | Assert-Equals -Expected '11223344' $actual[3].DataItems[0].Data | Assert-Equal -Expected '11223344'
$actual[3].DataItems[1].Type | Assert-Equals -Expected ([Ansible.Service.TriggerDataType]::String) $actual[3].DataItems[1].Type | Assert-Equal -Expected ([Ansible.Service.TriggerDataType]::String)
$actual[3].DataItems[1].Data | Assert-Equals -Expected 'aabbccdd' $actual[3].DataItems[1].Data | Assert-Equal -Expected 'aabbccdd'
$actual[4].Type | Assert-Equals -Expected ([Ansible.Service.TriggerType]::FirewallPortEvent) $actual[4].Type | Assert-Equal -Expected ([Ansible.Service.TriggerType]::FirewallPortEvent)
$actual[4].Action | Assert-Equals -Expected ([Ansible.Service.TriggerAction]::ServiceStop) $actual[4].Action | Assert-Equal -Expected ([Ansible.Service.TriggerAction]::ServiceStop)
$actual[4].SubType = [Guid][Ansible.Service.Trigger]::FIREWALL_PORT_CLOSE_GUID $actual[4].SubType = [Guid][Ansible.Service.Trigger]::FIREWALL_PORT_CLOSE_GUID
$actual[4].DataItems.Count | Assert-Equals -Expected 1 $actual[4].DataItems.Count | Assert-Equal -Expected 1
$actual[4].DataItems[0].Type | Assert-Equals -Expected ([Ansible.Service.TriggerDataType]::String) $actual[4].DataItems[0].Type | Assert-Equal -Expected ([Ansible.Service.TriggerDataType]::String)
,$actual[4].DataItems[0].Data | Assert-Equals -Expected @('1234', 'tcp', 'imagepath', 'servicename') , $actual[4].DataItems[0].Data | Assert-Equal -Expected @('1234', 'tcp', 'imagepath', 'servicename')
$actual[5].Type | Assert-Equals -Expected ([Ansible.Service.TriggerType]::IpAddressAvailability) $actual[5].Type | Assert-Equal -Expected ([Ansible.Service.TriggerType]::IpAddressAvailability)
$actual[5].Action | Assert-Equals -Expected ([Ansible.Service.TriggerAction]::ServiceStop) $actual[5].Action | Assert-Equal -Expected ([Ansible.Service.TriggerAction]::ServiceStop)
$actual[5].SubType = [Guid][Ansible.Service.Trigger]::NETWORK_MANAGER_LAST_IP_ADDRESS_REMOVAL_GUID $actual[5].SubType = [Guid][Ansible.Service.Trigger]::NETWORK_MANAGER_LAST_IP_ADDRESS_REMOVAL_GUID
$actual[5].DataItems.Count | Assert-Equals -Expected 0 $actual[5].DataItems.Count | Assert-Equal -Expected 0
} }
# Cannot test PreferredNode as we can't guarantee CI is set up with NUMA support. # Cannot test PreferredNode as we can't guarantee CI is set up with NUMA support.
@ -928,7 +943,8 @@ foreach ($testImpl in $tests.GetEnumerator()) {
try { try {
$test = $testImpl.Key $test = $testImpl.Key
&$testImpl.Value &$testImpl.Value
} finally { }
finally {
$null = Invoke-Sc -Action delete -Name $serviceName $null = Invoke-Sc -Action delete -Name $serviceName
} }
} }

@ -40,8 +40,7 @@ Try {
Exit-Json $result Exit-Json $result
} }
Finally Finally {
{
If ($fail_mode -contains "trailing_junk") { If ($fail_mode -contains "trailing_junk") {
Write-Output "trailing junk after module output" Write-Output "trailing junk after module output"
} }

@ -4,10 +4,13 @@
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
Function Assert-Equals($actual, $expected) { Function Assert-Equal($actual, $expected) {
if ($actual -cne $expected) { if ($actual -cne $expected) {
$call_stack = (Get-PSCallStack)[1] $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 Fail-Json -obj $result -message $error_msg
} }
} }
@ -19,21 +22,21 @@ $result = @{
#ConvertFrom-AnsibleJso #ConvertFrom-AnsibleJso
$input_json = '{"string":"string","float":3.1415926,"dict":{"string":"string","int":1},"list":["entry 1","entry 2"],"null":null,"int":1}' $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 $actual = ConvertFrom-AnsibleJson -InputObject $input_json
Assert-Equals -actual $actual.GetType() -expected ([Hashtable]) Assert-Equal -actual $actual.GetType() -expected ([Hashtable])
Assert-Equals -actual $actual.string.GetType() -expected ([String]) Assert-Equal -actual $actual.string.GetType() -expected ([String])
Assert-Equals -actual $actual.string -expected "string" Assert-Equal -actual $actual.string -expected "string"
Assert-Equals -actual $actual.int.GetType() -expected ([Int32]) Assert-Equal -actual $actual.int.GetType() -expected ([Int32])
Assert-Equals -actual $actual.int -expected 1 Assert-Equal -actual $actual.int -expected 1
Assert-Equals -actual $actual.null -expected $null Assert-Equal -actual $actual.null -expected $null
Assert-Equals -actual $actual.float.GetType() -expected ([Decimal]) Assert-Equal -actual $actual.float.GetType() -expected ([Decimal])
Assert-Equals -actual $actual.float -expected 3.1415926 Assert-Equal -actual $actual.float -expected 3.1415926
Assert-Equals -actual $actual.list.GetType() -expected ([Object[]]) Assert-Equal -actual $actual.list.GetType() -expected ([Object[]])
Assert-Equals -actual $actual.list.Count -expected 2 Assert-Equal -actual $actual.list.Count -expected 2
Assert-Equals -actual $actual.list[0] -expected "entry 1" Assert-Equal -actual $actual.list[0] -expected "entry 1"
Assert-Equals -actual $actual.list[1] -expected "entry 2" Assert-Equal -actual $actual.list[1] -expected "entry 2"
Assert-Equals -actual $actual.GetType() -expected ([Hashtable]) Assert-Equal -actual $actual.GetType() -expected ([Hashtable])
Assert-Equals -actual $actual.dict.string -expected "string" Assert-Equal -actual $actual.dict.string -expected "string"
Assert-Equals -actual $actual.dict.int -expected 1 Assert-Equal -actual $actual.dict.int -expected 1
$result.msg = "good" $result.msg = "good"
Exit-Json -obj $result Exit-Json -obj $result

@ -31,24 +31,32 @@ Function Test-ThrowException {
if ($data -eq "normal") { if ($data -eq "normal") {
Exit-Json -obj $result Exit-Json -obj $result
} elseif ($data -eq "fail") { }
elseif ($data -eq "fail") {
Fail-Json -obj $result -message "fail message" Fail-Json -obj $result -message "fail message"
} elseif ($data -eq "throw") { }
elseif ($data -eq "throw") {
throw [ArgumentException]"module is thrown" throw [ArgumentException]"module is thrown"
} elseif ($data -eq "error") { }
elseif ($data -eq "error") {
Write-Error -Message $data Write-Error -Message $data
} elseif ($data -eq "cmdlet_error") { }
elseif ($data -eq "cmdlet_error") {
Get-Item -Path "fake:\path" Get-Item -Path "fake:\path"
} elseif ($data -eq "dotnet_exception") { }
elseif ($data -eq "dotnet_exception") {
[System.IO.Path]::GetFullPath($null) [System.IO.Path]::GetFullPath($null)
} elseif ($data -eq "function_throw") { }
elseif ($data -eq "function_throw") {
Test-ThrowException 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 # 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 # don't use the RC to validate if the module failed
&cmd.exe /c exit 2 &cmd.exe /c exit 2
Exit-Json -obj $result Exit-Json -obj $result
} elseif ($data -eq "proc_exit_fail") { }
elseif ($data -eq "proc_exit_fail") {
&cmd.exe /c exit 2 &cmd.exe /c exit 2
Fail-Json -obj $result -message "proc_exit_fail" Fail-Json -obj $result -message "proc_exit_fail"
} }

@ -1,3 +1,3 @@
# Test script to create a file. # 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 # Test script to make sure the Ansible script module works when arguments are
# passed to the script. # passed to the script.
foreach ($i in $args) foreach ($i in $args) {
{
Write-Host $i; Write-Host $i;
} }

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

@ -26,7 +26,8 @@ $new_lines = [System.Collections.ArrayList]@()
foreach ($host_line in $hosts_file_lines) { foreach ($host_line in $hosts_file_lines) {
if ($host_line -in $hosts_entries) { if ($host_line -in $hosts_entries) {
$changed = $true $changed = $true
} else { }
else {
$new_lines += $host_line $new_lines += $host_line
} }
} }

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

@ -4,12 +4,6 @@
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$WarningPreference = "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 $LiteralPathRule = Import-Module -Name PSSA-PSCustomUseLiteralPath -PassThru
$LiteralPathRulePath = Join-Path -Path $LiteralPathRule.ModuleBase -ChildPath $LiteralPathRule.RootModule $LiteralPathRulePath = Join-Path -Path $LiteralPathRule.ModuleBase -ChildPath $LiteralPathRule.RootModule
@ -19,7 +13,8 @@ $PSSAParams = @{
Setting = (Join-Path -Path $PSScriptRoot -ChildPath "settings.psd1") Setting = (Join-Path -Path $PSScriptRoot -ChildPath "settings.psd1")
} }
$Results = @(ForEach ($Path in $Args) { $Results = @(
ForEach ($Path in $Args) {
$Retries = 3 $Retries = 3
Do { Do {
@ -34,7 +29,8 @@ $Results = @(ForEach ($Path in $Args) {
} }
} }
Until ($Retries -le 0) Until ($Retries -le 0)
}) }
)
# Since pwsh 7.1 results that exceed depth will produce a warning which fails the process. # Since pwsh 7.1 results that exceed depth will produce a warning which fails the process.
# Ignore warnings only for this step. # Ignore warnings only for this step.

@ -1,4 +1,40 @@
@{ @{
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 = @( ExcludeRules = @(
'PSUseOutputTypeCorrectly', 'PSUseOutputTypeCorrectly',
'PSUseShouldProcessForStateChangingFunctions', 'PSUseShouldProcessForStateChangingFunctions',
@ -8,6 +44,9 @@
'PSAvoidUsingUserNameAndPassWordParams', 'PSAvoidUsingUserNameAndPassWordParams',
# We send the module as a base64 encoded string and a BOM will cause # We send the module as a base64 encoded string and a BOM will cause
# issues here # 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'
) )
} }

@ -23,7 +23,8 @@ Function Resolve-CircularReference {
$value = $Hash[$key] $value = $Hash[$key]
if ($value -is [System.Collections.IDictionary]) { if ($value -is [System.Collections.IDictionary]) {
Resolve-CircularReference -Hash $value 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) { $values = @(foreach ($v in $value) {
if ($v -is [System.Collections.IDictionary]) { if ($v -is [System.Collections.IDictionary]) {
Resolve-CircularReference -Hash $v Resolve-CircularReference -Hash $v
@ -31,14 +32,17 @@ Function Resolve-CircularReference {
, $v , $v
}) })
$Hash[$key] = $values $Hash[$key] = $values
} elseif ($value -is [DateTime]) { }
elseif ($value -is [DateTime]) {
$Hash[$key] = $value.ToString("yyyy-MM-dd") $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 # Type can be set to a delegate function which defines it's own type. For the documentation we just
# reflection that as raw # reflection that as raw
if ($key -eq 'type') { if ($key -eq 'type') {
$Hash[$key] = 'raw' $Hash[$key] = 'raw'
} else { }
else {
$Hash[$key] = $value.ToString() # Shouldn't ever happen but just in case. $Hash[$key] = $value.ToString() # Shouldn't ever happen but just in case.
} }
} }

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

@ -66,28 +66,24 @@ Param (
[switch]$EnableCredSSP [switch]$EnableCredSSP
) )
Function Write-Log Function Write-ProgressLog {
{
$Message = $args[0] $Message = $args[0]
Write-EventLog -LogName Application -Source $EventSource -EntryType Information -EventId 1 -Message $Message Write-EventLog -LogName Application -Source $EventSource -EntryType Information -EventId 1 -Message $Message
} }
Function Write-VerboseLog Function Write-VerboseLog {
{
$Message = $args[0] $Message = $args[0]
Write-Verbose $Message Write-Verbose $Message
Write-Log $Message Write-ProgressLog $Message
} }
Function Write-HostLog Function Write-HostLog {
{
$Message = $args[0] $Message = $args[0]
Write-Output $Message Write-Output $Message
Write-Log $Message Write-ProgressLog $Message
} }
Function New-LegacySelfSignedCert Function New-LegacySelfSignedCert {
{
Param ( Param (
[string]$SubjectName, [string]$SubjectName,
[int]$ValidDays = 1095 [int]$ValidDays = 1095
@ -129,8 +125,7 @@ Function New-LegacySelfSignedCert
$AlternativeName += $hostFQDN $AlternativeName += $hostFQDN
$IAlternativeNames = New-Object -ComObject X509Enrollment.CAlternativeNames $IAlternativeNames = New-Object -ComObject X509Enrollment.CAlternativeNames
foreach ($AN in $AlternativeName) foreach ($AN in $AlternativeName) {
{
$AltName = New-Object -ComObject X509Enrollment.CAlternativeName $AltName = New-Object -ComObject X509Enrollment.CAlternativeName
$AltName.InitializeFromString(0x3, $AN) $AltName.InitializeFromString(0x3, $AN)
$IAlternativeNames.Add($AltName) $IAlternativeNames.Add($AltName)
@ -162,8 +157,7 @@ Function New-LegacySelfSignedCert
return $parsed_cert.Thumbprint return $parsed_cert.Thumbprint
} }
Function Enable-GlobalHttpFirewallAccess Function Enable-GlobalHttpFirewallAccess {
{
Write-Verbose "Forcing global HTTP firewall access" Write-Verbose "Forcing global HTTP firewall access"
# this is a fairly naive implementation; could be more sophisticated about rule matching/collapsing # this is a fairly naive implementation; could be more sophisticated about rule matching/collapsing
$fw = New-Object -ComObject HNetCfg.FWPolicy2 $fw = New-Object -ComObject HNetCfg.FWPolicy2
@ -217,8 +211,7 @@ Function Enable-GlobalHttpFirewallAccess
} }
# Setup error handling. # Setup error handling.
Trap Trap {
{
$_ $_
Exit 1 Exit 1
} }
@ -232,65 +225,57 @@ $myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal($myWin
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator $adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
# Check to see if we are currently running "as 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 "ERROR: You need elevated Administrator privileges in order to run this script."
Write-Output " Start Windows PowerShell by using the Run as Administrator option." Write-Output " Start Windows PowerShell by using the Run as Administrator option."
Exit 2 Exit 2
} }
$EventSource = $MyInvocation.MyCommand.Name $EventSource = $MyInvocation.MyCommand.Name
If (-Not $EventSource) If (-Not $EventSource) {
{
$EventSource = "Powershell CLI" $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 New-EventLog -LogName Application -Source $EventSource
} }
# Detect PowerShell version. # Detect PowerShell version.
If ($PSVersionTable.PSVersion.Major -lt 3) If ($PSVersionTable.PSVersion.Major -lt 3) {
{ Write-ProgressLog "PowerShell version 3 or higher is required."
Write-Log "PowerShell version 3 or higher is required."
Throw "PowerShell version 3 or higher is required." Throw "PowerShell version 3 or higher is required."
} }
# Find and start the WinRM service. # Find and start the WinRM service.
Write-Verbose "Verifying WinRM service." Write-Verbose "Verifying WinRM service."
If (!(Get-Service "WinRM")) If (!(Get-Service "WinRM")) {
{ Write-ProgressLog "Unable to find the WinRM service."
Write-Log "Unable to find the WinRM service."
Throw "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." Write-Verbose "Setting WinRM service to start automatically on boot."
Set-Service -Name "WinRM" -StartupType Automatic 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." Write-Verbose "Starting WinRM service."
Start-Service -Name "WinRM" -ErrorAction Stop 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. # 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) { If ($SkipNetworkProfileCheck) {
Write-Verbose "Enabling PS Remoting without checking Network profile." Write-Verbose "Enabling PS Remoting without checking Network profile."
Enable-PSRemoting -SkipNetworkProfileCheck -Force -ErrorAction Stop 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 { Else {
Write-Verbose "Enabling PS Remoting." Write-Verbose "Enabling PS Remoting."
Enable-PSRemoting -Force -ErrorAction Stop Enable-PSRemoting -Force -ErrorAction Stop
Write-Log "Enabled PS Remoting." Write-ProgressLog "Enabled PS Remoting."
} }
} }
Else Else {
{
Write-Verbose "PS Remoting is already enabled." Write-Verbose "PS Remoting is already enabled."
} }
@ -310,8 +295,7 @@ if ($token_value -ne 1) {
# Make sure there is a SSL listener. # Make sure there is a SSL listener.
$listeners = Get-ChildItem WSMan:\localhost\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 # We cannot use New-SelfSignedCertificate on 2012R2 and earlier
$thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays $thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays
Write-HostLog "Self-signed SSL certificate generated; thumbprint: $thumbprint" 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." Write-Verbose "Enabling SSL listener."
New-WSManInstance -ResourceURI 'winrm/config/Listener' -SelectorSet $selectorset -ValueSet $valueset 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." Write-Verbose "SSL listener is already active."
# Force a new SSL cert on Listener if the $ForceNewSSLCert # Force a new SSL cert on Listener if the $ForceNewSSLCert
If ($ForceNewSSLCert) If ($ForceNewSSLCert) {
{
# We cannot use New-SelfSignedCertificate on 2012R2 and earlier # We cannot use New-SelfSignedCertificate on 2012R2 and earlier
$thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays $thumbprint = New-LegacySelfSignedCert -SubjectName $SubjectName -ValidDays $CertValidityDays
@ -363,43 +345,35 @@ Else
# Check for basic authentication. # 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 ($DisableBasicAuth) {
{ If (($basicAuthSetting.Value) -eq $true) {
If (($basicAuthSetting.Value) -eq $true)
{
Write-Verbose "Disabling basic auth support." Write-Verbose "Disabling basic auth support."
Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $false 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." Write-Verbose "Basic auth is already disabled."
} }
} }
Else Else {
{ If (($basicAuthSetting.Value) -eq $false) {
If (($basicAuthSetting.Value) -eq $false)
{
Write-Verbose "Enabling basic auth support." Write-Verbose "Enabling basic auth support."
Set-Item -Path "WSMan:\localhost\Service\Auth\Basic" -Value $true 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." Write-Verbose "Basic auth is already enabled."
} }
} }
# If EnableCredSSP if set to true # If EnableCredSSP if set to true
If ($EnableCredSSP) If ($EnableCredSSP) {
{
# Check for CredSSP authentication # Check for CredSSP authentication
$credsspAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object { $_.Name -eq "CredSSP" } $credsspAuthSetting = Get-ChildItem WSMan:\localhost\Service\Auth | Where-Object { $_.Name -eq "CredSSP" }
If (($credsspAuthSetting.Value) -eq $false) If (($credsspAuthSetting.Value) -eq $false) {
{
Write-Verbose "Enabling CredSSP auth support." Write-Verbose "Enabling CredSSP auth support."
Enable-WSManCredSSP -role server -Force 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. # Configure firewall to allow WinRM HTTPS connections.
$fwtest1 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS" $fwtest1 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS"
$fwtest2 = netsh advfirewall firewall show rule name="Allow WinRM HTTPS" profile=any $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." 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 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." Write-Verbose "Updating firewall rule to allow WinRM HTTPS for any profile."
netsh advfirewall firewall set rule name="Allow WinRM HTTPS" new profile=any 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." Write-Verbose "Firewall rule already exists to allow WinRM HTTPS."
} }
# Test a remoting connection to localhost, which should work. # 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 $httpsOptions = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
$httpsResult = New-PSSession -UseSSL -ComputerName "localhost" -SessionOption $httpsOptions -ErrorVariable httpsError -ErrorAction SilentlyContinue $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" Write-Verbose "HTTP: Enabled | HTTPS: Enabled"
} }
ElseIf ($httpsResult -and !$httpResult) ElseIf ($httpsResult -and !$httpResult) {
{
Write-Verbose "HTTP: Disabled | HTTPS: Enabled" Write-Verbose "HTTP: Disabled | HTTPS: Enabled"
} }
ElseIf ($httpResult -and !$httpsResult) ElseIf ($httpResult -and !$httpsResult) {
{
Write-Verbose "HTTP: Enabled | HTTPS: Disabled" Write-Verbose "HTTP: Enabled | HTTPS: Disabled"
} }
Else Else {
{ Write-ProgressLog "Unable to establish an HTTP or HTTPS remoting session."
Write-Log "Unable to establish an HTTP or HTTPS remoting session."
Throw "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." 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_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_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_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.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_removes_file.ps1 pslint:PSCustomUseLiteralPath
test/integration/targets/win_script/files/test_script_with_args.ps1 pslint:PSAvoidUsingWriteHost # Keep 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/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 pep8:E231
test/support/network-integration/collections/ansible_collections/vyos/vyos/plugins/modules/vyos_command.py pylint:disallowed-name 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/async_status.ps1 pslint!skip
test/support/windows-integration/plugins/modules/setup.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_copy.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_dsc.ps1 pslint!skip test/support/windows-integration/plugins/modules/win_file.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_feature.ps1 pslint!skip test/support/windows-integration/plugins/modules/win_get_url.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_find.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_lineinfile.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_regedit.ps1 pslint!skip
test/support/windows-integration/plugins/modules/win_shell.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_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/executor/test_play_iterator.py pylint:disallowed-name
test/units/modules/test_apt.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 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