Thursday, November 26, 2020

Creating a PowerShell Cmdlet Using C# and DotNet.eXE

As part of a book I am writing, I wanted to show how simple it could be to create your own cmdlet in C#. This was to be a part of an advanced look at the things you can do with PowerShell 7 and DotNet Core. 

Most IT professionals know about the .NET objects produced by cmdlets, how to discover details about those objects, and how to reach into the .NET BCLs to do things that cmdlets can't. You can also extend PowerShell using a .NET Language such as C# to create a class. You use Add-Type to import the class into your PowerShell session and use it just like any other .NET class. Creating a cmdlet turned out to be more complex.

The first question any sane person might ask is; WHY? Why would anyone want to write a cmdlet or create a class using C#? The obvious reason, per Jimmy The Swede, is because you can. As an IT professional, knowing how is just another tool on your tool belt. But aside from that, there really are some good reasons. Using C# is easier when you need to perform asynchronous operations or multi-threading, or if you wish to use Language-Integrated Query (LINQ). IN those cases, C# is a lot easier than trying to use PowerShell in those scenarios. Another use case is taking some ASP.NET code, such as a page which creates a list of out of stock items and re-purposing it for administrative use.  Admittedly, there are not a lot of use cases in the wild - but as a PowerShell person, you should know!

If you are a hardcore Windows developer, you probably have Visual Studio and know how to use it. But in this article, I'm going to use a different approach which is to use the .NET SDK and the dotnet.exe command. You can do most of this from the command line, although there seems no simple way to install the SDK. So here goes

1. Install the .Net Core SDK.  

This is almost the hardest part of the job. You need to install the Software Development Kit in order to get the tools you need to create the cmdlet. This involves using the GUI.

Navigate to the Dotnet download page: https://dotnet.microsoft.com/download/


From here, click on the download button. This leads to the downloading page:


The download takes a few seconds/minutes to download. Eventually, you can see in the bottom left corner that the download is complete and you can now click on the open file as you can see here:



Clicking on the open file link runs the installer to install the .NET Core SDK on your computer. Eventually, you see this:



Click on Close and you now have the SDK loaded on your system. From here it's fairly simple - here's the script:

# 2. Create the cmdlet folder
New-Item -Path C:\Foo\Cmdlet -ItemType Directory -Force
Set-Location C:\Foo\Cmdlet

# 3. Creating a class library project
dotnet new classlib --name SendGreetings

# 4. Viewing contents of new folder
Set-Location -Path .\SendGreetings
Get-ChildItem

# 5. Creating global.json
dotnet new globaljson

# 6. Adding PowerShell package
dotnet add package PowerShellStandard.Library

# 7. Create the cmdlet source file
$Cmdlet = @"
using System.Management.Automation;  // Windows PowerShell assembly.
namespace Reskit
{
  // Declare the class as a cmdlet
  // Specify verb and noun = Send-Greeting
  [Cmdlet(VerbsCommunications.Send, "Greeting")]
  public class SendGreetingCommand : PSCmdlet
  {
    // Declare the parameters for the cmdlet.
    [Parameter(Mandatory=true)]
    public string Name
    {
      get { return name; }
      set { name = value; }
    }
    private string name;
    // Override the ProcessRecord method to process the
    // supplied name and write a geeting to the user by 
    // calling the WriteObject method.
    protected override void ProcessRecord()
    {
      WriteObject("Hello " + name + " - have a nice day!");
    }
  }
}
"@
$Cmdlet | Out-File .\SendGreetingCommand.cs

# 8. remove class file 
Remove-Item -Path .\Class1.cs

# 9. Build the cmdlet
dotnet build 

# 10. Importing the DLL holding the cmdlet
$DLLPath = '.\bin\Debug\net5.0\SendGreetings.dll'
Import-Module -Name $DLLPath
at that point, you can use the Send-Greeting command, like this:


Thanks to James O'Neil who got me started on this solution. Thanks also to Thomas Rayner's excellent PowerShell summit talk at  https://www.youtube.com/watch?v=O0lk92W799g&t=4s








No comments: