Cloud Scripts
Ready-to-run PowerShell scripts for Exchange Online, Microsoft 365, and Entra ID. Edit variables inline, copy to clipboard, or download as a .ps1 file.
Install-Module ExchangeOnlineManagement -Force
Connect-ExchangeOnline -UserPrincipalName ##ADMIN_UPN##
Get-InboxRule -Mailbox "##MAILBOX##" |
Select-Object Name, Enabled, Description,
@{N="Conditions";E={$_.Conditions | Out-String}},
@{N="Actions";E={$_.Actions | Out-String}} |
Format-List
Install-Module ExchangeOnlineManagement -Force
Connect-ExchangeOnline -UserPrincipalName ##ADMIN_UPN##
# Mailbox-level forwarding
Get-Mailbox -ResultSize Unlimited |
Where-Object { $_.ForwardingSmtpAddress -ne $null } |
Select-Object DisplayName, UserPrincipalName, ForwardingSmtpAddress, DeliverToMailboxAndForward |
Format-Table -AutoSize
# Inbox rule forwarding
Get-Mailbox -ResultSize Unlimited | ForEach-Object {
$rules = Get-InboxRule -Mailbox $_.UserPrincipalName -ErrorAction SilentlyContinue |
Where-Object { $_.ForwardTo -or $_.RedirectTo -or $_.ForwardAsAttachmentTo }
if ($rules) {
Write-Host "Mailbox: $($_.UserPrincipalName)" -ForegroundColor Yellow
$rules | Select-Object Name, ForwardTo, RedirectTo | Format-List
}
}
Install-Module ExchangeOnlineManagement -Force
Connect-ExchangeOnline -UserPrincipalName ##ADMIN_UPN##
Get-Mailbox -ResultSize Unlimited |
Get-MailboxStatistics |
Select-Object DisplayName,
@{N="Size (MB)"; E={[math]::Round($_.TotalItemSize.Value.ToMB(), 2)}},
ItemCount,
@{N="Deleted (MB)"; E={[math]::Round($_.TotalDeletedItemSize.Value.ToMB(), 2)}},
LastLogonTime |
Sort-Object "Size (MB)" -Descending |
Format-Table -AutoSize
Install-Module ExchangeOnlineManagement -Force
Connect-ExchangeOnline -UserPrincipalName ##ADMIN_UPN##
Get-Mailbox -ResultSize Unlimited | ForEach-Object {
$mb = $_
$mb.EmailAddresses | Where-Object { $_ -like "smtp:*" } | ForEach-Object {
[PSCustomObject]@{
DisplayName = $mb.DisplayName
UPN = $mb.UserPrincipalName
Type = $mb.RecipientTypeDetails
EmailAddress = $_ -replace "smtp:", ""
IsPrimary = $_ -clike "SMTP:*"
}
}
} | Sort-Object DisplayName | Format-Table -AutoSize
Install-Module ExchangeOnlineManagement -Force
Connect-ExchangeOnline -UserPrincipalName ##ADMIN_UPN##
$upn = "##USER_UPN##"
$groups = Get-Recipient -ResultSize Unlimited -RecipientTypeDetails MailUniversalDistributionGroup, MailUniversalSecurityGroup |
Where-Object { (Get-DistributionGroupMember -Identity $_.Identity -ResultSize Unlimited).PrimarySmtpAddress -contains $upn }
if ($groups.Count -eq 0) {
Write-Host "No groups found for $upn" -ForegroundColor Yellow
} else {
foreach ($group in $groups) {
Remove-DistributionGroupMember -Identity $group.Identity -Member $upn -Confirm:$false
Write-Host "Removed from: $($group.DisplayName)" -ForegroundColor Green
}
Write-Host "`nTotal groups removed from: $($groups.Count)" -ForegroundColor Cyan
}
Install-Module ExchangeOnlineManagement -Force
Connect-ExchangeOnline -UserPrincipalName ##ADMIN_UPN##
$report = @()
$mailboxes = Get-Mailbox -ResultSize Unlimited
foreach ($mb in $mailboxes) {
# FullAccess
Get-MailboxPermission -Identity $mb.Identity |
Where-Object { $_.AccessRights -like "*FullAccess*" -and !$_.IsInherited -and $_.User -notlike "NT AUTHORITY*" } |
ForEach-Object {
$report += [PSCustomObject]@{
Mailbox = $mb.PrimarySmtpAddress
Delegate = $_.User
Permission = "FullAccess"
}
}
# SendAs
Get-RecipientPermission -Identity $mb.Identity |
Where-Object { $_.Trustee -notlike "NT AUTHORITY*" } |
ForEach-Object {
$report += [PSCustomObject]@{
Mailbox = $mb.PrimarySmtpAddress
Delegate = $_.Trustee
Permission = "SendAs"
}
}
# SendOnBehalf
$mb.GrantSendOnBehalfTo | ForEach-Object {
$report += [PSCustomObject]@{
Mailbox = $mb.PrimarySmtpAddress
Delegate = $_
Permission = "SendOnBehalf"
}
}
}
$report | Sort-Object Mailbox | Format-Table -AutoSize
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'User.ReadWrite.All'
$upn = "##USER_UPN##" # Block sign-in Update-MgUser -UserId $upn -AccountEnabled:$false # Revoke all active sessions Revoke-MgUserSignInSession -UserId $upn Write-Host "Blocked sign-in and revoked sessions for: $upn" -ForegroundColor Green # Verify Get-MgUser -UserId $upn | Select-Object DisplayName, UserPrincipalName, AccountEnabled
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'UserAuthenticationMethod.Read.All', 'User.Read.All'
$users = Get-MgUser -All -Property DisplayName, UserPrincipalName, AccountEnabled
$report = foreach ($user in $users) {
$methods = Get-MgUserAuthenticationMethod -UserId $user.Id
$methodTypes = $methods.AdditionalProperties."@odata.type" -replace "#microsoft.graph.", ""
[PSCustomObject]@{
DisplayName = $user.DisplayName
UPN = $user.UserPrincipalName
AccountEnabled = $user.AccountEnabled
MFAMethodCount = $methods.Count
Methods = ($methodTypes | Where-Object { $_ -ne "passwordAuthenticationMethod" }) -join ", "
HasMFA = ($methods.Count -gt 1)
}
}
$report | Sort-Object HasMFA, DisplayName | Format-Table -AutoSize
Write-Host "`nUsers without MFA: $(($report | Where-Object { !$_.HasMFA }).Count)" -ForegroundColor Yellow
Write-Host "Users with MFA: $(($report | Where-Object { $_.HasMFA }).Count)" -ForegroundColor Green
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'User.Read.All', 'AuditLog.Read.All'
Get-MgUser -All -Property DisplayName, UserPrincipalName, AccountEnabled, SignInActivity |
Select-Object DisplayName, UserPrincipalName, AccountEnabled,
@{N="LastSignIn"; E={$_.SignInActivity.LastSignInDateTime}},
@{N="DaysSinceSignIn"; E={
if ($_.SignInActivity.LastSignInDateTime) {
(New-TimeSpan -Start $_.SignInActivity.LastSignInDateTime -End (Get-Date)).Days
} else { "Never" }
}} |
Sort-Object LastSignIn -Descending |
Format-Table -AutoSize
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'User.Read.All', 'AuditLog.Read.All'
$inactiveDays = ##DAYS##
$cutoff = (Get-Date).AddDays(-$inactiveDays)
Get-MgUser -All -Property DisplayName, UserPrincipalName, AccountEnabled, SignInActivity |
Where-Object {
$_.AccountEnabled -eq $true -and (
$_.SignInActivity.LastSignInDateTime -lt $cutoff -or
$null -eq $_.SignInActivity.LastSignInDateTime
)
} |
Select-Object DisplayName, UserPrincipalName,
@{N="LastSignIn"; E={$_.SignInActivity.LastSignInDateTime ?? "Never"}},
@{N="DaysInactive"; E={
if ($_.SignInActivity.LastSignInDateTime) {
(New-TimeSpan -Start $_.SignInActivity.LastSignInDateTime -End (Get-Date)).Days
} else { "Never signed in" }
}} |
Sort-Object LastSignIn |
Format-Table -AutoSize
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'User.Read.All'
# Get friendly SKU name mapping
$skuMap = @{}
Get-MgSubscribedSku | ForEach-Object { $skuMap[$_.SkuId] = $_.SkuPartNumber }
Get-MgUser -All -Property DisplayName, UserPrincipalName, AccountEnabled, AssignedLicenses |
Where-Object { $_.AssignedLicenses.Count -gt 0 } |
Select-Object DisplayName, UserPrincipalName, AccountEnabled,
@{N="Licences"; E={
($_.AssignedLicenses | ForEach-Object { $skuMap[$_.SkuId] ?? $_.SkuId }) -join ", "
}} |
Sort-Object DisplayName |
Format-Table -AutoSize
$total = (Get-MgUser -All -Filter "assignedLicenses/`$count ne 0" -ConsistencyLevel eventual -CountVariable c; $c)
Write-Host "`nTotal licensed users: $total" -ForegroundColor Cyan
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'User.ReadWrite.All'
$upn = "##USER_UPN##"
Update-MgUser -UserId $upn -PasswordProfile @{
ForceChangePasswordNextSignIn = $true
}
Write-Host "Password reset flag set for: $upn" -ForegroundColor Green
Write-Host "User will be prompted to change password on next sign-in." -ForegroundColor Yellow
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'RoleManagement.Read.Directory', 'User.Read.All'
$roles = Get-MgDirectoryRole -All
foreach ($role in $roles | Sort-Object DisplayName) {
$members = Get-MgDirectoryRoleMember -DirectoryRoleId $role.Id
if ($members.Count -gt 0) {
Write-Host "`n=== $($role.DisplayName) ===" -ForegroundColor Cyan
$members | ForEach-Object {
$user = Get-MgUser -UserId $_.Id -ErrorAction SilentlyContinue
if ($user) {
Write-Host " $($user.DisplayName) <$($user.UserPrincipalName)>"
}
}
}
}
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'User.Read.All', 'AuditLog.Read.All'
Get-MgUser -All -Filter "userType eq 'Guest'" -Property DisplayName, UserPrincipalName, Mail, CreatedDateTime, SignInActivity |
Select-Object DisplayName, UserPrincipalName, Mail,
@{N="Created"; E={$_.CreatedDateTime}},
@{N="LastSignIn"; E={$_.SignInActivity.LastSignInDateTime ?? "Never"}} |
Sort-Object Created -Descending |
Format-Table -AutoSize
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'User.ReadWrite.All'
$upn = "##USER_UPN##" Revoke-MgUserSignInSession -UserId $upn Write-Host "All sessions revoked for: $upn" -ForegroundColor Green Write-Host "The user will be signed out of all apps and devices immediately." -ForegroundColor Yellow # Also block sign-in if this is a security incident # Update-MgUser -UserId $upn -AccountEnabled:$false
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'Policy.Read.All'
Get-MgIdentityConditionalAccessPolicy -All |
Select-Object DisplayName, State,
@{N="IncludeUsers"; E={$_.Conditions.Users.IncludeUsers -join ", "}},
@{N="ExcludeUsers"; E={$_.Conditions.Users.ExcludeUsers -join ", "}},
@{N="IncludeApps"; E={$_.Conditions.Applications.IncludeApplications -join ", "}},
@{N="GrantControls"; E={$_.GrantControls.BuiltInControls -join ", "}} |
Format-List
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'Organization.Read.All'
Get-MgSubscribedSku |
Select-Object SkuPartNumber,
@{N="Total"; E={$_.PrepaidUnits.Enabled}},
ConsumedUnits,
@{N="Available"; E={$_.PrepaidUnits.Enabled - $_.ConsumedUnits}} |
Sort-Object SkuPartNumber |
Format-Table -AutoSize
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'User.Read.All'
Get-MgUser -All -Property DisplayName,UserPrincipalName,AccountEnabled,CreatedDateTime |
Where-Object { $_.AccountEnabled -eq $false } |
Select-Object DisplayName, UserPrincipalName, CreatedDateTime |
Sort-Object DisplayName |
Format-Table -AutoSize
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'UserAuthenticationMethod.Read.All', 'User.Read.All'
$upn = "##USER_UPN##"
$user = Get-MgUser -UserId $upn -ErrorAction SilentlyContinue
if (-not $user) {
Write-Host "User not found: $upn" -ForegroundColor Red
return
}
Get-MgUserAuthenticationMethod -UserId $user.Id |
Select-Object Id,
@{N="MethodType"; E={$_.AdditionalProperties."@odata.type" -replace "#microsoft.graph.",""}} |
Format-Table -AutoSize
Install-Module ExchangeOnlineManagement -Force
Connect-ExchangeOnline -UserPrincipalName ##ADMIN_UPN##
Get-Mailbox -RecipientTypeDetails UserMailbox -ResultSize Unlimited |
ForEach-Object {
$ar = Get-MailboxAutoReplyConfiguration -Identity $_.UserPrincipalName
[PSCustomObject]@{
DisplayName = $_.DisplayName
UserPrincipalName= $_.UserPrincipalName
AutoReplyState = $ar.AutoReplyState
InternalMessage = $ar.InternalMessage
ExternalMessage = $ar.ExternalMessage
}
} |
Sort-Object DisplayName |
Format-Table -AutoSize
Install-Module ExchangeOnlineManagement -Force
Connect-ExchangeOnline -UserPrincipalName ##ADMIN_UPN##
$suspicious = @()
Get-Mailbox -ResultSize Unlimited | ForEach-Object {
$mbx = $_
Get-InboxRule -Mailbox $mbx.UserPrincipalName -ErrorAction SilentlyContinue |
Where-Object {
$_.ForwardTo -or $_.RedirectTo -or $_.ForwardAsAttachmentTo -or
$_.DeleteMessage -eq $true -or $_.MoveToFolder -ne $null
} | ForEach-Object {
$suspicious += [PSCustomObject]@{
Mailbox = $mbx.UserPrincipalName
RuleName = $_.Name
Enabled = $_.Enabled
ForwardTo = $_.ForwardTo -join "; "
RedirectTo = $_.RedirectTo -join "; "
DeleteMsg = $_.DeleteMessage
MoveToFolder = $_.MoveToFolder
}
}
}
if ($suspicious.Count -eq 0) {
Write-Host "No suspicious rules found." -ForegroundColor Green
} else {
Write-Host "WARNING: $($suspicious.Count) suspicious rule(s) found!" -ForegroundColor Red
$suspicious | Format-List
}
Install-Module ExchangeOnlineManagement -Force
Connect-ExchangeOnline -UserPrincipalName ##ADMIN_UPN##
$startDate = (Get-Date).AddDays(-##DAYS##).ToString("yyyy-MM-dd")
$endDate = (Get-Date).ToString("yyyy-MM-dd")
$results = Search-UnifiedAuditLog `
-StartDate $startDate `
-EndDate $endDate `
-ResultSize 500
$results |
Select-Object CreationDate, UserIds, Operations, AuditData |
Sort-Object CreationDate -Descending |
Format-Table -AutoSize
Write-Host "`nTotal records: $($results.Count)" -ForegroundColor Cyan
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'Application.Read.All'
$apps = Get-MgApplication -All
foreach ($app in $apps | Sort-Object DisplayName) {
Write-Host "`n=== $($app.DisplayName) ===" -ForegroundColor Cyan
Write-Host " App ID: $($app.AppId)"
Write-Host " Created: $($app.CreatedDateTime)"
if ($app.RequiredResourceAccess) {
Write-Host " API Permissions:" -ForegroundColor Yellow
foreach ($resource in $app.RequiredResourceAccess) {
Write-Host " Resource: $($resource.ResourceAppId)"
$resource.ResourceAccess | ForEach-Object {
Write-Host " $($_.Type): $($_.Id)"
}
}
}
}
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'AuditLog.Read.All'
$startTime = (Get-Date).AddHours(-##HOURS##).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
Get-MgAuditLogSignIn -Filter "status/errorCode ne 0 and createdDateTime ge $startTime" -All |
Select-Object CreatedDateTime, UserPrincipalName,
@{N="Error"; E={$_.Status.FailureReason}},
@{N="IP"; E={$_.IpAddress}},
@{N="Location"; E={"$($_.Location.City), $($_.Location.CountryOrRegion)"}},
@{N="AppName"; E={$_.AppDisplayName}} |
Sort-Object CreatedDateTime -Descending |
Format-Table -AutoSize
Install-Module Microsoft.Graph -Force
Connect-MgGraph -Scopes 'Group.Read.All', 'User.Read.All'
$teams = Get-MgGroup -Filter "resourceProvisioningOptions/Any(x:x eq 'Team')" -All `
-Property Id, DisplayName, Description, Visibility, CreatedDateTime
foreach ($team in $teams | Sort-Object DisplayName) {
$owners = Get-MgGroupOwner -GroupId $team.Id
$members = Get-MgGroupMember -GroupId $team.Id -All
[PSCustomObject]@{
Team = $team.DisplayName
Visibility = $team.Visibility
Members = $members.Count
Owners = ($owners | ForEach-Object {
(Get-MgUser -UserId $_.Id -ErrorAction SilentlyContinue).UserPrincipalName
}) -join "; "
Created = $team.CreatedDateTime
}
} | Format-Table -AutoSize
About ToolForge Cloud Scripts
The Cloud Scripts library is a curated collection of ready-to-run PowerShell scripts for Microsoft 365, Exchange Online, and Entra ID (formerly Azure Active Directory) administration. These scripts are designed for IT professionals, system administrators, and MSPs who manage Microsoft cloud environments and need reliable, time-saving automation for everyday tasks.
What the library covers
- Exchange Online — Mailbox management, distribution group reporting, message trace, mail flow rules, and retention policy automation.
- Microsoft 365 licensing — Licence assignment and removal, bulk licence changes, and licence usage reporting.
- Entra ID / Azure AD — User creation, bulk password resets, MFA status reports, group management, and conditional access policy review.
- Security & compliance — Audit log queries, unified audit log exports, alert policy configuration, and data governance tasks.
- Teams & SharePoint — Teams channel management, SharePoint site reporting, and permission auditing.
How to use these scripts
- Browse the categories and find the script that matches your task.
- Read the description and prerequisites — most scripts require specific PowerShell modules (e.g.
ExchangeOnlineManagement,Microsoft.Graph, orAzureAD). - Copy the script to your PowerShell session and review it before running.
- Always test in a non-production tenant or with a small subset of users before running scripts at scale.
Scripts that make changes (rather than just reporting) are clearly labelled. Read-only reporting scripts are safe to run in production environments.
Contribute a script
If you have written a useful PowerShell script for Microsoft 365 or Azure administration, you can submit it for review and community publication. We review all submissions and publish scripts that meet our quality and safety standards. Submissions must be clearly documented and must not contain hardcoded credentials or sensitive data.