Added initial developing module doc for Windows (#27308)
* Added initial developing module doc for Windows * Added to dev index, made formatting changes and moved obsolete docs * changes based on PR feedback * some typos and extra information * Minor updates * minor wording clarifications * remove references to "remote" Windows hosts * update template/text to remove legacy `#WANT_JSON`/`#POWERSHELL_COMMON` * use per-execution envvars for ANSIBLE_KEEP_REMOTE_FILES instead of stateful * fix up minor sanity check issue
This commit is contained in:
parent
7cf4416c9c
commit
ad5fa60a2b
4 changed files with 265 additions and 70 deletions
|
@ -63,6 +63,8 @@ The following topics will discuss how to develop and work with modules:
|
|||
|
||||
:doc:`developing_modules_general`
|
||||
A general overview of how to develop, debug, and test modules.
|
||||
:doc:`developing_modules_general_windows`
|
||||
A general overview of how to develop, debug and test Windows modules.
|
||||
:doc:`developing_modules_documenting`
|
||||
How to include in-line documentation in your module.
|
||||
:doc:`developing_modules_best_practices`
|
||||
|
|
|
@ -136,79 +136,10 @@ The complete module metadata specification is here: `Ansible metadata block <htt
|
|||
|
||||
Windows modules checklist
|
||||
=========================
|
||||
* Favour native powershell and .net ways of doing things over calls to COM libraries or calls to native executables which may or may not be present in all versions of Windows
|
||||
* modules are in powershell (.ps1 files) but the docs reside in same name python file (.py)
|
||||
* look at ansible/lib/ansible/module_utils/powershell.ps1 for common code, avoid duplication
|
||||
* Ansible uses strictmode version 2.0 so be sure to test with that enabled
|
||||
|
||||
All powershell modules must start:
|
||||
For a checklist and details on how to write Windows modules please see :doc:`developing_modules_general_windows`
|
||||
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
#!powershell
|
||||
|
||||
<GPL header>
|
||||
|
||||
# WANT_JSON
|
||||
# POWERSHELL_COMMON
|
||||
|
||||
To parse all arguments into a variable modules generally use:
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
$params = Parse-Args $args
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* Try and use state present and state absent like other modules
|
||||
* You need to check that all your mandatory args are present. You can do this using the builtin Get-AnsibleParam function.
|
||||
* Required arguments:
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
$package = Get-AnsibleParam -obj $params -name name -failifempty $true
|
||||
|
||||
Required arguments with name validation:
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
$state = Get-AnsibleParam -obj $params -name "State" -ValidateSet "Present","Absent" -resultobj $resultobj -failifempty $true
|
||||
|
||||
Optional arguments with name validation
|
||||
---------------------------------------
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
$state = Get-AnsibleParam -obj $params -name "State" -default "Present" -ValidateSet "Present","Absent"
|
||||
|
||||
* If the "FailIfEmpty" is true, the resultobj parameter is used to specify the object returned to fail-json. You can also override the default message
|
||||
using $emptyattributefailmessage (for missing required attributes) and $ValidateSetErrorMessage (for attribute validation errors)
|
||||
* Look at existing modules for more examples of argument checking.
|
||||
|
||||
Results
|
||||
-------
|
||||
* The result object should always contain an attribute called changed set to either $true or $false
|
||||
* Create your result object like this
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
$result = New-Object psobject @{
|
||||
changed = $false
|
||||
other_result_attribute = $some_value
|
||||
};
|
||||
|
||||
If all is well, exit with a
|
||||
Exit-Json $result
|
||||
|
||||
* Ensure anything you return, including errors can be converted to json.
|
||||
* Be aware that because exception messages could contain almost anything.
|
||||
* ConvertTo-Json will fail if it encounters a trailing \ in a string.
|
||||
* If all is not well use Fail-Json to exit.
|
||||
|
||||
* Have you tested for powershell 3.0 and 4.0 compliance?
|
||||
|
||||
Deprecating and making module aliases
|
||||
======================================
|
||||
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
Windows Ansible Module Development Walkthrough
|
||||
==============================================
|
||||
|
||||
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:
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
|
||||
Windows environment setup
|
||||
=========================
|
||||
|
||||
TODO: Add in more information on how to use Vagrant to setup a Windows host.
|
||||
|
||||
|
||||
Windows new module development
|
||||
==============================
|
||||
|
||||
When creating a new module there are a few things to keep in mind:
|
||||
|
||||
- Module code is in Powershell (.ps1) files while the documentation is contained in Python (.py) files of the same name
|
||||
- Avoid using ``Write-Host/Debug/Verbose/Error`` in the module and add what needs to be returned to the ``$result`` variable
|
||||
- When trying an exception use ``Fail-Json -obj $result -message "exception message here"`` instead
|
||||
- Most new modules require check mode and integration tests before they are merged into the main Ansible codebase
|
||||
- Avoid using try/catch statements over a large code block, rather use them for individual calls so the error message can be more descriptive
|
||||
- Try and catch specific exceptions when using try/catch statements
|
||||
- Avoid using PSCustomObjects unless necessary
|
||||
- Look for common functions in ``./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 Ansible
|
||||
- Ensure the code runs under Powershell v3 and higher on Windows Server 2008 and higher; if higher minimum Powershell or OS versions are required, ensure the documentation reflects this clearly
|
||||
- Ansible runs modules under strictmode version 2.0. Be sure to test with that enabled by putting ``Set-StrictMode -Version 2.0`` at the top of your dev script
|
||||
- Favour native Powershell cmdlets over executable calls if possible
|
||||
- If adding an object to ``$result``, ensure any trailing slashes are removed or escaped, as ``ConvertTo-Json`` will fail to convert it
|
||||
- Use the full cmdlet name instead of aliases, e.g. ``Remove-Item`` over ``rm``
|
||||
- Use named parameters with cmdlets, e.g. ``Remove-Item -Path C:\temp`` over ``Remove-Item C:\temp``
|
||||
|
||||
A very basic powershell module template can be found found below:
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
#!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
|
||||
|
||||
$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:
|
||||
|
||||
- Native Powershell cmdlets like ``Remove-Item -Path C:\temp -Recurse``
|
||||
- .NET classes like ``[System.IO.Path]::GetRandomFileName()``
|
||||
- WMI objects through the ``New-CimInstance`` cmdlet
|
||||
- COM objects through ``New-Object -ComObject`` cmdlet
|
||||
- Calls to native executables like ``Secedit.exe``
|
||||
|
||||
|
||||
Windows playbook module testing
|
||||
===============================
|
||||
|
||||
To test a module you can do so with an Ansible playbook.
|
||||
|
||||
- Create a playbook in any directory ``touch testmodule.yml``
|
||||
- Create an inventory file in the same directory ``touch hosts``
|
||||
- Populate the inventory file with the variables required to connect to a Windows host(s).
|
||||
- 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
|
||||
|
||||
- Run the playbook ``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.
|
||||
|
||||
|
||||
Windows debugging
|
||||
=================
|
||||
|
||||
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.
|
||||
|
||||
- Copy the module script to the Windows server
|
||||
- Copy ``./lib/ansible/module_utils/powershell/Ansible.ModuleUtils.PowerShellLegacy.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.PowerShellLegacy.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.PowershellLegacy.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
|
||||
|
||||
- `Powershell ISE`_
|
||||
- `Visual Studio Code`_
|
||||
|
||||
.. _Powershell ISE: https://msdn.microsoft.com/en-us/powershell/scripting/core-powershell/ise/how-to-debug-scripts-in-windows-powershell-ise
|
||||
.. _Visual Studio Code: https://blogs.technet.microsoft.com/heyscriptingguy/2017/02/06/debugging-powershell-script-in-visual-studio-code-part-1/
|
||||
|
||||
To be able to view the arguments as passed by Ansible to the module follow
|
||||
these steps.
|
||||
|
||||
- Prefix the Ansible command with ``ANSIBLE_KEEP_REMOTE_FILES=1`` to get Ansible to keep the exec files on the server
|
||||
- Log onto the Windows server using the same user Ansible executed the module as
|
||||
- Navigate to ``%TEMP%\..``, there should be a folder starting with ``ansible-tmp-``
|
||||
- Inside this folder open up the powershell script for the module
|
||||
- In this script there is a raw JSON script under ``$json_raw`` which contains the module arguments under ``module_args``
|
||||
- These args can be assigned manually to the ``$complex_args`` variable that is defined on your debug script
|
||||
|
||||
|
||||
Windows unit testing
|
||||
====================
|
||||
|
||||
Currently 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.
|
||||
|
||||
|
||||
Windows integration testing
|
||||
===========================
|
||||
|
||||
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.
|
||||
|
||||
- Create a copy of ``./test/integration/inventory.winrm.template`` and just call it ``inventory.winrm``
|
||||
- Fill in entries under ``[windows]`` and set the required vars that are needed to connect to the host
|
||||
- To execute the integration tests, run ``ansible-test windows-integration win_stat``- you can replace ``win_stat`` with the role you wish to test
|
||||
|
||||
This 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:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- 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
|
||||
|
||||
|
||||
Windows communication and development support
|
||||
=============================================
|
||||
|
||||
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.
|
|
@ -18,6 +18,7 @@ To get started, select one of the following topics.
|
|||
overview_architecture
|
||||
developing_modules
|
||||
developing_modules_general
|
||||
developing_modules_general_windows
|
||||
developing_modules_documenting
|
||||
developing_modules_best_practices
|
||||
developing_modules_checklist
|
||||
|
|
Loading…
Reference in a new issue