Including Powershell module_utils

Hello,

I am trying to move some shared client stuff to a module_util so I can reuse it in my Powershell scripts. I used Windows module development walkthrough — Ansible Community Documentation for documentation.

Situation:

  • Local repo (not a Galaxy collection), added as project in AWX
  • Folders plugins/module_utils and plugins/modules

In the module_utils directory I have a file client.psm1 with 1 class, 1 function and an Export-ModuleMember for this function.

In the modules directory I have a file Get-ClientStuff.ps1 with a line #AnsibleRequires -PowerShell ..module_utils.client.

When I run the playbook that calls Get-ClientStuff I get the error Could not find collection imported module support code for '..module_utils.client".

I tried searching for this error but couldn’t find anything wrong.

Is this because I can’t use classes in module_utils, or am I doing something else wrong?

Joost

  • Local repo (not a collection)

The plugins/ structure is designed for collections and not the role directory structure. The name of module utils in a role directory should be Ansible.ModuleUtils.Name.ps1 and it would be referenced with #AnsibleRequires -PowerShell Ansible.ModuleUtils.Name. I highly recommend using a collection though as that is what most of the documentation is geared towards.

1 Like

I solved the AnsibleRequires error with the following steps:

  • Add ./plugins/module_utils to module_utils in ansible.cfg.
  • Change the AnsibleRequires line to #AnsibleRequires -PowerShell ansible.module_utils.myapplication

Next problem: The exported command is not found:

The term 'Connect-MyApplication' is not recognized as the name of a cmdlet,
  function, script file, or operable program. Check the spelling of the name, or
  if a path was included, verify that the path is correct and try again.

The contents of myapplication.psm1:

class MyApplication {
 /* Class Definition */
}

function Connect-MyApplication {
  param(
    [parameter(Mandatory = $false)]$apihost = 'api.myapplication.com',
    [parameter(Mandatory = $true)]$apiuser,
    [parameter(Mandatory = $true)]$apikey
  )
  return [MyApplication]::New($apihost, $apiuser, $apikey)
}

Export-ModuleMember -Function Connect-MyApplication

The issue now is the name of your module util. If using the role structure module utils need to be in the format Ansible.ModuleUtils.{name}. The case doesn’t matter but your _ in .module_utils. is what is causing it to be ignored.

See ansible/lib/ansible/executor/powershell/module_manifest.py at ac5eb232e9aa2be82687e016ae05e3883b6a8c33 · ansible/ansible · GitHub for the patterns that are checked. The “builtin” format is what was used for the role directory.

Then how can e.g. the microsoft.ad modules work with #AnsibleRequires -PowerShell ..module_utils._ADObject? (see microsoft.ad/plugins/modules/computer.ps1 at main · ansible-collections/microsoft.ad · GitHub). As far as I can see, I have the local repo file structure the same as that module:

\
  plugins\
    module_utils\
      client.psm1
    modules\
      getmyclientdata.ps1
  playbooks\
    getmyclientdata.yml

Because that’s a collection and that’s how the collection structure works.

  • Local repo (not a Galaxy collection), added as project in AWX

Unless I’m misreading what you said this meant you weren’t using a collection but trying to use the collection structure in the old role directory structure. The role structure is limited in how it works hence the rigid folder names and what a module util can be called.

What I am doing is Adding modules and plugins locally — Ansible Community Documentation
Apparently in that case, you can’t use AnsibleRequires for powershell modules? It works for Python modules (I can use from ansible.module_utils.myclient include myclient which loads the code from plugins/module_utils/myclient.py.
I don’t want to publish my code to Galaxy, so a collection is out of the question.

Please have a re read of what I’ve posted. You can use module_utils for both Python and PowerShell in the old role directory folder structure that you are using. The caveat is that the name of the module util for PowerShell utils need to be Ansible.ModuleUtils.{name} so in your example

module_utils/Ansible.ModuleUtils.MyApplication.psm1

Then in your PowerShell you can do

#AnsibleRequires -PowerShell Ansible.ModuleUtils.MyApplication

If you want a more relaxed naming standard you MUST use a collection. You don’t need to publish a collection it can stay local to your project. If you are wanting to use a collection adjacent to a playbook it can be in the following folder structure

main.yml  # playbook
collections/
  ansible_collections/
    namespace/
      name/
        plugins/
          module_utils/
            util_name.psm1
          modules/
            module.ps1

Change namespace and name to whatever you want for your collection and you can reference the module in your playbook with namespace.name.module:. See packer-windoze/collections/ansible_collections/jborean93/windoze/plugins at master · jborean93/packer-windoze · GitHub as an example of how a collection can be included adjacent to a playbook.

Since my playbooks are spread over multiple directories (per customer, per service), placing the collection at the same level as the playbooks will not work.
Renaming the module to Ansible.ModuleUtils.MyApplication.psm1 worked.

Since my playbooks are spread over multiple directories (per customer, per service), placing the collection at the same level as the playbooks will not work.

Won’t you have the same problem with the role structure? Either option allows you to package it up in a way you don’t have to distribute it public. Collections are just more flexible going forward so it is worth trying to use.