Entra / Microsoft 365 · SharePoint & OneDrive
Report SPO files in document library
Report files from a SharePoint Online document library using Microsoft Graph SDK cmdlets.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -Scopes "Sites.Read.All", "InformationProtectionPolicy.Read", "RecordsManagement.Read.All" -NoWelcome
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
function Get-DriveItems {[CmdletBinding()]param ([Parameter()]$Drive,[Parameter()]$FolderId)# Get data for a folder and its children[array]$Data = Get-MgDriveItemChild -DriveId $Drive -DriveItemId $FolderId -All# Split the data into files and folders[array]$Folders = $Data | Where-Object {$_.folder.childcount -gt 0} | Sort-Object Name$Global:TotalFolders = $TotalFolders + $Folders.Count[array]$Files = $Data | Where-Object {$null -ne $_.file.mimetype}# Process the filesForEach ($File in $Files) {If ($SensitivityLabelsAvailable -eq $true) {$SensitivityLabelName = $null; $SensitivityLabelInfo = $Null# Only check for sensitivity labels if they are available$FileType = $File.Name.Split(".")[1]If ($FileType -in $ValidFileTypes) {$Uri = ("https://graph.microsoft.com/beta/sites/{0}/drive/items/{1}/extractSensitivityLabels" -f $Site.Id, $File.id)Try {[array]$SensitivityLabelInfo = Invoke-MgGraphRequest -Uri $Uri -Method POSTIf ($SensitivityLabelInfo.labels.sensitivityLabelId) {[array]$LabelName = $SensitivityLabelsHash[$SensitivityLabelInfo.labels.sensitivityLabelId]}} Catch {Write-Host ("Error reading sensitivity label data from file {0}" -f $File.Name)[array]$LabelName = "Error"}}}# Get retention label informationIf ($RetentionLabelsAvailable -eq $true) {Try {$RetentionLabelName = $null; $RetentionLabelInfo = $null$Uri = ("https://graph.microsoft.com/v1.0/drives/{0}/items/{1}/retentionLabel" -f $Drive, $File.Id)[array]$RetentionLabelInfo = Invoke-MgGraphRequest -Uri $Uri -Method Get$RetentionLabelName = $RetentionLabelInfo.name} Catch {Write-Host ("Error reading retention label data from file {0}" -f $File.Name)}}If ($File.LastModifiedDateTime) {$LastModifiedDateTime = Get-Date $File.LastModifiedDateTime -format 'dd-MMM-yyyy HH:mm'} Else {$LastModifiedDateTime = $null}If ($File.CreatedDateTime) {$FileCreatedDateTime = Get-Date $File.CreatedDateTime -format 'dd-MMM-yyyy HH:mm'}If ([string]::IsNullOrEmpty($LabelName)) {$SensitivityLabelName = "No label"} Else {[string]$SensitivityLabelName = $LabelName[0].Trim()}If ([string]::IsNullOrEmpty($RetentionLabelName)) {$RetentionLabelName = "No label"} Else {[string]$RetentionLabelName = $RetentionLabelName.Trim()}$ReportLine = [PSCustomObject]@{FileName = $File.NameFolder = $File.parentreference.nameSize = (FormatFileSize $File.Size)Created = $FileCreatedDateTimeAuthor = $File.CreatedBy.User.DisplayNameLastModified = $LastModifiedDateTime'Last modified by' = $File.LastModifiedBy.User.DisplayName'Sensitivity label' = $SensitivityLabelName'Retention label' = $RetentionLabelNameWebURL = $File.WebUrl}$ReportData.Add($ReportLine)}# Process the foldersForEach ($Folder in $Folders) {Write-Host ("Processing folder {0} ({1} files/size {2})" -f $Folder.Name, $Folder.folder.childcount, (FormatFileSize $Folder.Size))Get-DriveItems -Drive $Drive -FolderId $Folder.Id}}function FormatFileSize {# Format File Size nicelyparam ([parameter(Mandatory = $true)]$InFileSize)If ($InFileSize -lt 1KB) { # Format the size of a document$FileSize = $InFileSize.ToString() + " B"}ElseIf ($InFileSize -lt 1MB) {$FileSize = $InFileSize / 1KB$FileSize = ("{0:n2}" -f $FileSize) + " KB"}Elseif ($InFileSize -lt 1GB) {$FileSize = $InFileSize / 1MB$FileSize = ("{0:n2}" -f $FileSize) + " MB"}Elseif ($InFileSize -ge 1GB) {$FileSize = $InFileSize / 1GB$FileSize = ("{0:n2}" -f $FileSize) + " GB"}Return $FileSize}# Disconnect from any previous Graph SDK sessionDisconnect-MgGraph# Connect to the Microsoft GraphConnect-MgGraph -Scopes "Sites.Read.All", "InformationProtectionPolicy.Read", "RecordsManagement.Read.All" -NoWelcomeWrite-Host "Setting up for the SharePoint Online site files report..."# Discover if the tenant uses sensitivity labels$Account = (Get-MgContext).Account[array]$SensitivityLabels = Get-MgBetaUserSecurityInformationProtectionSensitivityLabel -UserId $AccountIf ($SensitivityLabels) {$Global:SensitivityLabelsAvailable = $true[array]$Global:ValidfileTypes = "docx", "pptx", "xlsx", "pdf"$Global:SensitivityLabelsHash = @{}ForEach ($Label in $SensitivityLabels) {$SensitivityLabelsHash.Add($Label.Id, $Label.Name)}} Else {$Global:SensitivityLabelsAvailable = $false}# Discover if the tenant uses retention labels[array]$RetentionLabels = Get-MgSecurityLabelRetentionLabelIf ($RetentionLabels) {$Global:RetentionLabelsAvailable = $true} Else {$Global:RetentionLabelsAvailable = $false}# Find the siteWrite-Host "Looking for matching sites..."[array]$Sites = Get-MgSite -Search ($SiteName)If (!($Sites)) { # Nothing foundWrite-Host "No matching sites found - exiting"break}If ($Sites.Count -eq 1) { # Only one site found - go ahead$Global:Site = $Sites[0]$SiteName = $Site.DisplayNameWrite-Host "Found site to process:" $SiteName} ElseIf ($Sites.Count -gt 1) {# More than one site found. Ask which to useClear-Host[int]$i = 1Write-Host "More than one matching site was found. We need you to select a site to report."Write-Host " "ForEach ($SiteOption in $Sites) {Write-Host ("{0}: {1} ({2})" -f $i, $SiteOption.DisplayName, $SiteOption.Name); $i++}Write-Host ""[Int]$Answer = Read-Host "Enter the number of the site to use"If (($Answer -gt 0) -and ($Answer -le $i)) {[int]$Si = ($Answer-1)$SiteName = $Sites[$Si].DisplayNameWrite-Host ("OK. Selected site is {0}" -f $Sites[$Si].DisplayName)$Global:Site = $Sites[$Si]}}If (!($Site)) {Write-Host ("Can't find the {0} site - script exiting" -f $Uri)break}# Find the document libraries in the site[array]$Drives = Get-MgSiteDrive -SiteId $Site.IdIf (!($Drives)) {Write-Host "No document libraries found in the site" -ForegroundColor RedBreak}If ($Drives.Count -eq 1) { # Only one drive found - go ahead$Drive = $Drives$DriveName = $Drive.NameWrite-Host "Found drive to process:" $DriveName} Elseif ($Drives.Count -gt 1) { # More than one drive found. Ask which to useClear-Host; Write-Host "More than one drive found in site. We need you to select a drive to report."; [int]$i=1Write-Host " "ForEach ($DriveOption in $Drives) {Write-Host ("{0}: {1}" -f $i, $DriveOption.Name); $i++}Write-Host ""[Int]$Answer = Read-Host "Enter the number of the drive to use"If (($Answer -gt 0) -and ($Answer -le $i)) {[int]$Si = ($Answer-1)$DriveName = $Drives[$Si].NameWrite-Host "OK. Selected drive is" $Drives[$Si].Name$Drive = $Drives[$Si]}}If (!($Drive)) {Write-Host ("Can't find the {0} drive - script exiting" -f $Uri) ; break}[datetime]$StartProcessing = Get-Date$Global:TotalFolders = 1# Create output list and CSV file$Global:ReportData = [System.Collections.Generic.List[Object]]::new()$CSVOutputFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + ("\Files {0}-{1} library.csv" -f $Site.displayName, $DriveName)# Get the items in the root, including child foldersWrite-Host "Fetching file information..."Get-DriveItems -Drive $Drive.Id -FolderId "root"[datetime]$EndProcessing = Get-Date$ElapsedTime = ($EndProcessing - $StartProcessing)$FilesPerMinute = [math]::Round(($ReportData.Count / ($ElapsedTime.TotalSeconds / 60)), 2)Write-Host ""Write-Host ("Processed {0} files in {1} folders in {2}:{3} minutes ({4} files/minute)" -f `$ReportData.Count, $TotalFolders, $ElapsedTime.Minutes, $ElapsedTime.Seconds, $FilesPerMinute)Write-Host ""Write-Host "Retention Labels in Use"$ReportData | Group-Object 'Retention label' -NoElement | Sort-Object Count -Descending | Format-Table Name, CountWrite-Host ""Write-Host "Sensitivity Labels in Use"$ReportData | Group-Object 'Sensitivity label' -NoElement | Sort-Object Count -Descending | Format-Table Name, Count$ReportData | Out-GridView -Title ("Files in {0} document library for the {1} site" -f $DriveName, $SiteName)$ReportData | Export-Csv -Path $CSVOutputFile -NoTypeInformation -Encoding UTF8Write-Host ("Report data saved to {0}" -f $CSVOutputFile)Write-Host ""Write-Host "Generating report..."If (Get-Module ImportExcel -ListAvailable) {$ExcelGenerated = $TrueImport-Module ImportExcel -ErrorAction SilentlyContinue$ExcelOutputFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\SharePoint Files Report.xlsx"If (Test-Path $ExcelOutputFile) {Remove-Item $ExcelOutputFile -ErrorAction SilentlyContinue}$ReportData | Export-Excel -Path $ExcelOutputFile -WorksheetName "SharePoint Files Report" -Title ("SharePoint Files Report") -TitleBold -TableName "SPOFiles"} Else {$CSVOutputFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\SharePoint Files Report.CSV"$ReportData | Export-Csv -Path $CSVOutputFile -NoTypeInformation -Encoding Utf8}If ($ExcelGenerated) {Write-Host ("An Excel report is available in {0}" -f $ExcelOutputFile)} Else {Write-Host ("A CSV report is available in {0}" -f $CSVOutputFile)}
Attribution
Author
Office365itpros