This article describes how to find all SharePoint Online site collections — and optionally all subsites — in your tenant where the skybow Modern Forms Add-In has been installed.
As your tenant grows over time, it can become difficult to track on which sites the Add-In has been deployed — especially when multiple administrators or project teams are involved. This script gives you a full, auditable inventory of every installation across your tenant, including subsites.
The provided PnP PowerShell script iterates through all site collections and — when the -IncludeSubsites switch is used — all subsites within them. It checks whether skybow Modern Forms PRO is installed, optionally checks for the mFormsStoreLibrary document library (used by the Forms Designer), and exports the results to a timestamped CSV file.
Table of Contents
- Scenario
- Prerequisites
- How the Script Works
- Step-by-Step Guide
- The Complete Script
- Result
- Additional Hints & Special Cases
Scenario
- You are a SharePoint administrator managing a Microsoft 365 tenant.
- The skybow Modern Forms PRO Add-In has been installed on various SharePoint sites and subsites over time by different administrators or project teams.
- You now want to:
- Get a complete overview of all site collections and subsites where the Add-In is currently installed.
- Identify which installed version is running on each site or subsite.
- Optionally detect whether the mFormsStoreLibrary document library (used by the Forms Designer) exists on each web.
- Export the findings to a CSV file for documentation or governance purposes.
Prerequisites
- SharePoint Administrator or Global Administrator rights on your Microsoft 365 tenant.
-
PnP PowerShell module installed. Install or update it by running:
Install-Module PnP.PowerShell -Scope CurrentUser -Force - A registered Entra ID App Registration (formerly Azure AD) with the required SharePoint permissions, and its Client ID (Application ID) at hand.
- Access to your SharePoint Admin Center URL (e.g. https://contoso-admin.sharepoint.com).
- Sufficient permissions to connect to every site collection and subsite in the tenant (Global Admin or SharePoint Admin recommended).
IMPORTANT: The -ClientId parameter is required when using -Interactive authentication with modern PnP PowerShell. You must provide the Client ID of your own Entra ID App Registration, or use the well-known PnP Management Shell Client ID: 31359c7f-bd7e-475c-86db-fdb8c937548e. The PnP Management Shell app must be consented in your tenant first — see PnP PowerShell Authentication documentation for details.
How the Script Works
- Connects to your SharePoint Admin Center using interactive browser login with the provided
-ClientId. - Retrieves all site collections in the tenant, excluding system/service sites (e.g. app catalog, search center, redirect sites).
- Checks each site collection for a valid installation of skybow Modern Forms PRO.
- When
-IncludeSubsitesis used, recursively retrieves all subsites within each site collection usingGet-PnPSubWeb -Recurseand checks each one individually. - Optionally checks for the existence of the mFormsStoreLibrary document library on each web when
-CheckScriptFoldersis used. - Collects all results — including a Web Type column distinguishing site collections from subsites — and exports them to a timestamped CSV file.
Step-by-Step Guide
Step 1: Install or Verify PnP PowerShell
Before running the script, make sure the PnP PowerShell module is installed and up to date on your machine.
- Open PowerShell (run as Administrator if needed).
- Run the following command to install or update the module:
Install-Module PnP.PowerShell -Scope CurrentUser -Force
Note: If you already have PnP PowerShell installed, you can update it with Update-Module PnP.PowerShell.
Step 2: Prepare Your Client ID
PnP PowerShell requires a Client ID when using interactive (-Interactive) authentication. You have two options:
- Option A – Use the PnP Management Shell App (quickest):
- Use the well-known Client ID:
31359c7f-bd7e-475c-86db-fdb8c937548e - The first time you use it, you will be prompted to grant consent in your tenant. A Global Administrator must approve this once.
- Option B – Use your own Entra ID App Registration:
- Register a new app in Entra ID (Azure AD) > App registrations.
- Grant it the required SharePoint delegated permissions (e.g. AllSites.FullControl).
- Set the redirect URI to
http://localhostfor interactive login. - Copy the Application (client) ID — you will pass this as the
-ClientIdparameter.
Step 3: Save the Script to a Local File
Copy the complete script from the section below and save it as a .ps1 file on your machine.
- Save it as FindskybowModernFormsInstallations.ps1 in a folder of your choice.
- Make sure you remember the path — you will call the script from that location.
Step 4: Run the Script
Open PowerShell, navigate to the folder where you saved the script, and call it with the required parameters.
- Basic run — site collections only, app check only:
.\FindskybowModernFormsInstallations.ps1 -AdminCenterURL "https://contoso-admin.sharepoint.com" -ClientId "31359c7f-bd7e-475c-86db-fdb8c937548e"
- Include subsites — also scans all subsites within each site collection:
.\FindskybowModernFormsInstallations.ps1 -AdminCenterURL "https://contoso-admin.sharepoint.com" -ClientId "31359c7f-bd7e-475c-86db-fdb8c937548e" -IncludeSubsites
- Full run — subsites + mFormsStoreLibrary check:
.\FindskybowModernFormsInstallations.ps1 -AdminCenterURL "https://contoso-admin.sharepoint.com" -ClientId "31359c7f-bd7e-475c-86db-fdb8c937548e" -IncludeSubsites -CheckScriptFolders
IMPORTANT: Replace contoso-admin with your actual tenant name, and substitute the -ClientId value with your own App Registration Client ID if you are not using the PnP Management Shell app. A browser login window will appear — sign in with your SharePoint Administrator account.
Step 5: Review the CSV Output
Once the script completes, it saves the results to a timestamped CSV file in the same folder as the script.
- The file will be named something like: skybow_ModernForms_Inventory_2026-06-04_151500.csv
- Open it in Microsoft Excel or any CSV viewer.
- The file contains the following columns:
- Web Type — Site Collection or Subsite.
- Site URL — Full URL of the site collection or subsite.
- Site Title — Display name of the site or subsite.
- Parent Site URL — The parent site collection URL (populated for subsites).
- App Installed — Always Yes for matched sites.
- Installed Version — The currently installed version of skybow Modern Forms PRO.
-
mFormsStoreLibrary — Yes, No, or Not checked depending on whether
-CheckScriptFolderswas used. - Is Hub Site — Whether this site is a hub site (site collections only).
- Hub Site Id — The hub site GUID the site is associated with (if any).
The Complete Script
Copy the full script below and save it as FindskybowModernFormsInstallations.ps1:
#----------------------------------------------------------
# Find all Sites with skybow Modern Forms Add-In installed
# Optionally includes subsites and checks for mFormsStoreLibrary
#
# Author : skybow Support
# Requires: PnP PowerShell module (Install-Module PnP.PowerShell)
#
# Usage (basic):
# .\FindskybowModernFormsInstallations.ps1 `
# -AdminCenterURL "https://contoso-admin.sharepoint.com" `
# -ClientId "31359c7f-bd7e-475c-86db-fdb8c937548e"
#
# Usage (include subsites):
# .\FindskybowModernFormsInstallations.ps1 `
# -AdminCenterURL "https://contoso-admin.sharepoint.com" `
# -ClientId "31359c7f-bd7e-475c-86db-fdb8c937548e" `
# -IncludeSubsites
#
# Usage (full):
# .\FindskybowModernFormsInstallations.ps1 `
# -AdminCenterURL "https://contoso-admin.sharepoint.com" `
# -ClientId "31359c7f-bd7e-475c-86db-fdb8c937548e" `
# -IncludeSubsites `
# -CheckScriptFolders
#----------------------------------------------------------
param(
[Parameter(Mandatory = $true)]
[string]$AdminCenterURL,
# e.g. https://contoso-admin.sharepoint.com
[Parameter(Mandatory = $true)]
[string]$ClientId,
# Client ID of your Entra ID App Registration
# PnP Management Shell default: 31359c7f-bd7e-475c-86db-fdb8c937548e
[Parameter(Mandatory = $false)]
[switch]$IncludeSubsites,
# If set, also scans all subsites within each site collection
[Parameter(Mandatory = $false)]
[switch]$CheckScriptFolders
# If set, also checks each web for the mFormsStoreLibrary document library
)
$AppName = "skybow Modern Forms PRO"
$ScriptLibrary = "mFormsStoreLibrary"
$dateTime = (Get-Date).ToString("yyyy-MM-dd_HHmmss")
$outputFile = ".\skybow_ModernForms_Inventory_$dateTime.csv"
$results = [System.Collections.Generic.List[PSObject]]::new()
#----------------------------------------------------------
# Helper function: Check a single web for the app
#----------------------------------------------------------
function Invoke-WebAppCheck {
param(
[string]$WebUrl,
[string]$WebTitle,
[string]$WebType, # "Site Collection" or "Subsite"
[string]$ParentSiteUrl, # Empty for site collections
[bool] $IsHubSite,
[string]$HubSiteId
)
try {
Connect-PnPOnline -Url $WebUrl -Interactive -ClientId $ClientId
$webConnection = Get-PnPConnection
# Check if skybow Modern Forms PRO is installed on this web
$installedApp = Get-PnPApp -Connection $webConnection |
Where-Object { $_.Title -eq $AppName -and $null -ne $_.InstalledVersion }
if ($installedApp) {
Write-Host " [FOUND] [$WebType] $WebUrl | Version: $($installedApp.InstalledVersion)" `
-ForegroundColor Green
# Optional: Check for mFormsStoreLibrary
$scriptFolderExists = "Not checked"
if ($CheckScriptFolders) {
try {
$lib = Get-PnPList -Identity $ScriptLibrary `
-Connection $webConnection `
-ErrorAction SilentlyContinue
$scriptFolderExists = if ($lib) { "Yes" } else { "No" }
}
catch {
$scriptFolderExists = "Error checking"
}
}
$row = [PSCustomObject]@{
"Web Type" = $WebType
"Site URL" = $WebUrl
"Site Title" = $WebTitle
"Parent Site URL" = $ParentSiteUrl
"App Installed" = "Yes"
"Installed Version" = $installedApp.InstalledVersion
"mFormsStoreLibrary" = $scriptFolderExists
"Is Hub Site" = $IsHubSite
"Hub Site Id" = $HubSiteId
}
$script:results.Add($row)
}
}
catch {
Write-Warning " [ERROR] Could not process [$WebType] $WebUrl : $($_.Exception.Message)"
}
}
#----------------------------------------------------------
# Step 1: Connect to SharePoint Admin Center
#----------------------------------------------------------
Write-Host "`nConnecting to SharePoint Admin Center: $AdminCenterURL" -ForegroundColor Cyan
Connect-PnPOnline -Url $AdminCenterURL -Interactive -ClientId $ClientId
$adminConnection = Get-PnPConnection
#----------------------------------------------------------
# Step 2: Retrieve all relevant site collections
#----------------------------------------------------------
Write-Host "Retrieving all site collections (excluding system sites)..." -ForegroundColor Cyan
$excludedTemplates = @(
"PWA#0", "SRCHCEN#0", "REDIRECTSITE#0", "SPSMSITEHOST#0",
"APPCATALOG#0", "POINTPUBLISHINGHUB#0", "POINTPUBLISHINGTOPIC#0",
"EDISC#0", "STS#-1"
)
$sites = Get-PnPTenantSite -Detailed -Connection $adminConnection |
Where-Object { $excludedTemplates -notcontains $_.Template }
Write-Host "Found $($sites.Count) site collection(s) to scan.`n" -ForegroundColor Cyan
$counter = 0
#----------------------------------------------------------
# Step 3: Iterate through each site collection
#----------------------------------------------------------
foreach ($site in $sites) {
$counter++
Write-Progress -Activity "Scanning for '$AppName'" `
-Status "$counter / $($sites.Count) — $($site.Url)" `
-PercentComplete (($counter / $sites.Count) * 100)
# --- Check the site collection itself ---
Invoke-WebAppCheck `
-WebUrl $site.Url `
-WebTitle $site.Title `
-WebType "Site Collection" `
-ParentSiteUrl "" `
-IsHubSite $site.IsHubSite `
-HubSiteId $site.HubSiteId
# --- Optionally check all subsites ---
if ($IncludeSubsites) {
try {
Connect-PnPOnline -Url $site.Url -Interactive -ClientId $ClientId
$siteConnection = Get-PnPConnection
$subwebs = Get-PnPSubWeb -Recurse -Connection $siteConnection
if ($subwebs.Count -gt 0) {
Write-Host " Found $($subwebs.Count) subsite(s) under $($site.Url)" `
-ForegroundColor DarkCyan
foreach ($subweb in $subwebs) {
Invoke-WebAppCheck `
-WebUrl $subweb.Url `
-WebTitle $subweb.Title `
-WebType "Subsite" `
-ParentSiteUrl $site.Url `
-IsHubSite $false `
-HubSiteId ""
}
}
}
catch {
Write-Warning " [ERROR] Could not retrieve subsites for $($site.Url): $($_.Exception.Message)"
}
}
}
Write-Progress -Activity "Scanning for '$AppName'" -Completed
#----------------------------------------------------------
# Step 4: Export results to CSV
#----------------------------------------------------------
if ($results.Count -gt 0) {
$results | Export-Csv -Path $outputFile -NoTypeInformation -Force -Encoding UTF8
Write-Host "`n Scan complete. Found $($results.Count) web(s) with '$AppName' installed." `
-ForegroundColor Green
Write-Host " Results saved to: $outputFile" -ForegroundColor Cyan
}
else {
Write-Host "`n No sites or subsites found with '$AppName' installed." -ForegroundColor Yellow
}
Result: Full Installation Inventory Available
- You have a complete list of all SharePoint site collections — and subsites if
-IncludeSubsiteswas used — where skybow Modern Forms PRO is currently installed. - The Web Type column clearly distinguishes between Site Collection and Subsite entries in the CSV.
- The installed version number per web is captured — helping you identify outdated installations that may need an update.
- If
-CheckScriptFolderswas used, you also know which webs have the mFormsStoreLibrary provisioned by the Forms Designer. - The CSV export can be used for governance reports, license audits, or planning tenant-wide updates.
Additional Hints & Special Cases
SPFx App Scope: Site Collection vs. Subsite
The skybow Modern Forms PRO SPFx Add-In is installed at the site collection level, not per individual subsite. When the app is installed on a site collection, it is technically available across all subsites within it.
- The
-IncludeSubsitesscan is particularly valuable for detecting the mFormsStoreLibrary document library, which can exist independently in each subsite where the Forms Designer has been actively used. - If a subsite shows the app as installed, it is inheriting the installation from its parent site collection.
Client ID: PnP Management Shell vs. Your Own App Registration
You can use either the well-known PnP Management Shell Client ID or a custom Entra ID App Registration.
-
PnP Management Shell Client ID:
31359c7f-bd7e-475c-86db-fdb8c937548e— easiest to get started, requires one-time tenant consent by a Global Administrator. - Custom App Registration — recommended for production or recurring use; gives you full control over permissions and consent scope.
- See the PnP PowerShell Authentication documentation for full setup guidance.
Large Tenants: Expect Long Runtimes
The script connects to every site collection — and every subsite when -IncludeSubsites is used — individually. On large tenants this can take a significant amount of time.
- Consider running the script outside business hours or on a dedicated admin machine.
- You can limit the scan to a subset of sites by adding an additional
Where-Objectfilter afterGet-PnPTenantSite, e.g. filtering byTemplate,HubSiteId, or URL pattern.
App Not Found? Check the App Title Carefully
The script matches the app by its exact title: skybow Modern Forms PRO. If the app was deployed under a different display name or as a different package variant, it may not be detected.
- To list all apps installed on a specific site for verification, run:
Connect-PnPOnline -Url "https://contoso.sharepoint.com/sites/yoursite" -Interactive -ClientId "<your-client-id>"Get-PnPApp | Select-Object Title, InstalledVersion | Sort-Object Title
- Adjust the
$AppNamevariable at the top of the script if needed.
mFormsStoreLibrary: What Does it Mean if it's Missing?
The mFormsStoreLibrary document library is provisioned by the skybow Forms Designer on a site when it is first used for form customization. Its absence does not necessarily mean the app is not installed — it may simply not have been actively used yet on that site.
- Yes — The Forms Designer has been actively used on this site; form configurations exist.
- No — The app is installed but the Forms Designer may not have been used yet, or the library was deleted.
-
Not checked — The
-CheckScriptFoldersswitch was not used during the scan.
Excluded Site Templates (System Sites)
The following SharePoint site templates are excluded from the scan by default, as they are system or service sites where the Add-In would never be installed:
-
PWA#0— Project Web App -
SRCHCEN#0— Search Center -
REDIRECTSITE#0— Redirect sites -
APPCATALOG#0— App Catalog -
EDISC#0— eDiscovery -
STS#-1— Deleted / unavailable sites
You can adjust this exclusion list by modifying the $excludedTemplates array in the script.