<# .SYNOPSIS SharePoint Production Toolkit - Scriptates a full SharePoint farm inventory including:SharePoint Production Toolkit - Script 15 - Farm Inventory Report - Farm name and version - Servers in the farm - Server roles and status - Service instances across the farm Supports migration planning, governance reviews, and environment validation. .PARAMETER WebAppUrl Context only (not required for farm-level scan, kept for toolkit consistency) .PARAMETER OutputCsv Full path to output CSV .PARAMETER NoPrompt Skips confirmation prompt #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$OutputCsv, [string]$WebAppUrl, [switch]$NoPrompt ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" Write-Host "" Write-Host "SCRIPT 15 - FARM INVENTORY REPORT" -ForegroundColor Cyan Write-Host "READ-ONLY. NO CHANGES ARE MADE." -ForegroundColor Green Write-Host "" # ------------------------------------------------------------ # Load SharePoint Snap-in # ------------------------------------------------------------ try { if (-not (Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue)) { Add-PSSnapin "Microsoft.SharePoint.PowerShell" } } catch { throw "Run in SharePoint Management Shell. Error: $($_.Exception.Message)" } # ------------------------------------------------------------ # Output Setup # ------------------------------------------------------------ $outDir = Split-Path -Path $OutputCsv -Parent if ([string]::IsNullOrWhiteSpace($outDir)) { throw "Provide full output path. Example: C:\Temp\FarmInventory.csv" } if (-not (Test-Path $outDir)) { New-Item -Path $outDir -ItemType Directory -Force | Out-Null } $timestamp = (Get-Date).ToString("yyyyMMdd_HHmmss") $baseName = [System.IO.Path]::GetFileNameWithoutExtension($OutputCsv) $summaryPath = Join-Path $outDir ("{0}_{1}_Summary_ByRisk.csv" -f $baseName, $timestamp) $logPath = Join-Path $outDir ("{0}_{1}_RunLog.txt" -f $baseName, $timestamp) $errorPath = Join-Path $outDir ("{0}_{1}_Errors.csv" -f $baseName, $timestamp) # ------------------------------------------------------------ # Logging Helpers # ------------------------------------------------------------ $errors = New-Object System.Collections.Generic.List[object] $log = New-Object System.Collections.Generic.List[string] function Log { param($msg) $line = "[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $msg $log.Add($line) | Out-Null Write-Host $line } function Add-Error { param($scope, $msg) $errors.Add([pscustomobject]@{ Timestamp = Get-Date Scope = $scope Message = $msg }) | Out-Null } if (-not $NoPrompt) { Write-Host "This script inventories the SharePoint farm." -ForegroundColor Yellow if ((Read-Host "Type YES to continue") -ne "YES") { return } } # ------------------------------------------------------------ # Risk Model # ------------------------------------------------------------ function Get-RiskLevel { param($ServerStatus, $ServiceStatus) if ($ServerStatus -ne "Online") { return "High" } if ($ServiceStatus -eq "Disabled") { return "Medium" } return "Low" } function Get-Score { param($risk) switch ($risk) { "High" { return 30 } "Medium" { return 60 } "Low" { return 90 } default { return 50 } } } function Get-Recommendation { param($risk) switch ($risk) { "High" { return "Server or service not healthy. Investigate before migration." } "Medium" { return "Review service configuration and validate expected state." } "Low" { return "Farm configuration healthy. Suitable for migration readiness baseline." } } } # ------------------------------------------------------------ # Main Execution # ------------------------------------------------------------ $results = New-Object System.Collections.Generic.List[object] try { $farm = Get-SPFarm Log "Collected farm information." } catch { Add-Error "Farm" $_.Exception.Message throw } # Servers try { $servers = Get-SPServer Log ("Found {0} servers in farm." -f $servers.Count) } catch { Add-Error "Servers" $_.Exception.Message throw } # Service Instances try { $services = Get-SPServiceInstance -All Log ("Collected service instances.") } catch { Add-Error "Services" $_.Exception.Message throw } foreach ($server in $servers) { try { $serverName = $server.Name $serverRole = [string]$server.Role $serverState = [string]$server.Status $serverServices = $services | Where-Object { $_.Server.Name -eq $serverName } foreach ($svc in $serverServices) { $serviceName = $svc.TypeName $serviceState = [string]$svc.Status $risk = Get-RiskLevel -ServerStatus $serverState -ServiceStatus $serviceState $results.Add([pscustomobject]@{ FarmName = $farm.Name BuildVersion = $farm.BuildVersion ServerName = $serverName ServerRole = $serverRole ServerStatus = $serverState ServiceName = $serviceName ServiceStatus = $serviceState RiskLevel = $risk Score = Get-Score $risk Category = "FarmInventory" ActionRecommendation = Get-Recommendation $risk }) | Out-Null } } catch { Add-Error $server.Name $_.Exception.Message } } # ------------------------------------------------------------ # Export Detail # ------------------------------------------------------------ $results | Export-Csv -Path $OutputCsv -NoTypeInformation -Encoding UTF8 # ------------------------------------------------------------ # Export Summary # ------------------------------------------------------------ $results | Group-Object RiskLevel | ForEach-Object { [pscustomobject]@{ RiskLevel = $_.Name Count = $_.Count } } | Export-Csv -Path $summaryPath -NoTypeInformation -Encoding UTF8 # ------------------------------------------------------------ # Export Logs / Errors # ------------------------------------------------------------ $log | Set-Content $logPath if ($errors.Count -gt 0) { $errors | Export-Csv -Path $errorPath -NoTypeInformation -Encoding UTF8 Write-Host ("ERROR REPORT: {0}" -f $errorPath) -ForegroundColor Yellow } Write-Host "" Write-Host ("DETAIL REPORT: {0}" -f $OutputCsv) -ForegroundColor Green Write-Host ("SUMMARY REPORT: {0}" -f $summaryPath) -ForegroundColor Green Write-Host ("RUN LOG: {0}" -f $logPath) -ForegroundColor Green Write-Host "" Write-Host "Complete." -ForegroundColor Green .DESCRIPTION READ-ONLY.