Powershell CheatSheet
Microsoft Graph
Check for a role assignment
Get-RoleGroupMember "Organization Management" | foreach {
$user = $_.Name
Get-ManagementRoleAssignment -RoleAssignee $user | Where-Object {$_.Role -eq "ApplicationImpersonation"} | Select-Object @{Name="User";Expression={$user}}, Role
} | ft -a
Get Group Membership
Get-MgGroup -ConsistencyLevel eventual -Count groupCount -Search '"DisplayName:something"' | % {
$id = $_.id; Get-MgGroupMember -GroupId $id | % {
get-mguser -UserId $_.id
}
}
Exchange Online
Get Locale and Regional info for User and Exchange online
# Takes ages to run for some reason.
# Maybe faster putting mailboxes into a variable first.
Get-Maibox | % {
$mailbox = $_
Write-Host "Working on $mailbox.userprincipalname" -ForegroundColor Yellow
$mailboxConfig = Get-MailboxRegionalConfiguration -Identity $mailbox.UserPrincipalName
[PSCustomObject]@{
UserPrincipalName = if ($null -eq $mailbox.UserPrincipalName) { "Null" } else { $mailbox.UserPrincipalName }
DateFormat = if ($null -eq $mailboxConfig.DateFormat) { "Null" } else { $mailboxConfig.DateFormat }
Language = if ($null -eq $mailboxConfig.Language) { "Null" } else { $mailboxConfig.Language }
TimeFormat = if ($null -eq $mailboxConfig.TimeFormat) { "Null" } else { $mailboxConfig.TimeFormat }
TimeZone = if ($null -eq $mailboxConfig.TimeZone) { "Null" } else { $mailboxConfig.TimeZone }
}
} | ft -a
#### Setting the items
# Set Commands
# Connect to MG-Graph and Exchange Online First
'account@domain.com' | % {
Write-Host "Working on, $_..."
Update-MgUser -userId $_ -PreferredLanguage en-GB
Update-MgUser -userId $_ -UsageLocation GB
Set-MailboxRegionalConfiguration -UserId $_ -Language en-GB -DateFormat "dd/MM/yyyy" -TimeFormat "HH:mm" -TimeZone "GMT Standard Time"
Set-MailboxCalendarConfiguration -Identity $_ -WorkingHoursTimeZone "GMT Standard Time"
}
Azure
Re-instate\add partner access to subscription
Install-Module -Name Az.Resources -Force -Verbose
Import-Module -Name Az.Resources -Verbose -MinimumVersion 4.1.1
Connect-AzAccount -Tenant <customer tenant>
Set-AzContext -SubscriptionId <customer subscriptions>
New-AzRoleAssignment -ObjectId <principal ID> -RoleDefinitionName "Owner" -Scope "/subscriptions/<customer subscription>" -ObjectType "ForeignGroup"
Windows
Using Blocks in functions and scripts
## Using Blocks in functions\scripts
function Test {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string[]]$InputValues
)
Begin {
# The Begin block is used for initialization.
# This is where you define variables, open files, create objects, or set up anything needed before processing starts.
}
Process {
# The Process block runs once for each item in the input.
# This is where you apply the core logic of your function to each input object.
}
End {
# The End block is used for finalization.
# This is where you clean up resources, summarize results, or return final output.
}
}
Check for Pending Reboot
$keys = @(
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing",
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update",
"HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager"
)
$rebootRequired = $false
foreach ($key in $keys) {
if (Test-Path $key) {
$properties = Get-ItemProperty -Path $key -ErrorAction SilentlyContinue
if ($properties -match "RebootPending|RebootRequired|PendingFileRenameOperations") {
Write-Output "Reboot required due to: $key"
$rebootRequired = $true
}
}
}
if (-not $rebootRequired) {
Write-Output "No reboot required."
}
Easily remove File Name from a path
Uses a dotnet method
$outputLocation = "C:\Temp\filename - $(Get-Date -uformat "%A %dth %B").xlsx"
[System.IO.Path]::GetDirectoryName($outputLocation)
# Output will be
"C:\Temp"
Parse dateTime with dotnet Method
# Takes three items, value, format, third we put as null
[DateTime]::ParseExact("Date Value", 'dd.MM.yyyy', $null)
Figuring out return codes from MSI files
# Generic return statement string to search for.
`gc C:\windows\temp\forcepointv25install.log | Select-String 'MainEngineThread is returning'`
Otherwise, try searching for `return` or, `gc C:\windows\temp\forcepointv25Uninstall.log -Tail 10` to check the last 10 lines.
Catpuring Process Exist Codes
Checking if RegSVR32 errors upon launching. -PassThru 2>&1: Capture error stream for exit code (try\catch handles Pwsh Exectuion error, if statement will handle application exit code.)
try{
$process = Start-Process Regsvr32.exe -ArgumentList "/s $file" -NoNewWindow -Wait -PassThru 2>&1
if ($process.ExitCode -ne 0) { write-Host "Exit Code: $($process.ExitCode)" }
} catch {
LogToFile -Message "Error: $($Error[0].Exception.Message)"
}
Outputting the time to run the script
$startTime = Get-Date
# Script logic here
$endTime = Get-Date
Write-Host "Completed in $([math]::Round(($endTime - $startTime).TotalMinutes, 2)) minutes."
Create a new EventLog Source
# Create event log source (run once as admin)
New-EventLog -LogName Application -Source "MyAppSource"
# Log an event
Write-EventLog -LogName Application -Source "MyAppSource" -EventId 1000 -EntryType Information -Message "This is a test log message."
Pattern Matching with Regex
Iterating through a file and checking if the pattern matches.
Pattern for both examples as below:
$pattern = '\[(?<Date>\d{2}\.\d{2}\.\d{4})\s(?<Time>\d{2}:\d{2}:\d{2}\.\d{3})\]\s+\d+\s+\((?<Misc>\d+)\)\s+(?<Message>.*)'
Powershell method
###########################
# PowerShell Only Way #
###########################
$parsedlogs = @()
$count = 0
foreach ($line in $logs){
$count++
Write-Progress -PercentComplete ($count / $logs.count * 100) `
-Status "Processing Matches" `
-Activity "Processing match $count of $($logs.Count)"
if ($line -match $pattern) {
$parsedlogs += [PSCustomObject]@{
Date = $matches["Date"]
Time = $matches["Time"]
Message = $matches["Message"]
Misc = $matches["Misc"]
}
}
}
Write-Progress -Activity "Processing Matches" -Completed
#endRegion
DOTNET Regex method
Performs better with larger datasets.
####################
# Using DotNet #
####################
$logs = $logs -join "`r`n"
$items = [regex]::Matches($logs,$pattern)
$results = [System.Collections.ArrayList]@()
$count = 0
foreach($item in $items){
$count++
Write-Progress -PercentComplete ($count / $items.count * 100) `
-Status "Processing Matches" `
-Activity "Processing match $count of $($items.Count)"
$results.Add([PSCustomObject]@{
Date = [DateTime]::ParseExact($item.Groups["Date"].Value, 'dd.MM.yyyy', $null)
Time = [DateTime]::ParseExact($item.Groups["Time"].Value, 'HH:mm:ss.fff', $null)
Message = $item.Groups["Message"].Value
Misc = $item.Groups["Misc"].Value
}) | Out-Null # Stops powershell printing the new array size
}
Write-Progress -Activity "Processing Matches" -Completed
#endRegion
For Loop Examples
# Break the sites into chunks
$chunkSize = 2500
# $i = 0 - Initialises the loop.
# $i -lt $parsedSites.Count - Whilst $i is less than the count of $parsedSites, keep looping.
# $i += $chunkSize - After each loop iteration, $i increases by $chunkSize (which is 2500 in your case).This moves $i to the start index of the next chunk.
<#
for (Initialises the loop; Whilst $i is less than the count of $parsedSites, keep looping; $i increases by $chunkSize)
{
<Statement list>
}
#>
for ($i = 0; $i -lt $parsedSites.Count; $i += $chunkSize) {
$start = $i
# Stores the end of the array.
# [math]::Min(a, b) returns the smaller of a and b, preventing out-of-bounds errors.
# This returns whatever is smaller, $i + $chunkSize - 1 or $parsedSites.Count - 1.
$end = [math]::Min($i + $chunkSize - 1, $parsedSites.Count - 1)
# Assign the chunk of sites to the new variable
New-Variable -Name "spoSites$($start)to$($end)" -Value $parsedSites[$start..$end]
# Optionally, display the chunk variable's name and count to confirm
Write-Output "Created variable: $varName"
}
# Does the same but creates array's within hashtable
$chunks = @{}
for ($i = 0; $i -lt $parsedSites.Count; $i += $chunkSize) {
$start = $i
$end = [math]::Min($i + $chunkSize - 1, $parsedSites.Count - 1)
# Store the chunk in a hashtable with a key
$chunks["spoSites$($i)to$($end)"] = $parsedSites[$start..$end]
Write-Output "Created chunk: spoSites$($i)to$($end)"
}
Allow Write to USB
(Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Policies\Microsoft\FVE -Name RDVDenyWriteAccess).RDVDenyWriteAccess | % {
$key = $_
if($key -eq 1){
Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Policies\Microsoft\FVE -Name RDVDenyWriteAccess -Value 0
} else {
Write-Host "USBs should be writeable"
}
}
Check Process usage and commandline
# Array of strings into the search for the process
"Firefox","msi" | % {
$string = $_
gcim win32_Process | ? { $_.name -match $string -or $_.commandline -match $string } | `
Select name, creationdate, commandline
}
# Kill the process like so
gcim win32_process | ? name -match "msi" | ForEach-Object { Stop-Process -Id $_.ProcessId }
Delete keys from registry
# useful for removing app keys for win32apps
(gci -path HKLM:\SOFTWARE\Microsoft\IntuneManagementExtension\Win32Apps -Recurse | `
? Name -match "stringhere").PSPath | % {
Write-Host "Deleting key, $_ "
Remove-Item -Path $_ -Recurse | out-null
}
Search for strings between two strings
[CmdletBinding()]
param (
[Parameter()]
[array]
$logData = $proxylogs
)
# Initialize an array to store the extracted logs
$extractedLogs = @()
# Define start and end patterns
$startPattern = "Activating Job:" # Starting pattern
$endPattern = "Job Completed:" # Ending pattern
# Initialize capturing flag
$capturing = $false
# Loop through each line in the log data array
foreach ($line in $logData) {
# Start capturing after finding the "Activating Job:" pattern
if ($line -match $startPattern) {
$capturing = $true
}
# If capturing is true, add the current line to the extracted logs
if ($capturing) {
$extractedLogs += $line
}
# Stop capturing after finding the "Job Completed:" pattern
if ($line -match $endPattern -and $capturing) {
$capturing = $false
}
}
# Output the extracted logs
$extractedLogs
Get FISMO roles
# ` backtick denotes command continues on next line, makes this more easy to read.
Get-ADForest | `
Select SchemaMaster,DomainNamingMaster,`
@{ n='pdcemulator';e={ Get-ADDomain | Select -ExpandProperty pdcemulator} }, `
@{ n='ridmaster';e={ Get-ADDomain | Select -ExpandProperty ridmaster} }, `
@{ n='infrastructuremaster';e={ Get-ADDomain | Select -ExpandProperty infrastructuremaster} } | `
Format-List
Convert UNIX time
Function Convert-FromUnixDate ($UnixDate) {
[timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($UnixDate))
}
Check AzureAD\AAD Sync
# Get the status of the last 10 syncs.
Get-ADSyncRunStepResult | Select -First 10 StepNumber,StepResult, StartDate, EndDate | ft -a
# Get Scheduler status.
Get-ADSyncScheduler
PSSendGrid Example
$sendGridToken = "SG.apikey"
$attachmentPath = $reportFile # File path to the attachment\data.
$attachmentDisposition = "attachment" # Value can either be "attachment" or "inline", inlines is for images, check the command help examples for info.
$emailBody = @(
"Ener text here, the '`n' adds a carrige return `n"
) -join " "
try {
# Basic email with just content.
$Parameters = @{
FromAddress = "fromaddress@domain.com"
ToAddress = "AddressOne@domain.com", "AddressTwo@domain.com"
Subject = "Subject text"
Body = $emailBody
AttachmentPath = $attachmentPath
AttachmentDisposition = $attachmentDisposition
Token = $sendGridToken
FromName = "Name to show who from."
ToName = "test"
}
} catch {
$_
}
Send-PSSendGridMail @Parameters
Uninstall using Registry keys
# Find uninstall string
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*","HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" | % {
Get-ItemProperty $_ | Where-Object { $_.DisplayName -match 'greenshot' } | % {
$app = $_
[PSCustomObject]@{
Name = $app.displayname
Version = $app.DisplayVersion
InstallDate = [datetime]::ParseExact($app.InstallDate, "yyyyMMdd", $null)
UninstallString = $app.UninstallString
MSIExecCommand = if($app.UninstallString -match "MsiExec.exe") {"MSIExec.exe " + ($app.UninstallString -replace '/I|/X', '/x ' -replace "MsiExec.exe","") + " /NORESTART"} else {"N\A"}
Path = $app.PSPath -replace "Microsoft.PowerShell.Core\\Registry::",""
}
}
}
# Uninstall
"HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
"HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" |
% {
Get-ItemProperty $_ |
Select DisplayName, UninstallString |
Where-Object { $_.DisplayName -match '7-zip' } |
ForEach-Object {
$uninstallString = $_.UninstallString.replace('MsiExec.exe','').replace('/X','/x ')
$arguments = "$uninstallString /norestart /L*v `"C:\temp\7zipUninstall.log`""
Write-Host "Executing: MsiExec.exe $arguments"
$process = Start-Process -FilePath "MsiExec.exe" -ArgumentList $arguments -Wait -PassThru
Write-Host "Exit code: $($process.ExitCode)"
}
}
File\Folder size reports
########################## Basic File report for the directory you're currently in. ##########################
Get-ChildItem | Select FullName,@{l='Size(GB)';e={ [math]::round($_.length/1024,2) } }, CreationTime, LastAccessTime, LastWriteTime
########################## Folder size. ##########################
# /1MB - Convert to MB.
# /1GB - Convert to GB
(gci -path "Folder Path" -Recurse | measure -Property Length -Sum).sum /1MB
# Round to 2 decimal places.
[Math]::round((gci .\Networking\ -Recurse | measure -Property Length -Sum).sum /1MB,2)
#### Another one ####
$startFolder = "S:\Citrix_Profiles"
$colItems = Get-ChildItem $startFolder | Where-Object {$_.PSIsContainer -eq $true} | Sort-Object
$Output = foreach ($i in $colItems) {
$subFolderItems = Get-ChildItem $i.FullName -recurse -force | Where-Object {$_.PSIsContainer -eq $false} | Measure-Object -property Length -sum | Select-Object Sum
New-Object psobject -property @{
"Size(GB)" = “{0:N2}” -f ($subFolderItems.sum /1GB)
"Location" = $i.FullName
}
}
$Output | Export-csv -Path 'C:\Temp\FolderReport-$startFolder.csv' -NoTypeInformation
Find files for a string in their content
# Change the search parameters as needed.
$filesToSearch = (Get-ChildItem | Where-Object { $_.lastwritetime -gt (get-date).addDays(-1) -and -not $_.PSIsContainer }).FullName
foreach ($file in $filesToSearch) {
# Strings for it to look for.
$interestingStrings = Get-Content -Path $file | Select-String -SimpleMatch 'Endpoint Protection'
if (($interestingStrings | Measure-Object).Count -ne 0) {
Write-Host "Found refences in" $file
}
}
Getting Windows Events
$logNames = @(
'Application',
'System',
'Microsoft-Windows-Windows Defender/Operational'
)
$properties = @(
'LogName',
'timeCreated',
'id',
'leveldisplayname',
'message'
)
Get-WinEvent -LogName $logNames -MaxEvents 10 | Select-Object $properties | Where-Object Message -like "*Defender*" | Format-List
<# Another example which runs a bit quicker #>
Get-WinEvent -FilterHashTable @{ LogName = 'System' ; id = 107 ; StartTime = (get-date).AddDays(-1) }
<# Useful Event IDs
12 The operating system started at system time
13 The operating system is shutting down at system time.
20 The last shutdown's success status was true. The last boot's success status was true.
42 The sytem is entering sleep.
107 The system has returned from sleep.
109 The kernel power manager has initiated a shutdown transition.
1074 The process Explorer.EXE has initiated the shutdown of computer on behalf of user for the following reason
6005 The Event log service was started.
6006 The Event log service was stopped.
6013 The system uptime is 10 seconds.
#>
Grab Kerberos Events
# Get the event logs with relevant IDs
$events = Get-WinEvent -FilterHashTable @{ LogName = 'security' ; id = 4768,4769,4770,4771,4772,4773 ; StartTime = (get-date).Addhours(-8) }
# Filtering options
$events | ? message -Match 'Filter'| ft -AutoSize -wrap # -wrap stops truncating output.
# Get events matching a userID with the matching error code\event "success".
$events | ? {( $_.message -Match 'userID') -and ( $_.message -Match '0x0') -and ($_.Id -Match '4768' ) } | ft -AutoSize -Wrap
# Search for messages with a userID in it.
$events | ? message -Match 'userID'| ft -AutoSize -wrap
# Search multiple machines.
$listOfDevices = 'server1','server2'
$kerberosEvents = foreach ($device in $listOfDevices) {
Write-Host "Getting logs from $($device)." -ForegroundColor Yellow
Get-WinEvent -ComputerName $device -FilterHashTable @{ LogName = 'security' ; id = 4768,4769,4770,4771,4772,4773 ; StartTime = (get-date).AddMinutes(-30) }
Write-Host "Log collection from $($device) successfull logs." -ForegroundColor Green
}
Encrypt and De-crypt Credentials
###############################################
# Add password to Credential Manager. #
###############################################
# String to be secured in Credential Manager
$unsecureString = 'Password123'
# Install the module.
Install-Module -Name CredentialManager
# Create the new Credential in Credentials Manager.
New-StoredCredential -Target 'Target Name' -UserName 'Username' -Password $unsecureString -Type Generic -Persist LocalMachine
#endRegion
################################################
# Retrieve password from Credential Manager. #
################################################
# Get the password secure string.
$cred = (Get-StoredCredential -Target 'Target Name' -Type Generic).Password
# Marshall is a .NET class: Allocates an unmanaged binary string (BSTR) and copies the contents of a managed SecureString object into it.
$BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($cred)
# Allocates a managed String and copies all characters up to the first null character from a string stored in unmanaged memory into it.
[System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
#endRegion
Detect logged in user account name
(Get-Childitem -path "C:\Users\$((Get-Process -IncludeUserName -Name explorer | Select-Object UserName -Unique).username.split('\')[1])" | ? Name -Match 'OneDrive -').FullName
Find Expiring Certificates
# Find all Certificates on a device
Get-ChildItem -Path Cert: -Recurse -ExpiringInDays 10 | select thumbprint, notafter | sort notafter
# Find a Cert via Thumbprint
gci -Path Cert: -Recurse | ? thumbprint -match thumbprint | fl
Creating a Scheduled task
$scheduledTaskName = "Task Name"
$scheduledTaskFolder = "Folder Name"
$action = New-ScheduledTaskAction -Execute 'Powershell.exe' -Argument '-WindowStyle hidden -command "& C:\temp\script.ps1"'
$trigger = New-ScheduledTaskTrigger -AtLogon
$principal = New-ScheduledTaskPrincipal -GroupId "Users"
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Minutes 5) -Hidden
$task = New-ScheduledTask -Action $action -Principal $principal -Trigger $trigger -Settings $settings
try {
Register-ScheduledTask -TaskName $scheduledTaskName -InputObject $task -TaskPath $scheduledTaskFolder
}
catch {
WriteToLogFile "Unable to register task."
}
Enable TLS for a session
# Required on older servers when trying to install from nuget.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Search for mac on a network
1..254 | foreach { ping 10.140.0.$_ ; arp -a | select-string '00-c0' }
Find and remove strings for a file
$LinesToremove = @(
"String 1",
"String 2"
)
foreach ($string in $LinesToremove){
(select-string -Path $hostsFilePath -Pattern $string -NotMatch ).line | Set-Content $hostsFilePath
Start-Sleep -Milliseconds 100
}
CPU and Memory usage
for ($i=0; $i -lt 10; $i++) {
$cpuTime = [Math]::Round((Get-Counter '\Processor(_Total)\% Processor Time').CounterSamples.CookedValue,2)
$availMem = [Math]::Round((Get-Counter '\Memory\Available MBytes').CounterSamples.CookedValue,2)
$totalRam = (Get-CimInstance Win32_PhysicalMemory | Measure-Object -Property capacity -Sum).sum / 1MB
Write-Host "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss'): " -ForegroundColor Green -NoNewline
Write-Host " CPU Utilisation (%): $cpuTime, " -NoNewline -ForegroundColor Magenta
Write-Host " Available Memory (GB): $( [Math]::Round(($availMem /1024),2) ), " -NoNewline -ForegroundColor Cyan
Write-Host " Available Memory (%): $( [Math]::Round(($availMem / $totalRam * 100),2))." -ForegroundColor Yellow
Start-Sleep -Seconds 60
}
Intune
Device Sync
# Run these as a user:
intunemanagementextension://syncapp
intunemanagementextension://synccompliance
# Run as admin
Get-ScheduledTask -TaskName "Schedule #3 created by enrollment client" | Start-ScheduledTask
Find\Search for Strings in the IME Logs
gc C:\ProgramData\Microsoft\IntuneManagementExtension\Logs\intuneManagement*.log | `
Select-String -Pattern "retryCount:" | % {
if ($_ -match '<!\[LOG\[(?<Message>.*?)\]LOG\]!><time="(?<Time>[\d:.]+)" date="(?<Date>\d{1,2}-\d{1,2}-\d{4})"(?<Misc>.*?)>') {
[PSCustomObject]@{
Date = $matches.Date
Time = $matches.Time
Message = $matches.Message.Trim()
Misc = $matches.Misc.Trim()
}
}
else {
[PSCustomObject]@{ RawLog = $_ }
}
}