How to create PowerShell CmdLet ?
What is PowerShell CmdLet learn at Microsoft site for CmdLet .Create class library project.
Reference System.Management.Automation from PowerShell folder.
Typically
C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll on x64 system
CmdLet is class inherited from PSCmdlet class (System.Management.Automation namespace).
We need some attributes to describe our CmdLet, at least something like:
[Cmdlet(VerbsCommon.Remove, "Controller")]
Decorating class with this attribute will create Cmdlet which can be invoked by typing
Remove-Controller.
PowerShell CmdLet common Verbs and custom Verb
VerbsCommon class from System.Management.Automation contains set of common verbs i.e. Add, Clear,Get,Set and if you want to use verb which is not defined, attribute declaration will look like:
[Cmdlet("Specify", "Controller")]
This will create Cmdlet which can be invoked by typing
Specify-Controller.
CmdLet parameters
If you need parameters in your Cmdlet you will declare them as class properties decorated with Parameter attribute i.e.:[Parameter(Mandatory = true)]
public int ControllerId {get;set;}
CmdLet ProcessRecord method
In order to perform some work when CmdLet is invoked we need to override method:
protected virtual void ProcessRecord();
and we will do it like:
protected override void ProcessRecord()
{
WriteObject(String.Format("You have entered {0}", ControllerId));
base.ProcessRecord();
}
Importing PowerShell Module
Start Windows PowerShell and import module by issuing command:
Import-Module pathtodll
If you used non-common verb (like in our example) you will receive warning
WARNING: The names of some imported commands from the module 'PowershellPlayground' include unapproved verbs that might make them less discoverable. To find the commands with unapproved verbs, run the Import-Module command again with the Verbose parameter. For a list of approved verbs, type Get-Verb.
Your CmdLet is now available in this specific workspace. If you close Windows PowerShell window and open new, module will not be imported.
Let's run it by typing in PowerShell
Specify-Controller
As we marked ControllerId mandatory PowerShell will prompt:
cmdlet Specify-Controller at command pipeline position 1
Supply values for the following parameters:
ControllerId:
After you enter value you will have your value printed.
Optional CmdLet parameters
Let's change decoration of our parameter:[Parameter(Mandatory = false)]
public int ControllerId { get; set; }
Rebuild. Import module to workspace and run Specify-Controller.
PowerShell will not prompt to enter parameter value and will print:
You have entered 0
Let's play but more and change parameter decoration once more:
[Parameter(Mandatory = false)]
public int? ControllerId { get; set; }
Rebuild, import module and run
Specify-Controller
Have in mind that rebuild will fail if you have PowerShell workspace with imported dll as PowerShell locks imported file.
PowerShell will print
You have entered
Let's run command with parameter specified:
Specify-Controller -ControllerId 2
Powershell will print:
You have entered 2
Write object from CmdLet
Let's create new class in our projectpublic class Response
{
public int Number { get; set; }
public DateTime TimeProcessed { get; set; }
public string Machine { get; set; }
}
and change ProcessRecord implementation
protected override void ProcessRecord()
{
var response = new Response
{
Number = ControllerId,
TimeProcessed = DateTime.Now,
Machine = Environment.MachineName
};
WriteObject(response);
base.ProcessRecord();
}
Rebuild, import and run
Specify-Controller -ControllerId 4
Response will be
Number TimeProcessed Machine
------ ------------- -------
4 29.10.2014 9:39:43 LIGHTNING
Let's change our Response class
public class Response
{
[DisplayName("Time processed")]
public DateTime TimeProcessed { get; set; }
public string Machine { get; set; }
public int? Number { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public string Property4 { get; set; }
public string Property5 { get; set; }
public string Property6 { get; set; }
public string Property7 { get; set; }
public string Property8 { get; set; }
public string Property9 { get; set; }
public string Property10 { get; set; }
}
Rebuild, import module and run
Specify-Controller -ControllerId 4
Result is:
TimeProcessed : 29.10.2014 9:45:41
Machine : LIGHTNING
Number : 4
Property1 :
Property2 :
Property3 :
Property4 :
Property5 :
Property6 :
Property7 :
Property8 :
Property9 :
Property10 :
We can see that display name is not affecting how PowerShell prints object. We can also see that order of properties will be same as order in which they are specified in class.
CmdLet Parameter from Pipeline
Let's create another Cmdletusing System.Management.Automation;
namespace PowershellPlayground
{
[Cmdlet(VerbsCommon.Get,"CalculatedValue")]
public class ExampleCmdLet2 : PSCmdlet
{
[Parameter(Mandatory = true, Position = 1, ValueFromPipeline = true)]
public Response response { get; set; }
protected override void ProcessRecord()
{
response.Number += 10;
WriteObject(response);
base.ProcessRecord();
}
}
}
We have defined one parameter of Response type, mandatory and we have indicated that we will be able to receive this value from pipeline. Rebuild, import module and run:
Specify-Controller -ControllerId 5
Response will be
TimeProcessed : 29.10.2014 9:54:55
Machine : LIGHTNING
Number : 5
Now run
Specify-Controller -ControllerId 5 | Get-CalculatedValue
Response will be:
TimeProcessed : 29.10.2014 9:55:11
Machine : LIGHTNING
Number : 15
Create PowerShell Snap-in
To create PowerShell Snap-in we need to add extra class to our project. Here's example:using System.ComponentModel;
using System.Management.Automation;
namespace PowershellPlayground
{
[RunInstaller(true)]
public class PowerShellPlayground : PSSnapIn
{
public override string Description
{
get { return "Playing with PowerShell"; }
}
public override string Name
{
get { return "PlayingWithPowerShell"; }
}
public override string Vendor
{
get { return "MCo"; }
}
}
}
This class inherits abstract class PSSnapIn and overrides few properties talking about our Snap-in. Class is actual installer so it needs to be marked with RunInstaller(true).
Rebuild solution.
Installing PowerShell Snapin
In order to make PowerShell aware of our Snap-in we need to install it using installutil:From PowerShell:
Set-Alias installutil C:\windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe
installutil pathtodll
Uninstalling PowerShell Snapin
From PowerShell:Set-Alias installutil C:\windows\Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe
installutil -u pathtodll
Check that Snap-In is installed
Execute PowerShell CmdLet
Get-PSSnapin -Registered
You should be able to see following entry
Name : PlayingWithPowerShell
PSVersion : 3.0
Description : Playing with PowerShell
Add PSSnapin to workspace
Execute PowerShell CmdLet:
Add-PSSnapin PlayingWithPowerShell
List commands available in CmdLet
Execute PowerShell CmdLet:
Get-Command -PSSnapin PlayingWithPowerShell
Get all command with specific verb
Execute PowerShell CmdLet:
Get-Command -Verb Specify
Read more about Get-Command at Microsoft site.
Get detailed information on CmdLet usage
Execute PowerShell CmdLet:
Get-Help Specify-Controller
Response will be:
NAME
Specify-Controller
SYNTAX
Specify-Controller [-ControllerId <int>] [<CommonParameters>]
ALIASES
None
REMARKS
None
Override PowerShell CmdLet
You can override CmdLet by crating one with same name. We will override Get-WSManInstance CmdLet.Let's create our CmdLet
using System.Management.Automation;
namespace PowershellPlayground
{
[Cmdlet(VerbsCommon.Get, "WSManInstance")]
public class GetWsManInstance : PSCmdlet
{
protected override void ProcessRecord()
{
WriteObject("Hello World!");
base.ProcessRecord();
}
}
}
Rebuild but do not import.
Start PowerShell and run
Get-WSManInstance
PowerShell will respond with
cmdlet Get-WSManInstance at command pipeline position 1
Supply values for the following parameters:
ResourceURI:
This is "default" implementation from Microsoft.WSMan.Management module.
Lets import our module:
Import-Module pathtodll
Run
Get-WSManInstance
And PowerShell will respond with
Hello World!
If you have multiple overrides of same CmdLet imported in PowerShell workspace last CmdLet imported will be one that will be executed.