Source: PowerShellMagazine.com
If you’ve updated PowerShell to version 3.0 you probably noticed that help is no longer shipped in the box. PowerShell 3.0 includes a new feature, the Updatable Help system, which allows you to ensure that help on the local computer stays up to date.
PowerShell is now a part of the operating system and operating systems gets to be updated only during service packs or specific patches. The Updatable Help system and its cmdlets (*-Help) make it easy to download and install help files, or updating exiting help files, as soon as newer help files become available.
To download and install help files for the first time, use the Update-Help cmdlet. When executed, Update-Help goes through a series of steps to get the latest help versions of the modules installed on your system:
- It determines which modules support updatable help by checking the HelpInfoUri key in the module manifest file. The HelpInfoUri contains the Internet location where each module stores its updatable help files.
- Compares each module local help files with the newest help files that are available for each module.
- Downloads the new files (packaged in a cab file).
- Unwraps the help file package, verifies that the files are valid, and then installs the help files in the language-specific subdirectory of the module directory.
Note: There are a few things to take into account when using Update-Help. First, you must run PowerShell as an administrator as help files are written to the installation folder of PowerShell and that happens to be under theSystem32 folder. PowerShell allows you to update help files once every 24 hours. To override this behavior you must specify the -Force switch.
By default, the Update-Help cmdlet doesn’t generate any output. When executed, it displays a progress bar that prints information about the current module update phase.

If you want to see what’s going on under the hood, include the -Verbose switch:

One of the things that really annoys me is the output of the -Verbose switch, the way it is written makes it very hard to read and determine which files and modules has been updated. In this post I want to introduce you to a new feature in PowerShell 3.0 that can help you change the way the verbose information is displayed in the console.
In the previous version of PowerShell it was very hard to capture the output of the Verbose stream, or any other PowerShell-related stream. Luckily, this has changed in PowerShell 3.0 and now it is a very easy thing to do. We can now redirect and merge any of the pipeline output streams (see list below) to text files. You can read more about this in about_Redirection help topic.
By default, the Update-Help command doesn’t write anything to the pipeline so we can safely merge the verbose stream to the standard output stream and parse it without having to worry about information from both sources gets mixed together.
Each redirection operator uses a character to represent each output type:
- * – All output
- 1 – Success output
- 2 – Errors
- 3 – Warning messages
- 4 – Verbose output
- 5 – Debug messages
The Verbose stream constant is 4, and Success output is 1, so we use the redirection operator (e.g ‘>’) to funnel the output:
PS> $uh = Update-Help -Verbose -Force 4>&1
Let’s examine the first element; we can see that the connection is redirecting to another URI.
PS> $uh[0]
VERBOSE: Your connection has been redirected to the following URI:
VERBOSE: http://download.microsoft.com/download/3/4/C/34C6B4B6-63FC-46BE-9073-FC75EAD5A136/
The second element in the output of Update-Help contains the information we are after. We can see that theMicrosoft.PowerShell.Management was updated, and we
also get information about the path of the help file, its culture, and the version information.
PS> $uh[1]
VERBOSE: Microsoft.PowerShell.Management: Updated
VERBOSE:
C:\Windows\System32\WindowsPowerShell\v1.0\en-US\Microsoft.PowerShell.Commands.Management.dll-hel
p.xml. Culture en-US
VERBOSE: Version 3.1.0.0
Let’s see what Get-Member has to say about it:
PS> $uh[1] | Get-Member
TypeName: System.Management.Automation.VerboseRecord
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
WriteVerboseStream NoteProperty System.Boolean WriteVerboseStream=True
InvocationInfo Property System.Management.Automation.InvocationInfo InvocationInfo {get;}
Message Property string Message {get;set;}
PipelineIterationInfo Property System.Collections.ObjectModel.ReadOnlyCollection[int] PipelineIterationInfo {get;}
We get a System.Management.Automation.VerboseRecord object which describes a verbose message sent to the verbose stream, and information about the command that sent the message (InvocationInfo). The Message property contains the actual message we see in the console. If you need to identify verbose messages written to the success stream, you can safely rely on this type and filter objects accordingly.
PS> $uh[1].Message
Microsoft.PowerShell.Management: Updated C:\Windows\System32\WindowsPowerShell\v1.0\en-US\Microsoft.PowerShell.Commands
.Management.dll-help.xml. Culture en-US Version 3.1.0.0
We can split the message (by default the Split method breaks the string on the space character) and get access to the array elements we get back:
PS> $uh[1].Message.split()
Microsoft.PowerShell.Management:
Updated
C:\Windows\System32\WindowsPowerShell\v1.0\en-US\Microsoft.PowerShell.Commands.Management.dll-help.xml.
Culture
en-US
Version
3.1.0.0
Based on the output of the Split operation, we want to extract the relevant pieces of information, create a new custom object and write it back to the pipeline, one object at a time.
The information we want is:
- The module name–it’s the first item (index 0). The value is followed by a colon, we’ll remove it later on.
- The file path that’s being updated (third line, index 2).
- The culture of the help file (fifth line, index 4).
- The version of the new help file (last line, we can refer to it as index -1, which in PowerShell gives back the last array element).
We can now construct a new custom object. We’ll start by creating a custom object for the first array element:
$message = $uh[1].Message.split()[0,2,4,-1]
[PSCustomObject]@{
Module = $message[0] -replace ':$'
FileName = (Split-Path $message[1] -Leaf).Trim('.')
Culture = $message[2]
Version = $message[-1]
}
Module FileName Culture Version
------ -------- ------- -------
Microsoft.PowerShell.Manag... Microsoft.PowerShell.Comma... en-US 3.1.0.0
As soon as each object (verbose message) is processed, the object goes out to the console. As you can see, the output of the Module and FileName properties is truncated.
We could use the -AutoSize of the Format-Table cmdlet to adjust the columns size, but doing so will block output of objects to the console until all objects were processed.
When processing the verbose stream we also want to avoid processing unnecessary messages, we want to skip any messages that contains URI redirections, so we process only messages that contains the word “Updated”. We pipe the custom objects to the Tee-Object cmdlet, to send output to the console as soon as it flows in, and also save the output to a variable that we can format the way we want it to.
Here’s the full snippet. Output shown on screen is also saved in the UpdatedHelp variable. When the script finished executing we can investigate and format it as we like.
Update-Help -Force -Verbose 4>&1 |
Where-Object {$_.Message -like '*: Updated*'} |
ForEach-Object {
$message = $_.Message.Split()[0,2,4,-1]
[PSCustomObject]@{
Module = $message[0] -replace ':$'
FileName = (Split-Path $message[1] -Leaf).Trim('.')
Culture = $message[2]
Version = $message[-1]
}
} | Tee-Object -Variable UpdatedHelp
$UpdatedHelp | Format-Table -AutoSize

Hi, it’s been a while since my last post, I’ve been busy mostly at work and also by investing most of my time running the PowerShell Magazine website together with my friends. I hope to post more in the future. For the time being, here’s my latest post, cross-posted on the PowerShell Magazine.
Working with CSV files in PowerShell is a common practice. You import the file, loop on its records and you’re good to go. Sometimes however you may find yourself in a situation where you get a file that has blank lines in it, and those lines can break your script. Consider the following CSV content:
## sample.csv ##
column1,column2,column3
Value1,Value2,Value3
Value1,Value2,Value3
## file ends here
On the surface, nothing looks suspicious when you import the file:
PS> Import-Csv sample.csv
column1 column2 column3
------- ------- -------
Value1 Value2 Value3
Value1 Value2 Value3
PS>
But if you pipe it to Format-List you can clearly see what’s going on. You get empty objects for each empty line in the file.
PS> Import-Csv sample.csv | Format-List
column1 : Value1
column2 : Value2
column3 : Value3
column1 : Value1
column2 : Value2
column3 : Value3
column1 :
column2 :
column3 :
column1 :
column2 :
column3 :
column1 :
column2 :
column3 :
To filter out empty objects you need to test that all properties are not equal to an empty string and throw them away.
You might be attempted to do that with:
Import-Csv sample.csv |
Where-Object {$_.column1 -ne '' -and $_.column1 -ne '' -and $_.column1 -ne ''}
But what if each record has 20 properties, or even more? This is where the PSObject property comes to rescue. In a nutshell, PSObject allows us to work with any object in the same way without really knowing its structure. PowerShell wraps the base object in a PSObject and provide us a simplified and consistent view of the object, its methods, properties, and so on. One of the properties of PSObject is Properties, and it gives us a list of properties of the base object.
On a related note, PSObject and other members are not visible when you pipe an object to the Get-Member cmdlet. To reveal those members add the -Force switch to Get-Member.
For our purpose, we can process the properties list and filter out those who have a Value of null.
Import-Csv sample.csv |
Where-Object { ($_.PSObject.Properties | ForEach-Object {$_.Value}) -ne $null} |
Format-List
column1 : Value1
column2 : Value2
column3 : Value3
column1 : Value1
column2 : Value2
column3 : Value3
In PowerShell 3.0 and the new Member Enumeration feature we can get the same result in less characters:
Import-Csv sample.csv |
Where-Object { $_.PSObject.Properties.Value -ne $null}
I logged an Import-Csv feature enhancement, and you can add your vote if you’d like to have a built-in option to ignore empty lines.
I’ve just received THE email from Microsoft… I’m a PowerShell MVP for another year! I am very honored!
This is my 5th renewal and I would like to take this opportunity and say thank you to Microsoft and to all the people who supported me.
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
)
process
{
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
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.
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.
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 :)
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
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
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
@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.
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!
Yesterday I saw a tweet that caught my eye:

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

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.
PowerShell 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.
More Posts
Next page »