| inventory | ||
| roles/winrm_test/tasks | ||
| .gitignore | ||
| ansible.cfg | ||
| inventory-builder.yml | ||
| README.md | ||
| roles-winrm_test-tasks-main.yml | ||
| show_system_info.yml | ||
| test_winrm.yml | ||
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:
- 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
-
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
-
-
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
pwgenutility 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