I have spent a lot of my time over the last year going from a total novice with programming to actually being comfortable with Powershell. As someone who has come from a CLI networking background I did spent a lot of time inside devices but never had the chance to automate my workflows. Nor was there a need in a previous life.
I feel I have contributed a little bit to PowerNSX. I have made some useful scripts for myself. I wrote a fair smattering of documentation over on the [website](http://powernsx.github.io) and tested the be-jeebus out of it on Windows. This test shifted to the macOS distribution. My thinking and approach to scripts changes as I coded. My first forays into scripts were blind, ill-thought out, with a focus just to GSD!
SDDC-Lockdown, the deployment of a security model for the entire VMware stack in a provider/consumer model, was where I had an awakening. The premise of SDDC Lockdown was the following:
* Develop a provider / consumer model for firewall policy
* Allow lifecycling of any component without a change of security model
* Ensure zero dependency on infrastructure
* Fully automated deployment with administrative “assignment” of workloads
The goal for this was to be automated completely from user input of content to deployment of required objects. In addition factors such as dependencies, unknown inputs, order of operations, and PowerNSX not covering everything just yet all made it a great thinking exercise! Between coding all the things then actually trying to use it I realised that my first iteration didn’t work. I coded, deleted, refactored, coded some more, deployed, destroyed, coded, and refactored. This little project I realised could become quite modular and that changed the way I approached the deployment. Oh! Do not forget to make your code portable and reusable too!
Reflecting on it made me realise there is an art to coding that is more than just knowledge.
Slowly I have moved from creating crummy little scripts, opening PR’s and pestering Nick to fix to opening PR’s, **thinking* about the issue, and developing new cmdlets. Now it is not just Get and Delete commands. These are somewhat easy once you get the hang of it. What really started to make me think (with input from good mentors *cough* Nick again) were the Set commands.
Here is my first attempt at making a *set* cmdlet. These generally are trickier than get and remove. This involves using a basic cmdlet to retrieve the data from the NSX Manager. `Get-NsxFirewallThreshold` is the command in question.
Below is the first attempt at creating a *set* command.
``` function Set-NsxFirewallThreshold { <# .SYNOPSIS Sets the Distributed Firewall thresholds for CPU, Memory and Connections per Second .DESCRIPTION The firewall module generates system events when the memory and CPU usage crosses these thresholds. This command will set the threshold configuration for the distributed firewall .EXAMPLE PS /> Set-NsxFirewallThreshold -Cpu 70 -Memory 70 -ConnectionsPerSecond 35000 CPU Memory ConnectionsPerSecond --- ------ -------------------- 70 70 35000 #> param ( [Parameter (Mandatory=$true)] [ValidateRange(1,100)] [int]$Memory, [Parameter (Mandatory=$true)] [ValidateRange(1,100)] [int]$Cpu, [Parameter (Mandatory=$true)] [ValidateRange(1,500000)] [int]$ConnectionsPerSecond, [Parameter (Mandatory=$False)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection=$defaultNSXConnection ) begin { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("eventThresholds") $xmlDoc.appendChild($xmlRoot) | out-null #Create an Element and append it to the root [System.XML.XMLElement]$xmlcpu = $xmlDoc.CreateElement("cpu") [System.XML.XMLElement]$xmlmemory = $xmlDoc.CreateElement("memory") [System.XML.XMLElement]$xmlconnections = $xmlDoc.CreateElement("connectionsPerSecond") Add-XmlElement -xmlRoot $xmlcpu -xmlElementName "percentValue" -xmlElementText $cpu Add-XmlElement -xmlRoot $xmlmemory -xmlElementName "percentValue" -xmlElementText $memory Add-XmlElement -xmlRoot $xmlconnections -xmlElementName "value" -xmlElementText $ConnectionsPerSecond $xmlRoot.AppendChild($xmlcpu) | out-null $xmlRoot.AppendChild($xmlmemory) | out-null $xmlRoot.AppendChild($xmlconnections) | out-null $uri = "/api/4.0/firewall/stats/eventthresholds" $body = $xmlroot.outerXml Invoke-NsxWebRequest -method "PUT" -URI $uri -body $body | out-null Get-NsxFirewallThreshold } end {} }
This initial attempt allowed me to create an XML body from the three properties according to the API Spec defined for NSX.
The API spec by default requires the user to specify CPU, Memory, and ConnectionsPerSecond every time a new change is created. Whilst this makes sense technically from a usability point this isn’t logical.
* What if I just want to change the CPU value?
This will mean I will need to collect the values in `Get-NsxFirewallThreshold` and record the values to manually define them as parameters in `Set-NsxFirewallThreshold`. This is cumbersome and some additional logic
Here is revision number two:
function Set-NsxFirewallThreshold { <# .SYNOPSIS Sets the Distributed Firewall thresholds for CPU, Memory and Connections per Second .DESCRIPTION The firewall module generates system events when the memory and CPU usage crosses these thresholds. This command will set the threshold configuration for the distributed firewall .EXAMPLE PS /> Set-NsxFirewallThreshold -Cpu 70 -Memory 70 -ConnectionsPerSecond 35000 CPU Memory ConnectionsPerSecond --- ------ -------------------- 70 70 35000 #> param ( [Parameter (Mandatory=$false)] [ValidateRange(1,100)] [int]$Memory, [Parameter (Mandatory=$false)] [ValidateRange(1,100)] [int]$Cpu, [Parameter (Mandatory=$false)] [ValidateRange(1,500000)] [int]$ConnectionsPerSecond, [Parameter (Mandatory=$false)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection=$defaultNSXConnection ) begin { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("eventThresholds") $xmlDoc.appendChild($xmlRoot) | out-null #Create an Element and append it to the root [System.XML.XMLElement]$xmlcpu = $xmlDoc.CreateElement("cpu") [System.XML.XMLElement]$xmlmemory = $xmlDoc.CreateElement("memory") [System.XML.XMLElement]$xmlconnections = $xmlDoc.CreateElement("connectionsPerSecond") #Capture existing thresholds $CurrentThreshold = Get-NsxFirewallThreshold #Using PSBoundParamters.ContainsKey lets us know if the user called us with a given parameter. #If the user did not specify a given parameter, we dont want to modify from the existing value. if ( $PsBoundParameters.ContainsKey('Cpu') ) { Add-XmlElement -xmlRoot $xmlcpu -xmlElementName "percentValue" -xmlElementText $Cpu } else { Add-XmlElement -xmlRoot $xmlcpu -xmlElementName "percentValue" -xmlElementText $CurrentThreshold.Cpu } if ( $PsBoundParameters.ContainsKey('Memory') ) { Add-XmlElement -xmlRoot $xmlmemory -xmlElementName "percentValue" -xmlElementText $Memory } else { Add-XmlElement -xmlRoot $xmlmemory -xmlElementName "percentValue" -xmlElementText $CurrentThreshold.Memory } if ( $PsBoundParameters.ContainsKey('ConnectionsPerSecond') ) { Add-XmlElement -xmlRoot $xmlconnections -xmlElementName "value" -xmlElementText $ConnectionsPerSecond } else { Add-XmlElement -xmlRoot $xmlconnections -xmlElementName "value" -xmlElementText $CurrentThreshold.ConnectionsPerSecond } $xmlRoot.AppendChild($xmlcpu) | out-null $xmlRoot.AppendChild($xmlmemory) | out-null $xmlRoot.AppendChild($xmlconnections) | out-null $uri = "/api/4.0/firewall/stats/eventthresholds" $body = $xmlroot.outerXml Invoke-NsxWebRequest -method "PUT" -URI $uri -body $body | out-null Get-NsxFirewallThreshold } end {} }
* Create a blank XML document
* Retrieve the Event threshold values with `Get-NsxFirewallThreshold` and store in `$CurrentThreshold`
* Then use an if else statement with `$PsBoundParameters.ContainsKey` to perform the following:
* If parameter is defined use the new value
* Else use the existing value stored in the variable `$CurrentThreshold`
* Use value and place correctly into XML document structure
* Store document in a body
* Use body as payload when using `Invoke-NsxWebRequest`.
function Get-NsxFirewallThreshold { <# .SYNOPSIS Retrieves the Distributed Firewall thresholds for CPU, Memory and Connections per Second .DESCRIPTION The firewall module generates system events when the memory and CPU usage crosses these thresholds. This command will retrieve the threshold configuration for the distributed firewall .EXAMPLE PS /> Get-NsxFirewallThreshold CPU Memory ConnectionsPerSecond --- ------ -------------------- cpu memory connectionsPerSecond #> param ( [Parameter (Mandatory=$false)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection=$defaultNSXConnection ) begin { } process { $URI = "/api/4.0/firewall/stats/eventthresholds" try { $response = invoke-nsxwebrequest -method "get" -uri $URI -connection $connection [system.xml.xmldocument]$Content = $response.content } catch { Throw "Unexpected API response $_" } if ( Invoke-XPathQuery -Node $content -QueryMethod SelectSingleNode -query "child::eventThresholds" ){ $Content.eventThresholds } } end{} } function Set-NsxFirewallThreshold { <# .SYNOPSIS Sets the Distributed Firewall thresholds for CPU, Memory and Connections per Second .DESCRIPTION The firewall module generates system events when the memory and CPU usage crosses these thresholds. This command will set the threshold configuration for the distributed firewall .EXAMPLE PS /> Set-NsxFirewallThreshold -Cpu 70 -Memory 70 -ConnectionsPerSecond 35000 CPU Memory ConnectionsPerSecond --- ------ -------------------- cpu memory connectionsPerSecond #> param ( [Parameter (Mandatory=$false)] [ValidateRange(1,100)] [int]$Memory, [Parameter (Mandatory=$false)] [ValidateRange(1,100)] [int]$Cpu, [Parameter (Mandatory=$false)] [ValidateRange(1,500000)] [int]$ConnectionsPerSecond, [Parameter (Mandatory=$false)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection=$defaultNSXConnection ) begin { #Capture existing thresholds $currentthreshold = Get-NsxFirewallThreshold #Using PSBoundParamters.ContainsKey lets us know if the user called us with a given parameter. #If the user did not specify a given parameter, we dont want to modify from the existing value. if ( $PsBoundParameters.ContainsKey('Cpu') ) { $currentthreshold.cpu.percentValue = $Cpu } if ( $PsBoundParameters.ContainsKey('Memory') ) { $currentthreshold.memory.percentValue = $Memory } if ( $PsBoundParameters.ContainsKey('ConnectionsPerSecond') ) { $currentthreshold.connectionsPerSecond.value = $ConnectionsPerSecond } $uri = "/api/4.0/firewall/stats/eventthresholds" $body = $currentthreshold.outerXml Invoke-NsxWebRequest -method "PUT" -URI $uri -body $body | out-null Get-NsxFirewallThreshold }