New-SPOAlertInventoryList
This PnP PowerShell script creates the SharePoint Alert Inventory list used by Get-SPOAlertInventory to store the results of a tenant-wide classic Alerts scan. Run it once, on whichever site you want the results to live, before running the inventory scan — it is safe to re-run, since it only creates columns that don't already exist.
Purpose
Microsoft is retiring classic SharePoint Alerts in July 2026. Before that happens, every tenant needs an audit trail of who has alerts configured, on what, and what should happen to each one. This script builds the destination list for that audit:
- Creates a
GenericListnamedSPOAlertInventory(or a custom name) if it doesn't already exist - Renames the default
Titlecolumn toAlert Title - Adds columns for site identification, alert ownership, alert configuration detail, and scan metadata
- Adds a set of outreach/governance columns —
Reviewed,Action Needed,Review Notes— to support the follow-up workflow once the scan has run - An
Add-FieldIfMissinghelper checks for each column before creating it, so re-running the script against an existing list only fills in gaps
Prerequisites
- PnP.PowerShell 3.x module installed
- Owner or site collection administrator rights on the target site
- Run before Get-SPOAlertInventory, which writes its results into this list
PowerShell Script
<#
.SYNOPSIS
Creates the SharePoint Alert Inventory list on devintranet, with all
columns needed to store results from Get-SPOAlertInventory.ps1 and to
support the follow-up outreach workflow (review status, notes, action
needed) ahead of the July 2026 SharePoint Alerts retirement.
.NOTES
Requires: PnP.PowerShell 3.x
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$SiteUrl = "https://tenantName.sharepoint.com/sites/siteName",
[Parameter(Mandatory = $false)]
[string]$ClientId = "",
[Parameter(Mandatory = $false)]
[string]$ListName = "SPOAlertInventory",
[Parameter(Mandatory = $false)]
[string]$ListTitle = "SharePoint Alert Inventory"
)
Write-Host "Connecting to $SiteUrl ..." -ForegroundColor Cyan
Connect-PnPOnline -Url $SiteUrl -Interactive -ClientId $ClientId
# --- Create the list if it doesn't already exist ---
$existingList = Get-PnPList -Identity $ListName -ErrorAction SilentlyContinue
if ($existingList) {
Write-Host "List '$ListName' already exists. Skipping creation, will only ensure columns exist." -ForegroundColor Yellow
}
else {
Write-Host "Creating list '$ListName' ..." -ForegroundColor Cyan
New-PnPList -Title $ListName -Template GenericList -OnQuickLaunch | Out-Null
}
# --- Rename the default Title column to something meaningful ---
Set-PnPField -List $ListName -Identity "Title" -Values @{Title = "Alert Title"} | Out-Null
# --- Helper to add a field only if it doesn't already exist ---
function Add-FieldIfMissing {
param(
[string]$ListName,
[string]$InternalName,
[string]$DisplayName,
[string]$Type,
[string[]]$Choices,
[switch]$Required
)
$field = Get-PnPField -List $ListName -Identity $InternalName -ErrorAction SilentlyContinue
if ($field) {
Write-Host " Field '$InternalName' already exists, skipping." -ForegroundColor DarkGray
return
}
switch ($Type) {
"Choice" {
Add-PnPField -List $ListName -InternalName $InternalName -DisplayName $DisplayName `
-Type Choice -Choices $Choices -Required:$Required | Out-Null
}
default {
Add-PnPField -List $ListName -InternalName $InternalName -DisplayName $DisplayName `
-Type $Type -Required:$Required | Out-Null
}
}
Write-Host " Added field '$DisplayName' ($Type)" -ForegroundColor Green
}
Write-Host "Adding columns to '$ListName' ..." -ForegroundColor Cyan
# --- Site identification ---
Add-FieldIfMissing -ListName $ListName -InternalName "SiteUrl" -DisplayName "Site URL" -Type URL
Add-FieldIfMissing -ListName $ListName -InternalName "SiteTitle" -DisplayName "Site Title" -Type Text
# --- Alert ownership ---
Add-FieldIfMissing -ListName $ListName -InternalName "CreatedFor" -DisplayName "Created For" -Type User
Add-FieldIfMissing -ListName $ListName -InternalName "CreatedForLogin" -DisplayName "Created For (Login)" -Type Text
# --- Alert configuration detail ---
Add-FieldIfMissing -ListName $ListName -InternalName "AlertListUrl" -DisplayName "List / Library URL" -Type URL
Add-FieldIfMissing -ListName $ListName -InternalName "EventType" -DisplayName "Event Type" -Type Text
Add-FieldIfMissing -ListName $ListName -InternalName "AlertFilter" -DisplayName "Filter" -Type Choice -Choices @(
"AnythingChanges",
"SomeoneElseChangesAnItem",
"SomeoneElseChangesItemCreatedByMe",
"SomeoneElseChangesItemLastModifiedByMe"
)
Add-FieldIfMissing -ListName $ListName -InternalName "Frequency" -DisplayName "Frequency" -Type Choice -Choices @(
"Immediate",
"Daily",
"Weekly"
)
Add-FieldIfMissing -ListName $ListName -InternalName "DeliveryChannel" -DisplayName "Delivery Channel" -Type Text
Add-FieldIfMissing -ListName $ListName -InternalName "AlertTime" -DisplayName "Alert Time" -Type DateTime
# --- Scan metadata ---
Add-FieldIfMissing -ListName $ListName -InternalName "ScanDate" -DisplayName "Scan Date" -Type DateTime -Required
Add-FieldIfMissing -ListName $ListName -InternalName "ScanStatus" -DisplayName "Scan Status" -Type Choice -Choices @(
"OK",
"ERROR",
"CLEANUP_NEEDED"
)
Add-FieldIfMissing -ListName $ListName -InternalName "ScanStatusDetail" -DisplayName "Scan Status Detail" -Type Note
# --- Outreach / governance workflow ---
Add-FieldIfMissing -ListName $ListName -InternalName "Reviewed" -DisplayName "Reviewed" -Type Boolean
Add-FieldIfMissing -ListName $ListName -InternalName "ActionNeeded" -DisplayName "Action Needed" -Type Choice -Choices @(
"Migrate to SharePoint Rule",
"Migrate to Power Automate",
"Keep monitoring (not yet reviewed)",
"No longer needed, safe to ignore",
"Creator unreachable / orphaned"
)
Add-FieldIfMissing -ListName $ListName -InternalName "ReviewNotes" -DisplayName "Review Notes" -Type Note
Write-Host "`nList setup complete: '$ListTitle' ($ListName) on $SiteUrl" -ForegroundColor Green
Write-Host "Remaining manual step: consider adding a default view showing ScanStatus, ActionNeeded and Reviewed for quick triage." -ForegroundColor Yellow
Usage Notes
-SiteUrldefaults to a placeholder — point it at whichever site should host the inventory list-ListNamedefaults toSPOAlertInventory— keep it matched to the-ListNamepassed into Get-SPOAlertInventory, or the scan won't find the right list- The
CreatedForcolumn is a People field;CreatedForLoginstores the raw login name alongside it as a plain-text fallback in case the login can't be resolved to a person Add-FieldIfMissingmakes the whole script idempotent — safe to re-run if you add a new column later and want it applied to an existing list- Remaining manual step: add a default view surfacing
ScanStatus,ActionNeeded, andReviewedfor quick triage after a scan
Related
- Get-SPOAlertInventory — the tenant-wide scan that populates this list