PowerShell 2.0: Quick Tip – Capture Verbose, Error & Warning Streams

update: missing backtick ` to escape the $verbosepreference variable

A question came up on stack overflow (you don’t know what that is? shame on you!) today from someone asking how they could capture the Verbose stream from a pipeline they ran in a C# program. As it turns out, the same technique is used in script, so I’ll give that example instead since I’m sure the C# guys and gals will have no problem converting the script.

The key is using the new (to v2.0) System.Management.Automation.PowerShell Type, which has a built-in Type Accelerator of [powershell]. It has a static method, Create, which is used to create an instance. This instance is pretty much ready to roll. It has a Streams property, which is of Type PSDataStreams. This Type has properties representing each collection of Error, Progress, Verbose, Debug and Warning.

$ps = [powershell]::create()
$ps.Commands.AddScript("`$verbosepreference='continue'; write-verbose 42")
$ps.invoke()
$ps.streams.verbose
Which yields the VerboseRecord that was written out:
Message InvocationInfo                              PipelineIterationInfo
------- --------------                              ---------------------
42      System.Management.Automation.InvocationInfo {0, 0}
What's important to note about the above example is that I had to set the $verbosepreference to at least "continue" (the default is silentlycontinue) in order for the verbose record to be written. Have fun!

PowerShell v2.0 – Differences Between CTP3/Win7Beta and Win7RC

update #1 2009/5/23: noted that local jobs (start-job) no longer require an elevated shell.

update #2 2009/5/28: ISE object model and shortcut changes; update for set-psessionconfiguration cmdlet (new screenshot); module changes (highlighted new manifest member names, binary module can now be root module);

I’ve been meaning to write this for a while, but it’s been a busy time. This is a comparison of the significant differences between the standalone CTP3 or Windows 7 Beta version and the version that comes with Win7 RC (6.1.7100.0). For all intents and purposes, the CTP3 version (6.1.6469.0) is exactly the same as the Windows 7 Beta (6.1.7000.0) version.

This is not going to be an exhaustive list of differences, but I will continue to update this post as I find more things worth documenting. It should be safe to bookmark the permalink. One of the nicest things about this release is that it appears that, without exception, all built-in Cmdlets have help. A lot of things have been cleaned up and fixed in this build, from formatting of text to typos and minor bugs/glitches.

Cmdlet Differences

This is a table listing Cmdlets that have either changed (navy), been added (green, underlined) or removed (red, strike-through.) Changed Cmdlets have their parameters listed in the second column. A changed parameter means that its Type has been changed; e.g. it accepts a different .NET object than before. This is generally nothing to worry about since the corresponding source of such objects is usually changed to match – typically another Cmdlet. Parameters  and Cmdlets that have not changed, are not listed.

Cmdlet Parameters
Invoke-Command
Remove-Computer
Add-Computer
Rename-Computer
Test-Connection
Export-FormatData
Receive-Job
Start-Job
Get-Module
New-ModuleManifest
Set-PSBreakpoint
Enable-PSRemoting
Remove-PSSession
Enter-PSSession
New-PSSession
Export-PSSession
Import-PSSession
New-PSSessionOption
Get-WSManInstance
Set-PSSessionConfiguration ShowSecurityDescriptorUI

Alias Changes

Some tweaking of aliases here. Personally I find alias changes in general to get under my skin. Aliases are the first thing I learn and the first thing to trip me up when things change. Regardless, the changes appear to make sense and are perhaps a bit more mnemonic than before.

Removed

emm (Export-ModuleMember), which (Get-Command) and grid (Out-Gridview)

Added

ise (powershell_ise.exe), rmo (Remove-Module) and saps (Start-Process)

Changed

imo –> ipmo (Import-Module)

Language Enhancements

The major change that has come to light so far is that statements are now allowed on the right hand of an expression without having to use subexpressions. This is a great fix, and one that will reduce the margin for error (and confusion) by a large amount. Previously in CTP3, in order to use a statement like “if”, you had to use the following syntax:

$result = $( if ($true) { 42 } )

Now, you can drop the $( and ):

$result = if ($true) { 42 }

or

$sequence = foreach ($i in 0..15) { [math]::pow(2, $i) }

This is truly great stuff.

Jobs/Remoting

The best news here is the abundant help now available at your fingertips. Lots of examples and meaty information concerning PSSessions and PSSessionConfigurations.

Local Jobs

Local jobs created now with Start-Job { ... } use an IPC channel to talk to the local WinRM service to create jobs instead of using the more heavyweight HTTP channel. Yes, you can infer from this that local jobs are still out of process; they run in their own isolated runspace and have no access to the interactive session. What this means in simpler terms is that local jobs are a lot faster now to get started. The biggest win for local jobs is that they no longer require an elevated shell! you can submit local jobs as a regular user now – just not remote ones (unless the applicable remote PSSessionConfiguration is set to allow this - by default the ACL is admins only).

Remoting

A welcome addition to this build is a new, dedicated Enable-PSRemoting Cmdlet:

The Enable-PSRemoting cmdlet configures the computer to receive Windows PowerShell remote commands that are sent by using the WS-Management technology.

You need to run this command only once on each computer that will receive commands. You do not need to run it on computers that only send commands. Because the configuration activates listeners, it is prudent to run it only where it is needed.

The Enable-PSRemoting cmdlet performs the following operations:

  • Runs the Set-WSManQuickConfig cmdlet, which performs the following tasks:
    • Starts the WinRM service.
    • Sets the startup type on the WinRM service to Automatic.
    • Creates a listener to accept requests on any IP address.
    • Enables a firewall exception for WS-Management communications.
  • Enables all registered Windows PowerShell session configurations to receive instructions from a remote computer.
    • Registers the "Microsoft.PowerShell" session configuration, if it is not already registered.
    • Registers the "Microsoft.PowerShell32" session configuration on 64-bit computers, if it is not already registered.
    • Removes the "Deny Everyone" setting from the security descriptor for all the registered session configurations.
    • Restarts the WinRM service to make the preceding changes effective.

To run this cmdlet on Windows Vista, Windows Server 2008, and later versions of Windows, you must start Windows PowerShell with the "Run as administrator" option.

Session Configurations

There are a raft of Cmdlets dedicated to managing session configurations. So what is a session configuration? To qoute the ever-present help system:

A session configuration is a group of settings on the local computer that define the environment for the Windows PowerShell sessions that are created when remote users connect to the local computer.

Administrators of the computer can use session configurations to protect the computer and to define custom environments for users who connect to the computer.

Administrators can also use session configurations to determine the permissions that are required to connect to the computer remotely. By default, only members of the Administrators group have permission to use the session configuration to connect remotely, but you can change the default settings to allow all users, or selected users, to connect remotely to your computer.

Session configurations are a feature of Web Services for Management (WS-Management) based Windows PowerShell remoting. They are used only when you use the New-PSSession, Invoke-Command, or Enter-PSSession cmdlets to connect to a remote computer.

Note: To manage the session configurations on a computer that is running Windows Vista, Windows Server 2008, or a later version of Windows, start Windows PowerShell with the "Run as administrator" option.

image

That SecurityDescriptorSddl property looks like a lot of fun to modify, right? Don’t worry, the ShowSecurityDescriptorUI comes to the rescue:

image

Modules

Manifest Members

Modules have received some nice incremental improvements as well as some syntactic changes. You should have noticed above that the manifest fields (exposed as parameters on New-ModuleManifest)  have changed to reflect the two-stage process of how a module’s members get exposed to the importer’s scope. A module passively exports members with Export-ModuleMember, and the caller actively imports them with Import-Module; hence “FunctionsToExport,” which subtly says that this can be imported.

Binary Modules

A binary module is now allowed to be the root module in a manifest by pointing the ModuleToProcess key at the assembly, e.g. ModuleToProcess = “Pscx.dll.” This seems more intuitive than before with the [seemingly] arbitrary restriction that they must be secondary to a text based psm1.

Built-In Modules

Shipping with Windows 7 for PowerShell comes two new Modules along with the others you should have noticed that arrived with CTP3 (BitsTransfer/FileTransfer and PSDiagnostics.)

AppLocker

AppLocker provides simple, powerful, rule-based structures for specifying which applications can run that are centrally managed using Group Policy. It introduces "publisher rules" that are based on an application's digital signature, making it possible to build strong rules that account for application updates. For example, an organization can create a rule to "allow all versions greater than 1.0 of Microsoft Dynamics CRM to run if signed by Microsoft." With correctly structured rules, IT professionals can safely deploy updates to allowed applications without having to build a new rule for each version update.

About AppLocker on TechNet

TroubleshootingPack

Windows Troubleshooting Platform (WTP) provides ISVs, OEMs, and administrators the ability to write troubleshooting packs that discover and resolve software and hardware issues, such as configuration issues, failed hardware, network issues, and application compatibility issues. In WTP, an issue is referred to as a root cause. Previously, troubleshooting software and hardware issues was a manual process; however, using WTP you can automate the process of fixing the most common issues that the user might encounter.

About WTP on MSDN

PowerShell Integrated Scripting Environment

The ISE has been vastly improved in terms of usability and sharpness in this build. Difficult to isolate any one part of it that is much better; the whole experience is just way smoother and intuitive.

image

ISE Object Model

The object model naming has changed quite a bit to be more intuitive and friendly to beginners. Who needs to know about Runspaces? It’s a Tab, silly!

CTP3 RC
$psise.CurrentOpenedRunspace $psise.CurrentPowerShellTab
$psise.CurrentOpenedRunspace.ToolsMenu $psise.CurrentPowerShellTab.AddOnsMenu
$psise.CurrentOpenedRunspace.OpenedFiles $psise.CurrentPowerShellTab.Files
$psise.CurrentOpenedRunspace.OpenedFiles.RemoveUnsaved($file) $psise.CurrentPowerShellTab.Files.Remove($file,$true)
$psise.OpenedRunspaces $psise.PowerShellTabs
$psise.OpenedRunspaces[1].Execute("string") $psise.PowerShellTabs[1].Invoke({scriptBlock})*
$psise.CurrentOpenedFile $psise.CurrentFile
$psise.Options.LocalHelp $psise.Options.UseLocalHelp

* Note: You may only invoke script on a tab other than the tab you’re using to execute this command (because you can’t run two commands at the same time on the same tab!)

The PowerShell team posted a way to make the RC build fairly compatible with CTP3 by adding new members to the $PSISE object which will proxy attempts to use the old API to the new API. The post is called “Update-TypeData, ISE CTP3 vs ISE RC, and Teched2009 Demos.”

Shortcut Changes

The shortcut to jump to the Script Pane is now Ctrl+I. (use Ctrl+D to jump to the Command pane).

Summary

The RC build is all about bugfixes and incremental improvements. I’m sure there’ll be more fixes and additions as we near RTW/RTM and I’ll be here to post as much info about them as possible. This is still just a drop in the ocean of what PowerShell 2.0 can do, and I’ll post more technical demos of features over the next little while.

Tips: run “Get-Help about_*” to get a list of all the overview topics for the various features in PowerShell v2.

Have fun!

Pscx 1.2 Beta Released

Hey, so we did the unthinkable. We released another version of the PowerShell Community Extensions. We’re calling it a beta, because we’ve be so swamped with Real Life stuff that we’re not 100% confident that it is defect-free. It’s not going to murder your servers or anything, but there might be some documentation missing and other minor stuff. We’d really appreciate it if you can give it a test-drive. If you’re running PowerShell v1.0 or v2.0 CTP3, please use the MSI installer. It will upgrade your Pscx 1.1.1 install if you have one. If, on the other hand, you are running Windows 7 RC and/or have a later version of PowerShell than v2 CTP3, you can download the zipped module and unzip to your user profile module directory at ~\documents\windowspowershell\modules\ and load it using “import-module pscx.”

Thanks for your infinite patience (yes, it’s been a while and we’re sorry) and please leave comments and issues on the tracker at http://pscx.codeplex.com/

View the 1.2 Beta release page.

PowerShell 2.0 CTP3: Modules, in Practice - Closures

After some hints from Ibrahim on the Microsoft PowerShell team, I realised it was possible to rewrite the dynamic module body generation from the Get-Interface function in my last post without using string literals and nested here-string tricks. I was able to increase the brevity by using a new [to PowerShell] v2 feature called “Closures.” Ibrahim talks a bit about the technique over on the official PowerShell Blog. It’s a common feature in so-called Functional languages and there are plenty of other tidbits to read about it, both academic and practical if you search online.

A closure in PowerShell, in short, lets you take a snapshot of a ScriptBlock by calling its GetNewClosure() method. The snapshot is taken of its variables and will let you pass it around so it is no longer directly tied to the particular scope chain it was declared in. As some are fond of saying though, Thar Be Dragons. Although closing around a ScriptBlock will make copies of the PSVariables that are within, it is still subject to the whims of .NET – particularly ByRef and ByValue semantics. If a ScriptBlock contains a PSVariable pointing to an integer, it’s safe to say that integer will be copied and is frozen as that value as long as you remain within the domain of the current SessionState context (more about this later).

.NET intervenes: ByRef and ByVal

Closing around a ScriptBlock that contains PSVariables that point to reference types, however - like Arrays for example – will only duplicate the reference, not the instance itself. If elements in the array are changed via the PSVariable from the originating scope, the corresponding array element in the closure will also change. After all – you got a copy of the pointer, not the value itself. This is the very essence of ByRef and ByVal.

Let’s take a look at the revised Get-Interface script:

function Get-Interface {

#.Synopsis
#   Allows PowerShell to call specific interface implementations on any .NET object. 
#.Description
#   Allows PowerShell to call specific interface implementations on any .NET object. 
#
#   As of v2.0, PowerShell cannot cast .NET instances to a particular interface. This makes it
#   impossible (shy of reflection) to call methods on explicitly implemented interfaces.   
#.Parameter Object
#   An instance of a .NET class from which you want to get a reference to a particular interface it defines.
#.Parameter InterfaceType
#   An interface type, e.g. [idisposable], implemented by the target object.
#.Example
#   // a class with explicitly implemented interface   
#   public class MyObj : IDisposable {
#      void IDisposable.Dispose()
#   }
#   
#   ps> $o = new-object MyObj
#   ps> $i = get-interface $o ([idisposable])
#   ps> $i.Dispose()      
#.ReturnValue
#   A PSCustomObject with ScriptMethods and ScriptProperties representing methods and properties on the target interface.
#.Notes
# AUTHOR:    Oisin Grehan http://www.nivot.org/
# LASTEDIT:  2009-03-28 18:37:23
# REVISION:  0.2

    [CmdletBinding()]
    param(
        [ValidateNotNull()]
        $Object,
        
        [ValidateScript( { $_.IsInterface } )]
        [type]$InterfaceType
    )

    $script:t  = $Object.GetType()
    
    try {
        
        $script:m  = $t.GetInterfaceMap($InterfaceType)

    } catch [argumentexception] {
        
        throw "Interface $($InterfaceType.Name) not found on ${t}!"
    }

    $script:im = $m.InterfaceMethods
    $script:tm = $m.TargetMethods
    
    # TODO: use param blocks in functions instead of $args
    #       so method signatures are visible via get-member
    
    $body = {
         param($o, $i) 
         
         $script:t  = $o.GetType()
         $script:m  = $t.GetInterfaceMap($i)
         $script:im = $m.InterfaceMethods
         $script:tm = $m.TargetMethods
                  
         for ($ix = 0; $ix -lt $im.Count; $ix++) {
            
            $mb = $im[$ix]

            # for the function body, we close over $ix to capture the index
            # so even on the next iteration of this loop, the $ix value will
            # be frozen within the function's scriptblock body
            set-item -path function:script:$($mb.Name) -value {

                # call corresponding target method
                $tm[$ix].Invoke($o, $args)

            }.GetNewClosure() -verbose -force

            if (!$mb.IsSpecialName) {
                # only export the function if it is not a getter or setter.
                Export-ModuleMember $mb.Name -verbose
            }
         }
    }

    write-verbose $body.tostring()    
    
    # create dynamic module
    $module = new-module -ScriptBlock $body -Args $Object, $InterfaceType -Verbose
    
    # generate method proxies - all exported members become scriptmethods
    # however, we are careful not to export getters and setters.
    $custom = $module.AsCustomObject()
    
    # add property proxies - need to use scriptproperties here.
    # modules cannot expose true properties, only variables and 
    # we cannot intercept variables get/set.
    
    $InterfaceType.GetProperties() | % {

        $propName = $_.Name
        $getter   = $null
        $setter   = $null
       
        if ($_.CanRead) {
            
            # where is the getter methodinfo on the interface map?
            $ix = [array]::indexof($im, $_.GetGetMethod())
            
            # bind the getter scriptblock to our module's scope
            # and generate script to call target method
            #
            # NOTE: we cannot use a closure here because sessionstate
            #       is rebound to the module's, and $ix would be lost
            $getter = $module.NewBoundScriptBlock(
                [scriptblock]::create("`$tm[{0}].Invoke(`$o, @())" -f $ix))
        }
        
        if ($_.CanWrite) {
            
            # where is the setter methodinfo on the interface map?
            $ix = [array]::indexof($im, $_.GetSetMethod())
            
            # bind the setter scriptblock to our module's scope
            # and generate script to call target method
            #
            # NOTE: we cannot use a closure here because sessionstate
            #       is rebound to the module's, and $ix would be lost
            $setter = $module.NewBoundScriptBlock(
                [scriptblock]::create(
                    "param(`$value); `$tm[{0}].Invoke(`$o, `$value)" -f $ix))
        }
        
        # add our property to the pscustomobject
        $prop = new-object management.automation.psscriptproperty $propName, $getter, $setter
        $custom.psobject.properties.add($prop)
    }
    
    # insert the interface name at the head of the typename chain (for get-member info)
    $custom.psobject.TypeNames.Insert(0, $InterfaceType.FullName)
    
    # dump our pscustomobject to pipeline
    $custom
}

