Ansible managing Windows hosts with non admin user

Hello,

I use Ansible to manage Linux hosts and just recently had a requirement to manage Windows Servers.

I have Ansible working against a Windows 2012 R2 host using an account (test_user) that is part of the “administrators” group.

I would like reduce the rights of test_user, so it is no longer in the “administrators” group, but can still connect and copy files to its own homedrive, and basically run commands that a user that is part of the “Users” group can.

On removing the user Ansible provives the following error:

fatal: [servername] => 401 Unauthorized. basic auth failed

I did a bit of research and found the user needed to be part of the “Remote Management Users”, this would allow test_user to run Powershell remotely. I tested this from another Windows host, and yes it works.

$options=New-PSSessionOption -SkipCACheck -SkipCNCheck
Enter-PSSession -ComputerName servername -Credential servername\test_user -UseSSL -SessionOption $options

However via Ansible I get the following error:

fatal: [servername] => failed to exec cmd PowerShell -NoProfile -NonInteractive -EncodedCommand…

Does anyone know if it’s possible to run Ansible against a Windows hosts with a non admin user?

Thanks.

From here it looks like this is possible, although you would have to tweak user rights:

https://social.technet.microsoft.com/Forums/scriptcenter/en-US/60de5fcd-33e0-479b-9668-fcf683678a2f/winrm-for-nonadministrative-users?forum=ITCG

I get the impression that the intention for WinRM is for admistrative access, however. Have a look at the first paragraph of this page: https://msdn.microsoft.com/en-us/library/windows/desktop/aa384295(v=vs.85).aspx

Hope this helps.

Please report back if you are able to get this working - knowing the minimal set of user rights would be useful for others I think,

Jon

Interesting. My guess would be that this works, as Ansible doesn’t do anything outside the logged-on user’s profile (until you start pushing tasks that require admin access, of course). What is the exact error you’re getting? Can you use the “raw” module to do something simple like list the contents of your profile or something? I’d really like to see the full verbose log from your console aswell.

I was using the win_ping module. I will try the raw module as suggested.

I will also play around with "allow on Execute Methods and Remote enable" once I work out what and where they are set.

Thanks guys. I will report back soon.

I have since tried win_ping and raw, but I haven’t had time to test “allow on Execute Methods and Remote enable” on the windows server.

Both modules work fine when I’m a local admin on the windows server.

Both fail at the “Gather Facts” when I’m not a local admin.

ansible windows -i inventory/dev/hosts -m win_ping
Traceback (most recent call last):
File “/usr/lib/python2.6/site-packages/ansible/runner/connection_plugins/winrm.py”, line 161, in exec_command
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:], from_exec=True)
File “/usr/lib/python2.6/site-packages/ansible/runner/connection_plugins/winrm.py”, line 122, in _winrm_exec
self.shell_id = self.protocol.open_shell()
File “/usr/lib/python2.6/site-packages/winrm/protocol.py”, line 121, in open_shell
rs = self.send_message(xmltodict.unparse(rq))
File “/usr/lib/python2.6/site-packages/winrm/protocol.py”, line 193, in send_message
return self.transport.send_message(message)
File “/usr/lib/python2.6/site-packages/winrm/transport.py”, line 136, in send_message
raise WinRMTransportError(‘http’, error_message)
WinRMTransportError: 500 WinRMTransport. Bad HTTP response returned from server. Code 500
| FAILED => failed to exec cmd PowerShell -NoProfile -NonInteractive -EncodedCommand

I disabled gathering facts and get the same error message.

My output from the raw module:

ansible-playbook -i inventory/dev/hosts playbooks/test_windows.yml

PLAY [test script module] *****************************************************

TASK: [run ipconfig] **********************************************************
Traceback (most recent call last):
File “/usr/lib/python2.6/site-packages/ansible/runner/connection_plugins/winrm.py”, line 161, in exec_command
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:], from_exec=True)
File “/usr/lib/python2.6/site-packages/ansible/runner/connection_plugins/winrm.py”, line 122, in _winrm_exec
self.shell_id = self.protocol.open_shell()
File “/usr/lib/python2.6/site-packages/winrm/protocol.py”, line 121, in open_shell
rs = self.send_message(xmltodict.unparse(rq))
File “/usr/lib/python2.6/site-packages/winrm/protocol.py”, line 193, in send_message
return self.transport.send_message(message)
File “/usr/lib/python2.6/site-packages/winrm/transport.py”, line 136, in send_message
raise WinRMTransportError(‘http’, error_message)
WinRMTransportError: 500 WinRMTransport. Bad HTTP response returned from server. Code 500
fatal: [] => failed to exec cmd ipconfig

FATAL: all hosts have already failed – aborting

I managed to find the root/CIMV2 namespace, and I set the security permissions of “Execute Methods” and “Remote Enable” and restarted the WMI and WinRM services. Unfortunately I still receive the same error.

As I mentioned, I can use WinRM from another Windows server via Powershell session, without having to have an admin account. Once I have connected I’m able to run cmd or ipconfig.

I wonder what Ansible is doing?

could you try running playbook with -vvvvvv
this should show a bit more information about how ansible is connecting

also check the event log on the windows host to see if the login request is a success.

Something else you could try is to run the python pywinrm example here against your host:

https://github.com/diyan/pywinrm

Hopefully this should help isolate the problem.

Jon

Thank you for your help.

win_ping module verbose, without local admin on remote windows hosts:

[@ winRM]$ ansible windows -i inventory/dev/hosts -m win_ping -vvvv
<> ESTABLISH WINRM CONNECTION FOR USER: test_user on PORT 5986 TO
<> WINRM CONNECT: transport=plaintext endpoint=https://:5986/wsman
<> REMOTE_MODULE win_ping
<> EXEC (New-Item -Type Directory -Path $env:temp -Name “ansible-tmp-1456562221.68-167539675202015”).FullName | Write-Host -Separator ‘’;
<> WINRM EXEC ‘PowerShell’ [‘-NoProfile’, ‘-NonInteractive’, ‘-EncodedCommand’, ‘KABOAGUAdwAtAEkAdABlAG0AIAAtAFQAeQBwAGUAIABEAGkAcgBlAGMAdABvAHIAeQAgAC0AUABhAHQAaAAgACQAZQBuAHYAOgB0AGUAbQBwACAALQBOAGEAbQBlACAAIgBhAG4AcwBpAGIAbABlAC0AdABtAHAALQAxADQANQA2ADUANgAyADIAMgAxAC4ANgA4AC0AMQA2ADcANQAzADkANgA3ADUAMgAwADIAMAAxADUAIgApAC4ARgB1AGwAbABOAGEAbQBlACAAfAAgAFcAcgBpAHQAZQAtAEgAbwBzAHQAIAAtAFMAZQBwAGEAcgBhAHQAbwByACAAJwAnADsA’]
Traceback (most recent call last):
File “/usr/lib/python2.6/site-packages/ansible/runner/connection_plugins/winrm.py”, line 161, in exec_command
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:], from_exec=True)
File “/usr/lib/python2.6/site-packages/ansible/runner/connection_plugins/winrm.py”, line 122, in _winrm_exec
self.shell_id = self.protocol.open_shell()
File “/usr/lib/python2.6/site-packages/winrm/protocol.py”, line 121, in open_shell
rs = self.send_message(xmltodict.unparse(rq))
File “/usr/lib/python2.6/site-packages/winrm/protocol.py”, line 193, in send_message
return self.transport.send_message(message)
File “/usr/lib/python2.6/site-packages/winrm/transport.py”, line 136, in send_message
raise WinRMTransportError(‘http’, error_message)
WinRMTransportError: 500 WinRMTransport. Bad HTTP response returned from server. Code 500
| FAILED => failed to exec cmd PowerShell -NoProfile -NonInteractive -EncodedCommand KABOAGUAdwAtAEkAdABlAG0AIAAtAFQAeQBwAGUAIABEAGkAcgBlAGMAdABvAHIAeQAgAC0AUABhAHQAaAAgACQAZQBuAHYAOgB0AGUAbQBwACAALQBOAGEAbQBlACAAIgBhAG4AcwBpAGIAbABlAC0AdABtAHAALQAxADQANQA2ADUANgAyADIAMgAxAC4ANgA4AC0AMQA2ADcANQAzADkANgA3ADUAMgAwADIAMAAxADUAIgApAC4ARgB1AGwAbABOAGEAbQBlACAAfAAgAFcAcgBpAHQAZQAtAEgAbwBzAHQAIAAtAFMAZQBwAGEAcgBhAHQAbwByACAAJwAnADsA

raw module (ipconfig) verbose, without local admin on remote windows hosts:

[@ winRM]$ ansible-playbook -i inventory/dev/hosts playbooks/test_windows.yml -vvvv

PLAY [test script module] *****************************************************

TASK: [run ipconfig] **********************************************************
<> ESTABLISH WINRM CONNECTION FOR USER: test_user on PORT 5986 TO
<> WINRM CONNECT: transport=plaintext endpoint=https://:5986/wsman
<> EXEC ipconfig
<> WINRM EXEC ‘ipconfig’
Traceback (most recent call last):
File “/usr/lib/python2.6/site-packages/ansible/runner/connection_plugins/winrm.py”, line 161, in exec_command
result = self._winrm_exec(cmd_parts[0], cmd_parts[1:], from_exec=True)
File “/usr/lib/python2.6/site-packages/ansible/runner/connection_plugins/winrm.py”, line 122, in _winrm_exec
self.shell_id = self.protocol.open_shell()
File “/usr/lib/python2.6/site-packages/winrm/protocol.py”, line 121, in open_shell
rs = self.send_message(xmltodict.unparse(rq))
File “/usr/lib/python2.6/site-packages/winrm/protocol.py”, line 193, in send_message
return self.transport.send_message(message)
File “/usr/lib/python2.6/site-packages/winrm/transport.py”, line 136, in send_message
raise WinRMTransportError(‘http’, error_message)
WinRMTransportError: 500 WinRMTransport. Bad HTTP response returned from server. Code 500
fatal: [] => failed to exec cmd ipconfig

FATAL: all hosts have already failed – aborting

I don’t see any errors in the numerous windows logs, but I do see a successful logon:

An account was successfully logged on.

Subject:
Security ID: NETWORK SERVICE
Account Name: $
Account Domain: MHF
Logon ID: 0x3E4

Logon Type: 3

Impersonation Level: Impersonation

New Logon:
Security ID: \test_user
Account Name: test_user
Account Domain:
Logon ID: 0x19F85BC2C
Logon GUID: {00000000-0000-0000-0000-000000000000}

pywinrm:

The examples use http, which I haven’t been using. I therefore included transport over SSL.

Without admin:

import winrm
s = winrm.Session(‘’, auth=(‘test_user’, ‘**********’),transport=‘ssl’)
r = s.run_cmd(‘ipconfig’, [‘/all’])
print r.std_out

Traceback (most recent call last):
File “./process_remote_host.py”, line 6, in
r = s.run_cmd(‘ipconfig’, [‘/all’])
File “/usr/lib/python2.6/site-packages/winrm/init.py”, line 29, in run_cmd
shell_id = self.protocol.open_shell()
File “/usr/lib/python2.6/site-packages/winrm/protocol.py”, line 121, in open_shell
rs = self.send_message(xmltodict.unparse(rq))
File “/usr/lib/python2.6/site-packages/winrm/protocol.py”, line 193, in send_message
return self.transport.send_message(message)
File “/usr/lib/python2.6/site-packages/winrm/transport.py”, line 136, in send_message
raise WinRMTransportError(‘http’, error_message)
winrm.exceptions.WinRMTransportError: 500 WinRMTransport. Bad HTTP response returned from server. Code 500

With admin:

Windows IP Configuration

Host Name . . . . . . . . . . . . :
Primary Dns Suffix . . . . . . . :
Node Type . . . . . . . . . . . . : Hybrid
IP Routing Enabled. . . . . . . . : No
WINS Proxy Enabled. . . . . . . . : No

Please note it is an identical error for “Run powershell on remote host”

You can see it’s the same error when running via Ansible, as it is when I run directly from pywinrm.

I can think of a couple more options:

Get pywinrm working without SSL, and see where that takes me. I will need to make changes on the Windows host for this, because unencrypted traffic is currently not allowed. This was intended, I wanted to use SSL only.
I tested this from another windows server:
“Message = The WinRM client cannot process the request. Unencrypted traffic is currently disabled in the client configuration. Change the client configuration and try the request again.”

Or I take a closer look at pywinrm.

I don’t have time to do either this weekend. Hopefully I will get some time next week.

Thanks for this.

Since ansible and pywinrm are behaving the same, it occurs to me that the winrm configuration might not suit pywinrm.

From the above it appears you have created specific configuration for winrm, rather than using the settings that are applied if you run the ConfigureRemotingForAnsible.ps1.

I suggest you run the ConfigureRemotingForAnsible.ps1 script on a windows host and then compare the winrm configuration with your test machine.

You can see the winrm config by running

Winrm get winrm/config

From memory, pywinrm can not use credssp authorisation,

Is your test user a member of the WinRMRemoteWMIUsers__ group? I wouldn't expect anything to work if not but just trying to understand what you have in place.

Hope this helps,

Jon

Yes you are right. I tried running ConfigureRemotingForAnsible.ps1 but it failed. I’m sorry I don’t have the error message.

I have little experience of Powershell, but I managed to work through the script and run the individuals settings manually. Of course I could have missed something, but it seemed to work fine with Ansible once I was part of the administrators group on the Windows host.

Luckily I have access to another Windows servers (server B), and for this the script ConfigureRemotingForAnsible.ps1 works. Also I hadn’t placed the user in the WinRMRemoteWMIUsers__ group.

Unfortunately I’m receiving the same errorr, and like with Server A, as soon as I add my user to the administrators group, all works fine.

I’ve compared the WinRM settings across the two servers. Server A was configured manually, and Server B via ConfigureRemotingForAnsible.ps1. They are both the same:

Config
MaxEnvelopeSizekb = 500
MaxTimeoutms = 60000
MaxBatchItems = 32000
MaxProviderRequests = 4294967295
Client
NetworkDelayms = 5000
URLPrefix = wsman
AllowUnencrypted = false
Auth
Basic = true
Digest = true
Kerberos = true
Negotiate = true
Certificate = true
CredSSP = false
DefaultPorts
HTTP = 5985
HTTPS = 5986
TrustedHosts
Service
RootSDDL = O:NSG:BAD:P(A;;GA;;;BA)(A;;GR;;;IU)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)
MaxConcurrentOperations = 4294967295
MaxConcurrentOperationsPerUser = 1500
EnumerationTimeoutms = 240000
MaxConnections = 300
MaxPacketRetrievalTimeSeconds = 120
AllowUnencrypted = false
Auth
Basic = true
Kerberos = true
Negotiate = true
Certificate = false
CredSSP = false
CbtHardeningLevel = Relaxed
DefaultPorts
HTTP = 5985
HTTPS = 5986
IPv4Filter = *
IPv6Filter = *
EnableCompatibilityHttpListener = false
EnableCompatibilityHttpsListener = false
CertificateThumbprint
AllowRemoteAccess = true
Winrs
AllowRemoteShellAccess = true
IdleTimeout = 7200000
MaxConcurrentUsers = 10
MaxShellRunTime = 2147483647
MaxProcessesPerShell = 25
MaxMemoryPerShellMB = 1024
MaxShellsPerUser = 30

Do we know if anyone that has got this working with a user that is not part of the administrators group?

Hello,
Just to share my tests, I face exactly the same issues with the same configuration (same user on a windows box can run remote Powershell commands even when not in administrators group).
I tried with local user and basic auth and domain user with Kerberos. As soon as the user is member of the local Administrators group he can run remote shell commands, if not (I even created a dedicated groupe POSH-RemoteUsers in the domain and provided it with appropriate rights) I get the “winrm.exceptions.WinRMTransportError: 500 WinRMTransport. Bad HTTP response returned from server. Code 500” error.

Will try to debug a bit further but with little hope.

Patrick

The default WinRM ACL (at least on 2012R2- only thing I have booted to look at right now) only includes the local Administrators group (and INTERACTIVE, but not in a usable way). If you do a

winrm configSDDL default

on the host in question, you can add any user you want to that ACL (they only need Read and Execute to get logged in via WinRM). I usually just add the local “Remote Management Users” group, then add users to that (not sure why it’s not already included, since that was arguably its purpose). You can set this ACL in an automated fashion as well if you’re good with SDDL.

Thanks a lot,
That was it !!!

Have a nice day.

Patrick

Hi,

I’ve added my non-admin user to winrm configSDDL default and when I win_ping my windows host, I can get a reply. But when I run win_reboot command, it gives me access denied error.

Do any one have any idea on this?

Regards,
Zubair

You have to grant the non-admin user/group “Shut down the system” privileges on the machine(s) in question- default setting only allows that for Administrators and Backup Operators.