NSNitroExtractor.ps1 62 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139
  1. #Requires -Version 5.1
  2. <#
  3. .SYNOPSIS
  4. NetScaler NITRO API Configuration Extractor
  5. Connects live to a NetScaler ADC via the NITRO REST API and extracts
  6. configuration objects similar to the file-based extractor.
  7. .DESCRIPTION
  8. Prompts for NSIP, Username, and Password, authenticates via NITRO,
  9. then lets you select a vServer and extracts all dependent objects
  10. (services, monitors, policies, SSL certs, auth actions, etc.).
  11. .PARAMETER nsip
  12. IP or FQDN of the NetScaler management interface. Prompted if not supplied.
  13. .PARAMETER username
  14. NetScaler admin username. Prompted if not supplied.
  15. .PARAMETER password
  16. NetScaler admin password as SecureString. Prompted if not supplied.
  17. .PARAMETER vserver
  18. Partial or full vServer name to filter. Leave blank to list all.
  19. .PARAMETER outputFile
  20. Path to save extracted config. "screen" to print only. Prompted if blank.
  21. .PARAMETER textEditor
  22. Text editor to open output file after extraction.
  23. .PARAMETER useSSL
  24. Use HTTPS (default). Set to $false to use HTTP (not recommended).
  25. .PARAMETER skipCertCheck
  26. Skip SSL certificate validation (useful for self-signed certs on lab appliances).
  27. .PARAMETER nFactorNestingLevel
  28. How many nFactor Next Factor levels to traverse (default 5).
  29. .NOTES
  30. Change Log
  31. ----------
  32. 2025 Mar - Initial release based on NITRO API v2 (NetScaler 12.x / 13.x / 14.x)
  33. Supports: lb, cs, vpn, authentication, gslb vServers
  34. Supports: services, serviceGroups, monitors, servers
  35. Supports: SSL certs/profiles/ciphers/policies
  36. Supports: rewrite, responder, appfw, cmp, cache, transform policies
  37. Supports: AAA / nFactor (ldap, radius, saml, cert, tacacs, oauth, etc.)
  38. Supports: GSLB sites/services
  39. Supports: System settings (features, modes, HA, IPs, routes, DNS)
  40. #>
  41. param (
  42. [string] $nsip = "",
  43. [string] $username = "",
  44. [securestring] $password = $null,
  45. [string] $vserver = "",
  46. [string] $outputFile = "$env:USERPROFILE\Downloads\nsnitro_config.conf",
  47. [string] $textEditor = "notepad++.exe",
  48. [bool] $useSSL = $true,
  49. [switch] $skipCertCheck,
  50. [switch] $cswBind,
  51. [int] $nFactorNestingLevel = 5
  52. )
  53. Set-StrictMode -Off
  54. $ErrorActionPreference = "Stop"
  55. # ─────────────────────────────────────────────────────────────────────────────
  56. # TLS / Cert helpers
  57. # ─────────────────────────────────────────────────────────────────────────────
  58. if ($skipCertCheck) {
  59. if ($PSVersionTable.PSVersion.Major -ge 6) {
  60. # PowerShell Core / 7+
  61. $script:InvokeParams = @{ SkipCertificateCheck = $true }
  62. } else {
  63. # Windows PowerShell 5.x – add a permissive policy once
  64. if (-not ([System.Management.Automation.PSTypeName]'TrustAllCertsPolicy').Type) {
  65. Add-Type @"
  66. using System.Net;
  67. using System.Security.Cryptography.X509Certificates;
  68. public class TrustAllCertsPolicy : ICertificatePolicy {
  69. public bool CheckValidationResult(
  70. ServicePoint srvPoint, X509Certificate certificate,
  71. WebRequest request, int certificateProblem) { return true; }
  72. }
  73. "@
  74. }
  75. [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
  76. $script:InvokeParams = @{}
  77. }
  78. } else {
  79. $script:InvokeParams = @{}
  80. }
  81. [System.Net.ServicePointManager]::SecurityProtocol =
  82. [System.Net.SecurityProtocolType]::Tls12 -bor
  83. [System.Net.SecurityProtocolType]::Tls13
  84. # ─────────────────────────────────────────────────────────────────────────────
  85. # Prompt helpers
  86. # ─────────────────────────────────────────────────────────────────────────────
  87. function Prompt-Input ([string]$Prompt, [string]$Default = "") {
  88. $val = Read-Host $Prompt
  89. if (-not $val -and $Default) { return $Default }
  90. return $val
  91. }
  92. function Get-OutputFilePath {
  93. if ($IsMacOS) {
  94. $f = (('tell application "SystemUIServer"' + "`n" + 'activate' + "`n" +
  95. 'set theName to POSIX path of (choose file name default name "nsnitro_config.conf" with prompt "Save extracted config as")' + "`n" +
  96. 'end tell' | osascript -s s) -split '"')[1]
  97. return $f
  98. }
  99. [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
  100. $d = New-Object System.Windows.Forms.SaveFileDialog
  101. $d.Title = "Save Extracted NetScaler Config"
  102. $d.Filter = "NetScaler Config (*.conf)|*.conf|All files (*.*)|*.*"
  103. $d.ShowDialog() | Out-Null
  104. return $d.FileName
  105. }
  106. # ─────────────────────────────────────────────────────────────────────────────
  107. # Credential / connection setup
  108. # ─────────────────────────────────────────────────────────────────────────────
  109. cls
  110. Write-Host "╔══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
  111. Write-Host "║ NetScaler NITRO API Configuration Extractor ║" -ForegroundColor Cyan
  112. Write-Host "╚══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
  113. Write-Host ""
  114. if (-not $nsip) { $nsip = Prompt-Input "Enter NetScaler Management IP or FQDN" }
  115. if (-not $username) { $username = Prompt-Input "Enter Username" "nsroot" }
  116. if (-not $password) { $password = Read-Host "Enter Password" -AsSecureString }
  117. $proto = if ($useSSL) { "https" } else { "http" }
  118. $baseUrl = $proto + "://" + $nsip + "/nitro/v1"
  119. $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password)
  120. $plainPwd = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
  121. [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR)
  122. # Session cookie store
  123. $script:Session = $null
  124. # ─────────────────────────────────────────────────────────────────────────────
  125. # NITRO Helper Functions
  126. # ─────────────────────────────────────────────────────────────────────────────
  127. function Invoke-Nitro {
  128. param(
  129. [string] $Method = "GET",
  130. [string] $Resource,
  131. [string] $Action = "",
  132. [object] $Body = $null,
  133. [hashtable] $Query = @{}
  134. )
  135. $uri = "$baseUrl/config/$Resource"
  136. if ($Query.Count -gt 0) {
  137. $qs = ($Query.GetEnumerator() | ForEach-Object { "$($_.Key)=$([uri]::EscapeDataString($_.Value))" }) -join "&"
  138. $uri += "?$qs"
  139. }
  140. if ($Action) { $uri += "?action=$Action" }
  141. $headers = @{ "Content-Type" = "application/json" }
  142. if ($script:AuthToken) { $headers["Cookie"] = "NITRO_AUTH_TOKEN=$($script:AuthToken)" }
  143. $splat = @{
  144. Uri = $uri
  145. Method = $Method
  146. Headers = $headers
  147. WebSession = $script:Session
  148. UseBasicParsing = $true
  149. } + $script:InvokeParams
  150. if ($Body) { $splat["Body"] = ($Body | ConvertTo-Json -Depth 10 -Compress) }
  151. try {
  152. $resp = Invoke-WebRequest @splat
  153. $json = $resp.Content | ConvertFrom-Json
  154. return $json
  155. } catch {
  156. $msg = $_.Exception.Message
  157. if ($_.Exception.Response) {
  158. try {
  159. $stream = $_.Exception.Response.GetResponseStream()
  160. $reader = New-Object System.IO.StreamReader($stream)
  161. $errBody = $reader.ReadToEnd() | ConvertFrom-Json
  162. $msg = "NITRO Error $($errBody.errorcode): $($errBody.message)"
  163. } catch {}
  164. }
  165. Write-Warning "NITRO call failed [$Method $Resource]: $msg"
  166. return $null
  167. }
  168. }
  169. function Get-NitroObjects {
  170. param(
  171. [string] $Resource,
  172. [string] $Filter = "",
  173. [string[]] $Attrs = @(),
  174. [int] $PageSize = 0
  175. )
  176. $query = @{}
  177. if ($Filter) { $query["filter"] = $Filter }
  178. if ($Attrs) { $query["attrs"] = ($Attrs -join ",") }
  179. if ($PageSize -gt 0) { $query["count"] = "yes" }
  180. $result = Invoke-Nitro -Method GET -Resource $Resource -Query $query
  181. if (-not $result) { return @() }
  182. # The resource name is the key in the response object
  183. $key = $Resource -replace "/.*","" # strip any sub-resource path
  184. if ($result.PSObject.Properties[$key]) {
  185. return @($result.$key)
  186. }
  187. return @()
  188. }
  189. function Get-NitroBinding {
  190. param([string]$Resource, [string]$Name, [string]$BindType)
  191. $enc = [uri]::EscapeDataString($Name)
  192. $path = "${Resource}/${enc}"
  193. if ($BindType) { $path += "?type=$BindType" }
  194. $result = Invoke-Nitro -Method GET -Resource $path
  195. if (-not $result) { return @() }
  196. $key = ($Resource -replace "/.*","") + "_binding"
  197. if ($result.PSObject.Properties[$key]) { return @($result.$key) }
  198. # some bindings return the type directly
  199. $key2 = ($Resource -replace "/.*","") + "_" + $BindType + "_binding"
  200. if ($result.PSObject.Properties[$key2]) { return @($result.$key2) }
  201. return @()
  202. }
  203. # ─────────────────────────────────────────────────────────────────────────────
  204. # Login
  205. # ─────────────────────────────────────────────────────────────────────────────
  206. Write-Host "`nConnecting to $baseUrl ..." -ForegroundColor Yellow
  207. $loginBody = @{
  208. login = @{
  209. username = $username
  210. password = $plainPwd
  211. timeout = 3600
  212. }
  213. }
  214. # Use SessionVariable to capture cookies
  215. $loginUri = "$baseUrl/config/login"
  216. $loginHeaders = @{ "Content-Type" = "application/json" }
  217. $loginSplat = @{
  218. Uri = $loginUri
  219. Method = "POST"
  220. Headers = $loginHeaders
  221. Body = ($loginBody | ConvertTo-Json -Compress)
  222. SessionVariable = "webSession"
  223. UseBasicParsing = $true
  224. } + $script:InvokeParams
  225. try {
  226. $loginResp = Invoke-WebRequest @loginSplat
  227. $script:Session = $webSession
  228. $loginJson = $loginResp.Content | ConvertFrom-Json
  229. # Some versions return a token, others just use the session cookie
  230. if ($loginJson.PSObject.Properties["login"]) {
  231. $script:AuthToken = $loginJson.login.sessionid
  232. }
  233. Write-Host "✔ Login successful." -ForegroundColor Green
  234. } catch {
  235. Write-Host "✘ Login failed: $($_.Exception.Message)" -ForegroundColor Red
  236. exit 1
  237. }
  238. $plainPwd = $null # clear plain-text password from memory
  239. # ─────────────────────────────────────────────────────────────────────────────
  240. # Output helpers
  241. # ─────────────────────────────────────────────────────────────────────────────
  242. $script:outputLines = [System.Collections.Generic.List[string]]::new()
  243. function Out-Line ([string]$line) {
  244. $script:outputLines.Add($line)
  245. }
  246. function Out-Section ([string]$title) {
  247. Out-Line ""
  248. Out-Line "# $title"
  249. Out-Line "# $("-" * $title.Length)"
  250. }
  251. function Write-Output-File {
  252. param([string]$path)
  253. $content = $script:outputLines -join "`n"
  254. # UNIX line endings
  255. [IO.File]::WriteAllText($path, $content)
  256. Write-Host "`nConfig written to: $path" -ForegroundColor Green
  257. }
  258. function Format-NitroObject {
  259. # Convert a NITRO object (PSCustomObject) into a NetScaler CLI-like line
  260. param([string]$Cmd, [object]$Obj)
  261. $line = $Cmd
  262. foreach ($prop in $Obj.PSObject.Properties) {
  263. $val = $prop.Value
  264. if ($null -eq $val -or $val -eq "" -or $val -eq 0 -or $val -eq $false) { continue }
  265. if ($val -is [array] -and $val.Count -eq 0) { continue }
  266. $name = $prop.Name
  267. # skip internal / metadata fields
  268. if ($name -in @("__count","bindpoint","stateflag","flags","statechangetimesec",
  269. "statechangetimelarge","statechangetime","tickssincelaststatechange",
  270. "cursynfloodrate","vsvrbindsvcip","vsvrbindsvcport","policysubtype",
  271. "curstate","status","monstatcode","monstatparam1","monstatparam2",
  272. "monstatparam3","responsetime","riseapbrstatsmsgcode","lbvserver",
  273. "nodefaultbindings","translationip","translationmask","weight",
  274. "dynamicweight","dbslb","totalrequestbytes","totalresponsebytes",
  275. "curclntconnections","cursrvrconnections","surgecount","svrestablishedconn",
  276. "requestsinflight","avgsvrtttfb","curpersistencesessions")) { continue }
  277. if ($val -is [array]) {
  278. $val = $val -join ","
  279. }
  280. # quote values with spaces
  281. if ("$val" -match "\s") { $val = "`"$val`"" }
  282. $line += " -$name $val"
  283. }
  284. return $line
  285. }
  286. # ─────────────────────────────────────────────────────────────────────────────
  287. # Fetch & catalogue all main object types up front (cached hashtable)
  288. # ─────────────────────────────────────────────────────────────────────────────
  289. Write-Host "`nFetching configuration objects from NetScaler..." -ForegroundColor Yellow
  290. $cache = @{}
  291. function Fetch-Cache ([string]$Type) {
  292. if (-not $cache.ContainsKey($Type)) {
  293. $objs = Get-NitroObjects -Resource $Type
  294. $cache[$Type] = $objs
  295. Write-Host (" Loaded {0,-40} ({1} objects)" -f $Type, $objs.Count)
  296. }
  297. return $cache[$Type]
  298. }
  299. # Pre-load common types
  300. $types = @(
  301. "lbvserver","csvserver","vpnvserver","authenticationvserver","gslbvserver",
  302. "service","servicegroup","server","lbmonitor",
  303. "sslvserver","sslcertkey","sslprofile","sslcipher","sslpolicy","sslaction",
  304. "rewritepolicy","rewriteaction","rewritepolicylabel",
  305. "responderpolicy","responderaction","responderpolicylabel",
  306. "cspolicy","csaction","cspolicylabel",
  307. "appfwpolicy","appfwprofile","appfwpolicylabel",
  308. "cmppolicy","cmpaction","cmppolicylabel",
  309. "cachepolicy","cachecontentgroup","cacheselector","cachepolicylabel",
  310. "transformpolicy","transformaction","transformprofile","transformpolicylabel",
  311. "authenticationldapaction","authenticationldappolicy",
  312. "authenticationradiusaction","authenticationradiuspolicy",
  313. "authenticationsamlaction","authenticationsamlpolicy",
  314. "authenticationcertaction","authenticationcertpolicy",
  315. "authenticationtacacsaction","authenticationtacacspolicy",
  316. "authenticationpolicy","authenticationpolicylabel","authenticationloginschemapolicy",
  317. "authenticationloginschema","authenticationauthnprofile",
  318. "vpnsessionpolicy","vpnsessionaction","vpntrafficpolicy","vpntrafficaction",
  319. "tmsessionpolicy","tmsessionaction","tmtrafficpolicy","tmtrafficaction",
  320. "vpnclientlessaccesspolicy","vpnclientlessaccessprofile",
  321. "authorizationpolicy","authorizationpolicylabel",
  322. "auditsyslogpolicy","auditsyslogaction","auditnslogpolicy","auditnslogaction",
  323. "netprofile","nshttpprofile","nstcpprofile","dnssuffix","dnsprofile","dnsview",
  324. "gslbsite","gslbservice","nslimitidentifier","nslimitselector",
  325. "nsacl","route","nsip","vlan","interface","haparam","hanode","nsfeature","nsmode",
  326. "systemparameter","systemuser","systemgroup"
  327. )
  328. foreach ($t in $types) {
  329. $null = Fetch-Cache $t
  330. }
  331. Write-Host "`n✔ Object fetch complete." -ForegroundColor Green
  332. # ─────────────────────────────────────────────────────────────────────────────
  333. # vServer selection
  334. # ─────────────────────────────────────────────────────────────────────────────
  335. Write-Host ""
  336. # Collect all vServers from all types
  337. $allVServers = @()
  338. foreach ($vsType in @("lbvserver","csvserver","vpnvserver","authenticationvserver","gslbvserver")) {
  339. foreach ($vs in $cache[$vsType]) {
  340. $allVServers += [PSCustomObject]@{
  341. Type = $vsType
  342. Name = $vs.name
  343. VIP = if ($vs.PSObject.Properties["ipv46"]) { $vs.ipv46 }
  344. elseif ($vs.PSObject.Properties["ip"]) { $vs.ip }
  345. else { "" }
  346. Port = if ($vs.PSObject.Properties["port"]) { $vs.port } else { "" }
  347. Protocol = if ($vs.PSObject.Properties["servicetype"]) { $vs.servicetype } else { "" }
  348. State = if ($vs.PSObject.Properties["curstate"]) { $vs.curstate } else { "" }
  349. }
  350. }
  351. }
  352. # Filter by $vserver if provided
  353. if ($vserver) {
  354. $filtered = $allVServers | Where-Object { $_.Name -match [regex]::Escape($vserver) }
  355. } else {
  356. $filtered = $allVServers
  357. }
  358. if ($filtered.Count -eq 0) {
  359. Write-Host "No vServers found matching '$vserver'." -ForegroundColor Red
  360. exit 1
  361. }
  362. # Add System Settings option
  363. $sysOption = [PSCustomObject]@{
  364. Type = "sys"; Name = "** System Settings **"; VIP = ""; Port = ""; Protocol = ""; State = ""
  365. }
  366. $selectionList = @($sysOption) + ($filtered | Sort-Object Type, Name)
  367. Write-Host "Select Virtual Server(s) to extract:`n"
  368. if ($IsMacOS) {
  369. $names = $selectionList | ForEach-Object { $_.Name.Trim('"') }
  370. $vsRaw = (('tell application "SystemUIServer"' + "`n" + 'activate' + "`n" +
  371. 'set vserver to (choose from list {"' + ($names -join '","') + '"} with prompt "Cmd+Select Multiple vServers" with multiple selections allowed)' + "`n" +
  372. 'end tell' | osascript -s s) -replace ', ',',')
  373. $selectedNames = [regex]::Matches($vsRaw,'(?:([\w\s\.\*\-]+))') | ForEach-Object { $_.Value }
  374. $selectedVServers = $selectionList | Where-Object { $selectedNames -contains $_.Name.Trim('"') }
  375. } else {
  376. $selectedVServers = $selectionList | Out-GridView -Title "Ctrl+Select Multiple Virtual Servers to extract" -PassThru
  377. }
  378. if (-not $selectedVServers) {
  379. Write-Host "No selection made. Exiting." -ForegroundColor Yellow; exit 0
  380. }
  381. # ─────────────────────────────────────────────────────────────────────────────
  382. # Output file prompt
  383. # ─────────────────────────────────────────────────────────────────────────────
  384. if (-not $outputFile) { $outputFile = Get-OutputFilePath }
  385. if (-not $outputFile) { $outputFile = "screen" }
  386. # ─────────────────────────────────────────────────────────────────────────────
  387. # Extraction Engine
  388. # ─────────────────────────────────────────────────────────────────────────────
  389. # Lookup helpers – find object in cache by name
  390. function Get-ObjectByName ([string]$Type, [string]$Name) {
  391. return $cache[$Type] | Where-Object { $_.name -eq $Name }
  392. }
  393. function Get-BindingsFor ([string]$BindResource, [string]$Name) {
  394. # e.g. BindResource = "lbvserver_service_binding" Name = "vs1"
  395. $enc = [uri]::EscapeDataString($Name)
  396. $result = Invoke-Nitro -Method GET -Resource "${BindResource}/${enc}"
  397. if (-not $result) { return @() }
  398. if ($result.PSObject.Properties[$BindResource]) {
  399. return @($result.$BindResource)
  400. }
  401. return @()
  402. }
  403. # ── Write object config lines ─────────────────────────────────────────────────
  404. function Write-ObjectSection ([string]$Header, [string]$Type, [string[]]$Names, [string]$ExplainText="") {
  405. if (-not $Names -or $Names.Count -eq 0) { return }
  406. $uniqueNames = $Names | Select-Object -Unique
  407. Out-Section $Header
  408. foreach ($name in $uniqueNames) {
  409. $obj = Get-ObjectByName $Type $name
  410. if ($obj) {
  411. Out-Line (Format-NitroObject "add $($Type -replace 'vserver','vServer')" $obj)
  412. } else {
  413. Out-Line "# [not found in cache] $Type $name"
  414. }
  415. }
  416. if ($ExplainText) { Out-Line "# *** $ExplainText" }
  417. Out-Line ""
  418. }
  419. # ── SSL bindings helper ───────────────────────────────────────────────────────
  420. function Get-SSLObjectsForVS ([string]$VSName, [string]$VSType) {
  421. # sslvserver bindings
  422. $sslBindRes = "${VSType}_sslcertkey_binding"
  423. $bindings = Get-BindingsFor $sslBindRes $VSName
  424. $certs = @()
  425. foreach ($b in $bindings) {
  426. if ($b.PSObject.Properties["certkeyname"]) { $certs += $b.certkeyname }
  427. }
  428. # ssl profile
  429. $sslCfg = (Invoke-Nitro -Method GET -Resource "sslvserver/$([uri]::EscapeDataString($VSName))")
  430. $profile = ""
  431. if ($sslCfg -and $sslCfg.PSObject.Properties["sslvserver"]) {
  432. $profile = $sslCfg.sslvserver | Select-Object -First 1 -ExpandProperty sslprofile -ErrorAction SilentlyContinue
  433. }
  434. return @{ Certs = $certs; Profile = $profile }
  435. }
  436. # ─────────────────────────────────────────────────────────────────────────────
  437. # Per-vServer extraction
  438. # ─────────────────────────────────────────────────────────────────────────────
  439. $extracted = @{
  440. lbvservers = [System.Collections.Generic.List[string]]::new()
  441. csvservers = [System.Collections.Generic.List[string]]::new()
  442. vpnvservers = [System.Collections.Generic.List[string]]::new()
  443. authvservers = [System.Collections.Generic.List[string]]::new()
  444. gslbvservers = [System.Collections.Generic.List[string]]::new()
  445. services = [System.Collections.Generic.List[string]]::new()
  446. servicegroups = [System.Collections.Generic.List[string]]::new()
  447. servers = [System.Collections.Generic.List[string]]::new()
  448. monitors = [System.Collections.Generic.List[string]]::new()
  449. sslcerts = [System.Collections.Generic.List[string]]::new()
  450. sslprofiles = [System.Collections.Generic.List[string]]::new()
  451. sslciphers = [System.Collections.Generic.List[string]]::new()
  452. sslpolicies = [System.Collections.Generic.List[string]]::new()
  453. sslactions = [System.Collections.Generic.List[string]]::new()
  454. rewritepolicies = [System.Collections.Generic.List[string]]::new()
  455. rewriteactions = [System.Collections.Generic.List[string]]::new()
  456. rewritepolicylabels = [System.Collections.Generic.List[string]]::new()
  457. responderpolicies = [System.Collections.Generic.List[string]]::new()
  458. responderactions = [System.Collections.Generic.List[string]]::new()
  459. responderpolicylabels = [System.Collections.Generic.List[string]]::new()
  460. cspolicies = [System.Collections.Generic.List[string]]::new()
  461. csactions = [System.Collections.Generic.List[string]]::new()
  462. cspolicylabels = [System.Collections.Generic.List[string]]::new()
  463. appfwpolicies = [System.Collections.Generic.List[string]]::new()
  464. appfwprofiles = [System.Collections.Generic.List[string]]::new()
  465. cmppolicies = [System.Collections.Generic.List[string]]::new()
  466. cachepolicies = [System.Collections.Generic.List[string]]::new()
  467. transformpolicies = [System.Collections.Generic.List[string]]::new()
  468. transformactions = [System.Collections.Generic.List[string]]::new()
  469. transformprofiles = [System.Collections.Generic.List[string]]::new()
  470. ldapactions = [System.Collections.Generic.List[string]]::new()
  471. ldappolicies = [System.Collections.Generic.List[string]]::new()
  472. radiusactions = [System.Collections.Generic.List[string]]::new()
  473. radiuspolicies = [System.Collections.Generic.List[string]]::new()
  474. samlactions = [System.Collections.Generic.List[string]]::new()
  475. samlidppolicies = [System.Collections.Generic.List[string]]::new()
  476. certactions = [System.Collections.Generic.List[string]]::new()
  477. certpolicies = [System.Collections.Generic.List[string]]::new()
  478. tacacsactions = [System.Collections.Generic.List[string]]::new()
  479. tacacspolicies = [System.Collections.Generic.List[string]]::new()
  480. authpolicies = [System.Collections.Generic.List[string]]::new()
  481. authpolicylabels = [System.Collections.Generic.List[string]]::new()
  482. loginschemas = [System.Collections.Generic.List[string]]::new()
  483. loginschemapolicies = [System.Collections.Generic.List[string]]::new()
  484. authnprofiles = [System.Collections.Generic.List[string]]::new()
  485. vpnsessionpolicies = [System.Collections.Generic.List[string]]::new()
  486. vpnsessionactions = [System.Collections.Generic.List[string]]::new()
  487. vpntrafficpolicies = [System.Collections.Generic.List[string]]::new()
  488. vpntrafficactions = [System.Collections.Generic.List[string]]::new()
  489. auditsyslogpolicies = [System.Collections.Generic.List[string]]::new()
  490. auditsyslogactions = [System.Collections.Generic.List[string]]::new()
  491. auditnslogpolicies = [System.Collections.Generic.List[string]]::new()
  492. auditnslogactions = [System.Collections.Generic.List[string]]::new()
  493. authorizationpolicies = [System.Collections.Generic.List[string]]::new()
  494. netprofiles = [System.Collections.Generic.List[string]]::new()
  495. tcpprofiles = [System.Collections.Generic.List[string]]::new()
  496. httpprofiles = [System.Collections.Generic.List[string]]::new()
  497. gslbsites = [System.Collections.Generic.List[string]]::new()
  498. gslbservices = [System.Collections.Generic.List[string]]::new()
  499. doSys = $false
  500. }
  501. function Add-Unique ([string]$Key, [string]$Value) {
  502. if ($Value -and -not $extracted[$Key].Contains($Value)) {
  503. $extracted[$Key].Add($Value)
  504. }
  505. }
  506. # ── Pull bindings for an LB vServer ──────────────────────────────────────────
  507. function Process-LBvServer ([string]$Name) {
  508. Add-Unique "lbvservers" $Name
  509. Write-Host " Processing LB vServer: $Name"
  510. # Service bindings
  511. $svcBinds = Get-BindingsFor "lbvserver_service_binding" $Name
  512. foreach ($b in $svcBinds) {
  513. if ($b.PSObject.Properties["servicename"]) {
  514. $sn = $b.servicename; Add-Unique "services" $sn
  515. Process-Service $sn
  516. }
  517. }
  518. # ServiceGroup bindings
  519. $sgBinds = Get-BindingsFor "lbvserver_servicegroup_binding" $Name
  520. foreach ($b in $sgBinds) {
  521. if ($b.PSObject.Properties["servicegroupname"]) {
  522. $sg = $b.servicegroupname; Add-Unique "servicegroups" $sg
  523. Process-ServiceGroup $sg
  524. }
  525. }
  526. # Policies
  527. $polBinds = Get-BindingsFor "lbvserver_rewritepolicy_binding" $Name; foreach ($b in $polBinds) { if ($b.policyname) { Process-RewritePolicy $b.policyname } }
  528. $polBinds = Get-BindingsFor "lbvserver_responderpolicy_binding" $Name; foreach ($b in $polBinds) { if ($b.policyname) { Process-ResponderPolicy $b.policyname } }
  529. $polBinds = Get-BindingsFor "lbvserver_appfwpolicy_binding" $Name; foreach ($b in $polBinds) { if ($b.policyname) { Process-AppFWPolicy $b.policyname } }
  530. $polBinds = Get-BindingsFor "lbvserver_cmppolicy_binding" $Name; foreach ($b in $polBinds) { if ($b.policyname) { Add-Unique "cmppolicies" $b.policyname } }
  531. $polBinds = Get-BindingsFor "lbvserver_transformpolicy_binding" $Name; foreach ($b in $polBinds) { if ($b.policyname) { Process-TransformPolicy $b.policyname } }
  532. $polBinds = Get-BindingsFor "lbvserver_auditsyslogpolicy_binding" $Name; foreach ($b in $polBinds) { if ($b.policyname) { Add-Unique "auditsyslogpolicies" $b.policyname } }
  533. $polBinds = Get-BindingsFor "lbvserver_sslpolicy_binding" $Name; foreach ($b in $polBinds) { if ($b.policyname) { Process-SSLPolicy $b.policyname } }
  534. # SSL
  535. $ssl = Get-SSLObjectsForVS $Name "lbvserver"
  536. foreach ($c in $ssl.Certs) { Add-Unique "sslcerts" $c }
  537. if ($ssl.Profile) { Add-Unique "sslprofiles" $ssl.Profile }
  538. # Profiles
  539. $obj = Get-ObjectByName "lbvserver" $Name
  540. if ($obj) {
  541. if ($obj.PSObject.Properties["netprofile"] -and $obj.netprofile) { Add-Unique "netprofiles" $obj.netprofile }
  542. if ($obj.PSObject.Properties["tcpprofilename"] -and $obj.tcpprofilename) { Add-Unique "tcpprofiles" $obj.tcpprofilename }
  543. if ($obj.PSObject.Properties["httpprofilename"] -and $obj.httpprofilename) { Add-Unique "httpprofiles" $obj.httpprofilename }
  544. # auth
  545. if ($obj.PSObject.Properties["authnvsname"] -and $obj.authnvsname) { Process-AuthVServer $obj.authnvsname }
  546. }
  547. }
  548. function Process-Service ([string]$Name) {
  549. $obj = Get-ObjectByName "service" $Name
  550. if (-not $obj) { return }
  551. if ($obj.PSObject.Properties["servername"] -and $obj.servername) { Add-Unique "servers" $obj.servername }
  552. # Monitors
  553. $mBinds = Get-BindingsFor "service_lbmonitor_binding" $Name
  554. foreach ($b in $mBinds) { if ($b.PSObject.Properties["monitor_name"]) { Add-Unique "monitors" $b.monitor_name } }
  555. if ($obj.PSObject.Properties["sslprofile"] -and $obj.sslprofile) { Add-Unique "sslprofiles" $obj.sslprofile }
  556. }
  557. function Process-ServiceGroup ([string]$Name) {
  558. $obj = Get-ObjectByName "servicegroup" $Name
  559. if (-not $obj) { return }
  560. # Members
  561. $mems = Get-BindingsFor "servicegroup_servicegroupmember_binding" $Name
  562. foreach ($m in $mems) {
  563. if ($m.PSObject.Properties["servername"] -and $m.servername) { Add-Unique "servers" $m.servername }
  564. }
  565. # Monitors
  566. $mBinds = Get-BindingsFor "servicegroup_lbmonitor_binding" $Name
  567. foreach ($b in $mBinds) { if ($b.PSObject.Properties["monitor_name"]) { Add-Unique "monitors" $b.monitor_name } }
  568. if ($obj.PSObject.Properties["sslprofile"] -and $obj.sslprofile) { Add-Unique "sslprofiles" $obj.sslprofile }
  569. }
  570. function Process-CSvServer ([string]$Name) {
  571. Add-Unique "csvservers" $Name
  572. Write-Host " Processing CS vServer: $Name"
  573. $polBinds = Get-BindingsFor "csvserver_cspolicy_binding" $Name
  574. foreach ($b in $polBinds) {
  575. if ($b.PSObject.Properties["policyname"] -and $b.policyname) {
  576. Add-Unique "cspolicies" $b.policyname
  577. # Get action -> target LB
  578. $pol = Get-ObjectByName "cspolicy" $b.policyname
  579. if ($pol -and $pol.PSObject.Properties["action"] -and $pol.action) {
  580. Add-Unique "csactions" $pol.action
  581. $act = Get-ObjectByName "csaction" $pol.action
  582. if ($act) {
  583. if ($act.PSObject.Properties["targetlbvserver"] -and $act.targetlbvserver) { Process-LBvServer $act.targetlbvserver }
  584. if ($act.PSObject.Properties["targetvserver"] -and $act.targetvserver) {
  585. # Could be vpn or auth
  586. if ($cache["vpnvserver"] | Where-Object { $_.name -eq $act.targetvserver }) { Process-VPNvServer $act.targetvserver }
  587. if ($cache["authenticationvserver"] | Where-Object { $_.name -eq $act.targetvserver }) { Process-AuthVServer $act.targetvserver }
  588. }
  589. }
  590. }
  591. if ($b.PSObject.Properties["targetlbvserver"] -and $b.targetlbvserver) { Process-LBvServer $b.targetlbvserver }
  592. }
  593. }
  594. # Default LB
  595. $obj = Get-ObjectByName "csvserver" $Name
  596. if ($obj -and $obj.PSObject.Properties["lbvserver"] -and $obj.lbvserver) { Process-LBvServer $obj.lbvserver }
  597. # Policies on the CS vServer itself
  598. $polBinds = Get-BindingsFor "csvserver_rewritepolicy_binding" $Name; foreach ($b in $polBinds) { if ($b.policyname) { Process-RewritePolicy $b.policyname } }
  599. $polBinds = Get-BindingsFor "csvserver_responderpolicy_binding" $Name; foreach ($b in $polBinds) { if ($b.policyname) { Process-ResponderPolicy $b.policyname } }
  600. $polBinds = Get-BindingsFor "csvserver_auditsyslogpolicy_binding" $Name; foreach ($b in $polBinds) { if ($b.policyname) { Add-Unique "auditsyslogpolicies" $b.policyname } }
  601. $polBinds = Get-BindingsFor "csvserver_sslpolicy_binding" $Name; foreach ($b in $polBinds) { if ($b.policyname) { Process-SSLPolicy $b.policyname } }
  602. $ssl = Get-SSLObjectsForVS $Name "csvserver"
  603. foreach ($c in $ssl.Certs) { Add-Unique "sslcerts" $c }
  604. if ($ssl.Profile) { Add-Unique "sslprofiles" $ssl.Profile }
  605. if ($obj -and $obj.PSObject.Properties["netprofile"] -and $obj.netprofile) { Add-Unique "netprofiles" $obj.netprofile }
  606. }
  607. function Process-VPNvServer ([string]$Name) {
  608. Add-Unique "vpnvservers" $Name
  609. Write-Host " Processing VPN/Gateway vServer: $Name"
  610. $polBinds = Get-BindingsFor "vpnvserver_vpnsessionpolicy_binding" $Name
  611. foreach ($b in $polBinds) { if ($b.PSObject.Properties["policy"] -and $b.policy) { Process-VPNSessionPolicy $b.policy } }
  612. $polBinds = Get-BindingsFor "vpnvserver_authenticationldappolicy_binding" $Name
  613. foreach ($b in $polBinds) { if ($b.PSObject.Properties["policy"] -and $b.policy) { Process-LDAPPolicy $b.policy } }
  614. $polBinds = Get-BindingsFor "vpnvserver_authenticationradiuspolicy_binding" $Name
  615. foreach ($b in $polBinds) { if ($b.PSObject.Properties["policy"] -and $b.policy) { Process-RADIUSPolicy $b.policy } }
  616. $polBinds = Get-BindingsFor "vpnvserver_authenticationsamlpolicy_binding" $Name
  617. foreach ($b in $polBinds) { if ($b.PSObject.Properties["policy"] -and $b.policy) { Add-Unique "samlidppolicies" $b.policy } }
  618. $polBinds = Get-BindingsFor "vpnvserver_responderpolicy_binding" $Name
  619. foreach ($b in $polBinds) { if ($b.PSObject.Properties["policy"] -and $b.policy) { Process-ResponderPolicy $b.policy } }
  620. $polBinds = Get-BindingsFor "vpnvserver_rewritepolicy_binding" $Name
  621. foreach ($b in $polBinds) { if ($b.PSObject.Properties["policy"] -and $b.policy) { Process-RewritePolicy $b.policy } }
  622. $polBinds = Get-BindingsFor "vpnvserver_authenticationpolicy_binding" $Name
  623. foreach ($b in $polBinds) {
  624. if ($b.PSObject.Properties["policy"] -and $b.policy) { Process-AuthPolicy $b.policy }
  625. if ($b.PSObject.Properties["nextfactor"] -and $b.nextfactor) { Process-AuthPolicyLabel $b.nextfactor }
  626. }
  627. $ssl = Get-SSLObjectsForVS $Name "vpnvserver"
  628. foreach ($c in $ssl.Certs) { Add-Unique "sslcerts" $c }
  629. if ($ssl.Profile) { Add-Unique "sslprofiles" $ssl.Profile }
  630. $obj = Get-ObjectByName "vpnvserver" $Name
  631. if ($obj -and $obj.PSObject.Properties["authnprofile"] -and $obj.authnprofile) { Process-AuthnProfile $obj.authnprofile }
  632. }
  633. function Process-AuthVServer ([string]$Name) {
  634. Add-Unique "authvservers" $Name
  635. Write-Host " Processing AAA/Auth vServer: $Name"
  636. $polBinds = Get-BindingsFor "authenticationvserver_authenticationldappolicy_binding" $Name
  637. foreach ($b in $polBinds) { if ($b.PSObject.Properties["policy"] -and $b.policy) { Process-LDAPPolicy $b.policy } }
  638. $polBinds = Get-BindingsFor "authenticationvserver_authenticationradiuspolicy_binding" $Name
  639. foreach ($b in $polBinds) { if ($b.PSObject.Properties["policy"] -and $b.policy) { Process-RADIUSPolicy $b.policy } }
  640. $polBinds = Get-BindingsFor "authenticationvserver_authenticationpolicy_binding" $Name
  641. foreach ($b in $polBinds) {
  642. if ($b.PSObject.Properties["policy"] -and $b.policy) { Process-AuthPolicy $b.policy }
  643. if ($b.PSObject.Properties["nextfactor"] -and $b.nextfactor) { Process-AuthPolicyLabel $b.nextfactor }
  644. }
  645. $polBinds = Get-BindingsFor "authenticationvserver_authenticationloginschemapolicy_binding" $Name
  646. foreach ($b in $polBinds) { if ($b.PSObject.Properties["policy"] -and $b.policy) { Process-LoginSchemaPolicy $b.policy } }
  647. $ssl = Get-SSLObjectsForVS $Name "authenticationvserver"
  648. foreach ($c in $ssl.Certs) { Add-Unique "sslcerts" $c }
  649. if ($ssl.Profile) { Add-Unique "sslprofiles" $ssl.Profile }
  650. }
  651. function Process-GSLBvServer ([string]$Name) {
  652. Add-Unique "gslbvservers" $Name
  653. Write-Host " Processing GSLB vServer: $Name"
  654. $svcBinds = Get-BindingsFor "gslbvserver_gslbservice_binding" $Name
  655. foreach ($b in $svcBinds) {
  656. if ($b.PSObject.Properties["servicename"] -and $b.servicename) {
  657. Add-Unique "gslbservices" $b.servicename
  658. $svc = Get-ObjectByName "gslbservice" $b.servicename
  659. if ($svc -and $svc.PSObject.Properties["sitename"] -and $svc.sitename) { Add-Unique "gslbsites" $svc.sitename }
  660. }
  661. }
  662. $ssl = Get-SSLObjectsForVS $Name "gslbvserver"
  663. foreach ($c in $ssl.Certs) { Add-Unique "sslcerts" $c }
  664. if ($ssl.Profile) { Add-Unique "sslprofiles" $ssl.Profile }
  665. }
  666. function Process-RewritePolicy ([string]$Name) {
  667. Add-Unique "rewritepolicies" $Name
  668. $obj = Get-ObjectByName "rewritepolicy" $Name
  669. if ($obj -and $obj.PSObject.Properties["action"] -and $obj.action) { Add-Unique "rewriteactions" $obj.action }
  670. }
  671. function Process-ResponderPolicy ([string]$Name) {
  672. Add-Unique "responderpolicies" $Name
  673. $obj = Get-ObjectByName "responderpolicy" $Name
  674. if ($obj -and $obj.PSObject.Properties["action"] -and $obj.action) { Add-Unique "responderactions" $obj.action }
  675. }
  676. function Process-AppFWPolicy ([string]$Name) {
  677. Add-Unique "appfwpolicies" $Name
  678. $obj = Get-ObjectByName "appfwpolicy" $Name
  679. if ($obj -and $obj.PSObject.Properties["profilename"] -and $obj.profilename) { Add-Unique "appfwprofiles" $obj.profilename }
  680. }
  681. function Process-TransformPolicy ([string]$Name) {
  682. Add-Unique "transformpolicies" $Name
  683. $obj = Get-ObjectByName "transformpolicy" $Name
  684. if ($obj -and $obj.PSObject.Properties["action"] -and $obj.action) {
  685. Add-Unique "transformactions" $obj.action
  686. $act = Get-ObjectByName "transformaction" $obj.action
  687. if ($act -and $act.PSObject.Properties["profilename"] -and $act.profilename) { Add-Unique "transformprofiles" $act.profilename }
  688. }
  689. }
  690. function Process-SSLPolicy ([string]$Name) {
  691. Add-Unique "sslpolicies" $Name
  692. $obj = Get-ObjectByName "sslpolicy" $Name
  693. if ($obj -and $obj.PSObject.Properties["action"] -and $obj.action) { Add-Unique "sslactions" $obj.action }
  694. }
  695. function Process-LDAPPolicy ([string]$Name) {
  696. Add-Unique "ldappolicies" $Name
  697. $obj = Get-ObjectByName "authenticationldappolicy" $Name
  698. if ($obj -and $obj.PSObject.Properties["action"] -and $obj.action) { Add-Unique "ldapactions" $obj.action }
  699. }
  700. function Process-RADIUSPolicy ([string]$Name) {
  701. Add-Unique "radiuspolicies" $Name
  702. $obj = Get-ObjectByName "authenticationradiuspolicy" $Name
  703. if ($obj -and $obj.PSObject.Properties["action"] -and $obj.action) { Add-Unique "radiusactions" $obj.action }
  704. }
  705. function Process-AuthPolicy ([string]$Name) {
  706. Add-Unique "authpolicies" $Name
  707. $obj = Get-ObjectByName "authenticationpolicy" $Name
  708. if ($obj -and $obj.PSObject.Properties["action"] -and $obj.action) {
  709. $action = $obj.action
  710. # Detect action type by searching sub-caches
  711. foreach ($atype in @("authenticationldapaction","authenticationradiusaction",
  712. "authenticationsamlaction","authenticationcertaction",
  713. "authenticationtacacsaction")) {
  714. if ($cache[$atype] | Where-Object { $_.name -eq $action }) {
  715. switch ($atype) {
  716. "authenticationldapaction" { Add-Unique "ldapactions" $action }
  717. "authenticationradiusaction" { Add-Unique "radiusactions" $action }
  718. "authenticationsamlaction" { Add-Unique "samlactions" $action }
  719. "authenticationcertaction" { Add-Unique "certactions" $action }
  720. "authenticationtacacsaction" { Add-Unique "tacacsactions" $action }
  721. }
  722. }
  723. }
  724. }
  725. }
  726. function Process-AuthPolicyLabel ([string]$Name) {
  727. if ($extracted["authpolicylabels"].Contains($Name)) { return }
  728. Add-Unique "authpolicylabels" $Name
  729. Write-Host " Processing Auth Policy Label: $Name"
  730. $binds = Get-BindingsFor "authenticationpolicylabel_authenticationpolicy_binding" $Name
  731. foreach ($b in $binds) {
  732. if ($b.PSObject.Properties["policyname"] -and $b.policyname) { Process-AuthPolicy $b.policyname }
  733. if ($b.PSObject.Properties["nextfactor"] -and $b.nextfactor) { Process-AuthPolicyLabel $b.nextfactor }
  734. }
  735. }
  736. function Process-LoginSchemaPolicy ([string]$Name) {
  737. Add-Unique "loginschemapolicies" $Name
  738. $obj = Get-ObjectByName "authenticationloginschemapolicy" $Name
  739. if ($obj -and $obj.PSObject.Properties["action"] -and $obj.action) { Add-Unique "loginschemas" $obj.action }
  740. }
  741. function Process-VPNSessionPolicy ([string]$Name) {
  742. Add-Unique "vpnsessionpolicies" $Name
  743. $obj = Get-ObjectByName "vpnsessionpolicy" $Name
  744. if ($obj -and $obj.PSObject.Properties["action"] -and $obj.action) { Add-Unique "vpnsessionactions" $obj.action }
  745. }
  746. function Process-AuthnProfile ([string]$Name) {
  747. Add-Unique "authnprofiles" $Name
  748. $obj = Get-ObjectByName "authenticationauthnprofile" $Name
  749. if ($obj -and $obj.PSObject.Properties["authnvsname"] -and $obj.authnvsname) { Process-AuthVServer $obj.authnvsname }
  750. }
  751. # ── Dispatch selected vServers ────────────────────────────────────────────────
  752. Write-Host "`nExtracting configuration for selected vServers..." -ForegroundColor Yellow
  753. foreach ($sel in $selectedVServers) {
  754. switch ($sel.Type) {
  755. "lbvserver" { Process-LBvServer $sel.Name }
  756. "csvserver" { Process-CSvServer $sel.Name }
  757. "vpnvserver" { Process-VPNvServer $sel.Name }
  758. "authenticationvserver" { Process-AuthVServer $sel.Name }
  759. "gslbvserver" { Process-GSLBvServer $sel.Name }
  760. "sys" { $extracted["doSys"] = $true }
  761. }
  762. }
  763. # ─────────────────────────────────────────────────────────────────────────────
  764. # Build Output
  765. # ─────────────────────────────────────────────────────────────────────────────
  766. Write-Host "`nBuilding output..." -ForegroundColor Yellow
  767. $vsNames = ($selectedVServers | Where-Object { $_.Type -ne "sys" } | ForEach-Object { $_.Name }) -join ", "
  768. Out-Line "# NetScaler NITRO Config Extract"
  769. Out-Line "# NSIP : $nsip"
  770. Out-Line "# Extracted : $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
  771. Out-Line "# vServers : $vsNames"
  772. Out-Line ""
  773. # ── Helper to emit all objects of a type ─────────────────────────────────────
  774. function Emit-Objects ([string]$SectionTitle, [string]$CacheType, [string[]]$NameList, [string]$Cmd, [string]$Note="") {
  775. if (-not $NameList -or $NameList.Count -eq 0) { return }
  776. Out-Section $SectionTitle
  777. if ($Note) { Out-Line "# *** $Note" }
  778. foreach ($n in ($NameList | Select-Object -Unique)) {
  779. $obj = Get-ObjectByName $CacheType $n
  780. if ($obj) {
  781. Out-Line (Format-NitroObject $Cmd $obj)
  782. } else {
  783. Out-Line "# [not found in live config] $CacheType $n"
  784. }
  785. }
  786. Out-Line ""
  787. }
  788. # ── System Settings ───────────────────────────────────────────────────────────
  789. if ($extracted["doSys"]) {
  790. Out-Section "System Settings"
  791. $sysParam = Invoke-Nitro -Method GET -Resource "systemparameter"
  792. if ($sysParam -and $sysParam.PSObject.Properties["systemparameter"]) {
  793. Out-Line (Format-NitroObject "set system parameter" $sysParam.systemparameter)
  794. }
  795. Out-Section "Enabled Features"
  796. $features = Invoke-Nitro -Method GET -Resource "nsfeature"
  797. if ($features -and $features.PSObject.Properties["nsfeature"]) {
  798. $f = $features.nsfeature
  799. $enabled = $f.PSObject.Properties | Where-Object { $_.Value -eq "YES" -or $_.Value -eq $true } | ForEach-Object { $_.Name }
  800. Out-Line ("enable ns feature " + ($enabled -join " "))
  801. }
  802. Out-Section "Enabled Modes"
  803. $modes = Invoke-Nitro -Method GET -Resource "nsmode"
  804. if ($modes -and $modes.PSObject.Properties["nsmode"]) {
  805. $m = $modes.nsmode
  806. $enabledM = $m.PSObject.Properties | Where-Object { $_.Value -eq "YES" -or $_.Value -eq $true } | ForEach-Object { $_.Name }
  807. Out-Line ("enable ns mode " + ($enabledM -join " "))
  808. }
  809. Out-Section "NSIP Addresses"
  810. foreach ($ip in $cache["nsip"]) { Out-Line (Format-NitroObject "add ns ip" $ip) }
  811. Out-Section "VLANs"
  812. foreach ($v in $cache["vlan"]) { Out-Line (Format-NitroObject "add vlan" $v) }
  813. Out-Section "Routes"
  814. foreach ($r in $cache["route"]) { Out-Line (Format-NitroObject "add route" $r) }
  815. Out-Section "HA Nodes"
  816. foreach ($h in $cache["hanode"]) { Out-Line (Format-NitroObject "add ha node" $h) }
  817. Out-Section "System Users"
  818. foreach ($u in $cache["systemuser"]) { Out-Line (Format-NitroObject "add system user" $u) }
  819. Out-Section "System Groups"
  820. foreach ($g in $cache["systemgroup"]) { Out-Line (Format-NitroObject "add system group" $g) }
  821. Out-Section "ACLs"
  822. foreach ($a in $cache["nsacl"]) { Out-Line (Format-NitroObject "add ns acl" $a) }
  823. Out-Line ""
  824. }
  825. # ── SSL ───────────────────────────────────────────────────────────────────────
  826. Emit-Objects "SSL Certificates" "sslcertkey" $extracted["sslcerts"] "add ssl certKey" "Certificate files must be present in /nsconfig/ssl"
  827. Emit-Objects "SSL Profiles" "sslprofile" $extracted["sslprofiles"] "add ssl profile"
  828. Emit-Objects "SSL Policies" "sslpolicy" $extracted["sslpolicies"] "add ssl policy"
  829. Emit-Objects "SSL Actions" "sslaction" $extracted["sslactions"] "add ssl action"
  830. # ── Networking / Profiles ─────────────────────────────────────────────────────
  831. Emit-Objects "Net Profiles" "netprofile" $extracted["netprofiles"] "add netprofile"
  832. Emit-Objects "TCP Profiles" "nstcpprofile" $extracted["tcpprofiles"] "add ns tcpProfile"
  833. Emit-Objects "HTTP Profiles" "nshttpprofile" $extracted["httpprofiles"] "add ns httpProfile"
  834. # ── Servers, Services, Monitors ──────────────────────────────────────────────
  835. Emit-Objects "Servers" "server" $extracted["servers"] "add server"
  836. Emit-Objects "Monitors" "lbmonitor" $extracted["monitors"] "add lb monitor"
  837. Emit-Objects "Services" "service" $extracted["services"] "add service"
  838. Emit-Objects "Service Groups" "servicegroup" $extracted["servicegroups"] "add serviceGroup"
  839. # ── Rewrite / Responder ───────────────────────────────────────────────────────
  840. Emit-Objects "Rewrite Actions" "rewriteaction" $extracted["rewriteactions"] "add rewrite action"
  841. Emit-Objects "Rewrite Policies" "rewritepolicy" $extracted["rewritepolicies"] "add rewrite policy"
  842. Emit-Objects "Responder Actions" "responderaction" $extracted["responderactions"] "add responder action"
  843. Emit-Objects "Responder Policies" "responderpolicy" $extracted["responderpolicies"] "add responder policy"
  844. # ── AppFW ─────────────────────────────────────────────────────────────────────
  845. Emit-Objects "AppFW Profiles" "appfwprofile" $extracted["appfwprofiles"] "add appfw profile" "Manually export/import AppFW signatures and XML schema objects"
  846. Emit-Objects "AppFW Policies" "appfwpolicy" $extracted["appfwpolicies"] "add appfw policy"
  847. # ── Compression ───────────────────────────────────────────────────────────────
  848. Emit-Objects "CMP Policies" "cmppolicy" $extracted["cmppolicies"] "add cmp policy"
  849. # ── Transform ─────────────────────────────────────────────────────────────────
  850. Emit-Objects "Transform Profiles" "transformprofile" $extracted["transformprofiles"] "add transform profile"
  851. Emit-Objects "Transform Actions" "transformaction" $extracted["transformactions"] "add transform action"
  852. Emit-Objects "Transform Policies" "transformpolicy" $extracted["transformpolicies"] "add transform policy"
  853. # ── CS ────────────────────────────────────────────────────────────────────────
  854. Emit-Objects "CS Actions" "csaction" $extracted["csactions"] "add cs action"
  855. Emit-Objects "CS Policies" "cspolicy" $extracted["cspolicies"] "add cs policy"
  856. # ── AAA / Authentication ──────────────────────────────────────────────────────
  857. Emit-Objects "LDAP Actions" "authenticationldapaction" $extracted["ldapactions"] "add authentication ldapAction" "LDAP CA certs are in /nsconfig/truststore"
  858. Emit-Objects "LDAP Policies" "authenticationldappolicy" $extracted["ldappolicies"] "add authentication ldapPolicy"
  859. Emit-Objects "RADIUS Actions" "authenticationradiusaction" $extracted["radiusactions"] "add authentication radiusAction"
  860. Emit-Objects "RADIUS Policies" "authenticationradiuspolicy" $extracted["radiuspolicies"] "add authentication radiusPolicy"
  861. Emit-Objects "SAML Actions" "authenticationsamlaction" $extracted["samlactions"] "add authentication samlAction"
  862. Emit-Objects "Cert Actions" "authenticationcertaction" $extracted["certactions"] "add authentication certAction"
  863. Emit-Objects "TACACS Actions" "authenticationtacacsaction" $extracted["tacacsactions"] "add authentication tacacsAction"
  864. Emit-Objects "TACACS Policies" "authenticationtacacspolicy" $extracted["tacacspolicies"] "add authentication tacacsPolicy"
  865. Emit-Objects "Adv Auth Policies" "authenticationpolicy" $extracted["authpolicies"] "add authentication Policy"
  866. Emit-Objects "Auth Policy Labels" "authenticationpolicylabel" $extracted["authpolicylabels"] "add authentication policylabel"
  867. Emit-Objects "Login Schemas" "authenticationloginschema" $extracted["loginschemas"] "add authentication loginSchema"
  868. Emit-Objects "Login Schema Policies" "authenticationloginschemapolicy" $extracted["loginschemapolicies"] "add authentication loginSchemaPolicy"
  869. Emit-Objects "Authentication Profiles" "authenticationauthnprofile" $extracted["authnprofiles"] "add authentication authnProfile"
  870. # ── VPN Session ───────────────────────────────────────────────────────────────
  871. Emit-Objects "VPN Session Actions" "vpnsessionaction" $extracted["vpnsessionactions"] "add vpn sessionAction"
  872. Emit-Objects "VPN Session Policies" "vpnsessionpolicy" $extracted["vpnsessionpolicies"] "add vpn sessionPolicy"
  873. Emit-Objects "VPN Traffic Actions" "vpntrafficaction" $extracted["vpntrafficactions"] "add vpn trafficAction"
  874. Emit-Objects "VPN Traffic Policies" "vpntrafficpolicy" $extracted["vpntrafficpolicies"] "add vpn trafficPolicy"
  875. # ── Audit Syslog ──────────────────────────────────────────────────────────────
  876. Emit-Objects "Audit Syslog Actions" "auditsyslogaction" $extracted["auditsyslogactions"] "add audit syslogAction"
  877. Emit-Objects "Audit Syslog Policies" "auditsyslogpolicy" $extracted["auditsyslogpolicies"] "add audit syslogPolicy"
  878. Emit-Objects "Audit NSLog Actions" "auditnslogaction" $extracted["auditnslogactions"] "add audit nslogAction"
  879. Emit-Objects "Audit NSLog Policies" "auditnslogpolicy" $extracted["auditnslogpolicies"] "add audit nslogPolicy"
  880. # ── Authorization ─────────────────────────────────────────────────────────────
  881. Emit-Objects "Authorization Policies" "authorizationpolicy" $extracted["authorizationpolicies"] "add authorization policy"
  882. # ── GSLB ─────────────────────────────────────────────────────────────────────
  883. Emit-Objects "GSLB Sites" "gslbsite" $extracted["gslbsites"] "add gslb site"
  884. Emit-Objects "GSLB Services" "gslbservice" $extracted["gslbservices"] "add gslb service"
  885. # ── Authentication vServers (AAA) ─────────────────────────────────────────────
  886. Emit-Objects "Authentication Virtual Servers" "authenticationvserver" $extracted["authvservers"] "add authentication vServer"
  887. # ── vServers ──────────────────────────────────────────────────────────────────
  888. Emit-Objects "Load Balancing Virtual Servers" "lbvserver" $extracted["lbvservers"] "add lb vServer"
  889. Emit-Objects "Content Switching Virtual Servers" "csvserver" $extracted["csvservers"] "add cs vServer"
  890. Emit-Objects "Citrix Gateway Virtual Servers" "vpnvserver" $extracted["vpnvservers"] "add vpn vServer"
  891. Emit-Objects "GSLB Virtual Servers" "gslbvserver" $extracted["gslbvservers"] "add gslb vServer"
  892. # ── vServer bindings (SSL / Policies) ────────────────────────────────────────
  893. Out-Section "SSL Virtual Server Bindings"
  894. function Emit-SSLvServerBindings ([string]$VSType, [System.Collections.Generic.List[string]]$VSNames) {
  895. foreach ($vsName in ($VSNames | Select-Object -Unique)) {
  896. Out-Line "# --- SSL bindings for $VSType $vsName ---"
  897. # Cert bindings
  898. $cBinds = Get-BindingsFor "${VSType}_sslcertkey_binding" $vsName
  899. foreach ($b in $cBinds) {
  900. if ($b.PSObject.Properties["certkeyname"] -and $b.certkeyname) {
  901. $isCaCert = if ($b.PSObject.Properties["ca"] -and $b.ca) { " -CA" } else { "" }
  902. Out-Line "bind ssl $VSType $vsName -certkeyName $($b.certkeyname)$isCaCert"
  903. }
  904. }
  905. # Cipher bindings
  906. $cipBinds = Get-BindingsFor "${VSType}_sslciphersuite_binding" $vsName
  907. if ($cipBinds.Count -gt 0) {
  908. Out-Line "unbind ssl $VSType $vsName -cipherName DEFAULT"
  909. foreach ($b in $cipBinds) {
  910. if ($b.PSObject.Properties["ciphername"] -and $b.ciphername) {
  911. Out-Line "bind ssl $VSType $vsName -cipherName $($b.ciphername)"
  912. }
  913. }
  914. }
  915. # SSL profile
  916. $sslCfg = Invoke-Nitro -Method GET -Resource "sslvserver/$([uri]::EscapeDataString($vsName))"
  917. if ($sslCfg -and $sslCfg.PSObject.Properties["sslvserver"]) {
  918. $s = $sslCfg.sslvserver | Select-Object -First 1
  919. if ($s -and $s.PSObject.Properties["sslprofile"] -and $s.sslprofile) {
  920. Out-Line "set ssl $VSType $vsName -sslProfile $($s.sslprofile)"
  921. }
  922. }
  923. Out-Line ""
  924. }
  925. }
  926. Emit-SSLvServerBindings "vserver" $extracted["lbvservers"]
  927. Emit-SSLvServerBindings "vserver" $extracted["csvservers"]
  928. Emit-SSLvServerBindings "vserver" $extracted["vpnvservers"]
  929. Emit-SSLvServerBindings "vserver" $extracted["authvservers"]
  930. # ── Summary ───────────────────────────────────────────────────────────────────
  931. Out-Section "Extraction Summary"
  932. Out-Line "# LB vServers : $($extracted['lbvservers'].Count)"
  933. Out-Line "# CS vServers : $($extracted['csvservers'].Count)"
  934. Out-Line "# VPN vServers : $($extracted['vpnvservers'].Count)"
  935. Out-Line "# Auth vServers : $($extracted['authvservers'].Count)"
  936. Out-Line "# GSLB vServers : $($extracted['gslbvservers'].Count)"
  937. Out-Line "# Services : $($extracted['services'].Count)"
  938. Out-Line "# Service Groups : $($extracted['servicegroups'].Count)"
  939. Out-Line "# Servers : $($extracted['servers'].Count)"
  940. Out-Line "# Monitors : $($extracted['monitors'].Count)"
  941. Out-Line "# SSL Certs : $($extracted['sslcerts'].Count)"
  942. Out-Line "# SSL Profiles : $($extracted['sslprofiles'].Count)"
  943. Out-Line "# Rewrite Policies : $($extracted['rewritepolicies'].Count)"
  944. Out-Line "# Responder Policies : $($extracted['responderpolicies'].Count)"
  945. Out-Line "# AppFW Policies : $($extracted['appfwpolicies'].Count)"
  946. Out-Line "# Auth Policies (Adv) : $($extracted['authpolicies'].Count)"
  947. Out-Line "# LDAP Actions : $($extracted['ldapactions'].Count)"
  948. Out-Line "# RADIUS Actions : $($extracted['radiusactions'].Count)"
  949. Out-Line "# VPN Session Policies : $($extracted['vpnsessionpolicies'].Count)"
  950. # ─────────────────────────────────────────────────────────────────────────────
  951. # Write output
  952. # ─────────────────────────────────────────────────────────────────────────────
  953. if ($outputFile -and $outputFile -ne "screen") {
  954. Write-Output-File $outputFile
  955. # Try to open in a text editor - resolve full path automatically
  956. $editorPath = $null
  957. # 1. Check if the param value is already a valid full path or on PATH
  958. if ($textEditor) {
  959. if (Test-Path $textEditor -PathType Leaf) {
  960. $editorPath = $textEditor
  961. } elseif (Get-Command $textEditor -ErrorAction SilentlyContinue) {
  962. $editorPath = $textEditor
  963. }
  964. }
  965. # 2. Auto-detect Notepad++ in common install locations
  966. if (-not $editorPath) {
  967. $nppPaths = @(
  968. "$env:ProgramFiles\Notepad++\notepad++.exe",
  969. "${env:ProgramFiles(x86)}\Notepad++\notepad++.exe",
  970. "$env:LOCALAPPDATA\Programs\Notepad++\notepad++.exe"
  971. )
  972. foreach ($p in $nppPaths) {
  973. if (Test-Path $p -PathType Leaf) { $editorPath = $p; break }
  974. }
  975. }
  976. # 3. Try VS Code
  977. if (-not $editorPath) {
  978. foreach ($code in @("code","code.cmd")) {
  979. if (Get-Command $code -ErrorAction SilentlyContinue) { $editorPath = $code; break }
  980. }
  981. }
  982. # 4. Fall back to built-in Notepad (always present on Windows)
  983. if (-not $editorPath -and -not $IsMacOS) {
  984. $editorPath = "notepad.exe"
  985. }
  986. if ($editorPath) {
  987. Write-Host "Opening output file with: $editorPath" -ForegroundColor Cyan
  988. Start-Process -FilePath $editorPath -ArgumentList "`"$outputFile`""
  989. } else {
  990. Write-Host "No text editor found. Output saved to: $outputFile" -ForegroundColor Yellow
  991. }
  992. } else {
  993. $script:outputLines | ForEach-Object { Write-Output $_ }
  994. }
  995. # ─────────────────────────────────────────────────────────────────────────────
  996. # Logout
  997. # ─────────────────────────────────────────────────────────────────────────────
  998. $logoutBody = @{ logout = @{} }
  999. try {
  1000. Invoke-Nitro -Method POST -Resource "logout" -Body $logoutBody | Out-Null
  1001. Write-Host "✔ Logged out of NetScaler." -ForegroundColor Green
  1002. } catch {
  1003. # non-fatal
  1004. }
  1005. Write-Host "`nDone." -ForegroundColor Cyan