<< A B C D E F G H I J K L M N O P Q R S T U V W X Y Z


Active Directory Users and Computers (ADUC) - see RSAT



array, compare - differences

$a1 =@(1,2,3)


(Compare-Object $a1 $b1 ).InputObject



array, compare - SideIndicator

$a1 =@(1,2,3,4,5,8)


Compare-Object $a1 $b1


InputObject SideIndicator
----------- -------------
  6 =>
  8 <=

array, convert into from object - see object, convert into array

array, create from string with delimited values

$recipients = $addresses -split ";"

array, declare so increasing the number of elements is not restricted

[System.Collections.ArrayList]$array = @()

array, duplicate elements - find

$a = @(1,2,3,1,2)
$ht = @{}
$a | foreach {$ht["$_"] += 1}
$ht.keys | where {$ht["$_"] -gt 1} | % {Write-Host "Duplicate element found $_"}

array, find string as a member of

$array = 1,2,5,8,3,4,5

to use:


above returns True


above returns False

array, find elements of an array somewhere in a string

We're looking for a user that might belong to

the line below beginning with “if($null” has the test

$arrayTenants = ("UK","US","Germany")
#declare a hash of tenants with their respective qualifying OUs as elements
$O365Tenant = @{}
$O365Tenant["US"] = ("one","two","three")
$O365Tenant["UK" = ("four","five","six")
$O365Tenant["Germany"] = ("seven")
Get-ADUser -Filter 'name -like "*"' | ForEach-Object {
    $distinguishedName = $_.distinguishedName
    ForEach ($tenantName in $arrayTenants)
        if($null -ne ($O365Tenant[$tenantName] | ? { $distinguishedName -match $_ }))
           "$tenantName found"

see here for more

array, import from text file - see text file, import into an array, record for each field in a file, parse, CSV, import into an array

array index, find which inside of foreach loop

foreach ($item in $array) {

array, initialize

$myArray = @("a","b","c","d","e","f","g","h")
$myArray = "a","b","c","d","e","f","g","h"

array, loop through

create array and then display one at a time

$upn = @("user1@yourDomain.com", "user2@yourDomain.com", "user3@yourDomain.com")
$upn | %{"$_"}

to also list the indices:

$upn | %{"$($upn.IndexOf($_)) $_"}

array, methods available (static) - display

[array] | gm -s | select name, definition | ft -a

This is basically a "get help" command for the PowerShell array function

array of hashes, populate - see hash, array of, populate

array of objects - convert to array of strings

put all our OU names into an array

$arrayOUs = Get-ADObject -Filter {ObjectClass -eq 'organizationalunit'} -Properties CanonicalName | Select-Object -Property Name
# convert the array of objects into an array of strings
$strArrayOUs = $arrayOUs | ForEach {"$($_.Name)"}

array, print contents of

the trick is the built-in ofs variable

$proxyAddresses = $user.proxyAddresses
$ofs = '; ' # render $proxyAddresses array printable - separated by "; "
"$($proxyAddresses.Count) useful proxyAddresses: $proxyAddresses" # prints out array count & array contents separated by "; "

array one-liners

array, read into from text file - see text file, import into an array, record for each field in a file, parse, CSV, import into an array


background color, get

(Get-Host ).UI.RawUI.BackgroundColor

background color, set

$a = (Get-Host).UI.RawUI

$a.BackgroundColor = "DarkGreen"

There aren’t too many choices


only lists 16 color choices

break - see also continue, exit, return

terminates execution of a loop or switch statement and hands over control to next statement after it

This does not work like this if the loop in question is part of a piped foreach ("%") statement. If you try that, the dang code just dies right here and never proceeds to the next statement!


can’t load or run a script - see also scripts disabled

File C:\Scripts\ListDir.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing" for more details. At line:1 char:14 + .\ListDir.ps1 <<<< + CategoryInfo : NotSpecified: (:) [], PSSecurityException + FullyQualifiedErrorId : RuntimeException

From an elevated prompt:

Set-ExecutionPolicy Unrestricted

or better (restricted to only current user):

Set-ExecutionPolicy Unrestricted -Scope CurrentUser

Sometimes the command above won't work. It will likely complain about some more specific scope overriding your attempt.

Set-ExecutionPolicy : Windows PowerShell updated your execution policy successfully, but the setting is overridden by a policy defined at a more specific scope. Due to the override, your shell will retain its current effective execution policy of AllSigned.

To find out more, list all the different scopes:

Get-ExecutionPolicy -List

might return something like this:

Scope         ExecutionPolicy
-----         ---------------
MachinePolicy Undefined
UserPolicy    Undefined
Process       Restricted
CurrentUser   Undefined
LocalMachine  Unrestricted

More specific scopes like Process override less specific scopes like CurrentUser. So we need to change the more inclusive Process scope. Try this:

Set-ExecutionPolicy Bypass -Scope Process -Confirm

You may wonder: why not just set this Process scope to Unrestricted as well? But the system may not allow it. But setting it to Bypass instead will probably be allowed.

case statement - see switch

can’t run scripts - see scripts disabled, can’t load or run a script

certs, list

Get-ChildItem -Recurse Cert: | ft

clipboard, send output of a command to

simply append your command with "| clip"

color of Write-Host command - see also Write-Color

Write-Host "green color" -foreground "green"

multiple colors on one line

Write-Host "Green " -ForegroundColor Green -NoNewline; Write-Host "Red "-ForegroundColor Red -NoNewline; Write-Host "Yellow " -ForegroundColor Yellow

colors, show available

[enum]::GetValues([System.ConsoleColor]) | Foreach-Object {Write-Host $_ -ForegroundColor $_}

colors, multiple in one line - see Write-Color

command, run remotely - see remote command

command history

shows each command, sequence and duration for the current session - most recent up top

Get-History | select id, CommandLine, StartExecutionTime, EndExecutionTime, @{n="duration";e={$_.EndExecutionTime - $_.StartExecutionTime}} | sort id -Descending | ogv

comment out a line - use "#".  For a block of comments, "<#" at beginning of block followed by "#>" at end of block

compress a file - see zip a file

computer name see also environment variables


computer serial number - see PC, get serial number

computer remote session, - see remote session, initiate

concatenate - +

connectivity to a bunch of PCs in an OU, test

Get-ADComputer -Filter * -Server ad11 -SearchBase "OU=IT,OU=yourOU,DC=yourDomain,DC=com" Where {Test-Connection $_.Name -Count 1 -Quiet} | Select @{Name="Computername";Expression={$_.Name}}

continue - see also break, exit, return

skip the current execution of the loop and to return the execution to the top of the innermost loop

contains, does a variable contain a substring?

if ($DisplayName -match "departed")

copy files from one directory to another

$destination = "D:\xml\"
$location = "D:\Client Portal\"
function processCopy
  $files = Get-ChildItem -Path:$location
  foreach($file in $files)
   $path = $file.FullName
  if($file.Name -ne "copy.ps1"){
   Copy-Item -Path:$path -Destination:$destination -Force -Confirm:$false
   Write-Host "Item" $file.Name " has been sucessfully copied to" $destination
   if($file.Attributes -eq "Directory")
    copyFile $file $destination

function copyFile($directory, $target)
  $target += $directory.Name
  $files = Get-ChildItem -Path:$directory.FullName
  foreach($file in $files)
  $path = $file.FullName
  if($file.Name -ne "copy.ps1"){
  Copy-Item -Path:$path -Destination:$target -Force -Confirm:$false
  Write-Host "Item" $file.Name " has been sucessfully copied to" $target

  if($file.Attributes -eq "Directory")

  copyFile $file $target

function Pause ($Message="Press any key to continue...")
  Write-Host -NoNewLine $Message
  $null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
  Write-Host ""

Count how many files in a directory, excluding "bin" and "obj" files & directories

(gci "D:\VisualStudio\GID_CRM" -exclude bin, obj -Recurse).Count


CSV, import into an array - see also text file, read into array, record for each field in a file, parse

$dir = [environment]::getfolderpath("mydocuments")
$docName = "$($dir)/someFile.csv"
$csv = Import-Csv $docName
$i=0; $count = $csv.Count
foreach ($item in $csv)
    $i++; $percentTxt = ($i/$count).ToString("P")
    Write-Host "$i of $($count) ($percentTxt): Name = $($item.Name) and email = $($item.Email) and type = $($item.type)" -ForegroundColor Green

CSV, import special characters

Even though a CSV may look like it has special characters, doesn't mean those are actually stored as unicode that gets processed the right way.

$dir = [environment]::getfolderpath("mydocuments")
$originalFileName = "$($dir)/input.csv"
$convertedFileName = "$($dir)/$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss'))inputConverted.csv"
Get-Content $originalFileName | Out-File $convertedFileName -Encoding UTF8
Remove-Item $convertedFileName -recurse -force
$csv = Import-Csv $convertedFileName
Foreach ($item in $csv){Write-Host "$($item.Name) - $($item."email address")" -ForegroundColor "green"}

Had the original "input.csv" file had what appeared to be special characters and you merely "Import-Csv" on it and looped through using "foreach" without first "Get-Content" piped to "Out-File" with "-Encoding UTF8", you likely would have only seen unprintable character resembling a solid diamond with a question mark in it for each of those special characters.

Notice that we immediately delete the intermdiate $convertedFileNamefile because we don't want it left around to clutter things up once we're done with it.


date, include in file name - see timestamp, include in file name

date of files, determine

ls d:\filemaker -rec | foreach {$_.creationtime -lt "02/25/2013"}

date of most recent file in a directory

$latest = Get-ChildItem -Path $backedUpDirectoryPath | Sort-Object LastWriteTime -Descending | Select-Object -First 1

directory, loop through

$sourceDirectory= "D:\somedirectory"
ls $sourceDirectory -rec | foreach {
  $filename = $_.Name
  $length = $_.length
  $creationtime = $_.creationtime
  $DirectoryName = $_.Directory.Name
  Write-Host $i $filename ", " $DirectoryName ", " $length ", " $creationtime

digitally aigned, ps1 file won't run

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

delete file

Remove-Item $a[$i] -recurse -force

directory, delete certain subdirectories and everything underneath them

gci $workingDirectory -include bin, obj, Upload -Recurse -Force | Remove-Item -Recurse -Force

In Powershell 3.0, it is simpler:

dir -Directory #List only directories
dir -File #List only files

Or to limit to just 2 directories deep:

dir -Directory .\*\*

In PowerShell 2, to just get directories (with grouping):

Get-ChildItem -Recurse | ?{ $_.PSIsContainer }

Or to limit to just 2 directories deep:

Get-ChildItem .\*\* | ?{ $_.PSIsContainer }

To get a raw list of directories, without the grouping:

Get-ChildItem -Recurse | ?{ $_.PSIsContainer } | Select-Object FullName

directory size

(gci -Force "C:\Users\gollum\myPrecious\odes" -Recurse -ErrorAction SilentlyContinue | measure Length -s).sum

or in Gb

(gci -Force "C:\Users\gollum\myPrecious\odes" -Recurse -ErrorAction SilentlyContinue | measure Length -s).sum 1Gb

display value of a variable - simply put the variable in its own line. No write-host necessary:


disabled scripts - see scripts disabled

duplicate elements in an array, find - see array, duplicate elements - find

duration of last command - see time it took to run most recent command


edit scripts - powershell ISE (included with Windows 7, 8)

elapsed time - see time elapsed


$smtpFrom = "fromEmail@yourDomain"

$smtpTo ="toEmail@yourDomain"

$message = New-Object System.Net.Mail.MailMessage $smtpfrom, $smtpTo

$message.Subject = $messageSubject

$message.IsBodyHTML = $true

$message.Body = $body

$smtpServer = "yourTenant-com01ec.mail.protection.outlook.com"

$smtp = new-object Net.Mail.SmtpClient($smtpServer)

$ credentials =new-object system.net.networkcredential("whosSending@yourDomain","TopSecret")



This does not work all the time

send-mailmessage -to "user@mydomain.com" `
  -from "user@mydomain.com" `
  -subject "sample subject line" `
  -smtpServer mymailserver.mydomain.com `
  -body 'test body'

not only does it not work, putting in a "try-catch" block doesn't give you an error; it errors out right away when you first try to run.

email user info

get-msoluser -userprincipalname test@yourdomain.com | fl

emails, count of from one user for a day

[Int ] $intRec = 0
Get-TransportService | `
  Get-MessageTrackingLog -ResultSize Unlimited -Start "4/09/2015" -End "4/10/2015" `
  -Recipients "someuser@yourdomain.com" -EventID DELIVER | `
  ForEach { $intRec++ }
Write-Host "E-mails received:", $intRec

environment variables



computer name


user name




equal - -eq()

error, expand

$error | Format-List -Force

error when running script - see can't load a script

event log -

Get-WinEvent -FilterHashtable @{logname = "Application"; starttime = '11/14/22 07:40 am'; endtime = '11/14/22 07:55 am'} | ? {$_.LevelDisplayName -eq 'Error' -or $_.LevelDisplayName -eq 'Warning'}


Get-EventLog -LogName 'Application' | Select TimeWritten, Message, EventID | Where-Object -FilterScript {$_.TimeWritten -ge '11/14/22 07:40 am' -and $_.TimeWritten -le '11/14/22 07:55 am'}

execution policy - see scripts disabled

exit - see also break, continue, return

terminates the current execution session altogether. It also closes the console window and may or may not close ISE.

exit from for loop -

if $thing -eq 'some_condition')


file, append data

$logFile = "C:\Logs\LogFile.txt"
Add-Content $logFile "a new line"

file, folder exist?

if (Test-Path C:\Scripts)
 if (Test-Path C:\Scripts\Test.txt)
  "Folder and file exist"
  "Folder exists, file doesn't"
 "Folder does not exist"

file, read into array

$file = "someFile.htm"
$myDocs = [environment]::GetFolderPath("MyDocuments")
$file = $myDocs + "\" + $file
$fileContent = Get-Content $file
for ($i= 0; $i -lt $fileContent.length; $i++){

This is all well and good. But what if we really want an array of hashes? The following works good for CSVs

$csv= Import-Csv $inputfile
foreach($item in $csv)
   "$($item.UserPrincipalName) -  $($item.DisplayName)"

file name, include timestamp in - see timestamp, include in file name

filter timestamp example

filter timestamp {"$(Get-Date -Format G): $_"}

write-output "hello world" | timestamp

find whether elements of an array can be found anywhere in a string - see array, can elements of an array can be found anywhere in a string

fonts, install

$path = "directoryContainingFontFiles"
$shell = New-Object -ComObject Shell.Application

Or, if you want to check to see if the target PC already has it:

$sourcPath = "\\genetic-id.com\share\Dept_Shares\IT\Museo Sans Rounded\OpenType CFF Std"
$destinationPath = "c:\windows\fonts\"
$fontFileSource = Get-ChildItem $sourcPath | Select Name
# Check for presence of each file.  If any ONE of the 6 files are missing, install ALL of them.
foreach ($file in $fontFileSource)
    If (Test-Path "$($destinationPath)$($File.name)")
        #"font $($File.name) found, no action necessary"
        #"font $($File.name) not found, need to add"
        $shell = New-Object -ComObject Shell.Application

If you want to deploy these fonts across your organization, then invoke this from Group Policy.

foreign characters - see also special characters, CSV, import special characters

If a command exports users whose names contain foreign characters into a CVS file, those foreign characters will be replaced with a ?. To preserve those foreign characters, use Export-Csv $outputfile -Encoding UTF8

Get-Content $file | Out-File $fileUTF8 -NoTypeInformation -Encoding UTF8


greater than


greater than or equal


gridview - | Out-GridView or | ogv

group by

Get-User | Group RecipientTypeDetails


hash, clear


hash, declare

$guestUsers = @{}

hash, array of, populate

$guestUsers = @(
    @{email = 'bob.smith@yourDomain.com'; DisplayName = "Bob Smith"}
    @{email = 'jim.jones@yourDomain.com'; DisplayName = "Jim Jones"}
    @{email = 'sally.hayes@yourDomain.com'; DisplayName = "Sally Hayes"}

history of commands - see Command History

how long did the last command take to run? See time it took to run most recent command, Command History


import text file into array - see text file, import into an array, record for each field in a file, parse, CSV, import into an array

'Install-Module' is not recognized

Start with Get-PackageProvider


Hopefully, you ought to get at least 3 rows, one of which ought to be PowerShellGet. If Get-PackageProvider isn't recognized, try Install-PackageProvider

Install-PackageProvider -Name NuGet -Force

installed programs

The only reason I include the PSPath - which is quite lengthy & often useless - is because for some entries just about all the other fields are empty. I think these are mainly OS update entries.

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |  Select-Object DisplayName,InstallLocation,DisplayVersion,PSPath,Publisher,InstallDate | ogv

IP Address

# internal
Get-NetIPAddress | select IPAddress, InterfaceAlias, AddressFamily | ? {$_.AddressFamily -eq "IPv4" -and $_.InterfaceAlias -notlike "Loopback*"} | ft -a
# external - often need to include "-UseBasicParsing" parameter if "Internet Explorer's first-launch configuration is not complete" or you'll get an error
(Invoke-WebRequest -uri "http://ifconfig.me/ip" -UseBasicParsing).Content
# external with latitude/longitude, city, state, zip info
Invoke-RestMethod -Uri ('http://ipinfo.io/'+(Invoke-WebRequest -uri "http://ifconfig.me/ip" -UseBasicParsing).Content)

This might give error:

Invoke-WebRequest : The response content cannot be parsed because the Internet Explorer engine is not available, or Internet Explorer's first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again.


join members of an array together separated by character(s)

$groups = @("US Users","Staff","Big building")

$allGroups = ($replace | % {$groups}) -join ','




for the machine you're on


What languages are available on the machine?

Get-WinUserLanguageList | ft

last command, how long did it take to run? - see time it took to run most recent command

less than - -lt()

less than or equal - -le()

line continuation - use the “grave accent” (`) mark.

Get-ChildItem -Recurse `
-Filter *.jpg `
| Select LastWriteTime

However, this is only ever necessary in such cases as shown above. Usually you get automatic line continuation when a command cannot syntactically be complete at that point. This includes starting a new pipeline element:

Get-ChildItem |
Select Name,Length

Similar to the | a comma will also work in some contexts.

strings (in all varieties) may also extend beyond a single line:


They include the line breaks within the string, then.

loop, percent progress through - see percent progress through a loop

loop - several types

For - read content of a file as in

$file = "someFile.htm"
$myDocs = [environment]::GetFolderPath("MyDocuments")
$file = $myDocs + "\" + $file
$fileContent = Get-Content $file
for ($i = 0; $i -lt $fileContent.length; $i++){

ForEach - for “normal” arrays - see also array, loop through

ForEach-Object (or "%") for objects - you need to have the “{” on the same line right after the “ForEach-Object” or you’ll get prompted for:

cmdlet ForEach-Object at command pipeline position 2

Supply values for the following parameters:


unlike the other flavors of “ForEach” and “While” which expect the “{” to be on the next line.

While - loop through the contents of a file or database query/table

continue - see also break, exit, return

loop, escape - break (all by itself) - see break, continue, exit, return

Keep in mind that this does not work like you'd expect if the loop in question is part of a piped foreach ("%") statement. If you try that, the dang thing just dies and never proceeds to the next statement after the loop!

lower case - see also proper case

append ".ToLower" to string. For example:

$Username = "$($FirstName.Substring(0,1))$LastName".ToLower()

takes the first letter of $FirstName variable and appends to $LastName and then puts whole thing lower case.


MAC addresses for a PC


Module install attempt fails

If you attempt to run some command like

Install-PackageProvider -Name "PowerShellGet"





and get some error like this

PacketManagement\Find-Package: Unable to find module providers (PowerShellGet) At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\\PSModules.psm1:1397 char:3

look under Documents\WindowsPowerShell folder for a "Modules" folder under that and then delete or rename that "Modules" folder to something like "ModulesOld" and try again. It should recreate that "Modules" folder. And now things might work better.

Modules installed

Gets installed modules on a computer:


Gets the modules that have been imported or that can be imported into the current session:


What's available:

Get-Module -ListAvailable

update a module:

Update-Module -Name someModule -Verbose

Sometimes you must know where a module is installed. Especially if you need to uninstall or import.

$AzureInstalled = Get-InstalledModule | Where-Object {$_.Name -like 'Azure*'}
$AzureInstalled | select Name, InstalledLocation

Module providers

You may try to run a command like

Find-Module *ms*

and get an error

PacketManagement\Find-Package: Unable to find module providers (PowerShellGet) At C:\Program Files\WindowsPowerShell\Modules\PowerShellGet\\PSModules.psm1:1397 char:3

or some such directory path, file & line number. Before you go chasing down that directory to make sure the module exists, run this command first to find out what Package Providers you have


You should get a list of package providers. If the one you want (PowerShellGet in this case) is missing, then try:

Install-PackageProvider -Name "PowerShellGet"

if this fails, see Module install attempt fails

Modulo operator (remainder in division)

if $i below is 10, 20, 30, etc., the if statement below will be true

if ($i % 10 -eq 0) {$i}

My Documents location

$mydocs = [ environment]::getfolderpath("mydocuments")

change directory to my documents

$dir = [environment]::getfolderpath("mydocuments")

cd $dir

or if you don't want to mess with a variable and want to embed date/time in a file name

Get-AdUser | Export-Csv -Path "$([environment]::getfolderpath("mydocuments"))\fileBaseName$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss')).csv" -NoTypeInformation -Encoding UTF8


name of the PC/server you're on right now



$Get-WmiObject Win32_Computersystem).name

network connectivity for PCs - see connectivity to a bunch of PCs in an OU, test



null, assign

sometimes you can assign the $null variable. Other times you must use the -clear operator.

null, check if string is null

if ([string]::IsNullOrEmpty($mystring) {Write-Host "string is NULL or EMPTY"}
else {Write-Host "string has a value"}

null, check if property is null when filtering

Normally, you can simply filter on someProperty -ne $null. But when trying to filter on some commands like Get-AdUser, problems because PowerShell doesn't recognize what the $null variable is in that context. So instead, filter on -notlike "*".

Get-AdUser -filter (msExchHideFromAddressLists -notlike "*")} -Properties $Properties


object, convert into array

Let's say you already have an object $objResult with many fields, one of whose fields is called "Source Workstation". To convert this particular field to an array $PCs:

$PCs = $objResult.("Source Workstation")

To get rid of dupes:

$PCs = $PCs | select -Unique

open files

all the open files in a certain directory

Get-SmbOpenFile | Where-Object -Property Path -Like "*somePath*" | ft ClientUserName,Path


Get-SmbOpenFile | Where-Object -Property ShareRelativePath -Match "someString" | select ShareRelativePath | fl

supposedly, this will find open files on another server

Get-SmbOpenFile -ScopeName "someServer"

but this always fails for me

Get-SmbOpenFile : No MSFT_SMBOpenFile objects found with property 'ScopeName' equal to 'someServer'. Verify the value of the property and retry.



output results of a command to clipboard

simply append your command with "| clip"


password for SQL Server database doesn't work using non-integrated security - make sure your connection string includes, ";Trusted_Connection = yes"

PC serial number

gwmi win32_bios


$sessionOption = New-CimSessionOption -Protocol Dcom
$cimSession = new-cimsession -sessionOption $sessionOption -computername somePCName
Get-CimInstance Win32_BIOS -CimSession $cimSession

pc name - see name of the PC/server you're on right now

percent progress through a loop

$employees = Get-Msoluser -All
$i=0; $count = $employees.Count
$employeesWithMailboxes = @()
foreach ($employee in $employees) {
    $i++; $percentTxt = ($i/$count).ToString("P")
    $displayName = $employee.DisplayName
    $messagePrefix = "$i of $($count) ($percentTxt): $displayName"
    Write-Host "$messagePrefix now" -ForegroundColor Green


PowerShell version - see also Windows version

Use the built in variable



Name                           Value
----                           -----
PSVersion                      5.1.17763.134
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.17763.134
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3



gives version detail in table format

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      17763  134

Get-Host just shows you the version of the host (i.e. of Console.Exe or Visual Studio Code Host).

Name             : Visual Studio Code Host
Version          : 1.10.2
InstanceId       : 7f3dd862-f8ce-49a2-b420-b44ccebc0053
UI               : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture   : en-US
CurrentUICulture : en-US
PrivateData      : Microsoft.PowerShell.EditorServices.EditorServicesPSHost+ConsoleColorProxy
DebuggerEnabled  : True
IsRunspacePushed : False
Runspace         : System.Management.Automation.Runspaces.LocalRunspace

Or $Host.Version will zero into more detailed version info in a table format.

Major Minor Build Revision
----- ----- ----- --------
1     10    2     -1

means you have Visual Studio Code Host version 1

processes, how many are going on


process, is one particular process active?

process, kill one particular process

profile location - $profile

To create a whole new profile (it checks first to make sure we don't already have one):

if (!(test-path $profile)) {new-item -type file -path $profile -force}

progress through loop, percent - see percent progress through a loop

Prompt, change - helpful to include this in scripts that connect to other servers

function global:prompt{

    $promptString = "US $($executionContext.SessionState.Path.CurrentLocation) $([datetime]::Now.ToLongTimeString())>"
    write-host -NoNewLine -ForegroundColor Cyan $promptString
    return " "
#change prompt

That global: up top is pretty important

proper case

$words = 'wAr aNd pEaCe'

$TextInfo = (Get-Culture).TextInfo

if all caps:

$words = 'WAR AND PEACE'



property name has spaces - see spaces in property name

properties - sometimes you can just add Select-Object. But usually you need to add a Select-Object As in:

Get-MsolUser -All | Select-Object UserPrincipalName, isLicensed, immutableID


When to Quote in PowerShell


read text file into array - see text file, import into an array, record for each field in a file, parse, CSV, import into an array

record for each field in a file, parse

Instead of getting one record with each field delimited by something like a comma as you would in a CSV, occasionally you get a separate record for each file. In the case below, we have two separate fields - “DisplayName” and “Department” - and each is on its own record. We need to gather these two fields together and put them all together in one record.

$dir = [environment]::getfolderpath("mydocuments")
$docName = "$($dir)/ticket1519133.txt"
$fileContents = Get-Content -Path $docName
$i=0; $count = $fileContents.Count
$fieldCount = 2
$result = @()
foreach ($line in $fileContents)
    $i++; $percentTxt = ($i/$count).ToString("P")
    if ($i % $fieldCount -eq 1) {
        $DisplayName = $line
    } else {
        $j = [math]::floor([decimal]($i/$fieldCount))
        $jcount = [math]::floor($count/$fieldCount)
        Write-Host "$j of $($jcount) ($percentTxt): $line" -ForegroundColor Green
        $result += New-Object -TypeName PSObject -Property @{
            "DisplayName" = $DisplayName
            "Department" = $line
$result = $result | select DisplayName, Department
$result | ogv

remainder (as in division) - see modulo

remote command

on several different computers at once:

$Cred = Get-Credential
Invoke-Command -ComputerName Mordor, TheShire {Get-Service -Name W32time} -Credential $Cred

runs on the remote computer and the results are returned to your local computer as deserialized objects. That doesn't mean you can't start or stop a service using a method with Invoke-Command though. It just means that the method has to be called in the remote session.

Invoke-Command -ComputerName Mordor, TheShire {(Get-Service -Name W32time).Stop()} - Credential $Cred

remote session, initiate

$Cred = Get-Credential
Enter-PSSession -ComputerName MagicKindom -Credential $Cred

to end:



PowerShell Replace Method And Operator: Syntax, Examples or How to Use PowerShell Replace to Replace Text [Examples]

Method - case sensitive

("Sad to see you").Replace("S","Gl")

you can also “chain” these together

("Sad to see you go").Replace("S","Gl").Replace("you go","you're back!")

Operator - not case sensitive

("Sad to see you") -Replace("S","Gl")

return - see also break, continue, exit

terminates execution of the current function and passes control to the statement immediately after the function call

RSAT (e.g., ADUC

What available modules are installed?

Get-WindowsCapability -online | ? Name -like Rsat* | ft

Install RSAT for Windows 10 1809 and 1903 automated


Scheduling PowerShell Scripts (in Server 2008)

In the "Action" tab, for the "Start a program", need to have "powershell" for the "Program/script" and the file location for the "Add arguments (optional)". If you're attempting to run some commands - like New-Object IO.FileSystemWatcher then you must precede the file name with "-NoExit . " (including the dot).

Scripts disabled - see also can’t load or run a script

To see current policy:


To change current policy:

Set-ExecutionPolicy RemoteSigned

But what if you get this?

Set-ExecutionPolicy : Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell' is denied.
At line:1 char:1
+ Set-ExecutionPolicy RemoteSigned
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   + CategoryInfo          : NotSpecified: (:) [Set-ExecutionPolicy], UnauthorizedAccessException
   + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.SetExecutionPolicyCommand

Answer: run PowerShell as an administrator

serial number for a PC - see PC, get serial number


Find if you don't know the exact name but have some idea of the name. For example: you know the service has something to do with the word, profile:

get-service | Where-Object {$_.DisplayName -like "*profile*"} | ft

session, kill existing

Get-PSSession | Remove-PSSession

session, remote initiate - see remote session, initiate

shared directories for a server, list

get-WmiObject -class Win32_Share -computer someServer

spaces in property name - enclose with quotes as in

$_."IP Address"

special characters - see also CSV, import special characters, foreign characters

special characters, replace

[char[]]$replace = '!@#$%^&*(){}[]":;,<>/|\+=`~ '''
$regex = ($replace | % {[regex]::Escape($_)})-join '|'
cd 'D:\test
Get-ChildItem -recurse |
  ForEach {
    if ($_.Name -match $RegEx){
      Ren $_.Fullname -NewName $($_.Name -replace $RegEx, '_') -whatif
if ($DisplayName -match "departed")


[char[]]$replace = '!@#$%^&*(){}[]":;,<>/|\+=`~ '''

$regex = ($replace | % {[regex]::Escape($_)}) -join '|'

Get-ChildItem -recurse | Where-Object { $_.Name -match $RegEx} | Rename-Item -NewName {$_.Name -replace $RegEx, '_'} -whatif

sort -

Get-EventLog system -newest 5 | Sort-Object eventid

Get-MsolUser -All | Sort-Object -property UserPrincipalName

If you want to sort stuff descending, add the -Descending argument

Get-SPOSite | select Url, StorageUsageCurrent, WebsCount | sort StorageUsageCurrent -Descending | ft

If you want to sort stuff descending and ascending, need to do things a little differently

Get-SPOSite | select Url, StorageUsageCurrent | sort @{expression="StorageUsageCurrent";Descending=$true},@{expression="Url";Ascending=$true} | ft

SQL Server database, can't connect to using non-integrated security - make sure your connection string includes, ";Trusted_Connection = yes"

SQL, execute - see also here and here for arrays

$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$ConnectionString = "Server=someserver;Database=somedb;User Id=someuser;Password=yourpwd"
$SqlConnection.ConnectionString = $ConnectionString
catch [System.Data.SqlClient.SqlException]
    Write-Host "Problem connecting to database"
    write-host $_.Exception.ToString()
    #write-host $_.Exception.state
$sqlCommand = $sqlConnection.CreateCommand()
$SQLStr = "select * from table"
$sqlCommand.CommandText = $SQLStr
$dr = $sqlCommand.ExecuteReader()

if you then want to loop through the recordset returned:

while ($dr.Read())
  $firstFieldValue = $dr.GetValue(0)
  $secondFieldValue = $dr.GetValue(1)

This is all well and good for select statements.  But for update and inserts, need something else:

$updateSQL = "update table set somefield = 3"
$sqlCommand.CommandText = $updateSQL

Startup Programs, Generate a List

Get-CimInstance Win32_StartupCommand | Select-Object Name, command, Location, User | ogv

string, how many instances - see Counting the number of occurrences of a string or file in PowerShell

"OU=Pumpkin Patch,DC=farms,DC=Dogpatch,DC=gov" -split ",DC=" | measure | select -exp count

If you expect these strings to always be in the middle somewhere, then subtract 1. Because the example above returns 4 because that's how many substrings result from the split - even though there are only 3 instances of the string we're searching.

string, find just one

$s = "New York City"

"New","Old" | %{ if ($s.contains($_)) { "$s contains $_" }}

string, find if any of several match - see also string, how many instances

$ignoreDirectories = @("SYSVOL","#recycle","SysStates","LogsConfig")
if(Select-String -InputObject $filename -pattern $ignoreDirectories)
  Write-Host "ignore" $filename

“-match” also works but is usually used to find match in an array

string, can elements of an array can be found anywhere in - see array, can elements of an array can be found anywhere in a string

string, find files that contain in a directory

Select-String -Path d:\inetpub\somedirectory\*.aspx -pattern somestring

string, find whether it is in an array - array, find string as a member of

string, replace

$a = $a -replace "one", "two"

substring - see also string, how many instances

append ".Substring(start position, number of characters)" to string. For example:

$Username = "$($FirstName.Substring(0,1))$LastName".ToLower()

takes the first letter of $FirstName variable and appends to $LastName and then puts whole thing lower case.

switch statement - same as the “case” statement in other languages - see here


task scheduler, run a powershell script in

in the "Actions" tab, in the "Start a program" area, put in "powershell"

In the "Add arguments" area, put in "-ExecutionPolicy Bypass D:\path\YourPowerShellScript.ps1"

If you see, "This task requires that the user account specified has Log on as batch job rights" the user must be able to do the following

  1. Start -> Run
  2. Type in secpol.msc /s
  3. Select "Local Policies" in MSC snap in
  4. Select "User Rights Assignment"
  5. Right click on "Log on as batch job" and select Properties
  6. Click "Add User or Group", and include the relevant user.

tenant, which one am I on?


The command above returns a list of SKUs, each of which includes an AccountSkuID field. The first part of this each of the AccountSkuID field on every row in this list of SKUs has the tenant name. If you just want the tenant name all by itself, use this instead:


which looks at the AccountSkuID field of the first SKU in the array and then uses the portion before the colon.

term 'xx' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. (Exchange)

for example:

get-mailbox -ResultSize Unlimited


get-mailbox : The term 'get-mailbox' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:1 char:1
+ get-mailbox -ResultSize Unlimited
+ ~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (get-mailbox:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException

need to run:

add-pssnapin *exchange* -erroraction SilentlyContinue

test network connectivity for PCs - see connectivity to a bunch of PCs in an OU, test

text file, import into an array - see also CSV, import into an array, record for each field in a file, parse

$dir = [environment]::getfolderpath("mydocuments")
$docName = "$($dir)/textToImport.txt"
$fileContents = Get-Content -Path $docName
$i=0; $count = $fileContents.Count
foreach($line in $fileContents)
    $i++; $percentTxt = ($i/$count).ToString("P")
    Write-Host "$i of $($count) ($percentTxt): $line" -ForegroundColor Green

time elapsed

the best way to see how long recent commands took to run that I've found so far:

Get-History | select id, CommandLine, StartExecutionTime, EndExecutionTime, @{n="duration";e={$_.EndExecutionTime - $_.StartExecutionTime}} | sort id -Descending | ogv

loop example

$startTime = $(Get-Date)
Write-Host "Elapsed:00:00:00"
$NoEvent = $true
While ($NoEvent)
  Start-Sleep 1
  $elapsedTime = New-TimeSpan $startTime $(Get-Date)
  Write-Host "Elapsed:$($elapsedTime.ToString("hh\:mm\:ss"))"
  #Handle event
  if(event){$NoEvent = $false}

or use the .NET Stopwatch class

$Time = [System.Diagnostics.Stopwatch]::StartNew()
$NoEvent = $true
while ($NoEvent) {
    $CurrentTime = $Time.Elapsed
    Write-Host $([string]::Format("`rTime: {0:d2}:{1:d2}:{2:d2}", $CurrentTime.hours, $CurrentTime.minutes, $CurrentTime.seconds)) -nonewline
    Sleep 1
    #Handle event
    if(event){$NoEvent = $false}

time it took to run most recent command - see also Command History

(Get-History)[-1].EndExecutionTime - (Get-History)[-1].StartExecutionTime

how long it took each of the previous 10 commands to run

for ($i = -1; $i -gt -10; $i--){
    $duration = (Get-History)[$i].EndExecutionTime - (Get-History)[$i].StartExecutionTime
    "duration for event $($i): $duration"

timestamp - see filter timestamp example

timestamp, convert to string

(Get-Date).ToString("MM/dd/yyyy hh:mm:ss tt")

timestamp, include in file name

$outFile = "baseFileName_{0:yyyyMMdd-HHmm}.csv" -f (Get-Date)

or if you don't want to mess with a variable and want to send to the My Documents folder

Get-AdUser | Export-Csv -Path "$([environment]::getfolderpath("mydocuments"))\fileBaseName$((Get-Date).ToString('MM-dd-yyyy_hh-mm-ss')).csv" -NoTypeInformation -Encoding UTF8

title case - see proper case


ISESteroids - add-on for the Windows PowerShell ISE editor

top 5 - add following to any command: | Select-Object -First 5


" first last ".Trim()

try/catch - will suppress system error message but instead keep going and show error of your choice (and highlight just the pertinent part of the error message with back color if you have Write-Color module installed)

$dir = [environment]::getfolderpath("mydocuments")
$docName = "$($dir)/my.csv"
$csv = Import-Csv $docName
foreach ($item in $csv)
    try {
        $user = Get-MsolUser -UserPrincipalName $item.UserPrincipalName
        Write-Host "$($item.DisplayName) / $($item.UserPrincipalName) found" -ForegroundColor Green
    catch {
        Write-Color -T "$($item.DisplayName) / $($item.UserPrincipalName) ", "not found!" -C Blue, Red -B Black, DarkYellow

I've never been able to get try/catch to work properly with Get-Mailbox failure - see Get-Mailbox failure for work-around


UNC directory, change to

Set-Location \\yourDomain.com\share

updates, windows - see windows updates

upper case - see also proper case

append ".ToUpper" to string

user name environment variable - see environment variables

user (Office 365), does he exist?

$User = Get-MsolUser -UserPrincipalName $target -ErrorAction SilentlyContinue -ErrorVariable errorVariable
If ($User -ne $Null)
    write-host "$target exists" -Foregroundcolor green
} Else
    write-host "$target does not exist" -Foregroundcolor yellow

user (Office 365), restore when UserPrincipalName has a domain not accepted by the tenant

Restore-MsolUser -UserPrincipalName someuser@baddomain.com -AutoReconcileProxyConflicts -NewUserPrincipalName someuser@tenantname.onmicrosoft.com

user info (email) - Office 365

individual - use either UPN:

Get-MsolUser -userprincipalname test@yourdomain.com | fl

or objectID:

Get-MsolUser -ObjectId 81701046-cb37-439b-90ce-2afd9630af7d | fl


Get-MsolUser | Sort-Object DisplayName,UserPrincipalName

user profiles

Get-WmiObject -Class Win32_UserProfile -ComputerName 'SomePC'| ft LocalPath



Set-MsolUserPrincipalName -UserPrincipalName "becky.smith@yourcompany.onmicrosoft.com" -NewUserPrincipalName "becky.smith@yourcompany.com"


variable name has spaces - see spaces in property name

version, install latest

winget install Microsoft.PowerShell

if that fails:

Invoke-Expression "& { $(irm https://aka.ms/install-powershell.ps1) } -UseMSI"

As far as I can tell, this doesn't allow you to specify a version. Whether you want it or not, it's 7. But you can cancel it before it finishes.

version, which installed?



Visual Studio, install

Install-Script Install-VSCode -Scope CurrentUser; Install-VSCode.ps1

Visual Studio, shell setting, default - go to File → Preferences → Settings

Integrated Terminal

// Place your settings in this file to overwrite default and user settings.
     "terminal.integrated.shell.windows": "\\windows\\system32\\WindowsPowerShell\\v1.0\\powershell.exe"

or 64-bit PowerShell

     "terminal.integrated.shell.windows": "C:\\WINDOWS\\Sysnative\\WindowsPowerShell\\v1.0\\powershell.ex"

check if 64-bit:


should return True


Who am I?


Where am I? As in: which tenant am I on? - See which tenant am I on (right below)

Which tenant am I on? - the closest I can find is the command to list all the licenses that a tenant has available:


This will return a list of license SKUs. Embedded in each AccountSkuId will be the tenant name before the :. Pretty hokey.

Windows Services - see Services

Windows updates

$Session = New-Object -ComObject Microsoft.Update.Session
$Searcher = $Session.CreateUpdateSearcher()
$HistoryCount = $Searcher.GetTotalHistoryCount()
$Searcher.QueryHistory(0,$HistoryCount) | ForEach-Object {$_}

or try

gwmi -cl win32_reliabilityRecords | select ProductName, SourceName, TimeGenerated, EventIdentifier, Reco0rdNumber, __CLASS | ft


gwmi win32_quickfixengineering |sort installedon -desc | ft

Windows version - the single-line commands below will return various aspects

(Get-WmiObject Win32_OperatingSystem).Version # short number e.g., 10.0.18363
[environment]::OSVersion.Version # same info as above but in table form
Get-CimInstance Win32_OperatingSystem # table with more entries including serial number and system directory
Get-WindowsEdition -Online # edition e.g., Core
gwmi win32_operatingsystem | % caption # plain English version e.g., Microsoft Windows 10 Home
(gwmi win32_operatingsystem).caption # plain English version e.g., Microsoft Windows 10 Home


# Install from Powershell Gallery https://www.powershellgallery.com/packages/PSWriteColor
Install-Module -Name PSWriteColor
Import-Module PSWriteColor

tabs and vertical space

Write-Color -Text "This is text in Green ",
"followed by red ",
"and then we have Magenta... ",
"isn't it fun? ",
"Here goes DarkCyan" -Color Green, Red, Magenta, White, DarkCyan -StartTab 3 -LinesBefore 1 -LinesAfter 1

with background colors and aliases

Write-Color -T "My text", " is ", "all colorful" -C Yellow, Red, Green -B Green, Green, Yellow


xml, parse

If you include a line like this in your DNS:

        _dmarc	3600	 IN 	TXT	v=DMARC1; pct=100; p=none; rua=mailto:someUser@yourDomain.com

you'll get XML records mailed to you from various email providers such as yahoo & google. The code below will get some of the columns; not all columns fetched for simplicity's sake.

$dir = [environment]::getfolderpath("mydocuments")
$fileNameOnly = "google.com!youdDomain.com!1556064000!1556150399.xml"
$fileName = "$dir\$fileNameOnly"
[xml]$xml = Get-Content -Path $fileName
$xml_row = $xml.SelectNodes("//row")
$objResult = @()
foreach ($row in $xml_row) {
    $rowRecord = $row | select @{n='sourceIP';e={$_.source_IP}},@{n='DKIM';e={$_.policy_evaluated.dkim}},@{n='spf';e={$_.policy_evaluated.spf}}
    $objResult += $rowRecord
$objResult | ogv



zip a file

$src_folder = "D:\project"
$destfile = "D:\project.zip"
$compressionLevel = [System.IO.Compression.CompressionLevel]::Optimal
$includeBaseDir = $false
[System.IO.Compression.ZipFile]::CreateFromDirectory($src_folder, $destfile, $compressionLevel, $includeBaseDir)