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