PowerShell Function Output, Memory Management And ConvertTo-HTML

In this post I’m going to cover PowerShell function output, memory management and ConvertTo-HTML.

Function Output And Return Keyword
In PowerShell the Return keyword is just an exit point for your function, it doesn’t explicitly set the return value of your function. Everything written to the output stream will be returned by your function, calling return and optionally with a value to return will just add to the output of your function.

Sometimes you end up with extra output returned from you function which you may not want. To determine where the extra output came from, do not capture the result as a variable, rather let it go to the console so you can see at which point it is written. Pipe the offending cmdlets to Out-Null to suppress the output.

Memory Management
Since PowerShell is using .NET behind the scenes the same garbage collection rules apply. Memory management is not usually a problem if you execute the script and close PowerShell afterwards since the memory will be reclaimed. If you need to clean up memory during the execution of the script use Remove-Variable or Clear-Variable, depending on whether you want to use the variable again later in the script. This will make the memory from that variable available for garbage collection. The .NET CLR will do a garbage collection if there is memory pressure you don’t have to call Gc.Collect() explicitly unless your script can’t handle the delay when garbage collection happens. In which case you should call GC.collect() at a point in time where it is acceptable for your script but this usually applies to real time systems not the tasks you script with PowerShell.

ConvertTo-HTML And Strings
If you try to create an HTML table from the contents of a string array you will see something like this:

PowerShell Function Output, Memory Management And ConvertTo-HTML

This is because ConvertTo-HTML builds up the table using the properties of the input object, provided they can be displayed. A string object only has two properties Length and Chars[Int32] so it is adding the Length property to the table. To fix this you can make your own object and add a property to it that will be used in the table, in this case Text.

$strings = @('one', 'two', 'three')
 $newstrings = ($strings | ForEach-Object  { new-object -TypeName PSObject -Property @{"Text" = $_.ToString() } } )
 $html = $newstrings  |  ConvertTo-Html

PowerShell Function Output, Memory Management And ConvertTo-HTML

You can also instruct ConvertTo-HTML to include or exclude properties if you don’t want all the properties to show in the table.

Note: You can use Add-Member to add properties to PSObjects only so we couldn’t add a member to .NET string objects but if you for instance used Get-Content to read the strings from a file they will be PSObjects and you can add your own properties to the existing objects without creating new ones.

$PSObjectStrings | ForEach-Object { Add-Member -InputObject $_ -Type NoteProperty -Name "Text" -Value $_.ToString() }

Francois Delport

Creating Your Own PowerShell Host

In this post I’m going to have a quick look at creating your own PowerShell host. I’ve been looking into hosting PowerShell in a Windows service using C# for a new project, this post will serve as a quick intro. Most of the information in this post comes from this MSDN link but I’m hoping to create a one pager intro for busy people.

PowerShell Runspaces
When you execute PowerShell commands it is actually the runspace that is doing the execution work and handling the execution pipeline. The host is mostly responsible for handling input and output streams to interact with the runspace. Examples of hosts are PowerShell.exe, PowerShell ISE or a custom console, WPF, WinForms or Windows service application. The different hosts interact with the PowerShell runspace behind the scenes. When you create your own host you are creating a runspace instance and passing it commands to execute.

Creating A Runspace
Creating the default runspace is pretty easy, just call:

PowerShell ps = PowerShell.Create();

Note: You will need a reference to System.Management.Automation for the code to work.

You can do a lot more to customise the runspace instance. You can set initial variables, configure PowerShell options, import modules and set startup scripts. You can even constrain the commands that each runspace is allowed to execute. Some of the modifications can be applied to the default runspace while others require creating an empty initial state and runspace explicitly. Example here.

Running commands
Once you have a runspace you can give it commands to execute. You do this using the AddCommand and AddParameter methods of the PowerShell object.

powershell.AddCommand("sort-object") .AddParameter("descending").AddParameter("property", "handlecount");

You can add multiple commands to the pipeline before executing them, the results of the commands will be piped together. To start a new statement and a new pipeline call the AddStatement method. You can add script snippets or script files to the pipeline using the AddScript method. You can execute commands synchronously by using the Invoke method or asynchronously using the BeginInvoke method. Example here.

Handling Output
If you invoked the command synchronously you can get the output from the result of the Invoke method.

Collection<PSObject> results = powershell.Invoke();

If you called the command asynchronously you can also wait for it to complete execution or even better you can use events to handle the output as it occurs, example here.

Error Handling
Powershell has separate streams to handle different output, if you look at the PowerShell object’s Streams property you will see there are streams for Debug, Error, Information, Progress, Verbose and Warning. When non-terminating errors occur they will be written to the Error stream but the pipeline won’t raise exceptions. When terminating errors occur the pipeline will throw RuntimeExceptions and you can handle them in your code with a Try..Catch block.

Remote Connections
For commands that doesn’t have a ComputerName parameter to execute remotely you have to create a remote runspace, it is a bit like creating a remote session using Enter-PSSession. You create a WSManConnectionInfo object pointing to an endpoint on the remote machine and then you pass the WSManConnectionInfo object to the RunspaceFactory.CreateRunspace method. Example here.

Interacting With The Host
You can interact with the host from your Powershell scripts, although it is not really applicable to server automation it might be handy for user utilities. You can for instance manipulate variables and controls on the host from your PowerShell script. Example here.

Francois Delport

Error Handling In PowerShell

In this post I’m going to bring together a few odds and ends relating to error handling in PowerShell. I sometimes forget some of the features and it will be handy for future me and hopefully someone else to have it all in one place.

Terminating vs Non-Terminating Errors
In PowerShell errors can be terminating or non-terminating. Terminating errors will terminate the pipeline and your script will end if you didn’t catch the error in a try..catch block. Non-terminating errors will by default write an error message but continue with the next line of the script. You can control the behaviour of non-terminating errors using the $ErrorActionPreference variable at script level or -ErrorAction parameter at command level. The possible values are Stop, Inquire, Continue (*default), Suspend and SilentlyContinue. Stop will raise a terminating error from non-terminating errors, Inquire will prompt the user to continue, SilentlyContinue will not display the error but will continue executing. Under the covers non-terminating errors call the WriteError method while terminating errors call the ThrowTerminatingError method or raises an exception, there are some guidelines here to implement proper error reporting in your own scripts.

Try..Catch blocks
You can use try..catch blocks to catch terminating errors and handle them gracefully without your script terminating. By default non-terminating errors will not be caught by try..catch blocks. You have to use -ErrorAction or $ErrorActionPreference to change their error behaviour to Stop, which will give the same behaviour as terminating errors. Terminating errors caught by try..catch blocks will not display the usual red error message, which makes your script output look much better and you can control how you want to display the errors.

Storing And Retrieving Errors
PowerShell will keep errors that occurred in your script in the $Error variable, it appends errors to this array so you have to clear it yourself if need be by calling $Error.Clear(). You can for instance set the $ErrorActionPreference to SilentlyContinue to prevent error messages on screen and get the errors from the $Error array when you need them. You can also pass a variable to cmdlets that they can use to store errors that occurred during execution by specifying the
-ErrorVariable parameter and the name of your error variable. By default this variable will only show errors that occurred in the last command executed. It is cleared before every execution unless you specify that it should append errors to the variable by adding a plus sign to the variable name when you pass it to the command. Note: You pass in the variable name without the $ sign:

Get-ChildItem -path 'C:\DoesNotExist' -ErrorVariable +MyErrorVar

Viewing Error Details
By default you will not see much detail when you view an error object but you can pipe it to a list and pass the -force parameter to see all the properties.

$Error[0] | fl -Force

Or in this case I used the Show-Object command from the PowerShell CookBook module. There is quite a lot going on here, the objects in the array are ErrorRecords, they have an exception property that is the underlying .NET exception and it can contain an inner exception which is also an .NET exception.

Error Handling In PowerShell

These exceptions can be nested quite deep and can be a valuable source of information when tracking down errors.

Francois Delport