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 {}
}

 

My thought process here was now to allow the user to define one, two, or three of the variables.
The flow of the previous code is as follows:

* 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
    }
After a few iterations here is the final commit. This was a great learning exercise. Thinking about the cmdlet you are creating coupled with how it being used would definitely saved me a few iterations. A shoutout to Nick Bradford for the mentoring and the prods to make me think about this different and ultimately be a better coder.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

*