Private/04-ReportServer.ps1
|
function Test-AtlasLoopbackPortAvailable { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateRange(1024, 65535)] [int] $Port ) $listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, $Port) try { $listener.Start() return $true } catch [System.Net.Sockets.SocketException] { return $false } finally { $listener.Stop() } } function Start-AtlasReportServer { [Diagnostics.CodeAnalysis.SuppressMessageAttribute( 'PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Starts only the packaged loopback report server explicitly requested through OpenReport.' )] [CmdletBinding()] param( [Parameter(Mandatory)] [string] $ReportRoot, [ValidateRange(1024, 65535)] [int] $Port = 8766, [ValidateRange(0, 50)] [int] $PortSearchLimit = 20, [switch] $OpenBrowser ) $resolvedRoot = (Resolve-Path -LiteralPath $ReportRoot -ErrorAction Stop).Path $moduleRoot = Split-Path -Parent $PSScriptRoot $serverScript = Join-Path $moduleRoot 'tools\Start-IdentityAtlasDevServer.ps1' if (-not (Test-Path -LiteralPath $serverScript -PathType Leaf)) { throw "The Identity Atlas local report server was not found at '$serverScript'." } $maximumPort = [math]::Min(65535, $Port + $PortSearchLimit) $selectedPort = $null foreach ($candidatePort in $Port..$maximumPort) { if (Test-AtlasLoopbackPortAvailable -Port $candidatePort) { $selectedPort = $candidatePort break } } if ($null -eq $selectedPort) { throw "No available loopback port was found between $Port and $maximumPort." } $powerShellPath = (Get-Process -Id $PID).Path if (-not $powerShellPath) { $powerShellPath = (Get-Command pwsh -ErrorAction Stop).Source } $serverProcess = Start-Process ` -FilePath $powerShellPath ` -ArgumentList @( '-NoLogo' '-NoProfile' '-File' "`"$serverScript`"" '-Root' "`"$resolvedRoot`"" '-Port' [string] $selectedPort ) ` -WindowStyle Hidden ` -PassThru $reportUrl = "http://127.0.0.1:$selectedPort/" $ready = $false for ($attempt = 0; $attempt -lt 50; $attempt++) { if ($serverProcess.HasExited) { break } try { $response = Invoke-WebRequest -Uri $reportUrl -Method Head -TimeoutSec 1 -UseBasicParsing if ($response.StatusCode -eq 200) { $ready = $true break } } catch { Start-Sleep -Milliseconds 100 } } if (-not $ready) { if (-not $serverProcess.HasExited) { Stop-Process -Id $serverProcess.Id -ErrorAction SilentlyContinue } throw "The Identity Atlas local report server did not start at $reportUrl." } Write-Information "Identity Atlas report ready at $reportUrl" -InformationAction Continue if ($OpenBrowser) { Start-Process -FilePath $reportUrl } return [pscustomobject] @{ Url = $reportUrl Port = $selectedPort ProcessId = $serverProcess.Id ReportRoot = $resolvedRoot } } |