Using Enumerated types (Enums) in PowerShell

A question came up on the PowerShell newsgroup concerning how to use enums, particularly the shortcut form whereby posh will coerce strings. Roman Kuzmin of PowerShell/FarNet fame offered up a quick answer on how to provide multiple values to be "or'd" together for [flags] decorated enums:


To which the original poster (moff) replied:

Out of interest, can this syntax be used for xor'ing values together too? 
At the moment my statement looks like this: 

$pub_svr.ReplicationServer.Script(([Microsoft.SqlServer.Replication.scripto­­ptions]::Creation `
    -bor [Microsoft.SqlServer.Replication.scriptoptions]::IncludeAll ` 
    -bxor [Microsoft.SqlServer.Replication.scriptoptions]::IncludeReplicationJobs ))

To which I explained that sure, one way has you casting the operands (using system.attributetargets as the enum example):

$targets = ([attributetargets]"all" -bxor [attributetargets]"event,field")

And if the type name is long, another handy trick is to assign the enum type to a variable, e.g.

$enum = [Microsoft.SqlServer.Replication.ScriptO­ptions] 
$options = ($enum::creation -bor $enum::IncludeAll) -bxor $enum::includereplicationjobs

...and finally, if you want to cast multiple flags using a variable shortcut, use the -as operator:

$options = $enum::all -bxor ("includeall,includereplicationjobs" -as $enum)

because [$enum]"creation,includeall" (or other guessed-at variants) won't work.

Microsoft to release full source of the .NET BCL

Holy poop! This is huge!

I know Scott was leading the charge in a general source code cleanup which started some times ago, but it looks like they're nearly done! This is amazing - from within VS 2008 (or your favourite source code editor), you'll be able to seemlessly single-step debug from your own code in that of the .NET framework itself.

Bravo Microsoft! This is an incredibly symbolic step!

ETS CodeMethods in PowerShell

Marco Shaw recently asked on the powershell newsgroup:

Anyone have a working example of using "CodeMethod" to define methods
for a custom object?

And funnily enough, not even Microsoft themselves seem to have a decent working example. So, spelunking in the packaged Types.ps1xml that comes with a PowerShell install I found only three examples. Let's look at the ETS definition for one of them (XmlNode.ToString) in Types.ps1xml:


Using Reflector, I took a look at the managed code providing this service - btw, ETS CodeMethods are always provisioned by static methods on a managed type:

public static class ToStringCodeMethods
    public static string XmlNode(PSObject instance);

The C# 3.0 users among you will see a remarkable similarity to Extension Methods here, except here ETS CodeMethods can also replace a native method if you so desire. To define a new CodeMethod, you need to provide the full type name, the name of the static method and the ETS method name for the extension method on the target type. In this particular example, all instances of XmlNode exposed in PowerShell will now have their native ToString method replaced with this new one. When you invoke ToString on the XmlNode instance, the static method ToStringCodeMethods.XmlNode will be invoked and the instance parameter will be passed the PSObject wrapped XmlNode. Taking the only three examples in Types.ps1xml at face value, one might assume that CodeMethods can only be parameterless, but it appears not to be the case...

ETS Code Methods with additional parameters

A little experimentation verified that to me that we're not stuck with parameterless codemethods; any additional parameters you define on your static method will be bound to parameters provided in a script invocation of the ETS CodeMethod. The first argument is mandatory for any statics providing CodeMethod services: it will always be passed the ETS extended instance. Here are three examples of some multi-argument signatures and the backing managed code to provide ETS with the implementation:


And here is the corresponding backing managed code signatures:

public static class TestCodeMethods
    // Methods
    public static string TestNoArguments(PSObject instance) { ... }
    public static string TestOneArgument(PSObject instance, PSObject arg1) { ... }
    public static string TestVariableArguments(PSObject instance, params PSObject[] args) { ... }
Have fun!

Extending MoW's PowerTab - Inline Type Search

While I absolutely love MoW's PowerTab, there's been one little niggling thing that's been missing since day one; while you can navigate Types using the opening square bracket syntax, e.g. [<tab> , when using the new-object command this method is not totally syntactically compatible. (update: MoW explains in a comment below that there is a special trick for navigating without the square brackets, oops! me wrong!) You have to navigate to your type, then edit out the square brackets which is a little bit annoying. Also, when using this syntax you typically know exactly which type you need but can't remember the namespace. Or perhaps you know the type name, but don't want to have to type it all out (or navigate there).

So, I figured I'd crack open PowerTab and see how easy it was to implement. To cut a long story short, in about 30 minutes it was done. The thing that made it so easy was that MoW already has a DataSet in memory of all types and their namespaces, so the grunt work is done by a simple DataTable.Select call. I modified the function TabExpansion in TabExpansion.ps1 (version 0.96 b12), and put the following snippet just after the switch on $lastWord :

switch -regex ($lastWord) {

    # Handle inline type search, e.g. new-object .identityreference<tab> or .identityre<tab>
    '^\.(\w+)$' {
$typeName = $matches[1]
$types = $dsTabexpansion.tables["Types"]
$rowFilter = "name like '%.${typeName}%'"
       $$rowFilter) | % {$_["name"] } | Invoke-TabItemSelector $lastWord -Select $SelectionHandler

The type search is initiated by prefixing the full or partial type name with a period (.) and then hitting tab. There you have it, inline type search. Have fun!

Converting between SIDs and NT Accounts in PowerShell

Another answer I posted to the NG, and not all that hard once you know the right classes to use from the BCL. But if you didn't know where to look, I can imagine it being a royal pain in the ass.

--- begin ConvertTo-Sid.ps1 ---

param ($account = $(throw "need account in form domain\username or
[ntaccount] object"))

if ($account -is [security.principal.ntaccount]) { 
    $ntaccount = $account

} else {
$ntaccount = new-object security.principal.ntaccount $account

$ntaccount.translate( [security.principal.securityidentifier] )
-- end ConvertTo-Sid.ps1 ---

and the reverse:

--- begin ConvertTo-NTAccount.ps1 ---

param ($sid = $(throw "need sid string or [securityidentifier] object"))

if ($sid -is [security.principal.securityidentifier]) {
    $securityidentifier  = $sid

} else { 
    $securityidentifier  = new-object security.principal.securityidentifier $sid

$securityidentifier.translate( [security.principal.ntaccount] )

--- end ConvertTo-NTAccount.ps1 ---

You can pass strings as args, or their respective native objects. They both output objects. The output of one can be used as the input of the other.


Tired of the standard Windows Console with PowerShell?

Then get ready for PS+ !


A simple Snap-In picker script

I got tired of typing add-pssnapin blah, especially with Quest's AD snap-in's longer than usual name so I came up with a little script that enumerates registered snap-ins, and lists a simple numbered menu showing those that are not already loaded into memory. You just type the number and hit enter to load it. Enter on an empty line exits the script. Yeah, it's brain-dead, but hey, I think I was brain-dead to continually type long commands in every single session to load stuff ;-) 

UPDATE 2007-08-16: I was even more braindead than I realised - I copied up a broken version without the $notloaded array. Ooops. Fixed.

  1. write-progress "Enumerating Snapins" "Registered..."
  2. $snaps = get-pssnapin -r
  4. write-progress "Enumerating Snapins" "Loaded..."
  5. $loaded = get-pssnapin | % {$}
  6. $notloaded  = @()
  8. write-progress "Enumerating Snapins" "Complete." -Completed # glitchy
  10. $i = 0; "";
  12. foreach ($snap in $snaps) {
  13.         if (-not($loaded -contains $ {
  14.                 Write-Host -fore cyan -nonewline "${i}) "
  15.                 write-host -fore green $
  16.                     $notloaded  += $snap
  17.                 $i++
  18.         }
  19. }
  21. if ($i -eq 0) {
  22.         Write-Warning "Any eligible snapins are already loaded."
  23.         exit;
  24. }
  26. $i--;
  27. Write-Host -fore yellow "<enter> to quit"
  29. do {
  30.         write-host -nonewline "Load 0 - ${i}: ";
  31.         $choice = [console]::readline()
  32.         if ($choice -and ($choice -lt 0) -or ($choice -gt $i)) {
  33.                 Write-Warning "'${choice}' is out of range!"
  34.                 continue;
  35.         }
  36.         if ($choice) {
  37.                 "loading $($notloaded[$choice].name) ..."
  38.                 add-pssnapin $notloaded[$choice]
  39.         }
  40. } while ($choice)

Just pop it into a function or a ps1 script.

You'll notice how when this is run from a clear screen, the progress bar disappears, then suddenly reappears and the first few menu items are hidden. This is a bug in the powershell.exe host. Vote on it here:


Creating Functions from a .NET class's Static Methods

Someone on the PowerShell NG asked recently if there was some kind of equivalent to c#'s "using namespace;" or VB.NET's "Imports namespace" statements. Well, the short answer to that is no, not really. However, you can simulate it by creating functions for each static method on the class:

  1. param([type]$type = $(throw "need a type!"))
  3. $type | gm -static | ? {$_.membertype -eq "method" } | % {
  4.         $func = "function:$($"
  5.         if (test-path $func) { remove-item $func }
  6.         $flags = 'Public,Static,InvokeMethod,DeclaredOnly'
  7.         new-item $func -value "[$($type.fullname)].InvokeMember('$($', ${flags}, `$null, `$null, `$args[0])"
  8. }

Save this as "import.ps1" for example and use like so:

PS > . .\import.ps1 ([Math])

PS > Sin 1

Methods that take multiple args must have them passed as a single array:

PS > Max @(1,2)

Have fun!

Update: there are some interesting extensions appearing based around my initial post on


New Improved Get-WebService Proxy Generator for PowerShell

I noticed that my last script could not handle more complex webservices like the MSDN ContentService (unlike WSDL.EXE) for example, so I rewrote it from the ground up. This time, it supports Basic Profile 1.1 and can generate proxy instances for simple ASMX services and more complicated multi-schema services like the MSDN/TechNet Publishing System (MTPS) Content Service.

What's New:

  • No need for the .NET 2.0 SDK, just PowerShell
  • Auto discovery of all referenced schema
  • No need to point it at page.asmx?wsdl any more, just the asmx
  • Validation against WS-I Basic Profile 1.1

get-webservice2.ps1.txt (4.28 KB)  (updated 2007/12/19: errant comma in write-progress fixed)

  1. #
  2. # Get-WebService.ps1 (v2.0 Aug 6, 2007)
  3. #
  4. # Oisin Grehan <> (x0n)
  5. #
  6. # Usage:
  7. #   $proxy = .\get-webservice2.ps1 [-Url] http://site/service.asmx [-Anonymous] [[-SoapProtocol] <Soap | Soap12>]
  8. #
  9. # to see available webmethods:
  10. # $proxy | gm
  11. #
  13. # $url = ""
  15. param($url = $(throw "need `$url"), [switch]$Anonymous, [string]$protocol = "Soap")
  17. [void][system.Reflection.Assembly]::LoadWithPartialName("")
  19. trap {
  20.         "Error:`n`n $error";
  21.         break;
  22. }
  24. #$request = [System.Net.WebRequest]::Create($url);
  25. $dcp = new-object
  27. if (! $Anonymous) {
  28.     Write-Progress "Network Credentials" "Awaiting input..."
  29.     $dcp.Credentials = (Get-Credential).GetNetworkCredential()
  30. }
  32. Write-Progress "Discovery" "Searching..."
  33. $dcp.AllowAutoRedirect = $true
  34. [void]$dcp.DiscoverAny($url)
  35. $dcp.ResolveAll()
  37. # get service name
  38. foreach ($entry in $dcp.Documents.GetEnumerator()) { # needed for Dictionary
  39.     if ($entry.Value -is [System.Web.Services.Description.ServiceDescription]) {
  40.         $script:serviceName = $entry.Value.Services[0].Name
  41.         Write-Verbose "Service: $serviceName"
  42.     }
  43. }
  45. Write-Progress "WS-I Basic Profile 1.1" "Validating..."
  46. $ns = new-Object System.CodeDom.CodeNamespace # "WebServices"
  48. $wref = new-object System.Web.Services.Description.WebReference $dcp.Documents, $ns
  49. $wrefs = new-object
  50. [void]$wrefs.Add($wref)
  52. $ccUnit = new-object System.CodeDom.CodeCompileUnit
  53. [void]$ccUnit.Namespaces.Add($ns)
  55. $violations = new-object system.web.Services.Description.BasicProfileViolationCollection
  56. $wsi11 = []::BasicProfile1_1
  58. if ([system.web.Services.Description.WebServicesInteroperability]::CheckConformance($wsi11, $wref, $violations)) {
  59.     Write-Progress "Proxy Generation" "Compiling..."
  61.     $webRefOpts = new-object System.Web.Services.Description.WebReferenceOptions
  62.         $webRefOpts.CodeGenerationOptions = "GenerateNewAsync","GenerateProperties" #,"GenerateOldAsync"
  64.         #StringCollection strings = ServiceDescriptionImporter.GenerateWebReferences(
  65.         #       webReferences, codeProvider, codeCompileUnit, parameters.GetWebReferenceOptions());
  67.     $csprovider = new-object Microsoft.CSharp.CSharpCodeProvider
  68.         $warnings = [System.Web.Services.Description.ServiceDescriptionImporter]::GenerateWebReferences(
  69.                 $wrefs, $csprovider, $ccunit, $webRefOpts)
  71.     if ($warnings.Count -eq 0) {
  72.         $param = new-object system.CodeDom.Compiler.CompilerParameters
  73.         [void]$param.ReferencedAssemblies.Add("System.Xml.dll")
  74.         [void]$param.ReferencedAssemblies.Add("System.Web.Services.dll")       
  75.         $param.GenerateInMemory = $true;
  76.         #$param.TempFiles = (new-object System.CodeDom.Compiler.TempFileCollection "c:\temp", $true)
  77.         $param.GenerateExecutable = $false;
  78.         #$param.OutputAssembly = "$($ns.Name)_$($sdname).dll"
  79.         $param.TreatWarningsAsErrors = $false;
  80.         $param.WarningLevel = 4;
  82.         # do it
  83.         $compileResults = $csprovider.CompileAssemblyFromDom($param, $ccUnit);
  85.         if ($compileResults.Errors.Count -gt 0) {
  86.             Write-Progress "Proxy Generation" "Failed."
  87.             foreach ($output in $compileResults.Output) { write-host $output }
  88.             foreach ($err in $compileResults.Errors) { write-warning $err }           
  89.         } else {           
  90.             $assembly = $compileResults.CompiledAssembly
  92.             if ($assembly) {
  93.                 $serviceType = $assembly.GetType($serviceName)               
  94.                 $assembly.GetTypes() | % { Write-Verbose $_.FullName }
  95.             } else {
  96.                 Write-Warning "Failed: `$assembly is null"
  97.                                 return
  98.             }
  100.             # return proxy instance
  101.             $proxy = new-object $serviceType.FullName
  102.             if (! $Anonymous) {
  103.                 $proxy.Credentials = $dcp.Credentials
  104.             }
  105.             $proxy # dump instance to pipeline
  106.         }
  107.     } else {
  108.         Write-Progress "Proxy Generation" "Failed."       
  109.         Write-Warning $warnings
  110.     }
  111.     #Write-Progress -Completed
  112. }

Strongly typed webservice client proxies in PowerShell

For me, this opens up a whole new world of PowerShell interaction with remote servers. No more constructing soap packets - just point it at a WSDL url, and you get back a first-class object with methods that you can call corresponding to remote webmethods. PowerShell's [xml] accelerator automatically wraps the responses so you can easily manipulate the results.

Have fun!

UPDATE: newer more robust version 2.0 available!

About the author

Irish, PowerShell MVP, .NET/ASP.NET/SharePoint Developer, Budding Architect. Developer. Montrealer. Opinionated. Montreal, Quebec.

Month List

Page List