PowerShell; SharePoint Online Inventory and User Activity Report

I am frequently asked to generate various reports for SharePoint Online. Usually, I can get the requested information from the Microsoft 365 Reports console, this time was different. We had just merged two Microsoft 365 tenants. The data move had worked according to the tools. The problem was that independent reports showed people were still using the old target sites after the cut date.

Microsoft’s SharePoint activity reports obscure the user’s identity for security reasons. We specifically needed to identify who was accessing data in the old tenant to troubleshoot the issue. There is a setup option you can toggle to display user identities in the reports. I turned on the switch and waited five business days with no results. I couldn’t wait any longer and needed to pivot to another solution. If you want to try, the Microsoft documentation is here.

I decided to go with a PowerShell generated report instead. Power BI would also have been a good choice, but I had an ace up my sleeve. I have been using a PowerShell script written by Salaudeen Rajack on his SharePoint Diary blog for quite a while. I knew that it was almost perfect for this project. I would just need to craft a method for looping it through all of the sub-sites in our old tenant.

Salaudeen’s script is based on the open-source PnP SharePoint PowerShell module. He uses it to create a PowerShell function. This function takes a folder and file level inventory of all the documents stored in any given SharePoint site. It generates a report showing the URL, total size, type, creation date, last accessed date, along with the user account.

I didn’t want to authenticate hundreds of times as the function looped through each site in the tenant. So, I registered the PnP module as an App in Entra ID and used certificate-based authentication. Again, Salaudeen’s blog provides excellent instructions.

All that was left was to generate a list of all the individual site URLs. Then I needed to pass them through a logic loop to call the report function. The resulting report is perfect for understanding your SharePoint Online environment as a whole.

I ran the script above on a tenant with around 500 sub-sites and about a thousand users. It took nine and a half hours to finish. The resulting CSV file was 42 megabytes in size. You’ll need to know the URL for your SharePoint Online admin console. You’ll also need the ClientID from the Entra app you registered.

The script is configured for an interactive logon so that it will work with MFA. After the first logon prompt each sub-site will use the same session. Once you have generated the report, there is a lot you can do with Excel to highlight various aspects.

References:

PowerShell: Report To Avoid Hidden Exchange Mailbox Quota Violations

There are two separate quotas for a Microsoft 365 mailbox. One is for the size of the mailbox. The other lesser-known limit is on the recoverable items database.

It is the recoverable items that frequently catch people off-guard. This quota isn’t shown in the admin portal dashboards. Furthermore, the limit is usually only an issue if you have a mailbox on legal hold.

These two things combine to make a frustrating situation. A user’s mailbox can stop due to a quota violation when it isn’t shown as being full on the portal. Here is a link to Microsoft’s documentation on the subject if you would like to know more.

Import-Module ExchangeOnlineManagement
Connect-ExchangeOnline
$Report = @()
$UserMailboxes = (Get-ExoMailbox -ResultSize unlimited -Filter {(RecipientType -eq ‘UserMailbox’)}).UserPrincipalName
Foreach ($Mailbox in $UserMailboxes){
    $MailboxTotalSize = [PSCustomObject]@{
        Name = $Mailbox
        Mailbox = Get-ExoMailboxStatistics -Identity $Mailbox | Select-Object TotalItemSize -ExpandProperty TotalItemSize
        RecoverableItems = Get-ExoMailboxFolderStatistics -Identity $Mailbox -FolderScope RecoverableItems | Select-Object Name,FolderAndSubfolderSize -ExpandProperty FolderAndSubfolderSize |Where-Object {$_.Name -like “*Recoverable*”}
    }
    $Report += $MailboxTotalSize
}
$Report |Export-Csv -Path
“C\Temp\MailboxTrueSizeReport.csv” -NoTypeInformation

I use the report to help prevent quota lockouts at my employer. You will need to run the script from a machine that has the Exchange Online Management Powershell module installed. Executing the script also requires an account with Exchange Online mailbox administrator permissions.

The output is an easy-to-read three-column report. The headers are Name (the user’s UPN), Mailbox, and RecoverableItems. The script takes around one hour per thousand mailboxes on average. This time can vary wildly,  the point is that it’s slow. Don’t let the computer running it go to sleep.

PowerShell; Report Mailbox and OneDrive Size per User

Microsoft’s 365 has a ton of built-in reports for almost every metric you can think of. I recently needed to show Exchange Online and OneDrive consumption in the same report for a migration project. I was surprised to find the data I needed was available in separate reporting functions, but not together.

I briefly considered using Power BI or Excel to extract the data from the two separate reports and combining the attributes into a new one. Ultimately, I decided PowerShell would be faster. The script below requires that the MS 365 modules be installed on the machine it is run from. This Microsoft article has instructions for installing them. https://learn.microsoft.com/en-us/microsoft-365/enterprise/connect-to-all-microsoft-365-services-in-a-single-windows-powershell-window?view=o365-worldwide

#Import Required Modules 
Import-Module ExchangeOnlineManagement
Import-Module Microsoft.Online.SharePoint.PowerShell

#Connect to the Tennant
Connect-ExchangeOnline
Connect-SpOService -Url https://URL to top level SharePoint Site 

#Retrieve Data
Get-Mailbox -ResultSize Unlimited | Select-Object DisplayName, UserPrincipalName, ForwardingSmtpAddress, DeliverToMailboxAndForward, 
    @{N="MailboxSize"; E={(Get-MailboxStatistics -Identity $_.UserPrincipalName | Select-Object TotalItemSize).TotalItemSize}}, 
    @{N="MailboxLastLogon"; E={(Get-MailboxStatistics -Identity $_.UserPrincipalName | Select-Object LastLogonTime).LastLogonTime}}, 
    @{N="OneDriveUsedGB"; E={(Get-SPOSite -IncludePersonalSite $true -Limit all -Filter "Owner -like '$($_.UserPrincipalName)'" | Select-Object   StorageUsageCurrent).StorageUsageCurrent/1024}} |
Export-Csv -Path $env:userprofile\Documents\365_Use_Report.csv -NoTypeInformation