diff --git a/changelogs/fragments/powershell_use_set_tmpdir.yaml b/changelogs/fragments/powershell_use_set_tmpdir.yaml new file mode 100644 index 0000000000..d125cb9c41 --- /dev/null +++ b/changelogs/fragments/powershell_use_set_tmpdir.yaml @@ -0,0 +1,2 @@ +bugfixes: +- powershell - use the tmpdir set by `remote_tmp` for become/async tasks instead of the generic $env:TEMP - https://github.com/ansible/ansible/pull/40210 diff --git a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 index ce94827dbc..88e0cd095d 100644 --- a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 +++ b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.CommandUtil.psm1 @@ -335,7 +335,25 @@ Function Load-CommandUtils { # [Ansible.CommandUtil]::RunCommand(string lpApplicationName, string lpCommandLine, string lpCurrentDirectory, string stdinInput, string environmentBlock) # # there are also numerous P/Invoke methods that can be called if you are feeling adventurous + + # FUTURE: find a better way to get the _ansible_remote_tmp variable + $original_tmp = $env:TMP + $original_temp = $env:TEMP + + $remote_tmp = $original_tmp + $module_params = Get-Variable -Name complex_args -ErrorAction SilentlyContinue + if ($module_params) { + if ($module_params.Value.ContainsKey("_ansible_remote_tmp") ) { + $remote_tmp = $module_params.Value["_ansible_remote_tmp"] + $remote_tmp = [System.Environment]::ExpandEnvironmentVariables($remote_tmp) + } + } + + $env:TMP = $remote_tmp + $env:TEMP = $remote_tmp Add-Type -TypeDefinition $process_util + $env:TMP = $original_tmp + $env:TEMP = $original_temp } Function Get-ExecutablePath($executable, $directory) { diff --git a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1 b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1 index 6467bec22c..fcef6733a4 100644 --- a/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1 +++ b/lib/ansible/module_utils/powershell/Ansible.ModuleUtils.LinkUtil.psm1 @@ -2,7 +2,7 @@ # Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause) Function Load-LinkUtils() { - Add-Type -TypeDefinition @' + $link_util = @' using Microsoft.Win32.SafeHandles; using System; using System.Collections.Generic; @@ -464,6 +464,25 @@ namespace Ansible } '@ + # FUTURE: find a better way to get the _ansible_remote_tmp variable + $original_tmp = $env:TMP + $original_temp = $env:TEMP + + $remote_tmp = $original_tmp + $module_params = Get-Variable -Name complex_args -ErrorAction SilentlyContinue + if ($module_params) { + if ($module_params.Value.ContainsKey("_ansible_remote_tmp") ) { + $remote_tmp = $module_params.Value["_ansible_remote_tmp"] + $remote_tmp = [System.Environment]::ExpandEnvironmentVariables($remote_tmp) + } + } + + $env:TMP = $remote_tmp + $env:TEMP = $remote_tmp + Add-Type -TypeDefinition $link_util + $env:TMP = $original_tmp + $env:TEMP = $original_temp + [Ansible.LinkUtil]::EnablePrivilege("SeBackupPrivilege") } diff --git a/lib/ansible/modules/windows/async_status.ps1 b/lib/ansible/modules/windows/async_status.ps1 index efde748fb9..611a587823 100644 --- a/lib/ansible/modules/windows/async_status.ps1 +++ b/lib/ansible/modules/windows/async_status.ps1 @@ -22,9 +22,9 @@ $results = @{changed=$false} $parsed_args = Parse-Args $args $jid = Get-AnsibleParam $parsed_args "jid" -failifempty $true -resultobj $results $mode = Get-AnsibleParam $parsed_args "mode" -Default "status" -ValidateSet "status","cleanup" +$_remote_tmp = Get-AnsibleParam $parsed_args "_ansible_remote_tmp" -type "path" -default $env:TMP -# setup logging directory -$log_path = [System.IO.Path]::Combine($env:LOCALAPPDATA, ".ansible_async", $jid) +$log_path = [System.IO.Path]::Combine($_remote_tmp, ".ansible_async", $jid) If(-not $(Test-Path $log_path)) { diff --git a/lib/ansible/modules/windows/win_acl.ps1 b/lib/ansible/modules/windows/win_acl.ps1 index dc27132b8a..1077559fe5 100644 --- a/lib/ansible/modules/windows/win_acl.ps1 +++ b/lib/ansible/modules/windows/win_acl.ps1 @@ -120,7 +120,16 @@ namespace Ansible { } "@ +$params = Parse-Args $args; +$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP + +$original_tmp = $env:TMP +$original_temp = $env:TEMP +$env:TMP = $_remote_tmp +$env:TEMP = $_remote_tmp add-type $AdjustTokenPrivileges +$env:TMP = $original_tmp +$env:TEMP = $original_temp Function SetPrivilegeTokens() { # Set privilege tokens only if admin. @@ -143,7 +152,7 @@ Function SetPrivilegeTokens() { } -$params = Parse-Args $args; + $result = @{ changed = $false diff --git a/lib/ansible/modules/windows/win_defrag.ps1 b/lib/ansible/modules/windows/win_defrag.ps1 index d272d7f11c..70df9c88df 100644 --- a/lib/ansible/modules/windows/win_defrag.ps1 +++ b/lib/ansible/modules/windows/win_defrag.ps1 @@ -5,6 +5,8 @@ # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) #Requires -Module Ansible.ModuleUtils.Legacy +#Requires -Module Ansible.ModuleUtils.ArgvParser +#Requires -Module Ansible.ModuleUtils.CommandUtil $ErrorActionPreference = "Stop" @@ -27,120 +29,60 @@ if (-not (Get-Command -Name $executable -ErrorAction SilentlyContinue)) { Fail-Json $result "Command '$executable' not found in $env:PATH." } -$util_def = @' -using System; -using System.ComponentModel; -using System.IO; -using System.Threading; - -namespace Ansible.Command { - - public static class NativeUtil { - - public static void GetProcessOutput(StreamReader stdoutStream, StreamReader stderrStream, out string stdout, out string stderr) { - var sowait = new EventWaitHandle(false, EventResetMode.ManualReset); - var sewait = new EventWaitHandle(false, EventResetMode.ManualReset); - - string so = null, se = null; - - ThreadPool.QueueUserWorkItem((s)=> { - so = stdoutStream.ReadToEnd(); - sowait.Set(); - }); - - ThreadPool.QueueUserWorkItem((s) => { - se = stderrStream.ReadToEnd(); - sewait.Set(); - }); - - foreach(var wh in new WaitHandle[] { sowait, sewait }) - wh.WaitOne(); - - stdout = so; - stderr = se; - } - } -} -'@ - -Add-Type -TypeDefinition $util_def - -$arguments = "" +$arguments = @() if ($include_volumes) { foreach ($volume in $include_volumes) { if ($volume.Length -eq 1) { - $arguments += " $($volume):" + $arguments += "$($volume):" } else { - $arguments += " $volume" + $arguments += $volume } } } else { - $arguments = " /C" + $arguments += "/C" } if ($exclude_volumes) { - $arguments += " /E" + $arguments += "/E" foreach ($volume in $exclude_volumes) { if ($volume.Length -eq 1) { - $arguments += " $($volume):" + $arguments += "$($volume):" } else { - $arguments += " $volume" + $arguments += $volume } } } if ($check_mode) { - $arguments += " /A" + $arguments += "/A" } elseif ($freespace_consolidation) { - $arguments += " /X" + $arguments += "/X" } if ($priority -eq "normal") { - $arguments += " /H" + $arguments += "/H" } if ($parallel) { - $arguments += " /M" + $arguments += "/M" } -$arguments += " /V" +$arguments += "/V" -$proc = New-Object System.Diagnostics.Process -$psi = $proc.StartInfo -$psi.FileName = $executable -$psi.Arguments = $arguments -$psi.RedirectStandardOutput = $true -$psi.RedirectStandardError = $true -$psi.UseShellExecute = $false - -$result.cmd = "$executable$arguments" +$argument_string = Argv-ToString -arguments $arguments $start_datetime = [DateTime]::UtcNow +$result.cmd = "$executable $argument_string" -Try { - $proc.Start() | Out-Null # will always return $true for non shell-exec cases -} Catch [System.ComponentModel.Win32Exception] { - # fail nicely for "normal" error conditions - # FUTURE: this probably won't work on Nano Server - $excep = $_ - $result.rc = $excep.Exception.NativeErrorCode - Fail-Json $result $excep.Exception.Message -} - -$stdout = $stderr = [string] $null - -[Ansible.Command.NativeUtil]::GetProcessOutput($proc.StandardOutput, $proc.StandardError, [ref] $stdout, [ref] $stderr) | Out-Null - -$result.stdout = $stdout -$result.stderr = $stderr - -$proc.WaitForExit() | Out-Null - -$result.rc = $proc.ExitCode +$command_result = Run-Command -command "$executable $argument_string" $end_datetime = [DateTime]::UtcNow +$result.stdout = $command_result.stdout +$result.stderr = $command_result.stderr +$result.rc = $command_result.rc + $result.start = $start_datetime.ToString("yyyy-MM-dd hh:mm:ss.ffffff") $result.end = $end_datetime.ToString("yyyy-MM-dd hh:mm:ss.ffffff") $result.delta = $($end_datetime - $start_datetime).ToString("h\:mm\:ss\.ffffff") diff --git a/lib/ansible/modules/windows/win_file.ps1 b/lib/ansible/modules/windows/win_file.ps1 index f6441b095c..318396e714 100644 --- a/lib/ansible/modules/windows/win_file.ps1 +++ b/lib/ansible/modules/windows/win_file.ps1 @@ -11,6 +11,7 @@ $ErrorActionPreference = "Stop" $params = Parse-Args $args -supports_check_mode $true $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -default $false +$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP $path = Get-AnsibleParam -obj $params -name "path" -type "path" -failifempty $true -aliases "dest","name" $state = Get-AnsibleParam -obj $params -name "state" -type "str" -validateset "absent","directory","file","touch" @@ -51,7 +52,13 @@ namespace Ansible.Command { } } "@ +$original_tmp = $env:TMP +$original_temp = $env:TEMP +$env:TMP = $_remote_tmp +$env:TEMP = $_remote_tmp Add-Type -TypeDefinition $symlink_util +$env:TMP = $original_tmp +$env:TEMP = $original_temp # Used to delete directories and files with logic on handling symbolic links function Remove-File($file, $checkmode) { diff --git a/lib/ansible/modules/windows/win_find.ps1 b/lib/ansible/modules/windows/win_find.ps1 index 36fb58fb3b..87c45fed95 100644 --- a/lib/ansible/modules/windows/win_find.ps1 +++ b/lib/ansible/modules/windows/win_find.ps1 @@ -9,6 +9,7 @@ $ErrorActionPreference = "Stop" $params = Parse-Args -arguments $args -supports_check_mode $true +$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP $paths = Get-AnsibleParam -obj $params -name 'paths' -failifempty $true @@ -70,7 +71,13 @@ namespace Ansible.Command { } } "@ +$original_tmp = $env:TMP +$original_temp = $env:TEMP +$env:TMP = $_remote_tmp +$env:TEMP = $_remote_tmp Add-Type -TypeDefinition $symlink_util +$env:TMP = $original_tmp +$env:TEMP = $original_temp Function Assert-Age($info) { $valid_match = $true diff --git a/lib/ansible/modules/windows/win_get_url.ps1 b/lib/ansible/modules/windows/win_get_url.ps1 index 434371f125..fd88e8be29 100644 --- a/lib/ansible/modules/windows/win_get_url.ps1 +++ b/lib/ansible/modules/windows/win_get_url.ps1 @@ -9,6 +9,9 @@ $ErrorActionPreference = 'Stop' +$params = Parse-Args $args -supports_check_mode $true +$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false +$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP $webclient_util = @" using System.Net; @@ -26,7 +29,13 @@ $webclient_util = @" } } "@ +$original_tmp = $env:TMP +$original_temp = $env:TEMP +$env:TMP = $_remote_tmp +$env:TEMP = $_remote_tmp Add-Type -TypeDefinition $webclient_util +$env:TMP = $original_tmp +$env:TEMP = $original_temp Function CheckModified-File($url, $dest, $headers, $credentials, $timeout, $use_proxy, $proxy) { @@ -138,10 +147,6 @@ Function Download-File($result, $url, $dest, $headers, $credentials, $timeout, $ $result.dest = $dest } - -$params = Parse-Args $args -supports_check_mode $true -$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false - $url = Get-AnsibleParam -obj $params -name "url" -type "str" -failifempty $true $dest = Get-AnsibleParam -obj $params -name "dest" -type "path" -failifempty $true $timeout = Get-AnsibleParam -obj $params -name "timeout" -type "int" -default 10 diff --git a/lib/ansible/modules/windows/win_psexec.ps1 b/lib/ansible/modules/windows/win_psexec.ps1 index d491e1f058..1dc43c4a43 100644 --- a/lib/ansible/modules/windows/win_psexec.ps1 +++ b/lib/ansible/modules/windows/win_psexec.ps1 @@ -1,10 +1,11 @@ #!powershell -# -*- coding: utf-8 -*- # Copyright: (c) 2017, Dag Wieers (@dagwieers) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) #Requires -Module Ansible.ModuleUtils.Legacy +#Requires -Module Ansible.ModuleUtils.ArgvParser +#Requires -Module Ansible.ModuleUtils.CommandUtil # See also: https://technet.microsoft.com/en-us/sysinternals/pxexec.aspx @@ -35,153 +36,99 @@ If (-Not (Get-Command $executable -ErrorAction SilentlyContinue)) { Fail-Json $result "Executable '$executable' was not found." } -$util_def = @' -using System; -using System.ComponentModel; -using System.IO; -using System.Threading; - -namespace Ansible.Command { - - public static class NativeUtil { - - public static void GetProcessOutput(StreamReader stdoutStream, StreamReader stderrStream, out string stdout, out string stderr) { - var sowait = new EventWaitHandle(false, EventResetMode.ManualReset); - var sewait = new EventWaitHandle(false, EventResetMode.ManualReset); - - string so = null, se = null; - - ThreadPool.QueueUserWorkItem((s)=> { - so = stdoutStream.ReadToEnd(); - sowait.Set(); - }); - - ThreadPool.QueueUserWorkItem((s) => { - se = stderrStream.ReadToEnd(); - sewait.Set(); - }); - - foreach(var wh in new WaitHandle[] { sowait, sewait }) - wh.WaitOne(); - - stdout = so; - stderr = se; - } - } -} -'@ - -Add-Type -TypeDefinition $util_def - -$arguments = "" +$arguments = @() If ($nobanner -eq $true) { - $arguments += " -nobanner" + $arguments += "-nobanner" } # Support running on local system if no hostname is specified If ($hostnames) { - $arguments += " \\" + $($hostnames | sort -Unique) -join ',' + $hostname_argument = ($hostnames | sort -Unique) -join ',' + $arguments += "\\$hostname_argument" } # Username is optional If ($username -ne $null) { - $arguments += " -u `"$username`"" + $arguments += "-u" + $arguments += $username } # Password is optional If ($password -ne $null) { - $arguments += " -p `"$password`"" + $arguments += "-p" + $arguments += $password } If ($chdir -ne $null) { - $arguments += " -w `"$chdir`"" + $arguments += "-w" + $arguments += $chdir } If ($wait -eq $false) { - $arguments += " -d" + $arguments += "-d" } If ($noprofile -eq $true) { - $arguments += " -e" + $arguments += "-e" } If ($elevated -eq $true) { - $arguments += " -h" + $arguments += "-h" } If ($system -eq $true) { - $arguments += " -s" + $arguments += "-s" } If ($interactive -eq $true) { - $arguments += " -i" + $arguments += "-i" } If ($limited -eq $true) { - $arguments += " -l" + $arguments += "-l" } If ($priority -ne $null) { - $arguments += " -$priority" + $arguments += "-$priority" } If ($timeout -ne $null) { - $arguments += " -n $timeout" + $arguments += "-n" + $arguments += $timeout } # Add additional advanced options If ($extra_opts) { ForEach ($opt in $extra_opts) { - $arguments += " $opt" + $arguments += $opt } } -$arguments += " -accepteula" +$arguments += "-accepteula" +$arguments += $command -$proc = New-Object System.Diagnostics.Process -$psi = $proc.StartInfo -$psi.FileName = $executable -$psi.Arguments = "$arguments $command" -$psi.RedirectStandardOutput = $true -$psi.RedirectStandardError = $true -$psi.UseShellExecute = $false - -$result.psexec_command = "$executable$arguments $command" +$argument_string = Argv-ToString -arguments $arguments $start_datetime = [DateTime]::UtcNow +$result.psexec_command = "$executable $argument_string" -Try { - $proc.Start() | Out-Null # will always return $true for non shell-exec cases -} Catch [System.ComponentModel.Win32Exception] { - # fail nicely for "normal" error conditions - # FUTURE: this probably won't work on Nano Server - $excep = $_ - $result.rc = $excep.Exception.NativeErrorCode - Fail-Json $result $excep.Exception.Message -} - -$stdout = $stderr = [string] $null - -[Ansible.Command.NativeUtil]::GetProcessOutput($proc.StandardOutput, $proc.StandardError, [ref] $stdout, [ref] $stderr) | Out-Null - -$result.stdout = $stdout -$result.stderr = $stderr - -$proc.WaitForExit() | Out-Null - -If ($wait -eq $true) { - $result.rc = $proc.ExitCode -} else { - $result.rc = 0 - $result.pid = $proc.ExitCode -} +$command_result = Run-Command -command "$executable $argument_string" $end_datetime = [DateTime]::UtcNow +$result.stdout = $command_result.stdout +$result.stderr = $command_result.stderr + +If ($wait -eq $true) { + $result.rc = $command_result.rc +} else { + $result.rc = 0 + $result.pid = $command_result.rc +} + $result.start = $start_datetime.ToString("yyyy-MM-dd hh:mm:ss.ffffff") $result.end = $end_datetime.ToString("yyyy-MM-dd hh:mm:ss.ffffff") $result.delta = $($end_datetime - $start_datetime).ToString("h\:mm\:ss\.ffffff") Exit-Json $result - diff --git a/lib/ansible/modules/windows/win_regedit.ps1 b/lib/ansible/modules/windows/win_regedit.ps1 index 4003f1fee3..20f4f1bc41 100644 --- a/lib/ansible/modules/windows/win_regedit.ps1 +++ b/lib/ansible/modules/windows/win_regedit.ps1 @@ -12,6 +12,7 @@ $ErrorActionPreference = "Stop" $params = Parse-Args -arguments $args -supports_check_mode $true $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false $diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false +$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP $path = Get-AnsibleParam -obj $params -name "path" -type "str" -failifempty $true -aliases "key" $name = Get-AnsibleParam -obj $params -name "name" -type "str" -aliases "entry","value" @@ -370,7 +371,14 @@ if ($hive) { if (-not (Test-Path $hive)) { Fail-Json -obj $result -message "hive at path '$hive' is not valid or accessible, cannot load hive" } + + $original_tmp = $env:TMP + $original_temp = $env:TEMP + $env:TMP = $_remote_tmp + $env:TEMP = $_remote_tmp Add-Type -TypeDefinition $registry_util + $env:TMP = $original_tmp + $env:TEMP = $original_temp try { [Ansible.RegistryUtil]::EnablePrivileges() } catch [System.ComponentModel.Win32Exception] { diff --git a/lib/ansible/modules/windows/win_region.ps1 b/lib/ansible/modules/windows/win_region.ps1 index b947d51138..e05adfe6da 100644 --- a/lib/ansible/modules/windows/win_region.ps1 +++ b/lib/ansible/modules/windows/win_region.ps1 @@ -19,6 +19,7 @@ $params = Parse-Args -arguments $args -supports_check_mode $true $check_mode = Get-AnsibleParam -obj $params "_ansible_check_mode" -type 'bool' -default $false +$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP $location = Get-AnsibleParam -obj $params -name 'location' -type 'str' $format = Get-AnsibleParam -obj $params -name 'format' -type 'str' @@ -98,7 +99,14 @@ Function Copy-RegistryKey($source, $target) { Function Set-CultureLegacy($culture) { # For when Set-Culture is not available (Pre Windows 8 and Server 2012) $reg_key = 'HKCU:\Control Panel\International' + + $original_tmp = $env:TMP + $original_temp = $env:TEMP + $env:TMP = $_remote_tmp + $env:TEMP = $_remote_tmp Add-Type -TypeDefinition $lctype_util + $env:TMP = $original_tmp + $env:TEMP = $original_temp $lookup = New-Object Ansible.LocaleHelper($culture) # hex values are from http://www.pinvoke.net/default.aspx/kernel32/GetLocaleInfoEx.html diff --git a/lib/ansible/modules/windows/win_scheduled_task.ps1 b/lib/ansible/modules/windows/win_scheduled_task.ps1 index 6e2ce4d99b..4a466f5ddd 100644 --- a/lib/ansible/modules/windows/win_scheduled_task.ps1 +++ b/lib/ansible/modules/windows/win_scheduled_task.ps1 @@ -14,6 +14,7 @@ $ErrorActionPreference = "Stop" $params = Parse-Args -arguments $args -supports_check_mode $true $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false $diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false +$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP $name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true $path = Get-AnsibleParam -obj $params -name "path" -type "str" -default "\" @@ -82,7 +83,7 @@ if ($diff_mode) { $result.diff = @{} } -Add-Type -TypeDefinition @" +$task_enums = @" public enum TASK_ACTION_TYPE // https://msdn.microsoft.com/en-us/library/windows/desktop/aa383553(v=vs.85).aspx { TASK_ACTION_EXEC = 0, @@ -136,6 +137,14 @@ public enum TASK_TRIGGER_TYPE2 // https://msdn.microsoft.com/en-us/library/windo } "@ +$original_tmp = $env:TMP +$original_temp = $env:TEMP +$env:TMP = $_remote_tmp +$env:TEMP = $_remote_tmp +Add-Type -TypeDefinition $task_enums +$env:TMP = $original_tmp +$env:TEMP = $original_temp + ######################## ### HELPER FUNCTIONS ### ######################## diff --git a/lib/ansible/modules/windows/win_scheduled_task_stat.ps1 b/lib/ansible/modules/windows/win_scheduled_task_stat.ps1 index f866a836a6..e5a4960182 100644 --- a/lib/ansible/modules/windows/win_scheduled_task_stat.ps1 +++ b/lib/ansible/modules/windows/win_scheduled_task_stat.ps1 @@ -8,6 +8,8 @@ #Requires -Module Ansible.ModuleUtils.SID $params = Parse-Args -arguments $args +$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP + $path = Get-AnsibleParam -obj $params -name "path" -type "str" -default "\" $name = Get-AnsibleParam -obj $params -name "name" -type "str" @@ -15,7 +17,7 @@ $result = @{ changed = $false } -Add-Type -TypeDefinition @" +$task_enums = @" public enum TASK_ACTION_TYPE { TASK_ACTION_EXEC = 0, @@ -67,6 +69,14 @@ public enum TASK_TRIGGER_TYPE2 } "@ +$original_tmp = $env:TMP +$original_temp = $env:TEMP +$env:TMP = $_remote_tmp +$env:TEMP = $_remote_tmp +Add-Type -TypeDefinition $task_enums +$env:TMP = $original_tmp +$env:TEMP = $original_temp + Function Get-PropertyValue($task_property, $com, $property) { $raw_value = $com.$property diff --git a/lib/ansible/modules/windows/win_user_right.ps1 b/lib/ansible/modules/windows/win_user_right.ps1 index 0f308a919f..b65bf21d42 100644 --- a/lib/ansible/modules/windows/win_user_right.ps1 +++ b/lib/ansible/modules/windows/win_user_right.ps1 @@ -12,6 +12,7 @@ $ErrorActionPreference = 'Stop' $params = Parse-Args $args -supports_check_mode $true $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false $diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false +$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP $name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true $users = Get-AnsibleParam -obj $params -name "users" -type "list" -failifempty $true @@ -27,7 +28,7 @@ if ($diff_mode) { $result.diff = @{} } -Add-Type -TypeDefinition @" +$sec_helper_util = @" using System; using System.ComponentModel; using System.Runtime.InteropServices; @@ -266,6 +267,14 @@ namespace Ansible } "@ +$original_tmp = $env:TMP +$original_temp = $env:TEMP +$env:TMP = $_remote_tmp +$env:TEMP = $_remote_tmp +Add-Type -TypeDefinition $sec_helper_util +$env:TMP = $original_tmp +$env:TEMP = $original_temp + Function Compare-UserList($existing_users, $new_users) { $added_users = [String[]]@() $removed_users = [String[]]@() diff --git a/lib/ansible/modules/windows/win_whoami.ps1 b/lib/ansible/modules/windows/win_whoami.ps1 index 5f4e8cc553..e6b6fae08b 100644 --- a/lib/ansible/modules/windows/win_whoami.ps1 +++ b/lib/ansible/modules/windows/win_whoami.ps1 @@ -9,6 +9,9 @@ $ErrorActionPreference = "Stop" +$params = Parse-Args $args -supports_check_mode $true +$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP + $session_util = @' using System; using System.Collections; @@ -780,7 +783,14 @@ namespace Ansible } '@ +$original_tmp = $env:TMP +$original_temp = $env:TEMP +$env:TMP = $_remote_tmp +$env:TEMP = $_remote_tmp Add-Type -TypeDefinition $session_util +$env:TMP = $original_tmp +$env:TEMP = $original_temp + $session_info = [Ansible.SessionUtil]::GetSessionInfo() Function Convert-Value($value) { diff --git a/lib/ansible/plugins/shell/powershell.py b/lib/ansible/plugins/shell/powershell.py index 693d412796..af07ae1c59 100644 --- a/lib/ansible/plugins/shell/powershell.py +++ b/lib/ansible/plugins/shell/powershell.py @@ -1090,27 +1090,6 @@ $exec_wrapper = { $DebugPreference = "Continue" $ErrorActionPreference = "Stop" - # become process is run under a different console to the WinRM one so we - # need to set the UTF-8 codepage again - Add-Type -Debug:$false -TypeDefinition @' -using System; -using System.Runtime.InteropServices; - -namespace Ansible -{ - public class ConsoleCP - { - [DllImport("kernel32.dll")] - public static extern bool SetConsoleCP(UInt32 wCodePageID); - - [DllImport("kernel32.dll")] - public static extern bool SetConsoleOutputCP(UInt32 wCodePageID); - } -} -'@ - [Ansible.ConsoleCP]::SetConsoleCP(65001) > $null - [Ansible.ConsoleCP]::SetConsoleOutputCP(65001) > $null - Function ConvertTo-HashtableFromPsCustomObject($myPsObject) { $output = @{} $myPsObject | Get-Member -MemberType *Property | % { @@ -1123,6 +1102,57 @@ namespace Ansible return $output } + Function Invoke-Win32Api { + # Inspired by - Call a Win32 API in PowerShell without compiling C# code on + # the disk + # http://www.leeholmes.com/blog/2007/10/02/managing-ini-files-with-powershell/ + # https://blogs.technet.microsoft.com/heyscriptingguy/2013/06/27/use-powershell-to-interact-with-the-windows-api-part-3/ + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)] [String]$DllName, + [Parameter(Mandatory=$true)] [String]$MethodName, + [Parameter(Mandatory=$true)] [Type]$ReturnType, + [Parameter()] [Type[]]$ParameterTypes = [Type[]]@(), + [Parameter()] [Object[]]$Parameters = [Object[]]@() + ) + + $assembly = New-Object -TypeName System.Reflection.AssemblyName -ArgumentList "Win32ApiAssembly" + $dynamic_assembly = [AppDomain]::CurrentDomain.DefineDynamicAssembly($assembly, [Reflection.Emit.AssemblyBuilderAccess]::Run) + $dynamic_module = $dynamic_assembly.DefineDynamicModule("Win32Module", $false) + $dynamic_type = $dynamic_module.DefineType("Win32Type", "Public, Class") + + $dynamic_method = $dynamic_type.DefineMethod( + $MethodName, + [Reflection.MethodAttributes]"Public, Static", + $ReturnType, + $ParameterTypes + ) + + $constructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor([String]) + $custom_attributes = New-Object -TypeName Reflection.Emit.CustomAttributeBuilder -ArgumentList @( + $constructor, + $DllName + ) + + $dynamic_method.SetCustomAttribute($custom_attributes) + $win32_type = $dynamic_type.CreateType() + $win32_type::$MethodName.Invoke($Parameters) + } + + # become process is run under a different console to the WinRM one so we + # need to set the UTF-8 codepage again, this also needs to be set before + # reading the stdin pipe that contains the module args specifying the + # remote_tmp to use. Instead this will use reflection when calling the Win32 + # API no tmp files touch the disk + $invoke_args = @{ + DllName = "kernel32.dll" + ReturnType = [bool] + ParameterTypes = @([UInt32]) + Parameters = @(65001) + } + Invoke-Win32Api -MethodName SetConsoleCP @invoke_args > $null + Invoke-Win32Api -MethodName SetConsoleOutputCP @invoke_args > $null + # stream JSON including become_pw, ps_module_payload, bin_module_payload, become_payload, write_payload_path, preserve directives # exec runspace, capture output, cleanup, return module output @@ -1240,7 +1270,21 @@ Function Parse-BecomeFlags($flags) { Function Run($payload) { # NB: action popping handled inside subprocess wrapper + $original_tmp = $env:TMP + $original_temp = $env:TEMP + $remote_tmp = $payload["module_args"]["_ansible_remote_tmp"] + $remote_tmp = [System.Environment]::ExpandEnvironmentVariables($remote_tmp) + if ($null -eq $remote_tmp) { + $remote_tmp = $original_tmp + } + + # become process is run under a different console to the WinRM one so we + # need to set the UTF-8 codepage again + $env:TMP = $remote_tmp + $env:TEMP = $remote_tmp Add-Type -TypeDefinition $helper_def -Debug:$false + $env:TMP = $original_tmp + $env:TEMP = $original_tmp $username = $payload.become_user $password = $payload.become_password @@ -1252,7 +1296,7 @@ Function Run($payload) { } # NB: CreateProcessWithTokenW commandline maxes out at 1024 chars, must bootstrap via filesystem - $temp = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName() + ".ps1") + $temp = [System.IO.Path]::Combine($remote_tmp, [System.IO.Path]::GetRandomFileName() + ".ps1") $exec_wrapper.ToString() | Set-Content -Path $temp $rc = 0 @@ -1574,17 +1618,29 @@ Function Run($payload) { } "@ # END Ansible.Async native type definition + $original_tmp = $env:TMP + $original_temp = $env:TEMP + $remote_tmp = $payload["module_args"]["_ansible_remote_tmp"] + $remote_tmp = [System.Environment]::ExpandEnvironmentVariables($remote_tmp) + if ($null -eq $remote_tmp) { + $remote_tmp = $original_tmp + } + # calculate the result path so we can include it in the worker payload $jid = $payload.async_jid $local_jid = $jid + "." + $pid - $results_path = [System.IO.Path]::Combine($env:LOCALAPPDATA, ".ansible_async", $local_jid) + $results_path = [System.IO.Path]::Combine($remote_tmp, ".ansible_async", $local_jid) $payload.async_results_path = $results_path [System.IO.Directory]::CreateDirectory([System.IO.Path]::GetDirectoryName($results_path)) | Out-Null + $env:TMP = $remote_tmp + $env:TEMP = $remote_tmp Add-Type -TypeDefinition $native_process_util -Debug:$false + $env:TMP = $original_tmp + $env:TEMP = $original_temp # FUTURE: create under new job to ensure all children die on exit? diff --git a/test/integration/targets/win_async_wrapper/tasks/main.yml b/test/integration/targets/win_async_wrapper/tasks/main.yml index 38b4d528ca..e437e9cb43 100644 --- a/test/integration/targets/win_async_wrapper/tasks/main.yml +++ b/test/integration/targets/win_async_wrapper/tasks/main.yml @@ -159,6 +159,18 @@ - nonascii_output.stdout_lines[0] == 'über den Fußgängerübergang gehen' - nonascii_output.stderr == '' +- name: test async with custom remote_tmp + win_shell: echo hi + register: async_custom_tmp + async: 5 + vars: + ansible_remote_tmp: '{{win_output_dir}}' + +- name: assert results file is in the remote tmp specified + assert: + that: + - async_custom_tmp.results_file == win_output_dir + '\\.ansible_async\\' + async_custom_tmp.ansible_job_id + # FUTURE: figure out why the last iteration of this test often fails on shippable #- name: loop async success # async_test: