mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-24 11:21:25 -07:00
Fixes for WinRM/PowerShell support in v2.
- Add support for inserting module args into PowerShell modules. Fixes #11661. - Support Windows paths containing spaces. Applies changes from #10727 to v2. Fixes #9999. Should also fix ansible/ansible-modules-core#944 and ansible/ansible-modules-core#1007. - Change how execution policy is set for running remote scripts. Applies changes from #11092 to v2. Also fixes ansible/ansible-modules-core#1776. - Use codepage 65001 (UTF-8) for WinRM connection instead of default (CP437), convert command to UTF-8 and results from UTF-8. Replaces changes from #10024. Fixes #11198. - Close WinRM connection when task completes. - Use win_stat, win_file and win_copy modules instead of stat, file and copy when called from within other action plugins (only when using WinRM+PowerShell). - Unquote Windows path arguments before passing to win_stat, win_file, win_copy and slurp modules (only when using WinRM/PowerShell). - Check for win_ping module to determine if core modules are missing (only when using WinRM/PowerShell). - Add stdout_lines to result from running low level commands (so stdout_lines is available when using raw/script). - Update copy action plugin to use shell functions for joining paths and checking for trailing slash. - Update fetch action plugin to unquote source path when using Windows paths. - Add win_copy and win_template action plugins that inherit from copy and template. - Support running .bat and .cmd scripts using default system encoding instead of UTF-8. - Always send PowerShell commands as base64-encoded blobs to allow for running simple PowerShell commands via raw. - Support running modules on Windows with interpreters other than PowerShell. - Update integration tests to support above changes and test unicode fixes. - Add test for win_user error from ansible/ansible-modules-core#1241 (fixed by ansible/ansible-modules-core#1774). - Add test for additional win_stat output values (implemented by ansible/ansible-modules-core#1473). - Add test for OS architecture and name from setup.ps1 (implemented by ansible/ansible-modules-core#1100). All WinRM integration tests pass for me with these changes.
This commit is contained in:
parent
fa8043c0ba
commit
e87cf4a3cc
25 changed files with 356 additions and 118 deletions
|
@ -24,7 +24,9 @@ import random
|
|||
import shlex
|
||||
import time
|
||||
|
||||
_common_args = ['PowerShell', '-NoProfile', '-NonInteractive']
|
||||
from ansible.utils.unicode import to_bytes, to_unicode
|
||||
|
||||
_common_args = ['PowerShell', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Unrestricted']
|
||||
|
||||
# Primarily for testing, allow explicitly specifying PowerShell version via
|
||||
# an environment variable.
|
||||
|
@ -38,24 +40,32 @@ class ShellModule(object):
|
|||
return ''
|
||||
|
||||
def join_path(self, *args):
|
||||
return os.path.join(*args).replace('/', '\\')
|
||||
parts = []
|
||||
for arg in args:
|
||||
arg = self._unquote(arg).replace('/', '\\')
|
||||
parts.extend([a for a in arg.split('\\') if a])
|
||||
path = '\\'.join(parts)
|
||||
if path.startswith('~'):
|
||||
return path
|
||||
return '"%s"' % path
|
||||
|
||||
def path_has_trailing_slash(self, path):
|
||||
# Allow Windows paths to be specified using either slash.
|
||||
path = self._unquote(path)
|
||||
return path.endswith('/') or path.endswith('\\')
|
||||
|
||||
def chmod(self, mode, path):
|
||||
return ''
|
||||
|
||||
def remove(self, path, recurse=False):
|
||||
path = self._escape(path)
|
||||
path = self._escape(self._unquote(path))
|
||||
if recurse:
|
||||
return self._encode_script('''Remove-Item "%s" -Force -Recurse;''' % path)
|
||||
else:
|
||||
return self._encode_script('''Remove-Item "%s" -Force;''' % path)
|
||||
|
||||
def mkdtemp(self, basefile, system=False, mode=None):
|
||||
basefile = self._escape(basefile)
|
||||
basefile = self._escape(self._unquote(basefile))
|
||||
# FIXME: Support system temp path!
|
||||
return self._encode_script('''(New-Item -Type Directory -Path $env:temp -Name "%s").FullName | Write-Host -Separator '';''' % basefile)
|
||||
|
||||
|
@ -63,16 +73,17 @@ class ShellModule(object):
|
|||
# PowerShell only supports "~" (not "~username"). Resolve-Path ~ does
|
||||
# not seem to work remotely, though by default we are always starting
|
||||
# in the user's home directory.
|
||||
user_home_path = self._unquote(user_home_path)
|
||||
if user_home_path == '~':
|
||||
script = 'Write-Host (Get-Location).Path'
|
||||
elif user_home_path.startswith('~\\'):
|
||||
script = 'Write-Host ((Get-Location).Path + "%s")' % _escape(user_home_path[1:])
|
||||
script = 'Write-Host ((Get-Location).Path + "%s")' % self._escape(user_home_path[1:])
|
||||
else:
|
||||
script = 'Write-Host "%s"' % _escape(user_home_path)
|
||||
script = 'Write-Host "%s"' % self._escape(user_home_path)
|
||||
return self._encode_script(script)
|
||||
|
||||
def checksum(self, path, *args, **kwargs):
|
||||
path = self._escape(path)
|
||||
path = self._escape(self._unquote(path))
|
||||
script = '''
|
||||
If (Test-Path -PathType Leaf "%(path)s")
|
||||
{
|
||||
|
@ -93,16 +104,36 @@ class ShellModule(object):
|
|||
return self._encode_script(script)
|
||||
|
||||
def build_module_command(self, env_string, shebang, cmd, rm_tmp=None):
|
||||
cmd = cmd.encode('utf-8')
|
||||
cmd_parts = shlex.split(cmd, posix=False)
|
||||
if not cmd_parts[0].lower().endswith('.ps1'):
|
||||
cmd_parts[0] = '%s.ps1' % cmd_parts[0]
|
||||
script = self._build_file_cmd(cmd_parts)
|
||||
cmd_parts = shlex.split(to_bytes(cmd), posix=False)
|
||||
cmd_parts = map(to_unicode, cmd_parts)
|
||||
if shebang and shebang.lower() == '#!powershell':
|
||||
if not self._unquote(cmd_parts[0]).lower().endswith('.ps1'):
|
||||
cmd_parts[0] = '"%s.ps1"' % self._unquote(cmd_parts[0])
|
||||
cmd_parts.insert(0, '&')
|
||||
elif shebang and shebang.startswith('#!'):
|
||||
cmd_parts.insert(0, shebang[2:])
|
||||
catch = '''
|
||||
$_obj = @{ failed = $true; $msg = $_ }
|
||||
echo $_obj | ConvertTo-Json -Compress -Depth 99
|
||||
Exit 1
|
||||
'''
|
||||
script = 'Try { %s }\nCatch { %s }' % (' '.join(cmd_parts), 'throw')
|
||||
if rm_tmp:
|
||||
rm_tmp = self._escape(rm_tmp)
|
||||
script = '%s; Remove-Item "%s" -Force -Recurse;' % (script, rm_tmp)
|
||||
rm_tmp = self._escape(self._unquote(rm_tmp))
|
||||
rm_cmd = 'Remove-Item "%s" -Force -Recurse -ErrorAction SilentlyContinue' % rm_tmp
|
||||
script = '%s\nFinally { %s }' % (script, rm_cmd)
|
||||
return self._encode_script(script)
|
||||
|
||||
def _unquote(self, value):
|
||||
'''Remove any matching quotes that wrap the given value.'''
|
||||
m = re.match(r'^\s*?\'(.*?)\'\s*?$', value)
|
||||
if m:
|
||||
return m.group(1)
|
||||
m = re.match(r'^\s*?"(.*?)"\s*?$', value)
|
||||
if m:
|
||||
return m.group(1)
|
||||
return value
|
||||
|
||||
def _escape(self, value, include_vars=False):
|
||||
'''Return value escaped for use in PowerShell command.'''
|
||||
# http://www.techotopia.com/index.php/Windows_PowerShell_1.0_String_Quoting_and_Escape_Sequences
|
||||
|
@ -119,14 +150,10 @@ class ShellModule(object):
|
|||
|
||||
def _encode_script(self, script, as_list=False):
|
||||
'''Convert a PowerShell script to a single base64-encoded command.'''
|
||||
script = to_unicode(script)
|
||||
script = '\n'.join([x.strip() for x in script.splitlines() if x.strip()])
|
||||
encoded_script = base64.b64encode(script.encode('utf-16-le'))
|
||||
cmd_parts = _common_args + ['-EncodedCommand', encoded_script]
|
||||
if as_list:
|
||||
return cmd_parts
|
||||
return ' '.join(cmd_parts)
|
||||
|
||||
def _build_file_cmd(self, cmd_parts):
|
||||
'''Build command line to run a file, given list of file name plus args.'''
|
||||
return ' '.join(_common_args + ['-ExecutionPolicy', 'Unrestricted', '-File'] + ['"%s"' % x for x in cmd_parts])
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue