PowerShell for Windows Admins


December 16, 2017  9:20 AM

PowerShell v6: #9 Release candidate 2 features

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

A couple of features of PowerShell v6 release candidate 2 need commenting on.

Firstly, I was surprised when installing RC 2 on a Windows 10 machine (Insider build) that RC1 was removed. In the past you’ve been able to run numerous versions of PowerShell v6 side-by-side. This has consequences if the behaviour continues into the GA version and the 6.1 alpha/beta releases as you’ll have to choose between the stable, production version and the changeable new version.

Secondly, and this one was publicised by the PowerShell team, Pester is no longer installed by default with PowerShell v6. You can download the latest version of Pester from the PowerShell gallery. Not sure on thinking behind this decision but the download is easy so it’s not a deal breaker –you just have to remember to do it. May be best to use Save-Module for the download so you don’t have to perform the download if a new version of PowerShell v6 becomes available and overwrites the current version.

Next job is to install the OpenSSH optional feature and see how that goes. Be very interested to see if and how its updated as new versions of the Windows Insider builds are released.

December 15, 2017  2:28 PM

PowerShell v6: #8 Release candidate 2

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Release candidate 2 for PowerShell v6 is available for download from – https://github.com/PowerShell/PowerShell/releases

One worrying point is the the OpenSSH implementation which is required for remoting to and from Linux systems doesn’t appear to be any where near a release candidate – https://github.com/PowerShell/Win32-OpenSSH/releases. The latest release as of this writing is 0.0.24.0. The steps to install OpenSSH are still too complicated and time consuming. There is an installation script but it doesn’t complete all of the manual steps!

at the moment I’d really recommend that you only install OpenSSH where you need it and don’t consider it as a replacement for WinRM based remoting apart from a few narrow scenarios:

– Windows <-> Linux remoting

– Cross domain remoting

– Remoting to or from a non-domain system

Ducks need to be lined up if PowerShell v6 is going to GA in January 2018


December 13, 2017  9:51 AM

Using the Where method

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

You don’t usually see people using the where method. A recent question on the forums highlighted using the where method.

PowerShell deals in collections and will automatically create a collection of objects if the are multiple objects returned for instance

$procs = Get-Process

Iterating over a process can take time. PowerShell introduced 2 methods on collection to make this easier and much  faster to execute – where and foreach.

As an example

$Paths = @(
@{NodeName = ‘VMNAme1’; Instance = ‘Internal’ ; Path = ‘C:\Temp\’},
@{NodeName = ‘AnotherName’ ; Instance = ‘External’ ; Path = ‘C:\Temp2’},
@{NodeName = ‘VMNAME2’; Instance = ‘Internal’ ; Path = ‘C:\Temp1\’}
)

Get-ChildItem -Path $paths.where({$_.Nodename -eq $env:COMPUTERNAME}).Path

Depending on the machine on which you’re running you’ll get the appropriate folder.

The important bit is the $_.Nodename . You need to use the old style Where syntax when using the where method.


December 10, 2017  7:30 AM

Avoid the truncation in the display

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A question came up regarding my recent post about using the file system COM object to get folder sizes. The question was about the folder path and how you could see the whole path. In other words how to avoid the truncation in the display and the three dots.

When PowerShell displays objects it will automatically use a table format if the object has four properties or less. It uses a list if there are more properties. This behaviour can be overridden by PowerShell’s formatting system. This behaviour can lead to truncation of one or more fields.

This is the function to get folder sizes

function Get-FolderSize {
 [CmdletBinding()]
param (
 [string]$path = 'C:\MyData'
 )

if (-not (Test-Path -Path $path)){
 Throw "$path - Path not found"
 }

$fso = New-Object -ComObject "Scripting.FileSystemObject"

Get-ChildItem -Path $path -Directory -Recurse |
foreach {
 
 $size = ($fso.GetFolder("$($PSItem.FullName)")).Size
 
 $props = [ordered]@{
 Name = $PSItem.Name
 Created = $PSItem.CreationTime
 FilePath = $PSItem.FullName
 SizeMB = [math]::Round( ($size / 1mb), 2)
 }

New-Object -TypeName PSObject -Property $props
 }

}

If you just use the function as is

PS> Get-FolderSize -path C:\MyData\OneDrive\Data\Scripts

You can get truncated output – and you don’t see the size!

Office-Excel      10/02/2017 19:16:29 C:\MyData\OneDrive\Data\Scripts\Office-...
Office-OneNote    10/02/2017 19:16:26 C:\MyData\OneDrive\Data\Scripts\Office-...
Office-Outlook    10/02/2017 19:16:29 C:\MyData\OneDrive\Data\Scripts\Office-...
Office-PowerPoint 10/02/2017 19:16:29 C:\MyData\OneDrive\Data\Scripts\Office-...
Office-Visio      10/02/2017 19:18:00 C:\MyData\OneDrive\Data\Scripts\Office-...
Office-Word       10/02/2017 19:16:23 C:\MyData\OneDrive\Data\Scripts\Office-...

You could use Format-List

PS> Get-FolderSize -path C:\MyData\OneDrive\Data\Scripts | Format-List

which means you get lots of displays like this:

Name : Event Filters
Created : 10/02/2017 19:16:31
FilePath : C:\MyData\OneDrive\Data\Scripts\WMICookBook\PowerEvents\PowerEvents\Samples\Event 
 Filters
SizeMB : 0.02
Using Format-Table with the –wrap parameter will avoid the truncation

PS> Get-FolderSize -path C:\MyData\OneDrive\Data\Scripts | Format-Table –AutoSize –Wrap

Samples         10/02/2017 19:16:31 C:\MyData\OneDrive\Data\Scripts\WMICookBook\PowerEvent
 s\PowerEvents\Samples 
Event Consumers 10/02/2017 19:16:31 C:\MyData\OneDrive\Data\Scripts\WMICookBook\PowerEvent
 s\PowerEvents\Samples\Event Consumers 
Event Filters   10/02/2017 19:16:31 C:\MyData\OneDrive\Data\Scripts\WMICookBook\PowerEvent
 s\PowerEvents\Samples\Event Filters 
ConfigMgr       10/02/2017 19:16:31 C:\MyData\OneDrive\Data\Scripts\WMICookBook\PowerEvent
 s\PowerEvents\Samples\Event Consumers\ConfigMgr

Notice that the Size is again cut off. You need to ensure that your output pane in ISE or the PowerShell console is wide enough to display all the fields or PowerShell will truncate the display – without telling you.


December 6, 2017  9:19 AM

PowerShell v6: #7 Module paths

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

There is a very significant gap between the functionality available in PowerShell v6 as opposed to PowerShell v5.1. In part this is due to the underlying version of .NET but mainly to the defined module paths in the two versions.

In PowerShell v5.1 I have:

PS> $env:PSModulePath -split ‘;’
C:\Scripts\Modules
C:\Users\Richard.MANTICORE\Documents\WindowsPowerShell\Modules
C:\Program Files\WindowsPowerShell\Modules
C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules

In PowerShell v6 I have

PS C:\scripts> $env:PSModulePath -split ‘;’
C:\Users\Richard.MANTICORE\Documents\PowerShell\Modules
C:\Program Files\PowerShell\Modules
c:\program files\powershell\6.0.0-rc\Modules

The vast majority of the module supplied with PowerShell v5.1 reside in C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules which PowerShell v6 can’t see.

Unless you add it in yourself

$env:PSModulePath = $env:PSModulePath + ‘;C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules’

I’ve appended the PowerShell v5.1 modules to the PowerShell v6 module path.

There’s still no guarantee that the modules will work – it depends on the module code and if it accesses .NET classes that aren’t available in .NET core.

You’ll have to use trial and error to determine what works. For instance:

Get-NetAdapter
Get-Volume
Get-Partition

all work. BUT they’re from CDXML modules based on CIM classes not binary modules. As a rule thumb I’d expect the CIM based modules to just work. Binary module will definitely be trial and error.


December 5, 2017  3:23 PM

PowerShell v6: #6 Windows compatibility

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

PowerShell v1 through v5.1 have been based on the full .NET framework. PowerShell v6 is based on .NET core which is a cross platform subset of .NET that’s available for Windows, Linux and mac. This has meant that Powershell v6 on Windows is a poor relation of PowerShell v5.1 in terms of the functionality available – for instance the Active Directory module won’t run on PowerShell v6. This makes v6 a backward step for administering Windows. Steps are being taken that will improve Windows compatibility of PowerShell v6.

The .NET core team are releasing a Windows compatibility pack – see https://blogs.msdn.microsoft.com/dotnet/2017/11/16/announcing-the-windows-compatibility-pack-for-net-core/ for what’s likely to be in it.

A number of cmdlets including:

WMI cmdlets

Eventlog cmdlets

PerCounter cmdlets

are ear marked for being ported to PowerShell v6 using the compatibility pack. When this happens and what else is will be included is unsure. You can follow the issue at https://github.com/PowerShell/PowerShell/issues/5603


December 2, 2017  4:52 AM

Hyper-V VM start time

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Hyper-V, Powershell

Its fairly easy to see how long a VM has been running – but how do you know the Hyper-V VM start time?

In Hyper-V the VM uptime is easy to find

PS> Get-VM | where State -eq 'Running'

Name        State CPUUsage(%) MemoryAssigned(M) Uptime          Status             Version
----       ----- ----------- ----------------- ------           ------             -------
W16AS01    Running 6         1246              00:12:18.6480000 Operating normally 8.0
W16CN01    Running 0         538               00:09:18.2180000 Operating normally 8.2
W16DC01    Running 0         940               00:15:19.1550000 Operating normally 8.0
W17035CN01 Running 0         540               00:06:17.7980000 Operating normally 8.2
W1709CN01  Running 0         512               00:03:15.8540000 Operating normally 8.2

Sometimes you might want to know when the VM was started

The Uptime property is a TimeSpan so you can calculate the start time

PS> $now = Get-Date
PS> Get-VM | where State -eq 'Running' | select Name, @{N='StartTime'; E={$now - $_.Uptime}}

Name       StartTime
----       ---------
W16AS01    02/12/2017 10:23:15
W16CN01    02/12/2017 10:26:16
W16DC01    02/12/2017 10:20:15
W17035CN01 02/12/2017 10:29:16
W1709CN01  02/12/2017 10:32:18

Once you’ve added the calculated property you can use like any other property

PS> Get-VM | where State -eq 'Running' | select Name, @{N='StartTime'; E={$now - $_.Uptime}} | sort StartTime

Name       StartTime
----       ---------
W16DC01    02/12/2017 10:18:46
W16AS01    02/12/2017 10:21:47
W16CN01    02/12/2017 10:24:47
W17035CN01 02/12/2017 10:27:48
W1709CN01  02/12/2017 10:30:50

Using calculated fields like this is a handy technique for changing the way data is displayed.


November 29, 2017  10:21 AM

Get Folder sizes

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

One problem that comes up quite often is how do you get folder sizes. One option is use Measure-Object but the problem with that approach is that its going to be a pretty slow process if you have a lot of folders. PowerShell doesn’t have a method of directly getting the folder size and you have to count through all of the sub-folders which becomes a very repetitive exercise.

If you’re prepared to use an old style VBScript approach you can use the FileSystem COM object like this

function Get-FolderSize {
 [CmdletBinding()]
param (
 [string]$path = 'C:\MyData'
 )

if (-not (Test-Path -Path $path)){
 Throw "$path - Path not found"
 }

$fso = New-Object -ComObject "Scripting.FileSystemObject"

Get-ChildItem -Path $path -Directory -Recurse |
foreach {
 
 $size = ($fso.GetFolder("$($PSItem.FullName)")).Size
 
 $props = [ordered]@{
 Name = $PSItem.Name
 Created = $PSItem.CreationTime
 FilePath = $PSItem.FullName
 SizeMB = [math]::Round( ($size / 1mb), 2)
 }

New-Object -TypeName PSObject -Property $props
 }

}

 

use as

Get-FolderSize -path <folder path>

if you want the subfolders immediately after their parent then add a sort

Get-FolderSize -path <folder path> | sort FilePath

and if you want to order by size then

Get-FolderSize -path <folder path> | sort SizeMB -Descending


November 28, 2017  10:25 AM

Windows update module

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Windows 10, Windows Server

A Windows Update module is available on Windows versions 1709 and later. This includes Windows 10 Fall Creators Update, Windows Server 1709 and Windows Insider previews (Server and Client) post the 1709 release.

The module supplies the following cmdlets

Get-WUAVersion
Get-WUIsPendingReboot
Get-WULastInstallationDate
Get-WULastScanSuccessDate
Install-WUUpdates
Start-WUScan

The module is a CDXML module based on the root/Microsoft/Windows/WindowsUpdate/MSFT_WUOperations CIM class I discussed in a recent post.

If you’re working with these newer versions of Windows this module makes patching a good bit simpler. It shouldn’t be that much effort to backport the module using the MSFT_WUOperationsSession CIM class available on Windows Server 2016.


November 27, 2017  12:10 PM

Get an AD user’s manager

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Active Directory, Powershell

Interesting question on the forum about finding the manager for a given user in AD – assuming the Manager field is populated of course. If you’ve not worked with the AD cmdlets this is a good introduction to some of their quirks. This is how you get an AD user’s manager.

You need the manager property on the AD user account but that’s not one of the default properties that’s returned so you need to use the –Propertie parameter to ensure you get your data. Assuming you have a csv file with userids that looks like this

id userid
 -- ------
 1 DonBrown
 2 DonFox
 3 JamesBrown
 4 JamesBlack

You can use this code

Import-Csv -Path .\names.csv |
foreach {
 $user = Get-ADUser -Identity $_.userid -Properties Manager
 $_ | Add-Member -MemberType NoteProperty -Name 'Manager' -Value $user.Manager
 $_
}

This returns the distinguished name of the manager

id userid    Manager 
-- ------    ------- 
1 DonBrown   CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org
2 DonFox     CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org
3 JamesBrown CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org
4 JamesBlack CN=HARRIS Fred,OU=UserAccounts,DC=Manticore,DC=org

if you prefer to have their name then you need an extra step

Import-Csv -Path .\names.csv |
foreach {
 $user = Get-ADUser -Identity $_.userid -Properties Manager
 $manager = Get-ADUser -Identity $user.Manager
 
 $_ | Add-Member -MemberType NoteProperty -Name 'Manager' -Value $manager.Name
 $_
}

Which gives output like this

id userid    Manager 
-- ------    ------- 
1 DonBrown   HARRIS Fred
2 DonFox     HARRIS Fred
3 JamesBrown HARRIS Fred
4 JamesBlack HARRIS Fred

If you need to output the data to a csv file then just add Export-Csv

Import-Csv -Path .\names.csv |
foreach {
 $user = Get-ADUser -Identity $_.userid -Properties Manager
 $manager = Get-ADUser -Identity $user.Manager
 
 $_ | Add-Member -MemberType NoteProperty -Name 'Manager' -Value $manager.Name
 $_
 } | Export-Csv -Path names2.csv –NoTypeInformation


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: