Documentation Attributes for PowerShell versions

Hi

I’m trying to work through the process on how best to document the versions of PowerShell that a module may support. In the past modules only worked on Windows PowerShell provided by Windows. The upcoming Ansible 2.21 release adds support for running modules against PowerShell 7.x as well as running PowerShell modules on POSIX hosts. I still need to submit a PR to the Ansible documentation repo to document these changes and update the dev guide for PowerShell but the first thing I want to try and do is standardise a common way for collections to document what PowerShell versions a module supports.

The goal would be to have a common approach where a module could:

  • indicate what PowerShell version(s) it supports
    • 5.1 being Windows PowerShell (powershell.exe)
    • 7 being PowerShell 7 (pwsh.exe)
    • 7.x being a specific PowerShell 7 version rather than just 7 in general
    • the above may not be ideal as Ansible will only support one 7.x version in a release due to lifecycle constraints of PowerShell
  • a free form human description that can provide more context
  • maybe even a field that indicates what PowerShell version is used by default
    • the #!powershell shebang defaults to WinPS on Windows and Pwsh on POSIX
    • the #!/usr/bin/pwsh shebang defaults to Pwsh on Windows and POSIX
    • either option can still be overriden by ansible_pwsh_interpreter like how Python works

It would also potentially be nice if each version could have a ternary like value that indicates whether that version is:

  • :white_check_mark: validated through tests as working
  • :warning: may not be tested or tested thoroughly but should work
  • :cross_mark: known to not work

For example we have some modules that are next to impossible to test in CI and while they should work it has not been validated to work on version x, y, z, etc.

My first approach was to create a doc fragment with the powershell attribute defined as:

attributes:
  powershell:
    description:
      - List of target PowerShell versions specified by O(versions) supported by
        this module.
      - Version V(7) without a minor version specified means the V(7.x) versions
        should be supported by Ansible should work but see O(details) for more
        information.
      - PowerShell V(5.1) is supported on Windows only while V(7.x) is
        cross-platform. See O(platform) for more details on what platforms are
        supported.
    support: N/A

Each module would then define the versions, optional details, any other options relevant to PowerShell and that module.

extends_documentation_fragment:
  - ns.name.powershell_fragment
attributes:
  powershell:
    versions:
      - '5.1'
      - '7'

When attempting to see what that would look like in a PR I’ve found that our documentation builder is strict on what child keys are allowed in an attribute Document PowerShell version support by jborean93 · Pull Request #52 · ansible-collections/microsoft.iis · GitHub. From my understanding the attributes were designed as a free form and flexible way for metadata to be applied to modules but maybe it’s evolved from there to ensure the attribute table in the documentation can still be rendered. I’m not against the stricter validation logic here to enforce common values but just wanted to bring this up as if this approach is desired then we would need this validation to be updated to support the new powershell structure in whatever format is desired.

Another alternative I could do is use the support key indicate what versions are supported:

attributes:
  powershell:
    description:
      - Target PowerShell versions supported.
      - V(suppport=full) works on both Windows PowerShell 5.1 and Powershell 7.x.
      - V(support=partial) works on either, see O(details) for more information.

The advantage here is it works in the existing validation schema but looses some flexibility in specifying specific versions and relies on the description and details to provide more context. It also could potentially conflate partial support being the PowerShell version rather than specific features only being partially supported. I’m not the biggest fan of this approach as it really feels like shoehorning data into a structure that’s not really designed for it and the end result in the documentation is just poorer because of that.

A final alternatives is to just not use the attributes and use the notes or some other free form field to document what PowerShell versions are supported or not but this means our documentation builder can’t take advantage of this to provide a nicer indication of what is and isn’t supported. Maybe this is an ok stop gap until a decision is made on the correct approach and the relevant changes are made to the documentation validator and builder.

If we were to go with the first approach it could enable us to provide a more user friendly output rather than the somewhat limited attributes table we use today. For example we could generate a new section to the rst that could look something like:


Attributes

Existing attributes go here as they always have.

Attribute Support Description
action full Action description.
check_mode full Check mode description.

PowerShell

This module supports the following PowerShell versions

  • … list of versions indicated by the attribute
  • :white_check_mark: 5.1
  • :warning: 7 - May contain bugs
  • :cross_mark: 7 - Known to not support this version at all

… any further information from the details key.


I would love to see what people’s thoughts and potential other ideas may be. I want to ensure that whatever approach that is taken results in an easy way to document the PowerShell version requirements from a dev perspective but also a human friendly way to see those requirements in the module documentation itself.

While I’m not suggesting we should do this, the approach we take here could also be used for indicating things like Python versions, or even other module languages.

A module already has a requirements section in the DOCUMENTATION block.

I don’t think it was really supposed to be free-form data, at least to my knowledge. (The first implementation didn’t have more validation, but it got merged while the feature was still discussed in DaWGs, and there was talk about reworking the structure. Eventually strict validation was added later. There are several DaWG meeting logs from that period of time with more details; I haven’t re-read them in detail, but form what I skimmed it looks like there was consensus that there should be strict validation.)

What is free-form is that you can define arbitrary attributes with arbitrary description and details. You have a fixed set of supported values, and only very specific attributes can have more data present. This has always been ensured by the ansible-test sanity tests, and is also validated by antsibull-docs’ schema. (There’s no good way to render arbitrary metadata anyway.)

If we want to be able to show additional metadata, we should define what kind of generic metadata should be allowed and in which form it should be presented.

This is something I would avoid. It will also break down completely when there are more PowerShell versions that should be mentioned.

Generally, showing compatibility with specific versions (of something) is something more general that affects many modules (and plugins and roles). Supported Python and PowerShell versions have already been mentioned in this thread. As another example, for Keycloak modules, you might want to mention which verisons of Keycloak are supported by the module. Similar information makes sense for groups of modules for other software. This obviously can also be mentioned under requirements, as @ildjarn mentioned, but having this as an attribute improves readability IMO.

For a general attribute (that’s not one of the already special ones, i.e. action_group and platform), we could have a version_list like this:

version_list:
  - version: 3.x
    support: none
  - version: 4.x
    support: partial
    details:
      - While basic features work in 4.0, some things only work in 4.1+.
  - version: 5.0
    support: full
    version_added: 2.4.0
  - version: magic edition
    support: N/A

If this is present, support would be ignored (on the docsite rendering, simliar to the other special attributes) and the list is rendered with caption Versions:.

One disadvantage of that list is that the list contents have to be repeated in every single module using that attribute. It would be better to have some way to merge this data, similar to attributes themselves, where description (and membership) is usually part of a doc fragment, and support and details (and platforms) are filled out by individual modules. This would require support in ansible-core though, and probably (as most features) has no chance of getting backported to older ansible-core versions.

PowerShell has a native way of defining the minimum version required to run the .ps1 file:

#Requires -Version 7.0

One of the modules in the ansible.windows declares it: ansible.windows/plugins/modules/win_dsc.ps1 at main · ansible-collections/ansible.windows · GitHub

Could we leverage this existing metadata in some way?

Can you also declare a maximum supported version that way?

No you can’t.

There’s #Requires -PSEdition for Core/Desktop which can be used to set the max version to 5.1 (the last version for Desktop).

See about_Requires - PowerShell | Microsoft Learn

1 Like

Thanks everyone for the replies so far, appreciate it!

@ildjarn I did consider this and it’s certainly an option. It could be useful to say a module only works on version x, y, z but I do find it falls a bit short in providing a more consistent set of structured data. It’s probably a good place to put this if attributes do not end up being used.

@dbrennand this has some runtime implications as Ansible uses the #Requires -Version ... comment to do an actual check at runtime. Unfortunately this isn’t ideal from a documentation perspective as you cannot easily add extra information behind specific versions, like feature x won’t work on 7 but will on 5.1 and so forth. It also would require users to look at the module code rather than the documentation which isn’t ideal.

@felixfontein that’s totally fair I’m most likely just misremembering. Don’t get me wrong I like that we have stricter validation here it just surprised me when testing it out.

I like your idea of version_list and making things generic but I think it would be nice to provide a way to describe what the versions are for. It could maybe be something like requirements under attributes:

requirements:
  - type: powershell|python|package req|etc
    description:
      - Describes the type or provides generic context
    version_list:
      - ... your structure

This way we could use the same underlying structure for multiple types of requirements, Python versions, Python dependency requirements, etc. The same entries could then be used in the final RST to build a X Requirements section with the details mentioned.

That is definitely not ideal and even putting aside not being able to backport it the logic around merging such a structure won’t be fun to reason with. In saying that we could potentially avoid it by flattening it a bit more and use the type in the attribute name as a suffix

requirements_powershell:
  - description:
      - Some info about this requirements
      - May need validation to ensure this has no version/support key and
        there is only 1 description entry
  - version: 3.x
    support: none
  ...

It still wouldn’t provide a way to merge a base requirements list from a doc fragment with extra requirements in a module which would be useful for shared util dependencies but maybe an ok first attempt for simpler scenarios like PowerShell/Python versions while we wait for the minimum Ansible version to catch up with list merging.

1 Like