
Today is a really short blogpost just so I can publish a script I have created that makes a report of alle files in SharePoint that has versioning, to show how much storage the use, and how much storage you can save by limiting the number of major versions. If you still allow 500 major versions, you can in the script set 20 as the new limit, to see how much you save. The script is only for reporting purposes, and I will most likely at a later time create a script for purging/removing old versions.
To run the script, you will need an app registration with the following application rights:
Directory.Read.All, Files.Read.All and Sites.Read.All
If you want more in dept info around SharePoint versioning, take a look at Nikki Chapple´s great blogpost on the topic.
| # Parameters for the app registration | |
| $ClientId = "xxxxx" | |
| $TenantId = "xxxxx" | |
| $ClientSecret = "xxxxx" | |
| $NumberOfMajorVersionsToKeep = 20 | |
| $TotalSizeAllVersions = 0 | |
| $TotalSizeReducedVersions = 0 | |
| # Convert the Client Secret to a SecureString | |
| $SecureClientSecret = ConvertTo-SecureString -String $ClientSecret -AsPlainText -Force | |
| # Create a PSCredential object with the Client ID and Secure Client Secret | |
| $ClientSecretCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $ClientId, $SecureClientSecret | |
| # Connect to Microsoft Graph using the Tenant ID and Client Secret Credential | |
| Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $ClientSecretCredential | |
| # Initialize the output array | |
| $OutputData = @() | |
| function Get-AllDriveItems { | |
| param ( | |
| [Parameter(Mandatory)] | |
| [string]$DriveId, | |
| [Parameter()] | |
| [string]$ParentId, | |
| [Parameter()] | |
| [string]$CurrentPath = "" | |
| ) | |
| $Items = @() | |
| if ($ParentId) { | |
| Write-Host "Processing folder with ParentId: $ParentId" -ForegroundColor Cyan | |
| try { | |
| $ParentItem = Get-MgDriveItem -DriveId $DriveId -DriveItemId $ParentId -ExpandProperty 'children' -ErrorAction Stop | |
| $DriveItems = $ParentItem.Children | |
| if($DriveItems) { | |
| Write-Host "Found $($DriveItems.Count) child items in folder '$($ParentItem.Name)' (ParentId: $ParentId)" -ForegroundColor Green | |
| } else { | |
| Write-Host "No child items found in folder '$($ParentItem.Name)' (ParentId: $ParentId)" -ForegroundColor Yellow | |
| } | |
| } catch { | |
| Write-Warning "Failed to get children for item ID '$ParentId'. Error: $_" | |
| return $Items | |
| } | |
| } else { | |
| Write-Host "Processing root folder" -ForegroundColor Cyan | |
| try { | |
| $RootItem = Get-MgDriveRoot -DriveId $DriveId -ExpandProperty 'children' -ErrorAction Stop | |
| $DriveItems = $RootItem.Children | |
| if($DriveItems) { | |
| Write-Host "Found $($DriveItems.Count) items in root folder" -ForegroundColor Green | |
| } else { | |
| Write-Host "No items found in root folder" -ForegroundColor Yellow | |
| } | |
| } catch { | |
| Write-Error "Failed to get root item and its children. Error: $_" | |
| return $Items | |
| } | |
| } | |
| if (-not $DriveItems) { | |
| Write-Verbose "No items found at this level." | |
| return $Items | |
| } | |
| foreach ($Item in $DriveItems) { | |
| # Update the file path | |
| if ($CurrentPath -eq "") { | |
| $ItemPath = "/$($Item.Name)" | |
| } else { | |
| $ItemPath = "$CurrentPath/$($Item.Name)" | |
| } | |
| # Add the ItemPath property to the item | |
| $Item | Add-Member -NotePropertyName 'ItemPath' -NotePropertyValue $ItemPath | |
| $Items += $Item | |
| Write-Host "Found item: $($Item.Name) (Id: $($Item.Id))" -ForegroundColor Green | |
| if ($Item.Folder) { | |
| Write-Host "Recursing into folder: $($Item.Name) (Id: $($Item.Id))" -ForegroundColor Cyan | |
| if ($Item.Id) { | |
| $Items += Get-AllDriveItems -DriveId $DriveId -ParentId $Item.Id -CurrentPath $ItemPath | |
| } else { | |
| Write-Warning "Item '$($Item.Name)' has a null ID and cannot be processed." | |
| } | |
| } | |
| } | |
| return $Items | |
| } | |
| # Now, process all SharePoint sites and their document libraries | |
| Write-Host "Starting to process all SharePoint sites and their document libraries..." -ForegroundColor Magenta | |
| try { | |
| # Retrieve all SharePoint sites | |
| $sites = Get-MgSite -All | Where-Object { $_.WebUrl -notlike "*personal*" } | |
| } catch { | |
| Write-Error "Failed to retrieve SharePoint sites. Error: $_" | |
| exit | |
| } | |
| foreach ($site in $sites) { | |
| try { | |
| Write-Host "Processing site: $($site.DisplayName) <$($site.WebUrl)>" -ForegroundColor Magenta | |
| # Get all document libraries (drives) in the site | |
| $drives = Get-MgSiteDrive -SiteId $site.Id -ErrorAction Stop | |
| foreach ($drive in $drives) { | |
| Write-Host "Processing document library: $($drive.Name)" -ForegroundColor Cyan | |
| $driveId = $drive.Id | |
| # Get all items in the document library starting from the root | |
| $AllItems = Get-AllDriveItems -DriveId $driveId | |
| Write-Host "Total items retrieved in document library '$($drive.Name)': $($AllItems.Count)" -ForegroundColor Green | |
| foreach ($Item in $AllItems) { | |
| Write-Host "Processing item: $($Item.Name) (Id: $($Item.Id))" -ForegroundColor Gray | |
| # Check if item is a file | |
| if ($Item.File) { | |
| try { | |
| # Get versions for the item | |
| $Versions = @() | |
| $Versions = Get-MgDriveItemVersion -DriveId $driveId -DriveItemId $Item.Id -All -ErrorAction Stop | |
| } catch { | |
| Write-Warning "Failed to get versions for item '$($Item.Name)'. Error: $_" | |
| continue | |
| } | |
| if (!$Versions) { | |
| Write-Host "No versions found for item '$($Item.Name)'" -ForegroundColor Yellow | |
| $MajorVersionCount = 0 | |
| $MinorVersionCount = 0 | |
| } else { | |
| Write-Host "Found $($Versions.Count) versions for item '$($Item.Name)'" -ForegroundColor Green | |
| $MajorVersionCount = 0 | |
| $MinorVersionCount = 0 | |
| $TotalVersionSize = 0 | |
| $LastversionSize = 0 | |
| $LastversionSize = ($Versions | Sort-Object -Descending -Property TotalSizeReduced)[0].Size | |
| If($Versions){ | |
| foreach ($Version in $Versions) { | |
| $TotalVersionSize += $Version.Size | |
| if ($Version.id -like "*.0") { | |
| $MajorVersionCount++ | |
| } | |
| else { | |
| $MinorVersionCount++ | |
| } | |
| } | |
| } | |
| $TotalSizeReduced = 0 | |
| $ReducedMajorVersionCount = 0 | |
| $ReducedMinorVersionCount = 0 | |
| $ReducedMajorVersions = $Versions | Where-Object {$_.id -like "*.0"} | Sort-Object -Property LastModifiedDateTime -Descending | Select-Object -First $NumberOfMajorVersionsToKeep | |
| foreach ($ReducedMajorVersion in $ReducedMajorVersions){ | |
| $VersionNumber = @() | |
| $VersionNumber = ($ReducedMajorVersion.id).split(".")[0] | |
| $Versions = $Versions | Where-Object {$_.id -like "$VersionNumber.*"} | |
| foreach ($Version in $Versions) { | |
| $TotalSizeReduced += $Version.Size | |
| if ($Version.id -like "*.0") { | |
| $ReducedMajorVersionCount++ | |
| } | |
| else { | |
| $ReducedMinorVersionCount++ | |
| } | |
| } | |
| } | |
| # Create a custom object with the desired properties | |
| $OutputObject = [PSCustomObject]@{ | |
| SiteUrl = $site.WebUrl | |
| SiteName = $site.DisplayName | |
| DocumentLibrary = $drive.Name | |
| ItemName = $Item.Name | |
| ItemUrl = $Item.WebUrl | |
| FilePath = $Item.ItemPath | |
| LastVersionSizeMB = $LastversionSize/1MB | |
| TotalSizeMB = $TotalVersionSize/1MB | |
| TotalSizeReducedMB = $TotalSizeReduced/1MB | |
| MajorVersionCount = $MajorVersionCount | |
| MinorVersionCount = $MinorVersionCount | |
| ReducedMajorVersionCount = $ReducedMajorVersionCount | |
| ReducedMinorVersionCount = $ReducedMinorVersionCount | |
| } | |
| $TotalSizeAllVersions += $TotalVersionSize | |
| $TotalSizeReducedVersions += $TotalSizeReduced | |
| # Add the object to the output array | |
| $OutputData += $OutputObject | |
| } | |
| } | |
| } | |
| } | |
| } catch { | |
| Write-Warning "Failed to process site $($site.DisplayName). Error: $_" | |
| continue | |
| } | |
| } | |
| # Output or process the collected data as needed | |
| # For example, output to console | |
| #$OutputData | |
| "Before: " + $TotalSizeAllVersions/1GB | |
| "After: " + $TotalSizeReducedVersions/1GB | |
| # Optionally, export to CSV | |
| # $OutputData | Export-Csv -Path 'C:\Path\To\Output.csv' -NoTypeInformation |