Introduction
In a prior article, Constructing your First PowerShell Provider, we covered PowerShell Provider development. In my follow-up article, Debugging and Designing Custom .NET Framework PowerShell Providers, we looked at more advanced PowerShell development topics like Provider design and debugging. Both articles focused mostly on providers. Most solutions, however, will likely include Custom CmdLets that access data in the PowerShell Pipeline and work in conjunction with a custom provider.
In this article I want to “close the loop”, so to speak, between Custom Providers and Custom CmdLets. I’m going to demonstrate how Custom CmdLets work with a Customer Provider and then how the components leverage the PowerShell Pipeline.
Overview
A complete introduction to PowerShell CmdLets, providers, and the PowerShell Pipeline are beyond the scope of this article. However some context is needed to understand the technical issues.
A PowerShell Provider serves as a sort of data source. Like a database data source a PowerShell programmer can navigate a provider using standard conventions and a standard set of operations. CmdLets are the operations a programmer applies to a provider. A standard set of providers and CmdLets ships with PowerShell, but a developer is free to build Custom CmdLets and providers. CmdLets often write their results to the PowerShell Pipeline and pull data out of the PowerShell Pipeline. The PowerShell Pipeline enables chaining segments of CmdLet operations together, receiving data from the Pipeline, writing data to the Pipeline, or reading and writing to the Pipeline.
Because all CmdLets and Providers are built upon the .NET Framework, objects emitted by the standard set of CmdLets can be consumed by Custom CmdLets and vice-versa.
The accompanying sample applications demonstrate how the Provider, CmdLet, and Pipeline interaction works. Leveraging a standard Windows Event Log CmdLet, the sample:
- Navigates a Provider
- Reads the Application Event Log using the standard PowerShell CmdLet
- Packages the Event Log data and writes the data to the Pipeline
- Reads the data from a Pipeline and invokes a CmdLet which saves the Event Log information to the Provider
The full PowerShell code appears below:
add-PSSnapin PSCoreProviderPSSnapIn #Navigate to a place in the Provider set-location Test:Children1 #Read Event Log data, package the data, and write the packaged data to the current Provider Get-Eventlog -LogName Application -EntryType Information -Newest 5 | New-EventLogItem -SendCollection $false | Set-EventLogItem Get-Eventlog -LogName Application -EntryType Information -Newest 5 | New-EventLogItem -SendCollection $true | Set-EventLogItem
Figure 1
In the code above, Get-EventLog is a standard PowerShell CmdLet which reads events from the event Log and writes the information to the PowerShell Pipeline. Set-Location is a standard PowerShell CmdLet utilized to navigate a provider. The other CmdLets and providers are custom code from the sample.
There is a subtle difference between lines 5 and 6 that demonstrate different ways a CmdLet can send data to the pipeline. I’ll cover those differences later in the article. First, though, I’ll briefly cover building a custom CmdLet before delving into how the sample code executes.
Custom CmdLet Primer
A complete introduction to building a custom CmdLet is beyond the scope of this article. As I stated earlier though, some context is needed to understand the sample.
The code below differentiates a CmdLet from another .NET framework class.
[Cmdlet(VerbsCommon.Set, "EventLogItem", SupportsShouldProcess = false)] public class PSSetEventLog : PSCmdlet
CmdLets typically derive from PSCmdLet and are decorated with the Cmdlet attribute. The CmdLet attribute describes the CmdLet to PowerShell. When a CmdLet is invoked by PowerShell, CmdLet methods are called in the following order.
- BeginProcessing method is called when the CmdLet is first activated
- ProcessRecord is invoked for each piece of data in the Pipeline
- EndProcessing is called when CmdLet Activation ceases
CmdLets override one or more methods mentioned above. CmdLet parameters are expressed as class properties decorated with parameter attributes. A sample CmdLet parameter from the PSNewEventLog class appears below:
[Parameter(Mandatory = true, HelpMessage = @"Determines how to process pipeline values", ValueFromPipeline = false)] public bool SendCollection { get; set; }
When the PowerShell script runs, New-EventLogItem
is the first custom CmdLet invoked in the sample code. PSNewEventLog is the class associated with the CmdLet. PSNewEventLog receives data from the pipeline and sends data to the pipeline using one of two different methods. Later in the article I’ll explain the different methods, first I’ll explain how the CmdLet pulls data from the pipeline.