Basic Ansible project showing how to use WinRM to connect to a Windows host.
Find a file
2026-05-25 18:59:06 -07:00
inventory remove tracked file from remote 2026-05-25 18:59:06 -07:00
roles/winrm_test/tasks fixed multiple things 2026-05-23 23:10:17 -07:00
.gitignore update gitignore 2026-05-25 18:54:53 -07:00
ansible.cfg first commit 2026-05-23 22:59:01 -07:00
inventory-builder.yml fixed multiple things 2026-05-23 23:10:17 -07:00
README.md update README 2026-05-25 18:46:19 -07:00
roles-winrm_test-tasks-main.yml first commit 2026-05-23 22:59:01 -07:00
show_system_info.yml fixed multiple things 2026-05-23 23:10:17 -07:00
test_winrm.yml fixed multiple things 2026-05-23 23:10:17 -07:00

Ansible Hyper-V WinRM Test Project

Basic Ansible project to verify connection to a Windows Hyper-V host.

For the connection is uses:

  • WinRM transport via HTTPS port 5986
  • NTLM protocol
ansible_connection: winrm
ansible_port: 5986
ansible_winrm_transport: ntlm
ansible_winrm_scheme: https
ansible_winrm_server_cert_validation: ignore

NOTE: If the Hyper-V host is domain-joined, then it is preferable to use Kerberos with a configuration like:

ansible_connection:                   psrp
ansible_winrm_transport:              kerberos
ansible_psrp_auth:                    kerberos
ansible_psrp_cert_validation:         ignore
ansible_psrp_server_cert_validation:  ignore
ansible_psrp_negotiate_delegate:      true
ansible_psrp_message_encryption:      always

or use CredSSP:

ansible_connection:       winrm
ansible_winrm_server_cert_validation: ignore
ansible_winrm_transport:  credssp

This project was tested on an Ansible controller in a WSL Linux container on the same host as the Hyper-V hypervisor where the WSL container is configured to use mirrored networking.

Thus in the inventory: ansible_host: 127.0.0.1 is used.

For WSL in NAT mode the primary IP address of the host should be used instead.

This project contains three playbooks:

  1. test_winrm.yml
  • performs a basic connectivity test

  • run with: uv run ansible-playbook -i inventory/hosts.yml test_winrm.yml \ --vault-password-file .vault_pass.txt

  1. show_system_info.yml

    • show some basic system information about the Hyper-V host

    • run with: uv run ansible-playbook -i inventory/hosts.yml show_system_info.yml \ --vault-password-file .vault_pass.txt

  2. inventory-builder.yml

    • build an inventory .json

    • run with: uv run ansible-playbook -i inventory/hosts.yml inventory-builder.yml \ --vault-password-file .vault_pass.txt

Project Setup

The project depends on a Python virtual environment. It's recommended to use uv which is extremely fast and easy to use.

Once uv is installed:

uv init ansible-hyperv-winrm
cd ansible-hyperv-winrm
uv add ansible pywinrm

# test
uv run ansible --version

Vault configuration

  • requires the pwgen utility to be installed
# Generate vault password
pwgen -s 28 1 > .vault_pass.txt

uv run ansible-vault create host_vars/baccharis/vault.yml --vault-password-file .vault_pass.txt

ansible-vault edit host_vars/baccharis/vault.yml --vault-password-file .vault_pass.txt

uv run ansible-inventory -i inventory/hosts.yml --graph

WinRM Basics

  • management protocol used by Windows to remotely communicate with another server
  • uses SOAP-based WSMAN, or Web Services Management protocol
  • communicates over HTTP/HTTPS
  • included in all recent Windows operating systems
winrm get winrm/config

# get listener info
winrm enumerate winrm/config/listener

# list cert thumbprints
dir cert:\\LocalMachine\My | ft -AutoSize

# check for existing firewall rules
Get-NetFirewallPortFilter | Where-Object { $_.LocalPort -eq 5985 -or $_.LocalPort -eq 5986 } | Get-NetFirewallRule
Get-NetFirewallRule | Where-Object {$_.Name -like "*WinRM*"}

# list trusted hosts
Get-Item WSMan:\localhost\Client\TrustedHosts

# add trusted host, use -Concatenate to not overwrite existing
Set-Item WSMan:\localhost\Client\TrustedHosts -Value 'host1,host2' -Concatenate

# get trusted hosts
Get-Item WSMan:\localhost\Client\TrustedHosts | ft -AutoSize

# remove trusted host
$trustedHosts = (Get-Item WSMan:\localhost\Client\TrustedHosts).Value -split ','
$hostsToRemove = @('192.168.12.6')
$updatedList = $trustedHostsList | Where-Object { $hostsToRemove -notcontains $_ }
Set-Item -Path WSMan:\localhost\Client\TrustedHosts -Value ($updatedList -join ',') -Force


## Enable WinRM service and set up HTTP listener
Enable-PSRemoting -Force

# Open port 5985 for all profiles
$firewallParams = @{
    Action      = 'Allow'
    Description = 'Inbound rule for Windows Remote Management via WS-Management. [TCP 5985]'
    Direction   = 'Inbound'
    DisplayName = 'Windows Remote Management (HTTP-In)'
    LocalPort   = 5985
    Profile     = 'Any'
    Protocol    = 'TCP'
}
New-NetFirewallRule @firewallParams


# Allow local user accounts to be used with WinRM
# This can be ignored if using domain accounts
$tokenFilterParams = @{
    Path         = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
    Name         = 'LocalAccountTokenFilterPolicy'
    Value        = 1
    PropertyType = 'DWORD'
    Force        = $true
    }
New-ItemProperty @tokenFilterParams


## add HTTPS listener
# Create self signed certificate
$certParams = @{
    CertStoreLocation = 'Cert:\LocalMachine\My'
    DnsName           = $env:COMPUTERNAME
    NotAfter          = (Get-Date).AddYears(1)
    Provider          = 'Microsoft Software Key Storage Provider'
    Subject           = "CN=$env:COMPUTERNAME"
}
$cert = New-SelfSignedCertificate @certParams

# Create HTTPS listener
$httpsParams = @{
    Path                  = 'WSMan:\localhost\Listener'
    Address               = '*'
    CertificateThumbprint = $cert.Thumbprint
    Enabled               = $true
    Port                  = 5986
    Transport             = 'HTTPS'
    Force                 = $true
}
New-Item @httpsParams


# Open port 5986 for all profiles
$firewallParams = @{
    Action      = 'Allow'
    Description = 'Inbound rule for Windows Remote Management via WS-Management. [TCP 5986]'
    Direction   = 'Inbound'
    DisplayName = 'Windows Remote Management (HTTPS-In)'
    LocalPort   = 5986
    Profile     = 'Any'
    Protocol    = 'TCP'
}
New-NetFirewallRule @firewallParams