powershell: Respect remote_tmp path set by the user (#40210)

* powershell: Respect remote_tmp path set by the user

* Fixed up linting error and typo

* Added changelog
This commit is contained in:
Jordan Borean 2018-05-23 07:12:32 +10:00 committed by GitHub
parent 070a5557d1
commit f84f3de7c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 281 additions and 203 deletions

View file

@ -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

View file

@ -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) {

View file

@ -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")
}

View file

@ -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))
{

View file

@ -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

View file

@ -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")

View file

@ -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) {

View file

@ -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

View file

@ -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

View file

@ -1,10 +1,11 @@
#!powershell
# -*- coding: utf-8 -*-
# Copyright: (c) 2017, Dag Wieers (@dagwieers) <dag@wieers.com>
# 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

View file

@ -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] {

View file

@ -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

View file

@ -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 ###
########################

View file

@ -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

View file

@ -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[]]@()

View file

@ -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) {

View file

@ -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?

View file

@ -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: