How to Connect to Windows Ansible From Ubuntu
Connect Ansible on Ubuntu to Windows with PSRP, WinRM, or SSH, including inventory, authentication, testing, troubleshooting, and security.

Direct answer: Run Ansible on Ubuntu as the control node and configure each Windows machine as a managed node. Use PSRP or WinRM when your environment already uses Windows Remote Management, Active Directory, or Windows delegation. Use SSH when Win32-OpenSSH is configured and key-based authentication is preferable. The current Ansible Windows guide documents PSRP, WinRM, and SSH for Windows Server 2016, Windows 10, and newer targets. See Ansible’s Windows setup guide.
The basic sequence is:
- Install Ansible on Ubuntu.
- Prepare WinRM/PSRP or Win32-OpenSSH on Windows.
- Create an inventory with the correct connection plugin and authentication variables.
- Test network access and credentials with
win_ping. - Run a small ad hoc command before applying a larger playbook.
1. Choose PSRP, WinRM, or SSH
| Transport | Use it when | Ubuntu requirement | Windows requirement |
|---|---|---|---|
| PSRP | You want PowerShell remoting through WinRM and a modern Ansible plugin. | pypsrp>=0.4.0,<1.0.0 |
WinRM listener, authentication policy, and firewall access. |
| WinRM | Your organization already standardizes on WinRM and its authentication settings. | Ansible’s WinRM support and its Python dependencies. | WinRM listener, authentication policy, and firewall access. |
| SSH | You prefer OpenSSH, keys, or a non-domain setup. | OpenSSH client and Ansible 2.18 or newer for official Windows SSH support. | Win32-OpenSSH server, account policy, authorized keys, and firewall access. |
PSRP and WinRM both use Windows Remote Management. PSRP runs commands through PowerShell and requires the controller-side pypsrp package. SSH is also supported for Windows; Ansible’s documentation describes it as an alternative to the traditional PSRP and WinRM plugins. Read the WinRM connection documentation and Windows SSH documentation before choosing.

2. Install Ansible on Ubuntu
Use your team’s approved Ubuntu package or Python installation method. A virtual environment keeps controller dependencies separate:
sudo apt update
sudo apt install -y python3-venv python3-pip openssh-client
python3 -m venv ~/venvs/ansible
source ~/venvs/ansible/bin/activate
python -m pip install --upgrade pip ansible
ansible --version
For PSRP, install the documented dependency range:
python -m pip install 'pypsrp>=0.4.0,<1.0.0'
Keep the virtual environment active whenever you run Ansible, or install the same packages into the environment that actually executes your automation jobs.
3. Prepare the Windows host
PSRP or WinRM preparation
On Windows, create and start a WinRM listener, choose HTTP or HTTPS, enable only the authentication mechanisms your policy permits, and allow the selected port through the firewall. HTTPS and a trusted certificate are preferred for production. The exact commands depend on your domain policy, certificate infrastructure, and whether the host is domain joined, so follow your organization’s Windows remoting standard and the official WinRM variables and authentication guidance.
WinRM commands run in a non-interactive network-logon session. A command that succeeds in an interactive PowerShell window can therefore fail under Ansible because of permissions, profile differences, or a second network hop.
SSH preparation
Install Win32-OpenSSH Server, start the sshd service, configure the permitted shell and authentication methods, add the controller’s public key to the Windows account’s authorized keys, and open the SSH port in Windows Firewall. Test the service from Ubuntu with ordinary SSH before involving Ansible.
ssh -vvv WINDOWS_USER@192.0.2.20
GSSAPI/Kerberos requires Kerberos configuration on Ubuntu and matching Windows OpenSSH settings. The SSH plugin cannot obtain a Kerberos TGT from an explicit username/password combination; use the documented Kerberos workflow instead.
4. Create a PSRP inventory
Create inventory.yml. Store the password in Ansible Vault or an external secret store rather than committing it:
all:
children:
windows:
hosts:
win01:
ansible_host: 192.0.2.20
ansible_user: 'CONTOSO\\ansible'
ansible_password: '{{ vault_windows_password }}'
ansible_connection: psrp
ansible_psrp_auth: negotiate
ansible_psrp_cert_validation: ignore
ignore is useful for an initial lab test with a self-signed certificate. In production, issue a certificate trusted by the Ubuntu controller and validate it instead. Authentication values must match what the Windows listener permits. Common choices include Kerberos or Negotiate for domain environments and other methods documented by the PSRP plugin.
Create a vault file and encrypt it:
ansible-vault create group_vars/windows/vault.yml
# Add:
# vault_windows_password: 'replace-with-the-real-secret'
ansible-playbook -i inventory.yml --ask-vault-pass playbook.yml
5. Create a WinRM inventory
WinRM uses the ansible_winrm_* variable family. A minimal HTTPS example looks like this:
all:
children:
windows:
hosts:
win01:
ansible_host: 192.0.2.20
ansible_user: 'CONTOSO\\ansible'
ansible_password: '{{ vault_windows_password }}'
ansible_connection: winrm
ansible_port: 5986
ansible_winrm_transport: kerberos
ansible_winrm_server_cert_validation: validate
For a lab certificate, the corresponding validation setting can be relaxed temporarily, but disabling certificate validation should not become your normal production configuration. Do not treat disabled encryption as a fix for an authentication problem. Verify the transport, port, certificate name, and authentication protocol together.
6. Create an SSH inventory
all:
children:
windows:
hosts:
win01:
ansible_host: 192.0.2.20
ansible_user: ansible
ansible_connection: ssh
ansible_port: 22
ansible_ssh_private_key_file: ~/.ssh/windows_ansible_ed25519
ansible_shell_type: powershell
ansible_shell_type: powershell tells Ansible how to wrap commands for the Windows shell. If the server exposes a different configured shell, use the value required by that OpenSSH installation. Password authentication is possible when enabled by sshd, but keys are generally easier to rotate and audit.
7. Verify the connection in layers
Start with DNS and TCP reachability, then test the native transport, then use Ansible’s Windows module.
Check the port with cURL
For WinRM over HTTPS, this confirms that Ubuntu can reach the listener. It does not authenticate an Ansible session:
curl -vk --connect-timeout 10 https://192.0.2.20:5986/wsman
Check reachability with Python
python3 - <<'PY'
import socket
host = '192.0.2.20'
port = 5986
with socket.create_connection((host, port), timeout=10):
print(f'{host}:{port} is reachable')
PY
Check reachability with Node.js
node - <<'NODE'
const net = require('net');
const socket = net.createConnection({ host: '192.0.2.20', port: 5986, timeout: 10000 });
socket.on('connect', () => { console.log('192.0.2.20:5986 is reachable'); socket.end(); });
socket.on('timeout', () => { console.error('connection timed out'); socket.destroy(); process.exitCode = 1; });
socket.on('error', err => { console.error(err.message); process.exitCode = 1; });
NODE
Run Ansible’s Windows ping
ansible windows -i inventory.yml -m ansible.windows.win_ping --ask-vault-pass
A successful result means Ansible authenticated and executed a Windows module. Follow with a narrowly scoped command:
ansible windows -i inventory.yml -m ansible.windows.win_command -a 'whoami' --ask-vault-pass
For SSH, run ssh -vvv first, then repeat the win_ping command with the SSH inventory.
8. Run a first playbook safely
---
- name: Verify Windows management
hosts: windows
gather_facts: false
tasks:
- name: Confirm Ansible can use PowerShell
ansible.windows.win_ping:
- name: Display the account used by the session
ansible.windows.win_command: whoami
register: identity
- name: Print the account
ansible.builtin.debug:
var: identity.stdout
Use gather_facts: false while diagnosing connectivity. Once the connection is stable, enable facts only when the playbook needs them; this reduces startup work across many hosts.
9. Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
win_ping times out |
Firewall, wrong port, no listener, routing, or DNS issue. | Test the exact IP and port with curl or Python, inspect the Windows listener, and check firewall rules. |
| 401 or authentication failure | Wrong username format, disabled account, unsupported authentication method, or lockout. | Confirm the account, password, domain format, and enabled listener methods. Inspect the newest Windows Security event 4625 entry and its status/substatus codes. |
| Certificate validation error | Certificate name, trust chain, or expiration does not match. | Use a certificate trusted by Ubuntu and connect using its matching name. Relax validation only for a controlled lab test. |
| PSRP import or dependency error | pypsrp is missing or outside the supported range. |
Install pypsrp>=0.4.0,<1.0.0 in the same Python environment as Ansible. |
| SSH connection refused | sshd is stopped, the port is blocked, or the service listens on another port. |
Check the Windows service, firewall, configured port, and ordinary SSH from Ubuntu. |
| SSH permission denied | Wrong account, key permissions, missing authorized key, or disallowed authentication method. | Use ssh -vvv, verify the Windows account’s authorized keys and sshd_config, and confirm the key path in inventory. |
| Command works interactively but not in Ansible | WinRM uses a non-interactive network logon. | Check profile-dependent paths, local rights, environment variables, and whether the operation needs delegation. |
| Second network resource is inaccessible | Double-hop delegation is not configured. | Use an appropriate Kerberos or CredSSP delegation design, or stage the required file locally. Do not place reusable passwords in scripts. |
| Local administrator behaves unexpectedly | Local-account token filtering or missing logon rights. | Review the account’s rights and Windows security policy; test with a purpose-created automation account. |
Run with increased verbosity only while diagnosing:
ansible windows -i inventory.yml -m ansible.windows.win_ping -vvvv --ask-vault-pass
Verbose output can expose connection details, so avoid sharing logs that contain credentials, tokens, or private host information.
10. Security, performance, and reliability practices
- Secrets: Use Ansible Vault or an external secret manager. Keep passwords out of inventory committed to source control.
- Network exposure: Restrict WinRM and SSH ports to controller networks with firewalls. Prefer HTTPS for WinRM and secure SSH configuration.
- Least privilege: Use a dedicated account with only the rights required by the playbooks.
- Delegation: Design double-hop access explicitly. A successful first connection does not prove access to a second file share or database.
- Fact gathering: Disable it for connectivity checks and enable it only when needed.
- Concurrency: Start with a small batch using
serialin production playbooks, then increase gradually after measuring the controller and Windows hosts. - Retries: Add retries for transient service readiness, but do not hide persistent authentication or certificate errors with long retry loops.
- Version control: Pin Ansible and controller dependencies in a requirements file or managed execution environment so upgrades are deliberate.

Or skip the browser setup
If your next task is capturing a website for documentation, visual regression, or an agent workflow, ScreenshotNeo provides a single HTTP request instead of maintaining a browser controller. Cookie and consent banners are accepted and removed before the shot, along with more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and each response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server lets Claude, Cursor, and other MCP clients use take_screenshot, get_page_info, and capture_pdf.
See the ScreenshotNeo API documentation for all options:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
There are 1,000 screenshots per month free with no card. Paid plans start at $5 for 3,000 shots, and every feature is included on every plan. Create a free ScreenshotNeo account.
FAQ
Can Ubuntu manage Windows without installing Ansible on Windows?
Yes. Ubuntu is the control node; Windows needs the selected remoting service and the permissions required by your playbooks.
Is PSRP better than WinRM?
PSRP is the newer PowerShell-oriented plugin, while WinRM may fit an existing WinRM standard. Choose based on authentication, delegation, certificate, and operational requirements.
Can I use SSH for domain-joined Windows servers?
Yes, when Win32-OpenSSH and the required Kerberos/GSSAPI configuration are in place. Test ordinary SSH and document the authentication policy.
Why does a network share fail after win_ping succeeds?
The share may require a second network hop. Configure delegation deliberately or transfer credentials and files through a safer, explicit mechanism.
What should I check first when win_ping fails?
Check the host and port, then the listener, firewall, username format, authentication method, certificate policy, and Windows Security event 4625 details.