C#4PS – compiled powershell modules, part 3

This is part 3 of a multi-part blog.

Part 1Part 2 – Part 3

Previously on C#4PS:

We wrote and compiled a really useless class, and showed that we could access it in PowerShell.

capture

And to think they voted us “least likely to succeed”.

 

RunspacePool

In this chapter, we’re going to use the RunspacePool class to parallelise PowerShell code. And we’re going to compile it as a PowerShell module.

Background on the RunspacePool class

You may be familiar with Runspaces, which are an important part of the PowerShell remoting, multithreading and asynchronous landscapes. They are to be contrasted with PSJobs in that a PSJob is a separate process, whereas a runspace is merely a separate thread. That makes them faster, because threads don’t need to have a new process spun up by the OS, but it makes them non-thread-safe. Code is thread-dangerous if there is the possibility of multiple threads accessing the same variables and objects, because you can have this:

Part 1Part 2 – Part 3

Powershell snippets - Get names and IPs in a failover cluster

If you administer a lot of Windows clusters, you frequently need to get hostnames, IPs and SQL instance names on a regular basis but! the FailoverClusters powershell module declines to give that to you without a fight.

Hopefully this saves you a bit of effort. Usage:

Get-ClusterName
Get-ClusterIpAddress
Get-ClusterSqlInstanceName
#Get all the DNS names used on a cluster
function Get-ClusterName {
    Get-ClusterNode | select @{Name = "ClusterResource"; Expression={"Cluster Node"}}, OwnerGroup, Name, DnsSuffix
    Get-Cluster | Get-ClusterResource | ?{$_.ResourceType -like "Network Name"} | %{
        $_ | select `
            @{Name = "ClusterResource"; Expression={$_.Name}},
            OwnerGroup,
            @{Name="Name"; Expression={$_ | Get-ClusterParameter -Name Name | select -ExpandProperty Value}},
            @{Name="DnsSuffix"; Expression={$_ | Get-ClusterParameter -Name DnsSuffix | select -ExpandProperty Value}}
    }
}
#Get all the IP addresses used by a cluster
function Get-ClusterIpAddress {
    Get-Cluster | Get-ClusterResource | ?{$_.ResourceType -like "IP Address"} | %{
    $_ | select `
        @{Name = "ClusterResource"; Expression={$_.Name}},
        OwnerGroup,
        @{Name="Address"; Expression={$_ | Get-ClusterParameter -Name Address | select -ExpandProperty Value}},
        @{Name="SubnetMask"; Expression={$_ | Get-ClusterParameter -Name SubnetMask | select -ExpandProperty Value}}
    }
}
#Get all SQL cluster instance names
function Get-ClusterSqlInstanceName {
    if (-not (Get-Command Get-ClusterName -ErrorAction SilentlyContinue)) {throw "Please also import the Get-ClusterName function from https://github.com/fsackur"}

    $ClusterNames = Get-ClusterName;
    $ClusterGroups = Get-ClusterGroup
    $Namespace = (Get-WmiObject -Namespace "ROOT\Microsoft\SqlServer" -Class "__Namespace" -Filter "Name LIKE 'ComputerManagement%'" | sort Name -Descending | select -First 1 @{Name="Namespace"; Expression={$_.__NAMESPACE + "\" + $_.Name}}).Namespace
    $SqlInstanceWmi = Get-WmiObject -Namespace $Namespace -Class "SqlService" -Filter "SqlServiceType = 1"

    $SqlInstanceWmi | ForEach-Object {

        $InstanceId = $_.ServiceName
        [bool]$DefaultInstance = $InstanceId -like "MSSQLSERVER"
        $Instance = $InstanceId -replace '^MSSQL\$'

        $ClusteredWmi = Get-WmiObject -Namespace $Namespace -Class "SqlServiceAdvancedProperty" -Filter "PropertyName = 'CLUSTERED' AND ServiceName = `'$InstanceId`'"
        [bool]$Clustered = $ClusteredWmi.PropertyNumValue -ne 0
        # Virtual Server object, for clusters
        $VsNameWmi = Get-WmiObject -Namespace $Namespace -Class "SqlServiceAdvancedProperty" -Filter "PropertyName = 'VSNAME' AND ServiceName = `'$InstanceId`'"
        $VsName = $VsNameWmi.PropertyStrValue

        if ($VsName) {$NetworkName = $VsName} else {$NetworkName = $env:COMPUTERNAME}
        if ($DefaultInstance) {$InstanceName = $NetworkName} else {$InstanceName = $NetworkName + "\" + $Instance}

        $ClusterName = $ClusterNames | ?{$_.Name -like $NetworkName}

        return New-Object psobject -Property @{
            InstanceName = $InstanceName;
            OwnerGroup = $ClusterName.OwnerGroup
        }
    }
}

Code is here

C#4PS – compiled powershell modules, part 2

This is part 2 of a multi-part blog.

Part 1 – Part 2 – Part 3

Previously on C#4PS:

We installed Visual Studio and a couple of extensions, we set up a new project in a new solution, and we changed the theme to Dark because we are dark and moody.

Code window ready:

capture

That’s all we did in Part 1, which is why you feel so restless.

Write code and import it into PowerShell

We add a static property, Two, and a static method, GetCritique():

capture

Note that I’ve added “public” in front of the class definition. Otherwise, the class will load in PowerShell but it will be invisible and you won’t be able to call it, like God during a bout of existential angst.

We build it and run it in the usual way, by hitting Start in the tool bar, or pressing F5.

capture

Oh, we can’t. We’re building a class library, and that won’t run itself.

capture

So we instead use Ctrl-Shift-B, or Build > Build Solution. This puts the binary in StupidClass\bin\Debug, since I have the Debug build configuration selected, along with a .pdb file which helps a debugger provide readable output. We open PowerShell and run:

Import-Module ".\PSUGUK-Compiled-PSModule\StupidClass\bin\Debug\StupidClass.dll"

We can now use this class like any other .NET class:

capture

Tab completion works, we can access the static members with the double-colon syntax, we even get our nice typo - neough said.

If that didn’t work, did you remember to add “public” before “class StupidClass”?

Let’s add an instance method, GetInstanceCritique:

capture

This time, when we build again, it balks, because we have a file lock open on the output file. That’s because we’re trying to overwrite the .dll file that we just loaded in PowerShell.

capture

I know what you’re thinking. “Aha,” you nod, “we need to run Remove-Module in our PowerShell window.”

Won’t work. When you import that module, you load the StupidClass type into the AppDomain. You can never unload a class from an AppDomain in .NET, not even with -Force. Not even with sudo –force. You have to close the PowerShell host.

So go ahead and do that.

Now it builds when you press Ctrl-Shift-B, and if you open a new PowerShell host and import it again, you can instantiate the object with New-Object and call that instance method:

capture

I’m sure you are using tab completion here. This stuff practically writes itself, it’s so low-effort. What can we do about that annoying close-and-reopen malarkey? Well, you could write a script that uses System.IO.FileSystemWatcher and Register-ObjectEvent to pick up on file changes, but we are actually going to use Visual Studio’s Build Events.

Build Events

You access these from the Project Properties window:

capture

As a reminder, you can get to that either through Project > StupidClass Properties, or by right-clicking on the StupidClass project in Solution Explorer:

capture

We need a pre-build event to close the PowerShell window from the last run, and a post-build event to open PowerShell and import the module. You can paste the events straight in here or, if you don’t like this nice script I’ve cooked for you and want to do something different, you can click on the Edit buttons and access the Macros. Macros will embed variables related to the project or solution you are building, such as output paths.

Pre-build:

start powershell -NoProfile -Command "Get-Process powershell | where {$_.MainWindowTitle -eq '$(TargetName)'} | Stop-Process"

Post-Build (see note about cmdow):

C:\dev\cmdow\bin\Release\cmdow.exe /run powershell -NoExit -Command "sl $(ProjectDir); $host.UI.RawUI.WindowTitle ='$(TargetName)'; Import-Module $(TargetPath)"

Build Events are janky batch script and you’ll need to escape anything that looks like a macro.

What will annoy you quickly is how stubborn Visual Studio is about waiting for you to close the just-opened PowerShell host before it returns. StackOverflow pointed me to a little app called cmdow, which you will need to download from [here](https://github.com/ritchielawrence/cmdow/zipball/master”>. Update the build event script above to reflect where you save it. This allows you to code while you still have the class loaded in PowerShell.

Next up: write code that isn’t stupid!

Part 1 – Part 2 – Part 3

C#4PS - compiled powershell modules, part 1

This is part 1 of a multi-part blog.

Part 1 – Part 2Part 3

Intrrrrooooo!

You muck around with PowerShell for long enough, you probably write a few .psm1 script modules, you use advanced parameters, you understand types. You might start to find yourself a bit limited. This is an introduction to extending your content creation into C# and writing a compiled module in C# - that’s right, just like all the PowerShell that Microsoft ships.

I’m going to assume no prior knowledge of Visual Studio or of C#, but you should be familiar with PowerShell, using .NET classes, and have a grasp on the basics of object-oriented paradigms. Paradigms. I love that word.

This content was originally presented at a PowerShell User Group UK meetup. If you’re in the South East of the UK, come along and say hi! Or see other groups worldwide.

[caption id=”attachment_818” align=”aligncenter” width=”300”]600_455496529 Yochay Kiriaty presents to PSUG UK, November 2016[/caption]

But why?

* Compiled code will run faster than script
* Sometimes, fewer lines of code
* Using C# language features such as event-driven programming
* Anything with a GUI
* Anything of any complexity involving multi-threading
* Object inheritance, polymorphism, interfaces
* Using design patterns for which Visual Studio templates exist
* Using Visual Studio - it's very powerful
* More powerful IntelliSense than ISE
* Code analysis
* Organisation of large projects - if it's over 1000 lines, should it be in a script?
* It might fit in better to your organisation's deployment practices
* It rocks

Choose your weapon

You can use any IDE you want Visual Studio 2015. At time of writing, the Visual Studio 2017 RC isn’t compatible with PowerShell Tools.

A word on editions:

* **Visual Studio Express for Desktop** is _cost-free_, but you cannot install tools from the gallery. That rules out PowerShell Tools, but that's not strictly necessary for this guide. You are not limited in who can consume your code.
* **Visual Studio Community** is also cost-free, but you cannot use code that you produce at your company (unless you open-source it first). I'm using Community in this guide; code is [here](https://github.com/fsackur/PSUGUK-Compiled-PSModule)
* **Visual Studio Pro**,** Enterprise**,** Ultimate** are all fine if you can pay for them.

Download from (https://www.visualstudio.com/vs/community/). I recommend also installing PowerShell Tools for the bargain price of $free.

Setting up Visual Studio

Devs may spend days at a new job setting up their dev environment. We won’t. Here’s my cut-down first steps with VS:

* Tools > Options: <img class="aligncenter size-full wp-image-851" src="https://freddiesackur.files.wordpress.com/2017/02/capture.png" alt="capture" width="744" height="434" />I am not a fan of my projects going into my user profile. I set paths in Projects and Solutions; C:\dev is nice as far as I'm concerned.
* If you installed PowerShell Tools, then you'll want to go into Text Editor > PowerShell and enable line numbers. That will apply if you edit PS script in VS. <img class=" size-full wp-image-857 aligncenter" src="https://freddiesackur.files.wordpress.com/2017/02/capture1.png" alt="capture" width="744" height="434" />
* You have to use at least the Dark theme (Environment > General) if you want to be considered a coder: <img class="alignnone size-full wp-image-868" src="https://freddiesackur.files.wordpress.com/2017/02/capture3.png" alt="capture" width="744" height="434" /> I also like lyphtec's [Solarized](https://github.com/lyphtec/solarized).
* Even though VS Community is free, you do need to sign in to activate it. At the top-right: <img class="alignnone size-full wp-image-864" src="https://freddiesackur.files.wordpress.com/2017/02/capture2.png" alt="capture" width="649" height="308" />
* If you use Github, you can install the extension from Tools > Extensions. Select Online and type in the search bar. You can also install PowerShell Tools here. Unless, of course, you are using VS Express, in which case you can't. <img class="alignnone size-full wp-image-872" src="https://freddiesackur.files.wordpress.com/2017/02/capture4.png" alt="capture" width="941" height="653" />

That’s the VS general setup that you’ll need. You can import and export these settings, an exercise which I will leave to you.

Start a new project

File > New > Project. Hey, this is easy!

capture

Everything in Visual Studio exists inside a solution file (.sln). This can contain one or more projects. You will typically want to keep a one-to-one mapping between a solution and a git repo. In this exercise, we’re going to start off with a project to write a stupid class, which we will put in a project called StupidClass. In C:\dev. Got that? Good. We’ll do better projects later.

We are choosing .NET Framework 4 at the top because we want to support PowerShell 3.0. If we chose 4.5, our code would require PS 4.5. This can be changed later.

Typically you will write a Console application (CLI-only) or a Windows Forms or WPF Application (GUI). However, we’re writing classes to consume in PowerShell, so we choose Class Library. Have you ever noticed a lot of .dll files kicking around? Those are class libraries. We’re going to write and compile a .dll file. Click ‘OK’ if you’re cool with that.

Our brand-new project:

Capture.PNG

The main window is a code file at the moment. The bottom pane is the build output, which is empty because we haven’t compiled anything yet. The top-right is the Solution Explorer, which contains some things that are files and some things that aren’t.

The code:

capture

OK, that’s all wrong. We don’t want to call the class Class1, and we don’t want to call a namespace StupidClass. You know when you do something like this in PowerShell:

capture

In this case, we are accessing a class called RegexOptions (an Enum actually, but same thing applies) in the System.Text.RegularExpressions namespace. We use namespaces so that we don’t get clash for classes with the same names. If you have no better ideas for a namespace, use your organisation’s DNS name, backwards, e.g. com.contoso. That ought to avoid name clash with any third-party code, but is also not very helpful. For this guide I’m going to use the namespace Psuguk, for these guys. So:

In Solution Explorer (on the right-hand side), right-click on the Project and go to Properties. The project is going to be one down from the top-level in the tree (which you’ll guess correctly is the Solution itself).

capture

This gives you the Project Properties window, which we’ll use a lot in this guide. You can also get to it from the Project menu in the main menu bar.

capture

Change the Default Namespace to Psuguk. Capitalisation matters everywhere, and convention is that classes and namespaces use caps, but variables use camelCase.

FYI, “Assembly name” here means the output file name.

Oy, that did not update the code file. We actually do need to rename it in code. But don’t just type it - right-click on the namespace name and choose “Rename”. Now overtype it with “Psuguk”.

Capture.PNG

Why did we do that? Because when you have 19 source code files, doing it this way lets Visual Studio take care of trawling through and updating everything everywhere. It’s considerably smarter than Ctrl-H in ISE. This also lets me show you the right-click context menu; Go To Definition lets you zoom to the definition of whatever method or class you are clicking on, and is insanely useful.

For renaming Class1, please right-click on the source file Class1.cs in the Solution Explorer:

capture

If you rename it this way, Visual Studio will offer to rename it in code as well.

Code window ready:

capture

Boring bit over, we are ready to write and compile some code!

Part 1 – Part 2Part 3

PowerShell snippet - find the top CPU consumer

A snippet to provide text output on the process using the highest CPU.

This will tell you the service name if it is running in an instance of svchost, or the app pool name if it is an IIS worker process.

Get-Process does not provide a snapshot reading like Task Manager does, only a “CPU Time” counter that increments. This takes a view of CPU used over 5 seconds to determine the process consuming the most CPU. It then iterates over all processes, creating an array with a calculated CPU usage value. This code selects the top one offender, but it could be modified to show the top five and the CPU time used.

Example usage and output:

PS H:\PowerShell> Get-CpuTopOffender
Top CPU hog in last 5 seconds: chrome with PID 12684
PS H:\PowerShell> Get-CpuTopOffender
Top CPU hog in last 5 seconds: w3wp with PID 3480
Application pool: ASP.NET v4.0

Code:

#High CPU - find PID and process name of top offender, and application pool if the process is w3wp
function Get-CpuTopOffender {
    #we require this select statement to break the link to the live data, so that the processlist contains point-in-time data
    $ProcessList1 = Get-Process | select ProcessName, Id, @{name='ms'; expr={$_.TotalProcessorTime.TotalMilliSeconds}} |            Group-Object -Property ID -AsString -AsHashTable
    $Seconds = 5
    Start-Sleep -Seconds $Seconds
    $ProcessList2 = Get-Process | select ProcessName, Id, @{name='ms'; expr={$_.TotalProcessorTime.TotalMilliSeconds}} |
        Group-Object -Property ID -AsString -AsHashTable

    $CalculatedProcessList = @()

    foreach ($ProcessId in $ProcessList1.Keys) {
        $Name = ($ProcessList1.$ProcessID)[0].ProcessName

        $CpuTotalMs1 = ($ProcessList1.$ProcessID)[0].ms
        if (-not ($ProcessList2.$ProcessID)) {continue}
        $CpuTotalMs2 = ($ProcessList2.$ProcessID)[0].ms

        $Calc = New-Object psobject -Property @{
            Name = $Name;
            PID = $ProcessID;
            CPU = $($CpuTotalMs2 - $CpuTotalMs1)
        }

        $CalculatedProcessList += $Calc
    }

    $TopOffender = $CalculatedProcessList | sort CPU -Descending | select -First 1

    $Output = "Top CPU hog in last $Seconds seconds: $($TopOffender.Name) with PID $($TopOffender.PID)"

    #Add extra info
    if ($TopOffender.Name -like "svchost") {
        $ServiceNames = (Get-WmiObject -Query "SELECT Name FROM Win32_Service WHERE ProcessId = $($TopOffender.PID)" | %{$_.Name})
        $Output += "`nServices hosted: $($ServiceNames -join ', ')"
    }

    if ($TopOffender.Name -like "w3wp") {
        $ProcessWmi = (Get-WmiObject -Query "SELECT CommandLine FROM Win32_Process WHERE ProcessId = $($TopOffender.PID)")
        $Cli = $ProcessWmi.CommandLine
        $AppPool = ($Cli -split '"')[1]
        $Output += "`nApplication pool: $AppPool"
    }

    $Output
}

Testing jekyll

Trying to format code blocks!

this is formatted with md code block and powershell tag

Output with a class tag at the end
Page 3 of 3 Older →