# Wednesday, March 10, 2010

This is unashamedly a post for developers, in particular those with an interest in functional languages. With the advent of PowerShell 2.0, some of you may have noticed that ScriptBlocks - which I suppose could also be called anonymous functions or lambdas - gained a new method: GetNewClosure. Closures are one of the essential tools for functional programming., something I’ve been trying to learn more about over the last few years. I don’t really have an opportunity to use it in work other than the hybrid trickery available in C# 3.0, but I have been tinkering a lot with PowerShell 2.0 to see if some of the tricks of the functional trade could be implemented. It’s just a shell language, but there are some nice features in there that enable a wide variety of funky stuff.

Partial Application

In a nutshell, partial application of a function is when you pass in only some of the parameters and get a function back that can accept the remaining parameters:

# define a simple function
function test {
    param($a, $b, $c);
    "a: $a; b: $b; c:$c"
}

# partially apply with -c parameter
$f = merge-parameter (gcm test) -c 5

# partially apply with -c and -a then execute with -b (papp is an alias)
& (papp (papp (gcm test) -c 3) -a 2) -b 7

# partially apply the get-command cmdlet with -commandtype
# and assign the result to a new function
si function:get-function (papp (gcm get-command) -commandtype function)

This is by no means a complete implementation of a partial application framework for powershell. The merge-parameter function (aliased to papp) currently only works with the default parameterset and does not mirror any of the parameteric attributes in the applied function or cmdlet. I'm not saying it couldn't do that, but this is purely a proof of concept. The module is listed below and is also available from PoshCode at http://poshcode.org/1687

# save as functional.psm1 and drop into your module path
Set-StrictMode -Version 2

$commonParameters = @("Verbose",
                      "Debug",
                      "ErrorAction",
                      "WarningAction",
                      "ErrorVariable",
                      "WarningVariable",
                      "OutVariable",
                      "OutBuffer")

<#
.SYNOPSIS
    Support function for partially-applied cmdlets and functions.
#>
function Get-ParameterDictionary {
    [outputtype([Management.Automation.RuntimeDefinedParameterDictionary])]
    [cmdletbinding()]
    param(
        [validatenotnull()]
        [management.automation.commandinfo]$CommandInfo,
        [validatenotnull()]
        [management.automation.pscmdlet]$PSCmdletContext = $PSCmdlet
    )
    
    # dictionary to hold dynamic parameters
    $rdpd = new-object Management.Automation.RuntimeDefinedParameterDictionary

    try {
        # grab parameters from function
        if ($CommandInfo.parametersets.count > 1) {
            $parameters = $CommandInfo.ParameterSets[[string]$CommandInfo.DefaultParameterSet].parameters
        } else {
            $parameters = $CommandInfo.parameters.getenumerator() | % {$CommandInfo.parameters[$_.key]}
        }        
                
        $parameters | % {
            
            write-verbose "testing $($_.name)"
                                    
            # skip common parameters        
            if ($commonParameters -like $_.Name) {                                  
                
                write-verbose "skipping common parameter $($_.name)"
                
            } else {
                
                $rdp = new-object management.automation.runtimedefinedparameter
                $rdp.Name = $_.Name
                $rdp.ParameterType = $_.ParameterType
                
                # tag new parameters to match this function's parameterset
                $pa = new-object system.management.automation.parameterattribute
                $pa.ParameterSetName = $PSCmdletContext.ParameterSetName
                $rdp.Attributes.Add($pa)
                
                $rdpd.add($_.Name, $rdp)
            }
            
        }
    } catch {
    
        Write-Warning "Error getting parameter dictionary: $_"
    }
    
    # return
    $rdpd
}

<#
.SYNOPSIS
    Function that accepts a FunctionInfo or CmdletInfo reference and one or more parameters
    and returns a FunctionInfo bound to those parameter(s) and their value(s.)
.DESCRIPTION
    Function that accepts a FunctionInfo or CmdletInfo reference and one or more parameters
    and returns a FunctionInfo bound to those parameter(s) and their value(s.)
    
    Any parameters "merged" into the function are removed from the available parameters for
    future invocations. Multiple chained merge-parameter calls are permitted.
.EXAMPLE

    First, we define a simple function:
    
    function test {
        param($a, $b, $c, $d);
        "a: $a; b: $b; c:$c; d:$d"
    }
    
    Now we merge -b parameter into functioninfo with the static value of 5, returning a new
    functioninfo:
    
    ps> $x = merge-parameter (gcm test) -b 5
    
    We execute the new functioninfo with the & (call) operator, passing in the remaining 
    arguments:
    
    ps> & $x -a 2 -c 4 -d 9
    a: 2; b: 5; c: 4; d: 9
    
    Now we merge two new parameters in, -c with the value 3 and -d with 5:
    
    ps> $y = merge-parameter $x -c 3 -d 5
    
    Again we call $y with the remaining named parameter -a:
    
    ps> & $y -a 2
    a: 2; b: 5; c: 3; d: 5
.EXAMPLE

    Cmdlets can also be subject to partial application. In this case we create a new
    function with the returned functioninfo:
    
    ps> si function:get-function (merge-parameter (gcm get-command) -commandtype function)
    ps> get-function
                
.PARAMETER _CommandInfo
    The FunctionInfo or CmdletInfo into which to merge (apply) parameter(s.)
    
    The parameter is named with a leading underscore character to prevent parameter
    collisions when exposing the targetted command's parameters and dynamic parameters.
.INPUTS
    FunctionInfo or CmdletInfo
.OUTPUTS
    FunctionInfo
#>
function Merge-Parameter {    
    [OutputType([Management.Automation.FunctionInfo])]
    [CmdletBinding()]
    param(
        [parameter(position=0, mandatory=$true)]
        [validatenotnull()]
        [validatescript({
            ($_ -is [management.automation.functioninfo]) -or `
            ($_ -is [management.automation.cmdletinfo])
        })]
        [management.automation.commandinfo]$_Command
    )
    
    dynamicparam {
        # strict mode compatible check for parameter
        if ((test-path variable:_command)) {
            # attach input functioninfo's parameters to self
            Get-ParameterDictionary $_Command $PSCmdlet
        }
    }

    begin {
        write-verbose "merge-parameter: begin"
        
        # copy our bound parameters, except common ones              
        $mergedParameters = new-object 'collections.generic.dictionary[string,object]' $PSBoundParameters
        
        # remove our parameters, leaving only target function/CommandInfo's args to curry in
        $mergedParameters.remove("_Command") > $null
        
        # remove common parameters
        $commonParameters | % {
            if ($mergedParameters.ContainsKey($_)) {
                $mergedParameters.Remove($_)  > $null
            }
        }
    }
    
    process {
        write-verbose "merge-parameter: process"
        
        # temporary function name
        $temp = [guid]::NewGuid()

        $target = $_Command

        # splat our fixed named parameter(s) and then splat remaining args
        $partial = {
            [cmdletbinding()]
            param()
            
            # begin dynamicparam
            dynamicparam
            {                
                $targetRdpd = Get-ParameterDictionary $target $PSCmdlet
        
                # remove fixed parameters
                $mergedParameters.keys | % {
                    $targetRdpd.remove($_) > $null
                }
                $targetRdpd
            }
            begin {
                write-verbose "i have $($mergedParameters.count) fixed parameter(s)."
                write-verbose "i have $($targetrdpd.count) remaining parameter(s)"
            }
            # end dynamicparam
            process {
                $boundParameters = $PSCmdlet.MyInvocation.BoundParameters
                
                # remove common parameters (verbose, whatif etc)
                $commonParameters | % {
                    if ($boundParameters.ContainsKey($_)) {
                        $boundParameters.Remove($_)  > $null
                    }
                }
                
                # invoke command with fixed parameters and passed parameters (all named)
                . $target @mergedParameters @boundParameters
                
                if ($args) {
                    write-warning "received $($args.count) arg(s) not part of function."
                }
            }
        }
        
        # emit function/CommandInfo
        new-item -Path function:$temp -Value $partial.GetNewClosure()
    }
    
    end {
        # cleanup
        rm function:$temp
    }    
}

new-alias papp Merge-Parameter -force

Export-ModuleMember -Alias papp -Function Merge-Parameter, Get-ParameterDictionary

Have fun[ctional]!

posted on Wednesday, March 10, 2010 8:01:07 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Sunday, November 01, 2009

One of the things that never quite fit well with me with the remoting feature in PowerShell 2.0 is that while you can “telnet” to remote systems with Enter-PSSession and import commands and do all sorts of cool tricks, there is no way to send or retrieve files from the console. It seems like such a waste that you configure WinRM up with SSL and Kerberos and get this nice encrypted channel up, but if you want to transfer files you have to revert to file shares, remote desktop or classic ftp.

Back in the “good ole’ days” of BBSs and FidoNet, people used to use simple protocols like XModem (advancing to YModem and then ZModem) or Kermit that worked by streaming character data directly to your terminal. It wasn’t fast or particularly efficient, but it got the job done. I thought I’d take a crack at building something similar for PowerShell, and this first 0.5 release is the fruits of this weekend’s tinkering. At the moment it only can “pull” a file to the local system from a remote session, but the next release will allow “pushing” a file from a local system to a remote session.

image

The reason I named it after XModem is because it works in a similar way: files are not “pulled” from the remote server, but instead are “pushed.” X[YZ]Modem file transfer was initiated by the remote end. I’ll not spoil the fun by explaining how it works, but I think you’ll enjoy pulling it apart. It’s in a module format and is implemented in pure script.

Requirements

  • PowerShell 2.0 installed on both client and server with remoting enabled to the location of the file being transferred.

E.g. if you want to grab a file using Get-RemoteFile from a remote server, you must be able to create a valid PSSession to it with the New-PSSession cmdlet. When Send-LocalFile is implemented, you’ll need remoting enabled in the other direction too.

  • The PMODEM module must be findable on both the client and server via import-module and must be the same version.

Here’s the Get-RemoteFile function help (via –?):

NAME
    Get-RemoteFile

SYNOPSIS
    Retrieves a file from a remote computer via a supplied PSession.


SYNTAX
    Get-RemoteFile [-Session] <pssession> [-RemoteFile] <string> [[-LocalPath] <string>] [[-PacketSize] <int32>]
	[-PassThru] [-AsJob] [<commonparameters>]


DESCRIPTION
    Retrieves a remote file from a server via a supplied PSSession. All communication
    is performed out-of-band, yet inside the secure WinRM channel.

    No other ports, file shares or any other special configuration is needed. However,
    the PMODEM module must be on the remote computer and findable in its $ENV:PSModulePath;
    the protocol versions must also match on both ends. You will be warned of any
    misconfiguration(s).

    When not running asynchronously, progress records are generated.


RELATED LINKS

REMARKS
    To see the examples, type: "get-help Get-RemoteFile -examples".
    For more information, type: "get-help Get-RemoteFile -detailed".
    For technical information, type: "get-help Get-RemoteFile -full".

Things coming in later releases: wildcards/multiple file support, compression and integration via proxy functions (copy-item/move-item/remove-item/rename-item etc).

Download PModem

Grab pmodem-0.5.zip and unzip it into a folder in your $ENV:PSModulePath on the client and server computers you want to use PMODEM on.

Have fun!

posted on Sunday, November 01, 2009 8:57:10 PM (Eastern Standard Time, UTC-05:00)  #    Comments [1] Trackback
# Wednesday, October 28, 2009

( from: http://support.microsoft.com/default.aspx/kb/968929 – downloads at foot of page )

Windows PowerShell is a command-line shell and scripting language that is designed for system administration and Automation. Built on the Microsoft .NET Framework, Windows PowerShell enables IT professionals and developers to control and automate the administration of Windows and applications.

New features that are introduced in Windows PowerShell 2.0 include the following:

  • Remoting
    Windows PowerShell 2.0 lets you run commands on one or more remote computers from a single computer that is running Windows PowerShell. PowerShell remoting allows for multiple ways of connecting. These ways include interactive (1:1), fan-out (1:many), and fan-in (many:1 by using the IIS hosting model).
  • Integrated Scripting Environment
    PowerShell Integrated Scripting Environment (ISE) enables you to run interactive commands and edit and debug scripts in a graphical environment. The main features include color-coded syntax, selective execution, graphical debugging, Unicode support, and context-sensitive help.
  • Modules
    Modules allow for script developers and administrators to partition and organize their Windows PowerShell code in self-contained, reusable units. Code from a module executes in its own self-contained context and does not affect the state outside the module.
  • Advanced functions
    Advanced functions are functions that have the same capabilities and behavior as cmdlets. However, they are written completely in the Windows PowerShell language, instead of compiled C#.
  • Background jobs
    Windows PowerShell 2.0 allows for running a command or expression asynchronously and "in the background" without interacting with the console.
  • Eventing
    This feature adds support to the Windows PowerShell engine infrastructure for listening, forwarding, and acting on management and system events.
  • Script internationalization
    This new feature enables Windows PowerShell scripts to display messages in the spoken language that is specified by the UI culture setting on the user's computer.
  • Script debugging
    New debugging features were added to Windows PowerShell that let you set breakpoints on lines, columns, variables, and commands, and that let you specify the action that occurs when the breakpoint is hit.
  • New cmdlets
    Windows PowerShell 2.0 introduces over 100 built-in cmdlets. These cmdlets, excluding other tasks, enables you to do computer-related, event log, and performance counter management tasks.

WinRM 2.0

WinRM is the Microsoft implementation of WS-Management Protocol, a standard Simple Object Access Protocol (SOAP)–based, firewall-friendly protocol that allows for hardware and operating systems from different vendors to interoperate. The WS-Management Protocol specification provides a common way for systems to access and exchange management information across an IT infrastructure.

WinRM 2.0 includes the following new features:

  • The WinRM Client Shell API provides functionality to create and manage shells and shell operations, commands, and data streams on remote computers.
  • The WinRM Plug-in API provides functionality that enables a user to write plug-ins by implementing certain APIs for supported resources and operations.
  • WinRM 2.0 introduces a hosting framework. Two hosting models are supported. One is Internet Information Services (IIS)-based and the other is WinRM service-based.
  • Association traversal lets a user retrieve instances of Association classes by using a standard filtering mechanism.
  • WinRM 2.0 supports delegating user credentials across multiple remote computers.
  • Users of WinRM 2.0 can use Windows PowerShell cmdlets for system management.
  • WinRM has added a specific set of quotas that provide a better quality of service and allocate server resources to concurrent users. The WinRM quota set is based on the quota infrastructure that is implemented for the IIS service.

System requirements

WinRM 2.0 and PowerShell 2.0 can be installed on the following supported operating systems:

  • Windows Server 2008 with Service Pack 2
  • Windows Server 2003 with Service Pack 2
  • Windows Vista with Service Pack 2
  • Windows Vista with Service Pack 1
  • Windows XP with Service Pack 3
Windows PowerShell 2.0 requires the Microsoft .NET Framework 2.0 with Service Pack 1.

BITS 4.0

BITS 4.0 can be installed on the following supported operating systems:
  • Windows Server 2008 with Service Pack 2
  • Windows Vista with Service Pack 2
  • Windows Vista with Service Pack 1
posted on Wednesday, October 28, 2009 9:27:29 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Wednesday, October 21, 2009

You might not know it, but when you import a PowerShell module you can pass it one or more arguments by way of Import-Module's -ArgumentList (aliased to -Args) parameter. While it looks like passing parameters to a standard ps1 file, there are some limitations. Take this module for an example:

# -- begin foo.psm1 --
param()
 
. {
  param(
     [validateset("a","b","c")]
     $letter
 
     function Get-Letter {
        "You initialized the module with $letter"
     }
} @args
# -- end foo.psm1

ps> import-module foo -args b
ps> get-letter
"You initialized the module with b"

You might notice that I am dotting (dot-sourcing or dot-executing) the scriptblock. By doing this, you are ensuring that anything declared in the scriptblock is imported into the calling scope. You can also apply advanced-function style validation to the module initializer by splatting (@) the module arguments ($args). If the scriptblock was called instead (via & { ... } @args) then the nested scope created from the call (&) operator prevents the function Get-Letter from being exported from the module because it goes out of scope when the called scriptblock completes. So, why not put the param block at the top of the module where param() is now you might ask? Because that mechanism is broken and/or partially implemented in v2, and completely ignores [cmdletbinding()] directives and validators. Note also that there is no way to use switches in the traditional sense with module arguments; instead you can pass boolean literals like $true or $false which will be mapped positionally to any declared switches.

So where might you use this technique of module initializers? You could create a generalized custom module that works with your various development environments, or SQL clusters/servers. You pass the name of the environment (or server) to the module on import, and all of the functions exported then are "bound" to that cluster (or server) so you don't have to continually pass each function the cluster (or server) name as an argument.

Have fun!

posted on Wednesday, October 21, 2009 11:05:08 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Tuesday, August 18, 2009

One thing that has been lamented frequently about PowerShell is that it is very difficult to log, if not impossible, to log all of the various types of streams it has to a single source. The legacy Windows shell CMD, and Unix shells like Bash, Ksh etc only deal with three streams: stdin, stdout and stderr for Input, Ouput and Error respectively. PowerShell has many more: Input, Output, Verbose, Warning, Debug, Progress and Error. Finally, the APIs in v2.0 offer enough hooks to unify the logging but you got to work a bit to make it come together. Well, to be honest, you got me doing the work. The rest is easy ;)

import-module .\scriptlogger.psm1 -force

$logger = New-ScriptLogger

# override error handler
$logger.ErrorHandler = {
    param($record)
    
    $record.tostring() >> scriptlog.txt
}

# override verbose handler
$logger.VerboseHandler = {
    param($record)
    
    $record.message >> scriptlog.txt
}

# run scriptblock with logging
$logger.Invoke(
    {
        $verbosepreference='continue';
        $erroractionpreference = 'continue';
        $debugpreference = 'continue';
        write-verbose "verbose";
        write-error "an error";
        write-warning "a warning"
        Write-debug "debug string"
        "this is output"
        1,2,3
    })
And here is the module; save it as ScriptLogger.psm1. By default, all logging goes to an attached debugger, like sysinternals DbgView. You can override any of the handlers like above and do what you want. Each handler receives one argument: a ErrorRecord, WarningRecord, VerboseRecord, DebugRecord or ProgressRecord. All of these Types are native powershell types and are documented on MSDN.
<#
    Name     : Universal Script Logging Module (ScriptLogger.psm1)
    Version  : 0.1
    Author   : Oisin Grehan (MVP)
    Site     : http://www.nivot.org/
#>
function New-ScriptLogger {
    New-Module -AsCustomObject -ScriptBlock {
        
        $script:ps              = [powershell]::Create()
        $script:ar              = $null
        $script:module          = $ExecutionContext.SessionState.Module
        
        [scriptblock]
        $script:ErrorHandler    = {
            param(
                [Management.Automation.ErrorRecord]
                $record
            )
            [diagnostics.debug]::writeline(
                "Error: " + $record.tostring());
        }
        [scriptblock]
        $script:WarningHandler  = {
            param(
                [Management.Automation.WarningRecord]
                $record
            )        
            [diagnostics.debug]::writeline(
                "Warning: " + $record.message);
        }
        [scriptblock]
        $script:VerboseHandler  = {
            param(
                [Management.Automation.VerboseRecord]
                $record
            )
            [diagnostics.debug]::writeline(
                "Verbose: " + $record.message);
        }
        [scriptblock]
        $script:DebugHandler    = {
            param(
                [Management.Automation.DebugRecord]
                $record
            )
            [diagnostics.debug]::writeline(
                "Debug: " + $record.message);
        }
        [scriptblock]
        $script:ProgressHandler = {
            param(
                [Management.Automation.ProgressRecord]
                $record
            )        
            [diagnostics.debug]::writeline(
                "Progress: " + $record);
        }
        
        $script:Handlers   = @{
            Error = Register-ObjectEvent $ps.Streams.Error DataAdded -Action {
                & $event.MessageData {& $ErrorHandler @args} $event.sender[$eventargs.index]
            } -MessageData $module #-SupportEvent
            
            Warning = Register-ObjectEvent $ps.Streams.Warning DataAdded -Action {
                & $event.MessageData {& $WarningHandler @args} $event.sender[$eventargs.index]
            } -MessageData $module #-SupportEvent
            
            Verbose = Register-ObjectEvent $ps.Streams.Verbose DataAdded -Action {
                & $event.MessageData {& $VerboseHandler @args} $event.sender[$eventargs.index]
            } -MessageData $module #-SupportEvent
            
            Debug = Register-ObjectEvent $ps.Streams.Debug DataAdded -Action {
                & $event.MessageData {& $DebugHandler @args} $event.sender[$eventargs.index]
            } -MessageData $module #-SupportEvent
            
            Progress = Register-ObjectEvent $ps.Streams.Progress DataAdded -Action {
                & $event.MessageData {& $ProgressHandler @args} $event.sender[$eventargs.index]
            } -MessageData $module #-SupportEvent
        }
        
        function Invoke {
            param(
                [validatenotnullorempty()]
                [scriptblock]$script
            )
            
            try {
            
                write-host -foreground green "Running"
                
                $ps.commands.clear()
                $command = new-object management.automation.runspaces.command $script, $true            
                $ps.commands.addcommand($command) > $null                
                $ps.invoke() # returns output                
            
            } catch {
            
                # oops-ee!
                write-host -foreground red "Unhandled terminating error: $_"
                $record = new-object management.automation.errorrecord $(
                    new-object exception $_.tostring()), "TerminatingError", "NotSpecified", $null
                & $ErrorHandler $record
            
            } finally {
            
                write-host -foreground green "Complete"
            
            }
        }
                
        Export-ModuleMember -Function Invoke -Variable ErrorHandler, WarningHandler, VerboseHandler, DebugHandler, ProgressHandler
    }
}

Have fun!

posted on Tuesday, August 18, 2009 10:58:24 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [1] Trackback
# Friday, May 22, 2009

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!

posted on Friday, May 22, 2009 5:25:49 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Thursday, May 14, 2009

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.

posted on Thursday, May 14, 2009 8:28:13 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback
# Saturday, March 28, 2009

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.

posted on Saturday, March 28, 2009 6:23:11 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Thursday, March 26, 2009

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!

posted on Thursday, March 26, 2009 9:02:44 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Thursday, March 19, 2009

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!

posted on Thursday, March 19, 2009 10:20:46 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Monday, March 09, 2009

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!

posted on Monday, March 09, 2009 11:08:54 PM (Eastern Standard Time, UTC-05:00)  #    Comments [3] Trackback
# Tuesday, February 10, 2009

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!

posted on Tuesday, February 10, 2009 1:38:27 PM (Eastern Standard Time, UTC-05:00)  #    Comments [1] Trackback
# Tuesday, February 03, 2009

It’s been quite a few years -- November 14, 2006 to be exact -- since the final release of PowerShell 1.0. Let’s see what the lazy buggers have been up to since then. Only kidding, they’re far from lazy; we’ve gained an extra one hundred and six new cmdlets, moving from 131 to 237. We’ve also gained another provider, WSManProvider which lets you explore and manipulate the WS-Man configuration. Finally, the number of public Types (.NET classes usable by 3rd parties to extend PowerShell) has increased from 447 to 749. So how do I know all this? Well, some months ago I wrote a suite of build analysis Cmdlets for examining the assemblies that comprise PowerShell. In its current form it’s of very little use to 3rd parties, but I’ve had some requests to open it up and allow it to analyze any binary modules and snap-ins, which is a good idea I think. Watch this space. Anyway, I’m going to dump out some interest information on the differences between v1.0 and v2.0 CTP3, along with the one-liners I’m using to generate the information.

update feb 5: added breaking changes

Breaking Changes to Windows PowerShell 1.0

The following changes in Windows PowerShell V2.0 CTP3 might prevent features designed for Windows PowerShell 1.0 from working correctly.

    • The value of the PowerShellVersion registry entry in HKLM\SOFTWARE\Microsoft\PowerShell\1\PowerShellEngine has been changed to 2.0.
    • New cmdlets and variables have been added. These are listed below. These new elements might conflict with variables and functions in profiles and scripts.
    • -IEQ operator does a case insensitive comparison on characters.
    • Get-Command gets functions along with cmdlets by default.
    • Any native command that generates a user interface blocks if it is pipelined to the Out-Host cmdlet.
    • Added new language keywords: Begin, Process, and End. Any commands called begin, process or end are interpreted as language keywords and might result in parsing errors.
    • Cmdlet name resolution has changed. In Windows PowerShell 1.0, a runtime error was generated when two Windows PowerShell snap-ins exported cmdlets with the same name. In Windows PowerShell V2, the last cmdlet loaded is the one that is executed if the cmdlet name is not qualified by a snap-in name.
    • Terminating errors that are thrown in a pipeline do not terminate the pipeline. Instead, they are written to the host.
    • A function called with '-?' parameter gets the help topic for the function, if one is included in the function.

There are no changes to Cmdlets other than Get-Command’s –PSSnapin parameter is now an alias to a –Module parameter. This reflects the move away from administrator-installed snap-ins, and towards the friendlier Module system. For all intents and purposes, you can treat v1 snapins as modules as load them as such with the Import-Module Cmdlet.

New Cmdlets

Here are the new Cmdlets, sorted by noun and alongside the textual synopsis is one is present in the CTP3 help, or the syntax if there is no help. The command I ran to generate this was:

  1. compare-psbuildinfo (import-psbuildinfo .\rtm-6-0-6000-16386.psbuild) (import-psbuildinfo .\win7beta1-6-1-7000-0.psbuild) | ? {$_.diff -eq "Added"} | sort noun | % { $_ | add-member -name Synopsis -member noteproperty -value (& $_.name -?|select -expand synopsis) -passthru } | select name,synopsis | convertto-html -fragment > new-cmdlets.htm 

Name Synopsis
Invoke-Command Runs commands on local and remote computers.
Add-Computer Adds computers to a domain or workgroup.
Remove-Computer Removes computers from workgroups or domains.
Rename-Computer Renames a computer.
Restore-Computer Starts a system restore on the local computer.
Checkpoint-Computer Creates a system restore point on the local computer.
Restart-Computer Restarts ("reboots") the operating system on local and remote computers.
Stop-Computer Stops (shuts down) local and remote computers.
Reset-ComputerMachinePassword Resets the machine account password for the computer.
Disable-ComputerRestore Disables the System Restore feature on the specified file system drive.
Enable-ComputerRestore Enables the System Restore feature on the specified file system drive.
Get-ComputerRestorePoint Gets the restore points on the local computer.
Test-ComputerSecureChannel Test-ComputerSecureChannel [-Repair] [-Server <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm]
Test-Connection Sends ICMP echo request packets ("pings") to one or more computers.
Export-Counter The Export-Counter cmdlet takes PerformanceCounterSampleSet objects and exports them as counter log files.
Import-Counter Imports performance counter log files (.blg, .csv, .tsv) and creates the objects that represent each counter sample in the log.
Get-Counter Gets performance counter data from local and remote computers.
ConvertFrom-Csv Converts object properties in CSV format into CSV versions of the original objects.
ConvertTo-Csv Converts .NET objects into a series of comma-separated, variable-length (CSV) strings.
Register-EngineEvent Register-EngineEvent [-SourceIdentifier] <String> [[-Action] <ScriptBlock>] [-MessageData <PSObject>] [-SupportEvent] [-Forward] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Unregister-Event Unregister-Event [-SourceIdentifier] <String> [-Force] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm] Unregister-Event [-SubscriptionId] <Int32> [-Force] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm]
New-Event New-Event [-SourceIdentifier] <String> [[-Sender] <PSObject>] [[-EventArguments] <PSObject[]>] [[-MessageData] <PSObject>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Remove-Event Remove-Event [-SourceIdentifier] <String> [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm] Remove-Event [-EventIdentifier] <Int32> [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm]
Wait-Event Wait-Event [[-SourceIdentifier] <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Get-Event Get-Event [[-SourceIdentifier] <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Show-EventLog Displays the event logs of the local or a remote computer in Event Viewer.
New-EventLog Creates a new event log and a new event source on a local or remote computer.
Remove-EventLog Deletes an event log or unregisters an event source.
Clear-EventLog Deletes all entries from specified event logs on the local or remote computers.
Write-EventLog Writes an event to an event log.
Limit-EventLog Sets the event log properties that limit the size of the event log and the age of its entries.
Get-EventSubscriber Get-EventSubscriber [[-SourceIdentifier] <String>] [-Force] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Export-FormatData Export-FormatData [-InputObject <ExtendedTypeDefinition[]>] [-FilePath <String>] [-Force] [-NoClobber] [-IncludeScriptBlock] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Get-FormatData Get-FormatData [[-TypeName] <String[]>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Out-GridView Sends output to an interactive table in a separate window.
Clear-History Deletes entries from the command history.
Get-HotFix Gets the QFE updates that have been applied to the local and remote computers.
Stop-Job Stops a Windows PowerShell background job.
Wait-Job Suppresses the command prompt until one or all of the Windows PowerShell background jobs running in the session are complete.
Remove-Job Deletes a Windows PowerShell background job.
Start-Job Starts a Windows PowerShell background job.
Get-Job Gets Windows PowerShell background jobs that are running in the current session.
Receive-Job Gets the results of the Windows PowerShell background jobs in the current session. You can use this cmdlet to retrieve the output and errors of background jobs.
Update-List Adds and removes items from a property value that contains a collection of objects.
Import-LocalizedData Imports language-specific data into scripts and functions based on the UI culture that is selected for the operating system.
Send-MailMessage Sends an e-mail message.
Get-Module Get-Module [[-Name] <String[]>] [-All] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Get-Module [[-Name] <String[]>] [-ListAvailable] [-Recurse] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Remove-Module Remove-Module [-Name] <String[]> [-Force] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm] Remove-Module [-ModuleInfo] <PSModuleInfo[]> [-Force] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm]
New-Module New-Module [-ScriptBlock] <ScriptBlock> [-Function <String[]>] [-Cmdlet <String[]>] [-ReturnResult] [-AsCustomObject] [-ArgumentList <Object[]>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] New-Module [-Name] <String> [-ScriptBlock] <ScriptBlock> [-Function <String[]>] [-Cmdlet <String[]>] [-ReturnResult] [-AsCustomObject] [-ArgumentList <Object[]>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Import-Module Import-Module [-Name] <String[]> [-Prefix <String>] [-Function <String[]>] [-Cmdlet <String[]>] [-Variable <String[]>] [-Alias <String[]>] [-Force] [-PassThru] [-AsCustomObject] [-Version <Version>] [-ArgumentList <Object[]>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Import-Module [-Assembly] <Assembly[]> [-Prefix <String>] [-Function <String[]>] [-Cmdlet <String[]>] [-Variable <String[]>] [-Alias <String[]>] [-Force] [-PassThru] [-AsCustomObject] [-Version <Version>] [-ArgumentList <Object[]>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Import-Module [-ModuleInfo] <PSModuleInfo[]> [-Prefix <String>] [-Function <String[]>] [-Cmdlet <String[]>] [-Variable <String[]>] [-Alias <String[]>] [-Force] [-PassThru] [-AsCustomObject] [-Version <Version>] [-ArgumentList <Object[]>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Test-ModuleManifest Test-ModuleManifest [-Path] <String> [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
New-ModuleManifest New-ModuleManifest [-Path] <String> -NestedModules <String[]> [-Guid <Guid>] -Author <String> -CompanyName <String> -Copyright <String> [-ModuleToProcess <String>] [-ModuleVersion <Version>] -Description <String> [-PowerShellVersion <Version>] [-ClrVersion <Version>] [-RequiredModules <IDictionary[]>] -TypesToProcess <String[]> -FormatsToProcess <String[]> [-ScriptsToProcess <String[]>] -RequiredAssemblies <String[]> -OtherFiles <String[]> [-ExportedFunctions <String[]>] [-ExportedAliases <String[]>] [-ExportedVariables <String[]>] [-ExportedCmdlets <String[]>] [-PrivateData <Object>] [-PassThru] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] [-WhatIf] [-Confirm]
Export-ModuleMember Export-ModuleMember [[-Function] <String[]>] [-Cmdlet <String[]>] [-Variable <String[]>] [-Alias <String[]>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Register-ObjectEvent Register-ObjectEvent [-InputObject] <PSObject> [-EventName] <String> [[-SourceIdentifier] <String>] [[-Action] <ScriptBlock>] [-MessageData <PSObject>] [-SupportEvent] [-Forward] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Start-Process Starts one or more processes on the local computer.
Debug-Process Debugs one or more processes running on the local computer.
Wait-Process Waits for the processes to be stopped before accepting more input.
Enable-PSBreakpoint Enables the breakpoints in the current console.
Disable-PSBreakpoint Disables the breakpoints in the current console.
Remove-PSBreakpoint Deletes breakpoints from the current console.
Set-PSBreakpoint Sets a breakpoint on a line, command, or variable.
Get-PSBreakpoint Gets the breakpoints that are set in the current console.
Get-PSCallStack Displays the current call stack.
Remove-PSSession Closes one or more Windows PowerShell sessions (PSSessions).
Enter-PSSession Starts an interactive session with a remote computer.
Exit-PSSession Ends an interactive session with a remote computer.
Get-PSSession Gets the Windows PowerShell sessions (PSSessions) in the current session.
Export-PSSession Saves commands from another session in a script module file.
Import-PSSession Imports cmdlets, aliases, functions, and other command types from another session on a local or remote computer into the current session.
New-PSSession Creates a persistent connection to a local or remote computer.
Set-PSSessionConfiguration Set-PSSessionConfiguration [-Name] <String> [-ApplicationBase <String>] [-ThreadApartmentState <ApartmentState>] [-ThreadOptions <PSThreadOptions>] [-StartupScript <String>] [-MaximumReceivedDataSizePerCommandMB <Nullable`1>] [-MaximumReceivedObjectSizeMB <Nullable`1>] [-SecurityDescriptorSddl <String>] [-Force] [-NoServiceRestart] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Set-PSSessionConfiguration [-Name] <String> [-AssemblyName] <String> [-ConfigurationTypeName] <String> [-ApplicationBase <String>] [-ThreadApartmentState <ApartmentState>] [-ThreadOptions <PSThreadOptions>] [-StartupScript <String>] [-MaximumReceivedDataSizePerCommandMB <Nullable`1>] [-MaximumReceivedObjectSizeMB <Nullable`1>] [-SecurityDescriptorSddl <String>] [-Force] [-NoServiceRestart] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Enable-PSSessionConfiguration Enable-PSSessionConfiguration [[-Name] <String[]>] [-Force] [-SecurityDescriptorSddl <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Disable-PSSessionConfiguration Disable-PSSessionConfiguration [[-Name] <String[]>] [-Force] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Register-PSSessionConfiguration Register-PSSessionConfiguration [-Name] <String> [-ProcessorArchitecture <String>] [-ApplicationBase <String>] [-ThreadApartmentState <ApartmentState>] [-ThreadOptions <PSThreadOptions>] [-StartupScript <String>] [-MaximumReceivedDataSizePerCommandMB <Nullable`1>] [-MaximumReceivedObjectSizeMB <Nullable`1>] [-SecurityDescriptorSddl <String>] [-Force] [-NoServiceRestart] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Register-PSSessionConfiguration [-Name] <String> [-AssemblyName] <String> [-ConfigurationTypeName] <String> [-ProcessorArchitecture <String>] [-ApplicationBase <String>] [-ThreadApartmentState <ApartmentState>] [-ThreadOptions <PSThreadOptions>] [-StartupScript <String>] [-MaximumReceivedDataSizePerCommandMB <Nullable`1>] [-MaximumReceivedObjectSizeMB <Nullable`1>] [-SecurityDescriptorSddl <String>] [-Force] [-NoServiceRestart] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Unregister-PSSessionConfiguration Unregister-PSSessionConfiguration [-Name] <String> [-Force] [-NoServiceRestart] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Get-PSSessionConfiguration Get-PSSessionConfiguration [[-Name] <String[]>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Get-Random Gets a random number or selects objects randomly from a collection.
Set-StrictMode Establishes and enforces coding rules in expressions, scripts, and script blocks.
ConvertFrom-StringData Converts a string containing one or more "name=value" pairs to a hash table.
Undo-Transaction Rolls back the active transaction.
Use-Transaction Adds the script block to the active transaction.
Complete-Transaction Commits the active transaction.
Get-Transaction Gets the current (active) transaction.
Start-Transaction Starts a transaction.
Add-Type Adds a .NET type (a class) to a Windows PowerShell session.
New-WebServiceProxy Creates a Web service proxy object that lets you use and manage the Web service in Windows PowerShell.
Get-WinEvent Gets events from event logs and event tracing log files on local and remote computers. This cmdlet runs only on Windows Vista and later versions of Windows.
Register-WmiEvent Register-WmiEvent [-Class] <String> [[-SourceIdentifier] <String>] [[-Action] <ScriptBlock>] [-Namespace <String>] [-Credential <PSCredential>] [-ComputerName <String>] [-Timeout <Int64>] [-MessageData <PSObject>] [-SupportEvent] [-Forward] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Register-WmiEvent [-Query] <String> [[-SourceIdentifier] <String>] [[-Action] <ScriptBlock>] [-Namespace <String>] [-Credential <PSCredential>] [-ComputerName <String>] [-Timeout <Int64>] [-MessageData <PSObject>] [-SupportEvent] [-Forward] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Set-WmiInstance Creates or modifies instances of WMI classes.
Invoke-WmiMethod Calls WMI methods.
Remove-WmiObject Deletes WMI classes and instances.
Disconnect-WSMan Disconnect-WSMan [[-ComputerName] <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Connect-WSMan Connect-WSMan [[-ComputerName] <String>] [-ApplicationName <String>] [-Authentication <AuthenticationMechanism>] [-Credential <PSCredential>] [-OptionSet <Hashtable>] [-Port <Int32>] [-SessionOption <SessionOption>] [-UseSSL] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Connect-WSMan [-Authentication <AuthenticationMechanism>] [-ConnectionURI <Uri>] [-Credential <PSCredential>] [-OptionSet <Hashtable>] [-Port <Int32>] [-SessionOption <SessionOption>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Test-WSMan Test-WSMan [[-ComputerName] <String>] [-Authentication <AuthenticationMechanism>] [-Credential <PSCredential>] [-Port <Int32>] [-UseSSL] [-ApplicationName <String>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Invoke-WSManAction Invoke-WSManAction [-ResourceURI] <Uri> [-Action] <String> [[-SelectorSet] <Hashtable>] [-Authentication <AuthenticationMechanism>] [-ConnectionURI <Uri>] [-Credential <PSCredential>] [-FilePath <String>] [-OptionSet <Hashtable>] [-SessionOption <SessionOption>] [-ValueSet <Hashtable>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Invoke-WSManAction [-ResourceURI] <Uri> [-Action] <String> [[-SelectorSet] <Hashtable>] [-ApplicationName <String>] [-Authentication <AuthenticationMechanism>] [-ComputerName <String>] [-Credential <PSCredential>] [-FilePath <String>] [-OptionSet <Hashtable>] [-Port <Int32>] [-SessionOption <SessionOption>] [-UseSSL] [-ValueSet <Hashtable>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Get-WSManCredSSP Get-WSManCredSSP [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Enable-WSManCredSSP Enable-WSManCredSSP [-DelegateComputer] <String[]> [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Disable-WSManCredSSP Disable-WSManCredSSP [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Remove-WSManInstance Remove-WSManInstance [-ResourceURI] <Uri> [-SelectorSet] <Hashtable> [-ApplicationName <String>] [-Authentication <AuthenticationMechanism>] [-ComputerName <String>] [-Credential <PSCredential>] [-OptionSet <Hashtable>] [-Port <Int32>] [-SessionOption <SessionOption>] [-UseSSL] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Remove-WSManInstance [-ResourceURI] <Uri> [-SelectorSet] <Hashtable> [-Authentication <AuthenticationMechanism>] [-ConnectionURI <Uri>] [-Credential <PSCredential>] [-OptionSet <Hashtable>] [-SessionOption <SessionOption>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
New-WSManInstance New-WSManInstance [-ResourceURI] <Uri> [-SelectorSet] <Hashtable> [-ApplicationName <String>] [-Authentication <AuthenticationMechanism>] [-ComputerName <String>] [-Credential <PSCredential>] [-FilePath <String>] [-OptionSet <Hashtable>] [-Port <Int32>] [-SessionOption <SessionOption>] [-UseSSL] [-ValueSet <Hashtable>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] New-WSManInstance [-ResourceURI] <Uri> [-SelectorSet] <Hashtable> [-Authentication <AuthenticationMechanism>] [-ConnectionURI <Uri>] [-Credential <PSCredential>] [-FilePath <String>] [-OptionSet <Hashtable>] [-SessionOption <SessionOption>] [-ValueSet <Hashtable>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Get-WSManInstance Get-WSManInstance [-ResourceURI] <Uri> [-ApplicationName <String>] [-Authentication <AuthenticationMechanism>] [-ComputerName <String>] [-ConnectionURI <Uri>] [-Credential <PSCredential>] [-Dialect <Uri>] [-Fragment <String>] [-OptionSet <Hashtable>] [-Port <Int32>] [-SelectorSet <Hashtable>] [-SessionOption <SessionOption>] [-UseSSL] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Get-WSManInstance [-ResourceURI] <Uri> [-ApplicationName <String>] [-Authentication <AuthenticationMechanism>] [-BasePropertiesOnly] [-ComputerName <String>] [-ConnectionURI <Uri>] [-Credential <PSCredential>] [-Dialect <Uri>] -Enumerate [-Filter <String>] [-OptionSet <Hashtable>] [-Port <Int32>] [-References] [-ReturnType <String>] [-SessionOption <SessionOption>] [-Shallow] [-UseSSL] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Set-WSManInstance Set-WSManInstance [-ResourceURI] <Uri> [[-SelectorSet] <Hashtable>] [-ApplicationName <String>] [-Authentication <AuthenticationMechanism>] [-ComputerName <String>] [-Credential <PSCredential>] [-Dialect <Uri>] [-FilePath <String>] [-Fragment <String>] [-OptionSet <Hashtable>] [-Port <Int32>] [-SessionOption <SessionOption>] [-UseSSL] [-ValueSet <Hashtable>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Set-WSManInstance [-ResourceURI] <Uri> [[-SelectorSet] <Hashtable>] [-Authentication <AuthenticationMechanism>] [-ConnectionURI <Uri>] [-Credential <PSCredential>] [-Dialect <Uri>] [-FilePath <String>] [-Fragment <String>] [-OptionSet <Hashtable>] [-SessionOption <SessionOption>] [-ValueSet <Hashtable>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Set-WSManQuickConfig Set-WSManQuickConfig [-UseSSL] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
New-WSManSessionOption New-WSManSessionOption [-ProxyAccessType <ProxyAccessType>] [-ProxyAuthentication <ProxyAuthentication>] [-ProxyCredential <PSCredential>] [-SkipCACheck] [-SkipCNCheck] [-SkipRevocationCheck] [-SPNPort <Int32>] [-OperationTimeout <Int32>] [-NoEncryption] [-UseUTF16] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
Select-Xml Select-Xml [-XPath] <String> [-Path] <String[]> [-Namespace <Hashtable>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Select-Xml [-XPath] <String> [-Xml] <XmlNode[]> [-Namespace <Hashtable>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>] Select-Xml [-XPath] <String> [-Content] <String[]> [-Namespace <Hashtable>] [-Verbose] [-Debug] [-ErrorAction <ActionPreference>] [-WarningAction <ActionPreference>] [-ErrorVariable <String>] [-WarningVariable <String>] [-OutVariable <String>] [-OutBuffer <Int32>]
ConvertTo-Xml Creates an XML-based representation of an object.

Whew, that’s quite a few. Now, onto the API.

.NET API Differences

This time, I’m just dumping out the namespaces and Types per namespace. It would be a bit much to dump out all the Types themselves. The one-liner:

  1. $v1.api | group namespace | select name, count | convertto-html -fragment > .\v1-types.htm 
v1.0 RTM API

Here are the namspaces and respective public Type count for v1.0:

Name Count
Microsoft.PowerShell.Commands 196
Microsoft.PowerShell 11
Microsoft.PowerShell.Commands.Internal.Format 4
System.Management.Automation 169
System.Management.Automation.Internal 6
System.Management.Automation.Host 15
System.Management.Automation.Runspaces 33
System.Management.Automation.Provider 13

v2.0 CTP3 API

Here are the namspaces and respective public Type count for v2.0 CTP3:

Name Count
Microsoft.PowerShell.Commands 311
Microsoft.PowerShell.Commands.GetCounter 4
Microsoft.PowerShell 14
Microsoft.PowerShell.Commands.Internal.Format 4
Microsoft.WSMan.Management 40
System.Management.Automation 262
System.Management.Automation.Internal 7
System.Management.Automation.Runspaces 61
System.Management.Automation.Host 17
System.Management.Automation.Remoting 10
System.Management.Automation.Provider 14
Microsoft.PowerShell.Commands.Internal 4
Microsoft.PowerShell.Commands.Management 1

As you can see, there has been a nice expansion of namespaces and Types to work with, mostly coming from the sterling work being done to generalize the “Jobs” infrastructure. Also, a fair chunk is tied up with the refactoring and reorganizing of Runspaces to allow for jobs (which also covers eventing), remoting and background pipelines. This is something I will cover in more detail in a future post.

Have fun!

posted on Tuesday, February 03, 2009 7:18:14 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Tuesday, December 30, 2008

A module manifest file is a PowerShell data file (.psd1) with the same name as the module directory. For example, the FileTransfer module installed with CTP3 -- which contains Cmdlets for BITS -- is in a directory called $pshome\Modules\FileTransfer and contains a manifest file called $pshome\Modules\FileTransfer\FileTransfer.psd1. Module manifests are optional, but highly recommended.

A module manifest contains a hashtable declared using the @{} hashtable literal syntax. Valid keys are listed in the following table:

Key

Required

Type

Description

ModuleToProcess

Optional

String

Script module or binary module file associated with this manifest which also becomes the root module for nested modules. If no module is specified, the manifest itself becomes the root module for nested modules.

A binary module is the new name for a snap-in DLL. v1 style snapins can be loaded this way, without having to register them first with installutil.exe; A binary module does NOT need a PSSnapIn class defined.

ModuleVersion

Required

String convertible to System.Version

Version of the module

GUID

Optional

String

Unique identifier for the module which can be used to verify against the module name

Author

Optional

String

Identifies the author of the module

CompanyName

Optional

String

Identifies the company that created the module

Copyright

Optional

String

Module copyright

Description

Optional

String

Describes the contents of the module

PowerShellVersion

Optional

String convertible to System.Version

Minimum required version of the PowerShell engine

CLRVersion

Optional

String convertible to System.Version

Minimum required version of the CLR

RequiredModules

Optional

List of module names

List of modules that must already be loaded globally (note: required modules are not loaded automatically - this could optionally be done using a script in ScriptsToProcess).

RequiredAssemblies

Optional

String array

List of assemblies that will be loaded using the same algorithm as Add-Type

ScriptsToProcess

Optional

String array

Identifies the list of scripts to process when the module is imported. These scripts are dot sourced into the caller’s environment. Only .ps1 files can be specified.

TypesToProcess

Optional

String array

List of .ps1xml type files to process using Update-TypeData

FormatsToProcess

Optional

String array

List of .ps1xml format files to process using Update-FormatData

NestedModules

Optional

String array

List of .ps1, .psm1, .psd1, and .dll files to process on Import-Module. Files are processed in the order listed. DLLs are scraped for cmdlets/providers and .ps1 script files are dot sourced into the module’s session state.

ExportedFunctions

Optional

String array, wildcards supported

List of functions to export. If not defined or if asterisk is specified, all functions imported from nested modules are re‑exported. To prevent export, use the empty string ‘’.

ExportedCmdlets

Optional

String array, wildcards supported

List of cmdlets to export. If not defined or if asterisk is specified, all cmdlets imported from nested modules are re‑exported. To prevent export, use the empty string ‘’.

A big change in CTP3 is that binary Cmdlets can now be scoped – previously they were always global.

ExportedVariables

Optional

String array, wildcards supported

List of variables to export. If not defined or if asterisk is specified, all variables imported from nested modules are re‑exported. To prevent export, use the empty string ‘’.

ExportedAliases

Optional

String array, wildcards supported

List of aliases to export. If not defined or if asterisk is specified, all aliases imported from nested modules are re‑exported. To prevent export, use the empty string ‘’.

PrivateData

Optional

Object

Data to be passed to the module via the manifest file.

I’ve highlighted certain things in the description that are important to note. These are the things that might trip you up.

Have fun!

posted on Tuesday, December 30, 2008 11:48:20 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Friday, August 15, 2008

The new eventing infrastructure in PowerShell 2.0 is pretty delicious. You couldn’t do the following in 1.0 without a 3rd party snap-in (like my PSEventing snapin), but now it’s all there at the touch of your fingers. Well, it demands a bit of a sniff around WMI too, but hey, it works well.  With this module, anytime you add or remove a removable device like an external harddrive or USB key, or map a new network drive in explorer, PowerShell will now automatically add or remove a corresponding PSDrive for you.

  1. # AutoMount.psm1 v1.0  
  2. # Oisin "x0n" Grehan (MVP)  
  3.  
  4. $query = new-object System.Management.WqlEventQuery  
  5. $query.EventClassName = "__InstanceOperationEvent" 
  6.  
  7. # default to every 2 seconds  
  8. $query.WithinInterval = new-object System.TimeSpan 0,0,2  
  9.  
  10. # this WMI is only available with Windows 2003 and Vista (not XP it appears).  
  11. # this could be rewritten to use different WMI queries to support 2000/NT/XP also.  
  12. $query.QueryString = "Select * from Win32_VolumeChangeEvent" 
  13.  
  14. # attach a watcher  
  15. $watcher = new-object System.Management.ManagementEventWatcher $query 
  16.  
  17. # here we use -SupportEvent instead of -SourceIdentifier  
  18. # this prevents this event from being generally visible  
  19. # also note the use of the call operator to invoke a   
  20. # function in the scope of the module since this action  
  21. # occurs outside of module scope.  
  22. Register-ObjectEvent $watcher -EventName "EventArrived" `  
  23.     -SupportEvent "WMI.VolumeChange" -Action {  
  24.         & (get-module automount) VolumeChangeCallback @args 
  25.     }  
  26.  
  27. # New PSEvents:  
  28. #  
  29. #     PowerShell.DeviceConfigurationChanged  
  30. #     PowerShell.DeviceArrived  
  31. #     PowerShell.DeviceRemoved  
  32. #     PowerShell.DeviceDocking  
  33.  
  34. # win32_volumechangeevent event types  
  35. $eventTypes = @{  
  36.     1 = "ConfigurationChanged";  
  37.     2 = "Arrived";  
  38.     3 = "Removed";  
  39.     4 = "Docking";  
  40. }  
  41.  
  42. # private module level callback function  
  43. function VolumeChangeCallback ($sender, $eventargs) {  
  44.     trap { write-warning $_ }  
  45.  
  46.     $driveName = $eventArgs.NewEvent.DriveName.TrimEnd(":")  
  47.     $eventType = [int]$eventArgs.NewEvent.EventType # was uint16  
  48.  
  49.     $forwardedEvent = "Device$($eventTypes[$eventType])" 
  50.       
  51.     # forward a new simpler event specific to device event type  
  52.     [void]( New-PSEvent "PowerShell.$forwardedEvent" -Sender $driveName `  
  53.         -EventArguments $eventargs )  
  54. }  
  55.  
  56. # hook up our psdrive mount / unmount events  
  57. # and start the WMI watcher  
  58. function Enable-AutoMount {  
  59.  
  60.     Register-PSEvent -SourceIdentifier "PowerShell.DeviceArrived" `  
  61.         -Action {              
  62.             new-psdrive -name $args[0] -psprovider `  
  63.                 filesystem -root "$args[0]:";  
  64.          }  
  65.  
  66.     Register-PSEvent -SourceIdentifier "PowerShell.DeviceRemoved" `  
  67.         -Action {  
  68.             remove-psdrive -name $args[0] -ea 0; # may not exist  
  69.         }  
  70.       
  71.     $watcher.Start()  
  72. }  
  73.  
  74. # tear down our psdrive mount / unmount events  
  75. # and stop the WMI watcher  
  76. function Disable-AutoMount {  
  77.  
  78.     Unregister-PSEvent -SourceIdentifier "PowerShell.DeviceArrived" 
  79.     Unregister-PSEvent -SourceIdentifier "PowerShell.DeviceRemoved" 
  80.       
  81.     $watcher.Stop()  
  82. }  
  83.  
  84. # export functions to control automount  
  85. Export-ModuleMember Enable-AutoMount, Disable-AutoMount  
  86.  
  87. # start watching and (un)mounting  
  88. Enable-AutoMount 

This only works PowerShell v2.0 CTP2, and you’ll need to save it as AutoMount.psm1 in a directory under your documents folder like so (vista example):

%userprofile%\documents\windowspowershell\packages\automount\automount.psm1

You can then load it with the command:

ps> add-module automount

I have this in my profile.  You can temporarily disable automount with the function Disable-AutoMount and reenable it at anytime with Enable-AutoMount. The module also exposes four new events for you to consume yourself. You could, for example, hook your own script to run anytime a device is added and/or removed. This is what I do myself in the module. I hook a WMI event once then forward 1 of 4 possible new events depending on the type of WMI event that was raised.

NOTE: this particular flavour of WMI query only works in Vista and Windows 2003 it appears. I’m looking into getting it working with 2000/XP also.

Have fun!

posted on Friday, August 15, 2008 10:14:47 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0] Trackback