powershell max CERT

# Analyse des objets non bindés dans ns.conf
# NetScaler SSL Certificate Analyzer - Advanced Chain Detection
# Detects UNUSED Root/Intermediate certificates
# v6.0 - Complete chain analysis

$NS_CONF_PATH = "C:\tempmax\ns.conf"

Write-Host "`n" + ("=" * 80) -ForegroundColor Cyan
Write-Host "    NetScaler SSL Certificate Analysis - Advanced Chain Detection" -ForegroundColor Yellow
Write-Host ("=" * 80) + "`n" -ForegroundColor Cyan

# Load configuration
$configLines = Get-Content -Path $NS_CONF_PATH -Encoding UTF8 -ErrorAction SilentlyContinue
if (-not $configLines) {
    Write-Host "ERROR: Cannot load file $NS_CONF_PATH" -ForegroundColor Red
    exit
}
$configString = $configLines -join "`n"
Write-Host "Configuration loaded successfully`n" -ForegroundColor Green

# Collections
$allCertificates = @{}
$serverCertificates = @{}
$samlCertificates = @{}
$rootIntermediateCertificates = @{}
$boundCertificates = @{}
$certificatesInChains = @{} # NEW: Track which certs are in chains
$chainRelationships = @{}   # NEW: Track parent-child relationships

# STEP 1: Find ALL certificates
Write-Host "[1] Identifying all certificates..." -ForegroundColor Yellow
$certPattern = '(?i)add ssl certkey\s+("[^"]+"|[^\s]+)(?:\s+-cert\s+("[^"]+"|[^\s]+))?(?:.*?-key\s+("[^"]+"|[^\s]+))?'
$matches = [regex]::Matches($configString, $certPattern)

foreach ($match in $matches) {
    $certName = $match.Groups[1].Value.Trim('"')
    $hasKey = $match.Groups[3].Success
    
    $certInfo = @{
        Name = $certName
        HasPrivateKey = $hasKey
        Type = if ($hasKey) { "SERVER" } else { "ROOT/INTERMEDIATE" }
        BoundTo = @()
        LinkedTo = ""
        LinkedFrom = @()  # NEW: Track who links TO this cert
        IsInChain = $false # NEW: Track if part of any chain
        IsSAML = $false
    }
    
    $allCertificates[$certName] = $certInfo
    
    if ($hasKey) {
        $serverCertificates[$certName] = $certInfo
    } else {
        $rootIntermediateCertificates[$certName] = $certInfo
    }
}

Write-Host "  Total certificates found: $($allCertificates.Count)" -ForegroundColor Green
Write-Host "    - Server certificates (with private key): $($serverCertificates.Count)" -ForegroundColor Gray
Write-Host "    - Root/Intermediate certificates: $($rootIntermediateCertificates.Count)`n" -ForegroundColor Gray

# STEP 2: Find certificate chains (CRITICAL FOR ROOT/INTERMEDIATE)
Write-Host "[2] Analyzing certificate chains (critical for Root/Intermediate)..." -ForegroundColor Yellow
$linkPattern = '(?i)link ssl certkey\s+("[^"]+"|[^\s]+)\s+("[^"]+"|[^\s]+)'
$matches = [regex]::Matches($configString, $linkPattern)

foreach ($match in $matches) {
    $childCert = $match.Groups[1].Value.Trim('"')
    $parentCert = $match.Groups[2].Value.Trim('"')
    
    # Mark both certificates as being in a chain
    if ($allCertificates.ContainsKey($childCert)) {
        $allCertificates[$childCert].LinkedTo = $parentCert
        $allCertificates[$childCert].IsInChain = $true
        $certificatesInChains[$childCert] = $true
    }
    
    if ($allCertificates.ContainsKey($parentCert)) {
        $allCertificates[$parentCert].LinkedFrom += $childCert
        $allCertificates[$parentCert].IsInChain = $true
        $certificatesInChains[$parentCert] = $true
    }
    
    # Store the relationship
    if (-not $chainRelationships.ContainsKey($parentCert)) {
        $chainRelationships[$parentCert] = @()
    }
    $chainRelationships[$parentCert] += $childCert
}

$totalInChains = $certificatesInChains.Count
Write-Host "  Found $($matches.Count) chain links involving $totalInChains certificates`n" -ForegroundColor Green

# STEP 3: Find SAML certificate bindings
Write-Host "[3] Checking SAML certificate bindings..." -ForegroundColor Yellow
$samlActionPattern = '(?i)add authentication samlAction\s+("[^"]+"|[^\s]+).*?-samlIdPCertName\s+("[^"]+"|[^\s]+)'
$samlPolicyPattern = '(?i)add authentication samlPolicy\s+("[^"]+"|[^\s]+).*?-samlIdPCertName\s+("[^"]+"|[^\s]+)'

$samlBoundCount = 0
# Check samlAction bindings
$matches = [regex]::Matches($configString, $samlActionPattern)
foreach ($match in $matches) {
    $actionName = $match.Groups[1].Value.Trim('"')
    $certName = $match.Groups[2].Value.Trim('"')
    
    if (-not $boundCertificates.ContainsKey($certName)) {
        $boundCertificates[$certName] = @()
    }
    $boundCertificates[$certName] += "SAML_ACTION: $actionName"
    
    if ($allCertificates.ContainsKey($certName)) {
        $allCertificates[$certName].BoundTo += "SAML_ACTION: $actionName"
        $allCertificates[$certName].IsSAML = $true
        $allCertificates[$certName].Type = "SAML"
        
        if ($rootIntermediateCertificates.ContainsKey($certName)) {
            $samlCertificates[$certName] = $allCertificates[$certName]
            $samlBoundCount++
        }
    }
}

# Check samlPolicy bindings  
$matches = [regex]::Matches($configString, $samlPolicyPattern)
foreach ($match in $matches) {
    $policyName = $match.Groups[1].Value.Trim('"')
    $certName = $match.Groups[2].Value.Trim('"')
    
    if (-not $boundCertificates.ContainsKey($certName)) {
        $boundCertificates[$certName] = @()
    }
    $boundCertificates[$certName] += "SAML_POLICY: $policyName"
    
    if ($allCertificates.ContainsKey($certName)) {
        $allCertificates[$certName].BoundTo += "SAML_POLICY: $policyName"
        $allCertificates[$certName].IsSAML = $true
        $allCertificates[$certName].Type = "SAML"
        
        if ($rootIntermediateCertificates.ContainsKey($certName)) {
            $samlCertificates[$certName] = $allCertificates[$certName]
            $samlBoundCount++
        }
    }
}

Write-Host "  Found $samlBoundCount SAML certificate bindings`n" -ForegroundColor Green

# STEP 4: Find service/vserver bindings
Write-Host "[4] Checking service and vserver bindings..." -ForegroundColor Yellow
$serviceBindPattern = '(?i)bind ssl service\s+("[^"]+"|[^\s]+).*?-certkeyName\s+("[^"]+"|[^\s]+)'
$vserverBindPattern = '(?i)bind ssl vserver\s+("[^"]+"|[^\s]+).*?-certkeyName\s+("[^"]+"|[^\s]+)'

$totalBindings = 0
# Service bindings
$matches = [regex]::Matches($configString, $serviceBindPattern)
foreach ($match in $matches) {
    $serviceName = $match.Groups[1].Value.Trim('"')
    $certName = $match.Groups[2].Value.Trim('"')
    
    if (-not $boundCertificates.ContainsKey($certName)) {
        $boundCertificates[$certName] = @()
    }
    $boundCertificates[$certName] += "SERVICE: $serviceName"
    
    if ($allCertificates.ContainsKey($certName)) {
        $allCertificates[$certName].BoundTo += "SERVICE: $serviceName"
    }
    $totalBindings++
}

# Vserver bindings
$matches = [regex]::Matches($configString, $vserverBindPattern)
foreach ($match in $matches) {
    $vserverName = $match.Groups[1].Value.Trim('"')
    $certName = $match.Groups[2].Value.Trim('"')
    
    if (-not $boundCertificates.ContainsKey($certName)) {
        $boundCertificates[$certName] = @()
    }
    $boundCertificates[$certName] += "VSERVER: $vserverName"
    
    if ($allCertificates.ContainsKey($certName)) {
        $allCertificates[$certName].BoundTo += "VSERVER: $vserverName"
    }
    $totalBindings++
}

Write-Host "  Found $totalBindings service/vserver bindings`n" -ForegroundColor Green

# RESULTS
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host "                    CERTIFICATE USAGE ANALYSIS" -ForegroundColor Yellow
Write-Host ("=" * 80) + "`n" -ForegroundColor Cyan

# SAML certificates
Write-Host "[SAML CERTIFICATES]" -ForegroundColor Magenta
if ($samlCertificates.Count -gt 0) {
    Write-Host "These certificates are used for SAML authentication:`n" -ForegroundColor Gray
    $samlCertificates.Keys | Sort-Object | ForEach-Object {
        Write-Host "  [+] $_" -ForegroundColor Magenta
        $boundCertificates[$_] | ForEach-Object {
            Write-Host "       |-- $_" -ForegroundColor DarkGray
        }
    }
} else {
    Write-Host "  No SAML certificates found`n" -ForegroundColor Gray
}

# ROOT/INTERMEDIATE certificates - IN USE (in chains)
Write-Host "`n[ROOT/INTERMEDIATE CERTIFICATES - IN USE]" -ForegroundColor Green
$usedRootInt = $rootIntermediateCertificates.Keys | Where-Object { 
    $allCertificates[$_].IsInChain -and 
    -not $samlCertificates.ContainsKey($_)
}
if ($usedRootInt.Count -gt 0) {
    Write-Host "These certificates are part of certificate chains:`n" -ForegroundColor Gray
    $usedRootInt | Sort-Object | ForEach-Object {
        Write-Host "  [+] $_" -ForegroundColor Green
        if ($allCertificates[$_].LinkedTo) {
            Write-Host "       |-- Parent: $($allCertificates[$_].LinkedTo)" -ForegroundColor DarkGray
        }
        if ($allCertificates[$_].LinkedFrom.Count -gt 0) {
            foreach ($child in $allCertificates[$_].LinkedFrom) {
                Write-Host "       |-- Child: $child" -ForegroundColor DarkGray
            }
        }
    }
} else {
    Write-Host "  None found" -ForegroundColor Gray
}

