But why, you may wonder? Let me explain the problem I'm currently facing and the rationale behind my chosen approach. One of my job responsibilities involves deploying software that exclusively operates on Windows. Performing this task manually is exceptionally tedious, to say the least. So, why not automate it, you might ask? The challenge lies in the fact that my work laptop runs on Windows, and I'm unable to create Linux-based VMs due to company policies. Unfortunately, Ansible doesn't function within Windows when running inside Hyper-V. Although I've contemplated using WSL, I wasn't in the mood for it, plus I have a preference for Docker. Consequently, I arrived at this solution after spending several days troubleshooting these issues. I hope this explanation proves beneficial to anyone encountering a similar predicament.
Pre-requisites
before beginning, make sure you configure the following:
- Hyper-V Virtual Switch
- WinRM on your Target
- Firewall Ports on your Target
Hyper-V Virtual Switch
Since we are working with Windows Hyper-V we need to set up the connection type for our VM to use.
Go to Actions → Virtual Switch Manager → New virtual network switch to create a new connection. Make sure to connect you LAN and select External Network also allow your OS to share the network adapter otherwise you won’t be able use internet on your host machine. 💡
Sharing your adapter whilst on WiFi might behave unexpectedly.
💡
Internal Network isolates your VM from the network. You won’t be able to communicate with it using your host machine. However, it is possible to ping/communicate through another VM running in the same hyper-V manager. Private Network is almost the same except it restricts communication from other devices that are not on the same network.
apply and save the changes. Select your target VM, make sure it is turned-off, right click and go to → settings, assign the virtual switch you just created.
log into your VM and open up powershell/cmd.
type ipconfig
and note the IPv4 address.
Now open up the shell on your host machine (if you are on the same network) and ping the ip-address. if you get the response, you’re good to go.
WinRM
WinRM stands for Windows Remote Management, which is a Microsoft technology that allows for remote management of Windows machines. It uses the open standard WS-Management protocol for secure and reliable communication between the machines. fire up the powershell and type:
if the service is not running the type: Start-Service -Name WinRM Now enable the remote connection by typing Enable-PSRemoting -Force
Chocolatey
Install the chocolatey package which is to be used by ansible to install the softwares.
Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Ports
check the following ports (5985, 5986) in your Windows defender firewall. These ports are required by ansible_winrm to communicate.
if you don’t see these ports then simply create a new Inbound rule for the given ports and enable it for all Profiles.
Host Setup
Now it’s time to set up the host node which will control your hyper-V VM. Before diving, make sure you have Docker installed. Create a new directory and cd into it. Create the following three files and leave them empty for now.
- Dockerfile
- inventory.ini
- playbook.yaml (ignore .gitignore, LICENSE and README for now)
Playbook
Ansible playbooks are files containing a series of tasks that can be executed in order to automate IT infrastructure configurations and deployment processes. Playbooks are written in YAML format and can be used to execute tasks on a single or multiple hosts, depending on the requirements. They are a powerful tool for automating complex tasks and can greatly simplify IT operations. Enter the following contents into the playbook and save it.
---
- name: Install Software on Windows VM
hosts: windows_vm
gather_facts: false
vars:
ansible_winrm_transport: ntlm
tasks:
- name: Install NeoVim
win_command: choco install neovim -y
- name: Install Git
win_command: choco install git -y
- name: Install Google Chrome
win_command: choco install googlechrome -y
Inventory
In Ansible, an inventory is a file or collection of files that defines the hosts and groups of hosts on which Ansible commands and playbooks will run. It serves as the source of truth for Ansible about the infrastructure it manages. The inventory can include details about hostnames, IP addresses, remote connection information, host grouping, and variables that provide essential context for executing automation tasks across a network.
Enter the following contents and save the file.
[windows_vm] <ip-address> ansible_user=<username> ansible_password=<password> ansible_connection=winrm ansible_winrm_server_cert_validation=ignore
In the context of Ansible and WinRM, ansible_winrm_transport is an Ansible variable that specifies the transport method used for communication between the Ansible control node and the Windows VM. The two common options for this variable are ntlm and kerberos.
- NTLM (NT LAN Manager): NTLM is a Microsoft authentication protocol that uses a challenge-response mechanism. It is an older authentication method and may be suitable for simpler setups or legacy systems.
- Kerberos: Kerberos is a network authentication protocol that provides strong security and is the recommended authentication method for WinRM communication. It relies on a trusted third-party authentication server and provides secure mutual authentication between the Ansible control node and the Windows VM. To determine which transport method to use, you should consider the configuration of your Windows VM and the authentication mechanisms supported by your environment. If your Windows VM is part of an Active Directory domain and Kerberos is configured, it is generally recommended to use Kerberos for more secure authentication. However, if you are working in a non-domain environment or have specific requirements, NTLM can be used as an alternative.
Dockerfile
open up your editor and enter the following contents into the file and save it. (You can also use the official ansible image)
Build your Dockerfile
Now its time to build and run your image. Enter the following command:
docker build -t <tag-name> .
You should see the following output:
Now run your image:
docker run <tag-name>
if successful, you should see the following output:
Go to your target VM and check the tools installed by running the following command:
choco list
Troubleshooting Errors
If you get the following error: “auth method ntlm requires a username”
then check if the
- service is running
- ports are opened
NTLM uses port 5986, check whether the WinRM Listener is listening on the required port.
open up the powershell and type
Get-WSManListener
Make sure the port is correct. If the port is correct then the issue might be the certificates NTLM requires. Run the following command:
New-Item -Path WSMan:\localhost\Listener -Transport HTTPS -Address * -CertificateThumbPrint thumbprint -Force
Replace thumbprint with the actual thumbprint from your certificate:
Get-ChildItem -Path Cert:\LocalMachine\My
If you only have the self-signed certificate (localhost) then just omit the -CertificateThumbPrint parameter otherwise it will result in an error.
New-Item -Path WSMan:\localhost\Listener -Transport HTTPS -Address * -Force
if Windows cannot find the certificate due to any issue then create a new one
$cert = New-SelfSignedCertificate -DnsName "localhost" -CertStoreLocation "cert:\LocalMachine\My"
Make a note of the thumbprint value displayed after this command:
$cert.Thumbprint
Finally, run this command to include the local certificate:
New-Item -Path WSMan:\localhost\Listener -Transport HTTPS -Address * -CertificateThumbPrint $cert.Thumbprint -Force
Restart the WinRM service to apply the changes:
Restart-Service -Name WinRM
Hopefully ansible works after this.
You can find the Dockerfile here: https://github.com/hamzza-K/docker-ansible-hyperV.git