Behind the PowerShell Pipeline logo

Behind the PowerShell Pipeline

Subscribe
Archives
October 1, 2024

Automatic PowerShell Modules

In a previous email I demonstrated PowerShell code that generated custom PowerShell functions. This is a clever way to create commands with a very specific use case or a very specific target user. You can put your code in a script file that is dot-sources to create the set of tools.

But what if you want to create a module? This is where things get interesting. Keep in mind that what I want to demonstrate is for very specific purposes. This is not an all-purpose approach, although there may be parts of the technique that you can use in your own work.

Dynamic Modules

PowerShell has a little known feature that allows you to create a module on the fly. You can even do it from the console. The secret command is New-Module. I'll trust you will take time to read full help and examples for this command. I'll just show you a simple example.

New-Module -Name MyDynamic -ScriptBlock {
    Function Invoke-Hello {
        Write-Host "Hello, World!" -ForegroundColor Green
    }
} -function Invoke-Hello

This code creates a new module in memory called MyDynamic. Nothing is written to disk. The module as a single function called Invoke-Hello. You can use the function immediately.

My new dynamic module
figure 1

The module isn't technically visible in the session, but the command is.

Dynamic module behavior
figure 2

There is a module, but you can't view it.

PS C:\> Get-Command Invoke-Hello | Select-Object -ExpandProperty module | Select-Object Path,GUID

Path                                    Guid
----                                    ----
C:\50f8426a-a8de-4739-9136-87d45e912d68 00000000-0000-0000-0000-000000000000

The path is listed, but doesn't exist.

How can we use this feature to create a dynamic module using the techniques from the previous article?

Proof of Concept

I'll test my approach first by copying and pasting into an interactive console session. I'm going to create a command for every computer in my test Active Directory domain.

$Names = (Get-ADComputer -Filter *).Name

I'll use this function as my template.

Function Get-ServerInfo {
    [cmdletbinding()]
    Param(
        [parameter(Position = 0, Mandatory)]
        [string]$ComputerName
    )

    Write-Verbose "Starting $($MyInvocation.MyCommand)"
    Try {
        Write-Verbose "Resolving DNS name for $($ComputerName.ToUpper())"
        $r = Resolve-DnsName $ComputerName -Type A -ErrorAction Stop
        Write-Verbose "Getting AD computer object for $($ComputerName.ToUpper())"
        $ad = Get-ADComputer -Identity $ComputerName -Properties ManagedBy, Description, Enabled -ErrorAction Stop
    }
    Catch {
        Throw $_
    }

    if ($r) {
        [PSCustomObject]@{
            PSTypeName        = 'PSServerInfo'
            ComputerName      = $ComputerName.ToUpper()
            DNSHostName       = $r.Name.ToUpper()
            DistinguishedName = $ad.DistinguishedName
            Description       = $ad.Description
            ManagedBy         = $ad.ManagedBy
            IPAddress         = $r.IPAddress
            Enabled           = $ad.Enabled
        }
    }
    Write-Verbose "Ending $($MyInvocation.MyCommand)"
}

I need the function's definition.

$definition = (Get-Item Function:\Get-ServerInfo).definition

Next, I'll create an array of commands.

$cmds = @()
Foreach ($item in $Names) {
    Write-Host "Creating function: $item" -ForegroundColor Green
    $cmds+= "Function $item { $definition }"
    $PSDefaultParameterValues["$($item):ComputerName"] = $item
}

Because I am using an existing function with a mandatory parameter, I'm also setting the default parameter value for the ComputerName parameter. I showed you this last time.

Now to create the dynamic module.

New-Module -Name CompanyServerInfo -ScriptBlock ([scriptblock]::Create($cmds)) -function $Names
Creating an AD-based dynamic module
figure 3

We already know the module is hidden. But the commands are not.

Using the dynamic module command
figure 4

Creating a Module File

Now that this works, I need to write something that can be deployed. This time I'll explicitly define the function text. This allows me to include comment-based help. I can also adjust the code to hard-code the computer name within the command. Normally, we avoid this, but in this case, I'm creating targeted commands for a less-experienced PowerShell user.

Want to read the full issue?
GitHub Bluesky LinkedIn About Jeff
Powered by Buttondown, the easiest way to start and grow your newsletter.