# ROOT/INTERMEDIATE certificates - NOT IN USE (orphaned)
Write-Host "`n[ROOT/INTERMEDIATE CERTIFICATES - NOT IN USE]" -ForegroundColor Red
$unusedRootInt = $rootIntermediateCertificates.Keys | Where-Object { 
    -not $allCertificates[$_].IsInChain -and 
    -not $samlCertificates.ContainsKey($_)
}
if ($unusedRootInt.Count -gt 0) {
    Write-Host "These ROOT/INTERMEDIATE certificates are NOT part of any chain and can be removed:" -ForegroundColor Yellow
    Write-Host "(They are orphaned - not linked to or from any other certificate)`n" -ForegroundColor Gray
    $unusedRootInt | Sort-Object | ForEach-Object {
        Write-Host "  [X] $_" -ForegroundColor Red
    }
    
    Write-Host "`nCommands to remove unused ROOT/INTERMEDIATE certificates:" -ForegroundColor Yellow
    Write-Host "----------------------------------------------------" -ForegroundColor DarkGray
    $unusedRootInt | Sort-Object | ForEach-Object {
        Write-Host "  rm ssl certkey `"$_`"" -ForegroundColor Cyan
    }
} else {
    Write-Host "  All ROOT/INTERMEDIATE certificates are properly used in chains!" -ForegroundColor Green
}

# SERVER certificates - BOUND
Write-Host "`n[SERVER CERTIFICATES - IN USE]" -ForegroundColor Green
$boundServerCerts = $serverCertificates.Keys | Where-Object { $boundCertificates.ContainsKey($_) }
if ($boundServerCerts.Count -gt 0) {
    Write-Host "These server certificates are actively used:`n" -ForegroundColor Gray
    $boundServerCerts | Sort-Object | ForEach-Object {
        Write-Host "  [+] $_" -ForegroundColor Green
        $boundCertificates[$_] | Select-Object -First 3 | ForEach-Object {
            Write-Host "       |-- $_" -ForegroundColor DarkGray
        }
        if ($boundCertificates[$_].Count -gt 3) {
            Write-Host "       |-- ... and $($boundCertificates[$_].Count - 3) more" -ForegroundColor DarkGray
        }
    }
} else {
    Write-Host "  No bound server certificates found" -ForegroundColor Yellow
}

# SERVER certificates - NOT BOUND
Write-Host "`n[SERVER CERTIFICATES - NOT IN USE]" -ForegroundColor Red
$unboundServerCerts = $serverCertificates.Keys | Where-Object { -not $boundCertificates.ContainsKey($_) }
if ($unboundServerCerts.Count -gt 0) {
    Write-Host "These server certificates are NOT bound to anything and can be removed:`n" -ForegroundColor Yellow
    $unboundServerCerts | Sort-Object | ForEach-Object {
        Write-Host "  [X] $_" -ForegroundColor Red
    }
    
    Write-Host "`nCommands to remove unused server certificates:" -ForegroundColor Yellow
    Write-Host "----------------------------------------" -ForegroundColor DarkGray
    $unboundServerCerts | Sort-Object | ForEach-Object {
        Write-Host "  rm ssl certkey `"$_`"" -ForegroundColor Cyan
    }
} else {
    Write-Host "  All server certificates are in use!" -ForegroundColor Green
}

# SUMMARY
Write-Host "`n" + ("=" * 80) -ForegroundColor Cyan
Write-Host "                              SUMMARY" -ForegroundColor Yellow
Write-Host ("=" * 80) -ForegroundColor Cyan

$totalCerts = $allCertificates.Count
$totalServer = $serverCertificates.Count
$totalSAML = $samlCertificates.Count
$totalRootInt = $rootIntermediateCertificates.Count - $totalSAML
$totalUnusedRootInt = $unusedRootInt.Count
$totalUsedRootInt = $usedRootInt.Count
$totalBoundServer = $boundServerCerts.Count
$totalUnboundServer = $unboundServerCerts.Count

Write-Host "`nTotal certificates: $totalCerts"
Write-Host "  |"
Write-Host "  |-- Server certificates: $totalServer"
Write-Host "  |    |-- In use (bound): $totalBoundServer" -ForegroundColor Green
Write-Host "  |    |-- Not in use: $totalUnboundServer" -ForegroundColor $(if ($totalUnboundServer -gt 0) { "Red" } else { "Green" })
Write-Host "  |"
Write-Host "  |-- SAML certificates: $totalSAML" -ForegroundColor Magenta
Write-Host "  |"
Write-Host "  |-- Root/Intermediate certificates: $totalRootInt"
Write-Host "       |-- In chains (used): $totalUsedRootInt" -ForegroundColor Green
Write-Host "       |-- Orphaned (unused): $totalUnusedRootInt" -ForegroundColor $(if ($totalUnusedRootInt -gt 0) { "Red" } else { "Green" })

# Total removable certificates
$totalRemovable = $totalUnboundServer + $totalUnusedRootInt
if ($totalRemovable -gt 0) {
    Write-Host "`n[CLEANUP POTENTIAL]" -ForegroundColor Yellow
    Write-Host "Total certificates that can be removed: $totalRemovable" -ForegroundColor Red
    Write-Host "  - Unused server certificates: $totalUnboundServer" -ForegroundColor Red
    Write-Host "  - Orphaned ROOT/INTERMEDIATE certificates: $totalUnusedRootInt" -ForegroundColor Red
} else {
    Write-Host "`n[EXCELLENT] All certificates are properly used!" -ForegroundColor Green
}

Write-Host "`n" + ("=" * 80) -ForegroundColor Cyan
Write-Host "Analysis complete" -ForegroundColor Cyan