Private/Test-BlastRadius.ps1

function Test-BlastRadius {
    <#
    .SYNOPSIS
        Checks the blast radius of a proposed action on a target server.
    .DESCRIPTION
        Evaluates the impact of a remediation action by checking if the target is a
        Domain Controller, SQL Server, Exchange server, file server, or print server.
        Checks connected user count and dependent services to determine risk level.
        Returns a blast radius assessment with level, reason, and affected systems.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ComputerName,

        [Parameter()]
        [string]$Action = 'Unknown action',

        [Parameter()]
        [object]$RunbookStep
    )

    $level = 'Low'
    $reasons = [System.Collections.Generic.List[string]]::new()
    $affectedSystems = [System.Collections.Generic.List[string]]::new()
    $requiresApproval = $false

    # Check blast_radius hint from the runbook step
    if ($RunbookStep -and $RunbookStep.blast_radius) {
        switch ($RunbookStep.blast_radius) {
            'single_service' { $level = 'Low' }
            'single_server'  { $level = 'Medium'; $requiresApproval = $true }
            'multi_server'   { $level = 'High'; $requiresApproval = $true }
            'domain_wide'    { $level = 'Critical'; $requiresApproval = $true }
        }
    }

    # Check if the step explicitly requires approval
    if ($RunbookStep -and $RunbookStep.requires_approval -eq $true) {
        $requiresApproval = $true
    }

    # Attempt to detect server role via Active Directory
    $isDC = $false
    $serverRoles = [System.Collections.Generic.List[string]]::new()

    try {
        if (Get-Command Get-ADComputer -ErrorAction SilentlyContinue) {
            $adComputer = Get-ADComputer -Identity $ComputerName -Properties OperatingSystem, ServicePrincipalName, Description -ErrorAction Stop

            # Check for Domain Controller
            $spns = $adComputer.ServicePrincipalName
            if ($spns -and ($spns | Where-Object { $_ -like '*GC/*' -or $_ -like '*E3514235-4B06*' })) {
                $isDC = $true
                $serverRoles.Add('DomainController')
                $level = 'Critical'
                $reasons.Add("$ComputerName is a Domain Controller")
                $requiresApproval = $true
            }

            # Check for Exchange
            if ($spns -and ($spns | Where-Object { $_ -like '*exchangeMDB/*' -or $_ -like '*SMTP/*' })) {
                $serverRoles.Add('Exchange')
                if ($level -ne 'Critical') { $level = 'High' }
                $reasons.Add("$ComputerName is an Exchange server")
                $requiresApproval = $true
            }

            # Check for SQL Server
            if ($spns -and ($spns | Where-Object { $_ -like '*MSSQLSvc/*' })) {
                $serverRoles.Add('SQLServer')
                if ($level -ne 'Critical') { $level = 'High' }
                $reasons.Add("$ComputerName is a SQL Server")
                $requiresApproval = $true
            }
        }
    }
    catch {
        Write-Verbose "AD lookup not available or failed for ${ComputerName}: $_"
    }

    # Check server role via CIM/WMI
    try {
        $os = Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $ComputerName -ErrorAction Stop

        # Domain Controller check via OS caption
        if ($os.Caption -match 'Domain Controller') {
            if (-not $isDC) {
                $isDC = $true
                $serverRoles.Add('DomainController')
                $level = 'Critical'
                $reasons.Add("$ComputerName is a Domain Controller (detected via OS)")
                $requiresApproval = $true
            }
        }

        # Check for server features via services
        $services = Get-CimInstance -ClassName Win32_Service -ComputerName $ComputerName -ErrorAction Stop

        # SQL Server
        $sqlServices = $services | Where-Object { $_.Name -like 'MSSQL*' -or $_.Name -eq 'SQLAgent' }
        if ($sqlServices -and 'SQLServer' -notin $serverRoles) {
            $serverRoles.Add('SQLServer')
            if ($level -notin @('Critical', 'High')) { $level = 'High' }
            $reasons.Add("$ComputerName runs SQL Server services")
            $requiresApproval = $true
        }

        # Exchange
        $exchangeServices = $services | Where-Object { $_.Name -like 'MSExchange*' }
        if ($exchangeServices -and 'Exchange' -notin $serverRoles) {
            $serverRoles.Add('Exchange')
            if ($level -notin @('Critical', 'High')) { $level = 'High' }
            $reasons.Add("$ComputerName runs Exchange services")
            $requiresApproval = $true
        }

        # File Server (lanmanserver with shares)
        $fileShares = Get-CimInstance -ClassName Win32_Share -ComputerName $ComputerName -ErrorAction SilentlyContinue |
            Where-Object { $_.Name -notmatch '^\w\$' -and $_.Name -ne 'IPC$' -and $_.Name -ne 'ADMIN$' }
        if ($fileShares -and $fileShares.Count -gt 2) {
            $serverRoles.Add('FileServer')
            if ($level -eq 'Low') { $level = 'Medium' }
            $reasons.Add("$ComputerName is a file server with $($fileShares.Count) shares")
            $affectedSystems.AddRange([string[]]($fileShares | ForEach-Object { "Share: $($_.Name)" }))
        }

        # Print Server
        $printServices = $services | Where-Object { $_.Name -eq 'Spooler' -and $_.State -eq 'Running' }
        $printers = Get-CimInstance -ClassName Win32_Printer -ComputerName $ComputerName -ErrorAction SilentlyContinue |
            Where-Object { $_.Shared -eq $true }
        if ($printServices -and $printers -and $printers.Count -gt 0) {
            $serverRoles.Add('PrintServer')
            if ($level -eq 'Low') { $level = 'Medium' }
            $reasons.Add("$ComputerName is a print server with $($printers.Count) shared printers")
        }

        # Check connected users
        $sessions = Get-CimInstance -ClassName Win32_LogonSession -ComputerName $ComputerName -ErrorAction SilentlyContinue |
            Where-Object { $_.LogonType -in @(2, 10, 11) }
        if ($sessions -and $sessions.Count -gt 5) {
            if ($level -eq 'Low') { $level = 'Medium' }
            $reasons.Add("$($sessions.Count) active user sessions on $ComputerName")
            $requiresApproval = $true
        }
    }
    catch {
        Write-Verbose "CIM/WMI checks not available for ${ComputerName}: $_"
        if ($reasons.Count -eq 0) {
            $reasons.Add("Unable to verify server role for $ComputerName (remote access unavailable)")
        }
    }

    # If no specific concerns were found
    if ($reasons.Count -eq 0) {
        $reasons.Add("No elevated risk detected for action on $ComputerName")
    }

    [PSCustomObject]@{
        ComputerName    = $ComputerName
        Action          = $Action
        Level           = $level
        Reasons         = $reasons.ToArray()
        ServerRoles     = $serverRoles.ToArray()
        AffectedSystems = $affectedSystems.ToArray()
        RequiresApproval = $requiresApproval
        AssessedAt      = (Get-Date).ToString('o')
    }
}