Get Folder sizes
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
Windows update module
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
Get an AD user’s manager
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
PowerShell v6: #5 Get-Uptime
One new feature of PowerShell v6 (its actually been available since alpha 13 but I’d missed it) is the Get-Uptime cmdlet
PS C:\scripts> Get-Uptime Days : 0 Hours : 2 Minutes : 57 Seconds : 6 Milliseconds : 0 Ticks : 106260000000 TotalDays : 0.122986111111111 TotalHours : 2.95166666666667 TotalMinutes : 177.1 TotalSeconds : 10626 TotalMilliseconds : 10626000
You get a timespan object returned for the uptime.
If you want to get something similar in Windows PowerShell you need to access the Win32_OperatingSystem CIM class
PS> (Get-Date) - (Get-CimInstance -ClassName Win32_OperatingSystem | select -ExpandProperty LastBootUpTime) Days : 0 Hours : 3 Minutes : 7 Seconds : 7 Milliseconds : 850 Ticks : 112278501750 TotalDays : 0.129951969618056 TotalHours : 3.11884727083333 TotalMinutes : 187.13083625 TotalSeconds : 11227.850175 TotalMilliseconds : 11227850.175
Ah! We’re back to the days of the PowerShell one liner – though to be accurate it should one pipeliner.
Remember that Windows 8, 8.1 and 10 will only reset LastBootUpTime on a restart. If you use Shutdown on the Start menu its effectively hibernating which explains why these versions of windows boot up so quickly.
PowerShell v6: #4 profiles
Windows PowerShell (v1-v5.1) has always used profiles to configure your PowerShell session. You need execution policy set to something other than restricted so that the profile script can run.
You can have up to 4 profiles:
Description Path ----------- ---- Current User, Current Host $Home\[My ]Documents\WindowsPowerShell\Profile.ps1 Current User, All Hosts $Home\[My ]Documents\Profile.ps1 All Users, Current Host $PsHome\Microsoft.PowerShell_profile.ps1 All Users, All Hosts $PsHome\Profile.ps1
Most people only use 1. I use $Home\[My ]Documents\Profile.ps1 as my profile as its easier to change then in $PShome.
In PowerShell v6 your profile options are
Description Path ----------- ---- Current User, Current Host $Home\Documents\PowerShell\Profile.ps1 Current User, All Hosts $Home\Documents\Profile.ps1 All Users, Current Host $PsHome\Microsoft.PowerShell_profile.ps1 All Users, All Hosts $PsHome\Profile.ps1
The location of $PShome changes in PowerShell v6. In Windows PowerShell v1 through v5.1 its:
PS> $pshome
C:\Windows\System32\WindowsPowerShell\v1.0
In PowerShell v6 its:
PS C:\scripts> $pshome
C:\Program Files\PowerShell\6.0.0-beta.9
Be careful of using $Home\Documents\Profile.ps1 as it will also be applied to Windows PowerShell. The safest place to put your PowerShell v6 profile is $Home\Documents\PowerShell\Profile.ps1 as it will still apply when you upgrade to the next version e.g. from a beta version to release candidate or full release.
PowerShell v6: #3 Release Candidate
The PowerShell team have announced the availability of the PowerShell v6 release candidate. https://blogs.msdn.microsoft.com/powershell/2017/11/17/powershell-core-6-release-candidate/
A release candidate is just about done with only bugs to resolve – in other words about what you can expect in the final delivery.
Some work is still required - https://github.com/PowerShell/PowerShell/issues?q=is%3Aopen+is%3Aissue+milestone%3A6.0.0-GA
The full release of PowerShell 6.0 is expected to occur on 10 January 2018 according to the blog post. Work then commences on PowerShell 6.1 with beta releases every 3 weeks or so.
Cannot verify the file SHA256 when installing package
I’m doing some work requiring containers and decided to use Server 1709 as it has some significant changes when compared to Server 2016.
The documentation - https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/ - just gives options for Windows Server 2016 and Windows Server Insider Preview. As 1709 is the shipping version of the Insider Preview I decided that should work.
All went well until it was time to download and install the docker package
Install-Package -Name docker -ProviderName DockerMsftProviderInsider -RequiredVersion 17.06.0-ce -Verbose
I saw the index download
VERBOSE: Downloading https://dockermsft.blob.core.windows.net/dockercontainer/DockerMsftIndex.json
then hit a warning
WARNING: Cannot verify the file SHA256. Deleting the file.
The install then terminates with an object not found error
Install-Package : Cannot find path
'C:\Users\Richard\AppData\Local\Temp\2\DockerMsftProviderInsider\Docker-17-06-0-ce.zip' because it does not exist.
I tried to use Save-Package but got a similar error. This seems to a be a common issue from the thread here - https://github.com/OneGet/MicrosoftDockerProvider/issues/15
I modified the work around from that thread
First: Download the index file
PS> Start-BitsTransfer -Source https://dockermsft.blob.core.windows.net/dockercontainer/DockerMsftIndex.json -Destination c:\source
Convert to PowerShell object
$dv = Get-Content -Path c:\source\DockerMsftIndex.json | ConvertFrom-Json
You can see the versions available
$dv.versions
And extract a single version
PS> $dv.versions.'17.06.0-ce'
date : 2017-07-10T16:35:52
url : https://dockermsft.blob.core.windows.net/dockercontainer/docker-17-06-0-ce.zip
size : 16277800
notes : This is the latest CE version of docker
sha256 : 3D27360A11A3A627AAC9C6D73EB32D4A9B6DCCA6BCB4B2C7A5FCD9D2E0EC6C82
Now you can download the zip file
PS> Start-BitsTransfer -Source "https://dockermsft.blob.core.windows.net/dockercontainer/docker-17-06-0-ce.zip" -Destination C:\ source\docker.zip
Unblock the file just in case
PS> Unblock-File -Path C:\Source\docker.zip
Check the file hash
PS> $dv.versions.'17.06.0-ce'.sha256
3D27360A11A3A627AAC9C6D73EB32D4A9B6DCCA6BCB4B2C7A5FCD9D2E0EC6C82
PS> Get-FileHash -Path C:\Source\docker.zip | Format-List
Algorithm : SHA256
Hash : 3D27360A11A3A627AAC9C6D73EB32D4A9B6DCCA6BCB4B2C7A5FCD9D2E0EC6C82
Path : C:\Source\docker.zip
They look to be the same but to save wear and tear on my eyeballs
PS> $dv.versions.'17.06.0-ce'.sha256 -eq (Get-FileHash -Path C:\Source\docker.zip).hash
True
Now copy docker.zip to the folder Install-Package was trying to use.
PS> Copy-Item -Path C:\source\docker.zip -Destination C:\Users\Richard\AppData\Local\Temp\2\DockerMsftprovider\ -Force
Notice the 2 in the path. Not sure why that’s there but seems to be necessary.
Move into the folder
PS> Push-Location -Path C:\Users\Richard\AppData\Local\Temp\2\DockerMsftProvider\
The instructions say to rename the zip file but use copy-item instead of rename-item. Its because Install-package will delete the zip file when its completed. This way you have the original available if you need it.
You can now install the package.
Install-Package -Name docker -ProviderName DockerMsftProviderInsider -Verbose -RequiredVersion 17.06.0-ce
Because the download file exists the save is skipped. The hash verification works and docker is installed. The installation of docker also enables the containers feature.
Restart the VM to finish the installation and start the docker service.
This shouldn’t be necessary. Being able to download packages and install them should just work. There’s something wrong in the whole process which needs a MS fix.
Windows update change in Server 1709
When Windows Server 2016 was introduced a very nice CIM class was provided to work with Windows Updates. If you wanted to scan for available updates you could do something like this:
$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession <br>$result = $ci | Invoke-CimMethod -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=0";OnlineScan=$true} <br>$result.Updates
Unfortunately, if you try this on Windows Server 1709 you’ll get an error:
New-CimInstance : The WS-Management service cannot process the request. The class MSFT_WUOperationsSession does not exist in the root/microsoft/windows/windowsupdate namespace. At C:\Program Files\WindowsPowerShell\Modules\WSUSupdates\WSUSupdates.psm1:14 char:9 + $ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpda ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (MSFT_WUOperationsSession:CimInstance) [New-CimInstance], CimException + FullyQualifiedErrorId : HRESULT 0x80338000,Microsoft.Management.Infrastructure.CimCmdlets.NewCimInstanceCommand + PSComputerName : w1709cn01
This change does NOT appear to have been documented.
What you will find is the MSFT_WUOperations CIM class which appears to be a very simplified version of MSFT_WUOperationsSession as it has just 2 static methods.
PS> $class = Get-Cimclass -Namespace root/microsoft/windows/windowsupdate -ClassName MSFT_WUOperations PS> $class.CimClassMethods Name ReturnType Parameters Qualifiers ---- ---------- ---------- ---------- ScanForUpdates UInt32 {SearchCriteria, Updates} {implemented, static} InstallUpdates UInt32 {DownloadOnly, Updates, RebootRequired} {implemented, static}
To scan for available updates on Server 1709 you use it like this:
PS> Invoke-CimMethod -Namespace root/microsoft/windows/windowsupdate -ClassName MSFT_WUOperations -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=0"} | select -ExpandProperty Updates Description : A security issue has been identified in a Microsoft software product that could affect your system. You can help protect your system by installing this update from Microsoft. For a complete listing of the issues that are included in this update, see the associated Microsoft Knowledge Base article. After you install this update, you may have to restart your system. KBArticleID : MsrcSeverity : Critical RevisionNumber : 201 Title : Cumulative Update for Windows Server 2016 (1709) for x64-based Systems (KB4043961) UpdateID : 0d02abc5-41ec-4768-8419-8487fa2e322b PSComputerName :
To install updates on Server 2016 you’d do something like this
$ci = New-CimInstance -Namespace root/Microsoft/Windows/WindowsUpdate -ClassName MSFT_WUOperationsSession Invoke-CimMethod -InputObject $ci -MethodName ApplyApplicableUpdates
The equivalent for Server 1709 is
PS> $au = Invoke-CimMethod -Namespace root/microsoft/windows/windowsupdate -ClassName MSFT_WUOperations -MethodName ScanForUpdates -Arguments @{SearchCriteria="IsInstalled=0"} PS> Invoke-CimMethod -Namespace root/microsoft/windows/windowsupdate -ClassName MSFT_WUOperations -MethodName InstallUpdates -Arguments @{Updates = $au.Updates} RebootRequired ReturnValue PSComputerName -------------- ----------- -------------- True 0
in this case a reboot is required which can be managed with Restart-Computer
When is PowerShell not PowerShell?
When is PowerShell not PowerShell? When its PowerShell v6.
This applies to beta 9 and later
Check a v6 instance
PS C:\Program Files\PowerShell\6.0.0-beta.9> $PSVersionTable Name Value ---- ----- PSVersion 6.0.0-beta.9 PSEdition Core GitCommitId v6.0.0-beta.9 OS Microsoft Windows 10.0.17035 Platform Win32NT PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1 WSManStackVersion 3.0
Now try and run PowerShell
PS C:\Program Files\PowerShell\6.0.0-beta.9> powershell Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. PS> $PSVersionTable Name Value ---- ----- PSVersion 5.1.17035.1000 PSEdition Desktop PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} BuildVersion 10.0.17035.1000 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1
You get your v5.1 instance! You HAVE to use pwsh
PS> exit PS C:\Program Files\PowerShell\6.0.0-beta.9> pwsh PowerShell v6.0.0-beta.9 Copyright (C) Microsoft Corporation. All rights reserved. PS C:\Program Files\PowerShell\6.0.0-beta.9> $PSVersionTable Name Value ---- ----- PSVersion 6.0.0-beta.9 PSEdition Core GitCommitId v6.0.0-beta.9 OS Microsoft Windows 10.0.17035 Platform Win32NT PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1 WSManStackVersion 3.0
The command to start PowerShell v6 on Linux is also pwsh.
This is wrong on so many levels that its difficult to explain. I know its supposed to enable the separation on v6 and any older versions in a side by side install but having to remember this adds another level of unnecessary thought to using PowerShell. I’ve been a big fan of PowerShell for over 12 years but v6 and the changes that are being made are in many instances a step backwards for an illusionary gain.
PowerShell version
Depending on the version of Windows you’re running you could be using PowerShell version 1 through version 5.1 (admittedly I suspect there are very few people, if any, still running PowerShell v1). This is complicated by the various versions of Windows Management Framework that are available for download and the large number of alpha and beta versions of PowerShell v6 that have been made available. So how do you know which PowerShell version you have on any given machine?
The easiest way is to use the $PSVersiontable automatic variable. On a Windows 10 machine you’ll see something like this:
PS> $PSVersionTable Name Value ---- ----- PSVersion 5.1.16299.19 PSEdition Desktop PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} BuildVersion 10.0.16299.19 CLRVersion 4.0.30319.42000 WSManStackVersion 3.0 PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1
Notice the PSVersion property. You could just type
PS> $PSVersionTable.PSVersion Major Minor Build Revision ----- ----- ----- -------- 5 1 16299 19
On PowerShell v1 $PSVersionTable doesn’t exist so you’ll get nothing returned.
A PowerShell v6 instance on Windows will return
PS C:\Program Files\PowerShell\6.0.0-beta.9> $PSVersionTable Name Value ---- ----- PSVersion 6.0.0-beta.9 PSEdition Core GitCommitId v6.0.0-beta.9 OS Microsoft Windows 10.0.17035 Platform Win32NT PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1 WSManStackVersion 3.0
Notice the similarities and differences – especially the PSEdition property
PowerShell v6 on Linux gives something similar
Name Value ---- ----- PSVersion 6.0.0-beta.9 PSEdition Core GitCommitId v6.0.0-beta.9 OS Linux 3.10.0-514.6.1.el7.x86_64 #1 SMP Wed Ja... Platform Unix PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} PSRemotingProtocolVersion 2.3 SerializationVersion 1.1.0.1 WSManStackVersion 3.0
When looking at PowerShell versions remember that functionality was introduced in this order:
PowerShell v1 – basic core cmdlets
PowerShell v2 – remoting, modules, jobs, WMI cmdlets (apart form Get-WmiObject)
PowerShell v3 – CIM cmldets, CIM sessions, workflows
PowerShell v4 – DSC
PowerShell v5 – PowerShell classes
PowerShell v6 – check the release notes as it changes so much at the moment