Monthly Archive

Categories

Monthly Archives: April 2019

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 - https://github.com/PowerShell/openssh-portable

The installation instructions are available at

https://github.com/PowerShell/Win32-OpenSSH/wiki/Install-Win32-OpenSSH

 

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

https://github.com/darkoperator/Posh-SSH

 

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

Count is a property on arrays

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

 

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

PS> $x.Length
10

 

If the variable is a scalar you get 1 returned

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

 

With an empty array you get zero returned

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

 

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
1
PS> $x.length
10

 

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

What’s new in PowerShell v6.2

The What’s new in PowerShell v6.2 is available at - https://docs.microsoft.com/en-gb/powershell/scripting/whats-new/what-s-new-in-powershell-core-62?view=powershell-6 – together with the already existing documents for PowerShell v6.0 and v6.1.

 

Its worth reading through the three documents – one each for v6.0, v6.1 and v6.2 to see the whole range of changes in PowerShell core.

CIM_Component class

I saw a question about the CIM_Component class and wondered what it was. So I tried it

 

PS> Get-CimInstance -Namespace root\CIMV2 -ClassName CIM_Component | select -f 1 | fl *

GroupComponent : Win32_Directory (Name = "<directory path>")
PartComponent : CIM_DataFile (Name = "<file path>”)
PSComputerName :
CimClass : root/CIMV2:CIM_DirectoryContainsFile
CimInstanceProperties : {GroupComponent, PartComponent}
CimSystemProperties : Microsoft.Management.Infrastructure.CimSystemProperties

 

What you’ll get is a list of every file on your system – showing folder in the GroupComponent (parent) and File in the PartComponent (child).

The documentation is totally accurate and totally useless as it explains that you get a parent-child relationship but not what’s in it!

I’d strongly recommend against using this class – there are easier ways to get the information.

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 - https://devblogs.microsoft.com/powershell/using-psscriptanalyzer-to-check-powershell-version-compatibility/

 

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.

File times

There are three pairs of file times that are available on files on Windows

PS> Get-ChildItem -Path C:\test\Newoutdata01.txt | select *time*

CreationTime : 14/04/2019 17:28:41
CreationTimeUtc : 14/04/2019 16:28:41
LastAccessTime : 14/04/2019 17:28:41
LastAccessTimeUtc : 14/04/2019 16:28:41
LastWriteTime : 25/02/2019 17:42:49
LastWriteTimeUtc : 25/02/2019 17:42:49

 

This is how to modify those times

Get-ChildItem -Path C:\test\*.txt |

ForEach-Object {

$date = (Get-Date).AddMonths( -(Get-Random -Maximum 5 -Minimum 1) )

Set-ItemProperty -Path $_.FullName -Name CreationTime -Value $date

Set-ItemProperty -Path $_.FullName -Name LastAccessTime $date.AddDays((Get-Random -Maximum 5 -Minimum 1))

Set-ItemProperty -Path $_.FullName -Name LastWriteTime $date.AddDays((Get-Random -Maximum 8 -Minimum 2))

}

 

Use Get-ChildItem to iterate through the files. For each file use Set-Property to set the value on the appropriate property. The values I’m using are purely random – you’d normally use a known value.

 

The *Utc version of the properties will be automatically set based on its corresponding property

Get wireless networks

I saw a question on how to get wireless networks which got me thinking about string handling and working with legacy command line utilities. I also wanted to compare the options available in Windows PowerShell and PowerShell Core.

 

First up is a relatively brute force approach. This approach works is relatively easy to understand and maintain but isn’t very elegant.

function get-wlan {
[CmdletBinding()]
param()

$networks = netsh wlan show networks mode=bssid

$networks | Select-Object -Skip 4 |
Where-Object {$_ -ne ''} |
ForEach-Object {

if ($psitem.StartsWith('SSID')) {
$temp = $psitem -split ':'
Write-Verbose -Message $temp[1]
$network = $($temp[1].Trim())
}

if ($psitem.TrimStart().StartsWith('Authentication')){
$temp = $psitem -split ':'
Write-Verbose -Message $temp[1]
$authentication = $temp[1].Trim()
}

if ($psitem.TrimStart().StartsWith('Signal')){
$temp = $psitem -split ':'
Write-Verbose -Message $temp[1]
$signal = $temp[1].Trim()
}

if ($psitem.TrimStart().StartsWith('Radio type')){
$temp = $psitem -split ':'

$props = [ordered]@{
Network = $network
Authentication = $authentication
Type = $temp[1].Trim()
Signal = $signal
}
New-Object -TypeName PSobject -Property $props
}
}
}

 

Start by using netsh to get the wireless networks. Skip the first 4 lines (run the netsh command by itself to see what’s being skipped) and filter out blank lines (empty strings). For each line check if it’s of interest and if so split on the : character and take the data into a variable. On the radio type line create an object and output.