Published: August 08, 2018
Updated: January 30, 2019
Tags: Ansible
3 min read

Running Ansible Playbook On Windows And Linux Hosts In The Same Play

Ansible has no native way to execute on remote Windows and Linux hosts in the same play

Tested on Ansible version 2.4.3.0

Preface

Ansible was not designed in a way that allows managing different operating systems in the same play, but in some cases for convenience sake there might be a need to manage both Windows and Linux hosts in the same play (personal example - playbook which manages backup scripts across a datacenter without having the knowledge of which operating system is installed on the host).

In order to manage both operating systems types we use Ansible's group feature [1].
Configuring Ansible to manage Windows hosts is outside of the scope of this post, please refer to the official Ansible Documentation [2].

Before continuing, make sure you configured:

  • Ansible host that can query Windows hosts using DNS and authenticate with WinRM (WinRM relies on proper DNS)
  • Ansible host that can reach Linux hosts using SSH
  • WinRM on Windows hosts [3]
  • Dedicated Ansible group for Windows Hosts [4] (Or configure it during play in-memory)

By default, Ansible attempts to communicate with hosts via SSH, so we need to create an Ansible group (either in memory or in an inventory file) to set the connection to WinRM HTTP/S.

Once everything is configured, the easiest (but not the most accurate) way to attempt to discover the operating system of a host is by checking which port is accessible on the host.

Example

Example of a playbook which attempts to discover the remote hosts' operating system by attempting to connect the operating's system management protocol port. Assuming we pass a variable named input_hosts containing a list of the remote hosts.

# We execute the tasks on our Ansible host to check if it can reach the remote hosts
- hosts: localhost
  # Don't gather facts about localhost
  gather_facts: false
  tasks:
  # Check port 22(SSH) for connectivity
  - name: Attempt to connect via SSH
    # Module which attempts to open socket
    wait_for:
      host: {{ item }}
      # Attempt to connect to port 22
      port: 22
      # Timeout after 3 seconds
      timeout: 3
    # Ignore errors occured in the run
    ignore_error: true
    # Assign the result to a variable
    register: ssh_connectivity
    # Itterate over the variable input_hosts
    with_items: {{ input_hosts }}

  # Check port 5985(WinRM HTTP)/5986(WinRM HTTPS) for connectivity
  - name: Attempt to connect via WinRM
    # Module which attempts to open socket
    wait_for:
      host: {{ item }}
      # Attempt to connect to port 5985
      port: 5985
      timeout: 3
    ignore_error: true
    register: winrm_connectivity
    with_items: "{{ input_hosts }}"

  - name: Populate Linux hosts group
    # Add host to Ansible's in-memory group
    add_host:
      name: "{{ item.item }}"
      groups: Linux
    # Add to group only if succeded to connect to port 22
    when: item.state = "started"
    # Itterate over connectivity results
    with_items: "{{ ssh_connectivity.results }}"

  - name: Populate Windows hosts group
    # You can configure neccesary parameters for the Windows group here instead of in an inventory file
    add_host:
      name: "{{ item.item }}"
      groups: Windows
    when: item.state = "started"
    with_items: "{{ winrm_connectivity.results }}"

- hosts: Linux
  ......play tasks.......

- hosts: Windows
  ......play tasks.......

It's possible to consolidate those tasks into a role so they could be invoked and reused in an efficient way.

Summary

There are a few downsides to using this approach:

  • Relying on the management protocol port connectivity is not a reliable way to discover remote operating system since Windows hosts could also use SSH to be managed remotely, in that case the play will add the Windows host to the Linux group but probably will fail to authenticate.
  • You can't execute tasks in parallel on both types of operating systems.
  • More time-consuming compared to running a dedicated play tailored for specific operating system.

In conclusion, it's not always the most efficient way to attempt to manage Windows and Linux hosts with the same playbook, it depends on your use cases.


  1. Ansible documentation - Working with Inventory ↩︎

  2. Ansible Documentation ↩︎

  3. Ansible documentation - Setting up a Windows host ↩︎

  4. Ansible documentation - Windows Remote Management ↩︎