PowerShell for Windows Admins


May 23, 2019  1:20 PM

Create a symbolic link

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

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

May 21, 2019  12:26 PM

PowerShell Core v6.2.1

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

PowerShell Core v6.2.1 has been released – https://github.com/PowerShell/PowerShell/releases

as has v6.1.4

The new versions are to primarily fix the Security Vulnerability CVE-2019-0733 – https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0733

v6.2.1 also enables tab completion for functions


May 19, 2019  12:56 PM

Windows Server 2019 updates with CIM

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, Powershell

Windows Server 2019 updates with CIM remain the same as all server versions post Windows Server 2016. This code will check for and install any updates. Micorosft Update or WSUS will be used depending on how your system is configured

$au = Invoke-CimMethod -Namespace root/microsoft/windows/windowsupdate -ClassName MSFT_WUOperations -MethodName ScanForUpdates -Arguments @{SearchCriteria=”IsInstalled=0″}
$au.Updates

if ($au.Updates.Length -gt 0) {
Invoke-CimMethod -Namespace root/microsoft/windows/windowsupdate -ClassName MSFT_WUOperations -MethodName InstallUpdates -Arguments @{Updates = $au.Updates}
}
else {
Write-Warning “No updates available”
}

This code should work on Server 1709, 1803, 1809 and Windows Server 2019.

It won’t work on Windows Server 2016 as the CIM classes were changed post Windows Server 2016


May 19, 2019  12:46 PM

WSL improvements

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Windows 10

Windows Subsystem for Linux – WSL improvements have been recently announced – https://devblogs.microsoft.com/commandline/

WSL 2 is on the way which will allow more Linux apps in WSL including Docker

A Linux kernel will ship with Windows especially tuned for WSL 2

WSL 2 will be much faster and have full system call compatibility.

A new console – now called a Terminal (more Linux terminology) will also become available for WSL, Windows Command prompt and most importantly PowerShell. It’ll feature multiple tabs

Windows Terminal will be shipped via the Windows Store  – do I really want to access an online Store for server software?


May 6, 2019  1:02 PM

Modifying hashtables

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

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.


April 30, 2019  3:06 PM

PowerShell SSH

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

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.


April 30, 2019  1:48 PM

Count

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

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


April 30, 2019  7:01 AM

Where-Object options

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

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!

The 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.


April 29, 2019  1:52 PM

Opposing Automation

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Philosophy, Powershell

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.


April 29, 2019  12:36 PM

File rename

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

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


Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: