How to deploy services with Ansible on both Windows and Linux ?

If someday you have to deploy an infrastructure composed of both Linux and Windows hosts, you may very well be in trouble with either one or the other, since people that are familiar with both are rare. Well, fortunately, Ansible is a DevOps tool that doesn’t care and will help you with both.

What Ansible can do for you

Our use case:

First, let’s talk about the deployment architecture we actually want to have.
Please keep in mind that this is just an example of how to use Ansible connectors, which I think will help you set up your own deployment.

The following figure shows:

  • A machine that can initiate a deployment
  • Machines to be deployed
  • How they are connected

ansible

The idea here is to be able to administrate the deployment of all hosts from the same tool (Ansible) and the same var files (Ansible inventory).

 

The tricky part

The tricky part here is that, as of today, Windows and Linux remain two very different OSs, with a lot of specificities. In particular :

  • They traditionally use different remote access protocols
    • Linux: SSH
    • Windows: PSRP / WinRM

  • They are not structured the same way, therefore Ansible module made for Linux-like OSs are generally not compatible with Windows

  • They do not offer the same command line interpreters as a workaround when no Ansible module exists to implement your use case
    • Linux: Bash, Zsh, etc
    • Windows: PowerShell

How we can solve this : Ansible connectors

Our problem here is that we need to tell Ansible :

  • To connect to Linux using the SSH protocol
  • To connect to Windows using the PSRP protocol (PowerShell Remoting Protocol)

Well, Ansible has a mechanism to deal with heterogeneous targets connections: Ansible connectors.
Connectors are libraries, often written in Python, that Ansible will use to establish a connection to the target host.
The best part here is that Ansible handles the choice of the connector as well as the per-host configuration of a connector with its inventory. This means that you can use all of the power that Ansible gives you to customize the Ansible connector to use for a host or a group of hosts.

In our case we will use the following connectors:

Note: For windows hosts, we could also use the WinRM Ansible connector. In this case, I chose the PSRP connector instead because it offers the possibility to use a SOCKS5 proxy, which is really helpful to manage Windows hosts that are located behind a bastion.

 

Network considerations

Since the machine that will use Ansible need access to all targets, you need to ensure its ability to establish :

  • Connections to Linux machines with SSH
  • Connections to Windows machines with PSRP over WinRM

If you have to go through a bastion host located between your deployment machines and your targets, you can use for example:

In both case, and whatever your tunneling / proxying implementation is, you will have to carefully configure your network to accept the appropriate traffic:

  • Between the deployment machine and the bastion
  • Between the bastion and the target

And this configuration may differ on Linux and Windows hosts.

Connect Ansible to both Windows and Linux hosts

We will use the following inventory.yaml file: 

And we will create the following files and directories:

Set up Linux targets

Add the following variables to the ./group_vars/connector-linux.yaml

Set up Windows targets

Add the following variables to the ./group_vars/connector-windows.yaml

Note 1: Here we use WinRM basic authentication without message encryption because it is the easiest to set up for an example. But for production environments, it’s highly recommended to use stronger authentication mechanisms such as Kerberos.

Note 2: The user and password params are in the classic configuration file because it’s easier to show in this article, but it’s highly recommended to store them in an ansible-vault file instead.

 

Launch a test command on both kinds of targets

First of all, you can check that all your targets were defined as required, with the following command:

$ ansible-inventory -i hosts.yaml --list

If your inventory is to big for this output to be useful, you can also display variables host by host:

$ ansible-inventory -i hosts.yaml --host vm-linux-1

And since the output will be in json, you can use jq to quickly filter it to get the information you want.

Then, to check usability of Linux targets:

$ ansible -i hosts.yaml -l vm-linux-1 -m ping

And for Windows targets:

$ ansible -i hosts.yaml -l vm-windows-1 -m win_ping

 

Note 1: the ping and win_ping Ansible modules check that the target is reachable, and that it will be able to handle Ansible actions (e.g. meaning on Linux targets that it checks the availability of Python). So it’s not a network ping (as would be performed by Linux ping command for example), it really is an Ansible-like implementation of a ping.

Note 2: As you can see, because Linux and Windows are different, they require different implementations of the ping Ansible module, hence the use of “ping” and “win_ping” in the commande above.

 

Writing a playbook

Now that you can manage connections to both Linux and Windows targets, you will want to perform actions on them.
Although you can put all of the actions in the same playbook, regardless of the OS of the target, I think that in most cases it’s easier to put Windows and Linux tasks in separate playbooks.

For Linux targets, you can use:

  • Classic Ansible modules, like “file”, or “template”
  • Custom actions using the shell and command modules

And for Windows targets, you can use:

  • Ansible modules that have the “win_” prefix, like “win_template”
  • DSC configurations using the Ansible win_dsc module
  • Custom actions using the win_shell module

It’s quite simple.

If you know how to deal with both Linux and Windows configuration, then you should easily succeed in using Ansible to configure both kinds of hosts.

Going further with connectors

 

In this article, we used two specific Ansible connectors: SSH and PSRP.
But you can use the following command to list all connectors that are available in your environment:

$ ansible-doc -t connection -l

If I run this command on my computer, I find out that I have as many as 26 available connectors: Local, chroot, lxc, lxd, Docker, buildah, podman, netconf, …
This means that Ansible can help you in many heterogeneous deployment scenarios.
But each time you use a different connector, don’t forget to check what Ansible modules are applicable to the targeted environment. (E.g. netconf_config module for netconf connector).

 

We saw in this article that you can quickly configure Ansible to deploy and configure different kinds of targets (e.g. Linux and Windows targets) through the use of connectors that let you customize both the protocols and authentication methods.

What do you think? Leave your comments here !