Monthly Archive



Exclusive OR

PowerShell has a number of logical operators - -  -and, –or, –not (or !). One I’ve really thought about is the exclusive OR operator –xor.

With a standard –or operator the result is TRUE if one of the statements is TRUE

PS> ('a' -eq 'A') -or ('a' -eq 'z')


The standard –or operator is also TRUE if both statements are true

PS> ('a' -eq 'A') -or ('Z' -eq 'z')


The exclusive OR is only TRUE when one of the statements is TRUE and the other is FALSE. So this returns TRUE

PS> ('a' -eq 'A') -xor ('a' -eq 'z')


But when both statements are TRUE it returns FALSE

PS> ('a' -eq 'A') -xor ('Z' -eq 'z')


The exclusive OR seems like  bit of an oddity to me.


PowerShell has a bitwise xor operator  -- -bxor but outside of that I’m struggling to find a use for –xor. Certainly as an admin I can’t think of a situation where I’d use it.


Just out curiosity have you used –xor and if so how?

Articles published in 2019

I’ve had the following articles published in 2019



Create a symbolic link

I recently had to create a symbolic link to overcome a bug in OpenSSH whereby OPENSSH won’t work with the path C:\Program Files\PowerShell\6\pwsh.exe because it has a space.

The answer is to create a symbolic link which is a file that contains a reference to another file (or directory):

New-Item -ItemType SymbolicLink -Path C:\pwsh -Target 'C:\Program Files\PowerShell\6'


C:\pwsh is the symbolic link and the Target parameter has the original path. You can now use C:\pwsh in place of C:\Program Files\PowerShell\6


New-Item can also create a Hardlink or a Junction.

HardLink is a directory entry that associates a name with a file on disk

Junction is an NTFS artefact that is a pointer to a directory on the local volume

Modifying hashtables

Saw a question about modifying hashtables. The suggested code seemed like overkill.

The starting point is this hashtable:

$record = @{Name='Joe'; Date='2019-02-01'; Status='Pending'}


The updated data is:
$update = @{Name='Joe'; Date='2019-04-01'; Status='Hired'; City='York'; Country='England'}


You need to keep the Name and Date from $record. Modify the Status to be that of $update and add the City and County information to record.

Changing status is a simple assignment:

$record.Status = $update.Status


Adding the City and County information is done by adding them to the original hashtable.

$record += @{City=$update.City; Country=$update.Country}


You can check the results by displaying the hashtable.


Technically, you’re creating a new hashtable, adding the information from $record and then the City and Country information but as it stays in the original variable its always referred to as modifying.

PowerShell SSH

PowerShell SSH support is available by default in PowerShell v6.0 and later. The big thing for PowerShell v6.0 was SSH based remoting.


On Windows 10 / Server 2019 OpenSSH is available as an optional install. On earlier versions of Windows you need to install OpenSSH -

The installation instructions are available at


If you’re still using Windows PowerShell and need SSH support consider


I think SSH remoting should be considered a must learn as its ideal for non-domain remoting situations and avoids the need for certificates in non-domain scenarios.


Count is a property on arrays

PS> $x = 1..10
PS> $x.Count


The same information is available through Length (which is the property in the System.Array class definition)

PS> $x.Length


If the variable is a scalar you get 1 returned

PS> $x = 1
PS> $x.Count


With an empty array you get zero returned

PS> $x = @()
PS> $x.Count


This means that however many items are in the array you can safely check the number of items.

Be careful with strings as Count and Length give different results

PS> $x = 'asdfghjkl;'
PS> $x.count
PS> $x.length


If you want to test the number of elements returned use Count rather than length

Where-Object options

You have a few Where-Object options when you want to filter data. Remember that Where-Object filters objects based on property values and Select-Object filters objects based on object position in the pipeline or array as well as filtering properties.


The classic way to use Where-Object is to use a filter script

PS> Get-Process | Where-Object -FilterScript {$_.CPU -gt 5}


You’re filtering to accept process objects where the CPU property shows more than 5 seconds of usage


As an aside most people don’t use the FilterScript parameter they rely on positional parameters to assign the script block to FilterScript. Script analysers such as PSScriptAnalyzer (at least as far as the rules in VScode are concerned) don’t seem to object to the use of positional parameters in this case which is rather sloppy and remiss. Another reason I don’t like any of the analysis tools I’ve tried as they aren’t consistent.


PowerShell then introduced a simpler syntax

PS> Get-Process | Where-Object -Property CPU -gt -Value 5


which is usually used via positional parameters

PS> Get-Process | Where-Object CPU -gt 5


which again seems to sneak past the analysers. If you want multiple clauses in the filter using –and / –or to join them you have to use the original syntax. Also note that –gt isn’t an operator – its a parameter!


There is also the little used array method option to filter a collection of objects

PS> (Get-Process).Where({$_.CPU -gt 5})


which it should be noted is much faster than Where-Object on the pipeline.

When using Where-Object remember the options and if you’re using positional parameters remember what you’re actually doing.

Opposing Automation

Opposing Automation – no I don’t mean that you should oppose it. The sad fact is that there are very many administrators opposed to automation.


Within two hours of starting my last job I was told by my team lead “you’ll never automate anything here”. Needless to say our relationship never really worked after that. I ended up in a different team and yes I did automate a lot of stuff.


Other all time favourites of mine are:

“We’re too busy to automate”

“We’ve always done it this way”


If you meet real roadblocks like this you’ll more than likely end up looking for another job.


There are a few things you can try though.


In your own work try automating tasks that take a long time and very repetitive for example one company insisted on the event logs being checked for specific events on a regular basis. Now, the idea solution would be a monitoring solution but that means spending money… One solution is to RDP into all the boxes and query the event logs. On the other hand a quick script that remotes into the required servers and checks the logs. Report the date and time of the events by server and you’re done. Better still schedule the task for overnight and you don’t need to do much at all once its set up. Even if you have to create the functionality in chunks its surprising how quickly you can build up to a useful set of utilities. Don’t forget – automate what you need not what the books (including mine) suggest – you’re the one how knows what you need.


The other approach that can sometimes work is to find how you can help someone else get something done. Again you may need to build the functionality in chunks but getting other people to sell the benefits of automation will be a big help.


However, you approach the problem there will always be people opposed to automation – your only options are to work round them or to move somewhere where your ideas are appreciated. Don’t stay and suffer – its not worth it.

File rename

File rename is a topic that seems to keep recurring.

The simple answer is that you use Rename-Item

PS> Rename-Item -Path C:\test\Newoutdata01.txt -NewName OldData01.txt


If for whatever bizarre reason you have a character such as [ in your file name the rename won’t work

PS> Rename-Item -Path C:\test\New[outdata02.txt -NewName OldData02.txt
Rename-Item : Cannot retrieve the dynamic parameters for the cmdlet. The specified wildcard character pattern is not valid: New[outdata02.txt
At line:1 char:1
+ Rename-Item -Path C:\test\New[outdata02.txt -NewName OldData02.txt
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], ParameterBindingException
+ FullyQualifiedErrorId : GetDynamicParametersException,Microsoft.PowerShell.Commands.RenameItemCommand


The way round that is to use –LiteralPath

PS> Rename-Item -LiteralPath C:\test\New[outdata02.txt -NewName OldData02.txt


If you need to rename a bunch of files use Get-ChildItem and pipe the results into Rename-Item

PowerShell version incompatibilities

There are incompatibilities between Powershell versions – you can’t use classes in Windows PowerShell v4 and there are differences between Windows PowerShell v5.1 and v6.x. One way to deal with PowerShell version incompatibilities is described in the recent post from the PowerShell team -


I’m not a big fan of tools such as PSScriptAnalyzer as I find them restrictive and they get in the way of doing what I need to do. Having said that the usage described in the article is actually very useful especially the way you can configure which PowerShell versions you need to be worried about.


PSScriptAnalyzer is built into VSCode so the availability of the functionality to chaeck PowerShell version incompatibilities actually strengthens the case for using VSCode (I still don’t feel 100% comfortable with it – mainly because it does too much for what I need. The ISE is simpler and suits my needs better).


If you need work round PowerShell version incompatibilities in the code you write this may be of great help.