The changes are around line 56, where I now declare $body with a script syntax, no longer using [scriptblock]::create. Where previously I used a subexpression inside a here-string to general literal function declarations, I now use a “for” loop and set-item to create the functions (thanks Ibrahim!). You can see that I am calling GetNewClosure() on the function body each time I create a new function. This “captures” the value of the $ix index variable, so that the value will not change on subsequent loops. If I did not use closures, when the for loop ended, all of the functions would be using the same terminating value of the loop for $ix, which would be $im.count + 1. Doh.

Module SessionState Context, Wha?

So if I ripped out that literal string parsing stuff for the module $body, why am I still using it for the property getter/setter generation? (lines 115 / 130)  Well, I have no choice: Creating a new module creates an entirely new SessionState; in simple terms, session state is essentially a giant Hashtable in which PSVariables are stored. If I closed over those get/set ScriptBlocks containing the $ix PSVariable, when it is bound to the module it will use the new module’s SessionState – which is empty! The value of $ix would be undefined. The other variables in those strings are escaped, because they will be evaluated in the module’s context later, when the getters and setters are invoked. Those variables are seeded by the module’s initialization done at the new-module call at line 88.

It’s all very succinct, but rest assured, very powerful.

Have fun!

NOTE: I realise that some more refactoring could probably eliminate this script parsing, but the value is in understanding how modules may affect usage of closures, and the problems that may occur.

PowerShell 2.0 CTP3: Modules, in Practice – .NET Interfaces

This is a bit of a hardcore example of the power that modules have brought to the table in v2.0. First, a little background - PowerShell’s type adaptation system has always ignored the concept of interfaces. Frankly, it never really needed to pay them any attention. The adapted view of any instance of a .NET class is just an aggregate of all its methods; the most derived instance is the default, and only, view. This is the most simple and most frequently used view. However, this all goes to hell when you bring in the notion of explicit interfaces.

Explicit Interface Definitions

Here’s an example of a C# class, Test, with two interfaces: IFace, and IFaceEx. I’ve put this whole example in PowerShell script so you can easily test it without firing up Visual Studio. Add-Type cmdlet to the rescue!

if (-not ("Test" -as [type])) {    
    add-type @"
        public interface IFace {
            string Hello(string name);
            string Goodbye();
        }
        
        public interface IFaceEx {
            string Hello(string name);
            string Goodbye();
            string Prop1 {
                get;
                set;
            }                
        }
        
        public class Test : IFace, IFaceEx {
            public string Hello(string name) {
                return "Hello" + name + " from IFace";
            }
            public string Goodbye() {
                return "Goodbye from IFace";
            }            
            string IFaceEx.Hello(string name) {
                return "Hello " + name + " from IFaceEx";
            }
            string IFaceEx.Goodbye() {
                return "Goodbye from IFaceEx";
            }
            
            private string _prop1 = "Foo";
            
            string IFaceEx.Prop1 {
                get { return _prop1; }
                set { _prop1 = value; }
            }
        }
"@    
}

If you new up an instance of Test, you’ll see that the explicit interface definitions are hidden. Any calls to Hello(string) will invoke the IFace implementation.

There is no way to call IFaceEx.Hello without using reflection!

So, this is where the beauty of modules can help us. The following function will take an instance of a .NET class, and the interface you want a  reference to, and will return a PSCustomObject with ScriptMethods and ScriptProperties bound to that interface’s contract. You don’t have to do this only with explicit interfaces, any interfaces will work ;-)

Introducing Get-Interface

A script is worth a thousand words.

function Get-Interface {

#.Synopsis
#   Allows PowerShell to call specific interface implementations on any .NET object. 
#.Description
#   Allows PowerShell to call specific interface implementations on any .NET object. 
#
#   As of v2.0, PowerShell cannot cast .NET instances to a particular interface. This makes it
#   impossible (shy of reflection) to call methods on explicitly implemented interfaces.   
#.Parameter Object
#   An instance of a .NET class from which you want to get a reference to a particular interface it defines.
#.Parameter InterfaceType
#   An interface type, e.g. [idisposable], implemented by the target object.
#.Example
#   // a class with explicitly implemented interface   
#   public class MyObj : IDisposable {
#      void IDisposable.Dispose()
#   }
#   
#   ps> $o = new-object MyObj
#   ps> $i = get-interface $o ([idisposable])
#   ps> $i.Dispose()      
#.ReturnValue
#   A PSCustomObject with ScriptMethods and ScriptProperties representing methods and properties on the target interface.
#.Notes
#   AUTHOR:    Oisin Grehan http://www.nivot.org/
#   LASTEDIT:  2009-03-25 11:35:23

    [CmdletBinding()]
    param(
        [ValidateNotNull()]
        $Object,
        
        [ValidateScript( { $_.IsInterface } )]
        [type]$InterfaceType
    )

    $private:t  = $Object.GetType()
    
    try {
        
        $private:m  = $t.GetInterfaceMap($InterfaceType)

    } catch [argumentexception] {
        
        throw "Interface $($InterfaceType.Name) not found on ${t}!"
    }

    $private:im = $m.InterfaceMethods
    $private:tm = $m.TargetMethods
    
    # TODO: use param blocks in functions instead of $args
    #       so method signatures are visible via get-member
    
    $body = [scriptblock]::Create(@"
        param(`$o, `$i)    
        
        `$script:t  = `$o.GetType()
        `$script:m  = `$t.GetInterfaceMap(`$i)
        `$script:im = `$m.InterfaceMethods
        `$script:tm = `$m.TargetMethods
        
        # interface methods $($im.count)
        # target methods $($tm.count)
        
        $(
            for ($ix = 0; $ix -lt $im.Count; $ix++) {
                $mb = $im[$ix]
                @"
                function $($mb.Name) {
                    `$tm[$ix].Invoke(`$o, `$args)
                }

                $(if (!$mb.IsSpecialName) {
                    @"
                    Export-ModuleMember $($mb.Name)

"@
                })
"@
            }
        )
"@)
    write-verbose $body.tostring()    
    
    # create dynamic module
    $module = new-module -ScriptBlock $body -Args $Object, $InterfaceType -Verbose
        
    # generate method proxies - all exported members become scriptmethods
    # however, we are careful not to export getters and setters.
    $custom = $module.AsCustomObject()
    
    # add property proxies - need to use scriptproperties here.
    # modules cannot expose true properties, only variables and 
    # we cannot intercept variables get/set.
    
    $InterfaceType.GetProperties() | % {

        $propName = $_.Name
        $getter   = $null
        $setter   = $null
       
        if ($_.CanRead) {
            
            # where is the getter methodinfo on the interface map?
            $ix = [array]::indexof($im, $_.GetGetMethod())
            
            # bind the getter scriptblock to our module's scope
            # and generate script to call target method
            $getter = $module.NewBoundScriptBlock(
                [scriptblock]::create("`$tm[{0}].Invoke(`$o, @())" -f $ix))
        }
        
        if ($_.CanWrite) {
            
            # where is the setter methodinfo on the interface map?
            $ix = [array]::indexof($im, $_.GetSetMethod())
            
            # bind the setter scriptblock to our module's scope
            # and generate script to call target method
            $setter = $module.NewBoundScriptBlock(
                [scriptblock]::create(
                    "param(`$value); `$tm[{0}].Invoke(`$o, `$value)" -f $ix))
        }
        
        # add our property to the pscustomobject
        $prop = new-object management.automation.psscriptproperty $propName, $getter, $setter
        $custom.psobject.properties.add($prop)
    }
    
    # insert the interface name at the head of the typename chain (for get-member info)
    $custom.psobject.TypeNames.Insert(0, $InterfaceType.FullName)
    
    # dump our pscustomobject to pipeline
    $custom
}
Here's how to use it, with our example Test class as added earlier:
$if = Get-Interface (new-object test) ([ifaceex]) -verbose
$if.Hello("Oisin") # returns Hello Oisin from IFaceEx

$if.Prop1 = "Test" # property setter
$if.Prop1 # propery getter

Have fun!

PowerShell 2.0 CTP3: Modules, in Practice.

There’s a bit of a flap going on now around the blogosphere as various vendors, eager to get onto the PowerShell train, are doing the bare minimum to get their product “powershellized.” No one has spent any time reading – or if they did, they weren’t successful in trying to understand it – the Microsoft Command Line Standard.

Modules as Namespaces

That’s right, modules are not as crazy sounding as they seem. They can be used quite simply to just group a load of functions together while allowing easy disambiguation should there be a name collision with another function or cmdlet. It wasn’t always this easy.

Namespaces in PowerShell v1.0

In PowerShell v1.0 it was possible to load two snap-ins that contained identically named commands. This causes PowerShell to spit out an error about ambiguous commands should you try to invoke one. The answer to this was to get the containing snap-in name, and prefix it to the cmdlet name using a backslash as a separator:

$o = Pscx\New-Object Collections.Generic.Dictionary –Of String, Int

As you can see, this lets us call the hypothetical PowerShell Community Extensions version of New-Object to create a generic type. If you want to call the original one, you would have to prefix it like this:

$o = Microsoft.PowerShell.Utility\New-Object Int[] 5

Blech - that’s a bit of a lengthy sentence, but the solution to this is to use an alias. Aliases are found before Cmdlets in the search path:

New-Alias New-Object Microsoft.PowerShell.Utility\New-Object
$o = New-Object Int[] 5 

The reason we need an alias here is because if you just typed the cmdlet without the snap-in prefix, PowerShell v1.0 complains that it doesn’t know which one you want. We forgot to add the mind reader snap-in!

How about functions in PowerShell v1.0? If you dot source a ps1 file that contains functions that already exist in the caller’s session, how do you disambiguate them? Oops! You can’t. They are overwritten. Ouch.

You cannot use the namespace\ prefix for functions in v1 - the support is just not there. However…

Namespaces in PowerShell v2.0

Things are a lot better in this version. Let’s say you have two groups of functions that you use in your business. One for your SharePoint farms, and another set for your Citrix farms. Let’s ignore the perfectly acceptable idea of prefixing the noun to differentiate for the moment and just imagine we have two ps1 files, containing ideally named function with approved verb, simple noun:

# sharepoint functions
function Get-Server {
   # ... gets sharepoint server
}
function Get-Farm {
   # ... gets sharepoint farm
}

And the second script:

# citrix functions
function Get-Server {
    # ... gets citrix server
}
function Get-Farm {
    # ... gets citrix farm
}

If you tried to load these up with via a dot source, one after the other, the functions from the second ps1 would overwrite the first lot. So this is where the wonder world of modules is entered. First step:

After renaming both ps1 files to use the psm1 extension instead, you have created modules of the simplest form. Ignoring the details of how this is deployed (there are several ways – documented on Microsoft and on many blogs, including this one), let's look at how it's loaded and used:

import-module citrix
# Now our functions are loaded. Invocation options:
# assuming no clash, invoke with simple names
get-farm bleh
# load sharepoint module (it also has get-farm)
import-module sharepoint
# ok, sharepoint functions are loaded too, so we have two get-farm commands
# and the last loaded wins, so citrix get-farm is inaccessible through simple syntax, but...
# let's refer specifically to the citrix module function
citrix\get-farm
# and for sharepoint:
sharepoint\get-farm
# and thirdly, if you don't want to use the module qualifier (or "namespace") then you ALSO have the
# choice of applying a prefix on import - this is the primary use case for quick interactive use:
import-module citrix -prefix ctx
# now you can call functions like:
get-ctxfarm
# or even citrix\get-ctxfarm if you so felt like it, but redundant.
# same for sharepoint
import-module sharepoint -prefix sp
get-spfarm

Make sense so far? Unfortunately, as mentioned earlier, in v1.0 the module qualifier (or "namespace") works ONLY for binary commands imported in a Snap-In DLL. Let’s see how that works with get-childitem, a command from one of the preloaded Snap-Ins in PowerShell:

gcm get-childitem | select pssnapin

outputs:

PSSnapIn
--------
Microsoft.PowerShell.Management

Ergo, we invoke it like:
Microsoft.PowerShell.Management\get-childitem
# ... outputs file listing ...

Module Aliasing

Some modules may have longer names that you would be comfortable to type all the time. Thankfully, there is a trick you can do that takes advantage of the fact that modules can be nested. Let’s say you have a module from a vendor that is called something like “Vendor.Division.Product” and it has a cmdlet in it called Get-Thing that happens to clash with another Get-Thing you have loaded. Normally to disambiguate, you have to type:

import-module vendor.division.product
Vendor.Division.Product\Get-Thing

…which is a bit annoying. Instead, wrap the initial import-module statement in a dynamic module where you supply an explicit name for it, and import that instead ;-)

new-module –name product { import-module vendor.division.product } | import-module
# you can now invoke the command like this:
product\get-thing

Any modules that are imported inside a module have their commands automatically exported as if they are part of the root module (unless you control visibility with export-modulemember). Cool, eh?

Have fun!

Visual Studio 2008 Extensions for WSS 3.0 v1.3 March CTP

It looks like the SharePoint team is slowly catching up to what the community has had for years in various guises on CodePlex, but I’ve a good feeling that sometime in the future, the community will finally be able to relax and get back to real work as official support will surpass even the best of the open source offerings (optimistic? we’ll see). Here’s the feature set for the current CTP:

(These are features added in v1.3 which were not available in v1.2 or v1.1)

· Support for developing and deploying on 64 bit (x64).

  • New menu commands within Visual Studio
    • Package - package the solution but does not deploy.
    • Retract - retracts and deletes the active solution from SharePoint.
    • Quick Deploy
      • Copy to 12 - copies template files, modules to the 12 Hive.
      • Copy Binarie(s) - deploys only the assemblies.
      • Copy Both
      • Recycle Application Pool
      • Attach to IIS Worker Processes
  • WSP View Improvements (Create New Feature)
    • Allows specify feature scope (Web, Site, Web Application, Farm)
    • Allows creation of a feature receiver
    • Option to automatically create elements.xml with new feature
    • Elements can be dragged from one feature to another
  • Build Commands Updates
    • Package command added to Visual Studio IDE and command line.
    • Retract command added to Visual Studio IDE and command line.

1) Command line operation is as follows:
devenv.exe <Project.csproj or Solution.sln and other usual flags> /deploy Debug
devenv.exe <Project.csproj or Solution.sln and other usual flags> /deploy Debug /package
devenv.exe <Project.csproj or Solution.sln and other usual flags> /deploy Debug /retract
Note that with the /package and /retract flags the /deploy flag is required.  

  • CTRL-F5 no longer launches the debugger. Use F5 to launch the debugger. CTRL-F5 will just launch the web browser and show the SharePoint site.
  • SharePoint Solution Generator Enhancements
    • Export from publishing sites. Note that round tripping of generated sites is not supported for anything but the simplest of sites. Instead some editing may be required.
  • Correct VB and C# Inconsistency Correction
    • VB VSeWSS projects no longer use VSeWSS as the root namespace
  • New Item Templates for RootFiles
    • Create a <RootFiles> item from template.
  • Web Part Item Template Improvements
    • Allows selection of deployment model to the global assembly cache (GAC) or to the \BIN directory. For /BIN deployment a simple Code Access Security permissions set is provided.
  • Easier Web Part Project Item Rename
    • Detect web part rename and modify dependent files
  • Support for deploying dependant assemblies added as a reference with CopyLocal=true.
    • VSeWSS now checks for any "CopyLocal=true" reference and incorporates this binary in the manifest.  The binary is added to the manifest with a DeploymentTarget matching the first binary found in the manifest, usually the project target binary.  Subsequent changes to "CopyLocal", or removing a binary altogether will correctly be cleaned up when rebuilding the manifest (either through WSP view or through a deployment command).  Changes to the DeploymentTarget can be made by editing the manifest.
  • Improved solution validation during full deployment
  • Validation Logging
    • Adding the element <add key="IfLog" value="true" /> to the <appSettings> node of your web.config will turn on validation logging. Logging occurs during a full deployment from a VSeWSS project and can be used for diagnostic purposes. Note: The log location will be noted on the build output window."
  • Conflict resolution dialog when deploying from within Visual Studio
    • Duplicate Features: If a feature with the same name as a feature you are trying to deploy is already on the server a dialog will give you the choice of uninstalling the existing feature first. If the feature was deployed via WSP the entire solution is retracted. If the feature was manually installed it will be deactivated then uninstalled.
    • Duplicate Site Definition Folders: Site definition folder is the same as 12/Templates/SiteTemplates or the WebTemp folder already exists. The option to delete the deployed folder is available.
    • RootFiles and Templates: If there is a RootFile or Template with the same name the option to delete the files first is given.
    • Solutions: If a solution with the same name exists on the server (possibly unique solution IDs) the option is given to retract and delete the existing solution.
  • The List Definition from Content Type template now allows for the creation of a List Definition Event Receiver.

Grab it from the Microsoft Download Center.

CTP3 ProxyCommand Tricks: Extending Get-Help to cover .NET Types and Members

UPDATED 2009-03-11: BUGFIXES FOR CTP3! PLEASE RE-DOWNLOAD v0.2.1

One of the things I hear now and then is that while PowerShell the language can be mastered with the requisite effort, the .NET namespace is considerable harder to get a handle on. Wouldn’t it be nice if you could type things like:

PS> get-help -object [string]::format

Since Functions take precedence over native Cmdlets in the command search order, the first thing you might think is to create a function with the same name, get-help, that can do this. The function itself would have the same parameters as the native Cmdlet, and add one of its own: -object. If the arguments do not belong to the “object” ParameterSet, then the function would splat (using the @ operator) the original arguments to the native Cmdlet transparently and transfer control. The user is none the wiser we had a quick peek at the arguments. So, before we run off duplicating a Cmdlet’s surface through script, first stop, Command Proxies!

Command Proxies

First off, required reading: Extending and/or Modifying Commands with Proxies. Ok, now that you’ve got read that entirely and gotten it  out of the way, you understand that the [ProxyCommand]::Create(…) method lets you automatically generate a function that delegates to a steppable pipeline wrapping the original Cmdlet, right? What I did then is to pack all of these functions into a psm1 module, create a nice psd1 module manifest and away we go.

The ObjectHelp Get-Help Extension Module

So here’s a quick look at the help for my module, in friendly, easy to read Cmdlet help style. The code for the PSM1 module file is way too large to dump here on my blog like I would usually do, so it’s available at as an attachment at the foot of the page. This help below is in a comment in the psm1 file itself.

    NAME
   
        ObjectHelp Extensions Module 0.2 for PowerShell 2.0 CTP3
    
    SYNOPSIS
   
         Get-Help -Object allows you to display usage and summary help for .NET Types and Members.
        
    DETAILED DESCRIPTION
   
        Get-Help -Object allows you to display usage and summary help for .NET Types and Members.
   
        If local documentation is not found and the object vendor is Microsoft, you will be directed
        to MSDN online to the correct page. If the vendor is not Microsoft and vendor information
        exists on the owning assembly, you will be prompted to search for information using Microsoft
        Live Search.
    
    TODO
    
         * localize strings into PSD1 file
         * Implement caching in hashtables. XMLDocuments are fat pigs.
         * Support getting property/field help
         * PowerTab integration
         * Test with Strict Parser
            
    EXAMPLES

        # get help on a type
        PS> get-help -obj [int]

        # get help against live instances
        PS> $obj = new-object system.xml.xmldocument
        PS> get-help -obj $obj

        or even:
       
        PS> get-help -obj 42
       
        # get help against methods
        PS> get-help -obj $obj.Load

        # explictly try msdn
        PS> get-help -obj [regex] -online

        # go to msdn for regex's members
        PS> get-help -obj [regex] -online -members

    CREDITS
   
        Author: Oisin Grehan (MVP)
        Blog  : http://www.nivot.org/
   
        Have fun! 

Usage Examples

So what does it actually look like when you call help? If the help is available locally, it is displayed inline in the console (or ISE). Help for all types in MSCorlib and System is pre-cached. Any other help for types in assemblies belonging to the BCL (Base Class Libraries – effectively the stock .NET assemblies) will be loaded on-demand. This typically takes just a few seconds and will cached for the rest of your session.

image

TODO and BUGS

Right now, it cannot deal with properties. That is to say, trying:

PS> get-help –object $s.length

where $s is a string, will get help on the property _type_, not the property itself. I have some ideas to get around this, so if you can wait for 0.3, I’d be happy. Of course you can wait. You have no choice. :D

Download

The two files come in a zip file. Unzip this file to ~\documents\windowspowershell\modules\objecthelp\ where ~ is your home directory. On vista/win7 this would be c:\users\username. On XP, it would be c:\documents and settings\username. To load it, just execute:

PS> import-module objecthelp

That’s it! Have fun!

Q&A on Yours Truly for Microsoft Canadian Developers Connection

A couple of months ago I had a brief Q&A with the cdndevs guys over on blogs.msdn.com. It looks like it went live recently because I’ve been getting stopped in the streets and harassed for a photo etc. No, not really. But if you want to read, take a look over at http://blogs.msdn.com/cdndevs/archive/2009/02/12/mvp-insider-q-a-with-oisin-grehan.aspx ;-)

Anatomy of a PowerShell Module: The FileTransfer (BITS) Module

Some of you may have heard that CTP3 has support for transferring files from remote servers using BITS (Background Intelligent Transfer Service) and quite possibly had a little dig around for it. It’s not easy to find as using “get-help BITS” will not return any help unless you’ve already loaded the module. Not a great win for discoverability, but hey, sometimes you just have to read the manual, expletive deleted. So, lets have a quick look at how this works.

image

So, ok, we can see we’ve got a module for FileTransfer (BITS), but where did this come from? Take a peek into $pshome\Modules to find out. This is the location for system-wide (global) modules. It rests under $PSHome, which is the home for PowerShell, in $env:systemroot\windowspowershell\v1.0.

Yes, even PowerShell v2 lives there. This is one of those unfortunate things whereby the team decided early on that they would support side-by-side versions of PowerShell, implying that v2 might not be backwards compatible with v1. This is reflected also in the choice of file extension: ps1. The extension ps2 would have been used for v2. This was probably a poor show of faith in their own abilities to design such a great version one product, because as it turns out, v1.0 was such an excellent release that they were able to build on it for v2 without compromising, or breaking backwards compatibility (apart from a couple of minor edge cases, mainly bugfixes). So, the end result is that future versions of PowerShell will inherit the $pshome, pay a little inheritance tax and hopefully avoid foreclosure :).

The files in the FileTransfer module directory are laid out as follows:

image

(Hey look! another built-in module – PSDiagnostics - I’ll leave that one for you guys to explore).

So, there are four files there. A ps1xml Format file, which tells PowerShell how to textually render the .NET objects returned by the Cmdlets. A psd1 Module Manifest file, which tells PowerShell what files comprise the Module and a binary DLL which in this case does NOT contain the Cmdlets, but instead is what’s called an “Interop Assembly.”  (sometimes known as Primary Interop Assemblies or PIAs, read about them here: Primary Interop Assemblies – there are subtle differences between IAs and PIAs, but not enough to warrant discussion here). Essentially this DLL lets .NET, and by extension, PowerShell, talk to the native COM APIs that wrap the BITS services on XP,Vista,Win7,Win 2003 and Win 2008. Lets take a peek into the Module Manifest:

  1. @{  
  2. GUID="{8FA5064B-8479-4c5c-86EA-0D311FE48875}" 
  3. Author="Microsoft Corporation" 
  4. CompanyName="Microsoft Corporation" 
  5. Copyright="c Microsoft Corporation. All rights reserved." 
  6. ModuleVersion="1.0.0.0" 
  7. Description="Powershell File Transfer Module" 
  8. PowerShellVersion="2.0" 
  9. CLRVersion="2.0" 
  10. NestedModules="Microsoft.BackgroundIntelligentTransfer.Management" 
  11. FormatsToProcess="FileTransfer.Format.ps1xml" 
  12. RequiredAssemblies=Join-Path $psScriptRoot "Microsoft.BackgroundIntelligentTransfer.Management.Interop.dll" 

If it looks like I just dumped a Hashtable out, you’re right. That’s all a manifest is: a Hashtable. Of course, the key names are important and in this case, lets take a look at two in particular, NestedModules and RequiredAssemblies.

The RequiredAssemblies key is responsible for actively loading .NET assemblies containing standard .NET types. It does not extract Providers and Cmdlets and add them to the runspace; this is what the other key, NestedModules, does. Like I said already, this DLL is the interop assembly. So where is the NestedModules loading that other module from? I don’t see any other DLLs in the directory, and I know there are no Cmdlets in that interop assembly. Lets take a peek at one of the Cmdlets itself and find out:

image

Aha, it’s reading it from the GAC. When PowerShell was installed, it must have installed this DLL there. This is the DLL that contains our Cmdlets. I know this is true because I used one of the Cmdlets to tell me where it lives. “GCM” is the alias for Get-Command which will get us metadata about any given command in PowerShell (including functions – new to v2).

So, lets use a one-liner to get the names of all the Cmdlets in this module, and the synopsis summary for the help:

  1. gcm -module filetransfer | % { $_ | select name, @{Name="Help";Expression={& $_ -? | select -expand synopsis }}} | convertto-html –fragment 

This yields the following:

Name Help
Add-FileTransfer Adds one or more files to an existing Background Intelligent Transfer Service (BITS) t ransfer job.
Clear-FileTransfer Cancels a Background Intelligent Transfer Service (BITS) transfer job.
Complete-FileTransfer Completes a Background Intelligent Transfer Service (BITS) transfer job.
Get-FileTransfer Retrieves the associated BitsJob object for an existing Background Intelligent Transfe r Service (BITS) transfer job.
New-FileTransfer Creates a new Background Intelligent Transfer Service (BITS) transfer job.
Resume-FileTransfer Resumes a Background Intelligent Transfer Service (BITS) transfer job.
Set-FileTransfer Modifies the properties of an existing Business Intelligent Transfer Service (BITS) tr ansfer job.
Suspend-FileTransfer Suspends a Background Intelligent Transfer Service (BITS) transfer job.

I guess this covers it for the moment. Going into a full discussion about how BITS actually works is worth another post in itself. Suffice it to say, there are plenty of examples in the help itself. Just run:

get-help add-filetransfer –example | more

To see an example on how to use the Cmdlet (or any of the others). The team have done a good job here.

Have fun!

About the author

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

Month List

Page List