Implement PowerShell PSProvider *in* PowerShell
Asked Answered
S

3

6

I'm looking to implement a PowerShell Provider in PowerShell.

I keep thinking that if I just define the types, then import them into my session (import-module), I should be able to have them available.

For example, this does not work but its along the path of what I'd like to implement.

I'm obviously missing quite a bit...anyone know if this is possible?

# EnvironmentProvider.ps1
    $reference_assemblies = (

      "System.Management.Automation, Version=1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
    #  "System.Configuration.Install, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
    )

    $source = @"

    namespace Providers
    {

    using System.Management.Automation;
    using System.Management.Automation.Provider;


        [CmdletProvider("Environments", ProviderCapabilities.None)]
        public class EnvironmentProvider : DriveCmdletProvider
        {
            protected override PSDriveInfo NewDrive(PSDriveInfo drive)
            {
                return new EnvironmentDriveInfo(drive);
            }

            protected override object NewDriveDynamicParameters()
            {
                return base.NewDriveDynamicParameters();
            }

        }

         public class EnvironmentDriveInfo : PSDriveInfo
        {
            public EnvironmentDriveInfo(PSDriveInfo driveInfo) : base(driveInfo)
            {
            }
        }


    }
    "@

    # -ea silentlycontinue in case its already loaded
    #
    add-type -referencedassemblies $referenced_assemblies -typedefinition $source -language CSharp -erroraction silentlycontinue

After import-module, I try to create the drive "environments":

new-psdrive -psprovider Environments -name "Environments" -root ""

errors with:

New-PSDrive : Cannot find a provider with the name 'Environments'.

Assuming the provider actually worked, maybe have it return a list of environments: dev, qa, staging, production.

Then I'd like to be able to re-use this through:

c:\adminlib>import-module .\EnvironmentProvider.ps1
c:\adminlib>environments:

environments:>ls
dev
qa
staging
production

environments:> cd production
environments\production> [execute actions against production]

environments\production:> cd dev
environments\dev:> [execute actions against dev, etc]
Silurid answered 22/5, 2012 at 20:21 Comment(0)
C
6

I would strongly recommend looking at the stuff Oisin wrote, suspect for people like you, who can grab their head around it, that could be very good reference on how-to. Or maybe what to avoid? ;) You can find it on codeplex: http://psprovider.codeplex.com/

Cottrill answered 22/5, 2012 at 21:3 Comment(1)
While not implemented entirely in powershell, this looks like it will be perfect, thanks!Silurid
A
2

I know it's been some time since you asked the question, but I've been searching for that same answer myself. As it happens, re-reading the Samples in msdn finally got me my answer, and given the frustration quotient I thought I'd share:

The assembly containing the provider needs to be imported using Import-Module (not merely the module containing the add-type declaration). This can be done using two ways:

Option 1: Use the parameter of Add-Type that builds the runtime assembly as a .dll file and import the file.

Option 2: Import the runtime assembly from memory. This is how I did that with the standard msdn samples:

[appdomain]::CurrentDomain.GetAssemblies() | Where {$_.ExportedTypes -ne $null} | Where {($_.ExportedTypes | Select -ExpandProperty "Name") -contains "AccessDBProvider"} | Import-Module

Replace the Provider name in the where filter with your own.

Cheers, Fred

Acie answered 25/6, 2014 at 14:36 Comment(1)
Hey Fred - it's now 3 years after you posted that update but it just helped me tremendously. I had seen those custom Access provider pages many years ago and remembered them, today I looked for them but the source code is no longer available on Microsoft's site - but your reference did the job. Many thanks!Sammysamoan
N
0

I'm having a look at this for a project. It is early days, but I'd like to be able to define my providers entirely in PowerShell.

So far this translation of the code in the question is working for me.

class EnvironmentDriveInfo : System.Management.Automation.PSDriveInfo {

    EnvironmentDriveInfo ([System.Management.Automation.PSDriveInfo] $driveInfo) : base($driveInfo) {}
}

[System.Management.Automation.Provider.CmdletProvider('Environments', [System.Management.Automation.Provider.ProviderCapabilities]::None)]
class EnvironmentProvider : System.Management.Automation.Provider.DriveCmdletProvider {

    [System.Management.Automation.PSDriveInfo] NewDrive ([System.Management.Automation.PSDriveInfo] $drive) {

        return [EnvironmentDriveInfo]::new($drive)
    }

    [Object] NewDriveDynamicParameters () {

        return ([System.Management.Automation.Provider.DriveCmdletProvider]$this).NewDriveDynamicParameters()
    }
}

$sessionStateProviderEntry = [System.Management.Automation.Runspaces.SessionStateProviderEntry]::new('Environments', [EnvironmentProvider], $null)
$typeLocalPipeline = [PowerShell].Assembly.GetType('System.Management.Automation.Runspaces.LocalPipeline')
$methodGetExecutionContextFromTLS = $typeLocalPipeline.GetMethod('GetExecutionContextFromTLS', [System.Reflection.BindingFlags]'Static,NonPublic')
$context = $methodGetExecutionContextFromTLS.Invoke($null, [System.Reflection.BindingFlags]'Static,NonPublic', $null, $null, (Get-Culture))
$typeSessionStateInternal = [PowerShell].Assembly.GetType('System.Management.Automation.SessionStateInternal')
$constructor = $typeSessionStateInternal.GetConstructor([System.Reflection.BindingFlags]'Instance,NonPublic', $null, $context.GetType(), $null)
$sessionStateInternal = $constructor.Invoke($context)
$methodAddSessionStateEntry = $typeSessionStateInternal.GetMethod('AddSessionStateEntry', [System.Reflection.BindingFlags]'Instance,NonPublic', $null, $sessionStateProviderEntry.GetType(), $null)
$methodAddSessionStateEntry.Invoke($sessionStateInternal, $sessionStateProviderEntry)

New-PSDrive -Name Environments -PSProvider Environments -Root ''

New-PSDrive completes without error, though I don't know what sorts of trouble I'll run into with a real provider yet. Of course, using statements could make this more readable. I went without here to keep things straightforward.

Nunci answered 20/10 at 19:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.