# Friday, March 05, 2010

Paths in PowerShell are tough to understand [at first.] PowerShell Paths - or PSPaths, not to be confused with Win32 paths - in their absolute forms, come in two distinct flavours:

  • Provider-qualified: FileSystem::c:\temp\foo.txt
  • Drive-qualified: c:\temp\foo.txt

It's very easy to get confused over provider-internal (The ProviderPath property of a resolved PathInfo – and the bold portion of the provider-qualified path above) and drive-qualified paths since they look the same if you look at the default FileSystem provider drives. That is to say, the PSDrive has the same name (C) as the native backing store, the windows filesystem (C). So, to make it easier for yourself to understand the differences, create yourself a new PSDrive:

ps c:\> new-psdrive temp filesystem c:\temp\
ps c:\> cd temp:
ps temp:\>

Now, let's look at this again:

  • Provider-qualified: FileSystem::c:\temp\foo.txt
  • Drive-qualified: temp:\foo.txt

A bit easier this time to see what’s different this time. The bold text to the right of the provider name is the ProviderPath.

So, your goals for writing a generalized provider-friendly Cmdlet (or advanced function) that accepts paths are:

  • Define a LiteralPath path parameter aliased to PSPath
  • Define a Path parameter (which will resolve wildcards / glob)
  • Assume you are receiving PSPaths, NOT native provider-paths

Point number three is especially important. Also, obviously LiteralPath and Path should belong in mutually exclusive parameter sets.

Relative Paths

A good question is: how do we deal with relative paths being passed to a Cmdlet. As you should assume all paths being given to you are PSPaths,  let’s look at what the Cmdlet below does:

ps temp:\> write-zip -literalpath foo.txt

The command should assume foo.txt is in the current drive, so this should be resolved immediately in the ProcessRecord or EndProcessing block like (using the scripting API here to demo):

$provider = $null;
$drive = $null
$providerPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath("foo.txt", [ref]$provider, [ref]$drive)

Now you everything you need to recreate the two absolute forms of PSPaths, and you also have the native absolute ProviderPath. To create a provider-qualified PSPath for foo.txt, use $provider.Name + “::” + $providerPath. If $drive is not null (your current location might be provider-qualified in which case $drive will be null) then you should use $drive.name + ":\" + $drive.CurrentLocation + "\" + "foo.txt" to get a drive-qualified PSPath.

Have fun!

posted on Friday, March 05, 2010 1:02:31 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Wednesday, February 24, 2010

PowerShell guru and Admin-extraordinare Jeff Hicks asked a great question that many a Windows administrator has probably asked themselves when working with Hashtables in PowerShell: Why do the key/values come out in a different order than put in? This is a great question and the answer lies in computer science theory, particularly computational complexity theory. Rather than bore you with a ton of nonsense about O(1), O(n) and other propeller-head dribble, I figure I could explain it in terms everyone should be able to understand.

Hashtables, Buckets and HashCodes = Rolodex, Index Cards and Surnamescard-index

Yes, if the light hasn’t gone on yet, it will soon. Every .NET object includes a method called GetHashCode. This method returns a number that represents the identity of the object in a kind of fuzzy way. I say “fuzzy” because the hash code for a given instance of an object can be different on different platforms (xp, vista, 2003 etc) or on different bitness (64 vs 32 bit.) This method is used by hashtable to get the “surname” of an object. Instead of Index Cards, a Hashtable uses “buckets” to separate groups of objects. Any given Hashcode will naturally fall into a particular bucket as the function (result) of a high-speed optimized algorithm, much like any given surname naturally falls under a particular letter of the alphabet. Finally, it should be clear to you that using index cards [Hashtable buckets] is way faster than flicking through an unsorted folder [randomized list.]

Just like a Rolodex, the order you add names to it doesn’t dictate the order they are in as you flip through the cards sequentially, which is analogous to sending a Hashtable to out-default.

posted on Wednesday, February 24, 2010 5:38:55 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Sunday, January 17, 2010

Did you know that when you run Get-Help against a cmdlet to find out about its parameters, you might not be getting the whole truth? Certain cmdlets, especially those that operate on providers (FileSystem, Certificate, Registry etc) can adopt new parameters on the fly, depending on the path they are acting on. For example, when use you Get-Content on the file system (drive c: etc), it gets three new parameters in addition to the static ones listed by Get-Help (but more about this later): Delimiter, Encoding and Wait.

Determining Dynamic Parameters using Get-Help

Get-Help has a new parameter, –Path, which lets you give the help system some context for determining dynamic parameters:

-Path <string>
    Gets help that explains how the cmdlet works in the specified provider path. Enter a Windows PowerShell provider path.

    This parameter gets a customized version of a cmdlet help topic that explains how the cmdlet works in the specified Windows PowerShell provider path. This parameter is effective only for help about a provider cmdlet and only when the provider includes a custom version of the provider cmdlet help topic.

    To see the custom cmdlet help for a provider path, go to the provider path location and enter a Get-Help command or, from any path location, use the Path parameter of Get-Help to specify the provider path. For more information, see about_Providers.

Determining Dynamic Parameters using Get-Command

Get-Command has a new parameter, –ArgumentList, which acts similarly to unveil what dynamic parameters might be attached to a cmdlet for a given parameterset and path/literalpath if available on the chosen cmdlet. I’ve written a simple function that takes a cmdlet name as an argument and will display all of the dynamic parameters available for a cmdlet for each distinct provider:

# this function will pass a drive name in position 0 as an unnamed argument
# most path-oriented cmdlets accept this binding
function Get-DynamicParameter {
    param(        
        [string]$command
    ) 
  
    $parameters = @{}
    get-psdrive | sort -unique provider | % {
        $parameters[$_.provider.name] = gcm $command -args "$($_.name):" | % {
            $c = $_; @($_.parameters.keys) | sort | ? {
                $c.parameters[$_].isdynamic
            }
        }
    }
    $parameters    
}

Example use:

PS> Get-DynamicParameter get-content

Name                           Value
----                           -----
Alias
FileSystem                     {Delimiter, Encoding, Wait}
AssemblyCache
Registry
Environment
WSMan
Certificate
FeedStore
Function
Variable
PscxSettings

NOTE: when you don’t pass any context parameters to get-command via –argumentlist, it will take your current location as the context for dynamic parameters (if any are found.) So running get-command from c:\ instead hklm:\ will give you the additional parameters Delimiter, Encoding and Wait.

Have fun!

posted on Sunday, January 17, 2010 9:33:04 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
# Friday, October 30, 2009

UPDATE: Some general information on remoting: Hey, Scripting Guy! Tell Me About Remoting in Windows PowerShell 2.0
UPDATE 2:
This also applies to machines not attached to a domain (thanks @alexandair)

If you’re running Windows 7 (and if not, why not?) you may have noticed that premium versions include a license for Virtual XP Mode. (read more at http://www.microsoft.com/windows/virtual-pc/download.aspx) Essentially it’s an integrated version of Virtual PC with a full copy of Windows XP SP2 running in it. It’s pretty nice – you can have programs from the virtualized XP instance in your Windows 7 Start Menu and they can even share your desktop.

Problem

First thing you might think as a PowerShell fan is to put PowerShell v2.0 on Virtual XP Mode so you can tinker around with PowerShell Remoting (don’t forget to install .NET Framework 3.5 SP1 first!) Unfortunately it’s not all plain sailing as a default security configuration in Virtual XP prevents the Enable-PSRemoting Cmdlet from succeeding:

PS C:\Documents and Settings\XPMUser> Enable-PSRemoting

WinRM Quick Configuration
Running command "Set-WSManQuickConfig" to enable this machine for remote management through WinRM service.
 This includes:
    1. Starting or restarting (if already started) the WinRM service
    2. Setting the WinRM service type to auto start
    3. Creating a listener to accept requests on any IP address
    4. Enabling firewall exception for WS-Management traffic (for http only).

Do you want to continue?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
WinRM has been updated to receive requests.
WinRM service type changed successfully.
WinRM service started.

Set-WSManQuickConfig : Access is denied. 
At line:50 char:33
+             Set-WSManQuickConfig <<<<  -force
    + CategoryInfo          : InvalidOperation: (:) [Set-WSManQuickConfig], InvalidOperationException
    + FullyQualifiedErrorId : WsManError,Microsoft.WSMan.Management.SetWSManQuickConfigCommand

Resolution

Thankfully, it’s a quick fix; log into VXP and perform Start > Run… “gpedit.msc” and navigate your way to:

Computer Configuration > Windows Settings > Security Settings > Local Policies > Security Options

…and change: Network access: Sharing and security model for local accounts

…to: Classic – local users authenticate as themselves.

Now run Enable-PSRemoting again and it should work. No need to reboot!

fix_gpo_security_network_access

posted on Friday, October 30, 2009 12:46:44 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback