This post will cover the basics of getting your environment setup to create a PowerShell navigation provider. All of the code is available on GitHub. I have created a v0.1 tag which represents the code that will be covered in this post.

  • Create a new Class Library project in Visual Studio.
  • Add a reference to System.Management.Automation from the C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0 directory.

  • Modify your project debug settings to start an external program and start C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

  • Add a CmdletProvider attribute to your class, have it inherit from NavigationCmdletProvider and implement the required members:

    [CmdletProvider("MyPowerShellProvider", ProviderCapabilities.None)]
    public class MyProvider : NavigationCmdletProvider
    {
        protected override bool IsValidPath(string path)
        {
            return true;  // All paths are valid for now
        }
    }
    

The next thing we will do is create a script to install the provider when we start debugging.

  • Add a new file to the project and call it InstallProvider.ps1. This installs our PowerShell module into the current PowerShell session. The contents of the file are:

    Import-Module .\PowerShellProvider.dll
    
  • Set the Copy to Output Directory property to Copy if newer on the file.

  • You can manually run this file every time you start PowerShell or you can update your debug settings and add the following to the command line arguments:

    -NoExit  -File .\InstallProvider.ps1
    

Now we can start implementing the overrides we need to get the provider working. The first method you want to implement is the InitializeDefaultDrives. This method is called when you install the provider and will pre-create drives so users don't need to explicitly call New-Drive.

protected override Collection<PSDriveInfo> InitializeDefaultDrives()
{
    PSDriveInfo drive = new PSDriveInfo("MyDrive", this.ProviderInfo, "", "", null);
    Collection<PSDriveInfo> drives = new Collection<PSDriveInfo>() {drive};
    return drives;      
}

At this point you can start debugging and if you call Get-PSDrive, you will see the drive, but you will not be able to navigate to it. Let's fill out a few more overrides, so we can navigate to our provider. To start, we will allow the user to navigate to any folder they type in. We will implement ItemExists and IsItemContainer:

protected override bool ItemExists(string path)
{
    return true;
}

protected override bool IsItemContainer(string path)
{
    return true;
}

Now when you debug, you can navigate into the drive by typing cd MyDrive: and you can change folders to your heart's content. You'll notice if you type dir you still get an error. Let's fix that. Simply override the GetChildItems method:

protected override void GetChildItems(string path, bool recurse)
{
    WriteItemObject("Hello", "Hello", true);
} 

Now, you can navigate around and when you type dir, you will always get back your Hello object. Congratulations, you have a working navigation provider. In the next post in this series we will wire up the provider to a backend API and return custom objects instead of just strings.

The full code for the provider is:

[CmdletProvider("MyPowerShellProvider", ProviderCapabilities.None)]
public class MyPowerShellProvider : NavigationCmdletProvider
{

    protected override bool IsValidPath(string path)
    {
        return true;
    }

    protected override Collection<PSDriveInfo> InitializeDefaultDrives()
    {
        PSDriveInfo drive = new PSDriveInfo("MyDrive", this.ProviderInfo, "", "", null);
        Collection<PSDriveInfo> drives = new Collection<PSDriveInfo>() {drive};
        return drives;
    }

    protected override bool ItemExists(string path)
    {
        return true;
    }

    protected override bool IsItemContainer(string path)
    {
        return true;
    }

    protected override void GetChildItems(string path, bool recurse)
    {
        WriteItemObject("Hello", "Hello", true);
    }
}

You now have the most basic of basic providers available to you. In the next article, I cover the basics of getting GetChildItems() working to return something more real and more readable.