diff --git a/changelogs/fragments/window_become-better-errors.yaml b/changelogs/fragments/window_become-better-errors.yaml new file mode 100644 index 00000000000..f1a2d24903c --- /dev/null +++ b/changelogs/fragments/window_become-better-errors.yaml @@ -0,0 +1,2 @@ +bugfixes: +- windows become - Show better error messages when the become process fails diff --git a/docs/docsite/rst/user_guide/become.rst b/docs/docsite/rst/user_guide/become.rst index baefb7fda06..ad47644fed3 100644 --- a/docs/docsite/rst/user_guide/become.rst +++ b/docs/docsite/rst/user_guide/become.rst @@ -342,7 +342,7 @@ module execution. To determine the type of token that Ansible was able to get, run the following task and check the output:: - - win_shell: cmd.exe /c whoami && whoami /groups && whoami /priv + - win_whoami: become: yes Under the ``GROUP INFORMATION`` section, the ``Mandatory Label`` entry @@ -453,7 +453,11 @@ or with this Ansible task: Become Flags ------------ -Ansible 2.5 adds the ``become_flags`` parameter to the ``runas`` become method. This parameter can be set using the ``become_flags`` task directive or set in Ansible's configuration using ``ansible_become_flags``. The two valid values that are initially supported for this parameter are ``logon_type`` and ``logon_flags``. +Ansible 2.5 adds the ``become_flags`` parameter to the ``runas`` become method. +This parameter can be set using the ``become_flags`` task directive or set in +Ansible's configuration using ``ansible_become_flags``. The two valid values +that are initially supported for this parameter are ``logon_type`` and +``logon_flags``. .. Note:: These flags should only be set when becoming a normal user account, not a local service account like LocalSystem. @@ -490,7 +494,7 @@ For more information, see `dwLogonType `_. The ``logon_flags`` key specifies how Windows will log the user on when creating -the new process. The value can be set to one of the following: +the new process. The value can be set to none or multiple of the following: * ``with_profile``: The default logon flag set. The process will load the user's profile in the ``HKEY_USERS`` registry key to ``HKEY_CURRENT_USER``. @@ -500,6 +504,10 @@ the new process. The value can be set to one of the following: resource. This is useful in inter-domain scenarios where there is no trust relationship, and should be used with the ``new_credentials`` ``logon_type``. +By default ``logon_flags=with_profile`` is set, if the profile should not be +loaded set ``logon_flags=`` or if the profile should be loaded with +``netcredentials_only``, set ``logon_flags=with_profile,netcredentials_only``. + For more information, see `dwLogonFlags `_. Here are some examples of how to use ``become_flags`` with Windows tasks: @@ -519,10 +527,15 @@ Here are some examples of how to use ``become_flags`` with Windows tasks: ansible_become_flags: logon_type=new_credentials logon_flags=netcredentials_only - name: run a command under a batch logon - win_command: whoami + win_whoami: become: yes become_flags: logon_type=batch + - name: run a command and not load the user profile + win_whomai: + become: yes + become_flags: logon_flags= + Limitations ----------- @@ -535,7 +548,8 @@ Be aware of the following limitations with ``become`` on Windows: * By default, the become user logs on with an interactive session, so it must have the right to do so on the Windows host. If it does not inherit the ``SeAllowLogOnLocally`` privilege or inherits the ``SeDenyLogOnLocally`` - privilege, the become process will fail. + privilege, the become process will fail. Either add the privilege or set the + ``logon_type`` flag to change the logon type used. * Prior to Ansible version 2.3, become only worked when ``ansible_winrm_transport`` was either ``basic`` or ``credssp``. This diff --git a/lib/ansible/plugins/shell/powershell.py b/lib/ansible/plugins/shell/powershell.py index 3fd6411e156..693d4127967 100644 --- a/lib/ansible/plugins/shell/powershell.py +++ b/lib/ansible/plugins/shell/powershell.py @@ -1161,10 +1161,14 @@ namespace Ansible Write-Output ([System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes((Write-Output $output)))) } # end exec_wrapper -Function Dump-Error ($excep) { +Function Dump-Error ($excep, $msg=$null) { $eo = @{failed=$true} - $eo.msg = $excep.Exception.Message + $exception_message = $excep.Exception.Message + if ($null -ne $msg) { + $exception_message = "$($msg): $exception_message" + } + $eo.msg = $exception_message $eo.exception = $excep | Out-String $host.SetShouldExit(1) @@ -1243,7 +1247,7 @@ Function Run($payload) { try { $logon_type, $logon_flags = Parse-BecomeFlags -flags $payload.become_flags } catch { - Dump-Error -excep $_ + Dump-Error -excep $_ -msg "Failed to parse become_flags '$($payload.become_flags)'" return $null } @@ -1285,7 +1289,7 @@ Function Run($payload) { [Console]::Error.WriteLine($stderr.Trim()) } Catch { $excep = $_ - Dump-Error $excep + Dump-Error -excep $excep -msg "Failed to become user $username" } Finally { Remove-Item $temp -ErrorAction SilentlyContinue } diff --git a/test/integration/targets/win_become/tasks/main.yml b/test/integration/targets/win_become/tasks/main.yml index a71e2bb9c9a..e860ff78e44 100644 --- a/test/integration/targets/win_become/tasks/main.yml +++ b/test/integration/targets/win_become/tasks/main.yml @@ -125,6 +125,18 @@ - name: test with module that will return non-zero exit code (https://github.com/ansible/ansible/issues/30468) vars: *become_vars setup: + + - name: test become with invalid password + win_whoami: + vars: + ansible_become_pass: '{{ gen_pw }}abc' + become: yes + become_method: runas + become_user: '{{ become_test_username }}' + register: become_invalid_pass + failed_when: + - '"Failed to become user " + become_test_username not in become_invalid_pass.msg' + - '"LogonUser failed (The user name or password is incorrect, Win32ErrorCode 1326)" not in become_invalid_pass.msg' - name: test become with SYSTEM account win_whoami: @@ -215,21 +227,21 @@ become_flags: logon_type=batch invalid_flags=a become_method: runas register: failed_flags_invalid_key - failed_when: failed_flags_invalid_key.msg != "become_flags key 'invalid_flags' is not a valid runas flag, must be 'logon_type' or 'logon_flags'" + failed_when: "failed_flags_invalid_key.msg != \"Failed to parse become_flags 'logon_type=batch invalid_flags=a': become_flags key 'invalid_flags' is not a valid runas flag, must be 'logon_type' or 'logon_flags'\"" - name: test failure with invalid logon_type vars: *become_vars win_whoami: become_flags: logon_type=invalid register: failed_flags_invalid_type - failed_when: "failed_flags_invalid_type.msg != \"become_flags logon_type value 'invalid' is not valid, valid values are: interactive, network, batch, service, unlock, network_cleartext, new_credentials\"" + failed_when: "failed_flags_invalid_type.msg != \"Failed to parse become_flags 'logon_type=invalid': become_flags logon_type value 'invalid' is not valid, valid values are: interactive, network, batch, service, unlock, network_cleartext, new_credentials\"" - name: test failure with invalid logon_flag vars: *become_vars win_whoami: become_flags: logon_flags=with_profile,invalid register: failed_flags_invalid_flag - failed_when: "failed_flags_invalid_flag.msg != \"become_flags logon_flags value 'invalid' is not valid, valid values are: with_profile, netcredentials_only\"" + failed_when: "failed_flags_invalid_flag.msg != \"Failed to parse become_flags 'logon_flags=with_profile,invalid': become_flags logon_flags value 'invalid' is not valid, valid values are: with_profile, netcredentials_only\"" # Server 2008 doesn't work with network and network_cleartext, there isn't really a reason why you would want this anyway - name: become different types @@ -266,6 +278,34 @@ - become_netcredentials.label.account_name == 'High Mandatory Level' - become_netcredentials.label.sid == 'S-1-16-12288' + - name: become logon_flags bitwise tests when loading the profile + # Error code of 2 means no file found == no profile loaded + win_shell: | + Add-Type -Name "Native" -Namespace "Ansible" -MemberDefinition '[DllImport("Userenv.dll", SetLastError=true)]public static extern bool GetProfileType(out UInt32 pdwFlags);' + $profile_type = $null + $res = [Ansible.Native]::GetProfileType([ref]$profile_type) + if (-not $res) { + $last_err = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() + if ($last_err -eq 2) { + return $false + } else { + throw [System.ComponentModel.Win32Exception]$last_err + } + } else { + return $true + } + vars: *admin_become_vars + become_flags: logon_flags={{item.flags}} + register: become_logon_flags + failed_when: become_logon_flags.stdout_lines[0]|bool != item.actual + with_items: + - flags: + actual: False + - flags: netcredentials_only + actual: False + - flags: with_profile,netcredentials_only + actual: True + - name: echo some non ascii characters win_command: cmd.exe /c echo über den Fußgängerübergang gehen vars: *become_vars