Monthly Archive


PowerShell Basics

using help proactively

I was reading a thread on the forum about using a particular cmdlet and it occurred to me that the person posing the question hadn’t actually looked at the help file for the cmdlet.


One of the first things I do when coming across a new cmdlet – either in some code or when I need to perform a new task - is to read the FULL help file for the cmdlet.


Get-Help Get-Process –Full



Get-Help Get-Process –Online


The online version tends to be updated quicker than the downloadable version.


Two areas to concentrate on.

The parameter list shows you what parameters are available, the input and relevant information


The examples section is possibly the most useful as you’ll find how you can actually use the cmdlet.


The detailed description and notes a re worth a read – especially for a cmdlet you’ve not used before


Think about the PowerShell experts you’ve come across. How did they get to be experts? A lot of it was reading the help files.

Update-Help errors

Updatable help brings the benefit of up to date help with typos fixed and new edge cases described. The down side is that it sometimes fails:


PS> Update-Help -Force
Update-Help : Failed to update Help for the module(s) 'Microsoft.PowerShell.Operation.Validation'
with UI culture(s) {en-GB} : Unable to retrieve the HelpInfo XML file for UI culture en-GB. Make sure the HelpInfoUri property in the module manifest is valid or check your network connection and then try the command again.
At line:1 char:1
+ Update-Help -Force
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ResourceUnavailable: (:) [Update-Help], Exception
    + FullyQualifiedErrorId : UnableToRetrieveHelpInfoXml,Microsoft.PowerShell.Commands.UpdateHelpCo


Update-Help : Failed to update Help for the module(s) 'PSScriptAnalyzer' with UI culture(s) {en-US} : The Help content at the specified location is not valid. Specify a location that contains valid Help Content.
At line:1 char:1
+ Update-Help -Force
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Update-Help], Exception
    + FullyQualifiedErrorId : HelpContentXmlValidationFailure,Microsoft.PowerShell.Commands.UpdateHe


The failure messages are relatively self-explanatory if you read the details. The problem usually boils down to the needed help files not being available at the URI provided in the module manifest. This often occurs with new modules – the creation of the help files lags well behind the creation of the module usually.


Unfortunately there isn’t much you can do apart from try to find examples of the modules use online

Boolean in Where-Object filter

I was testing some code yesterday and realised there was a quirk in the way the original where syntax (with {}) worked and the way the newer syntax worked.


To demonstrate this I created a set of objects

$i = 0

$tests = while ($i -lt 25){
  New-Object -TypeName PSObject -Property @{
    Index = $i
    Current = if (-not($i % 2)){$true} else {$false}


Object properties are a numeric index and a boolean value


If you want just the $true values many people write this

$tests | where {$_.Current -eq $true}


or if using the newer syntax use this

$tests | where Current -eq $true


This is unnecessary typing as you can do this

$tests | where {$_.Current}

$tests | where Current


The reason is that the filter you are creating tests a property of the current object against your criteria and passes is if the result is true. A boolean property will by definition either be true of false so just need to test directly


if you want to double negative type test i.e. – not $true (which I don’t recommend as its very easy to get into  logic mess) then you have to do this

$tests | where {-not $_.Current}


as this fails
$tests | where –not Current


You could do this

$tests | where Current -ne $true


but it negates the whole code simplification objective


It would be nice to be able to do this:

PS>  Get-Date -Day 25 -Month December -Year 2016
Get-Date : Cannot bind parameter 'Month'. Cannot convert value "December" to type "System.Int32".
Error: "Input string was not in a correct format."
At line:1 char:25
+ Get-Date -Day 25 -Month December -Year 2016
+                         ~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-Date], ParameterBindingException
    + FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.GetDateCo


You have to do this:

PS>  Get-Date -Day 25 -Month 12 -Year 2016

25 December 2016 11:00:56


The time is set to current time automatically.


One of the gaps in .NET is an enum for the months of the year. You get an enum for days of the week:

PS>  [System.DayOfWeek]::Friday


PS>  0..6 | foreach {[System.DayOfWeek]$psitem}


PowerShell v5 gives you the capability to make your own enums:

enum MonthsOfYear {
January = 1
February = 2
March = 3
April = 4
May = 5
June = 6
July = 7
August = 8
September = 9
October = 10
November = 11
December = 12


PS>  [MonthsOfYear]10

PS>  [MonthsOfYear]::April


PS>  1..12 | foreach {[MonthsoFYear]$PSItem}


PS>  [System.Enum]::GetNames([MonthsOfYear])


Now you can use the name of the month

PS>  Get-Date -Day 25 -Month ([MonthsOfYear]::December) -Year 2016

25 December 2016 11:19:38


Enums are one of those pieces of functionality that you don’t necessarily think about but are very useful. The ability to create your own in PowerShell is very useful and will become more widespread as people realise its available.

WMI Filters

A common mistake with WMI/CIM filters is:

PS>  Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceId=C:"
Get-WmiObject : Invalid query "select * from Win32_LogicalDisk where DeviceId=C:"
At line:1 char:1
+ Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceId=C:"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-WmiObject], ManagementException
    + FullyQualifiedErrorId : GetWMIManagementException,Microsoft.PowerShell.Commands.GetWmiObjectCo


The clue is in the invalid query error message

When you use the –Filter parameter and are testing a property of type string the value you are testing against has to be in quotes

PS>  Get-WmiObject -Class Win32_LogicalDisk -Filter "DeviceId='C:'"

DeviceID     : C:
DriveType    : 3
ProviderName :
FreeSpace    : 108999528448
Size         : 248480002048
VolumeName   : Windows


The filter is defined as a string so you need to use single quotes inside the double quotes. You could mess around with all single quotes but then you have to escape the inner set of quotes – good luck with that – its an unnecessary exercise in frustration


How do you know the property is a string?

PS>  (Get-CimClass -ClassName Win32_LogicalDisk).CimClassProperties['DeviceId']

Name               : DeviceID
Value              :
CimType            : String
Flags              : Property, Key, ReadOnly, NullValue
Qualifiers         : {CIM_Key, read, key, MappingStrings...}
ReferenceClassName :


The same rules for –Filter apply to Get-CimInstance

Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DeviceId='C:'"

32 or 64 and/or Administrator

When you run the PowerShell console (or ISE) the default icon runs a 32 or 64 bit version that matches your OS. On a 64 bit machine you have the option of running in 32bit (icons have a (x86) suffix on the title.


How can you tell whether you’re running in 32 or 64 bit mode?


One way is shown in this forum question -


I prefer the simpler:

if ([System.IntPtr]::Size -eq 8) {$size = '64 bit'}
else {$size = '32 bit'}


I don’t like automatically kicking into the required bit version if its wrong. I prefer to abort the processing with a message to run as 32 or 64 bit as appropriate


You can perform a similar test for administrator privileges (running elevated)

$currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
$secprin = New-Object Security.Principal.WindowsPrincipal $currentUser
if ($secprin.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator))
{$admin = 'Administrator'}
else {$admin = 'non-Administrator'}


Though on later versions of Powershell its easier to use

#requires –RunAsAdministrator

Dates in file and folder names

If you want to incorporate the date in a file or folder name you can’t use Get-Date directly

PS>  Get-Date

01 June 2016 20:52:03


The simplest answer is to use the –Format or –Uformat parameters:

PS>  Get-Date -Format yyyyMMdd

PS>  Get-Date -UFormat %Y%m%d

Counting members

If you have a collection of objects

$proc = get-process


you can get the number of members using the Length property

PS>  $proc.Length

$proc is of type System.Array


PS>  $proc.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array


MSDN documenattion is at


PowerShell also adds a Count property that is an alias of Length


if you are after the number of members in a collection via the pipeline you need to use the Measure-Object cmdlet

PS>  $proc | Measure-Object

Count    : 71
Average  :
Sum      :
Maximum  :
Minimum  :
Property :


or measure directly

PS>  Get-Process | Measure-Object

Count    : 71
Average  :
Sum      :
Maximum  :
Minimum  :
Property :


Notice that Measure-Object can also be used to discover other statistics – the Average, Sum, Maximum and Minimum. These only work for numeric properties.

Loading assemblies

PowerShell is .NET based but doesn’t load all available .NET assemblies when it starts.


Many people still use something  like



to load additional assemblies.  This is a hang over from PowerShell v1 when there wasn’t another way to perform the load.

The LoadWithPartialName method has been deprecated - – and shouldn’t be used.


Your alternatives are:

Add-Type -AssemblyName System.Windows.Forms


or in PowerShell v5

using assembly System.Windows.Forms

using namespace System.Windows.Forms


can be used

New variables with the variable cmdlets


So you have some data in csv format:

column1 column2 column3 column4
------- ------- ------- -------
a1      b1      c1      d1
a1      b2      c1      d1
a1      b3      c1      d1
a2      b3      c1      d1
a2      b4      c1      d1
a2      b3      c1      d1
a3      b5      c1      d1
a3      b6      c1      d1
a3      b7      c1      d1
a4      b5      c1      d1


In a variable $cd


You want colum1 and column2 in a new variable


Simplest way is probably

$new1 = $cd | select Column1, column2


The *-Variable cmdlets don’t get out much so I thought examples using them would be useful

You could also use the New-Variable cmdlet

New-Variable -Name new2 -Value ($cd | select Column1, column2)


Set-Variable also works

Set-Variable -Name new3 -Value ($cd | select Column1, column2)