This is an example of how to create a Ansible Windows Module. This example how to add an AD User to a AD Group on a Windows 2012 Server in an idempotent way.

Requirements

Ansible Version

Modules work from version 2.0 to version 2.2

WinRM Authentication

Like SSH for Linux, WinRM allows you log into a Microsoft Windows Server host. Read the Ansible Windows Support page for more details on how to set this up. One thing though that WinRM ansible support doesn't cover is using Certificate based Authentication instead of password based authentication. I would like to experiment with this and possibly blog about this in the future.

PowerShell

Ansible uses PowerShell to program the server, much in the same way Python is used to program a Linux host. Windows Server 2012 and higher have PowerShell enabled by default. Only thing you need to be sure is that the user running PowerShell scripts has permission to do so. Checkout the Ansible Windows Support page for more details.

Create the Module

Powershell Part

Begin with this simple example to get you started. This Gist shows the ".ps1" part.

#!powershell

Copyright 2016, Stanley Karunditu <stanley@linuxsimba.com>

Ansible is free software: you can redistribute it and/or modify

it under the terms of the GNU General Public License as published by

the Free Software Foundation, either version 3 of the License, or

(at your option) any later version.

Ansible is distributed in the hope that it will be useful,

but WITHOUT ANY WARRANTY; without even the implied warranty of

MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

GNU General Public License for more details.

You should have received a copy of the GNU General Public License

along with Ansible. If not, see <http://www.gnu.org/licenses/>.

Not quite sure what these do..but I see it in all powershell examples, so just leave it here.

WANT_JSON

POWERSHELL_COMMON

Ansible Windows PowerShell Template

Example function called at the end of this script

This function checks AD group membership before adding a new user to a group

If the user already exists, then update the "msg" field in the Ansible JSON string

to say why it failed.

function GroupMembership ($member, $groupname) { # Check if the $member is a user or group # If not fail. $membertype = 'user' # Check if the member exists $memberisuser = get-aduser -filter {(name -eq $member)} if ($memberisuser -eq $null) { $membertype = 'group' $memberisgroup = get-adgroup -filter {(name -eq $member)} if ( $memberisgroup -eq $null ) { Fail-Json $result "member $member is not a user or group" } }

# Check if Group to add the member to exists. If it does # not exist then fail. $foundgroup = get-adgroup -filter {(name -eq $groupname)}

if ($foundgroup) { $groupname = $foundgroup.name } else { Fail-Json $result "Group $groupname.name does not exist. Cannot proceed with group membership" }

$gmembers = Get-AdGroupMember -Identity "$groupname" | Select -ExpandProperty Name

Set-Attr $result "group-members" $gmembers

if ($state -eq 'present') { # If the state is present and the member is in the group # no change. otherwise add the member to the group and # change the $result.changed var. if ($gmembers -contains $member) { $result.msg = "Member $member already exist in Group $groupname" } else { Add-ADGroupMember "$groupname" "$member" $result.msg = "$membertype $member added to group $groupname" $result.changed = $true }

} else {

if ($gmembers -contains $member){
  Remove-ADGroupMember &quot;$groupname&quot; &quot;$member&quot; -confirm:$false
  $result.changed = $true
  $result.msg = &quot;Removed $member from Group $groupname&quot;
} else {
  $result.msg = &quot;Member $member is already deleted from Group $groupname&quot;
}

}

}

Parse the Keywords from the ansible module call

Example: if the module command looks like this

----

ansible -m mywinmodule command -a "groups=['g1','g2']" windowshost

----

Then Parse-Args $args attributes like this

groups: ['g1',g2']

$params = Parse-Args $args;

Create a new object. Call it result. This will store the information you want to print

out in the json result string when the module exits. By default its a good idea to add

the "changed" and "msg" attributes.

When "changed" == "false" it means that no action was taken to change the system. It is also

a good idea to update the "msg" attribute and mention why no change occurred.

$result = New-Object psobject; Set-Attr $result "changed" $false; Set-Attr $result "msg" "";

Extract each attribute into a variable. During this stage you can perform various checks.

1. Like checking if the attribute is empty. If it is, then exit the module with a failure

$groupname = Get-Attr $params "group" -failifempty $true

2. Check if the attribute is of a certain type like Array or String. If not then fail.

$members = Get-Attr $params "members" -failifempty $true if ($members -is [System.String]) { [string[]]$members = $members.Split(",") } elseif ($members -isnot [System.Collections.IList]) { Fail-Json $result "members must be a string or array" }

3. Set default state of an attribute. In this case the "state" attribute is set to "present" if not set.

$state = Get-Attr $params "state" "present" $state = $state.ToString().ToLower() If (($state -ne "present") -and ($state -ne "absent")) { Fail-Json $result "state is '$state'; must be 'present' or 'absent'" }

Think about performing some idempotence tests. In this case it checks to see if Active directory is running.

try { $domain = get-addomain } catch { $errormsg = $_.Exception.Message Fail-Json $result "AD Domain not active: $errormsg" }

If something needs to change, go ahead and attempt to execute the change

In this case it executes a PowerShell function that adds a member to a group

$members = $members | ForEach { ([string]$).Trim() } | Where { $ } foreach ($member in $members) { GroupMembership -member $member -groupname $groupname }

Finally always return the $result attribute. This will have the "changed", "msg" and any other attribute

you want to add to the JSON output string

Exit-Json $result;

Documentation Part

Next create a sister document that is a ".py". It will contain the Ansible documentation part of the module.

#!/usr/bin/python

-- coding: utf-8 --

(c) 2016, Stanley Karunditu <stanley@linuxsimba.com>

This file is part of Ansible

Ansible is free software: you can redistribute it and/or modify

it under the terms of the GNU General Public License as published by

the Free Software Foundation, either version 3 of the License, or

(at your option) any later version.

Ansible is distributed in the hope that it will be useful,

but WITHOUT ANY WARRANTY; without even the implied warranty of

MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

GNU General Public License for more details.

You should have received a copy of the GNU General Public License

along with Ansible. If not, see <http://www.gnu.org/licenses/>.

this is a windows documentation stub. actual code lives in the .ps1

file of the same name

DOCUMENTATION = '''

module: winadgroupmembership versionadded: "2.0" short_description: Add AD Group Membership description: - Add AD Group Membership options: group: description: name of the group to add to required: yes member: description: name of the group or user to the group mentioned in the group attribute required: yes state: description: if present then add the membership. if absent the delete the membership choices: ['present', 'absent'] default: 'present'

author: Stanley Karunditu(stanley@linuxsimba.com) '''

EXAMPLES = '''

Add Group

$ ansible -i hosts -m winadgroup_membership -a "group=mastergroup member=user1" windows '''

For more examples, check out the Linuxsimba Windows Ansible Modules repo.