PowerShell for Windows Admins


July 9, 2017  1:42 PM

Office holidays

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Office holidays are a great thing. They usually occur on public holidays. There’s a web site – www.officeholidays.com – you can use to discover the public holidays in your country. 133 countries are available – http://www.officeholidays.com/countries/index.php.

You can also use PowerShell to extract the information

$uri = "http://www.officeholidays.com/countries/united_kingdom/index.php"
 $html = Invoke-WebRequest -Uri $uri
 $holidays = ($html.ParsedHtml.getElementsByTagName("table") | 
 where ClassName -eq 'list-table' | 
 select -ExpandProperty InnerText) -split "`n"

$holidays.Count

$hols = foreach ($holiday in $holidays[1..($holidays.Count -1)]){
 $x = $holiday -split " "
 $y = $x[0] -split "day"
 
 $props = [ordered]@{
 DayOfWeek = "$($y[0])day"
 Day = $x[1]
 Month = $y[1]
 Holiday = $x[4..($x.Count-1)] -join " "
 }
 
 New-Object -TypeName PSObject -Property $props
 
 }

$hols | Format-Table -AutoSize –Wrap

Create a string to hold the uri of the country you want. You may need to use the website to discover how they format your country name. Use Invoke-WebRequest to get the html containing the holidays. You then need to get the inner text of the html.

DayDateHolidayComments
SundayJanuary 01 Jan 1 New Years Day First Monday if 1st is Saturday or Sunday
MondayJanuary 02 Jan 2 Day after New Years Day Scotland Only
MondayJanuary 02 Jan 2 New Years Day (observed) Except Scotland
TuesdayJanuary 03 Jan 3 New Year's Day (in lieu) Scotland Only
TuesdayFebruary 28 Feb 28 Pancake Tuesday Shrove Tuesday. Not a Public Holiday
WednesdayMarch 01 Mar 1 St Davids Day Wales Only. Not a public holiday
FridayMarch 17 Mar 17 St Patricks Day Northern Ireland Only
SundayMarch 26 Mar 26 Mothering Sunday Not a National Holiday
FridayApril 14 Apr 14 Good Friday Friday before Easter Sunday
MondayApril 17 Apr 17 Easter Monday Except Scotland
SundayApril 23 Apr 23 St George's Day England Only. Not a public holiday
MondayMay 01 May 1 Early May Bank Holiday First Monday in May
MondayMay 29 May 29 Spring Bank Holiday Last Monday in May
SundayJune 18 Jun 18 Father's Day 3rd Sunday in June. Not a public holiday
WednesdayJuly 12 Jul 12 Battle of the Boyne Northern Ireland Only
MondayAugust 07 Aug 7 August Bank Holiday Scotland Only. First Monday in August
MondayAugust 28 Aug 28 August Bank Holiday Last Monday in August (except Scotland)
SundayNovember 05 Nov 5 Guy Fawkes Night England Only. Not a public holiday
SundayNovember 12 Nov 12 Remembrance Sunday Not a public holiday. Sunday closest to 11 November
ThursdayNovember 30 Nov 30 St Andrews Day Scotland Only. If November 30 falls on a weekend, the next Monday is a bank ho liday instead
MondayDecember 25 Dec 25 Christmas Day
TuesdayDecember 26 Dec 26 Boxing Day

Its a single string but does contain new line characters so you can split it into an array.

Iterate through the array – skip the first line – and extract the day of the week, the day, month and holiday details. Create an object for output. Display the objects to see your holidays.

DayOfWeek Day Month Holiday 
 --------- --- ----- ------- 
 Sunday 01 January New Years Day First Monday if 1st is Saturday or Sunday 
 Monday 02 January Day after New Years Day Scotland Only 
 Monday 02 January New Years Day (observed) Except Scotland 
 Tuesday 03 January New Year's Day (in lieu) Scotland Only 
 Tuesday 28 February Pancake Tuesday Shrove Tuesday. Not a Public Holiday 
 Wednesday 01 March St Davids Day Wales Only. Not a public holiday 
 Friday 17 March St Patricks Day Northern Ireland Only 
 Sunday 26 March Mothering Sunday Not a National Holiday 
 Friday 14 April Good Friday Friday before Easter Sunday 
 Monday 17 April Easter Monday Except Scotland 
 Sunday 23 April St George's Day England Only. Not a public holiday 
 Monday 01 May Early May Bank Holiday First Monday in May 
 Monday 29 May Spring Bank Holiday Last Monday in May 
 Sunday 18 June Father's Day 3rd Sunday in June. Not a public holiday 
 Wednesday 12 July Battle of the Boyne Northern Ireland Only 
 Monday 07 August August Bank Holiday Scotland Only. First Monday in August 
 Monday 28 August August Bank Holiday Last Monday in August (except Scotland) 
 Sunday 05 November Guy Fawkes Night England Only. Not a public holiday 
 Sunday 12 November Remembrance Sunday Not a public holiday. Sunday closest to 11 November 
 Thursday 30 November St Andrews Day Scotland Only. If November 30 falls on a weekend, the next Monday is a bank holiday instead 
 Monday 25 December Christmas Day 
 Tuesday 26 December Boxing Day

I’ve used some rather ugly, brute force string handling. There should be a better way to extracting the data from the string but I need to think about it

July 7, 2017  8:03 AM

Variable as a where clause

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

A post on the forum about using a variable as a where clause looked interesting. What the user wanted to do was to define a variable that contained the filter to be used by Where-Object.

As an example consider filtering the output of Get-Service to display only services that are stopped

PS> Get-Service | where Status -eq "Stopped"

You have to revert to the old style syntax

PS> Get-Service | where {$_.Status -eq "Stopped"}

The answer then becomes to create a scriptblock for the filter

PS> $filter = {$_.Status -eq "Stopped"}
 PS> Get-Service | where $filter

You could then use the filters like this

function gs {
 param (
 [string]$status
 )

switch ($status){
 "Running" {$filter = {$_.Status -eq "Running"}; break} 
 "Stopped" {$filter = {$_.Status -eq "Stopped"}; break} 
 default {$filter = {$_.Status -like "*"}; break} 
 }

Get-Service | where $filter
}

PS> gs -status Running
PS> gs -status stopped
PS> gs -status *

If you need a variable as a where clause then use a scriptblock.


July 5, 2017  1:39 PM

File name starting with space

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Interesting question on the forum regarding finding files with a file name starting with a space.

First problem was creating some files to match the criteria. Renaming in file explorer didn’t work so back to PowerShell

PS> Rename-Item -Path C:\test\file1.txt -NewName "C:\test\ file1.txt"

PS> Rename-Item -Path C:\test\junk.txt -NewName "C:\test\ junk.txt"

Make sure you put the new name in quotes so the space is included as part of the file name

After that its actually quite easy

PS> Get-ChildItem -Path C:\test\ -Filter ' *.txt'


 Directory: C:\test


 Mode LastWriteTime Length Name
 ---- ------------- ------ ----
 -a---- 30/06/2017 15:38 16 file1.txt
 -a---- 30/06/2017 10:50 26 junk.txt

Use the Filter parameter and specify ‘ *.txt’ as the pattern you want to match


July 3, 2017  12:52 PM

Topics for PowerShell Summit 2018

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

The planning for Summit 2018 has started – to be honest it started before Summit 2017 opened. We’ve reached the stage where we need to start thinking about the broad topics for PowerShell Summit 2018.

What do you want to hear about? Not the session titles, content and speakers but the broad areas of content you want us to include. We can’t actually promise to cover everything requested because we’re dependent on whats submitted when we open our call for topics towards the end of the month.

Looking at the agenda for Summit 2017 we had these very broad groups

PowerShell tool making
DSC and DSC resources
PowerShell Github repository
PowerShell v6
Remoting
Testing – Pester
Azure
PowerShell Functions
JEA
PowerShell v6
PowerShell on Linux
PowerShell modules
Regular Expessions
MSDeploy
PKI
Powershell Jobs, Workflows and runspaces
Nano server
PowerShell cmdlets – compiled and script

Are there any we should drop? Is there a topic we should include – this far out we can commission a specific expert speaker to cover a topic if required. This is your opportunity to help shape Summit 2018. Let us know what you think


July 1, 2017  1:06 PM

MVP 2017

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

I received the email this afternoon stating I’d received an MVP award for 2017-2018 for my work with PowerShell. This is the 10th consecutive year I’ve been honoured with an MVP award. Its as big an honour to receive the 10th as it was to receive the first.


June 30, 2017  1:26 PM

Learning PowerShell

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A recent post on powershell.org – https://powershell.org/2017/06/22/taking-powershell-to-the-next-level/ – gave this path for learning PowerShell and becoming more proficient

Books:
Learn Powershell In A Month of Lunches
Learn Powershell Toolmaking in a month of Lunches
Windows Powershell In Action 3rd Edition

Online:
Advanced Tools And Scripting with Powershell 3.0 Jump Start
Writing Powershell Powershell DSC Resources And Configuration
Demo Code

To that list I’d add

Books: PowerShell in Depth 2nd edition – read it before PowerShell in Action

In parallel with the books and online courses find an area that you can work in – Active directory, Exchange, Windows admin, Hyper-V, SharePoint, SQL Server or whatever and start solving practical problems. You’ll learn more fro doing that than all the books you’ll ever read


June 30, 2017  12:59 PM

Finding a CIM class

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, Powershell, WMI

One of the problems you might find is finding a CIM class. You know its name but you don’t know which namespace its in.

The old WMI cmdlets allow you to search the namespaces recursively

PS> Get-WmiObject -Class Win32_Process -Namespace root -Recurse -List


 NameSpace: ROOT\CIMV2

Name Methods Properties
 ---- ------- ----------
 Win32_Process {Create, Terminat... {Caption, CommandLine, CreationClassName, CreationDate...}

But the CIM cmdlets don’t have this functionality. I’ve been meaning to do something about this for ages but finally got motivated by something I read while proof reading PowerShell in Action – yes its getting closer, much closer.

What I ended up with is these 2 functions

function get-namespace {
 [cmdletBinding()]
param ([string]$namespace = 'root') 
 Get-CimInstance -Namespace $namespace -ClassName '__NAMESPACE' |
 foreach {
 "$namespace\" + $_.Name
 get-namespace $("$namespace\" + $_.Name)
 }
 }

function find-cimclass {
 [cmdletBinding()]
param (
 [string]$namespace = 'root',
 [string]$classname
 )

$class = $null

## test namespace for class
 $class = Get-CimClass -Namespace $namespace -ClassName $classname -ErrorAction SilentlyContinue

if (-not $class) {
 $namespaces = get-namespace -namespace $namespace
 foreach ($name in $namespaces){
 $class = $null
 $class = Get-CimClass -Namespace $name -ClassName $classname -ErrorAction SilentlyContinue
 if ($class){break}
 }
 }

$class
 }

Find-Cimclass takes a namespace and class name as parameters. It tries to find the class in the given namespace. If it can’t find it then get-namespace is called to generate a list of namespaces to search. The function iterates over the collection of namespaces testing each one for the class. When it finds the class it returns the class information.

Get-namespace searches for all instances of the __Namespace class in the given namespace. it then recursively call itself to test each of those namespaces. That way you get the whole tree.

If you’re searching for a given class I recommend that you start at the root class to ensure that you test everywhere.


June 29, 2017  2:56 PM

Joining and Testing folder paths

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Last time I showed how to split folder paths to just leave the path – no filenames or drive information. What about the opposite task – joining and testing folder paths.

Here’s an example

$basepath = 'C:\Scripts'

$pathsTotest = 'Containers','HyperV', 'NanoServer', 'NoSuchFolder'

$pathsToTest | foreach {
 $path = Join-Path -Path $basepath -ChildPath $psitem 
 Test-Path -Path $path
 }

When you run this the paths to test are joined to the base path and then tested.

The output is

True
True
True
False

which isn’t the most informative you’ll ever see.

The output can easily be made more friendly

$basepath = 'C:\Scripts'

$pathsTotest = 'Containers','HyperV', 'NanoServer', 'NoSuchFolder'

$pathsToTest | foreach {
 $path = Join-Path -Path $basepath -ChildPath $psitem 
 
 $props = @{
 Path = $path
 Found = Test-Path -Path $path
 }

New-Object -TypeName psobject -Property $props
}

Create an object for the output using the full path you’re testing and the result

The output looks like this

 Path                     Found
 ----                    -----
 C:\Scripts\Containers    True
 C:\Scripts\HyperV        True
 C:\Scripts\NanoServer    True
 C:\Scripts\NoSuchFolder False

You could save the output as a CSV for later work if required


June 29, 2017  1:50 PM

Just the folders

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Lets say you have a bunch of files in nested folders but you want just the folders not the file or drive

Our files look like this

C:\Scripts\HyperV\Setup
 C:\Scripts\HyperV\attachdisks.ps1
 C:\Scripts\HyperV\get-mountedvhdDrive.ps1
 C:\Scripts\HyperV\invoke-CIMshutdown.ps1
 C:\Scripts\HyperV\set-loopbackswitch.ps1
 C:\Scripts\HyperV\Set-NestedVirtualisation.ps1
 C:\Scripts\HyperV\set-realswitch.ps1
 C:\Scripts\HyperV\Start-AllWindowsVMs.ps1
 C:\Scripts\HyperV\Stop-AllVMs.ps1
 C:\Scripts\HyperV\Stop-Lab.ps1
 C:\Scripts\HyperV\test-HotfixIpresence.ps1
 C:\Scripts\HyperV\Setup\Copy-Updates.ps1
 C:\Scripts\HyperV\Setup\Get-LicenseStatus.ps1
 C:\Scripts\HyperV\Setup\Install-RollUp.ps1
 C:\Scripts\HyperV\Setup\New-VirtualMachine.ps1
 C:\Scripts\HyperV\Setup\SCsetup.ps1
 C:\Scripts\HyperV\Setup\Set-VMconfig1.ps1
 C:\Scripts\HyperV\Setup\Set-VMconfig2.ps1
 C:\Scripts\HyperV\Setup\setup (2).ps1
 C:\Scripts\HyperV\Setup\setup.ps1
 C:\Scripts\HyperV\Setup\sysprep.txt

You can strip out the file names using Split-Path and the –Parent parameter

PS> Get-ChildItem .\HyperV\ -Recurse | foreach {Split-Path -Path $_.Fullname -Parent }
 C:\Scripts\HyperV
 C:\Scripts\HyperV
 C:\Scripts\HyperV
 C:\Scripts\HyperV
 C:\Scripts\HyperV
 C:\Scripts\HyperV
 C:\Scripts\HyperV
 C:\Scripts\HyperV
 C:\Scripts\HyperV
 C:\Scripts\HyperV
 C:\Scripts\HyperV
 C:\Scripts\HyperV\Setup
 C:\Scripts\HyperV\Setup
 C:\Scripts\HyperV\Setup
 C:\Scripts\HyperV\Setup
 C:\Scripts\HyperV\Setup
 C:\Scripts\HyperV\Setup
 C:\Scripts\HyperV\Setup
 C:\Scripts\HyperV\Setup
 C:\Scripts\HyperV\Setup
 C:\Scripts\HyperV\Setup

Split-Path also has a –NoQualifier parameter that strips of the drive – unfortunately its in a different parameter set to –Parent BUT you can use a pipeline

PS> Get-ChildItem .\HyperV\ -Recurse | foreach {Split-Path -Path $_.Fullname -Parent | Split-Path -NoQualifier }
 \Scripts\HyperV
 \Scripts\HyperV
 \Scripts\HyperV
 \Scripts\HyperV
 \Scripts\HyperV
 \Scripts\HyperV
 \Scripts\HyperV
 \Scripts\HyperV
 \Scripts\HyperV
 \Scripts\HyperV
 \Scripts\HyperV
 \Scripts\HyperV\Setup
 \Scripts\HyperV\Setup
 \Scripts\HyperV\Setup
 \Scripts\HyperV\Setup
 \Scripts\HyperV\Setup
 \Scripts\HyperV\Setup
 \Scripts\HyperV\Setup
 \Scripts\HyperV\Setup
 \Scripts\HyperV\Setup
 \Scripts\HyperV\Setup

If you just want the folders and don’t care about the files try

PS> Get-ChildItem -Path .\ -Recurse -Directory | 
 where FullName -Like "*HyperV*" | 
foreach {Split-Path -Path $_.Fullname -Parent | Split-Path -NoQualifier }
 \Scripts
 \Scripts\HyperV


June 28, 2017  12:42 PM

Location, location

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Just recently I’ve found my self repeatedly working through a location, location pattern.

cd C:\test\
 .\hello.ps1
 cd C:\Scripts\

The pattern consists of changing to another folder. Running some code and then changing back to the original folder – assuming you can remember it.

I then remembered the location cmdlets

PS> Get-Command *-Location | ft -a

CommandType Name Version Source
 ----------- ---- ------- ------
 Cmdlet Get-Location 3.1.0.0 Microsoft.PowerShell.Management
 Cmdlet Pop-Location 3.1.0.0 Microsoft.PowerShell.Management
 Cmdlet Push-Location 3.1.0.0 Microsoft.PowerShell.Management
 Cmdlet Set-Location 3.1.0.0 Microsoft.PowerShell.Management

The 2 useful ones in this working pattern are Push-Location and Pop-Location

Push-Location adds the current location to the location stack and moves you to a new location if you supply a path

Pop-Location pulls the topmost location from the location stack and moves you to that location

The pattern then becomes

PS> Get-Location

Path
 ----
 C:\Scripts

PS> Push-Location -Path C:\test\
 PS> Get-Location

Path
 ----
 C:\test

PS> .\hello.ps1
 Hello world!
 PS> Pop-Location

PS> Get-Location

Path
 ----
 C:\Scripts

Saves all that remembering stuff


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: