PowerShell for Windows Admins


July 11, 2018  8:16 AM

Windows Server 2008 EoL

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Windows 2008, Windows 2008 R2

Windows Server 2008 EoL on 14 January 2020

Windows Server 2008 and 2008 R2 will be end of life and no longer supported from 14 January 2020 – https://cloudblogs.microsoft.com/windowsserver/2018/07/10/its-sunset-time-for-windows-server-2008-and-2008-r2/

There’s plenty of time to think about migrating off of the 2008 family but don’t leave it to the last minute.

Server 2008 had PowerShell v1 and 2008 R2 introduced PowerShell v2. They were good server products but its time to replace them if you haven’t already.

July 9, 2018  10:37 AM

Windows activation

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

enum Licensestatus {
Unlicensed = 0
Licensed = 1
OOBGrace = 2
OOTGrace = 3
NonGenuineGrace = 4
Notification = 5
ExtendedGrace = 6
}

Get-CimInstance -ClassName SoftwareLicensingProduct -Filter “PartialProductKey IS NOT NULL” |
select Name, ApplicationId, @{N=’LicenseStatus’; E={[LicenseStatus]$_.LicenseStatus} }


July 9, 2018  4:31 AM

CPU intensive processes

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

How do you find the most CPU intensive processes?

If you want to know the processes that are taking most CPU on your system right now then use Get-Process

PS> Get-Process | sort CPU -Descending | select -First 10

Will display the 10 processes that are running and are using the most CPU – see the CPU( s ) field.

Couple of issues with this approach. First only running processes are shown. If a process starts and runs to completion between 2 calls to Get-Process you’ll never know it happened. Especially relevant if the process runs over night. Second, Get-Process produces a lot of information you don’t necessarily need for this task.

The alternative is to use performance counters.

PS> Get-Counter -ListSet *Process* | select CounterSetName, Description

Lists possible counter sets you can use. The Process set looks like a good possibility:

PS> Get-Counter -ListSet Process | select -ExpandProperty Counter
\Process(*)\% Processor Time
\Process(*)\% User Time
\Process(*)\% Privileged Time
\Process(*)\Virtual Bytes Peak
\Process(*)\Virtual Bytes
\Process(*)\Page Faults/sec
\Process(*)\Working Set Peak
\Process(*)\Working Set
\Process(*)\Page File Bytes Peak
\Process(*)\Page File Bytes
\Process(*)\Private Bytes
\Process(*)\Thread Count
\Process(*)\Priority Base
\Process(*)\Elapsed Time
\Process(*)\ID Process
\Process(*)\Creating Process ID
\Process(*)\Pool Paged Bytes
\Process(*)\Pool Nonpaged Bytes
\Process(*)\Handle Count
\Process(*)\IO Read Operations/sec
\Process(*)\IO Write Operations/sec
\Process(*)\IO Data Operations/sec
\Process(*)\IO Other Operations/sec
\Process(*)\IO Read Bytes/sec
\Process(*)\IO Write Bytes/sec
\Process(*)\IO Data Bytes/sec
\Process(*)\IO Other Bytes/sec
\Process(*)\Working Set – Private

Of those \Process(*)\% Processor Time looks to be what we want.

If you just run

PS> Get-Counter -Counter ‘\Process(*)\% Processor Time’

You’ll get all counters. But you can’t give a process name so you’ll have to do a bit of digging into the object returned by Get-Counter

PS> Get-Counter -Counter ‘\Process(*)\% Processor Time’ | gm

TypeName: Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet

Name MemberType Definition
—- ———- ———-
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
CounterSamples Property Microsoft.PowerShell.Commands.GetCounter.PerformanceCo
Timestamp Property datetime Timestamp {get;set;}
Readings ScriptProperty System.Object Readings {get=$strPaths = “”…

Timestamp is the time that you retrieved the data. Countersamples gives the data for each process:

PS> $samples = Get-Counter -Counter ‘\Process(*)\% Processor Time’
PS> $samples.Timestamp

09 July 2018 10:58:34

PS> $samples.CounterSamples | select -First 1 | Format-List

Path : \\w510w10\process(idle)\% processor time
InstanceName : idle
CookedValue : 803.827924975895

Just selecting the top 10 processes won’t work.

PS> $samples.CounterSamples | sort CookedValue -Descending | select -f 10 | Format-List

Path : \\w510w10\process(_total)\% processor time
InstanceName : _total
CookedValue : 805.382717867531

Path : \\w510w10\process(idle)\% processor time
InstanceName : idle
CookedValue : 803.827924975895

Path : \\w510w10\process(powershell#1)\% processor time
InstanceName : powershell
CookedValue : 1.55479289163616

Because you get the _total and idle counts. So you need to skip them

PS> $samples.CounterSamples | sort CookedValue -Descending | select -Skip 2 -First 10 | Format-List

By default Get-Counter takes 1 sample. If you want to multiple samples you can use the –Continuous switch which will sample continuously. Use –SampleInterval to determine the number of seconds between samples.

Alternatively, use –MaxSamples and –SampleInterval to control the number of samples in a given period but you get a sample at each sampling interval.

However you decide to sample the data you’re going to need to preserve it for future analysis. Probably easiest is to output the top process at each sampling interval to a CSV file

$samplecount = 0

while ($true) {
$samplecount++

$samples = Get-Counter -Counter ‘\Process(*)\% Processor Time’
$counter = $samples.CounterSamples | sort CookedValue -Descending | select -Skip 2 -First 1

$props = [ordered]@{
Time = $samples.Timestamp
Process = $counter.InstanceName
Value = $counter.CookedValue
}

New-Object -TypeName PSObject -Property $props |
Export-Csv -Path C:\test\counters.csv -Append -NoTypeInformation

if ($samplecount -ge 10){break}

Start-Sleep -Seconds 10
}

You may see error messages like this:

Get-Counter : The data in one of the performance counter samples is not valid. View the Status property for each PerformanceCounterSample object to make sure it contains valid data.
At line:6 char:14
+ $samples = Get-Counter -Counter ‘\Process(*)\% Processor Time’
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidResult: (:) [Get-Counter], Exception
+ FullyQualifiedErrorId : CounterApiError,Microsoft.PowerShell.Commands.GetCounterCommand

So you may not your data exactly 10 seconds apart.

You could use –ErrorAction to silently continue and suppress the messages. You’ll still get your 10 samples.

You can run the script from a job or put an outer loop that starts sampling every minute, 10 minutes or whatever you need.

If you see different process showing in your samples then your machine’s resources aren’t been hogged by a single process.


July 5, 2018  10:43 AM

PowerShell not equal

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

The Powershell not equal operator is –ne. This how you use it.

When dealing with numbers:

PS> $a = 3
PS> $b = 5
PS> $a -ne $b
True

When dealing with strings you have a bit more choice:

PS> $a = ‘abcd’
PS> $b = ‘efgh’
PS> $a -ne $b
True

By default PowerShell is case insensitive so

PS> $a = ‘abcd’
PS> $b = ‘ABCD’
PS> $a -ne $b
False

If the only difference is case then –ne reports they are the same.

If you need a case insensitive test use –cne

PS> $a = ‘abcd’
PS> $b = ‘ABCD’
PS> $a -cne $b
True

Because there are case differences the strings are different so you get a result of True

If you need a case insensitive test use –ine so you know you’re ignoring case

PS> $a = ‘abcd’
PS> $b = ‘ABCD’
PS> $a -ine $b
False

The major problem with use not equal is that you’re in double negative

if X –ne Y then it means they are the same if the result is false

PS> $a = 3
PS> $b = 3
PS> $a -ne $b
False

Its always easier to comprehend a test for equality rather than inequality especially if you’re nesting tests.


July 4, 2018  5:34 AM

Variable squeezing

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

When you’re developing code interactively how often do you set a variable and then display it to test the result? You can do that in one go using variable squeezing.

Normally you’d do:

PS> $test = 1
PS> $test
1

Set the variable and display the contents as two separate actions.

By enclosing the first statement in parentheses you accomplish both tasks

PS> ($test2 = 2)
2

Great for interactive work but you don’t want in in scripts as you’d be getting output every time you set a variable value!


July 1, 2018  9:52 AM

MVP renewal

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

I received the email today stating that my MVP award has been renewed for another year – the 11th.

I’ve watched the PowerShell community grow, change and evolve over the last 12 years or so and its been an interesting and fascinating journey.

Thank you to everyone who takes the time to read my blog, listen to me speak or read my books. Without you this community wouldn’t be as strong


June 30, 2018  2:05 PM

PowerShell if not

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

When you’re using an if statement you’re usually testing for a positive so how do you do a PowerShell if not

There a few scenarios to cover. The simplest is if you’re testing a boolean:

PS> $x = $true

if ($x) {‘Yes’} else {‘No’}
Yes

In an if statement the test ($x) is evaluated and must give a true or false answer. If true then Yes else No

Let’s make turn the test into a not test. You can use ! or –not as you prefer

PS> $x = $true

if (!$x) {‘Yes’} else {‘No’}
if (-not $x) {‘Yes’} else {‘No’}
No
No

If the value is already false

PS> $x = $false

if (!$x) {‘Yes’} else {‘No’}
if (-not $x) {‘Yes’} else {‘No’}
Yes
Yes

Be careful as you’re getting into double negative territory which is always a headache when you come to review the code at some time in the future.

If you’re dealing with numeric values

PS> $x = 5

if ($x -ne 5) {‘Yes’} else {‘No’}
if ($x -lt 5) {‘Yes’} else {‘No’}
if ($x -gt 5) {‘Yes’} else {‘No’}
No
No
No

Be careful with the first one as you’ll only get Yes if $x –ne 5. Back to double negative thinking.

Notice you can’t do this

if ($x -not -lt 5) {‘Yes’} else {‘No’}

Double operators don’t work. You get an error about

At line:1 char:8
+ if ($x -not -lt 5) {‘Yes’} else {‘No’}
+ ~~~~
Unexpected token ‘-not’ in expression or statement.
At line:1 char:8

among other things.

Nulls can be tricky

PS> $x = $null

if ($x) {‘Yes’} else {‘No’}
if (!$x) {‘Yes’} else {‘No’}

No
Yes

A null value will evaluate as false unless you –not it – another double negative situation.

Using if not is relatively straight forward. Just make sure you have the logic thought through to deal with double negatives correctly.


June 30, 2018  1:23 PM

PowerShell sleep

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Getting PowerShell to sleep or pause during execution can sometimes be useful. This is how you can do a PowerShell sleep.

The starting point is Start-Sleep

PS> Get-Command Start-Sleep -Syntax

Start-Sleep [-Seconds] <int> [<CommonParameters>]

Start-Sleep -Milliseconds <int> [<CommonParameters>]

You can set a time in seconds or milliseconds that you want PowerShell to pause

PS> 1..5 | foreach {
Get-Date
Start-Sleep -Seconds 30
}

30 June 2018 20:08:02
30 June 2018 20:08:32
30 June 2018 20:09:02
30 June 2018 20:09:32
30 June 2018 20:10:02

If you need your script to sleep for more than a minute you still need to use seconds. So to sleep for 3 minutes use:

Start-Sleep -Seconds 180

If you want the pause to be under manual control use the pause function:

PS> 1..5 | foreach {
Get-Date
pause
}

30 June 2018 20:17:57
Press Enter to continue…:
30 June 2018 20:18:01
Press Enter to continue…:
30 June 2018 20:18:16
Press Enter to continue…:
30 June 2018 20:18:33
Press Enter to continue…:
30 June 2018 20:18:43
Press Enter to continue…:

The drawback is that it’s a manual process so not good for soemthing running in the middle of the night and you get the display contaminated with “press enter to continue…” statements.

Pause is a function that PowerShell automatically creates. It has a simple definition:

$null = Read-Host ‘Press Enter to continue…’


June 30, 2018  1:52 AM

PowerShell string concatenation

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

PowerShell string concatenation is performed using the concatenation operator which is +

PS> $a = ‘1234’
PS> $b = ‘5678’
PS> $c = $a + $b
PS> $c
12345678

When you concatenate 2 strings you’re creating a new string not adding extra characters to the end of the first string.

My preference is to use variable substitution rather than straight concatenation

PS> $d = “$a$b”
PS> $d
12345678

If your string has double quote delimiters you can substitute the value of variables as shown. Be careful though because substitution doesn’t work if you use single quotes:

PS> $e = ‘$a$b’
PS> $e
$a$b

One last trick with strings is the ability to use the multiply operator to generate strings

PS> ‘123’ * 5
123123123123123

PS> 1..10 | foreach {‘*’ * $_}
*
**
***
****
*****
******
*******
********
*********
**********


June 29, 2018  1:37 PM

Write-Host

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Write-Host has had a bad press over the years culminating in the infamous saying “if you use Write-Host a puppy will die” or words to that effect.

So what’s the fuss about?

Let’s take some code

Write-Host -Object “starting”

function t1 {
Write-Host -Object “In the function”

2+2
}

Write-Host -Object “pre-function”

t1

Write-Host -Object “post-function”

It’s not quite the world’s most fantastic piece of code but it’ll do for now

When you run this code you see output like this:

starting
pre-function
In the function
4
post-function

The problem is that you can’t easily separate the code output from the Write-Host messages as Write-Host does what it says and writes your message direct to the host.

What you should be doing is using one of the more specific Write cmdlets:

Write-Debug
Write-Error
Write-Host
Write-Information
Write-Progress
Write-Verbose
Write-Warning

Maybe in this case Write-Information

Write-Information -MessageData “starting” -InformationAction Continue

function t1 {
Write-Information -MessageData “In the function” -InformationAction Continue

2+2
}

Write-Information -MessageData “pre-function” -InformationAction Continue

t1

Write-Information -MessageData “post-function” -InformationAction Continue

with output of

starting
pre-function
In the function
4
post-function

But this looks just like Write-Host. In PowerShell v5 and later Write-Host is a wrapper for Write-Information

To see the difference remove the –InformationAction parameter and use this horribly contrived code

$inf = @()

Write-Information -MessageData “starting” -InformationVariable a
$inf += $a

function t1 {
Write-Information -MessageData “In the function” -InformationVariable a
$inf += $a

2+2
}

Write-Information -MessageData “pre-function” -InformationVariable a
$inf += $a

t1

Write-Information -MessageData “post-function” -InformationVariable a
$inf += $a

If you look at the contents of $inf you see the messages

starting
pre-function
post-function

Notice you’ve lost the message from within the function – we’ll come back to that another time

You have a record with some interesting data

PS> $inf | gm

TypeName: System.Management.Automation.InformationRecord

Name MemberType Definition
—- ———- ———-
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Computer Property string Computer {get;set;}
ManagedThreadId Property uint32 ManagedThreadId {get;set;}
MessageData Property System.Object MessageData {get;}
NativeThreadId Property uint32 NativeThreadId {get;set;}
ProcessId Property uint32 ProcessId {get;set;}
Source Property string Source {get;set;}
Tags Property System.Collections.Generic.List[string] Tags {get;}
TimeGenerated Property datetime TimeGenerated {get;set;}
User Property string User {get;set;}

For instance

PS> $inf | select TimeGenerated, MessageData

TimeGenerated MessageData
————- ———–
29/06/2018 20:25:44 starting
29/06/2018 20:25:44 pre-function
29/06/2018 20:25:44 post-function

could be useful for tracing progress

There’s a lot more in the write cmdlets that we’ll come back to another time


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: