Blog Archives

PowerShell: Simple way to add dynamic parameters to advanced function


I’ve been working on a module for Nimble Storage and I’ve been creating dynamic parameters for some of the functions and I thought, why not just create a helper function for this? So I did, here it is with a simple example. The code is actually not too bad once you look through it, mostly just populating some objects and dumping them in the DynamicParam block.

Thanks Bart for the initial help! :)

Two important notes about this:
1) You have to supply Begin/Process/End (dont need all, but need at least 1)
2) It does not populate the variable as you’d expect it to, you have to pull it from $PSBoundParameters

Function New-DynamicParam {
param(
[string]
$Name,
[string[]]
$Options,
[switch]
$mandatory,
[string]
$SetName="__AllParameterSets",
[int]
$Position,
[switch]
$ValueFromPipelineByPropertyName,
[string]
$HelpMessage

)
    #param attributes   
    $ParamAttr = New-Object System.Management.Automation.ParameterAttribute
    $ParamAttr.ParameterSetName = $SetName
    if($mandatory){ $ParamAttr.Mandatory = $True }
    if($Position -ne $null){$ParamAttr.Position=$Position}
    if($ValueFromPipelineByPropertyName){$ParamAttr.ValueFromPipelineByPropertyName = $True}
    if($HelpMessage){$ParamAttr.HelpMessage = $HelpMessage}

    ##param validation set
    $ParamOptions = New-Object System.Management.Automation.ValidateSetAttribute -ArgumentList $options

    $AttributeCollection = New-Object 'Collections.ObjectModel.Collection[System.Attribute]' 
    $AttributeCollection.Add($ParamAttr)
    $AttributeCollection.Add($ParamOptions)

    $Parameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter `
    -ArgumentList @($Name, [string], $AttributeCollection)
            
    $Dictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    $Dictionary.Add($Name, $Parameter)
    $Dictionary
}

Here is an example of how you would use it.

function Show-Free
{
    [CmdletBinding()]
    Param()
    DynamicParam {
    New-DynamicParam -name Drive -options @(gwmi win32_volume |%{$_.driveletter}|sort) -Position 0 
    }
    begin{
    #have to manually populate
    $drive = $PSBoundParameters.drive
    }
    process{
    $vol = gwmi win32_volume -Filter "driveletter='$drive'"
    "{0:N2}% free on {1}" -f ($vol.Capacity / $vol.FreeSpace),$drive
    }
}

I’ve set this up to only work with strings, but if you look at the code there is actually nothing forcing you to use strings, just makes most sense for this.

Enjoy!

PowerShell: Working with Modules in ISE


Now that ISE isn’t crap with V3 I find that I dont really use any other editors. The one problem I have is when I work with modules, so I did a little screwing around and came up with this.


Function Import-CurrentFileAsModule

{

[cmdletbinding()]

param()

#get paths

$filePath = $psise.CurrentFile.FullPath

$folder = split-path $filePath

#save if not already saved

if($psise.CurrentFile.IsUntitled){Write-Error "Must save file first! Sorry didn't feel like implementing the dialog box!" -ErrorAction Stop}

if(-not $psise.CurrentFile.IsSaved){$psise.CurrentFile.Save()}

$global:WorkingModule = $null

#import the folder or the file if its standalone

try{
$psise.CurrentPowerShellTab.files | ?{-not $_.issaved} |? {$_.fullpath -like "$folder*"} | %{$_.save()}
$Global:WorkingModule = Import-Module $folder -Force -ErrorAction Stop -PassThru -Verbose:$false | select -ExpandProperty name
}

catch{$folderFailed = $true}

if($folderFailed)

{

try {Import-Module $filePath -Force -ErrorAction Stop -Verbose:$false}

catch{ write-error "Not a module file!" -ErrorAction Stop}

}

##post processing

if(Test-Path function:\PostModuleProcess)

{

Write-Verbose "Processing PostModuleProcess Function"

PostModuleProcess

}

else

{

Write-Verbose "--Create a PostModuleProcess function to excute code after import--"

}

Write-Verbose "Remove -verbose tag from last cmd in this file to stop verbose messaging"

}

 

Function Get-ModuleVariable{

param($Name)

if($global:workingmodule)

{

if($name)

{

&(gmo $global:workingmodule){Get-Variable -Name $args[0] -Scope script -ValueOnly} $name

}

else

{

&(gmo $global:workingmodule){Get-Variable -Scope script -ValueOnly}

}

}

}

Function Set-ModuleVariable{

[cmdletbinding()]

param(

[parameter(manditory=$true)]

$Name,

[parameter(manditory=$true)]

$Value)

if($global:workingmodule)

{

&(gmo $global:workingmodule){Set-Variable -Name $args[0] -Value $args[1] -Scope script} $Name $Value

}

}

$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('Reload Module',{Import-CurrentFileAsModule -verbose},"F6")

,{Import-CurrentFileAsModule -verbose},"F6")

Just dump this in your profile or in a file and load it in your profile and F6 is ready to be used!

This create a couple of functions to allow you to look in to and edit module (script:  scope) variables, Get/Set-ModuleVariable and it will run PostModuleProcess function (if you create it) so that if you want to run any code after it loads you can easily do that.

You’ll notice in that last line that the Import-CurrentFileAsModule has the verbose tag on it, once you’re comfortable using it you can remove that.

Let me know what you think!

P.s. Thanks for the tips Jaykul!

opps! completely forgot that I should save all files in the module!

Using PowerShell Schedule Job to Reboot a Computer


I have to reboot a server tonight and well, I don’t want to have to VPN in to do it tonight. I know there is a Restart-Computer cmdlet which is cool but i need to schedule it, so i decided to use the PS Scehduled Job and came up with this


register-ScheduledJob -Name systemReboot -ScriptBlock {

Restart-Computer -ComputerName $server -Force -wait

Send-MailMessage -From jrich523@domain.com -To jrich523@domain.com -Subject "Rebooted" -SmtpServer smtp.domain.com

} -Trigger (New-JobTrigger -At "11/28/2012 6:00pm" -Once) -ScheduledJobOption (New-ScheduledJobOption -RunElevated) -Credential (Get-Credential)

Sorry, it’s one long line, but copy and paste it and you’ll be happy you took the time to figure it out.

Two important notes here are that I used the New-ScheduledJobOption to force it to run elevated, which is more often than not, needed.

The other part, which sort of threw me for a loop was that you have to specify credentials even if they are the creds you are currently logged in as. The reason for this is that if you don’t you wont have the creds in the job pass through to the script.

 

 

 

Enjoy!

PowerShell: Catching terminating and non-terminating errors in PS Jobs (Job Pattern)


–update: the receive-job has had -ErrorAction Stop added to it. Thanks Witquicked

Recently someone asked how you’d catch non-terminating errors returned by a ps job and how to figure out what that error is. I thought this would be way easier than it was.

I also realized there wasnt much info out there on this so here is the code I came up with.

$jobs=@()
$jobs += Start-Job -ScriptBlock {write-error "--non term msg here"}
$jobs += Start-Job -ScriptBlock {throw "--term!"}
$jobs += start-job -ScriptBlock {"im successful"}
$jobs += start-job -ScriptBlock {while($true){}}
Wait-Job $jobs -Timeout 5

foreach($job in $jobs)
{
	write-host "--------"	
	write-host "Job ID: $($job.id)"	
	switch($job.state){	
		"Completed" {		
			write-host "job ran to completion"			
			if($job.ChildJobs[0].Error)			
			{			
				write-host "non terminating errors"				
				write-host $job.ChildJobs[0].Error			
			}		
		}	
		"Failed"	
		{		
			write-host "Job Terminated with error "			
			write-host "Error message is:"			
			try{Receive-Job $job -ErrorAction Stop }catch{$_.exception.message}		
		}	
		"Running"		
		{		
			write-host "Job is still running, Forcefully stopping it"			
			$job.StopJob()		
		}
	}
}

This is also a pattern for handling PS jobs in general.

You certainly dont want to loop, which I see people doing a lot. There is also a timeout param which I would highly suggest using.

PowerShell: Remove a Drive Mount/Letter (More win32)


I’ve got a server with about 300 volumes on it and I obviously can’t use drive letters so I’ve got a bunch of mount points which works great the problem is that after the first reboot the automount kicked in and gave some of those volumes drive letters. Now all of my drive letters are taken up!

I don’t really want to use the storage manager to remove the 20 or so volume labels so I started to dig in to a PowerShell method and come to find out PSCX has a method but that was the only way. I’m not a huge fan of modules (didn’t want to deploy that to my server) so I figured if they can do it so can I.

With a little digging I found the win32 api that allows us to remove mount points (a drive letter is still a mount point) and this was easier than I would have imagined.

If you’ve read my Getting Started with Win32 API then this will make perfect sense to you.


Add-Type @"
using System;
using System.Runtime.InteropServices;

public class MountPoint
{
[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool DeleteVolumeMountPoint(string mountPoint);
}
"@

[MountPoint]::DeleteVolumeMountPoint("G:\")

Once I did that for each drive I needed to remove I loaded up DiskPart and typed AutoMount Disable which prevents drives from automounting at boot.
That’s all there is to it!

Inspect Methods in PowerShell


*EDIT: Code updated (Thanks Rob)

So the more you use PowerShell the less you want to leave it. When you end up with an object that you have no clue how to use you’ll likely use Get-Member to inspect it. Once you see what members it has and find a method you’d like to use you need to figure out what the params are.

$str = “hello”

$str | get-member

We can see that string has a bunch of methods and perhaps we want to use the compare but don’t know what the different overloads are so we can take a peek at them

$str.compare

You’ll notice there is () on that method call, and that’s what triggers PowerShell to show the different ways to use that method.

One problem I have with this is that some of them are so long you can’t see what belongs to what so here is my first attempt at showing it cleanly.

[string]::Compare.OverloadDefinitions -replace ‘\(‘,”`n`t ” -replace ‘,|\)’,”`n`t”

Thanks Rob for the shorter code.

Whats next? A function to do this.

function Format-Definition

{

[CmdletBinding()]

Param

(

[Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)]

[System.Management.Automation.PSMethod] $method

)

$defs = $method.OverloadDefinitions

foreach($def in $defs)

{

if($def -like “*()”)

{Write-Host $def -f 6}

else

{

$splits = $def -split ‘\(|\)|,’

write-host $splits[0] -f 6

$splits[1..100] | %{$rtn = $_.trim() -split ” ”

Write-host “`t$($rtn[0]) ” -F 8 -NoNewline

write-host $rtn[1]

}}}}

$str.compare | format-definition

Much better! Well, except for the colors, blame Rob for that :)

I don’t use the default color scheme so I figured I’d let you change that yourselves.

PowerShell: Loading and Unloading Registry Hives


PowerShell will by default expose your HKLM and HKCU hives via drives which work because of the Registry PSProvider.


get-psdrive

get-psprovider

Since we see that it’s the provider that allows us to map these hives we can take it a step further and map a hive from a file (update user hives on a remote system). The problem with this is that the Registry PSProvider doesn’t extend to files. However this doesn’t stop us.


reg load 'HKLM\TempUser' $ntuserlocation

cd hklm:\TempUser

gci

New-PSDrive -Name HKMyUser -PSProvider Registry -Root HKLM\TempUser

cd HKMyUser:\

gci

cd c:

Remove-PSDrive HKMyUser

reg unload hklm\TempUser

This all works great until we attempt to unload that hive file or in some cases the unload works ok but we still have handles to the hive file (you can use sysinternals Handle.exe to see this)

Why is that if we removed the drive and asked Reg.exe to unload the hive? The problem is that the system has not released the memory which still has pointers in to that file, preventing us from unloading the hive or stopping us from doing other things.

So whats the trick you ask?

Ask the system to clean up those references that are no longer in use.


[gc]::collect()

This uses the static method Collect from the GC class in .NET which is used for forcing the garbage collector to run and removing those unused references.

PowerShell: Getting Started with Win32 API


Blog on Request!

Someone asked me to delve in to Win32 API use in PowerShell. Personally I don’t feel that I’m the best person to explain this, but I’ve done it a few times now so I think I have a decent handle on it.

Let me just start off by saying Win32 API should be your last choice (ok, second to last, I hate COM). You should do a very through search of the .NET framework before jumping in to the Win32 API pool.

Once you’ve exhausted all other options and have ended up in the Win32 world, what do you do? I’m not going to go over how to find the right WIN32 API for the task because if you are at this point, you’ve probably already found the API and just want to know how to use it.

First off load up the MSDN doc for it and the PInvoke.net doc. Usually googling the API will land you on those two pages pretty quickly.

Now for an example lets take a look at an API I’ve recently been playing with, GetSystemFileCache (pinvoke)

The Signature that MSDN gives us looks like this.

BOOL WINAPI GetSystemFileCacheSize(   __out  PSIZE_T lpMinimumFileCacheSize,   __out  PSIZE_T lpMaximumFileCacheSize,   __out  PDWORD lpFlags );

Which is the signature you’d use if you were coding in C++.  Since there is no direct way to use this in PowerShell we’re going to embed this in C# code and then consume that code in PowerShell. In many cases I’ll write the C# console app first and then apply that to my PowerShell script. This is what I did with my NetStat script, which was fairly complex. In this case we’re just calling a simple method to pull the current Cache limits.

Lets take a look at this Signature. The first thing shown is BOOL, which is the return type which the docs show is any nonzero number for success. Easy enough. bool is the same in C++ as it is in C#.

Next is WINAPI which is just a tag to show its a win32 api, this can just be removed.

After this is our method name, GetSystemFileCacheSize, easy enough.

Now this is where it gets a little tricky because we often times need to convert C++ types to something C# knows.

We see we have 3 arguments here, all 3 of which are prefixed with __out, which means they are output variables (If you don’t know how to program that well it’s basically passing a reference to a variable to return multiple things). So anytime you see __out, that means the variable needs to be passed in as a reference. We’ll get to that in a moment. Next is the type that the variable needs to be. Two of which are whats called PSIZE_T and one is a PDWORD. In both cases these are prefixed with a P, which indicates Pointer (pointer, reference, __out – all the same thing) so really the type is SIZE_T and DWORD.

How do we know what SIZE_T and DWORD are in C#? Google is really your best option here, or hopefully PInvoke.net has a signature you can use. In this case PInvoke does have it, but lets pretend it doesn’t. When we google ‘C# dword’ we get some good results that let us know this is an unsigned Int (UInt32). Another clue we have is that in the MSDN docs it tells us that it’s a flag and it can have a value of 0x1 or 0x4, both of which fit in to an int just fine. So, which do we use? It doesn’t matter, provided all possible return types can fit in.

Next is that Size_T, again googling ‘C# SIZE_T’ gives us some good info as does PInvoke, but lets just google SIZE_T.

Doing so provides a great data type reference.

We can see that PSIZE_T is in there but it just says it’s a pointer to SIZE_T which we already knew.

“The maximum number of bytes to which a pointer can point. Use for a count that must span the full range of a pointer.

This type is declared in BaseTsd.h as follows:

typedef ULONG_PTR SIZE_T;

So this says it’s a ULONG (unsigned long, a long in C# is Int64, which google will help with again) It also indicates that it’s a pointer. This is where PInoke really saves us. It lets us know about an IntPtr type that is a special pointer specifically for this.

So now our new C# signature is as follows

bool GetSystemFileCacheSize(ref IntPtr lpMinimumFileCacheSize, ref IntPtr lpMaximumFileCacheSize, ref int lpFlags);

We see that it specifies ref for all the __out variables (in this case all of them, but some times they are __in) and a data type of IntPtr and int.

We’re still in the C# side of the world. we’ll need to use the System.Runtime.InteropServices (otherwise known as pinvoke). If you don’t know C# this might be a bit tricky, but we’re going to write a small C# class to contain our method and create a wrapper function around it. Not always needed but I find it makes it easier to work with.


using System;

using System.Runtime.InteropServices;

public class test1

{

[DllImport("kernel32.dll")]

public static extern bool GetSystemFileCacheSize(ref IntPtr lpMinimumFileCacheSize, ref IntPtr lpMaximumFileCacheSize, ref int lpFlags);

public struct CacheSize

{

public Int64 Min;

public Int64 Max;

public int Flags;

}

public static CacheSize GetSize()

{

IntPtr lpMin = IntPtr.Zero;

IntPtr lpMax = IntPtr.Zero;

int lpFlags = 0;

bool b = GetSystemFileCacheSize(ref lpMin, ref lpMax, ref lpFlags);

CacheSize size = new CacheSize();

size.Min = lpMin.ToInt64();

size.Max = lpMax.ToInt64();

size.Flags = lpFlags;

return size;

}

The first part of this is just adding the pinvoke namespace and the basic system namespace which helps with the IntPtr. We then create a public class. I usually start with Test1 because once you add a type to PowerShell you can’t overwrite it.

Then you’ll see the keywords [DLLImport(“kernel32.dll”)] so depending on what DLL your function is in will determin what you put in there which is followed by the signature.

Next I created a struct to help with the output to PowerShell. You’ll see how this comes in to play as we start to work with the PowerShell side of things. For now just see that it’s a simple holder for our return data. Next is our function wrapper. I’ve called it GetSize which returns my struct. You’ll also notice that it’s prefiexed with public static for both, which is important for accessing it within PowerShell.

In my function I set my min and max pointer to zero (special pointer zero) and the flags to zero. I then call the win32 api passing it the variables I created as references (pointers) and capture the return in b which if I was trying to do something for production, I’d check the return value.

I then create an object of my type CacheSize and populate it with the return values and return that to the caller.

Now that the C# is done, how do we apply this in PowerShell?

If you read my blog you’ll notice I’ve used Add-Type a few times which is a great tool for adding in types or C# code as we are doing here so we’ll just wrap this in a here-string and pass it to Add-Type like so:


Add-Type

<span style="color: #8b0000; font-family: Lucida Console; font-size: xx-small;"><span style="color: #8b0000; font-family: Lucida Console; font-size: xx-small;"><span style="color: #8b0000; font-family: Lucida Console; font-size: xx-small;">@'</span></span></span>

using System;

using System.Runtime.InteropServices;

public class test1

{

[DllImport("kernel32.dll")]

public static extern bool GetSystemFileCacheSize(ref IntPtr lpMinimumFileCacheSize, ref IntPtr lpMaximumFileCacheSize, ref int lpFlags);

public struct CacheSize

{

public Int64 Min;

public Int64 Max;

public int Flags;

}

public static CacheSize GetSize()

{

IntPtr lpMin = IntPtr.Zero;

IntPtr lpMax = IntPtr.Zero;

int lpFlags = 0;

bool b = GetSystemFileCacheSize(ref lpMin, ref lpMax, ref lpFlags);

CacheSize size = new CacheSize();

size.Min = lpMin.ToInt64();

size.Max = lpMax.ToInt64();

size.Flags = lpFlags;

return size;

}

}

'@

NOTE: Be careful of new lines in this code, the code formatter has been doing weird things to me lately.

Once you run that its imported the class in to our running environment and we can access that method like so.


$rtn = [test1]::getsize()

$rtn

$rtn | select @{n='min';e={$_.min /1mb}}, @{n='Max';e={$_.max/1gb}},flags

The return should be the same on all systems (at least what I’ve seen from tests) as 1MB min and 1024GB max. If you have a file server that’s hosting TB’s of data, you might find this to be a problem when its eaten all of your memory in system cache!

There is a way to set it with a win32 API that im working on now. I’ll be sure to post that solution when I figure it out!

I hope this has helped you understand how to use Win32 APIs a little better!

PowerShell: Custom Objects with Format Files


I was recently working on something and realized that the default output (everything) was getting rather annoying. I then realized that the object I had wasnt going to cut it and I had to create my own. This led to two problems:

1. I needed to get formatting for my data and since I was going to post the script online I didn’t want to make it a multi file solution. I wanted it so the end-user could just copy and paste the entire thing in to one file and run it.

2. In knowing that I needed custom formating I realized the normal PSObject wasnt going to cut it.

Step 1 (Item 2) The Custom Object

This part isn’t really all that hard, but its something I thought I would show. If you know C# this will make perfect sense, if you don’t, it will likely still make perfect sense. In order to have a type name with our object we have to define it via a C# class (Other .NET languages work, but I know and like C#.) You can make this as simple or as complex as you like but for this example its going to be very simple. We’re going to make a Contact object.

add-type @'
namespace JRICH
{
    public class Contact
    {
public string FName;
public string LName;
public string DisplayName()
        {
return (this.FName + " " + this.LName);
        }
    }
}
'@

There are a couple of note worthy items here. First you’ll see I’ve provided a namespace for this class. This isn’t needed but I find it to be good practice to help prevent duplication of class names. Also you’ll want to be weary of case in the C# side of things because it is case-sensitive. Along those lines you’ll notice that each line ends in a ;. As I said if you know C# this is no big deal, but if you are a scripter/admin learning PowerShell these are important notes.

One other thing I’ll say here is that if you are not used to C# and you plan on doing this you might want to grab a copy of Visual C# Express which you can download for free. The debugging and Intellisense will really help with learning C#.

Another reason to use Visual C# is that when creating a class in PowerShell once you add the type there is no way to remove it or overwrite it so after you add the type and decide you want to change it, you’ll need to close PowerShell and open it again.

In this case I’ve created a class called Contact which has a first name, last name and a display name method to give a friendly view of this.

Now that we have this custom object lets use it.

$contact = new-object jrich.contact
$contact.fname = "Justin"
$contact.lname = "Rich"
$contact
#FName  LName
#———————–
#Justin Rich
$contact.displayname()
#Justin Rich

Pretty easy huh?

Now that we have an object to work with lets put it in a script. Lets make a function that returns a handful of users. I’m making this overly simple but I think you’ll see how to apply this in your own scripts.

function Get-MyUsers ([string[]] $User)
{
    Begin
    {
        $results = @()
    }
    Process
    {
foreach($usr in $user)
        {
$tmp = new-object jrich.contact
$tmp.lname, $tmp.fname = $usr.split(',')
$results += $tmp
        }
    }
    End
    {
        $results
    }
}
Get-MyUsers @("rich,justin","smith,john","doe,jane")

Shortcut: Ctrl+J will bring up the snippet menu (in ISE) which will allow you to generate a function body very quickly.

Step 2 (Item 1) The Format File

Our simple function is all well and good but lets say we are unhappy with the default output format and want to change it so the user doesn’t have to do it every time. In order to change the default display we need a Format File. The Help system has About_format.ps1xml and MSDN has pretty detailed documents on it, but lets just say its fairly complex. Thankfully James Brundage over at Start-Automating.com has created EZOUT, a very nice tool to help us create those format files.

Im not going to go in to details on how to create these files or use that tool since there is a very nice YouTube video for it.

Back to our function. Lets say we’d like to make the default view be the return of the Displayname method.

This is the XML that the EZOUT has spit out for us.

xml version="1.0" encoding="utf-16"?>
<Configuration><ViewDefinitions><View>
    <Name>jrich.contact</Name>
    <ViewSelectedBy>
        <TypeName>jrich.contact</TypeName>
    </ViewSelectedBy>
    <TableControl>
        <TableHeaders>
            <TableColumnHeader><Label>DisplayName</Label></TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
            <TableRowEntry>
                <TableColumnItems>
$_.displayname()
                </TableColumnItems>
            </TableRowEntry>
        </TableRowEntries>
    </TableControl>
</View></ViewDefinitions></Configuration>

Typically what you would do is save this file and use the Update-FormatData cmdlet to import this in to PowerShell’s current session environment.

In our case, because we don’t want to have more than one file we want to embed this in to our script. Since the only way to get this format data in to the environment is to use the Update-FormatData we’ll need to write this out to a file so we’ll inject this in to our Begin script block along with our class definition to make this a stand alone script.

function Get-MyUsers ([string[]] $User)
{
    Begin
    {
        try{
            add-type @'
namespace JRICH
{
    public class Contact
    {
public string FName;
public string LName;
public string DisplayName()
        {
            return (this.FName + " " + this.LName);
        }
    }
}
'@
            }
        catch{}

$formatfile = "$pshome\jrich.contact.format.ps1xml"
$typedata = @'
<?xml version="1.0" encoding="utf-16"?>
<Configuration><ViewDefinitions><View>
    <Name>jrich.contact</Name>
    <ViewSelectedBy>
        <TypeName>jrich.contact</TypeName>
    </ViewSelectedBy>
    <TableControl>
        <TableHeaders>
            <TableColumnHeader><Label>DisplayName</Label></TableColumnHeader>
        </TableHeaders>
        <TableRowEntries>
            <TableRowEntry>
                <TableColumnItems>
                    <TableColumnItem><ScriptBlock>$_.displayname()</ScriptBlock></TableColumnItem>
                </TableColumnItems>
            </TableRowEntry>
        </TableRowEntries>
    </TableControl>
</View></ViewDefinitions></Configuration
'@

if(!(Test-Path $formatfile))
        {
$typedata | out-file $formatfile
        }
        Update-FormatData -AppendPath $formatfile
        $results = @()
    }
    Process
    {
foreach($usr in $user)
        {
            $tmp = new-object jrich.contact
            $tmp.lname, $tmp.fname = $usr.split(',')
$results += $tmp
        }
    }
    End
    {
        $results
    }
}

There are a couple of things to note here. First is that we’ve enclosed our type definition in a Try/Catch statement. The reason for this is that once we load our type in to the environment we cant do it again as we saw with creating it and trying to update it.

The next thing is the actual process of adding in the formating data. You’ll notice I define a file based on the $PSHome variable (PowerShell install location) and tacked on a file name. To make sure it’s never overwritten I’ve named it based on the namespace and class. We then define the XML as a string and check to see if that file is there and if not write the file and update the format data.

One thing to note is that this method should only be used when you want to make a single script file to be distributed. If you are making a module the process of format files is a bit different. Want to know about that? Comment on this post and I’ll do a write-up on it.

Hope this helps.

Managing Remote (WSMAN) sessions with PowerShell


There was a post on the forums asking about how to remove a session if the max sessions (5) had been reached. You could use Get-Process to find and kill a WsmProvHost but killing things is never a good idea. There is a Get-WSManInstance but it’s not very friendly so I’ve wrapped that and Remove-WSManInstance in to two functions that are much more user-friendly. Just copy and past this in to a file and you can use Import-Modulein your profile to add these functions in to your environment.

<#
.Synopsis
Display WSMan Connection Info
.DESCRIPTION
This is a wrapper to Get-WSManInstance
.EXAMPLE
Get-RemotePSSession ServerABC
.EXAMPLE
$s = Get-RemotePSSession ServerABC
$s | Remove-RemotePSSession
#>
function Get-RemotePSSession
{
[CmdletBinding()]
    Param
    (
        # Computer to query.
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0)]
        [string]
$ComputerName,

# use SSL
        [switch]
$UseSSL
    )

    Begin
    {
        try{
            Add-Type  @"
namespace JRICH{
public class PSSessionInfo
            {
                public string Owner;
public string ClientIP;
public string SessionTime;
public string IdleTime;
public string ShellID;
public string ConnectionURI;
public bool UseSSL=false;
            }}
"@
        }
        catch{}
        $results = @()

    }
    Process
    {

$port = if($usessl){5986}else{5985}
$URI = "http://$($computername):$port/wsman"
$sessions = Get-WSManInstance -ConnectionURI $URI shell -Enumerate

foreach($session in $sessions)
        {
            $obj = New-Object jrich.pssessioninfo
            $obj.owner = $session.owner
            $obj.clientip = $session.clientIp
$obj.sessiontime = [System.Xml.XmlConvert]::ToTimeSpan($session.shellRunTime).tostring()
$obj.idletime = [System.Xml.XmlConvert]::ToTimeSpan($session.shellInactivity).tostring()
            $obj.shellid = $session.shellid
            $obj.connectionuri = $uri
            $obj.UseSSL = $usessl
            $results += $obj
        }
    }
    End
    {
        $results
    }
}

<#
.Synopsis
Logoff remote WSMAN session
.DESCRIPTION
   This function will take a JRICH.PSSessionInfo object and disconnect it
.EXAMPLE
$s = Get-RemotePSSession ServerABC
$s | Remove-RemotePSSession
#>
function Remove-RemotePSSession
{
[CmdletBinding()]
    Param
    (
        # Session to be removed.
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0)]
        [jrich.pssessioninfo[]]
        $Session

    )

    Process
    {
foreach($connection in $session)
        {
Remove-WSManInstance -ConnectionURI $connection.connectionuri shell @{ShellID=$connection.shellid}
        }
    }

}

Edit: Thanks Alex for catching the use of “CredSSP” vs “UseSSL” which is the common arg on PSSession cmdlets!