DSC + Ansible

Edit: For detailed instructions on how to set up Ansible for managing Windows nodes, I’ve written a separate post which you can find here: http://hindenes.com/trondsworking/2015/02/21/megapost-getting-up-and-running-with-ansible-and-dsc/.

There is something to be said for running a single command and have a server (truckloads of servers) come to life in front of your eyes. Chef calls it “frigging delightful”, and I can’t argue with that.

2015-02-20 14_20_43-AnsibleNode - VMware Workstation

I’m a huge fan of Ansible, as you may already know. I’ve even made some humble contributions to the code relating to Ansible’s Windows support. Until now, Ansible and DSC have not been the best of friends, however. A reason for this is the way Ansible performs config management in an orchestrated way, which DSC didn’t lend itself to until recently. However, with the WMF5 February preview, Microsoft is fixing a lot of the issues that may have prevented toolmakers and ISVs to build solutions on top of DSC.

In other words, DSC has understood its place, and agreed to get out of the way. It should be an engine for config management tools to tap into, and with the newest preview, they’ve done just that.

This enabled me to sit down and chalk up a pretty basic Ansible module for DSC, called win_dsc5. This blog post is meant to get you started with it. I’ll add another post linked to my “Ansible+Windows” post which details how to set up your Windows node to be managed by Ansible. For now, there are some issues around WinRM and domain-joined computers, but they’re working on that – promise!

An ansible module is the basic building block for Ansible. You use Modules to create a play. These plays can be organized into roles or run directly. There’s a module for apt, there’s a module for win_feature (Windows Feature) and so on. Here’s the basic syntax for ad-hoc execution  against a group of servers:

ansible -i hosts -m win_feature -a "name=Web-Server,Web-Common-Http" all

I’m basically saying: take my hosts file (inventory) and run the win_feature against them, with these parameters. Inside a play the same module can be used in a more structured form:

- name: Install IIS
  hosts: all
  gather_facts: false
  tasks:
    - name: Install IIS
      win_feature:
        name: "Web-Server"
        state: absent
        restart: yes
        include_sub_features: yes
        include_management_tools: yes

As you can see, an Ansible module has a fixed set of arguments or properties. For the win_feature module, these are name, state, restart, and so on.

For my DSC module, this proposed an interesting problem: I didn’t want to hardcode the properties, I just wanted to pass whatever properties the DSC resource needed. So, instead of hardcoding them I created a set of rules instead:

1. Any parameter the DSC resource expects can be used in the Ansible module, with the following exceptions:

2. If DSC expects a “name” property, the corresponding ansible property will be item_name.

3. If DSC expects a Pscredential property such as “DomainUser” two properties are used in the Ansible implementation, “DomainUser_username” and “DomainUser_password”. This is done to enable credential representation in the json stream Ansible uses when it talks to a managed node.

4. If Ansible expects an array of strings ([string[]]), pass these as comma-separated values inside a quotation block, such as “MyProp1, MyProp2”.

With that taken care of, lets look at a few of these puppies:

For the “Package” resource, we can look at the valid properties:

Get-DscResource -Name Package | select -ExpandProperty Properties

So, the package resource expects parameters such as Name, ProductId, Arguments and so on. The Ansible representation of the “package” module would be something like:

- name: Make sure the mySqlServer installer package is present
  win_dsc5:
    resource_name="Package"
    Path="http://dev.mysql.com/get/Downloads/MySQLInstaller/mysql-installer-community-5.6.17.0.msi"
    ProductId="{437AC169-780B-47A9-86F6-14D43C8F596B}"
    item_name="MySQL Installer"

The resource_name is used to tell DSC what type of resource to use (which needs to exist on the target computer beforehand for now). All other parameters in the example are the same, except for the “Name” parameter which we replace with “item_name” in Ansible.

So, these statements make up a play, which is typically put in a role. From there you use a “playbook” to create the “link” from your inventory (node list) to Roles. There’s tons of flexibility in terms of variables, ifs, looping and so on, and I’ll update my examples to show some of this flexibility. For now, I’ve uploaded my dsc module along with some example roles (which actually create a working wordpress install, although with some caveats) in this Github repo:

https://github.com/trondhindenes/Ansible-win_dsc

Go configure something!