# Monday, December 15, 2008

The dynamic duo / masterminds of PowerShell will be cornered and fiercely grilled by none other than our very own master podcaster, Hal Rottenberg this Thursday.  From the mouth of the suffixed one himself:

Coming up on the PowerScripting Live show this Thursday will be Jeffrey Snover,
the architect for PowerShell as I’m sure you all know, and he’ll be accompanied
by none other than Bruce Payette, author of PowerShell in Action and a core
developer on the PowerShell team.

We’re excited and we hope you can make it this Thursday at 9pm EST!

The live stream address is http://www.ustream.tv/channel/powerscripting-podcast

So if you want to get the lowdown on CTP3 (maybe), join us, the unwashed masses as we clamour to be near our idols. A lock of their hair and a signed discarded printout of directions to building 18 could be yours!

I made that last bit up. Who cares! This is going to be cool! Join us!

posted on Monday, December 15, 2008 4:14:40 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Sunday, December 14, 2008

Now this is cool. I’ve always wanted to try out TDD, but as I’m primarily a SharePoint developer, a lot of the time my code is written on my laptop running XP, so I can’t actually test anything since SharePoint won’t install there. The folks at Bamboo Solutions came up with some clever hacks to get WSS 3.0 and MOSS running on Vista, but it’s a bit of a heavyweight solution, especially when you consider this:

Typemock are offering their new product for unit testing SharePoint called Isolator For SharePoint, for a special introduction price. it is the only tool that allows you to unit test SharePoint without a SharePoint server. To learn more click here.

The first 50 bloggers who blog this text in their blog and tell us about it, will get a Full Isolator license, Free. for rules and info click here.

Unfortunately the competition is actually over, but it’s still worth a peek!

posted on Sunday, December 14, 2008 6:58:36 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Tuesday, December 02, 2008

Normally parameters for a Cmdlet are defined directly as properties on the Cmdlet class itself. There are a few other ways for a Cmdlet to define parameters, and in this short series of three parts, I’ll cover the three variants you’ll likely to encounter as you become a more experienced PowerShell developer. First thing you need to do is to implement the System.Management.Automation.IDynamicParameters interface on your Cmdlet. This interface has one member, GetDynamicParameters. This interface can be implemented on either a Provider or a Cmdlet. In the former case, a provider is able to add new parameters at runtime to certain built-in Cmdlets like Get-ChildItem, Get-Item & Get-ItemProperty. It is typically used in a path context-aware manner – e.g. depending on whether the current argument is a folder or item, and what Type such a folder or item might be. We’re just to cover the Cmdlet variant in this post.

image

My example is just a simple Cmdlet named Get-OSVersion. It writes out Environment.OSVersion to the pipeline, and will dynamically add a parameter. It adds –EnsureElevated if you’re running Vista or higher, otherwise it will add –EnsureAdministrator. Statically defined dynamic parameter sets are defined by creating a simple class and decorating that class with parameter/properties you want to surface as if that class was the Cmdlet itself, then returning it from the GetDynamicProperties method at runtime. Simple!

  1. [Cmdlet(VerbsCommon.Get, "OSVersion")]   
  2. public class GetOSVersionCommand : PSCmdlet, IDynamicParameters   
  3. {   
  4.     private const string SWITCH_VISTA = "EnsureElevated";   
  5.     private const string SWITCH_WINXP = "EnsureAdministrator";   
  6.   
  7.     protected override void EndProcessing()   
  8.     {   
  9.         WriteObject(Environment.OSVersion);   
  10.            
  11.         string switchName = IsVistaOrHigher() ?   
  12.             SWITCH_VISTA : SWITCH_WINXP;   
  13.   
  14.         // not really a warning, just nice yellow text.   
  15.         WriteWarning(String.Format("{0}.IsPresent {1}",   
  16.             switchName, IsSwitchPresent(switchName)));   
  17.     }   
  18.   
  19.     // IDynamicParameters.GetDynamicParameters   
  20.     public object GetDynamicParameters()   
  21.     {   
  22.         if (IsVistaOrHigher())   
  23.         {   
  24.             return new VistaParameters();   
  25.         }   
  26.         else  
  27.         {   
  28.             return new WinXPParameters();   
  29.         }   
  30.     }   
  31.        
  32.     private bool IsSwitchPresent(string name)   
  33.     {   
  34.         // parameters bound at runtime   
  35.         Dictionary<stringobject> parameters = MyInvocation.BoundParameters;   
  36.   
  37.         // determine whether switch is set   
  38.         if (parameters.ContainsKey(name))   
  39.         {   
  40.             return ((SwitchParameter) parameters[name]).IsPresent;   
  41.         }   
  42.         return false;   
  43.     }   
  44.   
  45.     private static bool IsVistaOrHigher()   
  46.     {   
  47.         return (Environment.OSVersion.Version.Major >= 6);   
  48.     }   
  49.   
  50.     // dynamic parameters for Vista   
  51.     internal class VistaParameters   
  52.     {   
  53.         [Parameter]   
  54.         public SwitchParameter EnsureElevated   
  55.         {   
  56.             getset;   
  57.         }   
  58.     }   
  59.   
  60.     // dynamic parameters for Windows XP   
  61.     // (powershell won't run on windows 2000 ;-))   
  62.     internal class WinXPParameters   
  63.     {   
  64.         [Parameter]   
  65.         public SwitchParameter EnsureAdministrator   
  66.         {   
  67.             getset;   
  68.         }   
  69.     }   
  70. }  

Of course, adding Dynamic Parameters is no good if you don’t know how to read them back. In the code, you can see I’m using MyInvocation.BoundParameters to check if the Switches have been set.

posted on Tuesday, December 02, 2008 5:21:10 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Wednesday, November 19, 2008

It seems lots of people are catching the PowerShell bug these days. People who previously considered themselves as administrators have learned enough of the magic from using PowerShell from day to day over the last few months that they feel comfortable enough to tinker around with C#.

This is an example Cmdlet skeleton that is designed to work with Files and Directories. It looks after resolving wildcards, supports –WhatIf and –Confirm, accepting files and/or directories from the pipeline or accepting strings of paths from the pipeine. It also ensures that the only types of paths it can accept lie on the File System. Here’s an example of the invocation styles it supports:

cmdlet

Here’s the source code for this. It doesn’t really do a lot. It spits out two different types of custom objects: one for a file, and another for a directory. Feel free to do what you like with it; it’s yours. Have fun.

using System;
using System.Collections.Generic;
using System.IO;
using System.Management.Automation;
using Microsoft.PowerShell.Commands;
namespace PSQuickStart
{
    [Cmdlet(VerbsCommon.Get, Noun,
        DefaultParameterSetName = ParmamSetPath,
        SupportsShouldProcess = true)
    ]
    public class GetFileMetadataCommand : PSCmdlet
    {
        private const string Noun = "FileMetadata";
        private const string ParamSetLiteral = "Literal";
        private const string ParmamSetPath = "Path";
        private string[] _paths;
        private bool _shouldExpandWildcards;
        [Parameter(
            Position = 0,
            Mandatory = true,
            ValueFromPipeline = false,
            ValueFromPipelineByPropertyName = true,
            ParameterSetName = ParamSetLiteral)
        ]
        [Alias("PSPath")]
        [ValidateNotNullOrEmpty]
        public string[] LiteralPath
        {
            get { return _paths; }
            set { _paths = value; }
        }
        [Parameter(
            Position = 0,
            Mandatory = true,
            ValueFromPipeline = true,
            ValueFromPipelineByPropertyName = true,
            ParameterSetName = ParmamSetPath)
        ]
        [ValidateNotNullOrEmpty]
        public string[] Path
        {
            get { return _paths; }
            set
            {
                _shouldExpandWildcards = true;
                _paths = value;
            }
        }
        protected override void ProcessRecord()
        {
            foreach (string path in _paths)
            {
                // This will hold information about the provider containing
                // the items that this path string might resolve to.                
                ProviderInfo provider;
                // This will be used by the method that processes literal paths
                PSDriveInfo drive;
                // this contains the paths to process for this iteration of the
                // loop to resolve and optionally expand wildcards.
                List<string> filePaths = new List<string>();
                if (_shouldExpandWildcards)
                {
                    // Turn *.txt into foo.txt,foo2.txt etc.
                    // if path is just "foo.txt," it will return unchanged.
                    filePaths.AddRange(this.GetResolvedProviderPathFromPSPath(path, out provider));
                }
                else
                {
                    // no wildcards, so don't try to expand any * or ? symbols.                    
                    filePaths.Add(this.SessionState.Path.GetUnresolvedProviderPathFromPSPath(
                        path, out provider, out drive));
                }
                // ensure that this path (or set of paths after wildcard expansion)
                // is on the filesystem. A wildcard can never expand to span multiple
                // providers.
                if (IsFileSystemPath(provider, path) == false)
                {
                    // no, so skip to next path in _paths.
                    continue;
                }
                // at this point, we have a list of paths on the filesystem.
                foreach (string filePath in filePaths)
                {
                    PSObject custom;
                    // If -whatif was supplied, do not perform the actions
                    // inside this "if" statement; only show the message.
                    //
                    // This block also supports the -confirm switch, where
                    // you will be asked if you want to perform the action
                    // "get metadata" on target: foo.txt
                    if (ShouldProcess(filePath, "Get Metadata"))
                    {
                        if (Directory.Exists(filePath))
                        {
                            custom = GetDirectoryCustomObject(new DirectoryInfo(filePath));
                        }
                        else
                        {
                            custom = GetFileCustomObject(new FileInfo(filePath));
                        }
                        WriteObject(custom);
                    }
                }
            }
        }
        private PSObject GetFileCustomObject(FileInfo file)
        {
            // this message will be shown if the -verbose switch is given
            WriteVerbose("GetFileCustomObject " + file);
            // create a custom object with a few properties
            PSObject custom = new PSObject();
            custom.Properties.Add(new PSNoteProperty("Size", file.Length));
            custom.Properties.Add(new PSNoteProperty("Name", file.Name));
            custom.Properties.Add(new PSNoteProperty("Extension", file.Extension));
            return custom;
        }
        private PSObject GetDirectoryCustomObject(DirectoryInfo dir)
        {
            // this message will be shown if the -verbose switch is given
            WriteVerbose("GetDirectoryCustomObject " + dir);
            // create a custom object with a few properties
            PSObject custom = new PSObject();
            int files = dir.GetFiles().Length;
            int subdirs = dir.GetDirectories().Length;
            custom.Properties.Add(new PSNoteProperty("Files", files));
            custom.Properties.Add(new PSNoteProperty("Subdirectories", subdirs));
            custom.Properties.Add(new PSNoteProperty("Name", dir.Name));
            return custom;
        }
        private bool IsFileSystemPath(ProviderInfo provider, string path)
        {
            bool isFileSystem = true;
            // check that this provider is the filesystem
            if (provider.ImplementingType != typeof(FileSystemProvider))
            {
                // create a .NET exception wrapping our error text
                ArgumentException ex = new ArgumentException(path +
                    " does not resolve to a path on the FileSystem provider.");
                // wrap this in a powershell errorrecord
                ErrorRecord error = new ErrorRecord(ex, "InvalidProvider",
                    ErrorCategory.InvalidArgument, path);
                // write a non-terminating error to pipeline
                this.WriteError(error);
                // tell our caller that the item was not on the filesystem
                isFileSystem = false;
            }
            return isFileSystem;
        }
    }
}
posted on Wednesday, November 19, 2008 5:58:18 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback
# Thursday, November 13, 2008

Here’s just a quick rundown of the changes in PowerShell 2.0 in comparison to the last public release, CTP2. This may be helpful to those of you who don’t have access to the Windows 7 PDC bits. Remember, things are still subject to change - it’s not in beta yet.

Unchanged cmdlets are not listed here.

Added (or renamed*) Cmdlets

Enter-PSSession
Remove-PSSession
Get-PSSession
Exit-PSSession
Get-PSSessionConfiguration
Unregister-PSSessionConfiguration
Register-PSSessionConfiguration
Debug-Process
Test-ModuleManifest
New-ModuleManifest
New-WSManSessionOption
New-PSSession
Import-PSSession
Export-PSSession
Set-PSSessionConfiguration
Get-WSManInstance
Get-WSManCredSSP
Enable-WSManCredSSP
Set-WSManInstance
Set-WSManQuickConfig
New-WSManInstance
Remove-WSManInstance
Ping-WSMan
New-WebServiceProxy
Get-PSTransaction
Connect-WSMan
Disable-WSManCredSSP
Invoke-WSManAction
Disconnect-WSMan
Get-ComputerRestorePoint
Disable-ComputerRestore
Enable-ComputerRestore
Get-Counter
Clear-EventLog
Export-Counter
Import-Counter
Checkpoint-Computer
Restart-Computer
Stop-Computer
Ping-Computer
Resume-BitsTransfer
Set-BitsTransfer
Suspend-BitsTransfer
Add-BitsFile
Clear-BitsTransfer
Remove-EventLog
Restore-Computer
New-Module
Get-HotFix
Complete-BitsTransfer
Write-EventLog
Show-EventLog
Limit-EventLog
Get-BitsTransfer
New-BitsTransfer
New-EventLog

Changed Cmdlets (parameter or parameterset differences)

Invoke-Command
Receive-PSJob
Set-WmiInstance
Stop-PSJob
Wait-PSJob
Remove-PSJob
Export-ModuleMember
Start-PSJob
Get-PSJob
Add-Module
Set-Service

   

Removed (or renamed*) Cmdlets

Pop-Runspace
New-Runspace
Push-Runspace
Get-Runspace
Remove-Runspace

   

* e.g. The *-Runspace Cmdlets have been renamed to *-PSSession

posted on Thursday, November 13, 2008 11:15:00 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0] Trackback