Public/Export-SharingLinkReport.ps1
|
function Export-SharingLinkReport { <# .SYNOPSIS Write Get-SharingLink results to CSV or a standalone HTML report. .DESCRIPTION CSV for the record and for feeding other tools; HTML for the version you email to whoever asked "are we overshared". The HTML is self-contained - no external CSS or scripts - so it opens anywhere and survives being forwarded. .EXAMPLE Get-SharingLink -SiteUrl $url -UseExistingConnection | Export-SharingLinkReport -Path .\sharing.html -Html .EXAMPLE Get-SharingLink -Tenant -TenantAdminUrl $admin -ClientId $id -Interactive | Export-SharingLinkReport -Path .\sharing.csv #> [CmdletBinding()] param( [Parameter(Mandatory, ValueFromPipeline)] [psobject] $Link, [Parameter(Mandatory)] [string] $Path, [switch] $Html ) begin { $rows = [System.Collections.Generic.List[object]]::new() } process { $rows.Add($Link) } end { if ($rows.Count -eq 0) { Write-Warning "Nothing to export - Get-SharingLink returned no links." return } if (-not $Html) { $rows | Export-Csv -Path $Path -NoTypeInformation -Encoding UTF8 $file = Get-Item $Path # Visible by default (-InformationAction Continue), PSSA-clean, and # still returns the FileInfo so it can be piped. Write-Information "Report generated: $($rows.Count) link(s) written to $($file.FullName)" -InformationAction Continue return $file } # --- self-contained HTML ------------------------------------------------ Add-Type -AssemblyName System.Web # needed before HtmlEncode is called below $riskColour = @{ Critical = '#b91c1c'; High = '#c2410c'; Medium = '#a16207'; Low = '#4d7c0f' } $counts = $rows | Group-Object RiskLevel | ForEach-Object { "$($_.Name): $($_.Count)" } $bodyRows = foreach ($r in ($rows | Sort-Object @{e={@{Critical=0;High=1;Medium=2;Low=3}[$_.RiskLevel]}}, SiteTitle)) { $c = $riskColour[$r.RiskLevel] $expiry = if ($r.Expiration) { [datetime]$r.Expiration | Get-Date -Format 'yyyy-MM-dd' } else { '<em>never</em>' } $enc = { param($s) [System.Web.HttpUtility]::HtmlEncode([string]$s) } @" <tr> <td><span class="pill" style="background:$c">$($r.RiskLevel)</span></td> <td>$(& $enc $r.SiteTitle)</td> <td>$(& $enc $r.ItemName)</td> <td>$($r.Scope)</td> <td>$($r.Access)</td> <td>$(& $enc $r.Recipients)</td> <td>$expiry</td> </tr> "@ } $generated = Get-Date -Format 'yyyy-MM-dd HH:mm' # NOT $html - that is the same variable as the [switch]$Html parameter # (PowerShell names are case-insensitive), and assigning a string to it # throws "Cannot convert String to SwitchParameter". $reportHtml = @" <!doctype html><html><head><meta charset="utf-8"> <title>Sharing Link Audit</title> <style> body{font-family:Segoe UI,system-ui,sans-serif;margin:2rem;color:#1f2430} h1{margin:0 0 .25rem} .meta{color:#666;margin-bottom:1.5rem} .summary span{display:inline-block;margin-right:1rem;font-weight:600} table{border-collapse:collapse;width:100%;font-size:.9rem} th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #e5e7eb;vertical-align:top} th{background:#f3f4f6;position:sticky;top:0} .pill{color:#fff;padding:2px 8px;border-radius:10px;font-size:.8rem;font-weight:600} </style></head><body> <h1>Sharing Link Audit</h1> <div class="meta">Generated $generated · $($rows.Count) link(s)</div> <div class="summary">$([string]::Join(' ', ($counts | ForEach-Object { "<span>$_</span>" })))</div> <table> <thead><tr><th>Risk</th><th>Site</th><th>Item</th><th>Scope</th><th>Access</th><th>Shared with</th><th>Expires</th></tr></thead> <tbody> $([string]::Join("`n", $bodyRows)) </tbody></table> </body></html> "@ Set-Content -Path $Path -Value $reportHtml -Encoding UTF8 $file = Get-Item $Path Write-Information "Report generated: $($rows.Count) link(s) written to $($file.FullName)" -InformationAction Continue return $file } } |