Repository: ansible/ansible

Issue:
Windows: Using `become: yes` on win_shell task returns error
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
windows become

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
v2.4

##### CONFIGURATION
<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->


##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say “N/A” for anything that is not platform-specific.
-->
master node: RHEL7
target nodes: Windows10

##### SUMMARY
<!--- Explain the problem briefly -->
When running the following task with `become: yes`:
```yaml
  - name: Disable Zune Music and Zune Video appx
    win_shell: |
      Get-AppxPackage -name "Microsoft.ZuneMusic" | Remove-AppxPackage
      Get-AppxPackage -name "Microsoft.ZuneVideo" | Remove-AppxPackage
    become: yes
```

I get the following output:
```
TASK [Disable Zune Music and Zune Video appx] ****************************************************************************************************************
 [WARNING]: The Windows 'runas' become method is experimental, and may change significantly in future Ansible releases.

fatal: [computer33]: FAILED! => {"changed": false, "failed": true, "module_stderr": "Exception calling \"Run\" with \"1\" argument(s): \"The running command stopped because the preference variable \"ErrorAction\r\nPreference\" or common parameter is set to Stop: Cannot bind argument to parameter 'String' because it is null.\"\r\nAt line:45 char:1\r\n+ $output = $entrypoint.Run($payload)\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n+ CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException\r\n+ FullyQualifiedErrorId : ScriptMethodRuntimeException\r\n", "module_stdout": "", "msg": "MODULE FAILURE", "rc": 1}
```

However this task does not fail when run directly as Administrator.

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->
Run the following task in a playbook on Windows 10:
<!--- Paste example playbooks or commands between quotes below -->
```yaml
  - name: Disable Zune Music and Zune Video appx
    win_shell: |
      Get-AppxPackage -name "Microsoft.ZuneMusic" | Remove-AppxPackage
      Get-AppxPackage -name "Microsoft.ZuneVideo" | Remove-AppxPackage
    become: yes
    become_user: Administrator
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
It works.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
Task fails with cryptic error message.

<!--- Paste verbatim command output between quotes below -->
```
TASK [Disable Zune Music and Zune Video appx] ****************************************************************************************************************
 [WARNING]: The Windows 'runas' become method is experimental, and may change significantly in future Ansible releases.

fatal: [computer33]: FAILED! => {"changed": false, "failed": true, "module_stderr": "Exception calling \"Run\" with \"1\" argument(s): \"The running command stopped because the preference variable \"ErrorAction\r\nPreference\" or common parameter is set to Stop: Cannot bind argument to parameter 'String' because it is null.\"\r\nAt line:45 char:1\r\n+ $output = $entrypoint.Run($payload)\r\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\r\n+ CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException\r\n+ FullyQualifiedErrorId : ScriptMethodRuntimeException\r\n", "module_stdout": "", "msg": "MODULE FAILURE", "rc": 1}
```



Discussion:
Are you connecting using NTLM or Kerberos?  If so its a known issue at the moment: https://github.com/ansible/ansible/issues/22218

No, this is using CredSSP.

Resolving patch:
diff --git a/lib/ansible/playbook/play_context.py b/lib/ansible/playbook/play_context.py
--- a/lib/ansible/playbook/play_context.py
+++ b/lib/ansible/playbook/play_context.py
@@ -553,6 +553,13 @@ def detect_ksu_prompt(b_data):
             elif self.become_method == 'runas':
                 # become is handled inside the WinRM connection plugin
                 display.warning("The Windows 'runas' become method is experimental, and may change significantly in future Ansible releases.")
+
+                if not self.become_user:
+                    raise AnsibleError(("The 'runas' become method requires a username "
+                                        "(specify with the '--become-user' CLI arg, the 'become_user' keyword, or the 'ansible_become_user' variable)"))
+                if not self.become_pass:
+                    raise AnsibleError(("The 'runas' become method requires a password "
+                                       "(specify with the '-K' CLI arg or the 'ansible_become_password' variable)"))
                 becomecmd = cmd
 
             elif self.become_method == 'doas':
diff --git a/lib/ansible/plugins/shell/powershell.py b/lib/ansible/plugins/shell/powershell.py
--- a/lib/ansible/plugins/shell/powershell.py
+++ b/lib/ansible/plugins/shell/powershell.py
@@ -316,6 +316,15 @@
 
 } # end exec_wrapper
 
+Function Dump-Error ($excep) {
+    $eo = @{failed=$true}
+
+    $eo.msg = $excep.Exception.Message
+    $eo.exception = $excep | Out-String
+    $host.SetShouldExit(1)
+
+    $eo | ConvertTo-Json -Depth 10
+}
 
 Function Run($payload) {
     # NB: action popping handled inside subprocess wrapper
@@ -370,14 +379,25 @@
         $psi.Username = $username
         $psi.Password = $($password | ConvertTo-SecureString -AsPlainText -Force)
 
-        [Ansible.Shell.ProcessUtil]::GrantAccessToWindowStationAndDesktop($username)
+        Try {
+            [Ansible.Shell.ProcessUtil]::GrantAccessToWindowStationAndDesktop($username)
+        }
+        Catch {
+            $excep = $_
+            throw "Error granting windowstation/desktop access to '$username' (is the username valid?): $excep"
+        }
 
         Try {
             $proc.Start() | Out-Null # will always return $true for non shell-exec cases
         }
         Catch {
-            Write-Output $_.Exception.InnerException
-            return
+            $excep = $_
+            if ($excep.Exception.InnerException -and `
+                $excep.Exception.InnerException -is [System.ComponentModel.Win32Exception] -and `
+                $excep.Exception.InnerException.NativeErrorCode -eq 5) {
+              throw "Become method 'runas' become is not currently supported with the NTLM or Kerberos auth types"
+            }
+            throw "Error launching under identity '$username': $excep"
         }
 
         $payload_string = $payload | ConvertTo-Json -Depth 99 -Compress
@@ -404,6 +424,10 @@
             Throw "failed, rc was $rc, stderr was $stderr, stdout was $stdout"
         }
     }
+    Catch {
+        $excep = $_
+        Dump-Error $excep
+    }
     Finally {
         Remove-Item $temp -ErrorAction SilentlyContinue
     }


Test patch: