Windows Automation with Ansible: Getting Started Guide

14 minute read

ansible_2plus_signcropped-Windows-logo1

Overview

In this article we will focus on how to get started with automation of windows using Ansible. Specifically we will look at installing 3rd party software and OS updates. Automation is the basis for cloud-computing or cloud-native patterns and breeds a culture of innovation. Let's face it, we cannot innovate, if we are stuck doing mundane tasks and manual labor. Ansible has revolutionized automation because until Ansible, automating was rather complicated and required lots of domain knowledge. Ansible provides an automation language that the entire organization can use because it is so easy and so flexible. The best thing about Ansible though it it brings teams together and fosters a devops culture. It brings the Linux and Windows world together!

How Ansible Works

Ansible provides a runtime for executing playbooks. A playbook is simply a group of plays which are essentially steps executed in order. A play is one or more tasks. A task is simply the execution of an Ansible module. An Ansible module is python code that does something, like install a software package, update the system, change a configuration file, check if something is set correctly, etc. There are 1000s of Ansible modules and a huge community around it.

In order to run a playbook against hosts you need an inventory which is simply a grouping of hosts and host vars (parameters). That is about it for the basics. Below is a diagram showing the Ansible automation engine architecture.

ansible_arch

This guide assumes you know some Ansible or are familar with the basics. If not I created an Ansible getting started guide here. I highly recommend giving it a look before proceeding.

Prerequisites

In order for a windows host to be managed by Ansible there are a few prerequisites

  • Powershell version 3.0 or higher (Should be fine with Windows 2012 or higher)
  • .NET Framework 4.0 or higher (should be fine with Windows 2012 or higher)
  • Windows Remote Management Listener or SSH (cygwin)
  • Windows 7, 8.1, and 10, and server OSs including Windows Server 2008, 2008 R2, 2012, 2012 R2, 2016, and 2019
  • Chocolatey for installing 3rd party software
  • WSUS for updating OS packages and patching

Install Chocalatey and WSUS

There are various tools that can be used. I recommend using Chocolatey for installing packages and WSUS for OS updates/patching.

Install Chocalately

Using powershell we can install chocalately.

PS C:\windows\system32> Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

Install WSUS

Open server manager. In the top right under manage you can add or change roles.

server_manager

Under server roles select Windows Service Update Services. This will install WSUS. Once installed there will be a WSUS option in server manager. You can select it and configure what packages should be updated, etc.

wsus

Open WSUS and check that the computer is showing up under 'All Computers'. If not and you are running outside of domain you may need the following fix.

c:\> net stop wuauserv
c:\> regsvr32 /s wuapi.dll
c:\> regsvr32 /s wups.dll
c:\> regsvr32 /s wuaueng.dll
c:\> regsvr32 /s wucltui.dll
c:\> regsvr32 /s msxml3.dll
c:\> cd %windir%\SoftwareDistribution
c:\> rd /s/q DataStore
c:\> mkdir DataStore
c:\> rd /s/q Download
c:\> mkdir Download
c:\> net start wuauserv
c:\> reg delete HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate /v AccountDomainSid /f
c:\> reg delete HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate /v PingID /f
c:\> reg delete HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate /v SusClientId /f
c:\> wuauclt /resetauthorization
c:\> wuauclt /detectnow
c:\> wuauclt /reportnow

Configuring Windows Remote Management for Ansible

Ansible requires no agents. It uses what the OS provides for communication. In Windows you can use SSH or Windows Remote Management (WinRM). Since SSH is more or less bolted on, WinRM is usually the preferred choice.

Test to ensure WinRM is working

First install WinRM if it isn't installed. Execute the hostname command through WinRM.

PS C:\windows\system32> winrs -r:http://:5985/wsman -u: -p: hostname
win2012

Optionally you can also test WinRM by making a remote desktop connection from another windows host.

Note: you may run into an issue where you get an authentication error and a CredSSL issue if you have an older windows version. This can also happen if you don't have a valid SSL certificate. If you run into this issue, update your registry.

c:\>reg add "HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\CredSSP\Parameters" /f /v AllowEncryptionOracle /t REG_DWORD /d 2

Enable basic auth

There are several authentication methods. In this example we will use basic authentication which is username/password. This is of course the least secure method.

PS C:\> Set-Item -Path WSMan:\localhost\Service\Auth\Basic -Value $true

Update WinRM 

A script is provided by Ansible community to check WinRM and make necessary changes to allow Ansible to connect. In this case we are using basic authentication but likely you will want to use something more secure. The script can be found here and other authentication options are documented in the script header.

Open a powershell command prompt.

Store URL path to script.

PS C:\> $url = "https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1"

Store location for the script

PS C:\> $file = "$env:temp\ConfigureRemotingForAnsible.ps1"

Download script and output to file locally.

PS C:\> (New-Object -TypeName System.Net.WebClient).DownloadFile($url, $file)

Execute the script.

PS C:\> powershell.exe -ExecutionPolicy ByPass -File $file
Ok.

Check WinRM connection

PS C:\windows\system32> winrm enumerate winrm/config/Listener
Listener
Address = *
Transport = HTTP
Port = 5985
Hostname
Enabled = true
URLPrefix = wsman
CertificateThumbprint
ListeningOn = 10.10.1.139, 127.0.0.1, ::1, fe80::5efe:10.10.1.139%22, fe80::8155:327a:aeaf:365a%12

Listener
Address = *
Transport = HTTPS
Port = 5986
Hostname
Enabled = true
URLPrefix = wsman
CertificateThumbprint = 7a38de2c212764a54de106dc756f7cbc275156a3
ListeningOn = 10.10.1.139, 127.0.0.1, ::1, fe80::5efe:10.10.1.139%22, fe80::8155:327a:aeaf:365a%12

Ensure the HTTP/HTTPS ports are open.

More details about WinRM setup and how to setup a listener manually are documented here. In this case I used the default listener configured by WinRM.

Create Inventory File

We will create an inventory file with just a single host in a group called windows. From here on out we will be working on a Linux server where we have Ansible installed.

Create Ansible inventory file

$ vi inventory
[windows]
138.204.12.111

Test ansible connection

Using the inventory file we can test if Ansible can communicate with our windows server.

$ ansible -i ../inventory windows -m win_ping -e ansible_connection=winrm \
-e ansible_user=Admin -e ansible_password=<password> \
-e ansible_winrm_transport=basic \
-e ansible_winrm_server_cert_validation=ignore

138.201.147.202 | SUCCESS => {
"changed": false,
"ping": "pong"
}

Windows Patch Management

Now that Ansible is working with WinRM we can automate. In this case we will automate software package installation and updates.

Playbook and roles are available here.

Create Playbook

We haven't talked about roles. In Ansible roles are how we make playbooks reusable. It is always good practice to create roles. A role essentially allows you to organize Ansible plays and their dependencies together allowing them to be consumed easily. In order to use roles you need to create a certain directory structure and hierarchy. Create a playbook that imports our roles.

$ vi windows_baseline.yaml
---
- name: Windows Baseline
  hosts: ""
  connection: winrm
  gather_facts: true
  vars:
    ansible_user: ""
    ansible_password: ""
    ansible_connection: winrm 
    ansible_winrm_transport: basic 
    ansible_winrm_server_cert_validation: ignore

  tasks:
    - name: Install Baseline Packages
      include_role:
        name: install

    - name: Perform Updates
      include_role:
        name: updates

Here we are setting hosts, ansible user and password as variables. These inputs must be provided when executing the playbook. Hosts should be the name of our host group from our inventory file. This playbook has two tasks, a role to install packages and a role to do an OS update.

Create Ansible install role

At a minimum your role will need a tasks directory

$ mkdir -p roles/install/tasks

Create a task to install git using the chocalatey module.

$ vi roles/install/tasks/main.yaml
---
- name: Install Git
  win_chocolatey:
    name: git
    state: present

Create Ansible patch update role

At a minimum your role will need a tasks directory

$ mkdir -p roles/updates/tasks

Create a task to perform an OS update.

vi roles/updates/tasks/main.yaml
---
- name: Update windows packages
  win_updates:
    category_names:
      - CriticalUpdates
      - SecurityUpdates
    reboot: yes
    reboot_timeout: 500

Run playbook using inventory

The '-vvvvv' allows the playbook to run in debug mode for maximum verbosity.

$ ansible-playbook -i ./inventory -e target=windows -e user=Admin -e password= windows_baseline.yaml
PLAY [Windows Baseline] ****************************************************************************

TASK [Gathering Facts] *****************************************************************************
ok: [138.201.147.202]

TASK [Install Baseline Packages] *******************************************************************

TASK [install : Install Git] ***********************************************************************
ok: [138.201.147.202]

TASK [Perform Updates] *****************************************************************************

TASK [updates : Update windows packages] ***********************************************************
changed: [138.201.147.202]

PLAY RECAP *****************************************************************************************
138.201.147.202            : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=

Here are some additional examples of windows playbooks that may be of interest on your journey.

Summary

In this article we discussed the value of automation and why it is just a game changer. We provided a step-by-step on preparing a windows host for Ansible. Finally using the Ansible automation language, showed how to use native windows tooling to install and update OS patches. Hopefully this will provide a good starting point for a journey into windows automation with Ansible.

(c) 2020 Keith Tenzer