Howto Write a PowerShell Module or Function in C# with Visual Studio 2012

This is a post in a series of posts, you can read Step 2, 3, 4 and 5 also.

So you’ve probably more than stumbled upon Microsoft PowerShell at this point. Or you’ll have some really awesome couple of weeks ahead of yourself now that Windows Server 2012 is released. I won’t be getting into explaining PowerShell what it is and why to use it. You know that by now and if you don’t I’m sure that you will find really good resources by searching the web.

Over the last year I’ve been creating lots and lots of PowerShell scripts. Just take a look at my script (here) for downloading TechEd and MMS sessions or my hydration kit for System Center (here). One of the first things that you run into when creating scripts is the need for functions in order to create/do more with less (coding). I like to refer to or think of functions as loops such as do…while or foreach … loops. There isn’t really that much of object orientized programming that you need to know before creating PowerShell functions. They might not be optimal but they’ll work just fine… at least version 15 or 16 Winking smile

For me there were a little breakthrough when I learned howto create functions with CmdletBinding (feature like –Verbose or –Debug). Same with parameters and how to control them (like ValidateSet and so on). After that how to include comments/help for each parameter came pretty quick as well. And soon I were of creating modules with module manifest and stuff.

However I still wasn’t quite satisfied. I really wanted to compile those modules and be able to distribute them in a good fashion way. With the lack of knowledge how to do this I looked into signing and learned to and started to code sign my scripts. Which also is really nice, however users of my script still had to grant permission to run my scripts in PowerShell before the would execute without any issue (if Execution Policy was set to RemoteSigned or AllSigned). And that wasn’t always “good enough”. At least not for me Smile

After searching the web for some time a found a handful of sites with some info here and other info there. This is my contribution.

So lets get going, but where are we going? Our goal with this post is to have a PowerShell module that we can load into PowerShell and get functions that we can run. All in a compiled .DLL file. Let’s really get going then. Oh, we’ll use Visual Studio 2012.

 

Create a class

PoShClass001

First we’ll start by simply creating a project in Visual Studio 2012 containing a simple Class Library (DLL). There isn’t much to say here except – choose the right project template or you’ll get trouble following the rest of this guide.

Add reference to PowerShell

PoShClass002

In order to connect into the PowerShell runspace inside the program language and create functions in PowerShell we must add a reference to the PowerShell library (DLL) which is called “System Management Automation” – guess why.

System Management Automation 6.1

PoShClass003

Okey I admit – I’m coding on Windows 8. It’s great once you start coding in PowerShell and C# to easily transform this knowledge and start developing Windows 8 Apps (WinRT/Metro). Give it a try why don’t you! Anyhow, I still want my code to be able to run in Windows 7 without PowerShell 3.0 deployed so I’ve copied the System.Mangement.Automation.dll from Windows 7 (see second picture showing file version 6.1) into c:temp on my development machine. Foot note: I’m not 100% sure that this really is a requirement but I figured it didn’t hurt. We’ll also need System.Management framework imported as well.

PoShClass004

Removing what we don’t need

It’s always good to have it nice and clean around you. This time around, we won’t be able to compile the code.

PoShClass005

Uncheck Microsoft.CSharp.

PoShClass006

Remove the line containing “using System.Threading.Tasks;”. Soon we’ll start with that code. I promise!

Change to .Net Framework 3.5

PoShClass007

Double click “Properties” in the “Solution Explorer” to the right and when you get the same view as above change “Target Framework” to “.NET Framework 3.5” instead and select “Yes” in the popup question. While at it, you can see in the Solution Explorer that “System.Management” and “System.Management.Automation” has been added under “References”.

Creation of first PowerShell function in c#

PoShClass008

Just above the first class (which we will rename later) definition we add a line that starts with

[System.Management.Automation.Cmdlet(System.Management.Automation.VerbsCommon.Get, “OperatingSystemSKU”)]

or, if we go ahead and add a line after “using System.Text;” like

using System.Management.Automation;

we can shorten the VerbsCommon-string like in the picture below.

PoShClass009

As you can see in the first picture in the section there are lots of verbs to choose from. And this is a point where I spent some time figuring out myself since no one made it this visual or even commenting on it. Some verbs live within VerbsCommon such as “Get”, others like “Protect” live within “VerbsSecurity”. In PowerShell you can type “Get-Verbs” and you’ll get a neat list with the different verbs and where the live.

Note: It’s considered good practice to reuse these verbs and not make up some yourself when suitable. In other words, use the common verb “Get” instead of “Fetch” for getting information or something like that.

PoShClass010

As I wrote above we rename the class to something that links it to the function, in this example “Get_OperatingSystemSKU” and we make it a “subset” (my own word) of the class System.Management.Automation.Cmdlet. Again, if we add the “using System.Management.Automation;” in the beginning we only add “Cmdlet” so we get

public class Get_OperatingSystemSKU : Cmdlet

At this point we could actually compile the code and execute it but why would we? It doesn’t produce anything, no output.

Get something done!

The moment we all have been waiting for. Time to actually add some code. At this time what we’ve done in PowerShell equivalent is

function Get-OperatingSystemSKU

{

}

“Wow, that isn’t much.” you might think. Correct! But by using this method we’ll be able to c o m p i le it – and that is awesome Smile

Back to business… coding.

PoShClass011

Since this function doesn’t accept pipeline data/parameters (actually no parameters at all) we’ll use the EndProcessing instead of ProcessRecord which most blogs suggest. To actually get some output we write

WriteObject(“Hello world!”, true);

Don’t ask me about the true part, I’ve tried to find some logic to it but haven’t wasted enough time on it. It works – don’t break it (that much).

A “Hello World” in all glory but let’s get serious. WriteObject returns data to the console, try to always return objects instead of strings so that you’ll be able to reuse the data in the scripts that you’ll use this cmdlet in.

string ouput = “A string of output”;

WriteObject(output, true);

Would be better, sadly in this example that is not more with less.

PoShClass012

I’ve added some code to actually get that SKU back. At this link I found all different SKU’s there is in Microsoft Windows.

Run code, run

So we hit F6 and the code compiles.

PoShClass013

If we would have changed the file name for the class file in Solution Explorer the output file’s name would be different. For this guide this will do fine.

We copy the dll to a different folder and import it just as any other module and viola we have a new cmdlet that we can execute…

PoShClass014

Good luck with all coding!

 

/Tim

About The Author

Tim Nilimaa is a consultant with Lumagate in Sweden. He has been working with Configuration Manager for 8 years. His knowledge has been selected as a speaker at many events among them Microsoft Management Summit.

11 Comments

  1. Infoworks » Howto write PowerShell Cmdlet in C# using Visual Studio 2012: Write Verbose Output says:

    […] again I continue my work on my first DLL. You can read part 1, 2, 3 and 4 on the […]

    Reply
  2. James Sperring says:

    Nice introduction article. Just wanted to provide some clarification on the WriteObject(object, bool) method.

    The bool parameter is to tell PowerShell whether to enumerate the object passed in to it (providing the object implements IEnumerable). Passing it as true will cause a little extra processing in the System.Assembly.Automation class. Instead of just passing the input to the pipeline, it has to check whether it has an enumerator and then output it.

    You can call the method as just WriteObject(object) instead, or pass the bool parameter as false, unless you intend to pass an enumerable at some point.

    Hope that prevents some future confusion :)

    Reply
    • Tim says:

      Great clarification James! In other words, there is really only needed to (or actually, you shouldn’t unless) set this to true if passing an array to back to the console.

      Reply
  3. Rex says:

    Hey there,

    You don’t mention exporting the cmdlet. How are you able access the function after you import the module? I followed your tutorial but wound up with no exported commands when I type “get-module”.

    PS C:> Get-Module

    Module Type Name ExportedCommands
    ———- —- —————-
    Binary TestModule {}

    Reply
    • Tim says:

      You can simply type the commands “name”. I’ll try to find the time before the holidays to write something about exporting the modules as well. Thanks for the comment Rex!

      Reply
  4. will says:

    @Tim
    any answer for Rex question above?

    Reply
  5. Tim Nilimaa says:

    @will
    You can run Get-Command -Module to get the commands.

    Reply
  6. Abhijit says:

    Very Well explained Tim. Thanks a lot. This article serve my purpose of creating custom cmdlet.

    For those you are using VS10 & Windows7, System.Management.Automation need to added from “C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\v1.0.”

    Reply
    • Tim Nilimaa says:

      Great to hear that Abhijit! And that’s a good comment on whats needed for Windows 7 (actually, earlier version of PowerShell)

      Reply
  7. bitbonk says:

    The links to the other steps are broken.

    Reply