M365Buddy
SharePoint Online · PowerShell · Microsoft Graph

Generate and Email a SharePoint Site Inventory Report with Microsoft Graph

A combined PowerShell example that inventories SharePoint sites, exports the approved CSV columns, summarizes storage, and emails the report through Microsoft Graph app-only certificate authentication.

What this script does

This combined script inventories SharePoint Online site collections, collects the configured site metadata, exports the approved nine-column CSV, displays storage-utilization summaries, and emails the CSV through Microsoft Graph using app-only certificate authentication. It does not use SMTP or require the Microsoft Graph PowerShell module. The inventory logic and storage calculations are part of the script and should be reviewed before you adapt it for a different tenant.

Download the latest PowerShell script

Download the matching script and article version below. The script link points to the published file in the public/scripts/SharePoint folder of the M365Buddy website. If you maintain the site in GitHub, replace the repository file with the updated PS1 provided alongside this article, then commit and deploy both files.

Prerequisites

  1. Windows PowerShell 7.4 or later. Run this in PowerShell 7 (pwsh), not Windows PowerShell 5.1. Current PnP.PowerShell releases require PowerShell 7.4 or later.
  2. PnP.PowerShell. Install the module in the same PowerShell 7 environment you will use to run the script.
  3. An existing Entra ID app registration configured for the SharePoint inventory workload, including its approved SharePoint/Graph application permissions. Keep the permissions required by your working inventory workflow; do not remove them when adding mail permissions.
  4. A valid PFX certificate with a private key. The public certificate that matches the PFX must be uploaded to the app registration. The PFX path and password must be correct on the computer running the script.
  5. A SharePoint administrator endpoint and tenant root URL for the correct tenant, and an app that is authorized to run the inventory operations in the script.
  6. An Exchange Online mailbox for the sender. The address in $From must be a mailbox in the tenant, and the app must be allowed to send from it.
  7. Write access to the report output folder and enough local disk space for the generated CSV.
  8. Approved credential handling. Do not publish your real PFX password, private certificate, client secrets, or tenant-sensitive output in a public repository.

Official references: Install PnP.PowerShell · Install PowerShell 7 on Windows.

Required Microsoft Graph email permissions

The email flow creates a draft, creates an attachment upload session, uploads the CSV in chunks, and then sends the draft. Therefore, it needs both of the following Microsoft Graph Application permissions on the app registration.

PermissionTypeWhy it is neededRequired status
Mail.SendApplicationAllows the app to send the completed draft as the configured sender.Admin consent granted
Mail.ReadWriteApplicationAllows the app to create the draft and create the message attachment upload session.Admin consent granted

Where to configure them: Microsoft Entra admin center → App registrations → your app → API permissions → Add a permission → Microsoft Graph → Application permissions. Search for each permission, add it, and select Grant admin consent. Confirm that both permissions show the granted status. Do not add them as Delegated permissions for this certificate-based app-only flow.

The older direct sendMail approach used Mail.Send; this combined script's draft/upload-session approach also needs Mail.ReadWrite. After permission or admin-consent changes, rerun the script so it requests a fresh app-only access token. If Exchange application RBAC or another application-access restriction is configured in your tenant, make sure it permits this app to use the sender mailbox.

Official references: Microsoft Graph sendMail permissions · Attachment upload-session permissions · Attach large files with Microsoft Graph.

Install or verify PnP.PowerShell

Open PowerShell 7.4 or later. Check the current version and whether the module is available:

$PSVersionTable.PSVersion
Get-Module PnP.PowerShell -ListAvailable

If the module is not installed in that PowerShell 7 environment, install it for your user:

Install-Module PnP.PowerShell -Scope CurrentUser

If your organization manages PowerShell modules centrally, follow its approved installation and execution-policy process rather than bypassing policy.

Update the script configuration

Open the downloaded .ps1 file in a text editor and update the values in the CONFIGURATION section. Do not change the report calculations or output-column validation unless you deliberately intend to change the inventory.

SettingWhat to enterNotes
$TenantYour tenant ID or tenant domain, for example contoso.onmicrosoft.com.Must identify the same tenant as the app registration and SharePoint URLs.
$ClientIdThe app registration's Application (client) ID.Use the ID for the app whose public certificate and permissions are configured.
$PfxPathThe complete local path to the PFX file.The PFX must contain the private key and match the public certificate uploaded to Entra.
$PfxPasswordPlainTextYour local PFX password or the approved secret-injection approach used by your organization.The sample variable is plain text in the file; do not commit a real password to GitHub. Prefer an approved secret store for production.
$AdminCenterUrlYour SharePoint admin URL, e.g. https://contoso-admin.sharepoint.com.Use your tenant's actual admin endpoint.
$SharePointRootUrlYour SharePoint root URL, e.g. https://contoso.sharepoint.com.Use the actual root domain for your tenant.
$OutputDirectoryA local folder where the script can create the report.The script creates the folder if it does not exist, but the run account must be able to write there.
$FromThe sender mailbox, e.g. reports@contoso.com.The mailbox must exist and be permitted for this app.
$ToOne or more recipient email addresses.All configured recipients receive the same message and can see the other addresses in the To field.
$ThrottleLimitThe parallel-processing limit already configured in the script.Keep the existing value initially; change it only if you understand the effect on tenant requests and local resources.

Send to multiple recipients

Add each recipient address inside the $To array. Put each address in quotes. PowerShell allows one address per line as shown below; commas can also be used between entries.

$To = @(
    'user1@contoso.com'
    'user2@contoso.com'
    'user3@contoso.com'
)

Replace the sample addresses with real recipients. The script sends one report email to the whole list; it does not send an individual copy to each person. Recipients can see one another in the To field. The current script does not implement Bcc.

Run the script

  1. Save the downloaded PS1 file in a local folder, for example C:\Scripts\SharePoint\.
  2. Confirm all placeholder values are replaced, especially the tenant, client ID, certificate path/password, SharePoint URLs, sender, and recipients.
  3. Open PowerShell 7.4 or later and move to the folder where you saved the script.
  4. Run the script:
Set-Location "C:\Scripts\SharePoint"
.\SharePoint-Site-Inventory-GraphEmail.ps1

Replace the example folder with your actual location. Keep the window open while the inventory is collected. Runtime depends on tenant size, site count, network performance, and throttling.

What the script creates and emails

  • Connects to SharePoint Online using PnP.PowerShell and app-only certificate authentication.
  • Collects site metadata, site collection administrators, associated SharePoint owners, group visibility, storage usage, and other fields used by the existing inventory workflow.
  • Exports a CSV with exactly these nine approved columns: SiteName, SiteUrl, SiteType, Template, WebCreatedDate, Office365Visibility, StorageGB, SiteCollectionAdmins, and SPOwners.
  • Displays the total site count and storage summary in the PowerShell console.
  • Creates a Graph draft, uploads the CSV using the attachment upload session in sequential chunks, and requests Graph to send the email.
  • Uses a report subject that includes the current date, total storage used, and site count. The HTML email body includes inventory and storage summary information.

CSV file location and cleanup

The local CSV is written under $OutputDirectory. Its filename uses a date suffix, such as -SPOSiteConsumption_20261020.csv. Because the filename is date-based, a rerun on the same date reuses that path and overwrites the previous CSV. After Graph accepts the send request, the script removes the generated CSV. If the Graph email operation fails, it keeps the CSV and prints the exact path for troubleshooting.

Attachment size and service limits

This version uploads the CSV in chunks and does not apply the old script's fixed 3 MB cutoff. This does not mean attachments are unlimited. Microsoft's Graph upload-session guidance describes the large-attachment path for files from 3 MB to 150 MB, and Exchange Online and your organization's mail-flow policies can impose a lower maximum message size. Check your tenant's effective message-size limit before sending very large reports.

Reference: Microsoft Graph: Attach large files to Outlook messages or events.

Troubleshooting

  • 403 Forbidden / ErrorAccessDenied: confirm that both Mail.Send and Mail.ReadWrite are Microsoft Graph Application permissions with admin consent. Rerun the script so it requests a fresh token. Then confirm $From is a real mailbox and check Exchange application RBAC/access restrictions. Do not add unrelated permissions unless the failing Graph request and error point to them.
  • Invalid client, AADSTS, or token error: verify $Tenant, $ClientId, PFX path/password, certificate expiry, private-key availability, and that the uploaded public certificate matches the local PFX.
  • Sender mailbox not found or send denied: check the sender spelling, mailbox provisioning, and whether app-access restrictions permit the app to use that mailbox.
  • SharePoint connection or inventory errors: verify the existing PnP app permissions, certificate authentication, SharePoint admin URL, tenant root URL, installed PnP.PowerShell module, and tenant/site-level access.
  • CSV missing or invalid: check $OutputDirectory, available disk space, and the CSV path printed in the console. The script validates the expected column list before emailing.
  • Attachment upload or message-size failure: verify the upload-session request succeeded and check Exchange Online and mail-flow message-size limits. Chunking does not override service or tenant limits.
  • Email worked but you cannot find it: check the recipient's Inbox, Junk Email, quarantine, and any mail-flow rules; also check the sender's Sent Items if available.

Security and operations notes

  • The sample keeps $PfxPasswordPlainText in the script for ease of demonstration. For production, retrieve it from an approved secret store or prompt securely; never publish a real password or the private PFX file.
  • Use a dedicated app registration and mailbox where appropriate. Assign only approved permissions, and restrict which mailboxes the application can access using your organization's supported Exchange controls.
  • The script's successful Graph response means Graph accepted the send request; it is not a guarantee that every recipient's mail system delivered it to the Inbox.
  • Test configuration changes on a controlled run before using the report for production governance decisions. Do not change the report's storage calculations or CSV schema unless you explicitly want a different report.

Related articles and scripts

Continue with these M365Buddy guides for the inventory workflow, certificate authentication, storage governance, and alternative report-delivery options.

M365Buddy Community

Discuss this article

Have a question, correction, or a helpful tip? Join the discussion. Posting and replying uses your GitHub account; please keep tenant details and credentials out of public comments.

GitHub comments are being connected

The embedded discussion will appear here after GitHub Discussions is enabled for M365Buddy and the comments category is configured.

Visit the M365Buddy Community → Report an issue on GitHub →