How to Install Ansible and Manage Windows Server 2012 R2 with Ansible

Ansible is an agentless configuration management and automation platform that manages Windows hosts via WinRM (Windows Remote Management) rather than SSH. This makes it an excellent choice for automating Windows Server 2012 R2 deployments, as no software needs to be installed on the managed Windows node itself — only WinRM must be properly configured. The Ansible control node (which runs the Ansible engine) is typically a Linux machine, though Windows Subsystem for Linux can also be used. This tutorial covers configuring WS2012 R2 as an Ansible-managed node, installing Ansible on a Linux control node, and writing and running playbooks to manage the Windows server.

Prerequisites

  • Windows Server 2012 R2 target node with PowerShell 4.0
  • A Linux control node (Ubuntu, CentOS, or Debian) with Python 3.8+ and pip
  • Network connectivity between the control node and the Windows target on TCP port 5985 (WinRM HTTP) or 5986 (WinRM HTTPS)
  • A Windows administrator account or domain account with local admin rights on the target

Step 1: Configure WinRM on Windows Server 2012 R2

Ansible communicates with Windows hosts exclusively over WinRM. On the Windows target, run the Ansible-provided WinRM configuration script as a local administrator. Download and execute it from a PowerShell session:

Invoke-WebRequest -Uri "https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1" -OutFile "C:TempConfigureWinRM.ps1"
Set-ExecutionPolicy Bypass -Scope Process -Force
.C:TempConfigureWinRM.ps1

This script performs several operations: enables the WinRM service, creates an HTTPS listener with a self-signed certificate, opens firewall ports 5985 and 5986, and sets the authentication method to negotiate (NTLM/Kerberos). For air-gapped environments, copy the script to the server manually and run it locally.

Verify WinRM is listening correctly:

winrm enumerate winrm/config/listener
netstat -ano | findstr "5985|5986"

Enable Basic authentication if you will use local Windows accounts (less secure but simpler for testing):

winrm set winrm/config/service/auth '@{Basic="true"}'
winrm set winrm/config/service '@{AllowUnencrypted="false"}'

Step 2: Install Ansible on the Linux Control Node

On your Linux control node, install Ansible and the required Python WinRM libraries:

sudo apt-get update
sudo apt-get install -y python3 python3-pip
pip3 install ansible pywinrm requests-kerberos requests-ntlm

For Red Hat/CentOS-based control nodes:

sudo yum install -y python3 python3-pip
pip3 install ansible pywinrm

Verify Ansible installed correctly:

ansible --version

Step 3: Create the Ansible Inventory File

Create a project directory and an inventory file that defines your Windows Server 2012 R2 hosts:

mkdir ~/ansible-windows && cd ~/ansible-windows

cat > inventory.ini << 'EOF'
[windows_servers]
ws2012r2-01.domain.local

[windows_servers:vars]
ansible_user=Administrator
ansible_password=YourAdminPassword
ansible_connection=winrm
ansible_winrm_transport=ntlm
ansible_winrm_server_cert_validation=ignore
ansible_port=5986
ansible_winrm_scheme=https
EOF

For production use, store credentials in an Ansible Vault-encrypted file rather than in plain text in the inventory:

ansible-vault create group_vars/windows_servers/vault.yml
# Add these variables inside the vault:
# ansible_user: Administrator
# ansible_password: YourSecurePassword

Step 4: Test the WinRM Connection

Use the Ansible ping module for Windows (win_ping) to verify connectivity:

ansible windows_servers -i inventory.ini -m win_ping

A successful response looks like:

ws2012r2-01.domain.local | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

If you receive a connection error, confirm that WinRM is running on the target, the firewall allows the traffic, and that the credentials are correct.

Step 5: Write a Basic Windows Playbook

Create a playbook that performs common administrative tasks on the Windows server:

cat > windows_baseline.yml << 'EOF'
---
- name: Windows Server 2012 R2 Baseline Configuration
  hosts: windows_servers
  gather_facts: yes

  tasks:
    - name: Ensure Windows Firewall is enabled for all profiles
      win_firewall:
        state: enabled
        profiles:
          - Domain
          - Private
          - Public

    - name: Create standard directory structure
      win_file:
        path: "{{ item }}"
        state: directory
      loop:
        - C:Apps
        - C:Scripts
        - C:Logs

    - name: Set timezone to UTC
      win_timezone:
        timezone: UTC

    - name: Ensure Windows Update service is running
      win_service:
        name: wuauserv
        state: started
        start_mode: auto

    - name: Disable SMBv1
      win_regedit:
        path: HKLM:SYSTEMCurrentControlSetServicesLanmanServerParameters
        name: SMB1
        data: 0
        type: dword

    - name: Copy baseline configuration script
      win_copy:
        src: ./files/baseline.ps1
        dest: C:Scriptsbaseline.ps1

    - name: Run PowerShell command to collect system info
      win_shell: |
        $info = @{
          ComputerName = $env:COMPUTERNAME
          OSVersion = [System.Environment]::OSVersion.VersionString
          PSVersion = $PSVersionTable.PSVersion.ToString()
          AvailableRAM_GB = [Math]::Round((Get-WmiObject Win32_OperatingSystem).FreePhysicalMemory / 1MB, 2)
        }
        $info | ConvertTo-Json
      register: sysinfo

    - name: Show system info
      debug:
        msg: "{{ sysinfo.stdout }}"
EOF

Step 6: Install Windows Features with Ansible

The win_feature module manages Windows Server roles and features:

cat > install_iis.yml << 'EOF'
---
- name: Install and Configure IIS on WS2012 R2
  hosts: windows_servers
  tasks:
    - name: Install IIS Web Server role
      win_feature:
        name:
          - Web-Server
          - Web-Common-Http
          - Web-Static-Content
          - Web-Default-Doc
          - Web-Http-Errors
          - Web-Asp-Net45
          - Web-ISAPI-Ext
          - Web-ISAPI-Filter
          - Web-Mgmt-Console
        state: present
        include_management_tools: yes
      register: iis_install

    - name: Reboot if required after feature install
      win_reboot:
        reboot_timeout: 300
      when: iis_install.reboot_required

    - name: Ensure W3SVC service is started
      win_service:
        name: W3SVC
        state: started
        start_mode: auto
EOF

Run the playbook:

ansible-playbook -i inventory.ini install_iis.yml

Step 7: Use Ansible Roles for Reusable Configuration

For complex environments, structure your automation using Ansible roles. Create a basic role structure:

ansible-galaxy init roles/windows_baseline
# Creates:
# roles/windows_baseline/
#   tasks/main.yml
#   handlers/main.yml
#   vars/main.yml
#   defaults/main.yml
#   templates/
#   files/

Place task definitions in roles/windows_baseline/tasks/main.yml and reference the role from a site-level playbook. This approach enables you to manage dozens of Windows servers with consistent, versioned configuration.

Step 8: Schedule Recurring Ansible Runs

On the Linux control node, use cron to run playbooks on a schedule for drift remediation:

crontab -e
# Add: Run baseline enforcement every night at 2 AM
0 2 * * * cd /home/ansible/ansible-windows && ansible-playbook -i inventory.ini windows_baseline.yml >> /var/log/ansible-windows.log 2>&1

Summary

You have configured Windows Server 2012 R2 as an Ansible-managed node using WinRM, installed Ansible with WinRM support on a Linux control node, created an inventory file with secure credential storage, and written playbooks that install Windows features, configure the registry, manage services, and run PowerShell commands remotely. This Ansible foundation enables repeatable, auditable, idempotent configuration management across your entire WS2012 R2 server fleet without installing any agent software on the managed Windows machines.