2

I'm trying to write a script that needs to be detect what the ArgumentList of a PowerShell module is. Is there any way of finding this out?

The end game is to be able to use this to create a simple DI container for loading modules.

Daniel Gee
  • 101
  • 8
  • 1
    Related: [Understanding a Windows PowerShell Module](https://msdn.microsoft.com/en-us/library/dd878324(v=vs.85).aspx) and/or [_Use the Get-Command PowerShell Cmdlet to Find Parameter Set Information_](https://blogs.technet.microsoft.com/heyscriptingguy/2012/05/16/use-the-get-command-powershell-cmdlet-to-find-parameter-set-information/) – JosefZ Mar 06 '17 at 13:22
  • 1
    What exactly do you consider the "argument list of a PowerShell module"? Modules usually don't have parameters, only the cmdlets they expose do. – Ansgar Wiechers Mar 06 '17 at 21:03
  • 1
    Yes they usually don't have them but can have them. It just requires a param section in the psm1 file. With commands and functions you can inspect the parameters property from Get-Command to get a lot of information. There doesn't seem to be any equivalent for module parameters – Daniel Gee Mar 07 '17 at 10:35

1 Answers1

1

You can use the AST parser to show you the param() block of the module file. Maybe use Get-Module to find out information about where the module files are located, then parse those and walk the AST to get the information you're after. Does this seem like something that would be useful?

function Get-ModuleParameterList {
    [CmdletBinding()]
    param(
        [string] $ModuleName
    )

    $GetModParams = @{
        Name = $ModuleName
    }

    # Files need -ListAvailable
    if (Test-Path $ModuleName -ErrorAction SilentlyContinue) {
        $GetModParams.ListAvailable = $true
    }

    $ModuleInfo = Get-Module @GetModParams | select -First 1 # You'll have to work out what to do if more than one module is found

    if ($null -eq $ModuleInfo) {
        Write-Error "Unable to find information for '${ModuleName}' module"
        return
    }

    $ParseErrors = $null
    $Ast = if ($ModuleInfo.RootModule) {
        $RootModule = '{0}\{1}' -f $ModuleInfo.ModuleBase, (Split-Path $ModuleInfo.RootModule -Leaf)

        if (-not (Test-Path $RootModule)) {
            Write-Error "Unable to determine RootModule for '${ModuleName}' module"
            return
        }

        [System.Management.Automation.Language.Parser]::ParseFile($RootModule, [ref] $null, [ref] $ParseErrors)
    }
    elseif ($ModuleInfo.Definition) {
        [System.Management.Automation.Language.Parser]::ParseInput($ModuleInfo.Definition, [ref] $null, [ref] $ParseErrors)
    }
    else {
        Write-Error "Unable to figure out module source for '${ModuleName}' module"
        return
    }

    if ($ParseErrors.Count -ne 0) {
        Write-Error "Parsing errors detected when reading RootModule: ${RootModule}"
        return
    }

    $ParamBlockAst = $Ast.Find({ $args[0] -is [System.Management.Automation.Language.ParamBlockAst] }, $false)

    $ParamDictionary = [ordered] @{}
    if ($ParamBlockAst) {
        foreach ($CurrentParam in $ParamBlockAst.Parameters) {
            $CurrentParamName = $CurrentParam.Name.VariablePath.UserPath
            $ParamDictionary[$CurrentParamName] = New-Object System.Management.Automation.ParameterMetadata (
                $CurrentParamName,
                $CurrentParam.StaticType
            )

            # At this point, you can add attributes to the ParameterMetaData instance based on the Attribute

        }
    }
    $ParamDictionary
}

You should be able to give that a module name or the path to a module. It's barely been tested, so there are probably some instances where it won't work. Right now, it returns a dictionary like viewing the 'Parameters' property returned from Get-Command. If you want the attribute information, you'd need to do a little work to build each one.

Rohn Edwards
  • 2,499
  • 1
  • 14
  • 19