Tools/Get-SPOData.ps1
|
################################################################################################### # Script: Get-SPOData.ps1 # Author: Ryan Holderread - Rackspace Technology # Version: 2.0 # Last Updated: 2026-03-25 # # Description: # Companion script to the M365-QuickAssess module. # Runs in Windows PowerShell 5.1 (required by SharePoint Online Management Shell). # Spawned automatically by Get-SharePointData.ps1 -- do not run directly. # # Collects SharePoint site metrics and outputs a structured JSON file # that is merged back into the main assessment by the parent process. # # Parameters: # -OutputFile Full path to the JSON output file # -AdminUrl SharePoint admin URL (e.g. https://contoso-admin.sharepoint.com) ################################################################################################### param ( [Parameter(Mandatory)] [string]$OutputFile, [Parameter(Mandatory)] [string]$AdminUrl ) $ErrorActionPreference = "Stop" # ------------------------------------------------------------------- # Logging (console only -- parent process owns the log file) # ------------------------------------------------------------------- function Write-Log { param ( [string]$Message, [string]$Level = "INFO" ) $timestamp = ( Get-Date ).ToString("yyyy-MM-dd HH:mm:ss") $entry = "[$timestamp][$Level] $Message" switch ( $Level ) { "ERROR" { Write-Host $entry -ForegroundColor Red } "WARN" { Write-Host $entry -ForegroundColor Yellow } default { Write-Host $entry } } } # ------------------------------------------------------------------- # Module # ------------------------------------------------------------------- try { Import-Module Microsoft.Online.SharePoint.PowerShell -ErrorAction Stop } catch { Write-Log "Failed to import SharePoint Online Management Shell: $( $_.Exception.Message )" "ERROR" Write-Log "Install it with: Install-Module Microsoft.Online.SharePoint.PowerShell" exit 1 } # ------------------------------------------------------------------- # Connection # ------------------------------------------------------------------- try { Write-Log "Connecting to SharePoint Online: $AdminUrl" Connect-SPOService -Url $AdminUrl -UseSystemBrowser:$true Write-Log "Connected to SharePoint Online" } catch { Write-Log "SharePoint connection failed: $( $_.Exception.Message )" "ERROR" exit 1 } # ------------------------------------------------------------------- # Data Collection # ------------------------------------------------------------------- $Findings = @() $siteCount = 0 $totalStorageGB = 0 $largeSites = 0 $externalSharing = $false $uniquePerms = $false $hasAppCatalog = $false $customAppCount = 0 try { Write-Log "Collecting SharePoint site data" $sites = Get-SPOSite -Limit All -ErrorAction Stop $siteCount = $sites.Count $totalStorageGB = [math]::Round( ( $sites | Measure-Object StorageUsageCurrent -Sum ).Sum / 1024, 2 ) Write-Log "Sites: $siteCount TotalStorageGB: $totalStorageGB" # ------------------------------------------------------------------- # Large Sites (> 100 GB) # ------------------------------------------------------------------- $largeSiteObjects = $sites | Where-Object { $_.StorageUsageCurrent -gt 102400 } $largeSites = $largeSiteObjects.Count foreach ( $site in $largeSiteObjects ) { $sizeGB = [math]::Round( $site.StorageUsageCurrent / 1024, 2 ) $Findings += @{ Type = "LargeSharePointSite" Url = $site.Url Title = $site.Title SizeGB = $sizeGB } } # ------------------------------------------------------------------- # External Sharing # ------------------------------------------------------------------- $externalSharing = ( $sites | Where-Object { $_.SharingCapability -ne "Disabled" } ).Count -gt 0 if ( $externalSharing ) { $externalSites = $sites | Where-Object { $_.SharingCapability -ne "Disabled" } foreach ( $site in $externalSites | Select-Object -First 10 ) { $Findings += @{ Type = "ExternalSharing" Url = $site.Url } } } # ------------------------------------------------------------------- # Unique Permissions (signal only -- details require full discovery) # ------------------------------------------------------------------- $uniquePerms = ( $sites | Where-Object { $_.HasUniqueRoleAssignments -eq $true } ).Count -gt 0 # ------------------------------------------------------------------- # App Catalog # ------------------------------------------------------------------- try { $appCatalogSite = $sites | Where-Object { $_.Url -match "/sites/appcatalog" -or $_.Template -eq "APPCATALOG#0" } | Select-Object -First 1 if ( $appCatalogSite ) { $hasAppCatalog = $true Write-Log "App Catalog found: $( $appCatalogSite.Url )" try { $appCatalogApps = Get-SPOAppInfo -ProductId * -ErrorAction Stop $customAppCount = ( $appCatalogApps | Measure-Object ).Count Write-Log "Apps in catalog: $customAppCount (uploaded, not necessarily deployed)" } catch { Write-Log "Could not retrieve app catalog contents: $( $_.Exception.Message )" "WARN" $customAppCount = 0 } } else { Write-Log "No App Catalog detected" } } catch { Write-Log "App Catalog check failed: $( $_.Exception.Message )" "WARN" } } catch { Write-Log "SharePoint data collection failed: $( $_.Exception.Message )" "ERROR" exit 1 } # ------------------------------------------------------------------- # Storage Finding # ------------------------------------------------------------------- if ( $totalStorageGB -gt 100 ) { $severity = if ( $totalStorageGB -gt 500 ) { "High" } else { "Medium" } $significantSites = $sites | Where-Object { $_.StorageUsageCurrent -gt 102400 } $siteList = ( $significantSites | ForEach-Object { "$( $_.Title ) - $( $_.Url ) ($( [math]::Round( $_.StorageUsageCurrent / 1024, 2 ) ) GB)" } ) -join ", " if ( $siteList -eq "" ) { $siteList = "No sites over 100 GB detected" } $Findings += @{ Type = "StorageUsage" Severity = $severity Description = "$totalStorageGB GB total across $siteCount sites: $siteList" } } # ------------------------------------------------------------------- # Unique Permissions Finding # ------------------------------------------------------------------- if ( $uniquePerms ) { $Findings += @{ Type = "UniquePermissions" } } # ------------------------------------------------------------------- # Build Output # ------------------------------------------------------------------- $result = [ordered]@{ SharePoint = [ordered]@{ SharePointSiteCount = $siteCount SharePointTotalStorageGB = $totalStorageGB LargeSiteCount = $largeSites HasExternalSharing = $externalSharing HasUniquePermissions = $uniquePerms HasSharePointRetentionPolicies = $false HasAppCatalog = $hasAppCatalog HasSharePointCustomApps = ( $customAppCount -gt 0 ) SharePointCustomAppCount = $customAppCount } Summary = [ordered]@{ SharePointSiteCount = $siteCount SharePointTotalStorageGB = $totalStorageGB } Findings = $Findings } # ------------------------------------------------------------------- # Write Output # ------------------------------------------------------------------- try { Write-Log "Writing SharePoint output to $OutputFile" $result | ConvertTo-Json -Depth 6 | Out-File $OutputFile -Encoding utf8 Write-Log "SharePoint export complete" exit 0 } catch { Write-Log "Failed to write output file: $( $_.Exception.Message )" "ERROR" exit 1 } |