carlpett
carlpett

Reputation: 12613

Powershell "plugin" system

We've written a powershell script which processes images from an internal system and sends them off to another system. Now another part of the business would like to hook into this and do their own processing of the indata and push it to yet another system. Asking around, there are several intrested parties around the company, so I'd like to make it simple to add these new systems.

A first prototype simple opens all .ps1 files in a folder and runs a specially named function in there and hopes for the best, basically. However, this seems like it could be improved. Is there some established powershell best practice to do some plugin-like system? If not, given that this executes in a quite secured environment, and new modules will be checked in by administrators, are there any problems with my above approach?

Upvotes: 4

Views: 1181

Answers (1)

David Brabant
David Brabant

Reputation: 43559

Why wouldn't you use a config file for your main script, explicitly telling what script and what function to call? Something like this (warning: this is copy/pasted and adapted code from something I wrote. Might contain a few glitches, but this gives you the general idea):

<?xml version="1.0"?>
<configuration>
  <Plugins>
    <Plugin Path="c:\blah\plugin1.ps1" PowerShellFunction="My-Plugin-Function" />
  </Plugins>
</configuration>

In your main script:

function Load-Plugins
{
    param (
        [parameter(Mandatory = $true)][xml] $config,
        [parameter(Mandatory = $true)][string] $nodeType
    )

    $plugins = @{}

    foreach ($pluginNode in $config.SelectNodes($nodeType))
    {
        if ($pluginNode)
        {
            $Path = $pluginNode.Path
            $powerShellFunction = $pluginNode.PowerShellFunction

            $plugin = New-Object Object |
                Add-Member -MemberType NoteProperty -Name "Path" -Value $Path -PassThru |
                Add-Member -MemberType NoteProperty -Name "PowerShellFunction" -Value $powerShellFunction -PassThru

            $plugins[$Path] = $plugin
        }
    }

    return $plugins
}


function Execute-Plugins
{
    param (
        [parameter(Mandatory = $true)][hashtable] $plugins
    )

    $Error.Clear()

    if (!$plugins.Values)
        { return }

    foreach ($plugin in $plugins.Values)
    {
        & .\$plugin.Path
        Invoke-Expression "$($plugin.PowerShellFunction)"
    }
}


function Load-Script-Config  
{
    param (
        [parameter(Mandatory = $false)][string] $configFile
    )

    if (!$configFile)
        { $configFile = (Get-PSCallStack)[1].Location.Split(':')[0].Replace(".ps1", ".config") }

        return [xml](Get-Content $configFile)
}

$pluginConfig = Load-Script-Config
$plugins = Load-Plugins $config "configuration/Plugins/Plugin"
Execute-Plugins $plugins

Upvotes: 4

Related Questions