DCSIMG
Shay Levy

Shay Levy

If you repeat it, PowerShell it!

What is my Exchange server version

I’ve been asking myself the same question lately. Unfortunately the information is not always available in the product’s title or in Add/Remove programs. The only piece of available information is the product build version (either in About dialog or in the EMS using Get-ExchangeServer), now you need look it up in your favorite search engine and try to find its SP or cumulative patches level. What a pain, why?!

To save the frustration and confusion I’ve put together a quick and dirty function, give it a build number and hopefully you’ll get back the information. It may not contain the full list but it’s better than nothing. The information is based on the following links:

Exchange Server Build Numbers and Release Dates
Build numbers and release dates for Exchange Server
Exchange Server 2007: Platforms, Editions, and Versions

 

function Get-ExchangeVersion
{
  param(
    [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
    [Alias('bn')]
    [string]$BuildNumber
  )

  switch($BuildNumber)
  {
    '4.0.837'	{'Exchange Server 4.0'; break}
    '4.0.993'	{'Exchange Server 4.0 (a)'; break}
    '4.0.838'	{'Exchange Server 4.0 SP1'; break}
    '4.0.993'	{'Exchange Server 4.0 SP2'; break}
    '4.0.994'	{'Exchange Server 4.0 SP3'; break}
    '4.0.995'	{'Exchange Server 4.0 SP4'; break}
    '4.0.996'	{'Exchange Server 4.0 SP5'; break}
    '5.0.1457'	{'Exchange Server 5.0'; break}
    '5.0.1458'	{'Exchange Server 5.0 SP1'; break}		
    '5.0.1460'	{'Exchange Server 5.0 SP2'; break}		
    '5.5.1960'	{'Exchange Server 5.5'; break}
    '5.5.2232'	{'Exchange Server 5.5 SP1'; break}
    '5.5.2448'	{'Exchange Server 5.5 SP2'; break}
    '5.5.2650'	{'Exchange Server 5.5 SP3'; break}
    '5.5.2653'	{'Exchange Server 5.5 SP4'; break}
    '6.0.4417'	{'Exchange 2000 Server (a)'; break}
    '6.0.4712'	{'Exchange 2000 Server SP1'; break}
    '6.0.5762'	{'Exchange 2000 Server SP2'; break}
    '6.0.6249'	{'Exchange 2000 Server SP3'; break}
    '6.0.6487'	{'Exchange 2000 Server post-SP3'; break}
    '6.0.6556'	{'Exchange 2000 Server post-SP3'; break}
    '6.0.6603'	{'Exchange 2000 Server post-SP3'; break}
    '6.0.6620.5'{'Exchange 2000 Server post-SP3'; break}
    '6.0.6620.7'{'Exchange 2000 Server post-SP3'; break}
    '6.5.6944'	{'Exchange Server 2003'; break}
    '6.5.7226'	{'Exchange Server 2003 SP1'; break}
    '6.5.7638'	{'Exchange Server 2003 SP2'; break}
    '6.5.7653.33'{'Exchange Server 2003 post-SP2'; break}
    '6.5.7654.4'{'Exchange Server 2003 post-SP2'; break}
    '8.0.685.24'{'Exchange Server 2007'; break}
    '8.0.685.25'{'Exchange Server 2007 RTM'; break}
    '8.0.708.3'	{'Update Rollup 1 for Exchange Server 2007'; break}
    '8.0.711.2'	{'Update Rollup 2 for Exchange Server 2007'; break}
    '8.0.730.1'	{'Update Rollup 3 for Exchange Server 2007'; break}
    '8.0.744.0'	{'Update Rollup 4 for Exchange Server 2007'; break}
    '8.0.754.0'	{'Update Rollup 5 for Exchange Server 2007'; break}
    '8.0.783.2'	{'Update Rollup 6 for Exchange Server 2007'; break}
    '8.0.813.0'	{'Update Rollup 7 for Exchange Server 2007'; break}	
    '8.1.240.6'	{'Microsoft Exchange Server 2007 SP1'; break}
    '8.1.263.1'	{'Update Rollup 1 for Exchange Server 2007 SP1'; break}
    '8.1.278.2' {'Update Rollup 2 for Exchange Server 2007 SP1'; break}
    '8.1.291.2' {'Update Rollup 3 for Exchange Server 2007 SP1'; break}
    '8.1.311.3' {'Update Rollup 4 for Exchange Server 2007 SP1'; break}
    '8.1.336.1' {'Update Rollup 5 for Exchange Server 2007 SP1'; break}
    '8.1.340.1' {'Update Rollup 6 for Exchange Server 2007 SP1'; break}
    '8.1.359.2' {'Update Rollup 7 for Exchange Server 2007 SP1'; break}
    '8.1.375.2' {'Update Rollup 8 for Exchange Server 2007 SP1'; break}
    '8.1.393.1' {'Update Rollup 9 for Exchange Server 2007 SP1'; break}
    '8.2.176.2'	{'Exchange Server 2007 SP2'; break}
    '8.3.83.6'	{'Exchange Server 2007 SP3'; break}
    '14.0.639.21'{'Exchange Server 2010 RTM'; break}
    '14.1.218.15'{'Exchange Server 2010 SP1'; break}
    '14.2.247.5' {'Exchange Server 2010 Service Pack 2'; break}
    '14.1.255.2' {'Update Rollup 1 for Exchange Server 2010 SP1)'; break}
    '14.1.270.1'{'Update Rollup 2 for Exchange Server 2010 SP1'; break}
    '14.1.289.7'{'Update Rollup 3 for Exchange Server 2010 SP1'; break}
    '14.1.323.6'{'Update Rollup 4 for Exchange Server 2010 SP1'; break}
    '14.1.339.1'{'Update Rollup 5 for Exchange Server 2010 SP1'; break}
    '14.1.355.2'{'Update Rollup 6 for Exchange Server 2010 SP1'; break}
    '14.2.283.3'{'Update Rollup 1 for Exchange Server 2010 SP2'; break}    
    '14.2.298.4'{'Update Rollup 2 for Exchange Server 2010 SP2'; break}
    default	{'Unknown'}
  }
}

# example
PS> Get-ExchangeVersion -BuildNumber 14.1.218.15 Exchange Server 2010 SP1

Deprecation of cmdlets

Until PowerShell 3.0, if you shipped a cmdlet there was no good way to deprecate it. Cmdlet developers can use the .NET ObsoleteAttribute now to let users know that they should not use a cmdlet anymore. In the following example, the cmdlet can run but it will issue a warning that another cmdlet should be used from now on. This gives the user time to prepare for the change and fix any scripts dependent on old commands.

$code = @'
using System;
using System.Management.Automation;
namespace ObsoleteCmdlet
{
    [Cmdlet("Get","Foo")]
    [Obsolete("This cmdlet is obsolete. Please use Get-Bar instead.")]
    public class ObsoleteCmdlet : PSCmdlet
    {
        protected override void ProcessRecord()
        {
            WriteObject("Hello");
        }
    }
}
'@
#compile this code into the ObsoleteCmdlet.dll PS>
Add-Type -TypeDefinition $code -OutputAssembly $env:TEMP\ObsoleteCmdlet.dll # Import the module and run our cmdlet PS> Import-Module $env:TEMP\ObsoleteCmdlet.dll PS> Get-Foo WARNING: This cmdlet is obsolete. Please use Get-Bar instead. Hello

 

When you ship the next version of your cmdlet you can change the behavior of the Obsolete attribute, by using another constructor, and throw an error instead of a warning:

# [Obsolete("This cmdlet is obsolete. Please use Get-Bar instead.",true)]
PS> Get-Foo
This cmdlet is obsolete. Please use Get-Bar instead.
At line:1 char:1
+ get-foo
+ ~~~~~~~~
   + CategoryInfo          : InvalidOperation: (Get-Foo:String) [], RuntimeException
   + FullyQualifiedErrorId : UseOfDeprecatedCmdlet

It would be great to have this attribute in advanced functions and even on parameters. I logged a suggestion on connect about this, vote it up if you like it.

Creating objects with a cast in PowerShell 3.0

Starting with PowerShell 3.0, we now have another cool way to create new objects. We can create them by calling the default constructor of the type and initializing its properties with a cast:

[System.Drawing.Point]@{X=1;Y=2}

We put the type in square brackets and assign it a hash table containing the properties we want to initialize. Looking at this example got me thinking, how can I know the properties I can set for a specific class/type? As a learning tool, I wrote the Get-HashType function. Give it a type name and it will tell you the properties you can set.


#requires -Version 3.0
function Get-TypeHash
{
   [CmdletBinding()]
   [OutputType('System.String')]
	
   Param(
      [Parameter(
         Mandatory=$true,
         Position=0,
         ValueFromPipeline=$true,
         ValueFromPipelineByPropertyName=$true)]
      [Alias('FullName')]
      [Type]$TypeName
   )

   process
   {
      try
      {  
         if($TypeName.IsClass -and !$TypeName.GetConstructor([Type]::EmptyTypes))
         {
            throw "Constructor not found. Cannot find an appropriate `
                        constructor for type '$TypeFullName'"
         }


         $TypeFullName = $TypeName.FullName
         $WriteableProperties = $TypeName.GetProperties() | where CanWrite

         if($WriteableProperties.Count -gt 0)
         {         
            $hash = "[$TypeFullName]@{"

            $WriteableProperties | ForEach-Object {
               $hash+="`n`t{0} = <{1}>" -f $_.Name,$_.PropertyType
            }
         
            $hash+"`n}" 
         }
         else
         {
            Write-Warning "No writable properties found for type '$TypeFullName'"
         }    
      }
      catch
      {
         Write-Error $_
      }
   }
}


PS> Add-Type –Assembly System.Drawing
PS> Get-TypeHash –TypeName System.Drawing.Point [System.Drawing.Point]@{ X = <System.Int32> Y = <System.Int32> }

The output includes the writeable properties of the type and their corresponding value type. Copy the output, assign the values and paste it back to the console. This will create the object:

PS> [System.Drawing.Point]@{
>> X = 1
>> Y = 1
>> }
>>
 IsEmpty   X   Y
 -------   -   -
   False   1   1

The function works for types that have a default constructor and for structure types. In the example above, System.Drawing.Point is a structure.

How to protect your PowerShell Functions

Creating functions in PowerShell is relatively a simple operation. You declare the function’s name and value, press the Enter key and you’re good to go. It is also very simple to override the function just by re-creating it; that’s all it takes, simple and easy.

What if you need to prevent that from happening? You don’t want to allow someone or something from overriding your functions?

PowerShell gives us the option to do that via a dynamic parameter. Dynamic parameters are cmdlet parameters that are added by a Windows PowerShell provider and are available only when the cmdlet is being used in the provider-enabled drive. The Options dynamic parameter is supported by New-Item and Set-Item when you use them with the Alias,Variable or Function providers.

The Options parameter <System.Management.Automation.ScopedItemOptions> possible values:

  • None - No options. "None" is the default.
  • Private - The function is visible only in the current scope (not in child scopes).
  • ReadOnly - The properties of the function cannot be changed except by using the Force parameter. You can use Remove-Item to delete the function.
  • Constant - The function cannot be deleted, and its properties cannot be changed. Constant is available only when you are creating a function. You cannot change the option of an existing function to Constant.
  • AllScope - The function is copied to any new scopes that are created.


Let’s see how we can protect a function, we’ll start by marking it Read only.

# create a function

PS> function test { "I'm a read-only function" }

 

# set it as readonly

PS> Set-Item –Path Function:test –Options ReadOnly

 

# try to update the function definition

PS> function test { "new value" }

 

Cannot write to function test because it is read-only or constant.

At line:8 char:1

+ function test { "new value" }

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : WriteError: (test:String) [], SessionStateUnauthorizedAccessException

    + FullyQualifiedErrorId : FunctionNotWritable

 

We get an error when we try to update the function. Let’s try to override it with the Force switch:

PS> Set-Item –Path Function:test -Value { "new value" } -Force
PS>
test

new value

This time we don’t get the error and the function has been updated. If ReadOnly is not enough and doesn’t really protect the function like we want to, then we can set another value. This time let’s try to make it constant.

PS> Set-Item –Path Function:test -Value { "I'm constant" } -Options Constant -Force

Set-Item : Existing function test cannot be made constant. Functions can be made constant only at creation time.

At line:1 char:1

+ Set-Item –Path Function:test -Value { "I'm constant" } -Options Constant -Force

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : WriteError: (test:String) [Set-Item], SessionStateUnauthorizedAccessException

    + FullyQualifiedErrorId : FunctionCannotBeMadeConstant,Microsoft.PowerShell.Commands.SetItemCommand

 

PS> 

We get an error that functions can be made constant only at creation time. Let’s create a new function and then try to override it and remove it:PS C:\> # create a constant function

# # create a constant function
PS>
New-Item –Path Function:test2 -Value { "I'm constant" } -Options Constant

Capability Name ModuleName

---------- ---- ----------

Script    test2

 

# try to update the function

PS> Set-Item –Path Function:test2 -Value { "I'm constant 2" } -Force

 

# try to remove it

PS> Remove-Item Path Function:test2 -Force

 

 

Set-Item : Cannot write to function test2 because it is read-only or constant.

At line:5 char:1

+ Set-Item –Path Function:test2 -Value { "I'm constant 2" } -Force

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : WriteError: (test2:String) [Set-Item], SessionStateUnauthorizedAccessException

    + FullyQualifiedErrorId : FunctionNotWritable,Microsoft.PowerShell.Commands.SetItemCommand

 

Remove-Item : A positional parameter cannot be found that accepts argument 'Function:test2'.

At line:8 char:1

+ Remove-Item Path Function:test2 -Force

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : InvalidArgument: (:) [Remove-Item], ParameterBindingException

    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand

 

PS> 

 

As you can see none of the above changes the function. It is protected and the only option to get rid of it is by closing the shell. 

In addition to Aliases and Functions, we can also protect other objects, like: variables and modules. For variables use the Set-Variable cmdlet, it has its own options parameter. To protect modules from being unloaded we need to set the module’s AccessMode property. Here’s an example using dynamic module.

 

PS> New-Module -Name CantTouchThis -ScriptBlock {

 $ExecutionContext.SessionState.Module.AccessMode = 'constant'

} | Import-Module

 

Remove-Module -Name CantTouchThis -Force

 

 

Remove-Module : Unable to remove module 'CantTouchThis' because it is marked as constant. A module cannot be removed if it is marked constant.

At line:5 char:1

+ Remove-Module -Name CantTouchThis -Force

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : PermissionDenied: (CantTouchThis:PSModuleInfo) [Remove-Module], InvalidOperationException

    + FullyQualifiedErrorId : Modules_ModuleIsConstant,Microsoft.PowerShell.Commands.RemoveModuleCommand

 

Remove-Module : No modules were removed. Verify that the specification of modules to remove is correct and those modules exist in the runspace.

At line:5 char:1

+ Remove-Module -Name CantTouchThis -Force

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : ResourceUnavailable: (:) [Remove-Module], InvalidOperationException

    + FullyQualifiedErrorId : Modules_NoModulesRemoved,Microsoft.PowerShell.Commands.RemoveModuleCommand

 

PS>

 

I would like to have another way to create constant functions or modules so I logged two suggestions on Microsoft's Connect website, one for functions and another one for modules, vote them up and maybe we'll get them in v4 :)

Custom objects default display in PowerShell 3.0

In PowerShell 3.0 we can now create new custom objects using a hash table.

PS> [PSCustomObject]@{
   One = 1 
   Two = 2 
   Three = 3 
   Four = 4 
   Five = 5 
}


One   : 1
Two   : 2
Three : 3
Four  : 4
Five  : 5 

Behind the scenes, PowerShell creates a hash table and wraps it a PSCustomObject. It is way faster than using the New-Object cmdlet and it also provides consistency, while maintaining backwards compatibility. Another benefit of using PSCustomObject over New-Object is property order. PSCustomObject preserve the order of defined properties while New-Object doesn't.

 

PS > [PSCustomObject]@{One=1;Two=2;Three=3} 

One Two Three
--- --- -----
  1   2     3


PS > New-Object PSObject -Property @{One=1;Two=2;Three=3}

One Three Two
--- ----- ---
  1     3   2

Notice that when the new PSCustomObject is returned, all of its properties are displayed in the console. We can control the default display property set of an object by setting a DefaultDisplayPropertySet.

 

$psco = [PSCustomObject]@{
   One = 1
   Two = 2
   Three = 3
   Four = 4
   Five = 5 
}

 
[string[]]$DefaultProperties = 'Two','Four','Five'

# Add the PSStandardMembers.DefaultDisplayPropertySet member
$ddps = New-Object System.Management.Automation.PSPropertySet `
DefaultDisplayPropertySet,$DefaultProperties $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]$ddps # Attach default display property set $psco | Add-Member -MemberType MemberSet -Name PSStandardMembers `
-Value $PSStandardMembers -PassThru
Two Four Five --- ---- ---- 2 4 5

 

We can also define the properties of the type that will be displayed by default with the formatting cmdlets.

PS> Get-Date | Format-List 
PS> Update-TypeData -TypeName System.DateTime -DefaultDisplayPropertySet `
DateTime,DayOfWeek,Year,Month,Day PS> Get-Date | Format-List DisplayHint : DateTime Date : 4/9/2012 12:00:00 AM Day : 9 DayOfWeek : Monday DayOfYear : 100 Hour : 8 Kind : Local Millisecond : 598 Minute : 15 Month : 4 Second : 38 Ticks : 634695561385980985 TimeOfDay : 08:15:38.5980985 Year : 2012 DateTime : Monday, April 09, 2012 8:15:38 AM DateTime : Monday, April 09, 2012 8:15:39 AM DayOfWeek : Monday Year : 2012 Month : 4 Day : 9

 

I have put together a function that wraps the process of creating a new PSCustomObject and specifying a list of default properties to display.

function New-PSCustomObject
{
       [CmdletBinding()] 

       param(
              [Parameter(Mandatory,Position=0)]
              [ValidateNotNullOrEmpty()]
              [System.Collections.Hashtable]$Property,

              [Parameter(Position=1)]
              [ValidateNotNullOrEmpty()]
              [Alias('dp')]
              [System.String[]]$DefaultProperties
       )


       $psco = [PSCustomObject]$Property 

       # define a subset of properties
       $ddps = New-Object System.Management.Automation.PSPropertySet `
DefaultDisplayPropertySet,$DefaultProperties $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]$ddps # Attach default display property set $psco | Add-Member -MemberType MemberSet -Name PSStandardMembers `
-Value $PSStandardMembers -PassThru }
# usage example # define 5 properties PS> $property = @{Name='My Object';One=1;Two=2;Three=3;Four=4} # create the object and set just three properties for default display PS> New-PSCustomObject -Property $property -DefaultProperties Name,Two,Four Two Four Five --- ---- ---- 2 4 5

Add-Member enhancements in PowerShell 3.0

PowerShell 3.0 offers new ways to add Note properties to objects. In PowerShell 2.0, a typical command to do that would look like:

PS> New-Object -TypeName PSObject | 
Add-Member -MemberType NoteProperty -Name One -Value 1 -PassThru

One
---
  1

 

And when adding multiple note properties:

PS> New-Object -TypeName PSObject | 
      Add-Member -MemberType NoteProperty -Name One -Value 1 -PassThru | 
      Add-Member -MemberType NoteProperty -Name Two -Value 2 -PassThru | 
      Add-Member -MemberType NoteProperty -Name Three -Value 3 -PassThru


One Two Three
--- --- -----
  1   2     3

As you can see the syntax was very long and each time you created a NoteProperty you had to specify that. In Powershell 3.0 Add-Member have to new parameters: NotePropertyName and NotePropertyValue. Now you can easily add a NoteProperty without having to specify the member type and without having to specify parameter names, we take advantage of their positional value.

PS> New-Object -TypeName PSObject | Add-Member One 1

One
---
  1

 

To add multiple NoteProperty we can now use a hashtable:

PS> New-Object -TypeName PSObject | Add-Member @{One=1; Two=2; Three=3} -PassThru

One Three Two
--- ----- ---
  1     3   2

Notice that the object we got back didn't preserve the order of properties defined in the hashtable. Order in a dictionary is defined the moment you create it and the regular hashtable syntax @{} is an unordered dictionary. To make sure properties are listed in the order we defined them, we use an ordered hashtable.

PS> $pso = New-Object -TypeName PSObject
PS> $pso | Add-Member ([ordered]@{One=1; Two=2; Three=3}) -PassThru One Two Three --- --- ----- 1 2 3

 

Finally, there's another interesting new parameter: TypeName. We can use it to specify a name for the resultant object type. This is very useful when you use format files to define the default display of your objects. Previously we had to add the type name to the object's PSTypeNames list, now it is even easier.

PS> $pso = New-Object -TypeName PSObject 
PS> $pso | Add-Member ([ordered]@{One=1; Two=2; Three=3}) -TypeName MyType.MyObject 
PS> $pso | Get-Member TypeName: MyType.MyObject Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode Method int GetHashCode() GetType Method type GetType() ToString Method string ToString() One NoteProperty System.Int32 One=1 Three NoteProperty System.Int32 Three=3 Two NoteProperty System.Int32 Two=2

Measuring objects in PowerShell 3.0

The Measure-Object cmdlet gives us a great way to find minimum and maximum values in a collection of objects. For example, if we want to know the smallest and largest size of a file in the current directory:

PS> Get-ChildItem | Measure-Object -Property Length -Minimum -Maximum

Count    : 2751
Average  :
Sum      :
Maximum  : 56297240
Minimum  : 35
Property : Length

In PowerShell 2.0 it only worked with numeric properties (integers), we couldn't use it to compare properties like LastWriteTime (DateTime). In PowerShell 3.0 we can now use the Minimum and Maximum parameters to work with anything that is IComparable. We can tell if an instance of an object is IComparable by checking if the CompareTo method exist on the instance:

PS> $object.PSObject.Methods | Where-Object {$_.Name -eq 'CompareTo'}


But there's a simpler way using the -is operator. If we get $true - we can measure it:

PS> $object -is [IComparable]

 

In this example we measure the LastWriteTime property, which is a DateTime object. We can see the modification time of the oldest and newest files in the current directory.

PS> Get-ChildItem | Measure-Object -Property LastWriteTime -Minimum -Maximum

Count    : 2844
Average  :
Sum      :
Maximum  : 4/8/2012 8:03:32 PM
Minimum  : 9/4/2004 3:00:00 AM
Property : LastWriteTime

Leveraging Proxy Functions in PowerShell

@DSotnikov just released the video recording of the session from the PowerShell Deep Dive in Frankfurt (last year). In this video, MVP Kirk Munro (poshoholic) and I demoed a project we've been working on that let's you create proxy functions. Here's a reminder of the session abstract.

In this session as they take a deep dive into proxy functions in PowerShell. Shay and Kirk have been working together on PowerShell Proxy Extensions, a powerful module that leverages proxy functions and makes it easier than ever to create these powerful extensions to PowerShell. They will demonstrate what proxy functions are and why they are important, and then show how a little scripting savvy (and a really long script) can make your life easier by allowing you to create everything from very simple proxy functions that extend PowerShell to more complex proxy functions that override existing commands, fixing bugs and adding missing features at the same time, all while leveraging inline help as much as possible.

The module we demoed is available at http://pspx.codeplex.com/. Unfortunately I won't be able to make it this year to the Deep Dive in San Diego, but if you're attending you'll get the chance to see the module in action, plus a very cool project, written on top of the PowerShell Proxy Extensions (PSPX) module.

Counting objects in PowerShell 3.0

Consider the following command, how many objects are in $dir?

PS> $dir = Get-ChildItem


The most common way to find it is to check the Count property:

PS> $dir.Count


But… the Count property is available only if Get-Something returns more than one object (array/collection), If the result has one object only (scalar) the Count property returns nothing, not even zero. If the result contains more than one object, the objects are accumulated in an Array (collection), and Arrays have a Count property so we get the number of objects.

Now back to the question above, how can we safely determine object count? The most common solution was to use the @(…) array construction to force the result to an array:

PS> @($dir).Count


Now Count will return 0 or another positive number. Although we could get object count this way, it was very irritating and tedious to always make sure to convert the output!

In PowerShell 3.0 the saga ends! We are no longer required to convert the result. The magicians at Microsoft had spread some of their magic powder and now the Count property is available on single objects as well!

# get the process that is hosting the current Windows PowerShell session 
PS> $ps = Get-Process -Id $PID 
PS> $ps.Count
1


If you try to use tab completion on a single object, Count will not be one of the options and it is also not visible to the Get-Member cmdlet, you need to explicitly write it in order to get the value.

One more thing to notice is when the command didn’t return any objects($null result). Unlike other languages, $null in PowerShell “has” a Count property, and it makes sense. If you base you script on the Count property you may get 0 or 1 or more objects. In the case of the former, $null.count gives back 0.

Enjoy!

Auto reconnect to a server you just rebooted with PowerShell 3.0

Yesterday I saw a tweet that caught my eye:

image

Sounds familiar? What do you usually do to reconnect to your server once it is back online? How do you monitor it’s availability? In this post, Justin shares a great function (115 lines) that restarts a server, waits for it to come back online and then starts a remote desktop connection to it.

Now, what if I told you that in PowerShell 3.0 you can replace that function with only two lines of code?

In PowerShell 3.0, the Restart-Computer cmdlet got improved (mainly to support Workflow scenarios) and it now supports a few more parameters, notably the –Wait and –For parameters. When –Wait is specified, the command waits for all of the following service types to be available (can take a lot of time) before you can proceed to the next line of code. The following command lists the service types:

PS> [Enum]::GetNames(‘Microsoft.PowerShell.Commands.WaitForServiceTypes’)
Wmi WinRM PowerShell

Actually, these types are the possible values of the –For parameter. In the previous CTP version, there used to be a ‘Network’ type too, but looks like it was removed. We can specify one of above types, Restart-Computer waits for that component only to be available and then the rest of our code is executed.

In our case, we need to wait for the Remote Desktop Services service to start, but there’s no related service type we can use for that. We can wait for the ‘Wmi’ service. That’s because the WMI and the RDS services are dependent on the Remote Procedure Call (RPC) service, so if the WMI service is started we can assume that the RDS service is up as well.

Restart-Computer –ComputerName Server1 -Wait -For Wmi –Force             
mstsc –v Server1 /admin

That’s all it takes to connect to Server1 once it’s back online. Notice that the Restart-Computer command may fail if there are users logged on to the remote computer or if there are opened applications, so make sure you include the –Force switch. One “caveat” though, the command waits until the computer has finished restarting and doesn’t give back your prompt, you’re stuck waiting for it to finish. To continue working you can spin a new background job:

Start-Job -ScriptBlock{             
    Restart-Computer –ComputerName $args[0] -Wait -For Wmi–Force           
    mstsc -v $args[0] /admin             
} -ArgumentList Server1

PSRR - Remote Registry PowerShell 3.0 Module

PSRemoteRegistry

One of the new improvements in the .NET Framework version 4 is the Microsoft.Win32.RegistryView enumeration.

On the 64-bit version of Windows, portions of the registry are stored separately for 32-bit and 64-bit applications. There is a 32-bit view for 32-bit applications and a 64-bit view for 64-bit applications. Many of the 32-bit keys have the same names as their 64-bit counterparts, and vice versa. In the 64-bit version of Registry Editor, 32-bit keys are displayed under the following registry key: HKEY_LOCAL_MACHINE\Software\WOW6432Node.

I’m pleased to announce that the PSRemoteRegistry module has a new version specifically for PowerShell v3 that implements the new feature mentioned above. The new module, now called PSRR (to avoid name collisions with the previous module and to allow you to type less when you load it), was rewritten and is using the new language features introduced in PowerShell v3. To improve the readability of the functions, all functions' help are now stored in MAML files (instead of comment-based help).

The major addition to the PSRR module is the ability to manage both 64-bit and 32-bit registry keys and values by using the new View parameter (available on all module functions) from PowerShell x86 or x64 instances. See the help for any function on how to use theView Parameter.

You can download it HERE.

The PowerShell Experts Video Series

psmagPowerShell Magazine has started the PowerShell Experts Video Series, a series of short video interviews conducted at the PowerShell Deep Dive conference in Frankfurt, Germany.
Starting today, we will post one video interview per day.  The first one is with MVP Dmitry Sotnikov.

New advanced functions language features in PowerShell v3

PowerShell 3.0 CTP1 includes many new language enhancements. Here are four of them for advanced functions.

 

CmdletBinding and Parameter attributes

We are no longer required to assign a value of $true to CmdletBinding and Parameter attributes. For example, in v2 we had to write:

 

[CmdletBinding(SupportsShouldProcess=$true)]            
param(            
    [Parameter(Position=0,            
        ValueFromPipeline=$true,            
        ValueFromPipelineByPropertyName=$true            
    )]            
    [string]$Name            
)
In v3 we can omit the assignment.
            
[CmdletBinding(SupportsShouldProcess)]            
param(            
    [Parameter(Position=0,            
        ValueFromPipeline,            
        ValueFromPipelineByPropertyName            
    )]            
    [string]$Name            
)            
            

The absence of an attribute is same as explicitly setting the attribute to $false, but that isn’t new to v3, is the same as v2.

 

Built-in support for Paging operations

[CmdletBinding(SupportsPaging)]

When the SupportsPaging attribute of CmdletBinding is specified in advanced functions we get three additional parameters IncludeTotalCount,Skip, and First. These parameters allows us to page the results. When the IncludeTotalCount is specified the function writes to the host the total amount of objects in the form of:

Total count: n

 

The Skip parameter, UInt64, controls how many objects the function ignores. The value cannot be greater than the total amount of objects in the result. The First parameter, UInt64, controls how many objects the function returns from the beginning of the result.

You can find a script example in the samples folder of CTP1 (Samples\WindowsPowerShell\SupportsPagingSample\SupportsPaging01.ps1).

 

HelpUri

Assign it a web URL and Get-Help will pick it up when the Online switch has been specified, and will
display the page in your default browser. Get-Command also shows the URI in its result.

[CmdletBinding(SupportsPaging,HelpUri='http://PowerShay.com')]             
 

PositionalBinding

[CmdletBinding(PositionalBinding=$false)]             

Setting PositionalBinding to $false allows us to disable positional parameters binding and force users to write the parameter name and its value. See this post for more information.

Windows PowerShell 3.0 CTP1 is available for download!

Windows_PowerShell_iconWindows Management Framework 3.0 Community Technology Preview (CTP1) is available for download for Windows 7 SP1 and Windows Server 2008 R2 SP1.

Read the full announcement on the PowerShell team blog. Check out this post on the PowerShell Magazine site for a quick review of new cmdlets and parameters in v3.

How to disable positional parameter binding in PowerShell vNext

Windows PowerShell supports two kind of parameters: Named and Positional. In a nutshell, Named parameters must be specified on the command line while Positional parameters are inferred by the argument’s position on the command line.
The type of the parameter is controlled by the [Parameter()] attribute (see about_Parameters).

What if we wanted to disable positional parameters and force users to write the parameter name and its value?
Consider the following example, all parameters values are positional. Can you guess the parameter names?

PS> Register-ObjectEvent $timer Elapsed Timer.Random {$random = Get-Random -Min 0 -Max 100}


There is some logic in the order of values but that is a bit hard to read and involves some guess work as well as consulting the help files. It is also not recommended when writing production scripts and can cause a lot of frustration for users who are not familiar with the command.

In PowerShell v2 it was not possible to disable that behavior (see a work around at the end of this post).
By default, values on the command line where treated as positionals, but in PowerShell v3 the PowerShell team added a new
functionality to address it.

The CmdletBinding attribute is used in Advanced functions to identify them as functions that act similar to compiled cmdlets.
When we write advanced functions, we can add the CmdletBinding attribute so that Windows PowerShell will bind the parameters of the function in the same way that it binds the parameters of compiled cmdlets. Some of the known arguments of the CmdletBinding attribute are: SupportsShouldProcess, DefaultParameterSetName and ConfirmImpact.
You can read more about them in the about_Functions_CmdletBindingAttribute help topic.

In PowerShell v3 we have additional CmdletBinding attribute - PositionalBinding (there are more new attributes!).
As its name suggests, when specified it disables positional binding and force the caller to specify parameter names.
Let's test it with the following function.


function Test-PositionalBinding             
{             
    [CmdletBinding(PositionalBinding=$false)]             
            
    param(             
       $param1,$param2             
    )             
            
    Write-Host param1 is: $param1            
    Write-Host param2 is: $param2             
}             

 


As you can see, the PositionalBinding attribute is set to $false. The function will not allow positional binding.

PS> Test-PositionalBinding -param1 one -param2 two

param1 is: one
param2 is: two


In the example above we specified parameter names and the result is as expected. Let's try to pass the value of param1 by its position.


PS> Test-PositionalBinding one -param2 two

Test-PositionalBinding : A positional parameter cannot be found that accepts argument 'one'.
At line:1 char:1
+ Test-PositionalBinding one -param2 two
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Test-PositionalBinding], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Test-PositionalBinding


And it fails. Check this forum thread if you need to enable similar functionality in PowerShell v2, great workaround by @mjolinor.

Want to try PowerShell v3? Download the Windows 8 Developer Preview from MSDN.

More Posts Next page »