vkhitrin.com

Technology And Ramblings

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



Tags: #Ansible

Note

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

Verified

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.
Still, in some cases, for convenience’s 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 knowing which operating system is installed on the host).

To manage both operating system types, we use Ansible’s group feature.
Configuring Ansible to manage Windows hosts is outside this post’s scope. Please refer to the official Ansible documentation.

Before continuing, make sure you configured the following:

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 discover a host’s operating system is by checking which port is accessible on the host.

Example

Example of a playbook that attempts to discover the remote hosts’ operating system by attempting to connect the operating 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 they 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 that occurred 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 succeeded in connecting to port 22
    when: item.state = "started"
    # Itterate over connectivity results
    with_items: "{{ ssh_connectivity.results }}"

  - name: Populate Windows hosts group
    # You can configure the necessary 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 can be invoked and reused efficiently.

Summary

There are a few downsides to using this approach:

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.

↑ Top