powershell max policies
# Analyse des objets non bindés dans ns.conf
# NetScaler Policy Analyzer - Modified Version
# Affiche toutes les policies avec leur statut de binding
# v6.3 - Support complet des bindings globaux pour tous les types de policies
#
# Fonctionnalités:
# - Affiche TOUTES les policies (pas seulement les non bindées)
# - Code couleur : VERT pour les bindées, ROUGE pour les non bindées
# - Détails de binding en GRIS sous chaque policy bindée (avec priorités)
# - Gestion correcte des Service Groups bindés aux LB Vservers
# - Support complet des bindings globaux :
# * Authentication Policies (bind system global)
# * VPN Session/Traffic Policies (bind vpn global)
# * Responder Policies (bind responder global)
# * Rewrite Policies (bind rewrite global)
# * Et autres policies globales
# - Affichage des priorités pour les bindings globaux
# - Suppression de l'analyse des servers
$NS_CONF_PATH = "C:\tempmax\ns.conf"
Write-Host "`n" + ("=" * 80) -ForegroundColor Cyan
Write-Host " NetScaler Policy Analysis - Complete Edition" -ForegroundColor Yellow
Write-Host ("=" * 80) + "`n" -ForegroundColor Cyan
# Load configuration
$configLines = Load-NSConf -FilePath $NS_CONF_PATH
if ($configLines.Count -eq 0) {
exit
}
Write-Host "Configuration loaded: $($configLines.Count) lines`n" -ForegroundColor Green
# ADC Interface paths for each policy type
$policyPaths = @{
"authentication policy" = "Security > AAA-Application Traffic > Policies > Authentication > Advanced Policies"
"authentication policylabel" = "Security > AAA-Application Traffic > Policies > Authentication > Policy Labels"
"authentication authnprofile" = "Security > AAA-Application Traffic > Authentication Profile"
"authentication vserver" = "Security > AAA-Application Traffic > Authentication Virtual Servers"
"authentication loginschema" = "Security > AAA-Application Traffic > Authentication > Login Schemas"
"vpn sessionpolicy" = "Citrix Gateway > Policies > Session Policies"
"vpn trafficpolicy" = "Citrix Gateway > Policies > Traffic Policies"
"rewrite policy" = "AppExpert > Rewrite > Rewrite Policy"
"responder policy" = "AppExpert > Responder > Responder Policy"
"cache policy" = "AppExpert > Cache > Cache Policy"
"lb policy" = "Traffic Management > Load Balancing > Policies"
"cs policy" = "AppExpert > Content Switching > Policies"
"aaa policy" = "Citrix Gateway > Policies > AAA > AAA Policies"
"dns policy" = "Traffic Management > DNS > DNS Policies"
"appfw policy" = "Security > Application Firewall > Policies"
"videooptimization policy" = "Traffic Management > Video Optimization > Policies"
"contentinspection policy" = "Security > Content Inspection > Policies"
"servicegroup" = "Traffic Management > Load Balancing > Service Groups"
}
function Load-NSConf {
param([string]$FilePath)
if (Test-Path $FilePath) {
return Get-Content -Path $FilePath -Encoding UTF8
} else {
Write-Host "ERROR: File $FilePath not found." -ForegroundColor Red
return @()
}
}
function Find-DefinedObjects {
param(
[string[]]$ConfigLines,
[string]$ObjectType
)
$configString = $ConfigLines -join "`n"
$pattern = "(?i)add $ObjectType\s+(`"[^`"]+`"|\S+)"
$matches = [regex]::Matches($configString, $pattern)
$objects = @{}
foreach ($match in $matches) {
$objectName = $match.Groups[1].Value.Trim('"')
$objects[$objectName] = @{
Name = $objectName
Type = $ObjectType
BoundTo = @()
BindingDetails = @()
}
}
return $objects
}
function Find-BoundObjects {
param(
[string[]]$ConfigLines,
[string]$ObjectType,
[hashtable]$DefinedObjects
)
$configString = $ConfigLines -join "`n"
$boundObjects = @{}
switch ($ObjectType.ToLower()) {
"authentication authnprofile" {
$pattern = "(?is)add vpn vserver\s+(\S+).*?-authnProfile\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $pattern)
foreach ($match in $matches) {
$vserver = $match.Groups[1].Value.Trim('"')
$profile = $match.Groups[2].Value.Trim('"')
if (-not $boundObjects.ContainsKey($profile)) {
$boundObjects[$profile] = @()
}
$boundObjects[$profile] += "VPN_VSERVER: $vserver"
}
}
"authentication vserver" {
$mapping = @{}
$patternMap = "(?i)add authentication authnProfile\s+(`"([^`"]+)`"|\S+).*?-authnVsName\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $patternMap)
foreach ($match in $matches) {
$profile = $match.Groups[1].Value.Trim('"')
$aaaVserver = $match.Groups[3].Value.Trim('"')
$mapping[$profile] = $aaaVserver
}
$patternBound = "(?is)add vpn vserver\s+(\S+).*?-authnProfile\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $patternBound)
foreach ($match in $matches) {
$vserver = $match.Groups[1].Value.Trim('"')
$profile = $match.Groups[2].Value.Trim('"')
if ($mapping.ContainsKey($profile)) {
$aaaVserver = $mapping[$profile]
if (-not $boundObjects.ContainsKey($aaaVserver)) {
$boundObjects[$aaaVserver] = @()
}
$boundObjects[$aaaVserver] += "VPN_VSERVER: $vserver (via profile: $profile)"
}
}
}
"authentication policy" {
# Bound to authentication vservers
$directPattern = "(?is)bind authentication vserver\s+(\S+).*?-policy\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $directPattern)
foreach ($match in $matches) {
$vserver = $match.Groups[1].Value.Trim('"')
$policy = $match.Groups[2].Value.Trim('"')
if (-not $boundObjects.ContainsKey($policy)) {
$boundObjects[$policy] = @()
}
$boundObjects[$policy] += "AUTH_VSERVER: $vserver"
}
# Bound to policy labels
$labelPattern = "(?is)bind authentication policylabel\s+(\S+).*?-policyName\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $labelPattern)
foreach ($match in $matches) {
$label = $match.Groups[1].Value.Trim('"')
$policy = $match.Groups[2].Value.Trim('"')
if (-not $boundObjects.ContainsKey($policy)) {
$boundObjects[$policy] = @()
}
$boundObjects[$policy] += "POLICY_LABEL: $label"
}
# Alternative pattern for policy labels (using -policy instead of -policyName)
$labelPattern2 = "(?is)bind authentication policylabel\s+(\S+).*?-policy\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $labelPattern2)
foreach ($match in $matches) {
$label = $match.Groups[1].Value.Trim('"')
$policy = $match.Groups[2].Value.Trim('"')
if (-not $boundObjects.ContainsKey($policy)) {
$boundObjects[$policy] = @()
}
if ($boundObjects[$policy] -notcontains "POLICY_LABEL: $label") {
$boundObjects[$policy] += "POLICY_LABEL: $label"
}
}
# Bound globally to system
$globalPattern = "(?is)bind system global\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $globalPattern)
foreach ($match in $matches) {
$policy = $match.Groups[1].Value.Trim('"')
# Check if this is actually an authentication policy (exists in our defined objects)
if ($DefinedObjects.ContainsKey($policy)) {
if (-not $boundObjects.ContainsKey($policy)) {
$boundObjects[$policy] = @()
}
# Extract priority if available
if ($match.Groups[0].Value -match "-priority\s+(\d+)") {
$priority = $Matches[1]
$boundObjects[$policy] += "GLOBAL_SYSTEM: (priority: $priority)"
} else {
$boundObjects[$policy] += "GLOBAL_SYSTEM"
}
}
}
}
"authentication policylabel" {
$pattern = "(?is)bind authentication vserver\s+(\S+).*?-nextFactor\s+(\S+)"
$matches = [regex]::Matches($configString, $pattern)
foreach ($match in $matches) {
$vserver = $match.Groups[1].Value.Trim('"')
$label = $match.Groups[2].Value.Trim('"')
if (-not $boundObjects.ContainsKey($label)) {
$boundObjects[$label] = @()
}
$boundObjects[$label] += "AUTH_VSERVER: $vserver (as nextFactor)"
}
}
"responder policy" {
$pattern = "(?is)bind (?:vpn vserver|lb vserver|cs vserver)\s+(\S+).*?-policy\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $pattern)
foreach ($match in $matches) {
$vserver = $match.Groups[1].Value.Trim('"')
$policy = $match.Groups[2].Value.Trim('"')
if (-not $boundObjects.ContainsKey($policy)) {
$boundObjects[$policy] = @()
}
$boundObjects[$policy] += "VSERVER: $vserver"
}
# Check for global responder policy bindings
$globalPattern = "(?is)bind responder global\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $globalPattern)
foreach ($match in $matches) {
$policy = $match.Groups[1].Value.Trim('"')
if ($DefinedObjects.ContainsKey($policy)) {
if (-not $boundObjects.ContainsKey($policy)) {
$boundObjects[$policy] = @()
}
if ($match.Groups[0].Value -match "-priority\s+(\d+)") {
$priority = $Matches[1]
$boundObjects[$policy] += "GLOBAL_RESPONDER: (priority: $priority)"
} else {
$boundObjects[$policy] += "GLOBAL_RESPONDER"
}
}
}
}
{ $_ -in @("vpn sessionpolicy", "vpn trafficpolicy") } {
$pattern = "(?is)bind vpn vserver\s+(\S+).*?-policy\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $pattern)
foreach ($match in $matches) {
$vserver = $match.Groups[1].Value.Trim('"')
$policy = $match.Groups[2].Value.Trim('"')
if (-not $boundObjects.ContainsKey($policy)) {
$boundObjects[$policy] = @()
}
$boundObjects[$policy] += "VPN_VSERVER: $vserver"
}
# Check for global VPN policy bindings
$globalPattern = "(?is)bind vpn global.*?-policy\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $globalPattern)
foreach ($match in $matches) {
$policy = $match.Groups[1].Value.Trim('"')
if ($DefinedObjects.ContainsKey($policy)) {
if (-not $boundObjects.ContainsKey($policy)) {
$boundObjects[$policy] = @()
}
if ($match.Groups[0].Value -match "-priority\s+(\d+)") {
$priority = $Matches[1]
$boundObjects[$policy] += "GLOBAL_VPN: (priority: $priority)"
} else {
$boundObjects[$policy] += "GLOBAL_VPN"
}
}
}
}
{ $_ -in @("rewrite policy", "cache policy", "cs policy", "aaa policy",
"dns policy", "appfw policy", "videooptimization policy",
"contentinspection policy", "lb policy") } {
$pattern = "(?i)bind .*?-policyName\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $pattern)
foreach ($match in $matches) {
$policy = $match.Groups[1].Value.Trim('"')
if (-not $boundObjects.ContainsKey($policy)) {
$boundObjects[$policy] = @()
}
# Extract binding context from full match
$fullMatch = $match.Groups[0].Value
if ($fullMatch -match "bind\s+(\S+\s+\S+)") {
$context = $Matches[1].ToUpper()
$boundObjects[$policy] += $context
}
}
# Check for global bindings based on policy type
$globalType = switch ($ObjectType.ToLower()) {
"rewrite policy" { "rewrite" }
"cache policy" { "cache" }
"cs policy" { "cs" }
"aaa policy" { "aaa" }
"dns policy" { "dns" }
"appfw policy" { "appfw" }
"videooptimization policy" { "videooptimization" }
"contentinspection policy" { "contentinspection" }
"lb policy" { "lb" }
}
if ($globalType) {
$globalPattern = "(?is)bind $globalType global.*?(?:-policy|-policyName)\s+(`"([^`"]+)`"|\S+)"
$matches = [regex]::Matches($configString, $globalPattern)
foreach ($match in $matches) {
$policy = $match.Groups[1].Value.Trim('"')
if ($DefinedObjects.ContainsKey($policy)) {
if (-not $boundObjects.ContainsKey($policy)) {
$boundObjects[$policy] = @()
}
if ($match.Groups[0].Value -match "-priority\s+(\d+)") {
$priority = $Matches[1]
$boundObjects[$policy] += "GLOBAL_$($globalType.ToUpper()): (priority: $priority)"
} else {
$boundObjects[$policy] += "GLOBAL_$($globalType.ToUpper())"
}
}
}
}
}
"servicegroup" {
# Service groups are bound to LB vservers
$pattern = "(?i)bind lb vserver\s+(`"[^`"]+`"|\S+)\s+(`"[^`"]+`"|\S+)"
$matches = [regex]::Matches($configString, $pattern)
foreach ($match in $matches) {
$vserver = $match.Groups[1].Value.Trim('"')
$sg = $match.Groups[2].Value.Trim('"')
# Check if this is a service group (not a service)
if ($DefinedObjects.ContainsKey($sg)) {
if (-not $boundObjects.ContainsKey($sg)) {
$boundObjects[$sg] = @()
}
$boundObjects[$sg] += "LB_VSERVER: $vserver"
}
}
# Service groups can also be bound to CS vservers
$csPattern = "(?i)bind cs vserver\s+(`"[^`"]+`"|\S+)\s+-lbvserver\s+(`"[^`"]+`"|\S+)"
$csMatches = [regex]::Matches($configString, $csPattern)
foreach ($match in $csMatches) {
$csvserver = $match.Groups[1].Value.Trim('"')
$lbvserver = $match.Groups[2].Value.Trim('"')
# Now find service groups bound to this LB vserver
$lbPattern = "(?i)bind lb vserver\s+$([regex]::Escape($lbvserver))\s+(`"[^`"]+`"|\S+)"
$lbMatches = [regex]::Matches($configString, $lbPattern)
foreach ($lbMatch in $lbMatches) {
$sg = $lbMatch.Groups[1].Value.Trim('"')
if ($DefinedObjects.ContainsKey($sg)) {
if (-not $boundObjects.ContainsKey($sg)) {
$boundObjects[$sg] = @()
}
if ($boundObjects[$sg] -notcontains "CS_VSERVER: $csvserver (via LB: $lbvserver)") {
$boundObjects[$sg] += "CS_VSERVER: $csvserver (via LB: $lbvserver)"
}
}
}
}
}
default {
$patternDirect = "(?i)bind $ObjectType\s+(`"[^`"]+`"|\S+)"
$matches = [regex]::Matches($configString, $patternDirect)
foreach ($match in $matches) {
$obj = $match.Groups[1].Value.Trim('"')
if (-not $boundObjects.ContainsKey($obj)) {
$boundObjects[$obj] = @()
}
$boundObjects[$obj] += "DIRECT_BIND"
}
}
}
# Update defined objects with binding info
foreach ($objName in $boundObjects.Keys) {
if ($DefinedObjects.ContainsKey($objName)) {
$DefinedObjects[$objName].BoundTo = $boundObjects[$objName]
}
}
return $boundObjects
}
# Main analysis - REMOVED SERVER from the list
$policyTypes = @(
"authentication policy",
"authentication policylabel",
"authentication authnprofile",
"authentication vserver",
"authentication loginschema",
"vpn sessionpolicy",
"vpn trafficpolicy",
"rewrite policy",
"responder policy",
"cache policy",
"lb policy",
"cs policy",
"aaa policy",
"dns policy",
"appfw policy",
"videooptimization policy",
"contentinspection policy",
"servicegroup"
)
Write-Host "Starting policy analysis..." -ForegroundColor Yellow
Write-Host ("=" * 80) -ForegroundColor DarkGray
Write-Host ""
$allPolicies = @{}
$totalDefined = 0
$totalBound = 0
$totalUnbound = 0
foreach ($policyType in $policyTypes) {
Write-Progress -Activity "Analysis in progress" -Status "Processing: $policyType" `
-PercentComplete (($policyTypes.IndexOf($policyType) + 1) / $policyTypes.Count * 100)
$defined = Find-DefinedObjects -ConfigLines $configLines -ObjectType $policyType
$bound = Find-BoundObjects -ConfigLines $configLines -ObjectType $policyType -DefinedObjects $defined
# Store all policies for this type
if ($defined.Count -gt 0) {
$allPolicies[$policyType] = @{
Defined = $defined
Bound = $bound
}
$totalDefined += $defined.Count
$totalBound += $bound.Count
$totalUnbound += ($defined.Count - $bound.Count)
}
}
Write-Progress -Completed -Activity "Analysis in progress"
# Display results - COMPLETE LIST WITH COLOR CODING
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host " COMPLETE POLICY LIST WITH BINDING STATUS" -ForegroundColor Yellow
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host ""
Write-Host "Legend: " -NoNewline
Write-Host "[✓] Bound (Used) " -ForegroundColor Green -NoNewline
Write-Host "[X] Unbound (Unused) " -ForegroundColor Red -NoNewline
Write-Host "[GLOBAL_*] Global Binding" -ForegroundColor Cyan
Write-Host ""
foreach ($policyType in $policyTypes) {
if ($allPolicies.ContainsKey($policyType)) {
$policies = $allPolicies[$policyType]
$defined = $policies.Defined
$bound = $policies.Bound
$path = $policyPaths[$policyType.ToLower()]
if (-not $path) {
$path = "Path not defined"
}
Write-Host "[POLICY TYPE: $policyType]" -ForegroundColor Yellow
Write-Host "Location: $path" -ForegroundColor DarkGray
Write-Host "Total: $($defined.Count) | " -NoNewline
Write-Host "Bound: $($bound.Count) " -ForegroundColor Green -NoNewline
Write-Host "| Unbound: $($defined.Count - $bound.Count)" -ForegroundColor Red
Write-Host ""
# Sort policies alphabetically
foreach ($objName in ($defined.Keys | Sort-Object)) {
if ($bound.ContainsKey($objName)) {
# Policy is bound (used) - show in GREEN
Write-Host " [✓] $objName" -ForegroundColor Green
# Show binding details in GRAY
foreach ($binding in $defined[$objName].BoundTo) {
Write-Host " └─ $binding" -ForegroundColor Gray
}
} else {
# Policy is unbound (unused) - show in RED
if ($policyType -eq "authentication vserver") {
Write-Host " [X] $objName (not bound to any VPN Vserver)" -ForegroundColor Red
} elseif ($policyType -eq "servicegroup") {
Write-Host " [X] $objName (not bound to any LB Vserver)" -ForegroundColor Red
} else {
Write-Host " [X] $objName" -ForegroundColor Red
}
}
}
Write-Host ""
}
}
# Summary with tree view
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host " SUMMARY" -ForegroundColor Yellow
Write-Host ("=" * 80) -ForegroundColor Cyan
Write-Host ""
Write-Host "Total Analysis:" -ForegroundColor White
Write-Host " |"
Write-Host " |-- Total objects analyzed: $totalDefined"
Write-Host " | |-- " -NoNewline
Write-Host "Bound (in use): $totalBound" -ForegroundColor Green
Write-Host " | |-- " -NoNewline
Write-Host "Unbound (unused): $totalUnbound" -ForegroundColor $(if ($totalUnbound -gt 0) { "Red" } else { "Green" })
Write-Host " |"
Write-Host " |-- Policy types analyzed: $($allPolicies.Count)" -ForegroundColor White
if ($totalUnbound -gt 0) {
$percentage = [math]::Round(($totalUnbound / $totalDefined * 100), 1)
Write-Host "`n[WARNING] $totalUnbound object(s) ($percentage%) are not bound and can be reviewed for cleanup" -ForegroundColor Yellow
} else {
Write-Host "`n[EXCELLENT] All policies and objects are properly bound!" -ForegroundColor Green
}
Write-Host "`n" + ("=" * 80) -ForegroundColor Cyan
Write-Host "Analysis complete" -ForegroundColor Cyan
Write-Host "`nFor detailed cleanup commands, consider creating object-specific cleanup scripts." -ForegroundColor Gray
Write-Host "Always save config before making changes: save config" -ForegroundColor Yellow