In this section, we will walk through developing, testing, and debugging an Ansible Windows module.
Because Windows modules are written in Powershell and need to be run on a Windows host, this guide differs from the usual development walkthrough guide.
What’s covered in this section:
TODO: Add in more information on how to use Vagrant to setup a Windows host.
When creating a new module there are a few things to keep in mind:
Write-Host/Debug/Verbose/Error
in the module and add what needs to be returned to the $result
variableFail-Json -obj $result -message "exception message here"
instead./lib/ansible/module_utils/powershell/
and use the code there instead of duplicating work. These can be imported by adding the line #Requires -Module *
where * is the filename to import, and will be automatically included with the module code sent to the Windows target when run via AnsibleSet-StrictMode -Version 2.0
at the top of your dev script$result
, ensure any trailing slashes are removed or escaped, as ConvertTo-Json
will fail to convert itRemove-Item
over rm
Remove-Item -Path C:\temp
over Remove-Item C:\temp
A very basic powershell module template can be found found below:
#!powershell # This file is part of Ansible # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) #Requires -Module Ansible.ModuleUtils.Legacy.psm1 $ErrorActionPreference = 'Stop' $params = Parse-Args -arguments $args -supports_check_mode $true $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false $diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false # these are your module parameters, there are various types which can be # used to format your parameters. You can also set mandatory parameters # with -failifempty, set defaults with -default and set choices with # -validateset. $string = Get-AnsibleParam -obj $params -name "string" -type "str" -failifempty $true $bool = Get-AnsibleParam -obj $params -name "bool" -type "bool" -default $false $int = Get-AnsibleParam -obj $params -name "int" -type "int" $path = Get-AnsibleParam -obj $params -name "path" -type "path" $list = Get-AnsibleParam -obj $params -name "list" -type "list" $choices = Get-AnsibleParam -obj $params -name "choices" -type "str" -default "present" -validateset "absent","present" $result = @{ changed = $false } if ($diff_mode) { $result.diff = @{} } # code goes here # you can add/set new result objects with $result.changed = $true $result.new_result = "Hi" Exit-Json -obj $result
When in doubt, look at some of the core modules and see how things have been implemented there.
Sometimes there are multiple ways that Windows offers to complete a task; this is the order to favour when writing modules:
Remove-Item -Path C:\temp -Recurse
[System.IO.Path]::GetRandomFileName()
New-CimInstance
cmdletNew-Object -ComObject
cmdletSecedit.exe
To test a module you can do so with an Ansible playbook.
touch testmodule.yml
touch hosts
Add the following to the new playbook file:
--- - name: test out windows module hosts: windows tasks: - name: test out module win_module: name: test name
ansible-playbook -i hosts testmodule.yml
This can be pretty high level and is useful for seeing how Ansible runs with the new module end to end: but there are better ways to test out the module as shown below.
Debugging a module currently can only be done on a Windows host. This is extremely useful when developing a new module or looking at bug fixes. These are some steps that need to be followed to set this up.
./lib/ansible/module_utils/powershell/Ansible.ModuleUtils.Legacy.psm1
to the same directory as the script above To stop the script from exiting the editor on a successful run, in Ansible.ModuleUtils.Legacy.psm1
under the function Exit-Json
, replace the last two lines of the function with:
ConvertTo-Json -InputObject $obj -Depth 99
To stop the script from exiting the editor on a failed run, in Ansible.ModuleUtils.Legacy.psm1
under the function Fail-Json
, replace the last two lines of the function with:
Write-Error -Message (ConvertTo-Json -InputObject $obj -Depth 99)
Add the following to the start of the module script that was copied to the server:
### start setup code $complex_args = @{ "_ansible_check_mode" = $false "_ansible_diff" = $false "path" = "C:\temp" "state" = "present" } Import-Module -Name .\Ansible.ModuleUtils.Legacy.psm1 ### end setup code
You can add more args to $complex_args
as required by the module. The module can now be run on the Windows host either directly through Powershell or through an IDE.
There are multiple IDEs that can be used to debug a Powershell script, two of the most popular are
To be able to view the arguments as passed by Ansible to the module follow these steps.
ANSIBLE_KEEP_REMOTE_FILES=1
to get Ansible to keep the exec files on the server%TEMP%\..
, there should be a folder starting with ansible-tmp-
$json_raw
which contains the module arguments under module_args
$complex_args
variable that is defined on your debug scriptCurrently there is no mechanism to run unit tests for Powershell modules under Ansible CI. There is work in the pipeline to introduce this in the future, stay tuned.
Integration tests for Ansible modules are typically written as Ansible roles. The test roles are located in ./test/integration/targets
. You must first set up your testing environment, and configure a test inventory for Ansible to connect to. In this example we will set up a test inventory to connect to two hosts and run the integration tests for win_stat.
./test/integration/inventory.winrm.template
and just call it inventory.winrm
[windows]
and set the required vars that are needed to connect to the hostansible-test windows-integration win_stat
- you can replace win_stat
with the role you wish to testThis will execute all the tests currently defined for that role. You can set the verbosity level using the -v
argument just as you would with ansible-playbook.
When developing tests for a new module, it is recommended to test a scenario in check mode and 2 times not in check mode. This ensures that check mode does not make any changes but reports a change, as well as that the 2nd run is idempotent and does not report changes. Following is an example of one way that this can be done:
- name: remove a file (check mode) win_file: path: C:\temp state: absent register: remove_file_check check_mode: yes - name: get result of remove a file (check mode) win_command: powershell.exe "if (Test-Path -Path 'C:\temp') { 'true' } else { 'false' }" register: remove_file_actual_check - name: assert remove a file (check mode) assert: that: - remove_file_check|changed - remove_file_actual_check.stdout == 'true\r\n' - name: remove a file win_file: path: C:\temp state: absent register: remove_file - name: get result of remove a file win_command: powershell.exe "if (Test-Path -Path 'C:\temp') { 'true' } else { 'false' }" register: remove_file_actual - name: assert remove a file assert: that: - remove_file|changed - remove_file_actual.stdout == 'false\r\n' - name: remove a file (idempotent) win_file: path: C:\temp state: absent register: remove_file_again - name: assert remove a file (idempotent) assert: that: - not remove_file_again|changed
Join the IRC channel #ansible-devel
or #ansible-windows
on freenode for discussions surrounding Ansible development for Windows.
For questions and discussions pertaining to using the Ansible product, use the #ansible
channel.
© 2012–2017 Michael DeHaan
© 2017 Red Hat, Inc.
Licensed under the GNU General Public License version 3.
https://docs.ansible.com/ansible/latest/dev_guide/developing_modules_general_windows.html