#Powershell NSX module #Nick Bradford #nbradford@vmware.com #Version - See Manifest for version details. <# Copyright © 2015 VMware, Inc. All Rights Reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 2, as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License version 2 for more details. You should have received a copy of the General Public License version 2 along with this program. If not, see https://www.gnu.org/licenses/gpl-2.0.html. Some files may be comprised of various open source software components, each of which has its own license that is located in the source code of the respective component. #> #Requires -Version 3.0 #More sophisticated requirement checking done at module load time. #My installer home and valid PNSX branches (releases) (used in Update-Powernsx.) $PNsxUrlBase = "https://raw.githubusercontent.com/vmware/powernsx" $ValidBranches = @("master", "v2", "v3") $Script:AllValidServices = @("AARP", "AH", "ARPATALK", "ATMFATE", "ATMMPOA", "BPQ", "CUST", "DEC", "DIAG", "DNA_DL", "DNA_RC", "DNA_RT", "ESP", "FR_ARP", "FTP", "GRE", "ICMP", "IEEE_802_1Q", "IGMP", "IPCOMP", "IPV4", "IPV6", "IPV6FRAG", "IPV6ICMP", "IPV6NONXT", "IPV6OPTS", "IPV6ROUTE", "IPX", "L2_OTHERS", "L2TP", "L3_OTHERS", "LAT", "LLC", "LOOP", "MS_RPC_TCP", "MS_RPC_UDP", "NBDG_BROADCAST", "NBNS_BROADCAST", "NETBEUI", "ORACLE_TNS", "PPP", "PPP_DISC", "PPP_SES", "RARP", "RAW_FR", "RSVP", "SCA", "SCTP", "SUN_RPC_TCP", "SUN_RPC_UDP", "TCP", "UDP", "X25") $Script:AllServicesRequiringPort = @( "FTP", "L2_OTHERS", "L3_OTHERS", "MS_RPC_TCP", "MS_RPC_UDP", "NBDG_BROADCAST", "NBNS_BROADCAST", "ORACLE_TNS", "SUN_RPC_TCP", "SUN_RPC_UDP" ) $script:AllServicesNotRequiringPort = $Script:AllValidServices | Where-Object { $AllServicesRequiringPort -notcontains $_ } $script:AllServicesValidSourcePort = @( "FTP", "MS_RPC_TCP", "MS_RPC_UDP", "NBDG_BROADCAST", "NBNS_BROADCAST", "ORACLE_TNS", "SUN_RPC_TCP", "SUN_RPC_UDP", "TCP", "UDP" ) $Script:AllValidIcmpTypes = @("echo-reply", "destination-unreachable", "source-quench", "redirect", "echo-request", "time-exceeded", "parameter-problem", "timestamp-request", "timestamp-reply", "address-mask-request", "address-mask-reply", "router-solicitation", "router-advertisement", "source-host-isolated", "pointer-to-error", "redirect-host", "fragmentation-needed", "bad-length", "destination-network-prohibited", "ttl-zero-transit", "network-unreachable-tos", "network-unreachable", "host-unreachable", "communication-prohibited", "destination-host-unknown", "ttl-zero-reassembly", "port-unreachable", "address-mask-request" ) Set-StrictMode -Version Latest ######## ######## # Private functions Function _init { #Run at module load time. #PSEdition property does not exist pre v5. We need to do a few things in #exported functions to workaround some limitations of core edition, so we export #the global PNSXPSTarget var to reference if required. if ( ( $PSVersionTable.PSVersion.Major -ge 6 ) -or ( ( $PSVersionTable.PSVersion.Major -eq 5 ) -and ( $PSVersionTable.PSVersion.Minor -ge 1 ) ) ) { $script:PNsxPSTarget = $PSVersionTable.PSEdition } else { $script:PNsxPSTarget = "Desktop" } ## Define class required for certificate validation override. Version dependant. ## For whatever reason, this does not work when contained within a function? $TrustAllCertsPolicy = @" using System.Net; using System.Security.Cryptography.X509Certificates; public class TrustAllCertsPolicy : ICertificatePolicy { public bool CheckValidationResult( ServicePoint srvPoint, X509Certificate certificate, WebRequest request, int certificateProblem) { return true; } } "@ $InternalHttpClientHandler = @" using System.Net.Http; public class InternalHttpClientHandler : HttpClientHandler { public InternalHttpClientHandler(bool SkipCertificateCheck) { if (SkipCertificateCheck) { ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; } } } "@ if ( $script:PNsxPSTarget -eq "Desktop" ) { if ( -not ("TrustAllCertsPolicy" -as [type])) { Add-Type $TrustAllCertsPolicy } } elseif ( $script:PNsxPSTarget -eq "Core") { if ( -not ("InternalHttpClientHandler" -as [type]) ) { Add-Type $InternalHttpClientHandler -ReferencedAssemblies System.Net.Http, System.Security.Cryptography.X509Certificates, System.Net.Primitives -WarningAction "SilentlyContinue" } } #Custom class required for Core pseudo WebResponse and exception $InternalWebResponse = @" using System; using System.Collections.Generic; public class internalWebResponse { public int StatusCode; public string StatusDescription; public Dictionary Headers; public string Content; public internalWebResponse() { this.Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); } public override string ToString() { return this.Content; } } public class InternalWebRequestException: Exception { public internalWebResponse Response; public InternalWebRequestException(string message, internalWebResponse Response) : base(message){ this.Response = Response; } } "@ Add-Type $InternalWebResponse #Custom NSX API exception $InternalNsxApiException = @" using System; public class InternalNsxApiException: Exception { public InternalNsxApiException(){} public InternalNsxApiException(string message) : base(message) {} public InternalNsxApiException(string message, Exception inner) : base(message, inner) {} } "@ Add-Type $InternalNsxApiException -IgnoreWarnings -WarningAction "SilentlyContinue" } function Invoke-XpathQuery { <# .SYNOPSIS Invoke-XpathQuery provides a consistent way of performing xpath queries on XML element and document nodes on both Desktop and Core PowerShell editions. .DESCRIPTION Invoke-XpathQuery is required because of the differing XPath implementations in the dotnet FullCRL on Desktop and the dotNet Core CLR on Core editions of PowerShell. The is typcially only utilised internally by PowerNSX cmdlets, but is exported by the PowerNSX module to provide the same capability to anyone who needs to manipulate XML in scripts that consume PowerNSX without having to manually copy the function out of the PowerNSX code in order to leverage it for cross platform support. It is highly likely that if you dont know why you need this cmdlet, then you dont need it :) .EXAMPLE $NodeToRemove = (Invoke-XPathQuery -QueryMethod SelectSingleNode -Node $xmlnode -Query "descendant::subInterface[index=0]") returns a single XML node matching the specified XPATH query. .EXAMPLE $NodeToRemove = (Invoke-XPathQuery -QueryMethod SelectNodes -Node $xmlnode -Query "descendant::subInterface") returns a collection of XML nodes matching the specified XPATH query. #> param ( [Parameter (Mandatory = $true)] #XPath query method. Supports SelectSingleNode or SelectNodes. [ValidateSet("SelectSingleNode", "SelectNodes")] [string]$QueryMethod, [Parameter (Mandatory = $true)] #XmlDocument or XmlElement node to be queried. $Node, [Parameter (Mandatory = $true)] #Xpath Query. [string]$query ) If ( ( $script:PNsxPSTarget -eq "Core") -and ($PSVersionTable.GitCommitId -eq "v6.0.0-alpha.18") ) { #Use the XPath extensions class to perform the query switch ($QueryMethod) { "SelectSingleNode" { [System.Xml.XmlDocumentXPathExtensions]::SelectSingleNode($node, $query) } "SelectNodes" { [System.Xml.XmlDocumentXPathExtensions]::SelectNodes($node, $query) } } } else { #Perform the query with the native methods on the node switch ($QueryMethod) { "SelectSingleNode" { $node.SelectSingleNode($query) } "SelectNodes" { $node.SelectNodes($query) } } } } function Read-HostWithDefault { param( [string]$Default, [string]$Prompt ) if ($default) { $response = Read-Host -Prompt "$Prompt [$Default]" if ( $response -eq "" ) { $Default } else { $response } } else { Read-Host -Prompt $Prompt } } function ConvertFrom-Bitmask { param ( [Parameter(Mandatory = $true)] [ValidateRange(1, 32)] [int]$Bitmask ) [ipaddress]$base = "255.255.255.255" $invertedmask = [ipaddress]($base.address - [convert]::toint64(([math]::pow(2, (32 - $bitmask)) -bxor $base.Address) + 1)) [ipaddress]$subnetmask = "$(255-$($invertedmask.GetAddressBytes()[3]))." + "$(255-$($invertedmask.GetAddressBytes()[2]))." + "$(255-$($invertedmask.GetAddressBytes()[1]))." + "$(255-$($invertedmask.GetAddressBytes()[0]))" $subnetmask } function ConvertTo-Bitmask { param ( [Parameter(Mandatory = $true)] [ipaddress]$subnetmask ) $bitcount = 0 $boundaryoctetfound = $false $boundarybitfound = $false #start at most sig end. foreach ($octet in $subnetmask.GetAddressBytes()) { switch ($octet) { "255" { if ( $boundaryoctetfound ) { throw "SubnetMask specified is not valid. Specify a valid mask and try again." } else { $bitcount += 8 } } "0" { $boundaryoctetfound = $true } default { if ( $boundaryoctetfound ) { throw "SubnetMask specified is not valid. Specify a valid mask and try again." } else { $boundaryoctetfound = $true $boundaryoctet = $_ for ( $i = 7; $i -ge 0 ; $i-- ) { if ( $boundaryoctet -band [math]::pow(2, $i) ) { if ( $boundarybitfound) { #Already hit boundary - mask isnt valid. throw "SubnetMask specified is not valid. Specify a valid mask and try again." } $bitcount++ } else { $boundarybitfound = $true } } } } } } $bitcount } function Get-NetworkFromHostAddress { [CmdletBinding(DefaultParameterSetName = "mask")] param ( [Parameter(Mandatory = $true, ParameterSetName = "cidr")] [Parameter(Mandatory = $true, ParameterSetName = "mask")] [ipaddress]$Address, [Parameter(Mandatory = $true, ParameterSetName = "mask")] [ipaddress]$SubnetMask, [Parameter(Mandatory = $true, ParameterSetName = "cidr")] [ValidateRange(1, 32)] [int]$BitMask ) if ( $PsCmdlet.ParameterSetName -eq 'cidr') { $SubnetMask = convertfrom-bitmask -bitmask $BitMask } $NetAddress = "" for ( $i = 0; $i -le 3; $i++ ) { $NetAddress += "$($Address.GetAddressBytes()[$i] -band $SubnetMask.GetAddressBytes()[$i])." } [ipaddress]($NetAddress -replace "\.$", "") } function Test-AddressInNetwork { [CmdletBinding(DefaultParameterSetName = "mask")] param ( [Parameter(Mandatory = $true, ParameterSetName = "mask")] [ipaddress]$SubnetMask, [Parameter(Mandatory = $true, ParameterSetName = "cidr")] [ValidateRange(1, 32)] [int]$Bitmask, [Parameter(Mandatory = $true)] [ipaddress]$Network, [Parameter(Mandatory = $true)] [ipaddress]$Address ) if ( $PsCmdlet.ParameterSetName -eq 'cidr') { $SubnetMask = convertfrom-bitmask -bitmask $BitMask } $Network -eq (Get-NetworkFromHostAddress -Address $Address -SubnetMask $SubnetMask) } function Get-NetworkRange { #Im well aware that this is very inefficient, but I need it quickly, and CPUs are cheap ;) [CmdletBinding(DefaultParameterSetName = "mask")] param ( [Parameter(Mandatory = $true, ParameterSetName = "mask")] [ipaddress]$SubnetMask, [Parameter(Mandatory = $true, ParameterSetName = "cidr")] [ValidateRange(1, 32)] [int]$Bitmask, [Parameter(Mandatory = $true)] [ipaddress]$Network ) if ( $PsCmdlet.ParameterSetName -eq 'cidr') { $SubnetMask = convertfrom-bitmask -bitmask $BitMask } #Check that the network specified is a valid network address if ( -not (( Get-NetworkFromHostAddress -address $network -subnetmask $subnetmask ) -eq $network )) { throw "Specified Network address is not valid (Does not lie on subnet boundary)" } $Range = New-Object System.Collections.Arraylist $CurrAddressBytes = @( $Network.GetAddressBytes()[0], $Network.GetAddressBytes()[1], $Network.GetAddressBytes()[2], $Network.GetAddressBytes()[3]) do { $CurrAddressBytes[3] += 1 if ( $CurrAddressBytes[3] -eq 256 ) { $CurrAddressBytes[3] = 0 $CurrAddressBytes[2] += 1 if ( $CurrAddressBytes[2] -eq 256 ) { $CurrAddressBytes[2] = 0 $CurrAddressBytes[1] + 1 if ( $CurrAddressBytes[1] -eq 256 ) { $CurrAddressBytes[1] = 0 $CurrAddressBytes[0] + 1 if ( $CurrAddressBytes[0] -eq 256 ) { break } } } } $currentaddress = "$($CurrAddressBytes[0]).$($CurrAddressBytes[1]).$($CurrAddressBytes[2]).$($CurrAddressBytes[3])" $Range.Add($currentaddress) | Out-Null } while ( Test-AddressInNetwork -network $network -subnetmask $subnetmask -address $currentaddress ) #remove last and second last (last is above range, second last is broadcast address... ) $range.RemoveAt($range.Count - 1 ) $BroadCastAddress = $range[-1] $range.RemoveAt($range.Count - 1 ) [pscustomobject]@{ "NetworkAddress" = $network "ValidAddressRange" = $range "Broadcast" = $BroadCastAddress } } function Add-XmlElement { #Internal function used to simplify the exercise of adding XML text Nodes. param ( [System.XML.XMLElement]$xmlRoot, [String]$xmlElementName, [String]$xmlElementText ) #Create an Element and append it to the root [System.XML.XMLElement]$xmlNode = $xmlRoot.OwnerDocument.CreateElement($xmlElementName) [System.XML.XMLNode]$xmlText = $xmlRoot.OwnerDocument.CreateTextNode($xmlElementText) $xmlNode.AppendChild($xmlText) | Out-Null $xmlRoot.AppendChild($xmlNode) | Out-Null } function Get-FeatureStatus { param ( [string]$featurestring, [system.xml.xmlelement[]]$statusxml ) [system.xml.xmlelement]$feature = $statusxml | Where-Object { $_.featureId -eq $featurestring } | Select-Object -First 1 [string]$statusstring = $feature.status $message = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $feature -query 'message') if ( $message -and ( $message | Get-Member -MemberType Property -Name '#Text')) { $statusstring += " ($($message.'#text'))" } $statusstring } function ParseCentralCliResponse { param ( [Parameter ( Mandatory = $True, Position = 1)] [String]$response ) #Response is straight text unfortunately, so there is no structure. Having a crack at writing a very simple parser though the formatting looks.... challenging... #Control flags for handling list and table processing. $TableHeaderFound = $false $MatchedVnicsList = $false $MatchedRuleset = $false $MatchedAddrSet = $false $RuleSetName = "" $AddrSetName = "" $KeyValHash = @{} $KeyValHashUsed = $false #Defined this as variable as the swtich statement does not let me concat strings, which makes for a verrrrry long line... $RegexDFWRule = "^(?#\sinternal\s#\s)?(?rule\s)?(?\d+)\sat\s(?\d+)\s(?in|out|inout)\s" + "(?protocol|ethertype)\s(?.*?)\sfrom\s(?.*?)\sto\s(?.*?)(?:\sport\s(?.*))?\s" + "(?accept|reject|drop)(?:\swith\s(?log))?(?:\stag\s(?'.*'))?;" foreach ( $line in ($response -split '[\r\n]')) { #Init EntryHash hashtable $EntryHash = @{} switch -regex ($line.trim()) { #C CLI appears to emit some error conditions as ^ Error: "^Error \d+:.*$" { Write-Debug "$($MyInvocation.MyCommand.Name) : Matched Error line. $_ " Throw "CLI command returned an error: ( $_ )" } "^\s*$" { #Blank line, ignore... Write-Debug "$($MyInvocation.MyCommand.Name) : Ignoring blank line: $_" break } "^# Filter rules$" { #Filter line encountered in a ruleset list, ignore... if ( $MatchedRuleSet ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Ignoring meaningless #Filter rules line in ruleset: $_" break } else { throw "Error parsing Centralised CLI command output response. Encountered #Filter rules line when not processing a ruleset: $_" } } #Matches a single integer of 1 or more digits at the start of the line followed only by a fullstop. #Example is the Index in a VNIC list. AFAIK, the index should only be 1-9. but just in case we are matching 1 or more digit... "^(\d+)\.$" { Write-Debug "$($MyInvocation.MyCommand.Name) : Matched Index line. Discarding value: $_ " If ( $MatchedVnicsList ) { #We are building a VNIC list output and this is the first line. #Init the output object to static kv props, but discard the value (we arent outputing as it appears superfluous.) Write-Debug "$($MyInvocation.MyCommand.Name) : Processing Vnic List, initialising new Vnic list object" $VnicListHash = @{} $VnicListHash += $KeyValHash $KeyValHashUsed = $true } break } #Matches the start of a ruleset list. show dfw host host-xxx filter xxx rules will output in rulesets like this "ruleset\s(\S+) {" { #Set a flag to say we matched a ruleset List, and create the output object. Write-Debug "$($MyInvocation.MyCommand.Name) : Matched start of DFW Ruleset output. Processing following lines as DFW Ruleset: $_" $MatchedRuleset = $true $RuleSetName = $matches[1].trim() break } #Matches the start of a addrset list. show dfw host host-xxx filter xxx addrset will output in addrsets like this "addrset\s(\S+) {" { #Set a flag to say we matched a addrset List, and create the output object. Write-Debug "$($MyInvocation.MyCommand.Name) : Matched start of DFW Addrset output. Processing following lines as DFW Addrset: $_" $MatchedAddrSet = $true $AddrSetName = $matches[1].trim() break } #Matches a addrset entry. show dfw host host-xxx filter xxx addrset will output in addrsets. "^(?ip|mac)\s(?
.*),$" { #Make sure we were expecting it... if ( -not $MatchedAddrSet ) { Throw "Error parsing Centralised CLI command output response. Unexpected dfw addrset entry : $_" } #We are processing a RuleSet, so we need to emit an output object that contains the ruleset name. [PSCustomobject]@{ "AddrSet" = $AddrSetName; "Type" = $matches.Type; "Address" = $matches.Address } break } #Matches a rule, either within a ruleset, or individually listed. show dfw host host-xxx filter xxx rules will output in rulesets, #or show dfw host-xxx filter xxx rule 1234 will output individual rule that should match. $RegexDFWRule { #Check if the rule is individual or part of ruleset... if ( $Matches.ContainsKey("RuleSetMember") -and (-not $MatchedRuleset )) { Throw "Error parsing Centralised CLI command output response. Unexpected dfw ruleset entry : $_" } $Type = switch ( $matches.Type ) { "protocol" { "Layer3" } "ethertype" { "Layer2" } } $Internal = if ( $matches.ContainsKey("Internal")) { $true } else { $false } $Port = if ( $matches.ContainsKey("Port") ) { $matches.port } else { "Any" } $Log = if ( $matches.ContainsKey("Log") ) { $true } else { $false } $Tag = if ( $matches.ContainsKey("Tag") ) { $matches.Tag } else { "" } If ( $MatchedRuleset ) { #We are processing a RuleSet, so we need to emit an output object that contains the ruleset name. [PSCustomobject]@{ "RuleSet" = $RuleSetName; "InternalRule" = $Internal; "RuleID" = $matches.RuleId; "Position" = $matches.Position; "Direction" = $matches.Direction; "Type" = $Type; "Service" = $matches.Service; "Source" = $matches.Source; "Destination" = $matches.Destination; "Port" = $Port; "Action" = $matches.Action; "Log" = $Log; "Tag" = $Tag } } else { #We are not processing a RuleSet; so we need to emit an output object without a ruleset name. [PSCustomobject]@{ "InternalRule" = $Internal; "RuleID" = $matches.RuleId; "Position" = $matches.Position; "Direction" = $matches.Direction; "Type" = $Type; "Service" = $matches.Service; "Source" = $matches.Source; "Destination" = $matches.Destination; "Port" = $Port; "Action" = $matches.Action; "Log" = $Log; "Tag" = $Tag } } break } #Matches the end of a ruleset and addr lists. show dfw host host-xxx filter xxx rules will output in lists like this "^}$" { if ( $MatchedRuleset ) { #Clear the flag to say we matched a ruleset List Write-Debug "$($MyInvocation.MyCommand.Name) : Matched end of DFW ruleset." $MatchedRuleset = $false $RuleSetName = "" break } if ( $MatchedAddrSet ) { #Clear the flag to say we matched an addrset List Write-Debug "$($MyInvocation.MyCommand.Name) : Matched end of DFW addrset." $MatchedAddrSet = $false $AddrSetName = "" break } throw "Error parsing Centralised CLI command output response. Encountered unexpected list completion character in line: $_" } #More Generic matches #Matches the generic KV case where we have _only_ two strings separated by more than one space. #This will do my head in later when I look at it, so the regex explanation is: # - (?: gives non capturing group, we want to leverage $matches later, so dont want polluting groups. # - (\S|\s(?!\s)) uses negative lookahead assertion to 'Match a non whitespace, or a single whitespace, as long as its not followed by another whitespace. # - The rest should be self explanatory. "^((?:\S|\s(?!\s))+\s{2,}){1}((?:\S|\s(?!\s))+)$" { Write-Debug "$($MyInvocation.MyCommand.Name) : Matched Key Value line (multispace separated): $_ )" $key = $matches[1].trim() $value = $matches[2].trim() If ( $MatchedVnicsList ) { #We are building a VNIC list output and this is one of the lines. Write-Debug "$($MyInvocation.MyCommand.Name) : Processing Vnic List, Adding $key = $value to current VnicListHash" $VnicListHash.Add($key, $value) if ( $key -eq "Filters" ) { #Last line in a VNIC List... Write-Debug "$($MyInvocation.MyCommand.Name) : VNIC List : Outputing VNIC List Hash." [PSCustomobject]$VnicListHash } } else { #Add KV to hash table that we will append to output object $KeyValHash.Add($key, $value) } break } #Matches a general case output line containing Key: Value for properties that are consistent accross all entries in a table. #This will match a line with multiple colons in it, not sure if thats an issue yet... "^((?:\S|\s(?!\s))+):((?:\S|\s(?!\s))+)$" { if ( $TableHeaderFound ) { Throw "Error parsing Centralised CLI command output response. Key Value line found after header: ( $_ )" } Write-Debug "$($MyInvocation.MyCommand.Name) : Matched Key Value line (Colon Separated) : $_" #Add KV to hash table that we will append to output object $KeyValHash.Add($matches[1].trim(), $matches[2].trim()) break } #Matches a Table header line. This is a special case of the table entry line match, with the first element being ^No\. Hoping that 'No.' start of the line is consistent :S "^No\.\s{2,}(.+\s{2,})+.+$" { if ( $TableHeaderFound ) { throw "Error parsing Centralised CLI command output response. Matched header line more than once: ( $_ )" } Write-Debug "$($MyInvocation.MyCommand.Name) : Matched Table Header line: $_" $TableHeaderFound = $true $Props = $_.trim() -split "\s{2,}" break } #Matches the start of a Virtual Nics List output. We process the output lines following this as a different output object "Virtual Nics List:" { #When central cli outputs a NIC 'list' it does so with a vertical list of Key Value rather than a table format, #and with multi space as the KV separator, rather than a : like normal KV output. WTF? #So Now I have to go forth and collate my nic object over the next few lines... #Example looks like this: #Virtual Nics List: #1. #Vnic Name test-vm - Network adapter 1 #Vnic Id 50012d15-198c-066c-af22-554aed610579.000 #Filters nic-4822904-eth0-vmware-sfw.2 #Set a flag to say we matched a VNic List, and create the output object initially with just the KV's matched already. Write-Debug "$($MyInvocation.MyCommand.Name) : Matched VNIC List line. Processing remaining lines as Vnic List: $_" $MatchedVnicsList = $true break } #Matches a table entry line. At least three properties (that may contain a single space) separated by more than one space. "^((?:\S|\s(?!\s))+\s{2,}){2,}((?:\S|\s(?!\s))+)$" { if ( -not $TableHeaderFound ) { throw "Error parsing Centralised CLI command output response. Matched table entry line before header: ( $_ )" } Write-Debug "$($MyInvocation.MyCommand.Name) : Matched Table Entry line: $_" $Vals = $_.trim() -split "\s{2,}" if ($Vals.Count -ne $Props.Count ) { Throw "Error parsing Centralised CLI command output response. Table entry line contains different value count compared to properties count: ( $_ )" } #Build the output hashtable with the props returned in the table entry line for ( $i = 0; $i -lt $props.count; $i++ ) { #Ordering is hard, and No. entry is kinda superfluous, so removing it from output (for now) if ( -not ( $props[$i] -eq "No." )) { $EntryHash[$props[$i].trim()] = $vals[$i].trim() } } #Add the KV pairs that were parsed before the table. try { #This may fail if we have a key of the same name. For the moment, Im going to assume that this wont happen... $EntryHash += $KeyValHash $KeyValHashUsed = $true } catch { throw "Unable to append static Key Values to EntryHash output object. Possibly due to a conflicting key" } #Emit the entry line as a PSCustomobject :) [PSCustomObject]$EntryHash break } default { throw "Unable to parse Centralised CLI output line : $($_ -replace '\s','_')" } } } if ( (-not $KeyValHashUsed) -and $KeyValHash.count -gt 0 ) { #Some output is just key value, so, if it hasnt been appended to output object already, we will just emit it. #Not sure how this approach will work long term, but it works for show dfw vnic <> Write-Debug "$($MyInvocation.MyCommand.Name) : KeyValHash has not been used after all line processing, outputing as is: $_" [PSCustomObject]$KeyValHash } } function ConvertTo-NsxApiCriteriaOperator { #Convert the CriteriaOperator to the API AND/OR from the UI/PowerNSX value of ANY/ALL switch ( $args[0] ) { "any" { "OR" } "all" { "AND" } } } function ConvertFrom-NsxApiCriteriaOperator { #Convert from the CriteriaOperator of the API AND/OR to the UI/PowerNSX value of ANY/ALL switch ( $args[0] ) { "or" { "ANY" } "and" { "ALL" } } } function ConvertTo-NsxApiCriteriaCondition { switch ( $args[0] ) { "equals" { "=" } "notequals" { "!=" } "regex" { "similar_to" } default { $_ } } } function ConvertFrom-NsxApiCriteriaCondition { switch ( $args[0] ) { "=" { "equals" } "!=" { "notequals" } "similar_to" { "regex" } default { $_ } } } function ConvertTo-NsxApiCriteriaKey { switch ( $args[0] ) { "OSName" { "VM.GUEST_OS_FULL_NAME" } "ComputerName" { "VM.GUEST_HOST_NAME" } "VMName" { "VM.NAME" } "SecurityTag" { "VM.SECURITY_TAG" } default { $args[0] } } } function ConvertFrom-NsxApiCriteriaKey { switch ( $args[0] ) { "VM.GUEST_OS_FULL_NAME" { "OSName" } "VM.GUEST_HOST_NAME" { "ComputerName" } "VM.NAME" { "VMName" } "VM.SECURITY_TAG" { "SecurityTag" } default { $args[0] } } } function ConvertTo-NsxApiSectionOperation { switch ( $args[0] ) { "top" { "insert_top" } "bottom" { "insert_before_default" } "before" { "insert_before" } "after" { "insert_after" } default { $args[0] } } } function ConvertFrom-NsxApiSectionOperation { switch ( $args[0] ) { "insert_top" { "top" } "insert_before_default" { "bottom" } "insert_before" { "before" } "insert_after" { "after" } default { $args[0] } } } function ConvertTo-NsxApiSectionType { switch ( $args[0] ) { "LAYER3" { "layer3sections" } "LAYER2" { "layer2sections" } "L3REDIRECT" { "layer3redirectsections" } default { $args[0] } } } function ConvertTo-NsxApiActionType { switch ( $args[0] ) { "AntiVirus" { "ANTI_VIRUS" } "VulnerabilityManagement" { "VULNERABILITY_MGMT" } "FileIntegrityMonitoring" { "FIM" } } } function ConvertFrom-NsxApiActionType { switch ( $args[0] ) { "ANTI_VIRUS" { "AntiVirus" } "VULNERABILITY_MGMT" { "VulnerabilityManagement" } "FIM" { "FileIntegrityMonitoring" } } } ######## ######## # Validation Functions function ValidateUpdateBranch { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Case sensitive if ( $ValidBranches -Ccontains $argument ) { $true } else { throw "Invalid Branch. Specify one of the valid branches : $($Validbranches -join ", ")" } } Function ValidateTransportZone { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ( $argument -is [system.xml.xmlelement] ) { if ( -not ($argument | Get-Member -MemberType Property -Name objectId )) { throw "Invalid Transport Zone object specified" } if ( -not ($argument | Get-Member -MemberType Property -Name objectTypeName )) { throw "Invalid Transport Zone object specified" } if ( -not ($argument.objectTypeName -eq "VdnScope")) { throw "Invalid Transport Zone object specified" } $true } else { throw "Invalid Transport Zone object specified" } } Function ValidateLogicalSwitchOrDistributedPortGroup { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if (-not ( ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Host.Networking.DistributedPortGroupInterop] ) -or ($argument -is [System.Xml.XmlElement] ))) { throw "Must specify a distributed port group or a logical switch" } else { #Do we Look like XML describing a Logical Switch if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "Object specified does not contain an objectId property. Specify a Distributed PortGroup or Logical Switch object." } if ( -not ( $argument | Get-Member -Name objectTypeName -MemberType Properties)) { throw "Object specified does not contain a type property. Specify a Distributed PortGroup or Logical Switch object." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "Object specified does not contain a name property. Specify a Distributed PortGroup or Logical Switch object." } switch ($argument.objectTypeName) { "VirtualWire" { } default { throw "Object specified is not a supported type. Specify a Distributed PortGroup or Logical Switch object." } } } else { #Its a VDS type - no further Checking } } $true } Function ValidateLogicalSwitchOrDistributedPortGroupOrStandardPortGroup { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if (-not ( ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Host.Networking.VirtualPortGroupBaseInterop] ) -or ($argument -is [System.Xml.XmlElement] ))) { throw "Must specify a distributed port group, logical switch or standard port group" } #Do we Look like XML describing a Logical Switch if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "Object specified does not contain an objectId property. Specify a Distributed PortGroup, Standard PortGroup or Logical Switch object." } if ( -not ( $argument | Get-Member -Name objectTypeName -MemberType Properties)) { throw "Object specified does not contain a type property. Specify a Distributed PortGroup, Standard PortGroup or Logical Switch object." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "Object specified does not contain a name property. Specify a Distributed PortGroup, Standard PortGroup or Logical Switch object." } switch ($argument.objectTypeName) { "VirtualWire" { } default { throw "Object specified is not a supported type. Specify a Distributed PortGroup, Standard PortGroup or Logical Switch object." } } } $true } Function ValidateIpPool { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an OSPF Area element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "XML Element specified does not contain an objectId property." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property." } if ( -not ( $argument | Get-Member -Name usedPercentage -MemberType Properties)) { throw "XML Element specified does not contain a usedPercentage property." } $true } else { throw "Specify a valid IP Pool object." } } Function ValidateVdsContext { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name switch -MemberType Properties)) { throw "XML Element specified does not contain a switch property." } if ( -not ( $argument | Get-Member -Name mtu -MemberType Properties)) { throw "XML Element specified does not contain an mtu property." } if ( -not ( $argument | Get-Member -Name uplinkPortName -MemberType Properties)) { throw "XML Element specified does not contain an uplinkPortName property." } if ( -not ( $argument | Get-Member -Name promiscuousMode -MemberType Properties)) { throw "XML Element specified does not contain a promiscuousMode property." } $true } else { throw "Specify a valid Vds Context object." } } Function ValidateSegmentIdRange { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name Id -MemberType Properties)) { throw "XML Element specified does not contain an Id property." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property." } if ( -not ( $argument | Get-Member -Name begin -MemberType Properties)) { throw "XML Element specified does not contain a begin property." } if ( -not ( $argument | Get-Member -Name end -MemberType Properties)) { throw "XML Element specified does not contain an end property." } $true } else { throw "Specify a valid Segment Id Range object." } } Function ValidateDistributedSwitch { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if (-not ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Host.Networking.DistributedSwitchInterop] )) { throw "Must specify a distributed switch" } $true } Function ValidateLogicalSwitch { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if (-not ($argument -is [System.Xml.XmlElement] )) { throw "Must specify a logical switch" } else { #Do we Look like XML describing a Logical Switch if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "Object specified does not contain an objectId property. Specify a Logical Switch object." } if ( -not ( $argument | Get-Member -Name objectTypeName -MemberType Properties)) { throw "Object specified does not contain a type property. Specify a Logical Switch object." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "Object specified does not contain a name property. Specify a Logical Switch object." } switch ($argument.objectTypeName) { "VirtualWire" { } default { throw "Object specified is not a supported type. Specify a Logical Switch object." } } } $true } Function ValidateLogicalRouterInterfaceSpec { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #temporary - need to script proper validation of a single valid NIC config for DLR (Edge and DLR have different specs :()) if ( -not $argument ) { throw "Specify at least one interface configuration as produced by New-NsxLogicalRouterInterfaceSpec. Pass a collection of interface objects to configure more than one interface" } $true } Function ValidateEdgeInterfaceSpec { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #temporary - need to script proper validation of a single valid NIC config for DLR (Edge and DLR have different specs :()) if ( -not $argument ) { throw "Specify at least one interface configuration as produced by New-NsxLogicalRouterInterfaceSpec. Pass a collection of interface objects to configure more than one interface" } $true } Function ValidateEdgeInterfaceAddress { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name primaryAddress -MemberType Properties)) { throw "XML Element specified does not contain a primaryAddress property." } if ( -not ( $argument | Get-Member -Name subnetPrefixLength -MemberType Properties)) { throw "XML Element specified does not contain a subnetPrefixLength property." } if ( -not ( $argument | Get-Member -Name subnetMask -MemberType Properties)) { throw "XML Element specified does not contain a subnetMask property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } if ( -not ( $argument | Get-Member -Name interfaceIndex -MemberType Properties)) { throw "XML Element specified does not contain an interfaceIndex property." } $true } else { throw "Specify a valid Edge Interface Address." } } Function ValidateAddressGroupSpec { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name primaryAddress -MemberType Properties)) { throw "XML Element specified does not contain a primaryAddress property." } if ( -not ( $argument | Get-Member -Name subnetPrefixLength -MemberType Properties)) { throw "XML Element specified does not contain a subnetPrefixLength property." } $true } else { throw "Specify a valid Interface Spec." } } Function ValidateLogicalRouter { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if we are an XML element if ($argument -is [System.Xml.XmlElement] ) { if ( $argument | Get-Member -Name edgeSummary -MemberType Properties) { if ( -not ( $argument.edgeSummary | Get-Member -Name objectId -MemberType Properties)) { throw "XML Element specified does not contain an edgesummary.objectId property. Specify a valid Logical Router Object" } if ( -not ( $argument.edgeSummary | Get-Member -Name objectTypeName -MemberType Properties)) { throw "XML Element specified does not contain an edgesummary.ObjectTypeName property. Specify a valid Logical Router Object" } if ( -not ( $argument.edgeSummary | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain an edgesummary.name property. Specify a valid Logical Router Object" } if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "XML Element specified does not contain a type property. Specify a valid Logical Router Object" } if ($argument.edgeSummary.objectTypeName -ne "Edge" ) { throw "Specified value is not a supported type. Specify a valid Logical Router Object." } if ($argument.type -ne "distributedRouter" ) { throw "Specified value is not a supported type. Specify a valid Logical Router Object." } $true } else { throw "Specify a valid Logical Router Object" } } else { throw "Specify a valid Logical Router Object" } } Function ValidateEdge { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if we are an XML element if ($argument -is [System.Xml.XmlElement] ) { if ( $argument | Get-Member -Name edgeSummary -MemberType Properties) { if ( -not ( $argument.edgeSummary | Get-Member -Name objectId -MemberType Properties)) { throw "XML Element specified does not contain an edgesummary.objectId property. Specify an NSX Edge Services Gateway object" } if ( -not ( $argument.edgeSummary | Get-Member -Name objectTypeName -MemberType Properties)) { throw "XML Element specified does not contain an edgesummary.ObjectTypeName property. Specify an NSX Edge Services Gateway object" } if ( -not ( $argument.edgeSummary | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain an edgesummary.name property. Specify an NSX Edge Services Gateway object" } if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "XML Element specified does not contain a type property. Specify an NSX Edge Services Gateway object" } if ($argument.edgeSummary.objectTypeName -ne "Edge" ) { throw "Specified value is not a supported type. Specify an NSX Edge Services Gateway object." } if ($argument.type -ne "gatewayServices" ) { throw "Specified value is not a supported type. Specify an NSX Edge Services Gateway object." } $true } else { throw "Specify a valid Edge Services Gateway Object" } } else { throw "Specify a valid Edge Services Gateway Object" } } Function ValidateEdgeRouting { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name routingGlobalConfig -MemberType Properties)) { throw "XML Element specified does not contain a routingGlobalConfig property." } if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property." } if ( -not ( $argument | Get-Member -Name version -MemberType Properties)) { throw "XML Element specified does not contain a version property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid Edge Routing object." } } Function ValidateEdgeStaticRoute { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "XML Element specified does not contain a type property." } if ( -not ( $argument | Get-Member -Name network -MemberType Properties)) { throw "XML Element specified does not contain a network property." } if ( -not ( $argument | Get-Member -Name nextHop -MemberType Properties)) { throw "XML Element specified does not contain a nextHop property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid Edge Static Route object." } } Function ValidateEdgeBgpNeighbour { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name ipAddress -MemberType Properties)) { throw "XML Element specified does not contain an ipAddress property." } if ( -not ( $argument | Get-Member -Name remoteAS -MemberType Properties)) { throw "XML Element specified does not contain a remoteAS property." } if ( -not ( $argument | Get-Member -Name weight -MemberType Properties)) { throw "XML Element specified does not contain a weight property." } if ( -not ( $argument | Get-Member -Name holdDownTimer -MemberType Properties)) { throw "XML Element specified does not contain a holdDownTimer property." } if ( -not ( $argument | Get-Member -Name keepAliveTimer -MemberType Properties)) { throw "XML Element specified does not contain a keepAliveTimer property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid Edge BGP Neighbour object." } } Function ValidateEdgeOspfArea { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an OSPF Area element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name areaId -MemberType Properties)) { throw "XML Element specified does not contain an areaId property." } if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "XML Element specified does not contain a type property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid Edge OSPF Area object." } } Function ValidateEdgeOspfInterface { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an OSPF Area element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name areaId -MemberType Properties)) { throw "XML Element specified does not contain an areaId property." } if ( -not ( $argument | Get-Member -Name vnic -MemberType Properties)) { throw "XML Element specified does not contain a vnic property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid Edge OSPF Interface object." } } Function ValidateEdgeRedistributionRule { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an OSPF Area element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name learner -MemberType Properties)) { throw "XML Element specified does not contain an areaId property." } if ( -not ( $argument | Get-Member -Name id -MemberType Properties)) { throw "XML Element specified does not contain an id property." } if ( -not ( $argument | Get-Member -Name action -MemberType Properties)) { throw "XML Element specified does not contain an action property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid Edge Redistribution Rule object." } } Function ValidateLogicalRouterRouting { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an LogicalRouter routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name routingGlobalConfig -MemberType Properties)) { throw "XML Element specified does not contain a routingGlobalConfig property." } if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property." } if ( -not ( $argument | Get-Member -Name version -MemberType Properties)) { throw "XML Element specified does not contain a version property." } if ( -not ( $argument | Get-Member -Name logicalrouterId -MemberType Properties)) { throw "XML Element specified does not contain an logicalrouterId property." } $true } else { throw "Specify a valid LogicalRouter Routing object." } } Function ValidateLogicalRouterBridging { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an LogicalRouter bridging element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name version -MemberType Properties)) { throw "XML Element specified does not contain a version property." } if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property." } $true } else { throw "Specify a valid LogicalRouter bridging object." } } Function ValidateLogicalRouterStaticRoute { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an LogicalRouter routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "XML Element specified does not contain a type property." } if ( -not ( $argument | Get-Member -Name network -MemberType Properties)) { throw "XML Element specified does not contain a network property." } if ( -not ( $argument | Get-Member -Name nextHop -MemberType Properties)) { throw "XML Element specified does not contain a nextHop property." } if ( -not ( $argument | Get-Member -Name logicalrouterId -MemberType Properties)) { throw "XML Element specified does not contain an logicalrouterId property." } $true } else { throw "Specify a valid LogicalRouter Static Route object." } } Function ValidateLogicalRouterBridge { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an LogicalRouter routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name bridgeID -MemberType Properties)) { throw "XML Element specified does not contain a bridgeId property. Specify a valid LogicalRouter bridge instance" } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property. Specify a valid LogicalRouter bridge instance" } if ( -not ( $argument | Get-Member -Name virtualWire -MemberType Properties)) { throw "XML Element specified does not contain a virtualWire property. Specify a valid LogicalRouter bridge instance" } if ( -not ( $argument | Get-Member -Name dvportGroup -MemberType Properties)) { throw "XML Element specified does not contain an dvportGroup property. Specify a valid LogicalRouter bridge instance" } $true } else { throw "Specify a valid LogicalRouter bridge instance." } } Function ValidateLogicalRouterBgpNeighbour { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an LogicalRouter routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name ipAddress -MemberType Properties)) { throw "XML Element specified does not contain an ipAddress property." } if ( -not ( $argument | Get-Member -Name remoteAS -MemberType Properties)) { throw "XML Element specified does not contain a remoteAS property." } if ( -not ( $argument | Get-Member -Name weight -MemberType Properties)) { throw "XML Element specified does not contain a weight property." } if ( -not ( $argument | Get-Member -Name holdDownTimer -MemberType Properties)) { throw "XML Element specified does not contain a holdDownTimer property." } if ( -not ( $argument | Get-Member -Name keepAliveTimer -MemberType Properties)) { throw "XML Element specified does not contain a keepAliveTimer property." } if ( -not ( $argument | Get-Member -Name logicalrouterId -MemberType Properties)) { throw "XML Element specified does not contain an logicalrouterId property." } $true } else { throw "Specify a valid LogicalRouter BGP Neighbour object." } } Function ValidateLogicalRouterOspfArea { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an OSPF Area element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name areaId -MemberType Properties)) { throw "XML Element specified does not contain an areaId property." } if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "XML Element specified does not contain a type property." } if ( -not ( $argument | Get-Member -Name logicalrouterId -MemberType Properties)) { throw "XML Element specified does not contain an logicalrouterId property." } $true } else { throw "Specify a valid LogicalRouter OSPF Area object." } } Function ValidateLogicalRouterOspfInterface { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an OSPF Area element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name areaId -MemberType Properties)) { throw "XML Element specified does not contain an areaId property." } if ( -not ( $argument | Get-Member -Name vnic -MemberType Properties)) { throw "XML Element specified does not contain a vnic property." } if ( -not ( $argument | Get-Member -Name logicalrouterId -MemberType Properties)) { throw "XML Element specified does not contain an logicalrouterId property." } $true } else { throw "Specify a valid LogicalRouter OSPF Interface object." } } Function ValidateLogicalRouterRedistributionRule { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an OSPF Area element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name learner -MemberType Properties)) { throw "XML Element specified does not contain an areaId property." } if ( -not ( $argument | Get-Member -Name id -MemberType Properties)) { throw "XML Element specified does not contain an id property." } if ( -not ( $argument | Get-Member -Name action -MemberType Properties)) { throw "XML Element specified does not contain an action property." } if ( -not ( $argument | Get-Member -Name logicalrouterId -MemberType Properties)) { throw "XML Element specified does not contain an logicalrouterId property." } $true } else { throw "Specify a valid LogicalRouter Redistribution Rule object." } } Function ValidateEdgePrefix { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge prefix element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property." } if ( -not ( $argument | Get-Member -Name ipAddress -MemberType Properties)) { throw "XML Element specified does not contain an ipAddress property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid Edge Prefix object." } } Function ValidateLogicalRouterPrefix { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge prefix element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property." } if ( -not ( $argument | Get-Member -Name ipAddress -MemberType Properties)) { throw "XML Element specified does not contain an ipAddress property." } if ( -not ( $argument | Get-Member -Name logicalRouterId -MemberType Properties)) { throw "XML Element specified does not contain an logicalRouterId property." } $true } else { throw "Specify a valid LogicalRouter Prefix object." } } Function ValidateEdgeInterface { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Accepts an interface Object. if ($argument -is [System.Xml.XmlElement] ) { If ( $argument | Get-Member -Name index -MemberType Properties ) { #Looks like an interface object if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property. Specify a valid Edge Services Gateway Interface object." } if ( -not ( $argument | Get-Member -Name label -MemberType Properties)) { throw "XML Element specified does not contain a label property. Specify a valid Edge Services Gateway Interface object." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property. Specify a valid Edge Services Gateway Interface object." } } else { throw "Specify a valid Edge Services Gateway Interface object." } } else { throw "Specify a valid Edge Services Gateway Interface object." } $true } Function ValidateLogicalRouterInterface { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Accepts an interface Object. if ($argument -is [System.Xml.XmlElement] ) { If ( $argument | Get-Member -Name index -MemberType Properties ) { #Looks like an interface object if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property. Specify a valid Logical Router Interface object" } if ( -not ( $argument | Get-Member -Name label -MemberType Properties)) { throw "XML Element specified does not contain a label property. Specify a valid Logical Router Interface object" } if ( -not ( $argument | Get-Member -Name logicalRouterId -MemberType Properties)) { throw "XML Element specified does not contain an logicalRouterId property. Specify a valid Logical Router Interface object" } } else { throw "Specify a valid Logical Router Interface object." } } else { throw "Specify a valid Logical Router Interface object." } $true } Function ValidateEdgeSubInterface { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Accepts a Subinterface Object. if ($argument -is [System.Xml.XmlElement] ) { If ( $argument | Get-Member -Name vnicId -MemberType Properties ) { #Looks like a Subinterface object if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain a edgeId property." } if ( -not ( $argument | Get-Member -Name vnicId -MemberType Properties)) { throw "XML Element specified does not contain a vnicId property." } if ( -not ( $argument | Get-Member -Name index -MemberType Properties)) { throw "XML Element specified does not contain an index property." } if ( -not ( $argument | Get-Member -Name label -MemberType Properties)) { throw "XML Element specified does not contain a label property." } } else { throw "Object on pipeline is not a SubInterface object." } } else { throw "Pipeline object was not a SubInterface object." } $true } Function ValidateEdgeNat { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an EdgeNAT element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name version -MemberType Properties)) { throw "XML Element specified does not contain an version property." } if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } if ( -not ( $argument | Get-Member -Name natRules -MemberType Properties)) { throw "XML Element specified does not contain a natRules property." } $true } else { throw "Specify a valid EdgeNat object." } } Function ValidateEdgeNatRule { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an EdgeNAT element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name ruleId -MemberType Properties)) { throw "XML Element specified does not contain a ruleId property. Specify a valid EdgeNatRule object." } if ( -not ( $argument | Get-Member -Name ruleType -MemberType Properties)) { throw "XML Element specified does not contain a ruleType property. Specify a valid EdgeNatRule object." } if ( -not ( $argument | Get-Member -Name action -MemberType Properties)) { throw "XML Element specified does not contain an action property. Specify a valid EdgeNatRule object." } if ( -not ( $argument | Get-Member -Name vnic -MemberType Properties)) { throw "XML Element specified does not contain a vnic property. Specify a valid EdgeNatRule object." } if ( -not ( $argument | Get-Member -Name translatedAddress -MemberType Properties)) { throw "XML Element specified does not contain a translatedAddress property. Specify a valid EdgeNatRule object." } if ( -not ( $argument | Get-Member -Name originalAddress -MemberType Properties)) { throw "XML Element specified does not contain an originalAddress property. Specify a valid EdgeNatRule object." } if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property. Specify a valid EdgeNatRule object." } $true } else { throw "Specify a valid EdgeNatRule object." } } Function ValidateEdgeFw { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an EdgeFW element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property. Specify a valid Edge Firewall object." } if ( -not ( $argument | Get-Member -Name globalConfig -MemberType Properties)) { throw "XML Element specified does not contain a globalConfig property. Specify a valid Edge Firewall object." } if ( -not ( $argument | Get-Member -Name defaultPolicy -MemberType Properties)) { throw "XML Element specified does not contain a defaultPolicy property. Specify a valid Edge Firewall object." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property. Specify a valid Edge Firewall object." } $true } else { throw "Specify a valid Edge Firewall object." } } Function ValidateEdgeFwRule { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an EdgeFWRule element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name id -MemberType Properties)) { throw "XML Element specified does not contain an id property. Specify a valid Edge FirewallRule object." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an EdgeId property. Specify a valid Edge FirewallRule object." } if ( -not ( $argument | Get-Member -Name ruleType -MemberType Properties)) { throw "XML Element specified does not contain a ruleType property. Specify a valid Edge FirewallRule object." } if ( -not ( $argument | Get-Member -Name action -MemberType Properties)) { throw "XML Element specified does not contain an action property. Specify a valid Edge FirewallRule object." } $true } else { throw "Specify a valid Edge FirewallRule object." } } Function ValidateEdgeSslVpn { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property." } if ( -not ( $argument | Get-Member -Name logging -MemberType Properties)) { throw "XML Element specified does not contain a logging property." } if ( -not ( $argument | Get-Member -Name advancedConfig -MemberType Properties)) { throw "XML Element specified does not contain an advancedConfig property." } if ( -not ( $argument | Get-Member -Name clientConfiguration -MemberType Properties)) { throw "XML Element specified does not contain a clientConfiguration property." } if ( -not ( $argument | Get-Member -Name layoutConfiguration -MemberType Properties)) { throw "XML Element specified does not contain a layoutConfiguration property." } if ( -not ( $argument | Get-Member -Name authenticationConfiguration -MemberType Properties)) { throw "XML Element specified does not contain a authenticationConfiguration property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid Edge SSL VPN object." } } Function ValidateEdgeCsr { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name subject -MemberType Properties)) { throw "XML Element specified does not contain a subject property." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property." } if ( -not ( $argument | Get-Member -Name algorithm -MemberType Properties)) { throw "XML Element specified does not contain an algorithm property." } if ( -not ( $argument | Get-Member -Name keysize -MemberType Properties)) { throw "XML Element specified does not contain a keysize property." } $true } else { throw "Specify a valid Edge CSR object." } } Function ValidateEdgeCertificate { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name issuerCn -MemberType Properties)) { throw "XML Element specified does not contain an issuerCn property." } if ( -not ( $argument | Get-Member -Name subjectCn -MemberType Properties)) { throw "XML Element specified does not contain a subjectCn property." } if ( -not ( $argument | Get-Member -Name certificateType -MemberType Properties)) { throw "XML Element specified does not contain a certificateType property." } if ( -not ( $argument | Get-Member -Name x509Certificate -MemberType Properties)) { throw "XML Element specified does not contain an x509Certificate property." } $true } else { throw "Specify a valid Edge Certificate object." } } Function ValidateEdgeSslVpnUser { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "XML Element specified does not contain an objectId property." } if ( -not ( $argument | Get-Member -Name userId -MemberType Properties)) { throw "XML Element specified does not contain a userId property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeID property." } $true } else { throw "Specify a valid Edge SSL VPN User object." } } Function ValidateEdgeSslVpnIpPool { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "XML Element specified does not contain an objectId property." } if ( -not ( $argument | Get-Member -Name ipRange -MemberType Properties)) { throw "XML Element specified does not contain a userId property." } if ( -not ( $argument | Get-Member -Name netmask -MemberType Properties)) { throw "XML Element specified does not contain a netmask property." } if ( -not ( $argument | Get-Member -Name gateway -MemberType Properties)) { throw "XML Element specified does not contain a gateway property." } if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeID property." } $true } else { throw "Specify a valid Edge SSL VPN Ip Pool object." } } Function ValidateEdgeSslVpnPrivateNetwork { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "XML Element specified does not contain an objectId property." } if ( -not ( $argument | Get-Member -Name network -MemberType Properties)) { throw "XML Element specified does not contain a network property." } if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeID property." } $true } else { throw "Specify a valid Edge SSL VPN Private Network object." } } Function ValidateEdgeSslVpnClientPackage { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "XML Element specified does not contain an objectId property." } if ( -not ( $argument | Get-Member -Name profileName -MemberType Properties)) { throw "XML Element specified does not contain a profileName property." } if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeID property." } $true } else { throw "Specify a valid Edge SSL VPN Client Installation Package object." } } Function ValidateSecurityGroupMember { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Populate the global membertype cache if not already done #Using the API rather than hardcoding incase this changes with versions of NSX if ( -not (Test-Path Variable:\NsxMemberTypes) ) { $script:NsxMemberTypes = Get-NsxSecurityGroupMemberTypes } #check if we are valid type if ( ($argument -is [string]) -and ($argument -match "^vm-\d+$|^resgroup-\d+$|^dvportgroup-\d+$|^directory_group-\d+$|^domain-c\d+$" )) { #argument is moref string and refers to vm, resource pool, dvportgroup, directory group or cluster. $true } elseif ( ($argument -is [string]) -and ( $NsxMemberTypes -contains ($argument -replace "-\d+$"))) { #Argument is objectid and matches a recognised NSX SG membertype $true } elseif ( ($argument -is [string] ) -and ( [guid]::tryparse(($argument -replace ".\d{3}$", ""), [ref][guid]::Empty)) ) { #Argument is vNIC as object ID. $true } elseif ( $argument -is [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop] ) { #Argument is a NIC object. $true } elseif (($argument -is [VMware.VimAutomation.ViCore.Interop.V1.VIObjectInterop]) -and ( $NsxMemberTypes -contains $argument.ExtensionData.MoRef.Type)) { #Argument is a VI ob ject and matches a recognised NSX SG member type $true } elseif ($argument -is [System.Xml.XmlElement]) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "Member is not a supported type. Specify an object of type $($NsxMemberTypes -join ",")." } if ( -not ( $argument | Get-Member -Name objectTypeName -MemberType Properties)) { throw "Member is not a supported type. Specify an object of type $($NsxMemberTypes -join ",")." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "Member is not a supported type. Specify an object of type $($NsxMemberTypes -join ",")." } if ( $NsxMemberTypes -notcontains $argument.objectTypeName) { throw "Member is not a supported type. Specify an object of type $($NsxMemberTypes -join ",")." } $true } else { throw "Member is not a supported type. Specify an object of type $($NsxMemberTypes -join ",")." } } Function ValidateIPHost { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ( ( $argument -as [ipaddress] ) -or ( ( ValidateIPPrefix $argument ) -and ($argument -match '^(\d{1,3}\.){3}\d{1,3}\/32\s*$') ) ) { $true } } Function ValidateIPRange { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ( ($argument -as [string]) -and ($argument -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\-\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$") ) { $true } } Function ValidateIPPrefix { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ( ($argument -as [string]) -and ($argument -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\/([0-9]|[1-2][0-9]|3[0-2])?$") ) { $true } } Function ValidateFirewallRuleSourceDest { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Same requirements for SG membership except for bare IPAddress. if ( $argument -as [ipaddress] ) { $true } elseif ( ValidateIPRange -argument $argument ) { $true } elseif ( ValidateIPPrefix -argument $argument ) { $true } else { ValidateSecurityGroupMember $argument } } Function ValidateFirewallRule { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like a DFW rule if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name id -MemberType Properties )) { throw "Specified firewall rule XML element does not contain an id property." } if ( -not ( $argument | Get-Member -Name action -MemberType Properties)) { throw "Specified firewall rule XML element does not contain an action property." } if ( -not ( $argument | Get-Member -Name appliedToList -MemberType Properties)) { throw "Specified firewall rule XML element does not contain an appliedToList property." } #Validate that the rule has a parent node that we can use to update it if required. try { $ParentSection = Invoke-XpathQuery -query "parent::section" -QueryMethod SelectSingleNode -Node $argument $null = $Parentsection.HasAttribute("id") -as [int] $null = $argument.HasAttribute("id") } catch { Throw "Unable to retrieve rule and section details from the specified Firewall Rule. Specify a valid rule and try again." } $true } else { throw "Argument must be a firewall rule XML element as returned by Get-NsxFirewallRule" } } Function ValidateFirewallRuleMember { #Distinct from ValidateFirewallRuleMemberObject in that it checks for an arg that is a valid firewallrule member object, OR a string to match against the value of one. Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Same requirements for Firewall Rule SourceDest except for string match on name as well. If ( $argument -is [string] ) { $True } else { ValidateFirewallRuleSourceDest -argument $argument } } Function ValidateFirewallRuleMemberObject { #Distinct from ValidateFirewallRuleMember in that it checks for an arg that looks like the appropriate return object from get-nsxfirewallrulemember. Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Same requirements for Firewall Rule SourceDest except for string match on name as well. If ( $argument -is [pscustomobject] ) { if ( -not ( $argument | Get-Member -Name RuleId -MemberType Properties)) { throw "Specified argument is not a valid FirewallRuleMember object." } if ( -not ( $argument | Get-Member -Name SectionId -MemberType Properties)) { throw "Specified argument is not a valid FirewallRuleMember object." } if ( -not ( $argument | Get-Member -Name MemberType -MemberType Properties)) { throw "Specified argument is not a valid FirewallRuleMember object." } if ( -not ( $argument | Get-Member -Name Name -MemberType Properties)) { throw "Specified argument is not a valid FirewallRuleMember object." } if ( -not ( $argument | Get-Member -Name Value -MemberType Properties)) { throw "Specified argument is not a valid FirewallRuleMember object." } if ( -not ( $argument | Get-Member -Name Type -MemberType Properties)) { throw "Specified argument is not a valid FirewallRuleMember object." } $true } else { throw "Specified argument is not a valid FirewallRuleMember object." } } Function ValidateServiceGroup { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ( $argument -is [system.xml.xmlelement] ) { if ( -not ($argument | Get-Member -MemberType Property -Name objectId )) { throw "Invalid service group specified" } if ( -not ($argument | Get-Member -MemberType Property -Name objectTypeName )) { throw "Invalid service group specified" } if ( -not ($argument.objectTypeName -eq "ApplicationGroup")) { throw "Invalid service group specified" } $true } else { throw "Invalid Service Group specified" } } Function ValidateService { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ( $argument -is [system.xml.xmlelement] ) { if ( -not ($argument | Get-Member -MemberType Property -Name objectId )) { throw "Invalid service specified" } if ( -not ($argument | Get-Member -MemberType Property -Name objectTypeName )) { throw "Invalid service specified" } if ( -not ($argument.objectTypeName -eq "Application")) { throw "Invalid service specified" } $true } else { throw "Invalid Service specified" } } Function ValidateServiceOrServiceGroup { Param ( [Parameter (Mandatory = $true)] [object]$argument ) try { ValidateService -argument $argument } catch { try { ValidateServiceGroup -argument $argument } catch { throw "Invalid Service or Service Group specific" } } $true } Function ValidateFirewallRuleService { Param ( [Parameter (Mandatory = $true)] [object]$argument ) switch ($argument) { # Testing to see if a raw protocol/port has been provided. { $argument -is [string] } { # Now we check to see that the protocol provided is valid. if ($argument -match "/") { $exploded = $argument -split "/" if ( -not ($Script:AllValidServices -contains $exploded[0] ) ) { throw "Invalid protocol specified" } } elseif ( $Script:AllValidServices -notcontains $argument ) { throw "Invalid protocol specified" } $true break } # If an single xml element object or a collection of objects have been provide, # then we run it through validation to stop doing stupid stuff like trying to pass # a logical switch or IP Set through to here. { ($argument -is [System.Xml.XmlElement]) -or ($argument -is [System.Object]) } { foreach ( $item in $argument ) { try { ValidateService -argument $item } catch { try { ValidateServiceGroup -argument $item } catch { throw "Invalid Service or Service Group specified" } } } $true break } } } Function ValidateEdgeFirewallRuleService { Param ( [Parameter (Mandatory = $true)] [object]$argument ) switch ($argument) { # Testing to see if a raw protocol/port has been provided. { $argument -is [string] } { ## NB : Need to populate AllValidEdgeServices, and I havent yet found how to get this list. ## In mean time, we will rely on the API pushing back in event of invalid service being specified by user. # Now we check to see that the protocol provided is valid. # if ($argument -match "/") { # $exploded = $argument -split "/" # if ( -not ($Script:AllValidEdgeServices -contains $exploded[0] ) ) { # throw "Invalid protocol specified" # } # } elseif ( $Script:AllValidEdgeServices -notcontains $argument ) { # throw "Invalid protocol specified" # } $true break } # If an single xml element object or a collection of objects have been provide, # then we run it through validation to stop doing stupid stuff like trying to pass # a logical switch or IP Set through to here. { ($argument -is [System.Xml.XmlElement]) -or ($argument -is [System.Object]) } { foreach ( $item in $argument ) { try { ValidateService -argument $item } catch { try { ValidateServiceGroup -argument $item } catch { throw "Invalid Service or Service Group specified" } } } $true break } } } Function ValidateFirewallAppliedTo { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check types first if (-not ( ($argument -is [System.Xml.XmlElement]) -or ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop] ) -or ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Inventory.DatacenterInterop] ) -or ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VMHostInterop] ) -or ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Host.Networking.VirtualPortGroupBaseInterop] ) -or ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ResourcePoolInterop] ) -or ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop] ) -or ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop] ))) { throw "$($_.gettype()) is not a supported type. Specify a Datacenter, Cluster, Host ` DistributedPortGroup, PortGroup, ResourcePool, VirtualMachine, NetworkAdapter, ` IPSet, SecurityGroup, Logical Switch or Edge object." } else { #Check if we have an ID property if ($argument -is [System.Xml.XmlElement] ) { if ( $argument | Get-Member -Name edgeSummary ) { #Looks like an Edge, get the summary details... I KNEW this would come in handy when I wrote the Get-NSxEdge cmdlet... FIGJAM... $argument = $argument.edgeSummary } if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "XML Element specified does not contain an objectId property." } if ( -not ( $argument | Get-Member -Name objectTypeName -MemberType Properties)) { throw "XML Element specified does not contain a type property." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property." } switch ($argument.objectTypeName) { "IPSet" {} "SecurityGroup" {} "VirtualWire" {} "Edge" {} default { throw "AppliedTo is not a supported type. Specify a Datacenter, Cluster, Host, ` DistributedPortGroup, PortGroup, ResourcePool, VirtualMachine, NetworkAdapter, ` IPSet, SecurityGroup, Logical Switch or Edge object." } } } } $true } Function ValidateLoadBalancer { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an LB element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name version -MemberType Properties)) { throw "XML Element specified does not contain an version property." } if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid LoadBalancer object." } } Function ValidateLoadBalancerMonitor { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an LB monitor element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name monitorId -MemberType Properties)) { throw "XML Element specified does not contain a version property." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property." } if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "XML Element specified does not contain a type property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid LoadBalancer Monitor object." } } Function ValidateLoadBalancerVip { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an LB monitor element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name virtualServerId -MemberType Properties)) { throw "XML Element specified does not contain a virtualServerId property." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property." } if ( -not ( $argument | Get-Member -Name ipAddress -MemberType Properties)) { throw "XML Element specified does not contain an ipAddress property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid LoadBalancer VIP object." } } Function ValidateLoadBalancerMemberSpec { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property. Create with New-NsxLoadbalancerMemberSpec" } if ( -not ( $argument | Get-Member -Name ipAddress -MemberType Properties)) { throw "XML Element specified does not contain an ipAddress property. Create with New-NsxLoadbalancerMemberSpec" } if ( -not ( $argument | Get-Member -Name weight -MemberType Properties)) { throw "XML Element specified does not contain a weight property. Create with New-NsxLoadbalancerMemberSpec" } if ( -not ( $argument | Get-Member -Name minConn -MemberType Properties)) { throw "XML Element specified does not contain a minConn property. Create with New-NsxLoadbalancerMemberSpec" } if ( -not ( $argument | Get-Member -Name maxConn -MemberType Properties)) { throw "XML Element specified does not contain a maxConn property. Create with New-NsxLoadbalancerMemberSpec" } $true } else { throw "Specify a valid Member Spec object as created by New-NsxLoadBalancerMemberSpec." } } Function ValidateLoadBalancerApplicationProfile { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an LB applicationProfile element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name applicationProfileId -MemberType Properties)) { throw "XML Element specified does not contain an applicationProfileId property." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property." } if ( -not ( $argument | Get-Member -Name template -MemberType Properties)) { throw "XML Element specified does not contain a template property." } $True } else { throw "Specify a valid LoadBalancer Application Profile object." } } Function ValidateLoadBalancerPool { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an LB pool element if ($_ -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name poolId -MemberType Properties)) { throw "XML Element specified does not contain an poolId property." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property." } $True } else { throw "Specify a valid LoadBalancer Pool object." } } Function ValidateLoadBalancerPoolMember { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an LB pool element if ($_ -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name poolId -MemberType Properties)) { throw "XML Element specified does not contain an poolId property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } if ( -not ( $argument | Get-Member -Name ipAddress -MemberType Properties)) { if ( -not ( $argument | Get-Member -Name groupingObjectId -MemberType Properties ) ) { throw "XML Element specified does not contain an ipAddress property." } } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain a name property." } $True } else { throw "Specify a valid LoadBalancer Pool Member object." } } Function ValidateSecurityGroup { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like Security Tag element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "XML Element specified does not contain an objectId property. Specify a valid Security Group object." } if ( -not ( $argument | Get-Member -Name Name -MemberType Properties)) { throw "XML Element specified does not contain a Name property. Specify a valid Security Group object." } if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "XML Element specified does not contain a type property. Specify a valid Security Group object." } if ( -not ( $argument.type.typeName -eq "SecurityGroup" )) { throw "XML Element specified is not of the correct type. Specify a valid Security Group object." } $True } else { throw "Specify a valid Security Group object." } } Function ValidateSPFirewallSrcDest { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like Security Tag element if ( $argument -is [string] ) { if ($argument -notmatch "Any|PoliciesSecurityGroup" ) { throw "Specify 'Any', 'PoliciesSecurityGroup' or a valid PowerNSx SecurityGroup object" } $true } else { ValidateSecurityGroup $argument } } Function ValidateSecPolFwSpec { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like Security Tag element if ( $argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name Name -MemberType Properties)) { Throw "Specify a valid Security Policy Firewall Spec object as created by New-NsxSecurityPolicyFirewallRuleSpec." } if ( -not ( $argument | Get-Member -Name action -MemberType Properties)) { Throw "Specify a valid Security Policy Firewall Spec object as created by New-NsxSecurityPolicyFirewallRuleSpec." } if ( -not ( $argument | Get-Member -Name isEnabled -MemberType Properties)) { Throw "Specify a valid Security Policy Firewall Spec object as created by New-NsxSecurityPolicyFirewallRuleSpec." } if ( -not ( $argument | Get-Member -Name 'class' -MemberType Properties)) { Throw "Specify a valid Security Policy Firewall Spec object as created by New-NsxSecurityPolicyFirewallRuleSpec." } if ( -not ( $argument.class -eq "firewallSecurityAction" )) { Throw "Specify a valid Security Policy Firewall Spec object as created by New-NsxSecurityPolicyFirewallRuleSpec." } $true } else { Throw "Specify a valid Security Policy Firewall Spec object as created by New-NsxSecurityPolicyFirewallRuleSpec." } } Function ValidateSecPolGiSpec { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like Security Tag element if ( $argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name isEnabled -MemberType Properties)) { Throw "Specify a valid Security Policy Guest Introspection Spec object as created by New-NsxSecurityPolicyGuestIntrospectionSpec." } if ( -not ( $argument | Get-Member -Name 'class' -MemberType Properties)) { Throw "Specify a valid Security Policy Guest Introspection Spec object as created by New-NsxSecurityPolicyGuestIntrospectionSpec." } if ( -not ( $argument.class -eq "endpointSecurityAction" )) { Throw "Specify a valid Security Policy Guest Introspection Spec object as created by New-NsxSecurityPolicyGuestIntrospectionSpec." } $true } else { Throw "Specify a valid Security Policy Guest Introspection Spec object as created by New-NsxSecurityPolicyGuestIntrospectionSpec." } } Function ValidateSecPolNiSpec { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like Security Tag element if ( $argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name isEnabled -MemberType Properties)) { Throw "Specify a valid Security Policy Network Introspection Spec object as created by New-NsxSecurityPolicyGuestIntrospectionSpec." } if ( -not ( $argument | Get-Member -Name 'class' -MemberType Properties)) { Throw "Specify a valid Security Policy Network Introspection Spec object as created by New-NsxSecurityPolicyGuestIntrospectionSpec." } if ( -not ( $argument.class -eq "trafficSteeringSecurityAction" )) { Throw "Specify a valid Security Policy Network Introspection Spec object as created by New-NsxSecurityPolicyGuestIntrospectionSpec." } $true } else { Throw "Specify a valid Security Policy Network Introspection Spec object as created by New-NsxSecurityPolicyGuestIntrospectionSpec." } } Function ValidateSecurityTag { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like Security Tag element if ($_ -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "XML Element specified does not contain an objectId property." } if ( -not ( $argument | Get-Member -Name Name -MemberType Properties)) { throw "XML Element specified does not contain a Name property." } if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "XML Element specified does not contain a type property." } if ( -not ( $argument.Type.TypeName -eq 'SecurityTag' )) { throw "XML Element specifies a type other than SecurityTag." } $True } else { throw "Specify a valid Security Tag object." } } Function ValidateSpoofguardPolicy { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like Security Tag element if ($_ -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name policyId -MemberType Properties)) { throw "XML Element specified does not contain an policyId property." } if ( -not ( $argument | Get-Member -Name Name -MemberType Properties)) { throw "XML Element specified does not contain a Name property." } if ( -not ( $argument | Get-Member -Name operationMode -MemberType Properties)) { throw "XML Element specified does not contain an OperationMode property." } if ( -not ( $argument | Get-Member -Name defaultPolicy -MemberType Properties)) { throw "XML Element specified does not contain a defaultPolicy property." } $True } else { throw "Specify a valid Spoofguard Policy object." } } Function ValidateSpoofguardNic { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like Security Tag element if ($_ -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name id -MemberType Properties)) { throw "XML Element specified does not contain an id property." } if ( -not ( $argument | Get-Member -Name vnicUuid -MemberType Properties)) { throw "XML Element specified does not contain a vnicUuid property." } if ( -not ( $argument | Get-Member -Name policyId -MemberType Properties)) { throw "XML Element specified does not contain a policyId property." } $True } else { throw "Specify a valid Spoofguard Nic object." } } Function ValidateVirtualMachine { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if (-not ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop] )) { throw "Object is not a supported type. Specify a VirtualMachine object." } $true } Function ValidateVirtualMachineOrTemplate { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ( -not ( ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop]) -or ($argument -is [VMware.VimAutomation.ViCore.Interop.V1.Inventory.TemplateInterop]))) { throw "Object is not a supported type. Specify a VirtualMachine or Template object." } $true } Function ValidateTagAssignment { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like Security Tag Assignmenbt if ($argument -is [PSCustomObject] ) { if ( -not ( $argument | Get-Member -Name SecurityTag -MemberType Properties)) { throw "Specify a valid Security Tag Assignment. Specified object does not contain a SecurityTag property object." } if ( -not ( $argument | Get-Member -Name VirtualMachine -MemberType Properties)) { throw "Specify a valid Security Tag Assignment. Specified object does not contain a VirtualMachine property object." } if ( -not ( $argument.SecurityTag -is [System.Xml.XmlElement] )) { throw "Specify a valid Security Tag Assignment." } if ( -not ( $argument.VirtualMachine -is [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop])) { throw "Specify a valid Security Tag Assignment." } $True } else { throw "Specify a valid Security Tag Assignment." } } Function ValidateFwSourceDestFilter { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ( ($argument -as [ipaddress]) -or ($argument -as [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop]) ) { $true } elseif ( ($argument -as [string]) -and ($argument -match "^vm-\d+$") ) { $True } else { throw "Source or Destination Filter must be an IPAddress, VM object, or vmmoid" } } Function ValidateController { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name id -MemberType Properties)) { throw "Specify a valid Controller." } if ( -not ( $argument.id -match "controller-\d+")) { throw "Specify a valid Controller." } $true } else { throw "Specify a valid Controller." } } Function ValidateSecondaryManager { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name uuid -MemberType Properties)) { throw "Specify a valid secondary NSX manager." } if ( -not ( $argument | Get-Member -Name nsxManagerIp -MemberType Properties)) { throw "Specify a valid secondary NSX manager." } if ( -not ( $argument | Get-Member -Name isPrimary -MemberType Properties)) { throw "Specify a valid secondary NSX manager." } if ( $argument.isPrimary -eq 'true') { throw "The specified manager has the primary role. Specify a valid secondary NSX manager." } $true } else { throw "Specify a valid secondary NSX manager." } } Function ValidateDynamicCriteriaSpec { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name key -MemberType Properties)) { throw "XML Element specified does not contain a key property. Specify a valid Dynamic Criteria Spec." } if ( -not ( $argument | Get-Member -Name criteria -MemberType Properties)) { throw "XML Element specified does not contain a criteria property. Specify a valid Dynamic Criteria Spec." } if ( -not ( $argument | Get-Member -Name value -MemberType Properties)) { throw "XML Element specified does not contain a value property. Specify a valid Dynamic Criteria Spec." } $true } else { throw "Specify a valid Dynamic Criteria Spec." } } Function ValidateDynamicMemberSet { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [PSCustomObject] ) { if ( -not ( $argument | Get-Member -Name index -MemberType Properties)) { throw "Object specified does not contain an index property. Specify a valid Dynamic Member Set." } if ( -not ( $argument | Get-Member -Name SecurityGroupName -MemberType Properties)) { throw "Object specified does not contain a SecurityGroup Name property. Specify a valid Dynamic Member Set." } if ( -not ( $argument | Get-Member -Name SecurityGroup -MemberType Properties)) { throw "Object specified does not contain a SecurityGroup property. Specify a valid Dynamic Member Set." } if ( -not ( $argument | Get-Member -Name criteria -MemberType Properties)) { throw "Object specified does not contain a criteria property. Specify a valid Dynamic Member Set." } if ( -not ( $argument | Get-Member -Name SetOperator -MemberType Properties)) { throw "Object specified does not contain a Set Operator property. Specify a valid Dynamic Member Set." } $true } else { throw "Specify a valid Dynamic Member Set." } } Function ValidateDynamicCriteria { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [PSCustomObject] ) { if ( -not ( $argument | Get-Member -Name index -MemberType Properties)) { throw "Object specified does not contain an index property. Specify a valid Dynamic Criteria object." } if ( -not ( $argument | Get-Member -Name MemberSetIndex -MemberType Properties)) { throw "Object specified does not contain an index property. Specify a valid Dynamic Criteria object." } if ( -not ( $argument | Get-Member -Name SecurityGroupName -MemberType Properties)) { throw "Object specified does not contain a SecurityGroup Name property. Specify a valid Dynamic Criteria object." } if ( -not ( $argument | Get-Member -Name SecurityGroup -MemberType Properties)) { throw "Object specified does not contain a SecurityGroup property. Specify a valid Dynamic Criteria object." } if ( -not ( $argument | Get-Member -Name key -MemberType Properties)) { throw "Object specified does not contain a key property. Specify a valid Dynamic Criteria object." } if ( -not ( $argument | Get-Member -Name condition -MemberType Properties)) { throw "Object specified does not contain a condition property. Specify a valid Dynamic Criteria object." } if ( -not ( $argument | Get-Member -Name key -MemberType Properties)) { throw "Object specified does not contain a value property. Specify a valid Dynamic Criteria object." } $true } else { throw "Specify a valid Dynamic Criteria object." } } Function ValidateServiceDefinition { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "Object specified contains no objectId property. Specify a valid Service Definition object." } if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "Object specified contains no type property. Specify a valid Service Definition object." } if ( -not ( $argument.type.typename -eq "Service" )) { throw "Object specified is of the wrong type $($argument.type.typename). Specify a valid Service Definition object." } $true } else { throw "Specify a valid Service Definition object." } } Function ValidateServiceProfile { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "Object specified contains no objectId property. Specify a valid Service Profile object." } if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "Object specified contains no type property. Specify a valid Service Profile object." } if ( -not ( $argument.type.typename -eq "ServiceProfile" )) { throw "Object specified is of the wrong type $($argument.type.typename). Specify a valid Service Profile object." } $true } else { throw "Specify a valid Service Profile object." } } Function ValidateSecurityPolicy { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "Object specified contains no objectId property. Specify a valid Security Policy object." } if ( -not ( $argument | Get-Member -Name type -MemberType Properties)) { throw "Object specified contains no type property. Specify a valid Security Policy object." } if ( -not ( $argument.type.typename -eq "Policy" )) { throw "Object specified is of the wrong type $($argument.type.typename). Specify a valid Security Policy object." } $true } else { throw "Specify a valid Security Policy object." } } Function ValidateSecPolRule { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name objectId -MemberType Properties)) { throw "Object specified contains no objectId property. Specify a valid Security Policy Rule object." } if ( -not ( $argument | Get-Member -Name class -MemberType Properties)) { throw "Object specified contains no class attribute. Specify a valid Security Policy Rule object." } if ( -not ( ($argument.class -eq "firewallSecurityAction") -or ($argument.class -eq "endpointSecurityAction") -or ($argument.class -eq "trafficSteeringSecurityAction") )) { throw "Object specified is of the wrong class $($argument.class). Specify a valid Security Policy Rule object." } #Because we frequently rely on the parent node relationship to do editing of the parent policy xml, we have to make sure user hasnt concocted a rule out of thin air. try { $ParentPolicyObjectId = $argument.ParentNode.ParentNode.objectId if ( -not $ParentPolicyObjectId ) { throw "No parent node objectId found." } } catch { throw "An invalid policy rule was specified. Ensure a policy rule as retrieved by Get-NsxSecurityPolicyRule is specified. $_" } $true } else { throw "Specify a valid Security Policy Rule object." } } function ValidateFirewallDraft { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name id -MemberType Properties)) { throw "XML Element specified does not contain an id property." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain an name property." } if ( -not ( $argument | Get-Member -Name timestamp -MemberType Properties)) { throw "XML Element specified does not contain an timestamp property." } if ( -not ( $argument | Get-Member -Name preserve -MemberType Properties)) { throw "XML Element specified does not contain an preserve property." } if ( -not ( $argument | Get-Member -Name user -MemberType Properties)) { throw "XML Element specified does not contain an user property." } if ( -not ( $argument | Get-Member -Name mode -MemberType Properties)) { throw "XML Element specified does not contain an mode property." } $true } else { throw "Specify a valid Saved Distributed Firewall Configuration object." } } function ValidateFirewallSavedConfiguration { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name id -MemberType Properties)) { throw "XML Element specified does not contain an id property." } if ( -not ( $argument | Get-Member -Name name -MemberType Properties)) { throw "XML Element specified does not contain an name property." } if ( -not ( $argument | Get-Member -Name timestamp -MemberType Properties)) { throw "XML Element specified does not contain an timestamp property." } if ( -not ( $argument | Get-Member -Name preserve -MemberType Properties)) { throw "XML Element specified does not contain an preserve property." } if ( -not ( $argument | Get-Member -Name user -MemberType Properties)) { throw "XML Element specified does not contain an user property." } if ( -not ( $argument | Get-Member -Name mode -MemberType Properties)) { throw "XML Element specified does not contain an mode property." } if ( -not ( $argument | Get-Member -Name config -MemberType Properties)) { throw "XML Element specified does not contain an config property." } $true } else { throw "Specify a valid Saved Distributed Firewall Configuration object." } } Function ValidateEdgeDns { Param ( [Parameter (Mandatory = $true)] [object]$argument ) #Check if it looks like an Edge routing element if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property." } if ( -not ( $argument | Get-Member -Name cacheSize -MemberType Properties)) { throw "XML Element specified does not contain an cacheSize property." } if ( -not ( $argument | Get-Member -Name listeners -MemberType Properties)) { throw "XML Element specified does not contain an listeners property." } if ( -not ( $argument | Get-Member -Name dnsViews -MemberType Properties)) { throw "XML Element specified does not contain an dnsViews property." } if ( -not ( $argument | Get-Member -Name logging -MemberType Properties)) { throw "XML Element specified does not contain a logging property." } if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } $true } else { throw "Specify a valid Edge DNS object." } } function ValidateCliSettings { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name edgeId -MemberType Properties)) { throw "XML Element specified does not contain an edgeId property." } if ( -not ( $argument | Get-Member -Name remoteAccess -MemberType Properties)) { throw "XML Element specified does not contain an remoteAccess property." } if ( -not ( $argument | Get-Member -Name sshLoginBannerText -MemberType Properties)) { throw "XML Element specified does not contain an sshLoginBannerText property." } if ( -not ( $argument | Get-Member -Name passwordExpiry -MemberType Properties)) { throw "XML Element specified does not contain an passwordExpiry property." } $true } else { throw "Specify a valid CliSettings Configuration object." } } Function ValidateIPsec { Param ( [Parameter (Mandatory = $true)] [object]$argument ) if ($argument -is [System.Xml.XmlElement] ) { if ( -not ( $argument | Get-Member -Name enabled -MemberType Properties)) { throw "XML Element specified does not contain an enabled property." } if ( -not ( $argument | Get-Member -Name logging -MemberType Properties)) { throw "XML Element specified does not contain an logging property." } if ( -not ( $argument | Get-Member -Name sites -MemberType Properties)) { throw "XML Element specified does not contain an sites property." } if ( -not ( $argument | Get-Member -Name global -MemberType Properties)) { throw "XML Element specified does not contain an global property." } $true } else { throw "Specify a valid Edge IPsec object." } } ########## ########## # Helper functions function Format-XML () { <# .SYNOPSIS Accepts a string containing valid XML tags or an XMLElement object and outputs it as a formatted string including newline and indentation of child nodes. .DESCRIPTION Valid XML returned by the NSX API is a single string with no newlines or indentation. While PowerNSX cmdlets typicallly emit an XMLElement object, which PowerShell outputs as formatted tables or lists when outputing to host, making normal human interaction easy, for output to file or debug stream, format-xml converts the API returned XML to more easily read formated XML complete with linebreaks and indentation. As a side effect, this has the added benefit of being useable as an additional format handler on the PowerShell pipeline, so rather than displaying output objects using familiar table and list output formats, the user now has the option of displaying the native XML in a human readable format. .EXAMPLE Get-NsxTransportZone | Format-Xml Displays the XMLElement object returned by Get-NsxTransportZone as formatted XML. #> #NB: Find where I got this to reference... #Shamelessly ripped from the web with some modification, useful for formatting XML output into a form that #is easily read by humans. Seriously - how is this not part of the dotnet system.xml classes? param ( [Parameter (Mandatory = $false, ValueFromPipeline = $true, Position = 1) ] [ValidateNotNullorEmpty()] #String object containing valid XML, or XMLElement or XMLDocument object $xml = "", [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] #Number of whitespace charaters to indent child nodes by when formatting [int]$indent = 2 ) begin {} process { if ( ($xml -is [System.Xml.XmlElement]) -or ( $xml -is [System.Xml.XmlDocument] ) ) { try { [xml]$_xml = $xml.OuterXml } catch { throw "Specified XML element cannot be cast to an XML document." } } elseif ( $xml -is [string] ) { try { [xml]$_xml = $xml } catch { throw "Specified string cannot be cast to an XML document." } } else { throw "Unknown data type specified as xml to Format-Xml." } $StringWriter = New-Object System.IO.StringWriter $XmlSettings = New-Object System.Xml.XmlWriterSettings $XmlSettings.Indent = $true $XmlSettings.ConformanceLevel = "fragment" $XmlSettings.CheckCharacters = $False $XmlWriter = [System.XMl.XmlWriter]::Create($StringWriter, $XmlSettings) $_xml.WriteContentTo($XmlWriter) $XmlWriter.Flush() $StringWriter.Flush() Write-Output $StringWriter.ToString() } end {} } function Export-NsxObject { <# .SYNOPSIS Accepts any XMLElement and formats it and writes it to the specified file. .DESCRIPTION Most PowerNSX Objects are internally handled as XMLELement objects. For objects (or collections of objects) that are XMLElements, this cmdlet will format them (using format-xml) and then write them to disk using the specified encoding. An export file created in this fashion can be edited by hand with complete onus on the user to ensure the XML remains well formed and continues to be a 'valid' NSX object. The intent of this cmdlet is to allow easy storage of any PowerNSX objects to disk so they can be later re-imported to use as if they had been retrieved directly from the API. It is intended to be used in conjunction with Import-NsxObject .EXAMPLE Get-NsxEdge Edge01 | Export-NsxObject -FilePath EdgeExport.xml C:\ PS>$ImportedEdge = Import-NsxObject -FilePath EdgeExport.xml Exports the XMLElement object returned by Get-NsxEdge as formatted XML to the file ExdgeExport.xml in the current directory and then imports the content of the same file and stores the XML object in the variable $ImportedEdge. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] #Cant remove without breaking backward compatibility Param( [Parameter (Mandatory = $true, ValueFromPipeline = $True)] #PowerNSX Object to be exported [System.Xml.XmlElement[]]$Object, [Parameter (Mandatory = $true, Position = 1)] #Text Encoding used in export file. [ValidateNotNullOrEmpty()] [String]$FilePath, [Parameter (Mandatory = $false)] #Encoding type used in the output file. Defaults to utf-8 as the typical encoding for xml [ValidateSet("ascii", "bigendianunicode", "default", "oem", "string", "unicode", "unknown", "utf32", "utf7", "utf8" )] $Encoding = "utf8", [Parameter (Mandatory = $False)] #Prevents overwriting an existing file. Defaults to $True [switch]$NoClobber = $True ) begin { $XmlDoc = New-Object System.Xml.XmlDocument $ExportElem = $XmlDoc.CreateElement("PowerNSXExport") } process { foreach ( $xml in $Object ) { $ExportNode = $XmlDoc.ImportNode($xml, $true) $null = $ExportElem.AppendChild($ExportNode) } } End { $ExportElem | Format-XML | Out-File -FilePath $FilePath -Encoding $Encoding -NoClobber:$NoClobber } } function Import-NsxObject { <# .SYNOPSIS Reads from the specified file and returns an XMLElement object or collection. .DESCRIPTION Most PowerNSX Objects are internally handled as XMLELement objects. For objects (or collections of objects) that are XMLElements, this cmdlet will format them (using format-xml) and then write them to disk using the specified encoding. An export file created in this fashion can be edited by hand with complete onus on the user to ensure the XML remains well formed and continues to be a 'valid' NSX object. The intent of this cmdlet is to allow easy re-import of files exported using Export-NsxObject to use as if they had been retrieved directly from the API. It is intended to be used in conjunction with Export-NsxObject .EXAMPLE Get-NsxEdge Edge01 | Export-NsxObject -FilePath EdgeExport.xml C:\ PS>$ImportedEdge = Import-NsxObject -FilePath EdgeExport.xml Exports the XMLElement object returned by Get-NsxEdge as formatted XML to the file ExdgeExport.xml in the current directory and then imports the content of the same file and stores the XML object in the variable $ImportedEdge. #> Param( [Parameter (Mandatory = $true, Position = 1)] #Text Encoding used in export file. [ValidateScript( { if ( -not (Test-Path $_)) { Throw "File not found : $_" } else { $True } } )] [String]$FilePath ) begin { $XmlDoc = New-Object System.Xml.XmlDocument try { $xmldoc.Load($FilePath) } catch { Throw "An error occured attempting to load the file $filepath. Ensure the file contains a valid PowerNSX object export and has not been modified or corrupted. $_" } if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $xmlDoc -query "/PowerNSXExport")) { Throw "The XML content in $filepath is not a valid PowerNSX export format." } $Children = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $xmldoc.PowerNSXExport -query "*") foreach ($child in $Children) { $child } } Process {} End {} } ########## ########## # Core functions function Invoke-InternalWebRequest { <# .SYNOPSIS Constructs and performs REST call to NSX API while hiding platform specific limitations. .DESCRIPTION Limitations and differences in Desktop/Core iwr have required the development of this function. It aims to be consistent with the iwr interface in as far as PowerNSX uses it, but deal with limitations that occur in the different implementations. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] #Cant remove without breaking backward compatibility param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [Uri]$Uri, [parameter(Mandatory = $true)] [ValidateSet("get", "put", "post", "delete")] [string]$Method, [parameter(Mandatory = $true)] [hashtable]$Headers = @{}, [parameter(Mandatory = $true)] [string]$ContentType, [parameter(Mandatory = $true)] [int]$TimeoutSec = 0, [parameter(Mandatory = $false)] [string]$body, [parameter(Mandatory = $false)] [switch]$SkipCertificateCheck = $true ) Write-Debug "$($MyInvocation.MyCommand.Name) : Method : $method, Content-Type : $ContentType, SkipCertificateCheck : $SkipcertificateCheck" ################### # Below removed to fix Issue #215, Remove-NsxCluster (DELETE /2.0/nwfabric/configure) sends a body with a delete. # Unknown at this point if any other NSX APIs require it, but it seems prudent to make the fix generic case for get and delete to support body # and refactor the PowerShell core specific code below that relied on this assumption. # #Validate method and body # if ((($method -eq "get") -or ($method -eq "delete")) -and $PsBoundParameters.ContainsKey('body')) { # throw "Cannot specify a body with a $method request." # } ################### #For Core, we use the httpclient dotNet classes directly. if ( $script:PNsxPSTarget -eq "Core" ) { $httpClientHandler = New-Object InternalHttpClientHandler($SkipCertificateCheck) $httpClient = New-Object System.Net.Http.Httpclient $httpClientHandler #Add any required headers. if-match in particular doesnt validate on httpclient. Using TryAddWithoutValidation to avoid exception thrown on Core. foreach ( $header in $headerDictionary.Keys) { Write-Debug "$($MyInvocation.MyCommand.Name) : Adding Header : $header, $($headerDictionary.Item($header))" $null = $httpClient.DefaultRequestHeaders.TryAddWithoutValidation( $header, $headerDictionary.item($header) ) } #Set Timeout if ( $timeout -ne 0 ) { $httpClient.Timeout = New-Object Timespan(0, 0, $TimeoutSec) } else { $httpClient.Timeout = [timespan]::MaxValue } #Encoding $UTF8 = New-Object System.Text.UTF8Encoding try { Write-Debug "$($MyInvocation.MyCommand.Name) : Calling HTTPClient SendAsync" $request = New-Object System.Net.Http.HttpRequestMessage $request.Method = $method.ToUpper() $request.RequestUri = $Uri $content = $null if ( $PSBoundParameters.ContainsKey("Body")) { $content = New-Object System.Net.Http.StringContent($body, $UTF8, $contentType) Write-Debug "$($MyInvocation.MyCommand.Name) : Content Header $($content.Headers | Out-String -Stream)" } $request.Content = $content $task = $httpClient.SendAsync($request); if (!$task.result) { throw $task.Exception } $response = $task.Result #Generate lookalike webresponseobject - caller is me, so it doesnt need to pass too close an inspection! $WebResponse = New-Object InternalWebResponse $WebResponse.StatusCode = $response.StatusCode.value__ $WebResponse.StatusDescription = $response.ReasonPhrase $WebResponse.Content = $response.Content.ReadAsStringAsync().Result #Not worrying about RawContent here, as I dont use it. #No HTML parsing done either - again - I dont use it. #No Content Length - pretty sure I dont use it ;) #Fill the headers dict foreach ( $header in $response.Headers ) { #Response header values are an array of strings - if ( @($header.Value).count -gt 1 ) { if ($header.key -eq "X-Frame-Options") { # This header, according to RFC7034 should only have a # single value. The list of possible values are: DENY, # SAMEORIGIN and ALLOW-FROM and they are meant to be # mutually exclusive. Seems as though the NSX Manager # is sending back both DENY and SAMEORIGIN. As we aren't # rendering the responses in a browser which leverages # frames/iframes, just going to set this to DENY. The # RFC states that "If a browser or plugin cannot reliably # determine whether or not the origin of the content and # the frame are the same, this MUST be treated as 'DENY'." $WebResponse.Headers.Add($header.key, "DENY") } else { $WebResponse.Headers.Add($header.key, ($Header.Value -join ', ')) } } else { $WebResponse.Headers.Add($header.key, @($Header.Value)[0]) } } #If non success status, we still throw an exception with the response object so caller can determine details. if (!$response.IsSuccessStatusCode) { $errorMessage = "NSX API response indicates failure: ({0}) - {1}." -f $response.StatusCode.value__, $response.ReasonPhrase $internalWebException = New-Object internalWebRequestException $errorMessage, $WebResponse throw $internalWebException } $WebResponse } catch [Exception] { # if ( $gettask.Exception ) { # throw $gettask.Exception # } # else { $PSCmdlet.ThrowTerminatingError($_) # } } finally { if ( Test-Path variable:httpClient ) { $httpClient.Dispose() } if ( Test-Path variable:response ) { $response.Dispose() } if ( Test-Path variable:content ) { if ( $content -ne $null ) { $content.dispose() } } } } elseif ( $script:PNsxPSTarget -eq "Desktop" ) { #For now, we continue to pass thru to the iwr cmdlet on desktop. For now... #Use splatting to build up the IWR params $iwrSplat = @{ "method" = $method; "headers" = $headerDictionary; "ContentType" = $ContentType; "uri" = $Uri; "TimeoutSec" = $TimeoutSec; "UseBasicParsing" = $True; } if ( $PsBoundParameters.ContainsKey('Body')) { $iwrsplat.Add("body", $body) } if (( -not $ValidateCertificate) -and ([System.Net.ServicePointManager]::CertificatePolicy.tostring() -ne 'TrustAllCertsPolicy')) { #allow untrusted certificate presented by the remote system to be accepted [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy } #Dont catch here - bubble exception up as there is enough in it for the caller. Invoke-WebRequest @iwrsplat } } function Invoke-NsxRestMethod { <# .SYNOPSIS Constructs and performs a valid NSX REST call. This function is DEPRECATED Use Invoke-NsxWebRequest for all new functions. .DESCRIPTION Invoke-NsxRestMethod uses either a specified connection object as returned by Connect-NsxServer, or the $DefaultNsxConnection global variable if defined to construct a REST api call to the NSX API. Invoke-NsxRestMethod constructs the appropriate request headers required by the NSX API, including authentication details (built from the connection object), required content type and includes any custom headers specified by the caller that might be required by a specific API resource, before making the rest call and returning the appropriate XML object to the caller. .EXAMPLE Invoke-NsxRestMethod -Method get -Uri "/api/2.0/vdn/scopes" Performs a 'Get' against the URI /api/2.0/vdn/scopes and returns the xml object respresenting the NSX API XML reponse. This call requires the $DefaultNsxServer variable to exist and be populated with server and authentiation details as created by Connect-NsxServer -DefaultConnection .EXAMPLE $MyConnection = Connect-NsxServer -Server OtherNsxManager -DefaultConnection:$false Invoke-NsxRestMethod -Method get -Uri "/api/2.0/vdn/scopes" -connection $MyConnection Creates a connection variable for a non default NSX server, performs a 'Get' against the URI /api/2.0/vdn/scopes and returns the xml object respresenting the NSX API XML reponse. #> [CmdletBinding(DefaultParameterSetName = "ConnectionObj")] param ( [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #PSCredential object containing authentication details to be used for connection to NSX Manager API [System.Management.Automation.PSCredential]$cred, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #NSX Manager ip address or FQDN [string]$server, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #TCP Port on -server to connect to [int]$port, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #Protocol - HTTP/HTTPS [string]$protocol, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #URI Prefix to support URI rewrite scenario [AllowEmptyString()] [string]$UriPrefix = "", [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #Validates the certificate presented by NSX Manager for HTTPS connections [bool]$ValidateCertificate, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] [Parameter (ParameterSetName = "ConnectionObj")] #REST method of call. Get, Put, Post, Delete, Patch etc [string]$method, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] [Parameter (ParameterSetName = "ConnectionObj")] #URI of resource (/api/1.0/myresource). Should not include protocol, server or port. [string]$URI, [Parameter (Mandatory = $false, ParameterSetName = "Parameter")] [Parameter (ParameterSetName = "ConnectionObj")] #Content to be sent to server when method is Put/Post/Patch [string]$body = "", [Parameter (Mandatory = $false, ParameterSetName = "ConnectionObj")] #Pre-populated connection object as returned by Connect-NsxServer [psObject]$connection, [Parameter (Mandatory = $false, ParameterSetName = "ConnectionObj")] #Hashtable collection of KV pairs representing additional headers to send to the NSX Manager during REST call [System.Collections.Hashtable]$extraheader, [Parameter (Mandatory = $false, ParameterSetName = "ConnectionObj")] #Request timeout value - passed directly to underlying invoke-restmethod call [int]$Timeout = 600 ) Write-Debug "$($MyInvocation.MyCommand.Name) : ParameterSetName : $($pscmdlet.ParameterSetName)" #System.Net.ServicePointManager class does not exist on core. if ( $script:PNsxPSTarget -eq "Desktop" ) { if (( -not $ValidateCertificate) -and ([System.Net.ServicePointManager]::CertificatePolicy.tostring() -eq 'System.Net.DefaultCertPolicy')) { #allow untrusted certificate presented by the remote system to be accepted [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy } } if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { #ensure we were either called with a connection or there is a defaultConnection (user has #called connect-nsxserver) if ( $connection -eq $null) { #Now we need to assume that defaultnsxconnection does not exist... if ( -not (Test-Path variable:global:DefaultNSXConnection) ) { throw "Not connected. Connect to NSX manager with Connect-NsxServer first." } else { Write-Debug "$($MyInvocation.MyCommand.Name) : Using default connection" $connection = $DefaultNSXConnection } } $cred = $connection.credential $server = $connection.Server $port = $connection.Port $protocol = $connection.Protocol $uriprefix = $connection.UriPrefix } $headerDictionary = @{} $base64cred = [system.convert]::ToBase64String( [system.text.encoding]::ASCII.Getbytes( "$($cred.GetNetworkCredential().username):$($cred.GetNetworkCredential().password)" ) ) $headerDictionary.add("Authorization", "Basic $Base64cred") if ( $extraHeader ) { foreach ($header in $extraHeader.GetEnumerator()) { Write-Debug "$($MyInvocation.MyCommand.Name) : Adding extra header $($header.Key ) : $($header.Value)" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) Extra Header being added to following REST call. Key: $($Header.Key), Value: $($Header.Value)" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } $headerDictionary.add($header.Key, $header.Value) } } $FullURI = "$($protocol)://$($server):$($Port)$($UriPrefix)$($URI)" Write-Debug "$($MyInvocation.MyCommand.Name) : Method: $method, URI: $FullURI, URIPrefix: $UriPrefix, Body: `n$($body | Format-XML)" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) REST Call to NSX Manager via invoke-restmethod : Method: $method, URI: $FullURI, Body: `n$($body | Format-XML)" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } #Use splatting to build up the IRM params $irmSplat = @{ "method" = $method; "headers" = $headerDictionary; "ContentType" = "application/xml"; "uri" = $FullURI; "TimeoutSec" = $Timeout #Not supported on PowerShell 3 # "UseBasicParsing" = $True } if ( $PsBoundParameters.ContainsKey('Body')) { # If there is a body specified, add it to the invoke-restmethod args... $irmSplat.Add("body", $body) } #Core (for now) uses a different mechanism to manipulating [System.Net.ServicePointManager]::CertificatePolicy if ( ($script:PNsxPSTarget -eq 'Core') -and ( -not $ValidateCertificate )) { $irmSplat.Add("SkipCertificateCheck", $true) } #do rest call try { $response = Invoke-RestMethod @irmSplat } #If its a webexception, we may have got a response from the server with more information... #Even if this happens on PoSH Core though, the ex is not a webexception and we cant get this info :( catch [System.Net.WebException] { #Check if there is a response populated in the response prop as we can return better detail. $response = $_.exception.response if ( $response ) { $responseStream = $response.GetResponseStream() $reader = New-Object system.io.streamreader($responseStream) $responseBody = $reader.readtoend() $ErrorString = "$($MyInvocation.MyCommand.Name) : The NSX API response received indicates a failure. $($response.StatusCode.value__) : $($response.StatusDescription) : Response Body: $($responseBody)" #Log the error with response detail. if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) REST Call to NSX Manager failed: $ErrorString" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } throw $ErrorString } else { #No response, log and throw the underlying ex $ErrorString = "$($MyInvocation.MyCommand.Name) : Exception occured calling invoke-restmethod. $($_.exception.tostring())" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) REST Call to NSX Manager failed: $ErrorString" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } throw $_.exception.tostring() } } catch { #Not a webexception (may be on PoSH core), log and throw the underlying ex string $ErrorString = "$($MyInvocation.MyCommand.Name) : Exception occured calling invoke-restmethod. $($_.exception.tostring())" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) REST Call to NSX Manager failed: $ErrorString" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } throw $_.exception.tostring() } switch ( $response ) { { $_ -is [xml] } { $FormattedResponse = "`n$($response.outerxml | Format-XML)" } { $_ -is [System.String] } { $FormattedResponse = $response } default { $formattedResponse = "Response type unknown" } } Write-Debug "$($MyInvocation.MyCommand.Name) : Response: $FormattedResponse" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) Response: $FormattedResponse" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } if ( $script:PNsxPSTarget -eq "Desktop" ) { # Workaround for bug in invoke-restmethod where it doesnt complete the tcp session close to our server after certain calls. # We end up with connectionlimit number of tcp sessions in close_wait and future calls die with a timeout failure. # So, we are getting and killing active sessions after each call. Not sure of performance impact as yet - to test # and probably rewrite over time to use invoke-webrequest for all calls... PiTA!!!! :| $ServicePoint = [System.Net.ServicePointManager]::FindServicePoint($FullURI) $ServicePoint.CloseConnectionGroup("") | Out-Null Write-Debug "$($MyInvocation.MyCommand.Name) : Closing connections to $FullURI." } #Return $response } function Invoke-NsxWebRequest { <# .SYNOPSIS Constructs and performs a valid NSX REST call and returns a response object including response headers. .DESCRIPTION Invoke-NsxWebRequest uses either a specified connection object as returned by Connect-NsxServer, or the $DefaultNsxConnection global variable if defined to construct a REST api call to the NSX API. Invoke-NsxWebRequest constructs the appropriate request headers required by the NSX API, including authentication details (built from the connection object), required content type and includes any custom headers specified by the caller that might be required by a specific API resource, before making the rest call and returning the resulting response object to the caller. The Response object includes the response headers unlike Invoke-NsxRestMethod. .EXAMPLE $MyConnection = Connect-NsxServer -Server OtherNsxManager -DefaultConnection:$false $response = invoke-nsxwebrequest -method "post" -uri $URI -body $body -connection $MyConnection $edgeId = $response.Headers.Location.split("/")[$response.Headers.Location.split("/").GetUpperBound(0)] Creates a connection variable for a non default NSX server, performs a 'Post' against the URI $URI and then retrieves details from the Location header included in the response object. #> [CmdletBinding(DefaultParameterSetName = "ConnectionObj")] param ( [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #PSCredential object containing authentication details to be used for connection to NSX Manager API [System.Management.Automation.PSCredential]$cred, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #NSX Manager ip address or FQDN [string]$server, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #TCP Port on -server to connect to [int]$port, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #Protocol - HTTP/HTTPS [string]$protocol, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #URI prefix to support URI rewrite scenario [AllowEmptyString()] [string]$UriPrefix = "", [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] #Validates the certificate presented by NSX Manager for HTTPS connections [bool]$ValidateCertificate, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] [Parameter (ParameterSetName = "ConnectionObj")] #REST method of call. Get, Put, Post, Delete, Patch etc [string]$method, [Parameter (Mandatory = $true, ParameterSetName = "Parameter")] [Parameter (ParameterSetName = "ConnectionObj")] #URI of resource (/api/1.0/myresource). Should not include protocol, server or port. [string]$URI, [Parameter (Mandatory = $false, ParameterSetName = "Parameter")] [Parameter (ParameterSetName = "ConnectionObj")] #Content to be sent to server when method is Put/Post/Patch [string]$body = "", [Parameter (Mandatory = $false, ParameterSetName = "ConnectionObj")] #Pre-populated connection object as returned by Connect-NsxServer [psObject]$connection, [Parameter (Mandatory = $false, ParameterSetName = "ConnectionObj")] #Hashtable collection of KV pairs representing additional headers to send to the NSX Manager during REST call [System.Collections.Hashtable]$extraheader, [Parameter (Mandatory = $false, ParameterSetName = "ConnectionObj")] #Request timeout value - passed directly to underlying invoke-restmethod call [int]$Timeout = 600 ) Write-Debug "$($MyInvocation.MyCommand.Name) : ParameterSetName : $($pscmdlet.ParameterSetName)" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { #ensure we were either called with a connection or there is a defaultConnection (user has #called connect-nsxserver) if ( $connection -eq $null) { #Now we need to assume that defaultnsxconnection does not exist... if ( -not (Test-Path variable:global:DefaultNSXConnection) ) { throw "Not connected. Connect to NSX manager with Connect-NsxServer first." } else { Write-Debug "$($MyInvocation.MyCommand.Name) : Using default connection" $connection = $DefaultNSXConnection } } $cred = $connection.credential $server = $connection.Server $port = $connection.Port $protocol = $connection.Protocol $uriprefix = $connection.UriPrefix } $headerDictionary = @{} $base64cred = [system.convert]::ToBase64String( [system.text.encoding]::ASCII.Getbytes( "$($cred.GetNetworkCredential().username):$($cred.GetNetworkCredential().password)" ) ) $headerDictionary.add("Authorization", "Basic $Base64cred") if ( $extraHeader ) { foreach ($header in $extraHeader.GetEnumerator()) { Write-Debug "$($MyInvocation.MyCommand.Name) : Adding extra header $($header.Key ) : $($header.Value)" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) Extra Header being added to following REST call. Key: $($Header.Key), Value: $($Header.Value)" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } $headerDictionary.add($header.Key, $header.Value) } } $FullURI = "$($protocol)://$($server):$($Port)$($UriPrefix)$($URI)" Write-Debug "$($MyInvocation.MyCommand.Name) : Method: $method, URI: $FullURI, URIPrefix: $URIPrefix, Body: `n$($body | Format-XML)" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) REST Call to NSX Manager via invoke-webrequest : Method: $method, URI: $FullURI, Body: `n$($body | Format-XML)" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } #Use splatting to build up the IWR params $iwrSplat = @{ "Method" = $method; "Headers" = $headerDictionary; "ContentType" = "application/xml"; "Uri" = $FullURI; "TimeoutSec" = $Timeout; "SkipCertificateCheck" = !$ValidateCertificate } if ( $PsBoundParameters.ContainsKey('Body')) { # If there is a body specified, add it to the invoke-restmethod args... $iwrSplat.Add("body", $body) } #do rest call try { $response = invoke-internalwebrequest @iwrsplat } #If its a webexception, we may have got a response from the server with more information... #Even if this happens on PoSH Core though, the ex is not a webexception and we cant get this info :( catch [System.Net.WebException] { #Check if there is a response populated in the response prop as we can return better detail. if ( $_.exception.response ) { $response = $_.exception.response $responseStream = $response.GetResponseStream() $ResponseStream.Position = 0 $reader = New-Object system.io.streamreader($responseStream) $responseBody = $reader.readtoend() $ErrorString = "$($MyInvocation.MyCommand.Name) : The NSX API response received indicates a failure. $($response.StatusCode.value__) : $($response.StatusDescription) : Response Body: $($responseBody)" } else { #No response, log and throw the underlying ex $ErrorString = "$($MyInvocation.MyCommand.Name) : Exception occured calling invoke-restmethod. $($_.exception.tostring())" } if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) REST Call to NSX Manager failed: $ErrorString" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } $exc = New-Object InternalNsxApiException $ErrorString, $_.exception $errorID = 'NsxAPIFailureResult' $errorCategory = 'InvalidResult' $targetObject = 'Invoke-NsxWebRequest' $errorRecord = New-Object Management.Automation.ErrorRecord $exc, $errorID, $errorCategory, $targetObject $PsCmdlet.ThrowTerminatingError($errorRecord) } catch [internalWebRequestException] { #Generate on PoSH Core where we dont use iwr native cmdlet. if ($_.exception.response) { $response = $_.exception.response $ErrorString = "$($MyInvocation.MyCommand.Name) : The NSX API response received indicates a failure. $($response.StatusCode) : $($response.StatusDescription) : Response Body: $($response.Content)" } else { $ErrorString = "$($MyInvocation.MyCommand.Name) : Exception occured calling Invoke-InternalWebRequest. $($_.exception.tostring())" } #Log the error with response detail. if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) REST Call to NSX Manager failed: $ErrorString" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } $exc = New-Object InternalNsxApiException $ErrorString, $_.exception $errorID = 'NsxAPIFailureResult' $errorCategory = 'InvalidResult' $targetObject = 'Invoke-NsxWebRequest' $errorRecord = New-Object Management.Automation.ErrorRecord $exc, $errorID, $errorCategory, $targetObject $PsCmdlet.ThrowTerminatingError($errorRecord) } catch { #Not a webexception, log and throw the underlying ex string and trace $ErrorString = "$($MyInvocation.MyCommand.Name) : An unknown exception occured calling invoke-internalwebrequest. $($_.exception.tostring()) `nStackTrace:`n$($_.ScriptStackTrace)" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) REST Call to NSX Manager failed: $ErrorString" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } $exc = New-Object InternalNsxApiException $ErrorString, $_.exception $errorID = 'NsxAPIFailureResult' $errorCategory = 'InvalidResult' $targetObject = 'Invoke-NsxWebRequest' $errorRecord = New-Object Management.Automation.ErrorRecord $exc, $errorID, $errorCategory, $targetObject $PsCmdlet.ThrowTerminatingError($errorRecord) } #Output the response header dictionary foreach ( $key in $response.Headers.Keys) { Write-Debug "$($MyInvocation.MyCommand.Name) : Response header item : $Key = $($Response.Headers.Item($key))" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) Response header item : $Key = $($Response.Headers.Item($key))" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } } #And if there is response content... if ( $response.content ) { switch ( $response.content ) { { $_ -is [System.String] } { Write-Debug "$($MyInvocation.MyCommand.Name) : Response Body: $($response.content)" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) Response Body: $($response.content)" | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } } default { Write-Debug "$($MyInvocation.MyCommand.Name) : Response type unknown" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) Response type unknown ( $($Response.Content.gettype()) )." | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } } } } else { Write-Debug "$($MyInvocation.MyCommand.Name) : No response content" if ( $pscmdlet.ParameterSetName -eq "ConnectionObj" ) { if ( $connection.DebugLogging ) { "$(Get-Date -Format s) No response content." | Out-File -Append -FilePath $Connection.DebugLogfile -Encoding utf8 } } } $response } function Connect-NsxServer { <# .SYNOPSIS Connects to the specified NSX server and constructs a connection object. .DESCRIPTION The Connect-NsxServer cmdlet returns a connection object that contains the necessary information for PowerNSX cmdlets to locate and authenticate to NSX server in order to perform REST API calls. Because the underlying REST protocol is not connection oriented, the 'Connection' concept relates to just validating endpoint details and credentials and storing details relevant to the NSX manager endpoint. NOTE: Previous releases of PowerNSX required the user to specify the NSX manager endpoint directly and required the use of an NSX manager local account with super_user privileges. This behaviour as a default is now DEPRECATED and will be removed in a future release (the ability to connect directly to NSX using the admin account will not be removed, but will no longer be the default behaviour) The preferred method for most PowerNSX use is now to use the -vCenterServer parameter to specify the vCenter server that NSX is connected to, along with appropriate SSO credentials to authenticate to NSX. This will become the default behaviour in a future version (ie. will not require a -vCenterServer parameter name and will replace the current default behaviour of assuming the first argument specified without a parameter to be the desired NSX server endpoint.) Full support for all NSX roles is now available in NSX. (cmdlets that attempt API operations not supported by the users role will throw appropriate not authorised errors from the API.) Minimum required rights are vCenter Inventory ReadOnly and NSX Auditor role. .EXAMPLE Connect-NsxServer -vCenterServer vcenter.corp.local Connect to vCenter server vcenter.corp.local to determine the NSX server IP and return an appropriate connection object. SSO Credentials will be prompted for and will be used for both vCenter and NSX authentication. .EXAMPLE Connect-NsxServer -vCenterServer vcenter.corp.local -credential $cred Connect to vCenter server vcenter.corp.local using the SSO credentials in $cred to determine the NSX server IP and return an appropriate connection object. The credentials specified in -credential are used for both vCenter connection (if not already established) AND SSO authentication to NSX server. .EXAMPLE Connect-NsxServer -vCenterServer vcenter.corp.local -username me@vsphere.local -password secret Connect to vCenter server vcenter.corp.local using the SSO credentials in -username and -password to determine the NSX server IP and return an appropriate connection object. The credentials specified in -credential are used for both vCenter connection (if not already established) AND SSO authentication to NSX server. .EXAMPLE Connect-NsxServer -vCenterServer vcenter.corp.local -credential $cred -VIDefaultConnection:$false Connect to vCenter server vcenter.corp.local using the SSO credentials in -username and -password to determine the NSX server IP and return an appropriate connection object. The PowerCLi connection stored in the returned connection object is NOT stored in $DefaultViServer. .EXAMPLE Connect-NsxServer -NsxServer nsxserver -username admin -Password VMware1! Connects to the nsxserver 'nsxserver' with the specified credentials. If a registered vCenter server is configured in NSX manager, you are prompted to establish a PowerCLI session to that vCenter along with required authentication details. .EXAMPLE Connect-NsxServer -NsxServer nsxserver -username admin -Password VMware1! -DisableViAutoConnect Connects to the nsxserver 'nsxserver' with the specified credentials and supresses the prompt to establish a PowerCLI connection with the registered vCenter. .EXAMPLE Connect-NsxServer -NsxServer nsxserver -username admin -Password VMware1! -ViUserName administrator@vsphere.local -ViPassword VMware1! Connects to the nsxserver 'nsxserver' with the specified credentials and automatically establishes a PowerCLI connection with the registered vCenter using the credentials specified. .EXAMPLE $MyConnection = Connect-NsxServer -NsxServer nsxserver -username admin -Password VMware1! -DefaultConnection:$false Get-NsxTransportZone 'TransportZone1' -connection $MyConnection Connects to the nsxserver 'nsxserver' with the specified credentials and then uses the returned connection object in a subsequent call to Get-NsxTransportZone. The $DefaultNsxConnection parameter is not populated Note: Any PowerNSX cmdlets will fail if the -connection parameters is not specified and the $DefaultNsxConnection variable is not populated. Note: Pipline operations involving multiple PowerNSX commands that interact with the NSX API (not all) require that all cmdlets specify the -connection parameter (not just the fist one.) .EXAMPLE Connect-NsxServer -NsxServer nsxserver -username admin -Password VMware1! -ViUserName administrator@vsphere.local -ViPassword VMware1! -UriPRefix /other Automatically prepends /other to the URI used to access the NSX REST API for all PowerNSX cmdlets using the associated connection. UriPrefix is intended to allow for a reverse proxy that is doing URL rewriting in front of NSX. Connects to the nsxserver 'nsxserver' with the specified credentials and automatically establishes a PowerCLI connection with the registered vCenter using the credentials specified. #> [CmdletBinding(DefaultParameterSetName = "Legacy")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", "", Scope = "Function", Target = "*")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. param ( [Parameter (Mandatory = $true, Position = 1, ParameterSetName = "Legacy")] #NSX Manager address or FQDN. Deprecated. Use -vCenterServer with SSO credentials as preferred method, or -NsxServer with appliance admin user if required. [ValidateNotNullOrEmpty()] [string]$Server, [Parameter (Mandatory = $true, ParameterSetName = "NSXServer")] #NSX Manager address or FQDN. Recommended method is to use -vCenterServer with SSO credentials. Use this for cmdlets requiring local appliance credentials(Appliance Management and Central CLI). [ValidateNotNullOrEmpty()] [string]$NsxServer, [Parameter (Mandatory = $true, ParameterSetName = "vCenterServer")] #vCenter Server address or FQDN (not NSX Manager!). Used to determine NSX Server endpoint and authenticate using SSO credentials. Recommended method. [ValidateNotNullOrEmpty()] [string]$vCenterServer, [Parameter (Mandatory = $false, ParameterSetName = "vCenterServer")] #NSX Manager address used to override that registered in vCenter. Used for scenarios where NSX manager is behind a NAT device. [ValidateNotNullOrEmpty()] [string]$NsxServerHint, [Parameter (Mandatory = $false)] #TCP Port to connect to on -Server [ValidateRange(1, 65535)] [int]$Port = 443, [Parameter (Mandatory = $false)] #PSCredential object containing NSX API authentication credentials [PSCredential]$Credential, [Parameter (Mandatory = $false)] #Username used to authenticate to NSX API [ValidateNotNullOrEmpty()] [string]$Username, [Parameter (Mandatory = $false)] #Password used to authenticate to NSX API [ValidateNotNullOrEmpty()] [string]$Password = "", [Parameter (Mandatory = $false)] #Validates the certificate presented by NSX Manager for HTTPS connections. Defaults to False [ValidateNotNullOrEmpty()] [switch]$ValidateCertificate = $false, [Parameter (Mandatory = $false)] #NSX API transport protocol - HTTPS / HTTP . Defaults to HTTPS [ValidateNotNullOrEmpty()] [string]$Protocol = "https", [Parameter (Mandatory = $false)] #NSX API URI prefix. Supports reverse proxy in between client and NSX doing URI rewrites so that uri is prepended with $UriPrefix [ValidateNotNullOrEmpty()] [string]$UriPrefix = "", [Parameter (Mandatory = $false)] #If True, the $DefaultNsxConnection global variable is created and populated with connection details. #All PowerNSX commands that use the NSX API will utilise this connection unless they are called with the -connection parameter. #Defaults to True [bool]$DefaultConnection = $true, [Parameter (Mandatory = $false)] #If False, and a PowerCLI connection needs to be established to the registered vCenter, the Connect-ViServer call made by PowerNSX will specify the -NotDefault switch (see Get-Help Connect-ViServer) #Defaults to True [bool]$VIDefaultConnection = $true, [Parameter (Mandatory = $false, ParameterSetName = "Legacy")] [Parameter (Mandatory = $false, ParameterSetName = "NSXServer")] #If True, and the PowerNSX connection attempt is successful, an automatic PowerCLI connection to the registered vCenter server is not attempted. Defaults to False. [switch]$DisableVIAutoConnect = $false, [Parameter (Mandatory = $false, ParameterSetName = "Legacy")] [Parameter (Mandatory = $false, ParameterSetName = "NSXServer")] #UserName used in PowerCLI connection to registered vCenter. [string]$VIUserName, [Parameter (Mandatory = $false, ParameterSetName = "Legacy")] [Parameter (Mandatory = $false, ParameterSetName = "NSXServer")] #Password used in PowerCLI connection to registered vCenter. [string]$VIPassword = "", [Parameter (Mandatory = $false, ParameterSetName = "Legacy")] [Parameter (Mandatory = $false, ParameterSetName = "NSXServer")] #PSCredential object containing credentials used in PowerCLI connection to registered vCenter. [Alias ("ViCred")] [PSCredential]$VICredential, [Parameter (Mandatory = $false)] #Enable DebugLogging of all API calls to $DebugLogFile. Can be enabled on esisting connections with $connection.DebugLogging = $true. Defaults to False. [switch]$DebugLogging = $false, [Parameter (Mandatory = $false)] #If DebugLogging is enabled, specifies the file to which output is written. Defaults to $Env:temp\PowerNSXLog-@-.log [string]$DebugLogFile, [Parameter (Mandatory = $false)] #Supresses warning output from PowerCLI connection attempts (typically invalid Certificate warnings) [ValidateSet("Continue", "Ignore")] [string]$ViWarningAction = "Continue" ) function TestvCenterConn { #Internal function to test if registered vCenter has a current connection. param ( $RegisteredvCenterIP ) $ConnectedViServerConnection = $null if ((Test-Path variable:global:DefaultVIServer )) { #Already have a PowerCLI connection - is it to the right place? #the 'ipaddress' in vcinfo from NSX api can be fqdn, #Resolve to ip so we can compare to any existing connection. #Resolution can result in more than one ip so we have to iterate over it. $RegisteredvCenterIPs = ([System.Net.Dns]::GetHostAddressesAsync($RegisteredvCenterIP)).Result #Remembering we can have multiple vCenter connections too :| :outer foreach ( $VIServerConnection in $global:DefaultVIServer ) { $ExistingVIConnectionIPs = [System.Net.Dns]::GetHostAddressesAsync($VIServerConnection.Name).Result foreach ( $ExistingVIConnectionIP in [IpAddress[]]$ExistingVIConnectionIPs ) { foreach ( $RegisteredvCenterIP in [IpAddress[]]$RegisteredvCenterIPs ) { if ( $ExistingVIConnectionIP -eq $RegisteredvCenterIP ) { if ( $VIServerConnection.IsConnected ) { $ConnectedViServerConnection = $ViServerConnection Write-Host -ForegroundColor Green "Using existing PowerCLI connection to $($ExistingVIConnectionIP.IPAddresstoString)" break outer } else { Write-Host -ForegroundColor Yellow "Existing PowerCLI connection to $($ExistingVIConnectionIP.IPAddresstoString) is not connected." } } } } } } return $ConnectedViServerConnection } #Legacy mode warning if ( $PSCmdlet.ParameterSetName -eq "Legacy") { Write-Warning "The -Server parameter in Connect-NsxServer is deprecated and will be made non-default in a future release." Write-Warning "Recommended usage of Connect-NsxServer is to use the -vCenterServer parameter and valid SSO credentials (requires rights of at least Read-Only over vCenter Inventory and NSX Auditor role)." Write-Warning "Use the -NsxServer parameter to continue using direct connection to NSX and either appliance local or Enterprise_Administrator (only) level SSO credentials." $NsxServer = $Server } #Preclude certain param combinations that we dont want to accept. if ((($PsBoundParameters.ContainsKey("Credential")) -and ($PsBoundParameters.ContainsKey("Username"))) -or (($PsBoundParameters.ContainsKey("Credential")) -and ($PsBoundParameters.ContainsKey("Password")))) { Throw "Specify either -Credential or both -UserName and -Password to authenticate" } if ((($PsBoundParameters.ContainsKey("VICredential")) -and ($PsBoundParameters.ContainsKey("VIUsername"))) -or (($PsBoundParameters.ContainsKey("VICredential")) -and ($PsBoundParameters.ContainsKey("VIPassword")))) { Throw "Specify either -VICredential or both -VIUserName and -VIPassword to authenticate" } #Build cred object for default auth if user specified username/pass if ($PsBoundParameters.ContainsKey("UserName")) { $Credential = New-Object System.Management.Automation.PSCredential($Username, $(ConvertTo-SecureString $Password -AsPlainText -Force)) } elseif ( -not $PSBoundParameters.ContainsKey("Credential")) { if ( $PSCmdlet.ParameterSetName -eq "vCenterServer") { $Message = "vCenter Server SSO Credentials" } else { $Message = "NSX Manager Local or Enterprise Admin SSO Credentials" } $Credential = Get-Credential -Message $Message } #Debug log will contain all rest calls, request and response bodies, and response headers. if ( -not $PsBoundParameters.ContainsKey('DebugLogFile' )) { #Generating logfile name regardless of initial user pref on debug. They can just flip the prop on the connection object at a later date to start logging... $dtstring = Get-Date -Format "yyyy_MM_dd_HH_mm_ss" $DebugLogFile = "$($env:TEMP)\PowerNSXLog-$($Credential.UserName)@$NSXServer-$dtstring.log" } #If debug is on, need to test we can create the debug file first and throw if not... if ( $DebugLogging -and (-not ( New-Item -Path $DebugLogFile -Type file ))) { Throw "Unable to create logfile $DebugLogFile. Disable debugging or specify a valid DebugLogFile name." } #Defaults for vars we may not be able to set on the resulting connection object... $version = $null $buildnumber = $null $ViConnection = $null if ( ($pscmdlet.Parametersetname -eq "Legacy") -or ($pscmdlet.ParameterSetName -eq "NsxServer") ) { #User sepecified the NSX server endpoint and some credentials. Lets try to validate directly against NSX #on a somewhat random URI that we can hit without requiring special privileges and simply validate our credentials. $URI = "/api/2.0/nwfabric/features" #Even though there is partial version info available in the feature info - we cant get the manager version from here, so Im reluctant to return anything. try { $response = Invoke-NsxRestMethod -cred $Credential -server $NsxServer -port $port -protocol $Protocol -method "get" -URI $URI -ValidateCertificate:$ValidateCertificate -UriPrefix $uriprefix } catch { Throw "Connection to NSX server $NsxServer failed : $_" } #Right - we got here, so now we try to build a connection object and retrieve useful information $URI = "/api/1.0/appliance-management/global/info" #Test NSX connection try { $response = Invoke-NsxRestMethod -cred $Credential -server $NsxServer -port $port -protocol $Protocol -method "get" -URI $URI -ValidateCertificate:$ValidateCertificate -UriPrefix $uriprefix # try to populate version information # NSX-v 6.2.3 changed the output of the following API from JSON to XML. # # /api/1.0/appliance-management/global/info" # # Along with the return JSON/XML change, the data structure also received a # new base element named globalInfo. # # So what we do is try for the new format, and if it fails, lets default to # the old JSON format. if ( $response -as [System.Xml.XmlDocument] ) { if ( Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query "child::globalInfo/versionInfo" ) { $version = $response.globalInfo.versionInfo.majorVersion + "." + $response.globalInfo.versionInfo.minorVersion + "." + $response.globalInfo.versionInfo.patchVersion $BuildNumber = $response.globalInfo.versionInfo.BuildNumber } } else { if ( Get-Member -InputObject $response -MemberType NoteProperty -Name versionInfo ) { $Version = $response.VersionInfo.majorVersion + "." + $response.VersionInfo.minorVersion + "." + $response.VersionInfo.patchVersion $BuildNumber = $response.VersionInfo.BuildNumber } } } catch { #supression excep in event of 403. Valid non local account credentias are not able to query the appliance-management API if ( $_ -match '403 : Forbidden|403 \(Forbidden\)') { Write-Warning "A valid local admin account is required to access version information. This warning can be ignored if using SSO credentials to authenticate to NSX, however, appliance version information will not be available in the connection object. Use Connect-NsxServer -VCServer to avoid this warning." # write-warning "A valid local admin account is required to access version information. This warning can be ignored if using SSO credentials to authenticate to NSX, however, appliance version information will not be available in the connection object." } else { $_ } } #Try and get the registered VC info from NSX so we can build a VIconnection... try { $URI = "/api/2.0/services/vcconfig" $vcInfo = Invoke-NsxRestMethod -cred $Credential -server $NsxServer -port $port -protocol $Protocol -method "get" -URI $URI -ValidateCertificate:$ValidateCertificate -UriPrefix $uriprefix if ( $DebugLogging ) { "$(Get-Date -Format s) New PowerNSX Connection to $($credential.UserName)@$($Protocol)://$($NsxServer):$port, version $($Connection.Version)" | Out-File -Append -FilePath $DebugLogfile -Encoding utf8 } } catch { #Catch a forbidden as we may not be using an admin account - in which case, we cant query NSX for the registered vC... if ( $_ -match '403 : Forbidden|403 \(Forbidden\)') { Write-Warning "The credentials used are not sufficiently privileged to be able to query NSX for the registered vCenter Server. Use Connect-NsxServer -VCServer to avoid this warning." } else { $_ } } if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $vcinfo -query 'descendant::vcInfo/ipAddress')) { if ( $DebugLogging ) { "$(Get-Date -Format s) NSX Manager $NsxServer is not currently connected to any vCenter..." | Out-File -Append -FilePath $DebugLogfile -Encoding utf8 } Write-Warning "NSX Manager does not currently have a vCenter registration. Use Set-NsxManager to register a vCenter server." } else { $RegisteredvCenterIP = $vcInfo.vcInfo.ipAddress $VIServerConnection = TestvCenterConn -RegisteredvCenterIp $RegisteredvCenterIP if ( $VIServerConnection ) { $VIConnection = $VIServerConnection } else { if ( -not (($VIUserName -and $VIPassword) -or ( $VICredential ) )) { #We assume that if the user did not specify VI creds, then they may want a connection to VC, but we will ask. $decision = 1 if ( -not $DisableVIAutoConnect) { #Ask the question and get creds. $message = "PowerNSX requires a PowerCLI connection to the vCenter server NSX is registered against for proper operation." $question = "Automatically create PowerCLI connection to $($RegisteredvCenterIP)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 0) } if ( $decision -eq 0 ) { Write-Host Write-Warning "Enter credentials for vCenter $RegisteredvCenterIP" $VICredential = Get-Credential $VIConnection = Connect-VIServer -Credential $VICredential $RegisteredvCenterIP -NotDefault:(-not $VIDefaultConnection) -WarningAction:$ViWarningAction } else { Write-Host Write-Warning "Some PowerNSX cmdlets will not be fully functional without a valid PowerCLI connection to vCenter server $RegisteredvCenterIP" } } else { #User specified VI username/pwd or VI cred. Connect automatically to the registered vCenter Write-Host "Creating PowerCLI connection to vCenter server $RegisteredvCenterIP" if ( $VICredential ) { $VIConnection = Connect-VIServer -Credential $VICredential $RegisteredvCenterIP -NotDefault:(-not $VIDefaultConnection) -WarningAction:$ViWarningAction } else { $VIConnection = Connect-VIServer -User $VIUserName -Password $VIPassword $RegisteredvCenterIP -NotDefault:(-not $VIDefaultConnection) -WarningAction:$ViWarningAction } } } if ( $DebugLogging ) { "$(Get-Date -Format s) NSX Manager $NsxServer is registered against vCenter server $RegisteredvCenterIP. PowerCLI connection established to registered vCenter : $(if ($ViConnection ) { $VIConnection.IsConnected } else { "False" })" | Out-File -Append -FilePath $DebugLogfile -Encoding utf8 } } } else { #User specified a vCenter server endpoint. Try to query VC for the registered NSX manager. This is now the preferred method as we can always determine version information regardless of NSX role providing the user has at least R/O in the VI inventory. try { #Connect to specified VC using 'default credentials... $VIConnection = TestvCenterConn -RegisteredvCenterIp $vCenterServer if ( -not $VIConnection ) { $VIConnection = Connect-VIServer -Credential $Credential -Server $vCenterServer -NotDefault:(-not $VIDefaultConnection) -WarningAction:$ViWarningAction -ErrorAction Stop } $ExtensionManager = Get-View ExtensionManager -Server $ViConnection $NSXExtension = $ExtensionManager.ExtensionList | Where-Object { $_.key -eq 'com.vmware.vShieldManager' } if ( -not $NSXExtension ) { throw "The connected vCenter server does not have a registered NSX solution." } [string[]]$VersionCol = $NSXExtension.Client.Version -split "\." if ( -not ($versionCol.count -eq 4 )) { #Version string not as expected throw "Version information for the registered NSX solution could not be retrieved. Raw version string $($NSXExtension.Client.Version)" } $Version = $VersionCol[0] + "." + $VersionCol[1] + "." + $VersionCol[2] $BuildNumber = $VersionCol[3] [string[]]$EndpointCol = $NSXExtension.Client.url -split "/" if ( -not ($EndpointCol.count -eq 4 )) { #Endpoint string not as expected throw "Endpoint information for the registered NSX solution could not be retrieved. Raw endpoint URL $($NSXExtension.Client.Url)" } if ( -not $EndpointCol[2] -match "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$") { #IP:Port not parsed throw "Server information for the registered NSX solution could not be retrieved. Raw endpoint URL $($NSXExtension.Client.Url)" } [string[]]$ServerCol = $EndpointCol[2] -split ":" if ( -not ($ServerCol.count -eq 2)) { #IP:Port not parsed throw "Server information for the registered NSX solution could not be retrieved. Raw endpoint URL $($NSXExtension.Client.Url)" } if ( $PSBoundParameters.ContainsKey("NsxServerHint")) { $NsxServer = $NsxServerHint } else { $NsxServer = $ServerCol[0] } $Port = $ServerCol[1] $ProtocolCol = $EndpointCol[0] -split ":" if ( -not ($ProtocolCol.count -eq 2)) { #IP:Port not parsed throw "Protocol information for the registered NSX solution could not be retrieved. Raw endpoint URL $($NSXExtension.Client.Url)" } $Protocol = $ProtocolCol[0] } catch { throw "Unable to determine NSX server endpoint from vCenter. $_" } #Now we simply test the connection to NSX against a random unprivileged URI $URI = "/api/2.0/nwfabric/features" try { $response = Invoke-NsxRestMethod -cred $Credential -server $NsxServer -port $port -protocol $Protocol -method "get" -URI $URI -ValidateCertificate:$ValidateCertificate -UriPrefix $uriprefix } catch { Throw "Connection to NSX server $NsxServer failed : $_" } } #Setup the connection object $connection = [pscustomObject] @{ "Version" = $version "BuildNumber" = $BuildNumber "Credential" = $Credential "Server" = $NSXServer "Port" = $port "Protocol" = $Protocol "UriPrefix" = $UriPrefix "ValidateCertificate" = $ValidateCertificate "VIConnection" = $ViConnection "DebugLogging" = $DebugLogging "DebugLogfile" = $DebugLogFile } #Set the default connection is required. if ( $DefaultConnection) { Set-Variable -Name DefaultNSXConnection -Value $connection -Scope Global } #Return the connection $connection } function Disconnect-NsxServer { <# .SYNOPSIS Destroys the $DefaultNSXConnection global variable if it exists. .DESCRIPTION REST is not connection oriented, so there really isnt a connect/disconnect concept. Disconnect-NsxServer, merely removes the $DefaultNSXConnection variable that PowerNSX cmdlets default to using. .EXAMPLE Connect-NsxServer -Server nsxserver -username admin -Password VMware1! #> if (Get-Variable -Name DefaultNsxConnection -Scope global ) { Remove-Variable -Name DefaultNsxConnection -Scope global } } function Get-PowerNsxVersion { <# .SYNOPSIS Retrieves the version of PowerNSX. .EXAMPLE Get-PowerNsxVersion Get the installed version of PowerNSX #> #Updated to take advantage of Manifest info. Get-Module PowerNsx | Select-Object version, path, author, companyName } function Update-PowerNsx { <# .SYNOPSIS Updates PowerNSX to the latest version available in the specified branch. .EXAMPLE Update-PowerNSX -Branch master #> param ( [Parameter (Mandatory = $True, Position = 1)] #Valid Branches supported for upgrading to. [ValidateScript( { ValidateUpdateBranch $_ })] [string]$Branch, [ValidateSet("CurrentUser", "AllUsers")][string]$InstallType = "CurrentUser" ) $PNsxUrl = "$PNsxUrlBase/$Branch/PowerNSXInstaller.ps1" if ( $script:PNsxPSTarget -eq "Desktop") { #OS specific temp variable $tmpdir = $($env:Temp) } else { #OS specific temp variable if ( Test-Path env:TMPDIR ) { $tmpdir = $env:TMPDIR } else { $tmpdir = "/tmp" } #Disable progress dialogs from iwr $PreviousProgPref = $ProgressPreference $global:ProgressPreference = "SilentlyContinue" } if ( $Branch -eq "master" ) { Write-Warning "Updating to latest $branch branch commit. Stability is not guaranteed." } #Installer doesnt play nice in strict mode... Set-StrictMode -Off try { try { $filename = Split-Path $PNsxUrl -Leaf Invoke-WebRequest -Uri $PNsxUrl -OutFile "$tmpdir\$filename" } catch { #TODO: Confirm Proxy handling works with change to iwr. if ( $_.exception.innerexception -match "(407)") { $ProxyCred = Get-Credential -Message "Proxy Authentication Required" Invoke-WebRequest -Uri $PNsxUrl -OutFile "$tmpdir\$filename" -ProxyCredential $ProxyCred } else { throw $_ } } Invoke-Expression "& `"$tmpdir\$filename`" -Upgrade -InstallType $InstallType" } catch { throw $_ } ## Not reloading module now, too many issues unloading dependant modules exist to make this robust and clean on all platforms. # Import-Module PowerNSX -global -force Write-Host -ForegroundColor Magenta "PowerNSX has been updated. Please restart PowerShell to use the updated version." #Check to make sure we dont have mutiple installs.... if ( (Get-Module -ListAvailable PowerNSX | Measure-Object ).count -ne 1 ) { Write-Warning "Mutiple PowerNSX installations found. It is recommended to remove one of them or the universe may implode! (Or you may end up using an older version without realising, which is nearly as bad!)" foreach ( $mod in (Get-Module -ListAvailable PowerNSX) ) { Write-Warning "PowerNSX Install found in $($mod.path | Split-Path -Parent )" } } if ( $PreviousProgPref ) { #reenable progress dialogs from iwr $global:ProgressPreference = $PreviousProgPref } Set-StrictMode -Version Latest } function Wait-NsxJob { <# .SYNOPSIS Wait for the specified job until completion criteria tests true. .DESCRIPTION Attempt to wait for the specified job until completion criteria tests true. Timeout is either warning and allow user to quit/throw, or to Throw on timeout. Success and Failure criteria can be expressed via scriptblock ($job is the returned XML object that you can traverse) Status expression and ErrorExpression provide methods of providing output of Success or Failure criteria. Intended to be internal generic job wait routine for PowerNSX cmdlet consumption. PowerNSX users should use object specific Wait-Nsx*Job or Wiat-NsxGenericJob Cmdlets that call this cmdlet. #> param ( [Parameter (Mandatory = $true)] #Job Id string as returned from the api [string]$jobid, [Parameter (Mandatory = $true)] #Job Query URI. There are several job subsystems in NSX. Some of them overlap. [string]$JobStatusUri, [Parameter (Mandatory = $true)] #ScriptBlock that is used to evaluate completion. $job is the xml object returned from the API, so this should be something like { $job.controllerDeploymentInfo.status -eq "Success" } [System.Management.Automation.ScriptBlock]$CompleteCriteria, [Parameter (Mandatory = $true)] #ScriptBlock that is used to evaluate completion. $job is the xml object returned from the API, so this should be something like { $job.controllerDeploymentInfo.status -eq "Failure" }. Wait-NsxJob will return immediately if this tests true. [System.Management.Automation.ScriptBlock]$FailCriteria, [Parameter (Mandatory = $true)] #Scriptblock that is used to retrieve a status string. $job is the xml object returned from the API, so this should be something like { $job.controllerDeploymentInfo.status }. Used only in status output (not in job completion criteria.) [System.Management.Automation.ScriptBlock]$StatusExpression, [Parameter (Mandatory = $false)] #ScriptBlock that is used to retrieve any error string. Defaults to $StatusExpression. $job is the xml object returned from the API, so this should be something like { $job.controllerDeploymentInfo.ExceptionMessage }. This is only used in warning/error output (not in job completion criteria.) [System.Management.Automation.ScriptBlock]$ErrorExpression = $StatusExpression, [Parameter (Mandatory = $false)] #Seconds to wait before declaring a timeout [int]$WaitTimeout = 300, [Parameter (Mandatory = $false)] #Do we prompt user an allow them to reset the timeout timer, or throw on timeout [switch]$FailOnTimeout = $false, [Parameter (Mandatory = $false)] #Number of seconds to sleep between status checks [int]$SleepSeconds = 1, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { $yesnochoices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $yesnochoices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $yesnochoices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) function prompt_for_timeout { Write-Debug "$($MyInvocation.MyCommand.Name) : Timeout waiting for job $jobid" if ( -not $FailOnTimeout) { $message = "Waited more than $WaitTimeout seconds for job $jobid to complete. Recommend checking NSX Manager logs or vCenter tasks for the potential cause." $question = "Continue waiting for the job to complete?" $decision = $Host.UI.PromptForChoice($message, $question, $yesnochoices, 0) if ( $decision -eq 1 ) { throw "Timeout waiting for job $jobid to complete." } } else { throw "Timeout waiting for job $jobid to complete." } } } process { Write-Debug "$($MyInvocation.MyCommand.Name) : Waiting for job $jobid" $StatusString = "Unknown" $Timer = 0 #Now - loop, checking job status do { #Sleep Write-Progress -Activity "Waiting for NSX job $jobId to complete." -Status "$StatusString" Start-Sleep -Seconds $SleepSeconds $Timer += $SleepSeconds #Are we timed out? if ( $Timer -ge $WaitTimeout ) { prompt_for_timeout $timer = 0 } #Get updated jobStatus try { $response = Invoke-NsxWebRequest -method "get" -URI "$JobStatusUri/$jobId" -connection $connection [xml]$job = $response.Content Write-Debug "$($MyInvocation.MyCommand.Name) : Got job from $JobStatusUri for job $jobid" } catch { #Can fail if query is too quick Write-Warning "Unable to query for job $jobid at $JobStatusUri. Does the job exist?" } #Try get our status string. Failure here should indicate that the user needs to tell us that the API returned something unexpected, and/or PowerNSX has a bug that needs fixing. try { $StatusString = &$StatusExpression } catch { $StatusString = "Unknown" Write-Warning "Failed to retrieve job status when waiting for job $jobId. Please report this error on the PowerNSX issues page. (github.com/vmware/PowerNSX/issues) : $_" } if ( &$FailCriteria ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Failure criteria `"$FailCriteria`" evaluated to true." #Try get our error string. Failure here should indicate that the user needs to tell us that the API returned something unexpected, and/or PowerNSX has a bug that needs fixing. try { $ErrorString = &$ErrorExpression } catch { $ErrorString = "Unknown" Write-Warning "Failed to retrieve job error output when job $jobId failed. Please report this error on the PowerNSX issues page. (github.com/vmware/PowerNSX/issues) : $_" } Throw "Job $jobid failed with Status: $StatusString. Error: $ErrorString" } } until ( &$CompleteCriteria ) Write-Debug "$($MyInvocation.MyCommand.Name) : Completed criteria `"$CompleteCriteria`" evaluated to true." Write-Progress -Activity "Waiting for NSX job $jobId to complete." -Status "$StatusString" -Completed } end {} } function Wait-NsxGenericJob { <# .SYNOPSIS Wait for the specified job until it succeeds or fails. .DESCRIPTION Generic task framework handler. Wait-NsxGenericJob attempts waits for the specified job to succeed or fail. Wait-NsxGenericJob defaults to timeout at 30 seconds, when the user is prompted to continuing waiting or fail. If immediate failure upon timeout is desirable (eg within script), then the $failOnTimeout switch can be set. .EXAMPLE Wait-NsxGenericJob -Jobid jobdata-1234 Wait for job jobdata-1234 up to 30 seconds to complete successfully or fail. If 30 seconds elapse, then prompt for action. .EXAMPLE Wait-NsxGenericJob -Jobid jobdata-1234 -TimeOut 40 -FailOnTimeOut Wait for controller job jobdata-1234 up to 40 seconds to complete successfully or fail. If 40 seconds elapse, then throw an error. #> param ( [Parameter (Mandatory = $true)] #Job Id string as returned from the api [string]$JobId, [Parameter (Mandatory = $false)] #Seconds to wait before declaring a timeout. Timeout defaults to 30 seconds. [int]$WaitTimeout = 30, [Parameter (Mandatory = $false)] #Do we prompt user an allow them to reset the timeout timer, or throw on timeout [switch]$FailOnTimeout = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $WaitJobArgs = @{ "jobid" = $jobid "JobStatusUri" = "/api/2.0/services/taskservice/job" "CompleteCriteria" = { $job.jobInstances.jobInstance.status -eq "COMPLETED" } "FailCriteria" = { $job.jobInstances.jobInstance.status -eq "FAILED" } "StatusExpression" = { $execTask = @() $StatusMessage = "" $execTask = @($job.jobinstances.jobInstance.taskInstances.taskInstance | Where-Object { $_.taskStatus -eq "EXECUTING" }) if ( $exectask.count -eq 1) { $StatusMessage = "$($execTask.name) - $($execTask.taskStatus)" } else { $StatusMessage = "$($job.jobinstances.jobInstance.Status)" } $StatusMessage } "ErrorExpression" = { $failTask = @() $failMessage = "" $failTask = @($job.jobinstances.jobInstance.taskInstances.taskInstance | Where-Object { $_.taskStatus -eq "FAILED" }) if ( $failTask.count -eq 1) { $failMessage = "Failed Task : $($failTask.name) - $($failTask.statusMessage)" } else { $failMessage = "$($job.jobinstances.jobInstance.Status)" } $failMessage } "WaitTimeout" = $WaitTimeout "FailOnTimeout" = $FailOnTimeout "Connection" = $Connection } Wait-NsxJob @WaitJobArgs } ######### ######### # Infra functions function Get-NsxClusterStatus { <# .SYNOPSIS Retrieves the resource status from NSX for the given cluster. .DESCRIPTION All clusters visible to NSX manager (managed by the vCenter that NSX Manager is synced with) can have the status of NSX related resources queried. This cmdlet returns the resource status of all registered NSX resources for the given cluster. .EXAMPLE This example shows how to query the status for the cluster MyCluster PS C:\> get-cluster MyCluster | Get-NsxClusterStatus .EXAMPLE This example shows how to query the status for all clusters PS C:\> get-cluster MyCluster | Get-NsxClusterStatus #> param ( [Parameter ( Mandatory = $true, ValueFromPipeline = $true)] #Cluster Object to retrieve status details for. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop]$Cluster, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Get resource status for given cluster Write-Debug "$($MyInvocation.MyCommand.Name) : Query status for cluster $($cluster.name) ($($cluster.ExtensionData.Moref.Value))" $uri = "/api/2.0/nwfabric/status-without-alarms?resource=$($cluster.ExtensionData.Moref.Value)" try { $response = Invoke-NsxRestMethod -connection $connection -method get -URI $uri $response.resourceStatuses.resourceStatus.nwFabricFeatureStatus } catch { throw "Unable to query resource status for cluster $($cluster.Name) ($($cluster.ExtensionData.Moref.Value)). $_" } } end {} } function Invoke-NsxCli { <# .SYNOPSIS Provides access to the NSX Centralised CLI. .DESCRIPTION The NSX Centralised CLI is a feature first introduced in NSX 6.2. It provides centralised means of performing read only operations against various aspects of the dataplane including Logical Switching, Logical Routing, Distributed Firewall and Edge Services Gateways. The results returned by the Centralised CLI are actual (realised) state as opposed to configured state. They should you how the dataplane actually is configured at the time the query is run. WARNING: The Centralised CLI is primarily a trouble shooting tool and it and the PowerNSX cmdlets that expose it should not be used for any other purpose. All the PowerNSX cmdlets that expose the central cli rely on a bespoke text parser to interpret the results as powershell objects, and have not been extensively tested. .INPUTS System.String .PARAMETER Query string text of Central CLI command to execute .PARAMETER SupressWarning Switch parameter to ignore the experimental warning .PARAMETER Connection Proper NSX connection [PSCustomObject] .PARAMETER RawOutput Switch parameter that will not try to parse the output #> param ( [Parameter ( Mandatory = $true, Position = 1) ] #Free form query string that is sent to the NSX Central CLI API [ValidateNotNullOrEmpty()] [String]$Query, [Parameter ( Mandatory = $false) ] #Supress warning about experimental feature. Defaults to False [switch]$SupressWarning, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection, [Parameter(Mandatory = $false)] # switch param to support throwing raw output to avoid errors with the parser [switch]$RawOutput ) begin { if ( -not $SupressWarning ) { Write-Warning -Message "This cmdlet is experimental and has not been well tested. Its use should be limited to troubleshooting purposes only." } # end if } # end begin block process { Write-Verbose -Message "[$($MyInvocation.MyCommand.Name)] Executing Central CLI Query {$Query}" Write-Debug -Message "[$($MyInvocation.MyCommand.Name)] Building XML" #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlCli = $XMLDoc.CreateElement("nsxcli") $xmlDoc.appendChild($xmlCli) | Out-Null Add-XmlElement -xmlRoot $xmlCli -xmlElementName "command" -xmlElementText $Query #show cluster all $Body = $xmlCli.OuterXml $uri = "/api/1.0/nsx/cli?action=execute" Write-Debug -Message "[$($MyInvocation.MyCommand.Name)] Invoking POST method. Entering 'try/catch' block" try { $response = Invoke-NsxRestMethod -connection $connection -method post -URI $uri -body $Body -extraheader @{"Accept" = "text/plain" } if ($RawOutput) { Write-Verbose -Message "[$($MyInvocation.MyCommand.Name)] Returning Raw Output" $response } else { Write-Verbose -Message "[$($MyInvocation.MyCommand.Name)] Parsing Output" ParseCentralCliResponse $response } # end if/else } catch { throw "[$($MyInvocation.MyCommand.Name)][ERROR] Unable to execute Centralized CLI query. $_.Exception.Message. Try re-running command with the -RawOutput parameter." } # end try/catch } # end process block end { Write-Verbose -Message "[$($MyInvocation.MyCommand.Name)] Processing Complete" } # end block } # end function Invoke-NsxCli function Get-NsxCliDfwFilter { <# .SYNOPSIS Uses the NSX Centralised CLI to retrieve the VMs VNIC filters. .DESCRIPTION The NSX Centralised CLI is a feature first introduced in NSX 6.2. It provides centralised means of performing read only operations against various aspects of the dataplane including Logical Switching, Logical Routing, Distributed Firewall and Edge Services Gateways. The results returned by the Centralised CLI are actual (realised) state as opposed to configured state. They show you how the dataplane actually is configured at the time the query is run. This cmdlet accepts a VM object, and leverages the Invoke-NsxCli cmdlet by constructing the appropriate Centralised CLI command without requiring the user to do the show cluster all -> show cluster domain-xxx -> show host host-xxx -> show vm vm-xxx dance -> show dfw host host-xxx filter xxx rules dance. It returns objects representing the Filters defined on each vnic of the VM #> Param ( [Parameter (Mandatory = $True, ValueFromPipeline = $True)] #PowerCLI Virtual Machine object. [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop]$VirtualMachine, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $query = "show vm $($VirtualMachine.ExtensionData.Moref.Value)" $filters = Invoke-NsxCli $query -SupressWarning -Connection $connection foreach ( $filter in $filters ) { #only match slot 2 filters if ( $filter -notmatch 'nic-\d+-eth\d-vmware-sfw.2' ) { Write-Warning "Ignoring filter `'$($filter.Filters)`' on VM $($filter.VM)" } else { #Execute the appropriate CLI query against the VMs host for the current filter... $query = "show vnic $($Filter."Vnic Id")" Invoke-NsxCli $query -Connection $connection } } } end {} } function Get-NsxCliDfwRule { <# .SYNOPSIS Uses the NSX Centralised CLI to retrieve the rules configured for the specified VMs vnics. .DESCRIPTION The NSX Centralised CLI is a feature first introduced in NSX 6.2. It provides centralised means of performing read only operations against various aspects of the dataplane including Logical Switching, Logical Routing, Distributed Firewall and Edge Services Gateways. The results returned by the Centralised CLI are actual (realised) state as opposed to configured state. They show you how the dataplane actually is configured at the time the query is run. This cmdlet accepts a VM object, and leverages the Invoke-NsxCli cmdlet by constructing the appropriate Centralised CLI command without requiring the user to do the show cluster all -> show cluster domain-xxx -> show host host-xxx -> show vm vm-xxx dance -> show dfw host host-xxx filter xxx dance. It returns objects representing the DFW rules instantiated on the VMs vnics dfw filters. #> Param ( [Parameter (Mandatory = $True, ValueFromPipeline = $True)] #PowerCLI VirtualMachine object [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop]$VirtualMachine, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( $VirtualMachine.PowerState -eq 'PoweredOn' ) { #First we retrieve the filter names from the host that the VM is running on try { $query = "show vm $($VirtualMachine.ExtensionData.Moref.Value)" $VMs = Invoke-NsxCli $query -Connection $connection } catch { #Invoke-nsxcli threw an exception. There are a couple we want to handle here... switch -regex ($_.tostring()) { "\( Error 100: \)" { Write-Warning "Virtual Machine $($VirtualMachine.Name) has no DFW Filter active."; return } default { throw } } } #Potentially there are multiple 'VMs' (VM with more than one NIC). foreach ( $VM in $VMs ) { #Execute the appropriate CLI query against the VMs host for the current filter... $query = "show dfw host $($VirtualMachine.VMHost.ExtensionData.MoRef.Value) filter $($VM.Filters) rules" $rule = Invoke-NsxCli $query -SupressWarning -Connection $connection $rule | Add-Member -MemberType NoteProperty -Name "VirtualMachine" -Value $VirtualMachine $rule | Add-Member -MemberType NoteProperty -Name "Filter" -Value $($VM.Filters) $rule } } else { Write-Warning "Virtual Machine $($VirtualMachine.Name) is not powered on." } } end {} } function Get-NsxCliDfwAddrSet { <# .SYNOPSIS Uses the NSX Centralised CLI to retrieve the address sets configured for the specified VMs vnics. .DESCRIPTION The NSX Centralised CLI is a feature first introduced in NSX 6.2. It provides centralised means of performing read only operations against various aspects of the dataplane including Logical Switching, Logical Routing, Distributed Firewall and Edge Services Gateways. The results returned by the Centralised CLI are actual (realised) state as opposed to configured state. They show you how the dataplane actually is configured at the time the query is run. This cmdlet accepts a VM object, and leverages the Invoke-NsxCli cmdlet by constructing the appropriate Centralised CLI command without requiring the user to do the show cluster all -> show cluster domain-xxx -> show host host-xxx -> show vm vm-xxx dance -> show dfw host host-xxx filter xxx dance. It returns object representing the Address Sets defined on the VMs vnics DFW filters. #> Param ( [Parameter (Mandatory = $True, ValueFromPipeline = $True)] #PowerCLI VirtualMachine object [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop]$VirtualMachine, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #First we retrieve the filter names from the host that the VM is running on $query = "show vm $($VirtualMachine.ExtensionData.Moref.Value)" $Filters = Invoke-NsxCli $query -Connection $connection #Potentially there are multiple filters (VM with more than one NIC). foreach ( $filter in $filters ) { #only match slot 2 filters if ( $filter -notmatch 'nic-\d+-eth\d-vmware-sfw.2' ) { Write-Warning "Ignoring filter `'$($filter.Filters)`' on VM $($filter.VM)" } else { #Execute the appropriate CLI query against the VMs host for the current filter... $query = "show dfw host $($VirtualMachine.VMHost.ExtensionData.MoRef.Value) filter $($Filter.Filters) addrset" Invoke-NsxCli $query -SupressWarning -Connection $connection } } } end {} } function Get-NsxHostUvsmLogging { <# .SYNOPSIS Retrieves the Uvsm Logging level from the specified host. INCOMPLETE .DESCRIPTION .EXAMPLE #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VMHostInterop]$VMHost, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #UVSM Logging URI $URI = "/api/1.0/usvmlogging/$($VMHost.Extensiondata.Moref.Value)/root" try { $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection [PSCustomobject]@{ "LoggerName" = $response.LoggingLevel.LoggerName; "LogLevel" = $response.LoggingLevel.Level; "HostName" = $VMhost.Name; "HostId" = $VMhost.Extensiondata.Moref.Value } } catch { Write-Warning "Error querying host $($VMhost.Name) for UVSM logging status. Check Guest Introspection is enabled, and USVM is available." } } end {} } function Set-NsxHostUvsmLogging { <# .SYNOPSIS Sets the Uvsm Logging on the specified host. INCOMPLETE .DESCRIPTION .EXAMPLE #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VMHostInterop]$VMHost, [Parameter (Mandatory = $true)] [ValidateSet("OFF", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE", IgnoreCase = $false)] [string]$LogLevel, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("logginglevel") $xmlDoc.appendChild($xmlRoot) | Out-Null Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "loggerName" -xmlElementText "root" Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "level" -xmlElementText $LogLevel # #Do the post $body = $xmlroot.OuterXml $URI = "/api/1.0/usvmlogging/$($VMhost.Extensiondata.Moref.Value)/changelevel" Write-Progress -Activity "Updating log level on host $($VMhost.Name)" Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection | Out-Null Write-Progress -Activity "Updating log level on host $($VMhost.Name)" -Completed } end {} } function New-NsxManager { <# .SYNOPSIS Uses an existing PowerCLI connection to deploy and configure the NSX Manager VM from OVA. .DESCRIPTION The NSX management plane is provided by NSX Manager, the centralized network management component of NSX. It provides the single point of configuration for NSX operations, and provides NSX's REST API. The New-NsxManager cmdlet deploys and configures a new NSX Manager appliance using PowerCLI .INPUTS System.String System.Int32 System.RuntimeType .EXAMPLE New-NSXManager -NsxManagerOVF ".\VMware-NSX-Manager-6.2.0-2986609.ova" -Name TestingNSXM -ClusterName Cluster1 -ManagementPortGroupName Net1 -DatastoreName DS1 -FolderName Folder1 -CliPassword VMware1!VMware1! -CliEnablePassword VMware1!VMware1! -Hostname NSXManagerHostName -IpAddress 1.2.3.4 -Netmask 255.255.255.0 -Gateway 1.2.3.1 -DnsServer 1.2.3.5 -DnsDomain corp.local -NtpServer 1.2.3.5 -EnableSsh -StartVm -wait Deploys a new NSX Manager, starts the VM, and blocks until the API becomes available. .EXAMPLE $nsxManagerBuildParams = @{ NsxManagerOVF = ".\VMware-NSX-Manager-6.2.0-2986609.ova" Name = TestingNSXM ClusterName = Cluster1 ManagementPortGroupName = Net1 DatastoreName = DS1 FolderName = Folder1 CliPassword = VMware1!VMware1! CliEnablePassword = VMware1!VMware1! Hostname = NSXManagerHostName IpAddress = 1.2.3.4 Netmask = 255.255.255.0 Gateway = 1.2.3.1 DnsServer = 1.2.3.5 DnsDomain = corp.local NtpServer = 1.2.3.5 EnableSsh = $true StartVm = $true Wait = $true } # end $nsxManagerBuildParams New-NSXManager @nsxManagerBuildParams Uses 'splatting' technique to specify build configuration and then deploys a new NSX Manager, starts the VM, and blocks until the API becomes available. #> [CmdletBinding(DefaultParameterSetName = "Default")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. param ( [Parameter ( Mandatory = $True )] #Local Path to NSX MAnager OVA [ValidateScript( { if ( -not (Test-Path $_)) { throw "NSX Manager OVF file not found: $_." } $true })] [string]$NsxManagerOVF, [Parameter ( Mandatory = $True )] #The name of the deployed VM. [ValidateNotNullOrEmpty()] [String]$Name, [Parameter ( Mandatory = $True )] #Name of the vSphere Cluster to which the VM will be deployed. [ValidateNotNullOrEmpty()] [string]$ClusterName, [Parameter ( Mandatory = $True )] #Name of the portgroup to which the management interface of the VM will be connected. [ValidateNotNullOrEmpty()] [string]$ManagementPortGroupName, [Parameter ( Mandatory = $True )] #Name of the Datastore to which the VM will be deployed. [ValidateNotNullOrEmpty()] [string]$DatastoreName, [Parameter ( Mandatory = $True )] #Name of the vSphere VM Inventory folder to which the VM will be deployed. [ValidateNotNullOrEmpty()] [string]$FolderName, [Parameter ( Mandatory = $True )] #CLI Password for the deployed NSX Manager. [ValidateNotNullOrEmpty()] [string]$CliPassword, [Parameter ( Mandatory = $True )] #Enable password for the deployed NSX Manager. [ValidateNotNullOrEmpty()] [string]$CliEnablePassword, [Parameter ( Mandatory = $True )] #Guest Hostname for the deployed NSX Manager. [ValidateNotNullOrEmpty()] [string]$Hostname, [Parameter ( Mandatory = $True )] #IP Address assigned to the management interface. [ValidateNotNullOrEmpty()] [ipaddress]$IpAddress, [Parameter ( Mandatory = $True )] #Netmask for the management interface. [ValidateNotNullOrEmpty()] [ipaddress]$Netmask, [Parameter ( Mandatory = $True )] #Gateway Address for the deployed NSX Manager. [ValidateNotNullOrEmpty()] [ipaddress]$Gateway, [Parameter ( Mandatory = $True )] #DNS Server for the deployed NSX Manager [ValidateNotNullOrEmpty()] [ipaddress[]]$DnsServer, [Parameter ( Mandatory = $True )] #DNS Domain Name for the deployed NSX Manager. [ValidateNotNullOrEmpty()] [string]$DnsDomain, [Parameter ( Mandatory = $True )] #NTP Server for the deployed NSX Manager (One only.) [ValidateNotNullOrEmpty()] [ipAddress]$NtpServer, [Parameter ( Mandatory = $False)] #Configured Memory for the deployed VM. Overrides default in OVA. Non-Production use only! [ValidateRange(8, 16)] [int]$ManagerMemoryGB, [Parameter ( Mandatory = $True, ParameterSetName = "StartVM" )] #Start the VM once deployment is completed. [switch]$StartVM = $false, [Parameter ( Mandatory = $False, ParameterSetName = "StartVM")] #Wait for the NSX Manager API to become available once deployment is complete and the appliance is started. Requires -StartVM, and network reachability between this machine and the management interface of the NSX Manager. [ValidateScript( { If ( -not $StartVM ) { throw "Cant wait for Manager API unless -StartVM is enabled." } $true })] [switch]$Wait = $false, [Parameter ( Mandatory = $False, ParameterSetName = "StartVM")] #How long to wait before timeout for NSX MAnager API to become available once the VM has been started. [int]$WaitTimeout = 600, [Parameter ( Mandatory = $False )] #Enable SSH on the deployed NSX Manager. [switch]$EnableSsh = $false, [Parameter (Mandatory = $false)] #Disk format on the deployed NSX Manager [ValidateSet ("Thin2GB", "Thick", "Thick2GB", "Thin", "EagerZeroedThick")] [string]$DiskStorageFormat = "Thick" ) Begin { # Check that we have a PowerCLI connection open... Write-Verbose -Message "Verifying PowerCLI connection" If ( -not (Test-Path Variable:Global:defaultVIServer) ) { throw "Unable to deploy NSX Manager OVA without a valid PowerCLI connection. Use Connect-VIServer or Connect-NsxServer to extablish a PowerCLI connection and try again." } else { Write-Verbose -Message "PowerCLI connection discovered; validating connection state" if (($Global:defaultViServer).IsConnected -eq $true) { Write-Verbose -Message "Currently connected to VI Server: $Global:defaultViServer" } else { throw "Connection to VI Server: $Global:defaultViServer is present, but not connected. You must be connected to a vCenter Server to continue." } # end if/else } # end if/elseif Write-Verbose -Message "Selecting VMHost for deployment in Cluster: $ClusterName" # Chose a target host that is not in Maintenance Mode and select based on available memory $TargetVMHost = $null $TargetVMHost = Get-Cluster $ClusterName | Get-VMHost | Where-Object { $_.ConnectionState -eq 'Connected' } | Sort-Object MemoryUsageGB | Select-Object -First 1 # throw an error if there are not any hosts suitable for deployment (ie: all hosts are in maint. mode) if ($targetVmHost -eq $null) { throw "Unable to deploy NSX Manager to cluster: $ClusterName. There are no VMHosts suitable for deployment. Check the selected cluster to ensure hosts exist and that at least one is connected and not in Maintenance Mode." } else { Write-Verbose -Message "Deploying to Cluster: $ClusterName and VMHost: $($TargetVMHost.Name)" } # end if/else $targetVmHost.Count } # end BEGIN block Process { Write-Verbose -Message "Setting up OVF configuration" ## Using the PowerCLI command, get OVF draws on the location of the OVA from the defined variable. $OvfConfiguration = Get-OvfConfiguration -Ovf $NsxManagerOVF #Network Mapping to portgroup need to be defined. #6.4.0 GA changed the name of the network that is mapped, so now we need to #determine what it is rather than assume it is vsmgmt $networkobj = Get-Member -MemberType CodeProperty -InputObject $OvfConfiguration.NetworkMapping $networkname = $networkobj.name $OvfConfiguration.NetworkMapping.$networkname.Value = $ManagementPortGroupName # OVF Configuration values. $OvfConfiguration.common.vsm_cli_passwd_0.value = $CliPassword $OvfConfiguration.common.vsm_cli_en_passwd_0.value = $CliEnablePassword $OvfConfiguration.common.vsm_hostname.value = $Hostname $OvfConfiguration.common.vsm_ip_0.value = $IpAddress $OvfConfiguration.common.vsm_netmask_0.value = $Netmask $OvfConfiguration.common.vsm_gateway_0.value = $Gateway $OvfConfiguration.common.vsm_dns1_0.value = $DnsServer.IPAddressToString -join "," $OvfConfiguration.common.vsm_domain_0.value = $DnsDomain $OvfConfiguration.common.vsm_ntp_0.value = $NtpServer $OvfConfiguration.common.vsm_isSSHEnabled.value = $EnableSsh # Deploy the OVA. Write-Progress -Activity "Deploying NSX Manager OVA" $VM = Import-VApp -Source $NsxManagerOvf -OvfConfiguration $OvfConfiguration -Name $Name -Location $ClusterName -VMHost $TargetVMHost -Datastore $DatastoreName -DiskStorageFormat $DiskStorageFormat If ( $PSBoundParameters.ContainsKey('FolderName')) { Write-Progress -Activity "Moving NSX Manager VM to folder: $folderName" $VM | Move-VM -Location $FolderName Write-Progress -Activity "Moving NSX Manager VM to folder: $folderName" -Completed } # end if if ( $PSBoundParameters.ContainsKey('ManagerMemoryGB') ) { # Hack VM to reduce Ram for constrained environments. This is NOT SUITABLE FOR PRODUCTION!!! Write-Warning -Message "Changing Memory configuration of NSX Manager VM to $ManagerMemoryGB GB. Not supported for Production Use!" # start Get-VM $Name | Set-VM -MemoryGB $ManagerMemoryGB -Confirm:$false | Get-VMResourceConfiguration | Set-VMResourceConfiguration -MemReservationMB 0 -CpuReservationMhz 0 | Out-Null # end } # end if Write-Progress -Activity "Deploying NSX Manager OVA" -Completed if ( $StartVM ) { Write-Progress -Activity "Starting NSX Manager" $VM | Start-VM Write-Progress -Activity "Starting NSX Manager" -Completed } # end if if ( $PSBoundParameters.ContainsKey('Wait')) { # User wants to wait for Manager API to start. $waitStep = 30 $Timer = 0 Write-Progress -Activity "Waiting for NSX Manager api to become available" -PercentComplete $(($Timer / $WaitTimeout) * 100) do { # sleep a while, the VM will take time to start fully.. Start-Sleep $WaitStep $Timer += $WaitStep try { # use splatting to keep the line width in-check/make it easier to read parameters $connectParams = $null $connectParams = @{ NsxServer = $ipAddress UserName = 'admin' Password = $cliPassword DisableViAutoConnect = $true DefaultConnection = $false } # end $connectParams # casting to [void]; it inches some performance out by not having to process anything through the pipeline; sans | Out-Null Connect-NsxServer @connectParams | Out-Null break } catch { Write-Progress -Activity "Waiting for NSX Manager api to become available" -PercentComplete $(($Timer / $WaitTimeout) * 100) } # end try/catch if ( $Timer -ge $WaitTimeout ) { # We exceeded the timeout - what does the user want to do? $message = "Waited more than $WaitTimeout seconds for NSX Manager API to become available. Recommend checking boot process, network config etc." $question = "Continue waiting for NSX Manager?" $yesnochoices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $yesnochoices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $yesnochoices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $yesnochoices, 0) if ($decision -eq 0) { # User waits... $Timer = 0 } else { throw "Timeout waiting for NSX Manager appliance API to become available." } # end if/else $decision } # end if $Timer -ge $WaitTimeout } while ( $true ) Write-Progress -Activity "Waiting for NSX Manager api to become available" -Completed } # end if $PSBoundParameters.ContainsKey('Wait') } # end PROCESS block End { } # end END block } # end function New-NsxManager function Set-NsxManager { <# .SYNOPSIS Configures appliance settings for an existing NSX Manager appliance. .DESCRIPTION The NSX management plane is provided by NSX Manager, the centralized network management component of NSX. It provides the single point of configuration for NSX operations, and provides NSX's REST API. The Set-NsxManager cmdlet allows configuration of the appliance settings such as syslog, vCenter registration and SSO configuration. .EXAMPLE Set-NsxManager -NtpServer 0.pool.ntp.org -Timezone UTC Configures NSX Manager NTP Server. .EXAMPLE Set-NsxManager -SyslogServer syslog.corp.local -SyslogPort 514 -SyslogProtocol tcp Configures NSX Manager Syslog destination. .EXAMPLE Set-NsxManager -ssoserver sso.corp.local -ssousername administrator@vsphere.local -ssopassword VMware1! Configures the SSO Server registration of NSX Manager. .EXAMPLE Set-NsxManager -vcenterusername administrator@vsphere.local -vcenterpassword VMware1! -vcenterserver vcenter.corp.local Configures the vCenter registration of NSX Manager. .EXAMPLE Set-NsxManager -vcenterusername administrator@vsphere.local -vcenterpassword VMware1! -vcenterserver vcenter.corp.local -sslThumbPrint F6:48:11:0E:AC:F7:93:A3:1B:60:8F:A2:53:C4:88:77:3F:CD:B1:81 Configures the vCenter registration of NSX Manager using the specified thumbprint. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", "", Scope = "Function", Target = "*")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] #Cant remove without breaking backward compatibility Param ( [Parameter (Mandatory = $True, ParameterSetName = "Syslog")] #Syslog server to which syslogs will be forwarded. [ValidateNotNullOrEmpty()] [string]$SyslogServer, [Parameter (Mandatory = $False, ParameterSetName = "Syslog")] #TCP/UDP port on destination syslog server to connect to. [ValidateRange (1, 65535)] [int]$SyslogPort = 514, [Parameter (Mandatory = $False, ParameterSetName = "Syslog")] #Syslog Protocol - either TCP or UDP. [ValidateSet ("tcp", "udp")] [string]$SyslogProtocol = "udp", [Parameter (Mandatory = $True, ParameterSetName = "Sso")] #SSO Server to register this NSX Manager with. [ValidateNotNullOrEmpty()] [string]$SsoServer, [Parameter (Mandatory = $False, ParameterSetName = "Sso")] #TCP Port on SSO server to connect to when registering. [ValidateNotNullOrEmpty()] [string]$SsoPort = 443, [Parameter (Mandatory = $True, ParameterSetName = "Sso")] #SSO Username used for registration. [ValidateNotNullOrEmpty()] [string]$SsoUserName, [Parameter (Mandatory = $True, ParameterSetName = "Sso")] #SSO Password used for registration. [ValidateNotNullOrEmpty()] [string]$SsoPassword, [Parameter (Mandatory = $True, ParameterSetName = "vCenter")] #vCenter server to register this NSX Manager with. [ValidateNotNullOrEmpty()] [string]$vCenterServer, [Parameter (Mandatory = $True, ParameterSetName = "vCenter")] #UserName used for vCenter connection. [ValidateNotNullOrEmpty()] [string]$vCenterUserName, [Parameter (Mandatory = $True, ParameterSetName = "vCenter")] #Password used for vCenter connection. [ValidateNotNullOrEmpty()] [string]$vCenterPassword, [Parameter (Mandatory = $False, ParameterSetName = "vCenter")] [Parameter (Mandatory = $False, ParameterSetName = "Sso")] #SSL Thumbprint to validate certificate presented by SSO/vCenter server against. [ValidateNotNullOrEmpty()] [string]$SslThumbprint = "", [Parameter (Mandatory = $False, ParameterSetName = "vCenter")] [Parameter (Mandatory = $False, ParameterSetName = "Sso")] #Accept any SSL certificate presented by SSO/vCenter. [switch]$AcceptAnyThumbprint = $True, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument switch ( $PsCmdlet.ParameterSetName ) { "Syslog" { $role = Get-NsxUserRole $Connection.Credential.Username if ( $role.role -ne 'super_user' ) { throw "Appliance Management APIs require a local NSX Manager account (super_user role access) " } #Create the XMLRoot [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("syslogserver") $xmlDoc.appendChild($xmlRoot) | Out-Null #Create an Element and append it to the root Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "syslogServer" -xmlElementText $syslogServer.ToString() Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "port" -xmlElementText $SyslogPort.ToString() Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "protocol" -xmlElementText $SyslogProtocol $uri = "/api/1.0/appliance-management/system/syslogserver" Invoke-NsxRestMethod -method "put" -body $xmlRoot.outerXml -URI $uri -connection $Connection } "Sso" { If ( (-not $PsBoundParameters.ContainsKey('SslThumbprint')) -and (-not $AcceptAnyThumbprint )) { Throw "Must specify an SSL Thumbprint or AcceptAnyThumbprint" } if ($PsBoundParameters.ContainsKey('SslThumbprint')) { #Explicit override of the default behaviour. $AcceptAnyThumbprint = $False } #Create the XMLRoot [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("ssoConfig") $xmlDoc.appendChild($xmlRoot) | Out-Null #Create an Element and append it to the root $SsoLookupServiceUrl = "https://$($SsoServer.ToString()):$($SsoPort.ToString())/lookupservice/sdk" Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "ssoLookupServiceUrl" -xmlElementText $SsoLookupServiceUrl Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "ssoAdminUsername" -xmlElementText $SsoUserName.ToString() Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "ssoAdminUserpassword" -xmlElementText $SsoPassword Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "certificateThumbprint" -xmlElementText $SslThumbprint $uri = "/api/2.0/services/ssoconfig" try { $null = Invoke-NsxWebRequest -method "post" -body $xmlRoot.outerXml -URI $uri -connection $Connection } catch { #it sucks that at the moment I can't parse the response body as xml :( I really need to fix this. $thumbprintMatch = '[<"]details[>"]:*"*(([A-F0-9]{2}:)+[A-F0-9]{2})' if (($AcceptAnyThumbprint) -and ($_ -match $thumbprintMatch)) { #API responded with a thumbprint Write-Warning "Using thumbprint presented by the SSO server: $($Matches[1])" $xmlRoot.certificateThumbprint = $matches[1] $null = Invoke-NsxWebRequest -method "post" -body $xmlRoot.outerXml -URI $uri -connection $Connection } else { #rethrow throw "An error occured configuring the specified SSO server. $_" } } } "vCenter" { If ( (-not $PsBoundParameters.ContainsKey('SslThumbprint')) -and (-not $AcceptAnyThumbprint )) { Throw "Must specify an SSL Thumbprint or AcceptAnyThumbprint" } if ($PsBoundParameters.ContainsKey('SslThumbprint')) { #Explicit override of the default behaviour. $AcceptAnyThumbprint = $False } #Build the XML [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("vcInfo") $xmlDoc.appendChild($xmlRoot) | Out-Null Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "ipAddress" -xmlElementText $vCenterServer.ToString() Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "userName" -xmlElementText $vCenterUserName.ToString() Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "password" -xmlElementText $vCenterPassword.ToString() Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "certificateThumbprint" -xmlElementText $SslThumbprint Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "assignRoleToUser" -xmlElementText "true" Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "pluginDownloadServer" -xmlElementText "" $uri = "/api/2.0/services/vcconfig" try { $null = Invoke-NsxWebRequest -method "put" -body $xmlRoot.outerXml -URI $uri -connection $Connection } catch { #it sucks that at the moment I can't parse the response body as xml :( I really need to fix this. $thumbprintMatch = '[<"]details[>"]:*"*(([A-F0-9]{2}:)+[A-F0-9]{2})' if (($AcceptAnyThumbprint) -and ($_ -match $thumbprintMatch)) { #API responded with a thumbprint Write-Warning "Using thumbprint presented by the vCenter server: $($Matches[1])" $xmlRoot.certificateThumbprint = $matches[1] $null = Invoke-NsxWebRequest -method "put" -body $xmlRoot.outerXml -URI $uri -connection $Connection } else { #rethrow throw "An error occured configuring the specified vCenter server. $_" } } } } } function Get-NsxManagerCertificate { <# .SYNOPSIS Retrieves NSX Manager Certificates. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. Details of the SSL Certificate installed on the NSX Manager are required by certain workflows within NSX The Get-NsxManagerCertificate cmdlet retrieves the configured certificates configured on the NSX Manager against which the command is run. .EXAMPLE Get-NsxManagerCertificate Retrieves the SSL Certificates from the connected NSX Manager Get-NsxManagerCertificate | where { $_.isCa -eq "false" } | select-object sha1Hash Retrieves the SSL Certificates SHA1 hash from the connected NSX Manager #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $role = Get-NsxUserRole $Connection.Credential.Username if ( $role.role -ne 'super_user' ) { throw "Appliance Management APIs require a local NSX Manager account (super_user role access) " } $URI = "/api/1.0/appliance-management/certificatemanager/certificates/nsx" [System.Xml.XmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::x509Certificates/x509certificate')) { $response.X509Certificates.x509certificate } } function Get-NsxManagerSsoConfig { <# .SYNOPSIS Retrieves NSX Manager SSO Configuration. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The SSO configuration of NSX Manager controls its registration with VMware SSO server for authentication purposes. The Get-NsxManagerSsoConfig cmdlet retrieves the SSO configuration and status of the NSX Manager against which the command is run. .EXAMPLE Get-NsxManagerSsoConfig Retrieves the SSO configuration from the connected NSX Manager #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $URI = "/api/2.0/services/ssoconfig" [System.Xml.XmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::ssoConfig/vsmSolutionName')) { $ssoConfig = $response.ssoConfig #Only if its configured do we get status $URI = "/api/2.0/services/ssoconfig/status" [System.Xml.XmlDocument]$status = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection Add-XmlElement -xmlRoot $ssoConfig -xmlElementName "Connected" -xmlElementText $status.boolean #really? Boolean? What bonehead wrote this API? $ssoConfig } } function Get-NsxManagerVcenterConfig { <# .SYNOPSIS Retrieves NSX Manager vCenter Configuration. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The vCenter configuration of NSX Manager controls its registration with VMware vCenter server for authentication purposes. The Get-NsxManagerVcenterConfig cmdlet retrieves the vCenterconfiguration and status of the NSX Manager against which the command is run. .EXAMPLE Get-NsxManagerSsoConfig Retrieves the SSO configuration from the connected NSX Manager #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $URI = "/api/2.0/services/vcconfig" [System.Xml.XmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::vcInfo/ipAddress')) { $vcConfig = $response.vcInfo #Only if its configured do we get status $URI = "/api/2.0/services/vcconfig/status" [System.Xml.XmlDocument]$status = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection Add-XmlElement -xmlRoot $vcConfig -xmlElementName "Connected" -xmlElementText $status.vcConfigStatus.Connected $vcConfig } } function Get-NsxManagerTimeSettings { <# .SYNOPSIS Retrieves NSX Manager Time Configuration. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Get-NsxManagerTimeSettings cmdlet retrieves the time related configuration of the NSX Manager against which the command is run. .EXAMPLE Get-NsxManagerTimeSettings Retrieves the time configuration from the connected NSX Manager #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $role = Get-NsxUserRole $Connection.Credential.Username -Connection $connection if ( $role.role -ne 'super_user' ) { throw "Appliance Management APIs require a local NSX Manager account (super_user role access) " } $URI = "/api/1.0/appliance-management/system/timesettings" $result = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection #NSX 6.2.3/4 changed API schema here! :( Grrrr. Have to test and return consistent object if ( $result -is [System.Xml.XmlDocument]) { #Assume the timesettings child exists. $result.timeSettings } elseif ( $result -is [pscustomobject] ) { #Pre 6.2.3 manager response. [System.XML.XMLDocument]$xmldoc = New-Object System.Xml.XmlDocument [System.XML.XMLElement]$xmlTimeSettings = $xmlDoc.CreateElement('timeSettings') $xmldoc.AppendChild($xmlTimeSettings) | Out-Null [System.XML.XMLElement]$xmlNTPServerString = $xmlDoc.CreateElement('ntpServer') $xmlTimeSettings.Appendchild($xmlNTPServerString) | Out-Null Add-XmlElement -xmlRoot $xmlNTPServerString -xmlElementName "string" -xmlElementText $result.ntpServer Add-XmlElement -xmlRoot $xmlTimeSettings -xmlElementName "datetime" -xmlElementText $result.datetime Add-XmlElement -xmlRoot $xmlTimeSettings -xmlElementName "timezone" -xmlElementText $result.timezone $xmlTimeSettings } } function Set-NsxManagerTimeSettings { <# .SYNOPSIS Configures -NtpServer and TimeZone settings for an existing NSX Manager appliance. .DESCRIPTION The NSX management plane is provided by NSX Manager, the centralized network management component of NSX. It provides the single point of configuration for NSX operations, and provides NSX's REST API. The Set-NsxManagerTimeZone cmdlet allows configuration of the appliances timezone related settings. .EXAMPLE Set-NsxManagerTimeSettings -NtpServer 0.pool.ntp.org -Timezone UTC Configures NSX Manager NTP Server. #> Param ( [Parameter (Mandatory = $False)] #NTP server for time synchronization.. [ValidateNotNullOrEmpty()] [string[]]$NtpServer, [Parameter (Mandatory = $False)] #Time Zone, default UTC. [ValidateNotNullOrEmpty()] [string]$TimeZone = "UTC", [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $role = Get-NsxUserRole $Connection.Credential.Username -Connection $Connection if ( $role.role -ne 'super_user' ) { throw "Appliance Management APIs require a local NSX Manager account (super_user role access) " } $uri = "/api/1.0/appliance-management/system/timesettings" [System.Xml.XmlDocument]$Existing = Invoke-NsxRestMethod -method get -URI $uri -connection $Connection #Api barfs if we set the time with the value we get from it. And in a non intuitive way... sigh again... $null = $Existing.timeSettings.RemoveChild((Invoke-XpathQuery -Node $Existing -QueryMethod SelectSingleNode -query "child::timeSettings/datetime") ) If ( Invoke-XpathQuery -Node $Existing -QueryMethod SelectSingleNode -query "child::timeSettings") { if ( $PSBoundParameters.ContainsKey("ntpserver")) { if ( Invoke-XpathQuery -Node $Existing -QueryMethod SelectSingleNode -query "child::timeSettings/ntpServer" ) { Write-Warning "Existing NTP servers are configured and will be retained. Use Clear-NsxManagerTimeSettings to remove them." #Api doesnt allow 'updates', so we have to save existing, then remove, then readd the union of old and new. Clear-NsxManagerTimeSettings -Connection $Connection foreach ($Server in $ntpserver) { if ( $Existing.timeSettings.ntpServer.string -notcontains $server ) { Add-XmlElement -xmlRoot $Existing.timeSettings.ntpServer -xmlElementName "string" -xmlElementText $server.ToString() } } } else { [System.XML.XMLElement]$xmlNtpNode = $Existing.CreateElement('ntpServer') $Existing.timeSettings.Appendchild($xmlNtpNode) | Out-Null foreach ($Server in $ntpserver) { Add-XmlElement -xmlRoot $xmlNtpNode -xmlElementName "string" -xmlElementText $server.ToString() } } } if ($PSBoundParameters.ContainsKey("TimeZone")) { $Existing.timeSettings.timezone = $timeZone.ToString() } $uri = "/api/1.0/appliance-management/system/timesettings" $null = Invoke-NsxRestMethod -method put -body $Existing.timeSettings.outerXml -URI $uri -connection $Connection Get-NsxManagerTimeSettings -Connection $Connection } else { throw "Unexpected response from API when querying existing time settings." } } function Clear-NsxManagerTimeSettings { <# .SYNOPSIS Removes NtpServer and TimeZone settings for an existing NSX Manager appliance. .DESCRIPTION The NSX management plane is provided by NSX Manager, the centralized network management component of NSX. It provides the single point of configuration for NSX operations, and provides NSX's REST API. The Remove-NsxManagerTimeSettings cmdlet allows an appliances existing timezone related settings to be cleared. .EXAMPLE Remove-NsxManagerTimeSettings UnConfigures NSX Manager NTP Server and TimeZone. #> Param ( [switch]$ClearTimeZone = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $role = Get-NsxUserRole $Connection.Credential.Username -Connection $connection if ( $role.role -ne 'super_user' ) { throw "Appliance Management APIs require a local NSX Manager account (super_user role access) " } $Existing = Get-NsxManagerTimeSettings -Connection $connection if ( Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $Existing -query "child::ntpServer") { #API errors if you clear when there arent any existing NTP servers configured... sigh... $uri = "/api/1.0/appliance-management/system/timesettings/ntp" $null = Invoke-NsxRestMethod -method delete -URI $uri -connection $Connection } if ($ClearTimeZone ) { $null = Set-NsxManagerTimeSettings -TimeZone UTC -Connection $connection } } function Get-NsxManagerSyslogServer { <# .SYNOPSIS Retrieves NSX Manager Syslog Configuration. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Get-NsxManagerSyslog cmdlet retrieves the time related configuration of the NSX Manager against which the command is run. .EXAMPLE Get-NsxManagerSyslogServer Retrieves the Syslog server configuration from the connected NSX Manager #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $role = Get-NsxUserRole $Connection.Credential.Username if ( $role.role -ne 'super_user' ) { throw "Appliance Management APIs require a local NSX Manager account (super_user role access) " } $URI = "/api/1.0/appliance-management/system/syslogserver" $result = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection # Make sure we actually get a response. If there are no syslog servers # configured, then the API does not return any response body. if ( $result ) { #NSX 6.2.3/4 changed API schema here! :( Grrrr. Have to test and return consistent object if ( $result -is [System.Xml.XmlDocument]) { #Assume the timesettings child exists. $result.syslogserver } elseif ( $result -is [pscustomobject] ) { #Pre 6.2.3 manager response. [System.XML.XMLDocument]$xmldoc = New-Object System.Xml.XmlDocument [System.XML.XMLElement]$xmlSyslog = $xmlDoc.CreateElement('syslogserver') $xmldoc.AppendChild($xmlSyslog) | Out-Null Add-XmlElement -xmlRoot $xmlSyslog -xmlElementName "syslogServer" -xmlElementText $result.syslogServer Add-XmlElement -xmlRoot $xmlSyslog -xmlElementName "port" -xmlElementText $result.port Add-XmlElement -xmlRoot $xmlSyslog -xmlElementName "protocol" -xmlElementText $result.protocol $xmlSyslog } } } function Get-NsxManagerNetwork { <# .SYNOPSIS Retrieves NSX Manager Network Configuration. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Get-NsxManagerNetwork cmdlet retrieves the network related configuration of the NSX Manager against which the command is run. .EXAMPLE Get-NsxManagerNetwork Retrieves the Syslog server configuration from the connected NSX Manager #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $role = Get-NsxUserRole $Connection.Credential.Username if ( $role.role -ne 'super_user' ) { throw "Appliance Management APIs require a local NSX Manager account (super_user role access) " } $URI = "/api/1.0/appliance-management/system/network" $result = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $result -is [System.Xml.XmlDocument]) { #Assume the child exists. $result.network } elseif ( $result -is [pscustomobject] ) { #Pre 6.2.3 manager response. #This hacky attempt to return a consistent object is definately not that universal - but there is fidelity lost in the API reponse that #prevents me from easily reconsructing the correct XML. So I had to reverse engineer based on a 6.2.3 example response. Hopefully this #will just go away quietly... [System.XML.XMLDocument]$xmldoc = New-Object System.Xml.XmlDocument [System.XML.XMLElement]$xmlnetwork = $xmlDoc.CreateElement('network') [System.XML.XMLElement]$xmlnetworkIPv4AddressDto = $xmlDoc.CreateElement('networkIPv4AddressDto') $xmldoc.AppendChild($xmlnetwork) | Out-Null if ( $result.networkIPv4AddressDto) { $xmlnetwork.AppendChild($xmlnetworkIPv4AddressDto) | Out-Null Add-XmlElement -xmlRoot $xmlnetworkIPv4AddressDto -xmlElementName "ipv4Address" -xmlElementText $result.networkIPv4AddressDto.ipv4Address Add-XmlElement -xmlRoot $xmlnetworkIPv4AddressDto -xmlElementName "ipv4NetMask" -xmlElementText $result.networkIPv4AddressDto.ipv4NetMask Add-XmlElement -xmlRoot $xmlnetworkIPv4AddressDto -xmlElementName "ipv4Gateway" -xmlElementText $result.networkIPv4AddressDto.ipv4Gateway } if ( $result.hostname ) { Add-XmlElement -xmlRoot $xmlnetwork -xmlElementName "hostName" -xmlElementText $result.hostname } if ( $result.domainName ) { Add-XmlElement -xmlRoot $xmlnetwork -xmlElementName "domainName" -xmlElementText $result.domainName } if ( $result.networkIPv6AddressDto) { [System.XML.XMLElement]$xmlnetworkIPv6AddressDto = $xmlDoc.CreateElement('networkIPv6AddressDto') $xmlnetwork.AppendChild($xmlnetworkIPv6AddressDto) | Out-Null Add-XmlElement -xmlRoot $xmlnetworkIPv6AddressDto -xmlElementName "ipv6Address" -xmlElementText $result.networkIPv4AddressDto.ipv6Address Add-XmlElement -xmlRoot $xmlnetworkIPv6AddressDto -xmlElementName "ipv6NetMask" -xmlElementText $result.networkIPv4AddressDto.ipv6NetMask Add-XmlElement -xmlRoot $xmlnetworkIPv6AddressDto -xmlElementName "ipv6Gateway" -xmlElementText $result.networkIPv4AddressDto.ipv6Gateway } if ( $result.dns ) { [System.XML.XMLElement]$xmldns = $xmlDoc.CreateElement('dns') $xmlnetwork.AppendChild($xmldns) | Out-Null foreach ( $server in $result.dns.ipv4Dns ) { Add-XmlElement -xmlRoot $xmldns -xmlElementName "ipv4Address" -xmlElementText $server } foreach ( $server in $result.dns.ipv6Dns ) { Add-XmlElement -xmlRoot $xmldns -xmlElementName "ipv6Address" -xmlElementText $server } if ( $result.dns.domainList ) { Add-XmlElement -xmlRoot $xmldns -xmlElementName "domainList" -xmlElementText $result.dns.domainList } } $xmlnetwork } } function Get-NsxManagerBackup { <# .SYNOPSIS Retrieves NSX Manager Backup Configuration. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Get-NsxManagerBackup cmdlet retrieves the backup related configuration of the NSX Manager against which the command is run. .EXAMPLE Get-NsxManagerBackup Retrieves the Backup server configuration from the connected NSX Manager #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $role = Get-NsxUserRole $Connection.Credential.Username if ( $role.role -ne 'super_user' ) { throw "Appliance Management APIs require a local NSX Manager account (super_user role access) " } $URI = "/api/1.0/appliance-management/backuprestore/backupsettings" $result = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $result -is [System.Xml.XmlDocument]) { #Assume the child exists. $result.backupRestoreSettings } elseif ( $result -is [pscustomobject] ) { #Pre 6.2.3 manager response. #This hacky attempt to return a consistent object is definately not that universal - but there is fidelity lost in the API reponse that #prevents me from easily reconsructing the correct XML. So I had to reverse engineer based on a 6.2.3 example response. Hopefully this #will just go away quietly... [System.XML.XMLDocument]$xmldoc = New-Object System.Xml.XmlDocument [System.XML.XMLElement]$xmlbackupRestoreSettings = $xmlDoc.CreateElement('backupRestoreSettings') foreach ( $Property in ($result | Get-Member -MemberType NoteProperty )) { if ( $result."$($Property.Name)" -is [string]) { Add-XmlElement -xmlRoot $xmlbackupRestoreSettings -xmlElementName "$($Property.Name)" -xmlElementText $result."$($Property.Name)" } elseif ( $result."$($Property.Name)" -is [system.object]) { [System.XML.XMLElement]$xmlObjElement = $xmlDoc.CreateElement($Property.Name) $xmlbackupRestoreSettings.AppendChild($xmlObjElement) | Out-Null foreach ( $ElementProp in ($result."$($Property.Name)" | Get-Member -MemberType NoteProperty )) { Add-XmlElement -xmlRoot $xmlObjElement -xmlElementName "$($ElementProp.Name)" -xmlElementText $result."$($Property.Name)"."$($ElementProp.Name)" } } } $xmlbackupRestoreSettings } } function Get-NsxManagerComponentSummary { <# .SYNOPSIS Retrieves NSX Manager Component Summary Information. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Get-NsxManagerComponentSummary cmdlet retrieves the component summary related information of the NSX Manager against which the command is run. .EXAMPLE Get-NsxManagerComponentSummary Retrieves the component summary information from the connected NSX Manager #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $role = Get-NsxUserRole $Connection.Credential.Username if ( $role.role -ne 'super_user' ) { throw "Appliance Management APIs require a local NSX Manager account (super_user role access) " } $URI = "/api/1.0/appliance-management/summary/components" $result = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $result -is [System.Xml.XmlDocument]) { #Assume the child exists. $result.ComponentsSummary } elseif ( $result -is [pscustomobject] ) { #Pre 6.2.3 manager response. #This hacky attempt to return a consistent object is definately not that universal - but there is fidelity lost in the API reponse that #prevents me from easily reconsructing the correct XML. So I had to reverse engineer based on a 6.2.3 example response. Hopefully this #will just go away quietly... [System.XML.XMLDocument]$xmldoc = New-Object System.Xml.XmlDocument [System.XML.XMLElement]$xmlComponentsSummary = $xmlDoc.CreateElement('componentsSummary') [System.XML.XMLElement]$xmlComponentsByGroup = $xmlDoc.CreateElement('componentsByGroup') $xmldoc.AppendChild($xmlComponentsSummary) | Out-Null $xmlComponentsSummary.AppendChild($xmlComponentsByGroup) | Out-Null foreach ( $NamedProperty in (Get-Member -InputObject $result.componentsByGroup -MemberType NoteProperty)) { [System.XML.XMLElement]$xmlEntry = $xmlDoc.CreateElement('entry') $xmlComponentsByGroup.AppendChild($xmlEntry) | Out-Null Add-XmlElement -xmlRoot $xmlEntry -xmlElementName "string" -xmlElementText $NamedProperty.Name [System.XML.XMLElement]$xmlComponents = $xmlDoc.CreateElement('components') $xmlEntry.AppendChild($xmlComponents) | Out-Null foreach ( $component in $result.componentsByGroup.($NamedProperty.name).components) { [System.XML.XMLElement]$xmlComponent = $xmlDoc.CreateElement('component') $xmlComponents.AppendChild($xmlComponent) | Out-Null foreach ( $NoteProp in ($component | Get-Member -MemberType NoteProperty) ) { #Check if I actually have a value if ( $component.($NoteProp.Name) ) { $Property = $component.($NoteProp.Name) Write-Debug "GetType: $($Property.gettype())" Write-Debug "Is: $($Property -is [array])" #Switch on my value switch ( $Property.gettype() ) { "System.Object[]" { Write-Debug "In: Array" [System.XML.XMLElement]$xmlCompArray = $xmlDoc.CreateElement($NoteProp.Name) $xmlComponent.AppendChild($xmlCompArray) | Out-Null foreach ( $member in $Property ) { #All examples ive seen have strings, but not sure if this will stand up to scrutiny... Add-XmlElement -xmlRoot $xmlCompArray -xmlElementName $member.GetType().Name.tolower() -xmlElementText $member.ToString() } } "string" { Write-Debug "In: String" Add-XmlElement -xmlRoot $xmlComponent -xmlElementName $NoteProp.Name -xmlElementText $Property } "bool" { Write-Debug "In: Bool" Add-XmlElement -xmlRoot $xmlComponent -xmlElementName $NoteProp.Name -xmlElementText $Property.ToString().tolower() } default { Write-Debug "Fuck it : $_" } } } } } } $xmlComponentsSummary } } function Get-NsxManagerSystemSummary { <# .SYNOPSIS Retrieves NSX Manager System Summary Information. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Get-NsxManagerSystemSummary cmdlet retrieves the component summary related information of the NSX Manager against which the command is run. .EXAMPLE Get-NsxManagerSystemSummary Retrieves the system summary information from the connected NSX Manager #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $role = Get-NsxUserRole $Connection.Credential.Username if ( $role.role -ne 'super_user' ) { throw "Appliance Management APIs require a local NSX Manager account (super_user role access) " } $URI = "/api/1.0/appliance-management/summary/system" $result = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $result -is [System.Xml.XmlDocument]) { #Assume the child exists. $result.systemSummary } elseif ( $result -is [pscustomobject] ) { #Pre 6.2.3 manager response. #This hacky attempt to return a consistent object is definately not that universal - but there is fidelity lost in the API reponse that #prevents me from easily reconsructing the correct XML. So I had to reverse engineer based on a 6.2.3 example response. Hopefully this #will just go away quietly... [System.XML.XMLDocument]$xmldoc = New-Object System.Xml.XmlDocument [System.XML.XMLElement]$xmlsystemSummary = $xmlDoc.CreateElement('systemSummary') foreach ( $Property in ($result | Get-Member -MemberType NoteProperty )) { if ( $result."$($Property.Name)" -is [string]) { Add-XmlElement -xmlRoot $xmlsystemSummary -xmlElementName "$($Property.Name)" -xmlElementText $result."$($Property.Name)" } elseif ( $result."$($Property.Name)" -is [system.object]) { [System.XML.XMLElement]$xmlObjElement = $xmlDoc.CreateElement($Property.Name) $xmlsystemSummary.AppendChild($xmlObjElement) | Out-Null foreach ( $ElementProp in ($result."$($Property.Name)" | Get-Member -MemberType NoteProperty )) { Add-XmlElement -xmlRoot $xmlObjElement -xmlElementName "$($ElementProp.Name)" -xmlElementText $result."$($Property.Name)"."$($ElementProp.Name)" } } } $xmlsystemSummary } } function Get-NsxManagerRole { <# .SYNOPSIS Retrieves NSX Manager Role Configuration. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Get-NsxManagerRole cmdlet retrieves the universal sync role of the NSX Manager against which the command is run. .EXAMPLE Get-NsxManagerRole Retrieves the universal sync role from the connected NSX Manager #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $URI = "/api/2.0/universalsync/configuration/role" [System.Xml.XmlDocument]$result = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $result -query 'child::universalSyncRole') { $result.universalSyncRole } } function Set-NsxManagerRole { <# .SYNOPSIS Sets the NSX Manager Role. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Set-NsxManagerRole cmdlet sets the universal sync role of the NSX Manager against which the command is run. The only state transitions that are allowed are Standalone (default) to Primary, Secondary to Primary, Primary to StandAlone, or Secondary to StandAlone. This cmdlet does not configure a manager as the Secondary role. To configure an NSX Manager as secondary, you must use Add-NsxSecondaryManager against the Primary NSX Manager. .EXAMPLE Set-NsxManagerRole -Role Primary Sets the universal sync role to Primary for the connected NSX Manager .EXAMPLE Set-NsxManagerRole -Role StandAlone Sets the universal sync role to Standalone for the connected NSX Manager. Note, if running this against a manager that currently is configured as secondary, and universal objects exist, then the state will transition to TRANSIT rather than standalone. The may then be configured as PRIMARY, or if all universal objects are deleted, as STANDALONE. #> param ( [Parameter (Mandatory = $True)] #New Role for connected NSX Manager [ValidateSet("Primary", "StandAlone")] [String]$Role, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) switch ($role) { "Primary" { $URI = "/api/2.0/universalsync/configuration/role?action=set-as-primary" } "StandAlone" { $URI = "/api/2.0/universalsync/configuration/role?action=set-as-standalone" } Default { Throw "Not Implemented" } } try { $null = Invoke-NsxWebRequest -method "post" -URI $URI -connection $connection } Catch { $ParsedXmlError = $false if ( $_ -match '.*(\<\?xml version="1\.0" encoding="UTF-8"\?\>\s.*)' ) { if ( $matches[1] -as [xml] ) { $Error = [xml]$matches[1] $ErrorCode = Invoke-XpathQuery -Node $error -QueryMethod SelectSingleNode -query "child::error/errorCode" if ( $errorCode.'#text' -eq '125023') { Write-Warning $Error.error.details $ParsedXmlError = $true } } } if ( -not $ParsedXmlError ) { #If we didnt get some XML out of the error that we parsed as expected... Throw "Failed setting NSX Manager role. $_" } } #Regetting here, to catch the in transit state that a secondary edge will likely be when told to become standalone Get-NsxManagerRole -Connection $Connection } function Invoke-NsxManagerSync { <# .SYNOPSIS Triggers synchronisation of universal objects from a primary NSX Manager. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Invoke-NsxManagerSync cmdlet triggers the universal sync service to replicate universally scoped objects to secondary NSX Managers. No response is returned from a successful call. Use Get-NsxManagerSyncStatus to determine last successful sync time. .EXAMPLE Invoke-NsxManagerSync Triggers synchronisation. May only be run on a primary NSX manager. #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $URI = "/api/2.0/universalsync/sync?action=invoke" try { $null = Invoke-NsxWebRequest -method "post" -URI $URI -connection $connection } catch { Throw "Failed to invoke synchronisation. $_" } } function Get-NsxManagerSyncStatus { <# .SYNOPSIS Retrieves NSX Manager universal sync status. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Get-NsxManagerSyncStatus cmdlet retrieves the current status of the universal sync service on the NSX Manager against which the command is run. .EXAMPLE Get-NsxManagerSyncStatus Returns the universal replication syncronisation status for the default NSX manager. #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $URI = "/api/2.0/universalsync/status" [System.Xml.XmlDocument]$result = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $result -query 'child::replicationStatus') { $result.replicationStatus } } function Add-NsxSecondaryManager { <# .SYNOPSIS Adds a standalone NSX Manager to an existing CrossVC configured NSX environment. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Add-NsxSecondaryManager cmdlet adds a standalone NSX Manager to a CrossVC configured NSX environment. The connected NSX Manager must be configured with the Primary Role, and the standalone NSX Manager to be added must be configured with the Standalone role. .EXAMPLE Add-NsxSecondaryManager -NsxManager nsx-m-01b -Username admin -Password VMware1! -AcceptPresentedThumbprint Adds the NSX Manager nsx-m-01b as a secondary to the currently connected primary NSX Manager and accepts whatever thumbprint the server returns. .EXAMPLE Add-NsxSecondaryManager -NsxManager nsx-m-01b -Credential $Cred -AcceptPresentedThumbprint Adds the NSX Manager nsx-m-01b as a secondary to the currently connected primary NSX Manager and accepts whatever thumbprint the server returns. Credentials are specified as a PSCredential object. .EXAMPLE Add-NsxSecondaryManager -NsxManager nsx-m-01b -Username admin -Password VMware1! -Thumbprint d7:8d:8a:06:55:52:2a:49:00:06:b1:58:c2:cd:2b:82:21:6b:2f:92 Adds the NSX Manager nsx-m-01b as a secondary to the currently connected primary NSX Manager and validates that the thumbprint presented by the server is as specified. #> [CmdletBinding(DefaultParameterSetName = "Credential")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", "", Scope = "Function", Target = "*")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. param ( [Parameter (Mandatory = $True)] #Hostname or IPAddress of the Standalone NSX Manger to be added [ValidateNotNullorEmpty()] [String]$NsxManager, [Parameter (Mandatory = $False)] #SHA1 hash of the NSX Manager certificate. Required unless -AcceptPresentedThumprint is specified. [ValidateNotNullorEmpty()] [String]$Thumbprint, [Parameter (Mandatory = $False)] #Accept any thumbprint presented by the server specified with -NsxManager. Insecure. [Switch]$AcceptPresentedThumbprint, [Parameter (Mandatory = $False, ParameterSetName = "UserPass")] #Username for NSX Manager to be added. A local account with SuperUser privileges is required. Defaults to admin. [ValidateNotNullorEmpty()] [String]$Username = "admin", [Parameter (Mandatory = $True, ParameterSetName = "UserPass")] #Password for NSX Manager to be added. A local account with SuperUser privileges is required. [ValidateNotNullorEmpty()] [String]$Password, [Parameter (Mandatory = $False, ParameterSetName = "Credential")] #Credential object for NSX Manager to be added. A local account with SuperUser privileges is required. [ValidateNotNullorEmpty()] [pscredential]$Credential, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) #Validate connected Manager is role Primary $ConnectedMgrRole = Get-NsxManagerRole -Connection $Connection if ( $ConnectedMgrRole.role -ne 'PRIMARY') { throw "The connected NSX Manager is currently configure with the role $($ConnectedMgrRole.role), but must be configured as PRIMARY to allow a secondary NSX Manager to be added to it." } #Build cred object for default auth if user specified username/pass if ($PSCmdlet.ParameterSetName -eq "UserPass" ) { $Credential = New-Object System.Management.Automation.PSCredential($Username, $(ConvertTo-SecureString $Password -AsPlainText -Force)) } else { #We need user/pass to generate the xml for the primary NSX Manager. if ( -not $PSBoundParameters.ContainsKey("Credential")) { $Credential = Get-Credential -Message "NSX manager credentials" } $UserName = $Credential.Username $Password = $Credential.GetNetworkCredential().Password } #Validate manager to be added is role standalone $NewMgrConnection = Connect-NsxServer -NsxServer $NsxManager -Credential $Credential -DisableVIAutoConnect -DefaultConnection:$false -WarningAction SilentlyContinue $NewMgrRole = Get-NsxManagerRole -Connection $NewMgrConnection if ( $NewMgrRole.role -ne 'STANDALONE') { throw "The specified NSX Manager is currently configured with the role $($NewMgrRole.role) but must be configured as STANDALONE to be added to a Cross VC environment." } #Make sure we have a thumbprint if ( $AcceptPresentedThumbprint ) { #Get the cert thumbprint of the specified manager. try { $Cert = Get-NsxManagerCertificate -Connection $NewMgrConnection $Thumbprint = $Cert.Sha1Hash } catch { throw "Failed retrieving the certificate thumbprint from the specified NSX manager. $_" } } elseif ( -not $PSBoundParameters.ContainsKey("Thumbprint")) { throw "The Thumbprint of the NSX Manager to be added as secondary must be specified, or -AcceptPresentedThumbprint specified (insecure)." } $XmlDoc = New-Object System.Xml.XmlDocument $NsxManagerInfoElement = $XmlDoc.CreateElement("nsxManagerInfo") Add-XmlElement -xmlRoot $NsxManagerInfoElement -xmlElementName nsxManagerIp -xmlElementText $NsxManager Add-XmlElement -xmlRoot $NsxManagerInfoElement -xmlElementName nsxManagerUsername -xmlElementText $UserName Add-XmlElement -xmlRoot $NsxManagerInfoElement -xmlElementName nsxManagerPassword -xmlElementText $Password Add-XmlElement -xmlRoot $NsxManagerInfoElement -xmlElementName certificateThumbprint -xmlElementText $Thumbprint $URI = "/api/2.0/universalsync/configuration/nsxmanagers" try { $response = Invoke-NsxWebRequest -method "post" -body $NsxManagerInfoElement.OuterXml -URI $URI -connection $connection $content = [xml]$response.content $content.nsxManagerInfo } Catch { Throw "Failed adding secondary NSX Manager. $_" } } function Get-NsxSecondaryManager { <# .SYNOPSIS Gets configured secondary NSX Managers from the connected NSX Manager. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. If run against a primary NSX Manager, the Get-NsxSecondaryManager cmdlet retrieves configured secondary NSX managers. If run against a secondary NSX Manager, information about the configured primary is returned. .EXAMPLE Get-NsxSecondaryManager -connection $PrimaryNsxManagerConnection uuid : 08edd323-fd72-4fd6-9de5-6072bb077d0e nsxManagerIp : nsx-m-01b nsxManagerUsername : replicator-08edd323-fd72-4fd6-9de5-6072bb077d0e certificateThumbprint : d7:8d:8a:06:55:52:2a:49:00:06:b1:58:c2:cd:2b:82:21:6b:2f:92 isPrimary : false Retrieves the configured secondary NSX managers on the primary NSX manager defined in the connection $PrimaryNsxManagerConnection. .EXAMPLE Get-NsxSecondaryManager -connection $SecondaryNsxManagerConnection uuid : 423CA89C-FCED-43C8-6D20-E15CF52E654A nsxManagerIp : 192.168.102.201 primaryUuid : 8f356635-3c5f-4d72-8f42-bbc6419ce678 primaryNsxManagerIp : 192.168.101.201 isPrimary : false Retrieves the configured details of the specified secondary NSX manager, and the primary NSX manager IP Address and uuid #> [CmdletBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $True, ParameterSetName = "uuid")] #UUID of Nsx Secondary Manager to return [ValidateNotNullOrEmpty()] [string]$Uuid, [Parameter (Mandatory = $True, ParameterSetName = "Name", Position = 1)] #Name of Nsx Secondary Manager to return [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $URI = "/api/2.0/universalsync/configuration/nsxmanagers" $response = Invoke-NsxWebRequest -method "get" -URI $URI -connection $connection try { $content = [xml]$response.content if ( Invoke-XpathQuery -QueryMethod SelectSingleNode -query "child::nsxManagerInfos/nsxManagerInfo" -Node $content ) { switch ( $PSCmdlet.ParameterSetName ) { "Name" { $content.nsxManagerInfos.nsxManagerInfo | Where-Object { $_.nsxManagerIp -match $Name } } "Uuid" { $content.nsxManagerInfos.nsxManagerInfo | Where-Object { $_.uuid -eq $uuid } } default { $content.nsxManagerInfos.nsxManagerInfo } } } } catch { throw "Unable to retrieve secondary NSX Manager information. $_" } } function Remove-NsxSecondaryManager { <# .SYNOPSIS Removes a secondary NSX Manager from a CrossVC configured NSX environment. .DESCRIPTION The NSX Manager is the central management component of VMware NSX for vSphere. The Remove-NsxSecondaryManager cmdlet removes a secondary NSX Manager from a CrossVC configured NSX environment. .EXAMPLE Get-NsxSecondaryManager nsx-m-01b | Remove-NsxSecondaryManager Remove the connected and functional NSX manager nsx-m-01b. nsx-m-01b will be configured as a standalone manager. If nsx-m-01b is not online, or functional, the attempt will fail and -force must be used. .EXAMPLE Get-NsxSecondaryManager nsx-m-01b | Remove-NsxSecondaryManager -force Remove the NSX manager nsx-m-01b - no attempt is made to reconfigure the secondary. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] #Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $True, ValueFromPipeline = $true)] #Secondary NSX Manager object to be removed as returned by Get-NsxSecondaryManager [ValidateScript( { ValidateSecondaryManager $_ })] [System.Xml.XmlElement]$SecondaryManager, [Parameter (Mandatory = $False)] #Confirm removal. [switch]$Confirm = $True, [Parameter (Mandatory = $False)] #Force removal of a missing secondary. [switch]$Force, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( $confirm ) { $message = "Removal of a secondary NSX Manager will prevent synchronisation of universal objects to the manager being removed." $question = "Proceed with removal of secondary NSX Manager $($SecondaryManager.nsxManagerIp)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { if ( $PSBoundParameters.ContainsKey("Force")) { $URI = "/api/2.0/universalsync/configuration/nsxmanagers/$($SecondaryManager.uuid)?force=true" } else { $URI = "/api/2.0/universalsync/configuration/nsxmanagers/$($SecondaryManager.uuid)" } try { $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection } Catch { Throw "Failed removing secondary NSX Manager. $_" } } } end {} } function Wait-NsxControllerJob { <# .SYNOPSIS Wait for the specified controller job until it succeeds or fails. .DESCRIPTION Attempt to wait for the specified controller deployment succeeds or fails. Wait-NsxControllerJob defaults to timeout at 300 seconds, when the user is prompted to continuing waiting of fail. If immediate failure upon timeout is desirable (eg within script), then the $failOnTimeout switch can be set. .EXAMPLE Wait-NsxControllerJob -Jobid jobdata-1234 Wait for controller job jobdata-1234 up to 300 seconds to complete successfully or fail. If 300 seconds elapse, then prompt for action. .EXAMPLE Wait-NsxControllerJob -Jobid jobdata-1234 -TimeOut 400 -FailOnTimeOut Wait for controller job jobdata-1234 up to 40 seconds to complete successfully or fail. If 400 seconds elapse, then throw an error. #> param ( [Parameter (Mandatory = $true)] #Job Id string as returned from the api [string]$JobId, [Parameter (Mandatory = $false)] #Seconds to wait before declaring a timeout. Timeout defaults to 800 seconds, which is longer than the NSX internal timeout and rollback of a failed controller deployment of around 720 seconds. [int]$WaitTimeout = 800, [Parameter (Mandatory = $false)] #Do we prompt user an allow them to reset the timeout timer, or throw on timeout [switch]$FailOnTimeout = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) # Seriously - the NSX Task framework is the work of the devil. # In order to get a _complete_ picture of a controller deployment, we have to track the jobinstance returned by querying the taskservice uri for our returned jobid. # The jobinstance returned is a 'parent' of the job we got from the API. For a controller deployment job, there are multiple tasks that are executed. # Their status output doesnt exist until they are commenced though, which results in an increasing number of taskinstances that we need to track as deployment is performed. # A task instance state is either COMPLETED, at which time the next task is added, or EXECUTING, and has an interesting taskStatus property, or FAILED, and has an interesting taskMessage property (usually the cause of failure). When all tasks are COMPLETED, the parent jobInstance is COMPLETED # What makes this annoying is the deployment overall success/failure is only determinable from the parent job instance, however the current status and/or any error messages must be retrieved from the task in a FAILED state. The number of taskInstances is indeterminate # and grows as the job progresses... # # What we do here is declare state of the job we are monitoring on the jobinstance.status, but the output is obtained from the currently EXECUTING (status) or FAILED (error) child task # This really underscores the flexibility of having a generic Wait-NsxJob cmdlet I think :) FIGJAM... :) $WaitJobArgs = @{ "jobid" = $jobid "JobStatusUri" = "/api/2.0/services/taskservice/job" "CompleteCriteria" = { $job.jobInstances.jobInstance.status -eq "COMPLETED" } "FailCriteria" = { $job.jobInstances.jobInstance.status -eq "FAILED" } "StatusExpression" = { $execTask = @() $StatusMessage = "" $execTask = @($job.jobinstances.jobInstance.taskInstances.taskInstance | Where-Object { $_.taskStatus -eq "EXECUTING" }) if ( $exectask.count -eq 1) { $StatusMessage = "$($execTask.name) - $($execTask.taskStatus)" } else { $StatusMessage = "$($job.jobinstances.jobInstance.Status)" } $StatusMessage } "ErrorExpression" = { $failTask = @() $failMessage = "" $failTask = @($job.jobinstances.jobInstance.taskInstances.taskInstance | Where-Object { $_.taskStatus -eq "FAILED" }) if ( $failTask.count -eq 1) { $failMessage = "Failed Task : $($failTask.name) - $($failTask.statusMessage)" } else { $failMessage = "$($job.jobinstances.jobInstance.Status)" } $failMessage } "WaitTimeout" = $WaitTimeout "FailOnTimeout" = $FailOnTimeout "Connection" = $Connection } Wait-NsxJob @WaitJobArgs } function New-NsxController { <# .SYNOPSIS Deploys a new NSX Controller. .DESCRIPTION An NSX Controller is a member of the NSX Controller Cluster, and forms the highly available distributed control plane for NSX Logical Switching and NSX Logical Routing. The New-NsxController cmdlet deploys a new NSX Controller. .EXAMPLE $ippool = New-NsxIpPool -Name ControllerPool -Gateway 192.168.0.1 -SubnetPrefixLength 24 -StartAddress 192.168.0.10 -endaddress 192.168.0.20 C:\PS> $ControllerCluster = Get-Cluster vSphereCluster C:\PS> $ControllerDatastore = Get-Datastore $ControllerDatastoreName -server $Connection.VIConnection C:\PS> $ControllerPortGroup = Get-VDPortGroup $ControllerPortGroupName -server $Connection.VIConnection C:\PS> New-NsxController -ipPool $ippool -cluster $ControllerCluster -datastore $ControllerDatastore -PortGroup $ControllerPortGroup -password "VMware1!VMware1!" Creates a new controller. Because it is the first controller, a password must be specified. .EXAMPLE New-NsxController -ControllerName "MyController" -ipPool $ippool -cluster $ControllerCluster -datastore $ControllerDatastore -PortGroup $ControllerPortGroup -password "VMware1!VMware1!" -confirm:$false Creates a new controller with the specified name and without prompting for confirmation. .EXAMPLE New-NsxController -ipPool $ippool -cluster $ControllerCluster -datastore $ControllerDatastore -PortGroup $ControllerPortGroup A secondary or tertiary controller requires a Password to NOT be defined. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $False)] # Controller Name. Will be autogenerated in form of ControllerN if not provided. [Alias ("Name")] [string]$ControllerName, [Parameter (Mandatory = $False)] # Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $True)] # Pre Created IP Pool object from which controller IP will be allocated [ValidateScript( { ValidateIpPool $_ })] [System.Xml.XmlElement]$IpPool, [Parameter (Mandatory = $true, ParameterSetName = "ResourcePool")] # vSphere DRS Resource Pool into which to deploy Controller VM [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ResourcePoolInterop]$ResourcePool, [Parameter (Mandatory = $true, ParameterSetName = "Cluster")] # vSphere Cluster into which to deploy the Controller VM [ValidateScript( { if ( $_ -eq $null ) { throw "Must specify Cluster." } if ( -not $_.DrsEnabled ) { throw "Cluster is not DRS enabled." } $true })] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop]$Cluster, [Parameter (Mandatory = $true)] # vSphere Datastore into which to deploy the Controller VM [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.DatastoreManagement.DatastoreInterop]$Datastore, [Parameter (Mandatory = $true)] # vSphere DVPortGroup OR NSX Logical Switch object to connect the Controller VM to [ValidateScript( { ValidateLogicalSwitchOrDistributedPortGroup $_ })] [object]$PortGroup, [Parameter (Mandatory = $False)] # Controller Password (Must be same on all controllers) [string]$Password, [Parameter ( Mandatory = $False)] # Block until Controller deployment job is 'COMPLETED' (Will timeout with prompt after -WaitTimeout seconds) # Useful if automating the deployment of multiple controllers (first must be running before deploying second controller) # so you dont have to write looping code to check status of controller before continuing. # NOTE: Not waiting means we do NOT return a controller object! [switch]$Wait = $false, [Parameter ( Mandatory = $False)] # Timeout waiting for controller to become 'RUNNING' before user is prompted to continue or cancel. Defaults to 800 seconds to exceed the normal NSX rollback timeout of 720 seconds. [int]$WaitTimeout = 800, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { $Ctrlcount = Get-NsxController -Connection $Connection | Measure-Object if ( ($PSBoundParameters.ContainsKey("Password")) -and ($Ctrlcount.count -gt 0)) { Write-Warning "A controller is already deployed but a password argument was specified to New-NsxController. The new controller will be configured with the same password as the initial one and the specified password ignored" } if ( -not ($PSBoundParameters.ContainsKey("Password")) -and ($Ctrlcount.count -eq 0)) { Throw "A password is required to deploy the inital controller. Try again and specify the -Password parameter." } # AutoGen a sane controller name. NSX 6.4 api makes this mandatory, but I want the same requirement to avoid backward breaking change in PowerNSX. Eng take note! :| if ( -not $PsBoundParameters.ContainsKey("ControllerName")) { $ControllerName = "Controller$($Ctrlcount.count + 1)" Write-Warning "Using autogenerated name for new controller : $ControllerName" } } process { [System.Xml.XmlDocument]$xmlDoc = New-Object System.Xml.XmlDocument #Create the new route element. $ControllerSpec = $xmlDoc.CreateElement('controllerSpec') Add-XmlElement -xmlRoot $ControllerSpec -xmlElementName "ipPoolId" -xmlElementText $IpPool.objectId.ToString() #The following is required (or error is thrown), but is ignored, as the #OVF options end up setting the VM spec... :| Add-XmlElement -xmlRoot $ControllerSpec -xmlElementName "deployType" -xmlElementText "small" switch ( $PsCmdlet.ParameterSetName ) { "Cluster" { Add-XmlElement -xmlRoot $ControllerSpec -xmlElementName "resourcePoolId" -xmlElementText $Cluster.ExtensionData.Moref.Value.ToString() } "ResourcePool" { Add-XmlElement -xmlRoot $ControllerSpec -xmlElementName "resourcePoolId" -xmlElementText $ResourcePool.ExtensionData.Moref.Value.ToString() } } # Check for presence of optional controller name if ($PSBoundParameters.ContainsKey("Password") -and ($Ctrlcount.count -eq 0)) { Add-XmlElement -xmlRoot $ControllerSpec -xmlElementName "password" -xmlElementText $Password.ToString() } Add-XmlElement -xmlRoot $ControllerSpec -xmlElementName "datastoreId" -xmlElementText $DataStore.ExtensionData.Moref.value.ToString() Add-XmlElement -xmlRoot $ControllerSpec -xmlElementName "networkId" -xmlElementText $PortGroup.ExtensionData.Moref.Value.ToString() Add-XmlElement -xmlRoot $ControllerSpec -xmlElementName "name" -xmlElementText $ControllerName $URI = "/api/2.0/vdn/controller" $body = $ControllerSpec.OuterXml if ( $confirm ) { $message = "Adding a new controller to the NSX controller cluster. ONLY three controllers are supported. Then shalt thou count to three, no more, no less. Three shall be the number thou shalt count, and the number of the counting shall be three. Four shalt thou not count, neither count thou two, excepting that thou then proceed to three. Five is right out. Once the number three, being the third number, be reached, then lobbest thou thy Holy Hand Grenade of Antioch towards thy foe, who being naughty in My sight, shall snuff it." $question = "Proceed with controller deployment?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Deploying NSX Controller" try { $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection } catch { throw "Controller deployment failed. $_" } if ( -not (($response.Content -match "jobdata-\d+") -and ($response.Headers.keys -contains "location") -and ($response.Headers["Location"] -match "/api/2.0/vdn/controller/" )) ) { throw "Controller deployment failed. $($response.content)" } #The post is ansync - the controller deployment can fail after the api accepts the post. we need to check on the status of the job. if ( $Wait ) { #Get the new controller id so we can get its status later... $controllerid = $response.Headers["location"] -replace "/api/2.0/vdn/controller/" $jobid = $response.content Write-Debug "$($MyInvocation.MyCommand.Name) : Controller deployment job $jobid returned in post response" #First we wait for NSX job framework to give us the needful try { Wait-NsxControllerJob -JobId $JobID -Connection $Connection -WaitTimeout $WaitTimeout Get-NsxController -Connection $connection -ObjectId $controllerId } catch { throw "Controller deployment job failed. $_" } } } } end {} } function Get-NsxController { <# .SYNOPSIS Retrieves NSX Controllers. .DESCRIPTION An NSX Controller is a member of the NSX Controller Cluster, and forms the highly available distributed control plane for NSX Logical Switching and NSX Logical Routing. The Get-NsxController cmdlet deploys a new NSX Controller via the NSX API. .EXAMPLE Get-NsxController Retrieves all controller objects from NSX manager .EXAMPLE Get-NsxController -objectId Controller-1 Returns a specific NSX Controller object from NSX manager #> param ( [Parameter (Mandatory = $false, Position = 1)] #ObjectId of the NSX Controller to return. [string]$ObjectId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $URI = "/api/2.0/vdn/controller" [System.Xml.XmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::controllers/controller')) { if ( $PsBoundParameters.containsKey('objectId')) { $response.controllers.controller | Where-Object { $_.Id -eq $ObjectId } } else { $response.controllers.controller } } } function Remove-NsxController { <# .SYNOPSIS Removes a controller .DESCRIPTION An NSX Controller is a member of the NSX Controller Cluster, and forms the highly available distributed control plane for NSX Logical Switching and NSX Logical Routing. The Renove-NsxController cmdlet removes an existing NSX Controller. .EXAMPLE Get-NsxController "Controller1" | Remove-NsxController Removes the controller named Controller1 .EXAMPLE Remove-NsxController -objectId controller-3 Removes the controller with id controller-3 #> [CmdletBinding(DefaultParameterSetName = "Object")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1, ParameterSetName = "Object")] #PowerNSX Controller object obtained via Get-NsxController [ValidateScript( { ValidateController $_ })] [System.Xml.XmlElement]$Controller, [Parameter (Mandatory = $true, ParameterSetName = "objectId")] #ObjectID of the controller to remove [ValidateNotNullorEmpty()] [string]$objectId, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter ( Mandatory = $False)] #Block until Controller Removal job is COMPLETED (Will timeout with prompt after 720 seconds) #Useful if automating the removal of multiple controllers (first must be removed before removing second controller) #so you dont have to write looping code to check status of controller before continuing. [switch]$Wait = $false, [Parameter (Mandatory = $false)] #Force the removal of the last controller. WARNING THIS WILL IMPACT LOGICAL SWITCHING AND ROUTING FUNCTIONALITY [switch]$Force = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( $PSCmdlet.ParameterSetName -ne "objectId" ) { $objectId = $Controller.id } if ( $confirm ) { $message = "Controller removal will impact the high availability of the NSX control plane." $question = "Proceed with removal of Controller $($objectId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/2.0/vdn/controller/$($objectId)?forceRemoval=$force" Write-Progress -Activity "Remove Controller $objectId" try { $response = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection } catch { throw "Controller deployment failed. $_" } if ( -not ($response.Content -match "jobdata-\d+")) { throw "Controller deployment failed. $($response.content)" } #The post is ansync - the controller deployment can fail after the api accepts the post. we need to check on the status of the job. if ( $Wait ) { $jobid = $response.content Write-Debug "$($MyInvocation.MyCommand.Name) : Controller deployment job $jobid returned in post response" #First we wait for NSX job framework to give us the needful try { Wait-NsxControllerJob -JobId $JobID -Connection $Connection } catch { throw "Controller removal job failed. $_" } } Write-Progress -Activity "Remove Controller $objectId" -Completed } } end {} } function Invoke-NsxControllerStateUpdate { <# .SYNOPSIS Update controller state information. .DESCRIPTION An NSX Controller is a member of the NSX Controller Cluster, and forms the highly available distributed control plane for NSX Logical Switching and NSX Logical Routing. When a NSX Controller cluster is re-deployed in an existing environment, it will not have knowledge of the currently configured logical networking constructs deployed. The Invoke-NsxControllerStateUpdate cmdlet pushes the information back to the controllers after a re-deploy. .EXAMPLE Invoke-NsxControllerStateUpdate Invoke the process to push configuration state informaiton back to the controller cluster. .EXAMPLE Invoke-NsxControllerStateUpdate -Wait -WaitTimeout 20 -FailOnTimeout Invoke the process to push configuration state and wait for the job to finish. If 20 seconds elapses (default is 30), then throw an error. #> param ( [Parameter ( Mandatory = $False)] # Block until the job is 'COMPLETED' (Will timeout with prompt after -WaitTimeout seconds) # Useful if automating the re-deployment of the controller cluster so you dont have to write # looping code to check status of the job before continuing. [switch]$Wait = $false, [Parameter(Mandatory = $false)] # If job reaches -WaitTimeout without failing or completing, do we prompt, or fail with error? [switch]$FailOnTimeout = $false, [Parameter(Mandatory = $false)] # Seconds to wait for connection job to complete. Defaults to 30 seconds. [int]$WaitTimeout = 30, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $URI = "/api/2.0/vdn/controller/synchronize" try { $response = Invoke-NsxWebRequest -method "put" -URI $URI -connection $connection } catch { Throw "Failed to invoke controller state update. $_" } if ( -not ($response.Content -match "jobdata-\d+")) { throw "Controller Update State failed. No jobdata returned. $($response.content)" } # The post is ansync - the job can fail after the api accepts the post. # we need to check on the status of the job. if ( $Wait ) { $jobid = $response.content Write-Debug "$($MyInvocation.MyCommand.Name) : Controller Update State job $jobid returned in post response" #First we wait for NSX job framework to give us the needful try { Wait-NsxGenericJob -JobId $response.Content -Connection $Connection -WaitTimeout $WaitTimeout -FailOnTimeout:$FailOnTimeout } catch { throw "Controller Update State failed. $_" } } Write-Progress -Activity "Controller Update State." -Completed } function New-NsxIpPool { <# .SYNOPSIS Creates a new IP Pool. .DESCRIPTION An IP Pool is a simple IPAM construct in NSX that simplifies automated IP address asignment for multiple NSX technologies including VTEP interfaces NSX Controllers. The New-NsxIpPool cmdlet creates a new IP Pool on the connected NSX manager. .EXAMPLE New-NsxIpPool -name Controller_Pool -Gateway "192.168.103.1" -SubnetPrefixLength "24" -DnsServer1 "192.168.100.4" -DnsSuffix "lab.local" -StartAddress "192.168.103.101" -EndAddress "192.168.103.115" This example creates a pool called Controller_Pool. It uses the IP range 192.168.103.101-115, has a defined gateway of 192.168.103.1 and has DNS settings configured. #> param ( [Parameter (Mandatory = $true, Position = 1)] #Name of IP Pool [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true)] #Gateway address [ValidateNotNullOrEmpty()] [ipAddress]$Gateway, [Parameter (Mandatory = $true)] #Prefix length of network address (1-31) [ValidateNotNullOrEmpty()] [string]$SubnetPrefixLength, [Parameter (Mandatory = $false)] #IP Address of first DNS Server [ValidateNotNullOrEmpty()] [ipAddress]$DnsServer1, [Parameter (Mandatory = $false)] #IP Address of second DNS Server [ValidateNotNullOrEmpty()] [ipAddress]$DnsServer2, [Parameter (Mandatory = $false)] #DNS Domain Name [ValidateNotNullOrEmpty()] [string]$DnsSuffix, [Parameter (Mandatory = $true)] #First Valid Address in the pool [ValidateNotNullOrEmpty()] [ipaddress]$StartAddress, [Parameter (Mandatory = $true)] #Last Valid Address in the pool [ValidateNotNullOrEmpty()] [ipaddress]$EndAddress, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlPool = $XMLDoc.CreateElement("ipamAddressPool") $xmlDoc.Appendchild($xmlPool) | Out-Null #Mandatory and default params Add-XmlElement -xmlRoot $xmlPool -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlPool -xmlElementName "prefixLength" -xmlElementText $SubnetPrefixLength Add-XmlElement -xmlRoot $xmlPool -xmlElementName "gateway" -xmlElementText $Gateway #Start/End of range $xmlIpRanges = $xmlDoc.CreateElement("ipRanges") $xmlIpRange = $xmlDoc.CreateElement("ipRangeDto") $xmlPool.Appendchild($xmlIpRanges) | Out-Null $xmlIpRanges.Appendchild($xmlIpRange) | Out-Null Add-XmlElement -xmlRoot $xmlIpRange -xmlElementName "startAddress" -xmlElementText $StartAddress Add-XmlElement -xmlRoot $xmlIpRange -xmlElementName "endAddress" -xmlElementText $EndAddress #Optional params if ( $PsBoundParameters.ContainsKey('DnsServer1')) { Add-XmlElement -xmlRoot $xmlPool -xmlElementName "dnsServer1" -xmlElementText $DnsServer1 } if ( $PsBoundParameters.ContainsKey('DnsServer2')) { Add-XmlElement -xmlRoot $xmlPool -xmlElementName "dnsServer2" -xmlElementText $DnsServer2 } if ( $PsBoundParameters.ContainsKey('DnsSuffix')) { Add-XmlElement -xmlRoot $xmlPool -xmlElementName "dnsSuffix" -xmlElementText $DnsSuffix } # #Do the post $body = $xmlPool.OuterXml $URI = "/api/2.0/services/ipam/pools/scope/globalroot-0" Write-Progress -Activity "Creating IP Pool." $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Creating IP Pool." -Completed Get-NsxIpPool -ObjectId $response.content -Connection $connection } end {} } function Get-NsxIpPool { <# .SYNOPSIS Retrieves NSX Ip Pools. .DESCRIPTION An IP Pool is a simple IPAM construct in NSX that simplifies automated IP address asignment for multiple NSX technologies including VTEP interfaces NSX Controllers. .EXAMPLE This example retrieves all NSX IP Pools Get-NsxIpPool .EXAMPLE This example retrieves an NSX IP Pool by name Get-NsxIpPool -name Controller_Pool #> [CmdletBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $false, Position = 1, ParameterSetName = "Name")] #Name of the Pool to retrieve [string]$Name, [Parameter (Mandatory = $false, ParameterSetName = "ObjectId")] #ObjectID of the Pool to retrieve [string]$ObjectId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) if ( $PsBoundParameters.ContainsKey('ObjectId')) { $URI = "/api/2.0/services/ipam/pools/$ObjectId" $response = Invoke-NsxWebRequest -method "get" -URI $URI -connection $connection [system.xml.xmlDocument]$content = $response.content if (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $content -query 'child::ipamAddressPool') { $content.ipamAddressPool } } else { $URI = "/api/2.0/services/ipam/pools/scope/globalroot-0" $response = Invoke-NsxWebRequest -method "get" -URI $URI -connection $connection [system.xml.xmlDocument]$content = $response.content if (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $content -query 'child::ipamAddressPools/ipamAddressPool') { If ( $PsBoundParameters.ContainsKey("Name")) { $content.ipamAddressPools.ipamAddressPool | Where-Object { $_.name -eq $Name } } else { $content.ipamAddressPools.ipamAddressPool } } } } function Get-NsxVdsContext { <# .SYNOPSIS Retrieves a VXLAN Prepared Virtual Distributed Switch. .DESCRIPTION Before it can be used for VXLAN, a VDS must be configured with appropriate teaming and MTU configuration. The Get-NsxVdsContext cmdlet retrieves VDS's that have been prepared for VXLAN configuration. #> [CmdletBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $false, Position = 1, ParameterSetName = "Name")] #Name of VDS context to retrieve [string]$Name, [Parameter (Mandatory = $false, ParameterSetName = "ObjectId")] #ObjectId of VDS context to retrieve [string]$ObjectId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) if ( $PsBoundParameters.ContainsKey('ObjectId')) { $URI = "/api/2.0/vdn/switches/$ObjectId" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection If ( $response | Get-Member -MemberType properties vdsContext ) { $response.vdsContext } } else { $URI = "/api/2.0/vdn/switches" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection If ( $PsBoundParameters.ContainsKey("Name")) { if ( $response.vdsContexts -as [system.xml.xmlelement]) { If ( $response | Get-Member -MemberType properties vdsContexts ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response.vdsContexts -query "descendant::vdsContext")) { $response.vdsContexts.vdsContext | Where-Object { $_.switch.name -eq $Name } } } } } else { if ( $response.vdsContexts -as [system.xml.xmlelement]) { If ( $response | Get-Member -MemberType properties vdsContexts ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response.vdsContexts -query "descendant::vdsContext")) { $response.vdsContexts.vdsContext } } } } } } function New-NsxVdsContext { <# .SYNOPSIS Creates a VXLAN Prepared Virtual Distributed Switch. .DESCRIPTION Before it can be used for VXLAN, a VDS must be configured with appropriate teaming and MTU configuration. The New-NsxVdsContext cmdlet configures the specified VDS for use with VXLAN. #> param ( [Parameter (Mandatory = $true, Position = 1)] #PowerCLI VDSwitch Object to configure for NSX [ValidateScript( { ValidateDistributedSwitch $_ })] [object]$VirtualDistributedSwitch, [Parameter (Mandatory = $true)] #Teaming configuration for NSX Logical Switches [ValidateSet("FAILOVER_ORDER", "ETHER_CHANNEL", "LACP_ACTIVE", "LACP_PASSIVE", "LOADBALANCE_LOADBASED", "LOADBALANCE_SRCID", "LOADBALANCE_SRCMAC", "LACP_V2", IgnoreCase = $false)] [string]$Teaming, [Parameter (Mandatory = $true)] #MTU of VTEP interfaces created on the specified VDS. Minimum of 1600 bytes is required. [ValidateRange(1600, 9000)] [int]$Mtu, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlContext = $XMLDoc.CreateElement("nwFabricFeatureConfig") $xmlDoc.Appendchild($xmlContext) | Out-Null Add-XmlElement -xmlRoot $xmlContext -xmlElementName "featureId" -xmlElementText "com.vmware.vshield.vsm.vxlan" #configSpec $xmlResourceConfig = $xmlDoc.CreateElement("resourceConfig") $xmlConfigSpec = $xmlDoc.CreateElement("configSpec") $xmlConfigSpec.SetAttribute("class", "vdsContext") $xmlContext.Appendchild($xmlResourceConfig) | Out-Null $xmlResourceConfig.Appendchild($xmlConfigSpec) | Out-Null Add-XmlElement -xmlRoot $xmlConfigSpec -xmlElementName "teaming" -xmlElementText $Teaming.toString() Add-XmlElement -xmlRoot $xmlConfigSpec -xmlElementName "mtu" -xmlElementText $Mtu.ToString() $xmlSwitch = $xmlDoc.CreateElement("switch") $xmlConfigSpec.Appendchild($xmlSwitch) | Out-Null Add-XmlElement -xmlRoot $xmlSwitch -xmlElementName "objectId" -xmlElementText $VirtualDistributedSwitch.Extensiondata.Moref.Value.ToString() Add-XmlElement -xmlRoot $xmlResourceConfig -xmlElementName "resourceId" -xmlElementText $VirtualDistributedSwitch.Extensiondata.Moref.Value.ToString() # #Do the post $body = $xmlContext.OuterXml $URI = "/api/2.0/nwfabric/configure" Write-Progress -Activity "Configuring VDS context on VDS $($VirtualDistributedSwitch.Name)." $null = Invoke-NsxRestMethod -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Configuring VDS context on VDS $($VirtualDistributedSwitch.Name)." -Completed Get-NsxVdsContext -ObjectId $VirtualDistributedSwitch.Extensiondata.Moref.Value -Connection $connection } end {} } function Remove-NsxVdsContext { <# .SYNOPSIS Removes the VXLAN preparation of a Virtual Distributed Switch. .DESCRIPTION Before it can be used for VXLAN, a VDS must be configured with appropriate teaming and MTU configuration. The Remove-NsxVdsContext cmdlet unconfigures the specified VDS for use with VXLAN. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #NSX VDS Context Object ID to remove [ValidateScript( { ValidateVdsContext $_ })] [System.Xml.XmlElement]$VdsContext, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Vds Context removal is permanent." $question = "Proceed with removal of Vds Context for Vds $($VdsContext.Switch.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/2.0/vdn/switches/$($VdsContext.Switch.ObjectId)" Write-Progress -Activity "Remove Vds Context for Vds $($VdsContext.Switch.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Vds Context for Vds $($VdsContext.Switch.Name)" -Completed } } end {} } function New-NsxClusterVxlanConfig { <# .SYNOPSIS Configures a vSphere cluster for VXLAN. .DESCRIPTION VXLAN configuration of a vSphere cluster involves associating the cluster with an NSX prepared VDS, and configuration of VLAN id for the atuomatically created VTEP portgroup, VTEP count and VTEP addressing. If the VDS specified is not configured for VXLAN, then an error is thrown. Use New-NsxVdsContext to configure a VDS for use with NSX. If the specified cluster is not prepared with the necessary VIBs installed, then installation occurs automatically. Use Install-NsxCluster to prepare a clusters hosts for use with NSX without configuring VXLAN If an IP Pool is not specified, DHCP will be used to configure the host VTEPs. The New-NsxClusterVxlan cmdlet will perform the VXLAN configuration of all hosts within the specified cluster. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop]$Cluster, [Parameter (Mandatory = $true)] [ValidateScript( { ValidateDistributedSwitch $_ })] [object]$VirtualDistributedSwitch, [Parameter (Mandatory = $False)] [ValidateScript( { ValidateIpPool $_ })] [System.Xml.XmlElement]$IpPool, [Parameter (Mandatory = $False)] [int]$VlanId = "", [Parameter (Mandatory = $False)] [ValidateNotNullorEmpty()] [int]$VtepCount, [Parameter (Mandatory = $False)] [ValidateNotNullorEmpty()] [int]$VxlanPrepTimeout = 120, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $VxlanWaitTime = 10 #seconds #Check that the VDS has a VDS context in NSX and is configured. try { $vdscontext = Get-NsxVdsContext -ObjectId $VirtualDistributedSwitch.Extensiondata.MoRef.Value -Connection $connection } catch { throw "Specified VDS is not configured for NSX. Use New-NsxVdsContext to configure the VDS and try again." } #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlContext = $XMLDoc.CreateElement("nwFabricFeatureConfig") $xmlDoc.Appendchild($xmlContext) | Out-Null Add-XmlElement -xmlRoot $xmlContext -xmlElementName "featureId" -xmlElementText "com.vmware.vshield.vsm.vxlan" #cluster configSpec $xmlResourceConfig = $xmlDoc.CreateElement("resourceConfig") $xmlConfigSpec = $xmlDoc.CreateElement("configSpec") $xmlConfigSpec.SetAttribute("class", "clusterMappingSpec") $xmlContext.Appendchild($xmlResourceConfig) | Out-Null $xmlResourceConfig.Appendchild($xmlConfigSpec) | Out-Null if ( $PSBoundParameters.ContainsKey('IpPool')) { Add-XmlElement -xmlRoot $xmlConfigSpec -xmlElementName "ipPoolId" -xmlElementText $IpPool.objectId.toString() } Add-XmlElement -xmlRoot $xmlConfigSpec -xmlElementName "vlanId" -xmlElementText $VlanId.ToString() Add-XmlElement -xmlRoot $xmlConfigSpec -xmlElementName "vmknicCount" -xmlElementText $VtepCount.ToString() $xmlSwitch = $xmlDoc.CreateElement("switch") $xmlConfigSpec.Appendchild($xmlSwitch) | Out-Null Add-XmlElement -xmlRoot $xmlSwitch -xmlElementName "objectId" -xmlElementText $VirtualDistributedSwitch.Extensiondata.Moref.Value.ToString() Add-XmlElement -xmlRoot $xmlResourceConfig -xmlElementName "resourceId" -xmlElementText $Cluster.Extensiondata.Moref.Value.ToString() # NSX 6.4.0 introduced changes that require the (redundant) vdsContext to be passed in this call too. # switch configSpec $xmlvdsResourceConfig = $xmlDoc.CreateElement("resourceConfig") $xmlvdsConfigSpec = $xmlDoc.CreateElement("configSpec") $xmlvdsConfigSpec.SetAttribute("class", "vdsContext") $xmlContext.Appendchild($xmlvdsResourceConfig) | Out-Null $xmlvdsResourceConfig.Appendchild($xmlvdsConfigSpec) | Out-Null Add-XmlElement -xmlRoot $xmlvdsConfigSpec -xmlElementName "mtu" -xmlElementText $vdsContext.mtu Add-XmlElement -xmlRoot $xmlvdsConfigSpec -xmlElementName "teaming" -xmlElementText $vdsContext.teaming $xmlvdsSwitch = $xmlDoc.CreateElement("switch") $xmlvdsConfigSpec.Appendchild($xmlvdsSwitch) | Out-Null Add-XmlElement -xmlRoot $xmlvdsSwitch -xmlElementName "objectId" -xmlElementText $VirtualDistributedSwitch.Extensiondata.Moref.Value.ToString() Add-XmlElement -xmlRoot $xmlvdsResourceConfig -xmlElementName "resourceId" -xmlElementText $VirtualDistributedSwitch.Extensiondata.Moref.Value.ToString() Write-Progress -Id 1 -Activity "Configuring VXLAN on cluster $($Cluster.Name)." -Status "In Progress..." # #Do the post $body = $xmlContext.OuterXml $URI = "/api/2.0/nwfabric/configure" $null = Invoke-NsxRestMethod -method "post" -URI $URI -body $body -connection $connection #Get Initial Status $status = $cluster | Get-NsxClusterStatus -Connection $connection $hostprep = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.nwfabric.hostPrep' -statusxml $status $fw = Get-FeatureStatus -featurestring 'com.vmware.vshield.firewall' -statusxml $status $messagingInfra = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.messagingInfra' -statusxml $status $VxlanConfig = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.vxlan' -statusxml $status $timer = 0 while ( ($hostprep -ne 'GREEN') -or ($fw -ne 'GREEN') -or ($messagingInfra -ne 'GREEN') -or ($VxlanConfig -ne 'GREEN')) { Start-Sleep $VxlanWaitTime $timer += $VxlanWaitTime #Get Status $status = $cluster | Get-NsxClusterStatus -Connection $connection $hostprep = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.nwfabric.hostPrep' -statusxml $status $fw = Get-FeatureStatus -featurestring 'com.vmware.vshield.firewall' -statusxml $status $messagingInfra = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.messagingInfra' -statusxml $status $VxlanConfig = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.vxlan' -statusxml $status #Check Status if ( $hostprep -eq 'GREEN' ) { $status = "Complete" } else { $status = "Waiting" } Write-Progress -ParentId 1 -Id 2 -Activity "Vib Install Status: $hostprep" -Status $status if ( $fw -eq 'GREEN' ) { $status = "Complete" } else { $status = "Waiting" } Write-Progress -ParentId 1 -Id 3 -Activity "Firewall Install Status: $fw" -Status $status if ( $messagingInfra -eq 'GREEN' ) { $status = "Complete" } else { $status = "Waiting" } Write-Progress -ParentId 1 -Id 4 -Activity "Messaging Infra Status: $messagingInfra" -Status $status if ( $VxlanConfig -eq 'GREEN' ) { $status = "Complete" } else { $status = "Waiting" } Write-Progress -ParentId 1 -Id 5 -Activity "VXLAN Config Status: $VxlanConfig" -Status $status if ($Timer -ge $VxlanPrepTimeout) { $message = "Cluster $($cluster.name) preparation has not completed within the timeout period." $question = "Continue waiting (y) or quit (n)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 0) if ( $decision -eq 1 ) { Throw "$($cluster.name) cluster preparation failed or timed out." } $Timer = 0 } } Write-Progress -ParentId 1 -Id 2 -Activity "Vib Install Status: $hostprep" -Completed Write-Progress -ParentId 1 -Id 3 -Activity "Firewall Install Status: $fw" -Completed Write-Progress -ParentId 1 -Id 4 -Activity "Messaging Infra Status: $messagingInfra" -Completed Write-Progress -ParentId 1 -Id 5 -Activity "VXLAN Config Status: $VxlanConfig" -Completed Write-Progress -Id 1 -Activity "Configuring VXLAN on cluster $($Cluster.Name)." -Completed $cluster | Get-NsxClusterStatus -Connection $connection } end {} } function Install-NsxCluster { <# .SYNOPSIS Prepares a vSphere cluster for use with NSX. .DESCRIPTION Preparation of a vSphere cluster involves installation of the vibs required for VXLAN, Logical routing and Distributed Firewall. The Install-NsxCluster cmdlet will perform the vib installation of all hosts within the specified cluster. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop]$Cluster, [PArameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [int]$VxlanPrepTimeout = 120, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $VxlanWaitTime = 10 #seconds #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlContext = $XMLDoc.CreateElement("nwFabricFeatureConfig") $xmlDoc.Appendchild($xmlContext) | Out-Null #configSpec $xmlResourceConfig = $xmlDoc.CreateElement("resourceConfig") $xmlContext.Appendchild($xmlResourceConfig) | Out-Null Add-XmlElement -xmlRoot $xmlResourceConfig -xmlElementName "resourceId" -xmlElementText $Cluster.Extensiondata.Moref.Value.ToString() Write-Progress -Id 1 -Activity "Preparing cluster $($Cluster.Name)." -Status "In Progress..." # #Do the post $body = $xmlContext.OuterXml $URI = "/api/2.0/nwfabric/configure" $null = Invoke-NsxRestMethod -method "post" -URI $URI -body $body -connection $connection #Get Initial Status $status = $cluster | Get-NsxClusterStatus -Connection $Connection $hostprep = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.nwfabric.hostPrep' -statusxml $status $fw = Get-FeatureStatus -featurestring 'com.vmware.vshield.firewall' -statusxml $status $messagingInfra = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.messagingInfra' -statusxml $status $timer = 0 while ( ($hostprep -ne 'GREEN') -or ($fw -ne 'GREEN') -or ($messagingInfra -ne 'GREEN') ) { Start-Sleep $VxlanWaitTime $timer += $VxlanWaitTime #Get Status $status = $cluster | Get-NsxClusterStatus -Connection $Connection $hostprep = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.nwfabric.hostPrep' -statusxml $status $fw = Get-FeatureStatus -featurestring 'com.vmware.vshield.firewall' -statusxml $status $messagingInfra = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.messagingInfra' -statusxml $status #Check Status if ( $hostprep -eq 'GREEN' ) { $status = "Complete" } else { $status = "Waiting" } Write-Progress -ParentId 1 -Id 2 -Activity "Vib Install Status: $hostprep" -Status $status if ( $fw -eq 'GREEN' ) { $status = "Complete" } else { $status = "Waiting" } Write-Progress -ParentId 1 -Id 3 -Activity "Firewall Install Status: $fw" -Status $status if ( $messagingInfra -eq 'GREEN' ) { $status = "Complete" } else { $status = "Waiting" } Write-Progress -ParentId 1 -Id 4 -Activity "Messaging Infra Status: $messagingInfra" -Status $status if ($Timer -ge $VxlanPrepTimeout) { $message = "Cluster $($cluster.name) preparation has not completed within the timeout period." $question = "Continue waiting (y) or quit (n)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 0) if ( $decision -eq 1 ) { Throw "$($cluster.name) cluster preparation failed or timed out." } $Timer = 0 } } Write-Progress -ParentId 1 -Id 2 -Activity "Vib Install Status: $hostprep" -Completed Write-Progress -ParentId 1 -Id 3 -Activity "Firewall Install Status: $fw" -Completed Write-Progress -ParentId 1 -Id 4 -Activity "Messaging Infra Status: $messagingInfra" -Completed Write-Progress -Id 1 -Activity "Preparing cluster $($Cluster.Name)." -Status "In Progress..." -Completed $cluster | Get-NsxClusterStatus -Connection $connection } end {} } function Remove-NsxCluster { <# .SYNOPSIS Unprepares a vSphere cluster for use with NSX. .DESCRIPTION Preparation of a vSphere cluster involves installation of the vibs required for VXLAN, Logical routing and Distributed Firewall. The Remove-NsxCluster cmdlet will perform the vib removal of all hosts within the specified cluster and will also unconfigure VXLAN if configured. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop]$Cluster, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [int]$VxlanPrepTimeout = 120, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $VxlanWaitTime = 10 #seconds #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlContext = $XMLDoc.CreateElement("nwFabricFeatureConfig") $xmlDoc.Appendchild($xmlContext) | Out-Null #configSpec $xmlResourceConfig = $xmlDoc.CreateElement("resourceConfig") $xmlContext.Appendchild($xmlResourceConfig) | Out-Null Add-XmlElement -xmlRoot $xmlResourceConfig -xmlElementName "resourceId" -xmlElementText $Cluster.Extensiondata.Moref.Value.ToString() if ( $confirm ) { $message = "Unpreparation of cluster $($Cluster.Name) will result in unconfiguration of VXLAN, removal of Distributed Firewall and uninstallation of all NSX VIBs." $question = "Proceed with un-preparation of cluster $($Cluster.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { ############### #Even though it *usually* unconfigures VXLAN automatically, ive had several instances where an unprepped #cluster had VXLAN config still present, and prevented future prep attempts from succeeding. #This may not resolve this issue, but hopefully will... $cluster | Remove-NsxClusterVxlanConfig -Confirm:$false -Connection $connection | Out-Null #Now we actually do the unprep... ############## Write-Progress -Id 1 -Activity "Unpreparing cluster $($Cluster.Name)." -Status "In Progress..." # #Do the post $body = $xmlContext.OuterXml $URI = "/api/2.0/nwfabric/configure" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -body $body -connection $connection #Get Initial Status $status = $cluster | Get-NsxClusterStatus -Connection $connection $hostprep = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.nwfabric.hostPrep' -statusxml $status $fw = Get-FeatureStatus -featurestring 'com.vmware.vshield.firewall' -statusxml $status $messagingInfra = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.messagingInfra' -statusxml $status $timer = 0 while ( ($hostprep -ne 'UNKNOWN') -or ($fw -ne 'UNKNOWN') -or ($messagingInfra -ne 'UNKNOWN') ) { Start-Sleep $VxlanWaitTime $timer += $VxlanWaitTime #Get Status $status = $cluster | Get-NsxClusterStatus -Connection $connection $hostprep = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.nwfabric.hostPrep' -statusxml $status $fw = Get-FeatureStatus -featurestring 'com.vmware.vshield.firewall' -statusxml $status $messagingInfra = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.messagingInfra' -statusxml $status #Check Status if ( $hostprep -eq 'UNKNOWN' ) { $status = "Complete" } else { $status = "Waiting" } Write-Progress -ParentId 1 -Id 2 -Activity "Vib Install Status: $hostprep" -Status $status if ( $fw -eq 'UNKNOWN' ) { $status = "Complete" } else { $status = "Waiting" } Write-Progress -ParentId 1 -Id 3 -Activity "Firewall Install Status: $fw" -Status $status if ( $messagingInfra -eq 'UNKNOWN' ) { $status = "Complete" } else { $status = "Waiting" } Write-Progress -ParentId 1 -Id 4 -Activity "Messaging Infra Status: $messagingInfra" -Status $status if ($Timer -ge $VxlanPrepTimeout) { #Need to do some detection of hosts needing reboot here and prompt to do it automatically... $message = "Cluster $($cluster.name) unpreparation has not completed within the timeout period." $question = "Continue waiting (y) or quit (n)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 0) if ( $decision -eq 1 ) { Throw "$($cluster.name) cluster unpreparation failed or timed out." } $Timer = 0 } } Write-Progress -ParentId 1 -Id 2 -Activity "Vib Install Status: $hostprep" -Completed Write-Progress -ParentId 1 -Id 3 -Activity "Firewall Install Status: $fw" -Completed Write-Progress -ParentId 1 -Id 4 -Activity "Messaging Infra Status: $messagingInfra" -Completed Write-Progress -Id 1 -Activity "Unpreparing cluster $($Cluster.Name)." -Status "In Progress..." -Completed $cluster | Get-NsxClusterStatus -Connection $connection } } end {} } function Remove-NsxClusterVxlanConfig { <# .SYNOPSIS Unconfigures VXLAN on an NSX prepared cluster. .DESCRIPTION VXLAN configuration of a vSphere cluster involves associating the cluster with an NSX prepared VDS, and configuration of VLAN id for the atuomatically created VTEP portgroup, VTEP count and VTEP addressing. The Remove-NsxClusterVxlan cmdlet will perform the unconfiguration of VXLAN on all hosts within the specified cluster only. VIBs will remain installed. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop]$Cluster, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [int]$VxlanPrepTimeout = 120, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $VxlanWaitTime = 10 #seconds #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlContext = $XMLDoc.CreateElement("nwFabricFeatureConfig") $xmlDoc.Appendchild($xmlContext) | Out-Null #ResourceID (must specific explicitly VXLAN) Add-XmlElement -xmlRoot $xmlContext -xmlElementName "featureId" -xmlElementText "com.vmware.vshield.vsm.vxlan" #configSpec $xmlResourceConfig = $xmlDoc.CreateElement("resourceConfig") $xmlContext.Appendchild($xmlResourceConfig) | Out-Null Add-XmlElement -xmlRoot $xmlResourceConfig -xmlElementName "resourceId" -xmlElementText $Cluster.Extensiondata.Moref.Value.ToString() if ( $confirm ) { $message = "Unconfiguration of VXLAN for cluster $($Cluster.Name) will result in loss of communication for any VMs connected to logical switches running in this cluster." $question = "Proceed with unconfiguration of VXLAN for cluster $($Cluster.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Id 1 -Activity "Unconfiguring VXLAN on $($Cluster.Name)." -Status "In Progress..." # #Do the post $body = $xmlContext.OuterXml $URI = "/api/2.0/nwfabric/configure" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -body $body -connection $connection #Get Initial Status $status = $cluster | Get-NsxClusterStatus -Connection $connection $VxlanConfig = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.vxlan' -statusxml $status $timer = 0 while ( $VxlanConfig -ne 'UNKNOWN' ) { Start-Sleep $VxlanWaitTime $timer += $VxlanWaitTime #Get Status $status = $cluster | Get-NsxClusterStatus -Connection $connection $VxlanConfig = Get-FeatureStatus -featurestring 'com.vmware.vshield.vsm.vxlan' -statusxml $status #Check Status if ( $VxlanConfig -eq 'UNKNOWN' ) { $status = "Complete" } else { $status = "Waiting" } Write-Progress -ParentId 1 -Id 5 -Activity "VXLAN Config Status: $VxlanConfig" -Status $status if ($Timer -ge $VxlanPrepTimeout) { $message = "Cluster $($cluster.name) VXLAN unconfiguration has not completed within the timeout period." $question = "Continue waiting (y) or quit (n)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 0) if ( $decision -eq 1 ) { Throw "$($cluster.name) cluster VXLAN unconfiguration failed or timed out." } $Timer = 0 } } Write-Progress -ParentId 1 -Id 5 -Activity "VXLAN Config Status: $VxlanConfig" -Completed Write-Progress -Id 1 -Activity "Unconfiguring VXLAN on $($Cluster.Name)." -Status "In Progress..." -Completed $cluster | Get-NsxClusterStatus -Connection $connection | Where-Object { $_.featureId -eq "com.vmware.vshield.vsm.vxlan" } } } end {} } function New-NsxSegmentIdRange { <# .SYNOPSIS Creates a new VXLAN Segment ID Range. .DESCRIPTION Segment ID Ranges provide a method for NSX to allocate a unique identifier (VNI) to each logical switch created within NSX. The New-NsxSegmentIdRange cmdlet creates a new Segment range on the connected NSX manager. .EXAMPLE PS C:\> New-NsxSegmentIdRange -Name LocalSegmentRange -Description "VNI Range for local logical switches" -Begin 1000 -End 1999 Creates a Segment Id Range which is used for logical switches .EXAMPLE PS C:\> New-NsxSegmentIdRange -Name LocalSegmentRange -Description "VNI Range for local logical switches" -Begin 77000 -End 77999 -universal Creates a Universal Segment Id Range which is used for universal logical switches #> param ( [Parameter (Mandatory = $true, Position = 1)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$Description, [Parameter (Mandatory = $true)] [ValidateRange(5000, 16777215)] [int]$Begin, [Parameter (Mandatory = $true)] [ValidateRange(5000, 16777215)] [int]$End, [Parameter (Mandatory = $false)] [switch]$Universal = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRange = $XMLDoc.CreateElement("segmentRange") $xmlDoc.Appendchild($xmlRange) | Out-Null #Mandatory and default params Add-XmlElement -xmlRoot $xmlRange -xmlElementName "name" -xmlElementText $Name.ToString() Add-XmlElement -xmlRoot $xmlRange -xmlElementName "begin" -xmlElementText $Begin.ToString() Add-XmlElement -xmlRoot $xmlRange -xmlElementName "end" -xmlElementText $End.ToString() #Optional params if ( $PsBoundParameters.ContainsKey('Description')) { Add-XmlElement -xmlRoot $xmlRange -xmlElementName "description" -xmlElementText $Description.ToString() } # #Do the post $body = $xmlRange.OuterXml $URI = "/api/2.0/vdn/config/segments?isUniversal=$($Universal.ToString().ToLower())" Write-Progress -Activity "Creating Segment Id Range" $response = Invoke-NsxRestMethod -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Creating Segment Id Range" -Completed Get-NsxSegmentIdRange -ObjectId $response.segmentRange.id -Connection $connection } end {} } function Get-NsxSegmentIdRange { <# .SYNOPSIS Reieves VXLAN Segment ID Ranges. .DESCRIPTION Segment ID Ranges provide a method for NSX to allocate a unique identifier (VNI) to each logical switch created within NSX. The Get-NsxSegmentIdRange cmdlet retrieves Segment Ranges from the connected NSX manager. #> [CmdletBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $false, Position = 1, ParameterSetName = "Name")] [Parameter (Mandatory = $false, ParameterSetName = "UniversalOnly", Position = 1)] [Parameter (Mandatory = $false, ParameterSetName = "LocalOnly", Position = 1)] #Name of the segment ID range to return [string]$Name, [Parameter (Mandatory = $false, ParameterSetName = "ObjectId")] #ObjectId of the segment ID range to return [string]$ObjectId, [Parameter (Mandatory = $true, ParameterSetName = "UniversalOnly")] #Return only Universal objects [switch]$UniversalOnly, [Parameter (Mandatory = $true, ParameterSetName = "LocalOnly")] #Return only Locally scoped objects [switch]$LocalOnly, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) Process { if ( $PsBoundParameters.ContainsKey('ObjectId')) { $URI = "/api/2.0/vdn/config/segments/$ObjectId" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $response.segmentRange } else { $URI = "/api/2.0/vdn/config/segments" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if (([bool](($response.segmentRanges).PSobject.Properties.name -match "segmentRange"))) { switch ( $PSCmdlet.ParameterSetName ) { "Name" { $response.segmentRanges.segmentRange | Where-Object { $_.name -eq $Name } } "UniversalOnly" { $response.segmentRanges.segmentRange | Where-Object { $_.isUniversal -eq "true" } } "LocalOnly" { $response.segmentRanges.segmentRange | Where-Object { $_.isUniversal -eq "false" } } Default { $response.segmentRanges.segmentRange } } } } } } function Remove-NsxSegmentIdRange { <# .SYNOPSIS Removes a Segment Id Range .DESCRIPTION Segment ID Ranges provide a method for NSX to allocate a unique identifier (VNI) to each logical switch created within NSX. The Remove-NsxSegmentIdRange cmdlet removes the specified Segment Id Range from the connected NSX manager. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateSegmentIdRange $_ })] [System.Xml.XmlElement]$SegmentIdRange, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Segment Id Range removal is permanent." $question = "Proceed with removal of Segment Id Range $($SegmentIdRange.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/2.0/vdn/config/segments/$($SegmentIdRange.Id)" Write-Progress -Activity "Remove Segment Id Range $($SegmentIdRange.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Segment Id Range $($SegmentIdRange.Name)" -Completed } } end {} } function Get-NsxTransportZone { <# .SYNOPSIS Retrieves a TransportZone object. .DESCRIPTION Transport Zones are used to control the scope of logical switches within NSX. A Logical Switch is 'bound' to a transport zone, and only hosts that are members of the Transport Zone are able to host VMs connected to a Logical Switch that is bound to it. All Logical Switch operations require a Transport Zone. .EXAMPLE PS C:\> Get-NsxTransportZone -name TestTZ Get the NSX Transport Zone named "TestTZ" .EXAMPLE PS C:\> Get-NsxTransportZone -LocalOnly Get all Local NSX Transport Zones configured .EXAMPLE PS C:\> Get-NsxTransportZone -UniversalOnly Get all Universal NSX Transport Zones configured #> [CmdLetBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $true, Position = 1, ParameterSetName = "Name")] [Parameter (Mandatory = $false, ParameterSetName = "UniversalOnly", Position = 1)] [Parameter (Mandatory = $false, ParameterSetName = "LocalOnly", Position = 1)] #TransportZoneName [string]$name, [Parameter (Mandatory = $true, ParameterSetName = "objectId")] #NSX ObjectId [ValidateNotNullOrEmpty()] [string]$objectId, [Parameter (Mandatory = $true, ParameterSetName = "UniversalOnly")] #Return only Universal objects [switch]$UniversalOnly, [Parameter (Mandatory = $true, ParameterSetName = "LocalOnly")] #Return only Locally scoped objects [switch]$LocalOnly, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) if ( $psCmdlet.ParameterSetName -eq "objectId" ) { #Just getting a single Transport Zone by ID $URI = "/api/2.0/vdn/scopes/$objectId" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $response.vdnscope } else { #Getting all TZ and optionally filtering on name $URI = "/api/2.0/vdn/scopes" [system.xml.xmldocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query "child::vdnScopes/vdnScope")) { $return = $response.vdnscopes.vdnscope if ( $psboundParameters.ContainsKey("Name") ) { $return = $return | Where-Object { $_.name -eq $name } } if ( $UniversalOnly ) { $return | Where-Object { $_.isUniversal -eq 'True' } } elseif ( $LocalOnly ) { $return | Where-Object { $_.isUniversal -eq 'False' } } else { $return } } } } function New-NsxTransportZone { <# .SYNOPSIS Creates a new Nsx Transport Zone. .DESCRIPTION An NSX Transport Zone defines the maximum scope for logical switches that are bound to it. NSX Prepared clusters are added to Transport Zones which allows VMs on them to attach to any logical switch bound to the transport zone. The New-NsxTransportZone cmdlet creates a new Transport Zone on the connected NSX manager. At least one cluster is required to be a member of the Transport Zone at creation time. #> param ( [Parameter (Mandatory = $true, Position = 1)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$Description, [Parameter (Mandatory = $true)] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop[]]$Cluster, [Parameter (Mandatory = $true)] [ValidateSet("UNICAST_MODE", "MULTICAST_MODE", "HYBRID_MODE", IgnoreCase = $false)] [string]$ControlPlaneMode, [Parameter (Mandatory = $false)] [switch]$Universal = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlScope = $XMLDoc.CreateElement("vdnScope") $xmlDoc.Appendchild($xmlScope) | Out-Null #Mandatory and default params Add-XmlElement -xmlRoot $xmlScope -xmlElementName "name" -xmlElementText $Name.ToString() Add-XmlElement -xmlRoot $xmlScope -xmlElementName "controlPlaneMode" -xmlElementText $ControlPlaneMode.ToString() #Dont ask me, I just work here :| [System.XML.XMLElement]$xmlClusters = $XMLDoc.CreateElement("clusters") $xmlScope.Appendchild($xmlClusters) | Out-Null foreach ( $instance in $cluster ) { [System.XML.XMLElement]$xmlCluster1 = $XMLDoc.CreateElement("cluster") $xmlClusters.Appendchild($xmlCluster1) | Out-Null [System.XML.XMLElement]$xmlCluster2 = $XMLDoc.CreateElement("cluster") $xmlCluster1.Appendchild($xmlCluster2) | Out-Null Add-XmlElement -xmlRoot $xmlCluster2 -xmlElementName "objectId" -xmlElementText $Instance.ExtensionData.Moref.Value } #Optional params if ( $PsBoundParameters.ContainsKey('Description')) { Add-XmlElement -xmlRoot $xmlScope -xmlElementName "description" -xmlElementText $Description.ToString() } # #Do the post $body = $xmlScope.OuterXml $URI = "/api/2.0/vdn/scopes?isUniversal=$($Universal.ToString().ToLower())" Write-Progress -Activity "Creating Transport Zone." $response = Invoke-NsxRestMethod -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Creating Transport Zone." -Completed Get-NsxTransportZone -objectId $response -Connection $connection } end {} } function Wait-NsxTransportZoneJob { <# .SYNOPSIS Wait for the specified member add/remove job until it succeeds or fails. .DESCRIPTION Attempt to wait for the specified transport zone modificationjob until it succeeds or fails. Wait-NsxTransportZoneJob defaults to timeout at 300 seconds, when the user is prompted to continuing waiting of fail. If immediate failure upon timeout is desirable (eg within script), then the $failOnTimeout switch can be set. .EXAMPLE Wait-NsxTransportZoneJob -Jobid jobdata-1234 Wait for transportzone job jobdata-1234 up to the default of 30 seconds to complete successfully or fail. If 30 seconds elapse, then prompt for action. .EXAMPLE Wait-NsxTransportZoneJob -Jobid jobdata-1234 -TimeOut 40 -FailOnTimeOut Wait for transportzone job jobdata-1234 up to 40 seconds to complete successfully or fail. If 40 seconds elapse, then throw an error. #> param ( [Parameter (Mandatory = $true)] #Job Id string as returned from the api [string]$JobId, [Parameter (Mandatory = $false)] #Seconds to wait before declaring a timeout. Timeout defaults to 30 seconds. [int]$WaitTimeout = 30, [Parameter (Mandatory = $false)] #Do we prompt user an allow them to reset the timeout timer, or throw on timeout [switch]$FailOnTimeout = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $WaitJobArgs = @{ "jobid" = $jobid "JobStatusUri" = "/api/2.0/services/taskservice/job" "CompleteCriteria" = { $job.jobInstances.jobInstance.status -eq "COMPLETED" } "FailCriteria" = { $job.jobInstances.jobInstance.status -eq "FAILED" } "StatusExpression" = { $execTask = @() $StatusMessage = "" $execTask = @($job.jobinstances.jobInstance.taskInstances.taskInstance | Where-Object { $_.taskStatus -eq "EXECUTING" }) if ( $exectask.count -eq 1) { $StatusMessage = "$($execTask.name) - $($execTask.taskStatus)" } else { $StatusMessage = "$($job.jobinstances.jobInstance.Status)" } $StatusMessage } "ErrorExpression" = { $failTask = @() $failMessage = "" $failTask = @($job.jobinstances.jobInstance.taskInstances.taskInstance | Where-Object { $_.taskStatus -eq "FAILED" }) if ( $failTask.count -eq 1) { $failMessage = "Failed Task : $($failTask.name) - $($failTask.statusMessage)" } else { $failMessage = "$($job.jobinstances.jobInstance.Status)" } $failMessage } "WaitTimeout" = $WaitTimeout "FailOnTimeout" = $FailOnTimeout "Connection" = $Connection } Wait-NsxJob @WaitJobArgs } function Add-NsxTransportZoneMember { <# .SYNOPSIS Adds a new cluster to an existing Transport Zone. .DESCRIPTION An NSX Transport Zone defines the maximum scope for logical switches that are bound to it. NSX Prepared clusters are added to Transport Zones which allows VMs on them to attach to any logical switch bound to the transport zone. The Add-NsxTransportZoneMember cmdlet adds a new cluster to an existing Transport Zone on the connected NSX manager. .EXAMPLE Get-NsxTransportZone TZ1 | Add-NsxTransportZoneMember -Cluster (Get-cluster) Adds all clusters from the connected vCenter server to the Transport Zone TZ1 .EXAMPLE Get-NsxTransportZone -Connection $bconn -UniversalOnly | Add-NsxTransportZoneMember -Cluster (Get-cluster Compute1_b -Server vc-01b.corp.local) -Connection $bconn Gets the universal transport zone from the NSX server specified by $bconn and adds the cluster Compute1_b from vCenter server vc-01b.corp.local to it. This is an example of adding a secondary NSX manager associated VC cluster to a universal transport zone. Care must be taken to ensure only the clusters from the associated vCenter server are added to the nsx manager specified in the connection object (or the default connection) #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #PowerNSX Transport Zone object to be updated [ValidateScript( { ValidateTransportZone $_ })] [System.Xml.XmlElement]$TransportZone, [Parameter (Mandatory = $true)] #Cluster to be added to the Transport Zone [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop[]]$Cluster, [Parameter ( Mandatory = $False)] #Block until transport zone update job is 'COMPLETED' (Will timeout with prompt after -WaitTimeout seconds) #Useful if automating the tz modification so you dont have to write looping code to check status of the tz before continuing. #NOTE: Not waiting means we do NOT return an updated tz object! [switch]$Wait = $True, [Parameter ( Mandatory = $False)] #Timeout waiting for tz update job to complete before user is prompted to continue or cancel. Defaults to 30 seconds. [int]$WaitTimeout = 30, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlScope = $XMLDoc.CreateElement("vdnScope") $xmlDoc.Appendchild($xmlScope) | Out-Null Add-XmlElement -xmlRoot $xmlScope -xmlElementName "objectId" -xmlElementText $TransportZone.objectId [System.XML.XMLElement]$xmlClusters = $XMLDoc.CreateElement("clusters") $xmlScope.Appendchild($xmlClusters) | Out-Null foreach ( $instance in $cluster ) { [System.XML.XMLElement]$xmlCluster1 = $XMLDoc.CreateElement("cluster") $xmlClusters.Appendchild($xmlCluster1) | Out-Null [System.XML.XMLElement]$xmlCluster2 = $XMLDoc.CreateElement("cluster") $xmlCluster1.Appendchild($xmlCluster2) | Out-Null Add-XmlElement -xmlRoot $xmlCluster2 -xmlElementName "objectId" -xmlElementText $Instance.ExtensionData.Moref.Value } #Do the post $body = $xmlScope.OuterXml $URI = "/api/2.0/vdn/scopes/$($TransportZone.objectId)?action=expand" Write-Progress -Activity "Updating Transport Zone." try { $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection } catch { throw "Transport Zone update failed. $_" } if ( -not ($response.Content -match "jobdata-\d+")) { throw "Transport Zone update failed. $($response.content)" } #The post is ansync - the tz modification can fail after the api accepts the post. we need to check on the status of the job. if ( $Wait ) { $jobid = $response.content Write-Debug "$($MyInvocation.MyCommand.Name) : TZ update job $jobid returned in post response" #First we wait for NSX job framework to give us the needful try { Wait-NsxTransportZoneJob -Jobid $JobID -Connection $Connection -WaitTimeout $WaitTimeout Get-NsxTransportZone -Connection $connection -objectId $TransportZone.objectId } catch { throw "Cluster addition to Transport Zone $($TransportZone.Name) failed. $_" } } Write-Progress -Activity "Updating Transport Zone." -Completed } end {} } function Remove-NsxTransportZoneMember { <# .SYNOPSIS Removes an existing cluster from an existing Transport Zone. .DESCRIPTION An NSX Transport Zone defines the maximum scope for logical switches that are bound to it. NSX Prepared clusters are added to Transport Zones which allows VMs on them to attach to any logical switch bound to the transport zone. The Remove-NsxTransportZoneMember cmdlet removes a cluster from an existing Transport Zone on the connected NSX manager. .EXAMPLE Get-NsxTransportZone -UniversalOnly -Connection $bconn | Remove-NSxTransportZoneMember -Cluster (get-cluster Compute1_b -Server vc-01b.corp.local) -Connection $bconn Remove the cluster Compute1_b defined in vCenter server vc-01b.corp.local from the universal transport zone configured on the nsx manager specified by $bconn #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #PowerNSX Transport Zone object to be updated [ValidateScript( { ValidateTransportZone $_ })] [System.Xml.XmlElement]$TransportZone, [Parameter (Mandatory = $true)] #Cluster to be added to the Transport Zone [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop[]]$Cluster, [Parameter ( Mandatory = $False)] #Block until transport zone update job is 'COMPLETED' (Will timeout with prompt after -WaitTimeout seconds) #Useful if automating the tz modification so you dont have to write looping code to check status of the tz before continuing. #NOTE: Not waiting means we do NOT return an updated tz object! [switch]$Wait = $True, [Parameter ( Mandatory = $False)] #Timeout waiting for tz update job to complete before user is prompted to continue or cancel. Defaults to 30 seconds. [int]$WaitTimeout = 30, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) #Todo: Improve to accept cluster name as arg instead of PowerCLI object. begin {} process { #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlScope = $XMLDoc.CreateElement("vdnScope") $xmlDoc.Appendchild($xmlScope) | Out-Null Add-XmlElement -xmlRoot $xmlScope -xmlElementName "objectId" -xmlElementText $TransportZone.objectId [System.XML.XMLElement]$xmlClusters = $XMLDoc.CreateElement("clusters") $xmlScope.Appendchild($xmlClusters) | Out-Null foreach ( $instance in $cluster ) { [System.XML.XMLElement]$xmlCluster1 = $XMLDoc.CreateElement("cluster") $xmlClusters.Appendchild($xmlCluster1) | Out-Null [System.XML.XMLElement]$xmlCluster2 = $XMLDoc.CreateElement("cluster") $xmlCluster1.Appendchild($xmlCluster2) | Out-Null Add-XmlElement -xmlRoot $xmlCluster2 -xmlElementName "objectId" -xmlElementText $Instance.ExtensionData.Moref.Value } #Do the post $body = $xmlScope.OuterXml $URI = "/api/2.0/vdn/scopes/$($TransportZone.objectId)?action=shrink" Write-Progress -Activity "Updating Transport Zone." try { $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection } catch { throw "Transport Zone update failed. $_" } if ( -not ($response.Content -match "jobdata-\d+")) { throw "Transport Zone update failed. $($response.content)" } #The post is ansync - the tz modification can fail after the api accepts the post. we need to check on the status of the job. if ( $Wait ) { $jobid = $response.content Write-Debug "$($MyInvocation.MyCommand.Name) : TZ update job $jobid returned in post response" #First we wait for NSX job framework to give us the needful try { Wait-NsxTransportZoneJob -Jobid $JobID -Connection $Connection -WaitTimeout $WaitTimeout Get-NsxTransportZone -Connection $connection -objectId $TransportZone.objectId } catch { throw "Cluster removal from Transport Zone $($TransportZone.Name) failed. $_" } } Write-Progress -Activity "Updating Transport Zone." -Completed } end {} } function Remove-NsxTransportZone { <# .SYNOPSIS Removes an NSX Transport Zone. .DESCRIPTION An NSX Transport Zone defines the maximum scope for logical switches that are bound to it. NSX Prepared clusters are added to Transport Zones which allows VMs on them to attach to any logical switch bound to the transport zone. The Remove-NsxTransportZone cmdlet removes an existing Transport Zone on the connected NSX manager. If any logical switches are bound to the Transport Zone, the attempt to remove the Transport Zone will fail. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateTransportZone $_ })] [System.Xml.XmlElement]$TransportZone, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Transport Zone removal is permanent." $question = "Proceed with removal of Transport Zone $($TransportZone.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/2.0/vdn/scopes/$($TransportZone.objectId)" Write-Progress -Activity "Remove Transport Zone $($TransportZone.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Transport Zone $($TransportZone.Name)" -Completed } } end {} } function Add-NsxLicense { <# .SYNOPSIS Adds the specified NSX license to vCenter .DESCRIPTION All 6.2.3 and higher deployments of NSX require a valid license in order to prepare the infrasturucture for NSX. The Add-NsxLicense cmdlet adds the license to the vCenter associated with the specified (or default) NSX connection. .EXAMPLE Add-NsxLicense "aaaa-bbbb-cccc-dddd-eeee" Add the NSX License to vCenter #> param ( [Parameter (Mandatory = $true, Position = 1)] [ValidateNotNullOrEmpty()] [string]$LicenseKey, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { If ( -not ( Get-Member -InputObject $Connection -MemberType Properties -Name VIConnection ) -or (-not ( $Connection.ViConnection.IsConnected)) ) { throw "Specified connection has no associated vCenter server, or server is not connected." } } process { if ( [version]$Connection.Version -gt [version]"6.2.3") { try { $ServiceInstance = Get-View ServiceInstance -Server $Connection.VIConnection $LicenseManager = Get-View $ServiceInstance.Content.licenseManager -Server $connection.VIConnection $LicenseAssignmentManager = Get-View $LicenseManager.licenseAssignmentManager -Server $connection.VIConnection $LicenseAssignmentManager.UpdateAssignedLicense("nsx-netsec", $LicenseKey, $NULL) } catch { throw "Unable to configure NSX license. Check the license is valid and try again. $_" } } } end {} } function Get-NsxLicense { <# .SYNOPSIS Retrieves configured NSX license from vCenter .DESCRIPTION All 6.2.3 and higher deployments of NSX require a valid license in order to prepare the infrasturucture for NSX. The Get-NsxLicense cmdlet retrieves existing licenses from the vCenter associated with the specified (or default) NSX connection. .EXAMPLE Get-NsxLicense Get information about NSX License #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { If ( -not ( Get-Member -InputObject $Connection -MemberType Properties -Name VIConnection ) -or (-not ( $Connection.ViConnection.IsConnected)) ) { throw "Specified connection has no associated vCenter server, or server is not connected." } } process { if ( [version]$Connection.Version -gt [version]"6.2.3") { try { $ServiceInstance = Get-View ServiceInstance -Server $Connection.VIConnection $LicenseManager = Get-View $ServiceInstance.Content.licenseManager -Server $connection.VIConnection $LicenseManager.Licenses | Where-Object { $_.EditionKey -match 'nsx' } } catch { throw "Unable to retrieve NSX license. $_" } } } end {} } function Invoke-NsxClusterResolveAll { <# .SYNOPSIS Invokes the 'Resolve All' task for a cluster .DESCRIPTION If the cluster status is in a state where a 'resolve' action is available, the Invoke-NsxClusterResolveAll cmdlet can be executed against the cluster to trigger the Resolve All task. This command does NOT block on a resolve as no job data is returned from the NSX api for us to check on. Use Get-NsxClusterStatus to check the status of a given cluster. If the cluster status is such that a 'Resolve' operation is not available, this is a no-op (no error is thrown.) .EXAMPLE Get-Cluster Cluster01 | Invoke-NsxClusterResolveAll Triggers a 'Resolve All' For the cluster Cluster01. #> param ( [Parameter ( Mandatory = $true, ValueFromPipeline = $true)] #Cluster to trigger resolve on. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop]$Cluster, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Get the agency Id for the cluster $response = Invoke-NsxWebRequest -method Get -URI "/api/2.0/vdn/config/cluster/agency/$($cluster.extensiondata.moref.value)" [xml]$Content = $response.content $null = Invoke-NsxWebRequest -method Post -URI "/api/2.0/vdn/config/agency/$($Content.AgencyInfo.Agencyid)?action=resolveAll" } end {} } ######### ######### # User related functions function Get-NsxUserRole { <# .SYNOPSIS Retrieves the user role .DESCRIPTION Each user has a role (with permissions) on NSX .EXAMPLE get-NsxUserRole admin Get the role of admin user #> param ( [Parameter(Mandatory = $true, Position = 1)] #Username to query role details. [ValidateNotNullorEmpty()] [string]$UserName, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { try { $result = Invoke-NsxRestMethod -method get -URI "/api/2.0/services/usermgmt/role/$UserName" -connection $connection } catch { throw "Unable to retrieve role details from NSX. $_" } $result.accessControlEntry } end {} } ######### ######### # L2 related functions function Get-NsxLogicalSwitch { <# .SYNOPSIS Retrieves a Logical Switch object .DESCRIPTION An NSX Logical Switch provides L2 connectivity to VMs attached to it. A Logical Switch is 'bound' to a Transport Zone, and only hosts that are members of the Transport Zone are able to host VMs connected to a Logical Switch that is bound to it. All Logical Switch operations require a Transport Zone. .EXAMPLE Get-NsxLogicalswitch -name LS1 Get a named Logical Switch (LS1) from all transport zones .EXAMPLE Get-NsxTransportZone -LocalOnly | Get-NsxLogicalswitch -name LS1 Get a named Logical Switch (LS1) from all Local Transport Zones (use -UniversalOnly for Universal Transport Zones) .EXAMPLE Get-NsxTransportZone | Get-NsxLogicalswitch Get all logical switches from all Transport Zones. .EXAMPLE Get-NsxTransportZone -UniversalOnly | Get-NsxLogicalswitch Get all logical switches from all Universal Transport Zones (use -LocalOnly for Local Transport Zones) #> [CmdletBinding(DefaultParameterSetName = "vdnscope")] param ( [Parameter (Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = "vdnscope")] [ValidateScript( { ValidateTransportZone $_ })] [alias("vdnScope")] [System.Xml.XmlElement]$TransportZone, [Parameter (Mandatory = $false, Position = 1)] [string]$Name, [Parameter (Mandatory = $true, ParameterSetName = "virtualWire")] [ValidateNotNullOrEmpty()] [alias("virtualWireId")] [string]$ObjectId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $psCmdlet.ParameterSetName -eq "virtualWire" ) { #Just getting a single named Logical Switch $URI = "/api/2.0/vdn/virtualwires/$ObjectId" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $response.virtualWire } else { #Getting all LS in a given VDNScope $lspagesize = 10 if ( $PSBoundParameters.ContainsKey('TransportZone')) { $URI = "/api/2.0/vdn/scopes/$($TransportZone.objectId)/virtualwires?pagesize=$lspagesize&startindex=00" } else { $URI = "/api/2.0/vdn/virtualwires?pagesize=$lspagesize&startindex=00" } $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $logicalSwitches = @() #LS XML is returned as paged data, means we have to handle it. #May refactor this later, depending on where else I find this in the NSX API (its not really documented in the API guide) $itemIndex = 0 $startingIndex = 0 $pagingInfo = $response.virtualWires.dataPage.pagingInfo if ( [int]$paginginfo.totalCount -ne 0 ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Logical Switches count non zero" do { Write-Debug "$($MyInvocation.MyCommand.Name) : In paging loop. PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" while (($itemindex -lt ([int]$paginginfo.pagesize + $startingIndex)) -and ($itemIndex -lt [int]$paginginfo.totalCount )) { Write-Debug "$($MyInvocation.MyCommand.Name) : In Item Processing Loop: ItemIndex: $itemIndex" Write-Debug "$($MyInvocation.MyCommand.Name) : $(@($response.virtualwires.datapage.virtualwire)[($itemIndex - $startingIndex)].objectId)" #Need to wrap the virtualwire prop of the datapage in case we get exactly 1 item - #which powershell annoyingly unwraps to a single xml element rather than an array... $logicalSwitches += @($response.virtualwires.datapage.virtualwire)[($itemIndex - $startingIndex)] $itemIndex += 1 } Write-Debug "$($MyInvocation.MyCommand.Name) : Out of item processing - PagingInfo: PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" if ( [int]$paginginfo.totalcount -gt $itemIndex) { Write-Debug "$($MyInvocation.MyCommand.Name) : PagingInfo: PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" $startingIndex += $lspagesize if ( $PSBoundParameters.ContainsKey('vndScope')) { $URI = "/api/2.0/vdn/scopes/$($TransportZone.objectId)/virtualwires?pagesize=$lspagesize&startindex=$startingIndex" } else { $URI = "/api/2.0/vdn/virtualwires?pagesize=$lspagesize&startindex=$startingIndex" } $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $pagingInfo = $response.virtualWires.dataPage.pagingInfo } } until ( [int]$paginginfo.totalcount -le $itemIndex ) Write-Debug "$($MyInvocation.MyCommand.Name) : Completed page processing: ItemIndex: $itemIndex" } if ( $name ) { $logicalSwitches | Where-Object { $_.name -eq $name } } else { $logicalSwitches } } } end { } } function New-NsxLogicalSwitch { <# .SYNOPSIS Creates a new Logical Switch .DESCRIPTION An NSX Logical Switch provides L2 connectivity to VMs attached to it. A Logical Switch is 'bound' to a Transport Zone, and only hosts that are members of the Transport Zone are able to host VMs connected to a Logical Switch that is bound to it. All Logical Switch operations require a Transport Zone. A new Logical Switch defaults to the control plane mode of the Transport Zone it is created in, but CP mode can specified as required. .EXAMPLE Get-NsxTransportZone | New-NsxLogicalSwitch -name LS6 Create a Logical Switch with default control plane mode on all Transport Zones. .EXAMPLE Get-NsxTransportZone -LocalOnly | New-NsxLogicalSwitch -name LS6 Create a Logical Switch with default control plane mode on All Local Transport Zones. (Use -UniversalOnly for create on Universal Transport Zones) .EXAMPLE Get-NsxTransportZone | New-NsxLogicalSwitch -name LS6 -ControlPlaneMode MULTICAST_MODE Create a Logical Switch with a specific control plane mode on all Transport Zones. #> [CmdletBinding()] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullOrEmpty()] [alias("vdnScope")] [System.XML.XMLElement]$TransportZone, [Parameter (Mandatory = $true, Position = 1)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$Description = "", [Parameter (Mandatory = $false)] [string]$TenantId = "", [Parameter (Mandatory = $false)] [ValidateSet("UNICAST_MODE", "MULTICAST_MODE", "HYBRID_MODE", IgnoreCase = $false)] [string]$ControlPlaneMode, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("virtualWireCreateSpec") $xmlDoc.appendChild($xmlRoot) | Out-Null #Create an Element and append it to the root Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText $Description Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "tenantId" -xmlElementText $TenantId if ( $ControlPlaneMode ) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "controlPlaneMode" -xmlElementText $ControlPlaneMode } #Do the post $body = $xmlroot.OuterXml $URI = "/api/2.0/vdn/scopes/$($TransportZone.objectId)/virtualwires" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection #response only contains the vwire id, we have to query for it to get output consisten with get-nsxlogicalswitch Get-NsxLogicalSwitch -virtualWireId $response.content -Connection $connection } end {} } function Remove-NsxLogicalSwitch { <# .SYNOPSIS Removes a Logical Switch .DESCRIPTION An NSX Logical Switch provides L2 connectivity to VMs attached to it. A Logical Switch is 'bound' to a Transport Zone, and only hosts that are members of the Transport Zone are able to host VMs connected to a Logical Switch that is bound to it. All Logical Switch operations require a Transport Zone. .EXAMPLE Get-NsxTransportZone | Get-NsxLogicalSwitch LS6 | Remove-NsxLogicalSwitch Remove a Logical Switch from all Transport Zones. .EXAMPLE Get-NsxTransportZone -UniversalOnly | Get-NsxLogicalSwitch LS6 | Remove-NsxLogicalSwitch Remove a Logical Switch from all Universal Transport Zones (use -LocalOnly to remove from all Local Transport Zones). .EXAMPLE Get-NsxTransportZone | Get-NsxLogicalSwitch LS6 | Remove-NsxLogicalSwitch -confirm:$false Remove a Logical Switch from all Transport Zones without confirmation. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$virtualWire, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Logical Switch removal is permanent." $question = "Proceed with removal of Logical Switch $($virtualWire.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/2.0/vdn/virtualwires/$($virtualWire.ObjectId)" Write-Progress -Activity "Remove Logical Switch $($virtualWire.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Logical Switch $($virtualWire.Name)" -Completed } } end {} } function Set-NsxLogicalSwitch { <# .SYNOPSIS Configure a Logical Switch .DESCRIPTION An NSX Logical Switch provides L2 connectivity to VMs attached to it. A Logical Switch is 'bound' to a Transport Zone, and only hosts that are members of the Transport Zone are able to host VMs connected to a Logical Switch that is bound to it. All Logical Switch operations require a Transport Zone. .EXAMPLE Get-NsxTransportZone | Get-NsxLogicalSwitch LS6 | Set-NsxLogicalSwitch -name "LS6-new" Rename the LogicalSwitch LS6 to LS6-new .EXAMPLE Get-NsxTransportZone | Get-NsxLogicalSwitch LS6 | Set-NsxLogicalSwitch -description "My Logical Switch Number 6" Configure LogicalSwitch LS6 Description (to My Logical Switch Number 6) #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$virtualWire, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$name, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$description, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { $ObjectId = $virtualWire.ObjectId #Clone the xml so we dont modify source... $_virtualWire = $virtualWire.CloneNode($true) if ( $PsBoundParameters.ContainsKey('name') ) { $_virtualWire.name = $name } if ( $PsBoundParameters.ContainsKey('description') ) { $_virtualWire.description = $description } $uri = "/api/2.0/vdn/virtualwires/$($ObjectId)" $null = Invoke-NsxWebRequest -method put -URI $uri -body $_virtualWire.OuterXml -connection $connection Get-NsxLogicalSwitch -ObjectId $ObjectId -Connection $connection } end {} } function Connect-NsxLogicalSwitch { <# .SYNOPSIS Connects a VM to a logical switch .DESCRIPTION An NSX Logical Switch provides L2 connectivity to VMs attached to it. A Logical Switch is 'bound' to a Transport Zone, and only hosts that are members of the Transport Zone are able to host VMs connected to a Logical Switch that is bound to it. Connect-NsxLogicalSwitch accepts either a VM or NIC and attaches it to the specified LogicalSwitch. #> [CmdLetBinding(DefaultParameterSetName = "VM")] param( [Parameter(Mandatory = $true, ParameterSetName = "VM", ValueFromPipeline = $true)] #VM or collection of VMs to attach to specified logical switch. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop[]]$VirtualMachine, [Parameter(Mandatory = $true, ParameterSetName = "NIC", ValueFromPipeline = $true)] #Network Adapter or collection of Network Adapters to attach to specified logical switch. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop[]]$NetworkAdapter, [Parameter(Mandatory = $true, Position = 1)] #Logical Switch to connect NICs or VMs to. [ValidateScript( { ValidateLogicalSwitch $_ })] [System.Xml.XmlElement]$LogicalSwitch, [Parameter(Mandatory = $false)] #If specified VM is multi homed, connect all NICs to the same network. Defaults to $false [switch]$ConnectMultipleNics = $false, [Parameter(Mandatory = $false)] #If job reaches -WaitTimeout without failing or completing, do we prompt, or fail with error? [switch]$FailOnTimeout = $false, [Parameter(Mandatory = $false)] #Seconds to wait for connection job to complete. Defaults to 30 seconds. [int]$WaitTimeout = 30, [Parameter (Mandatory = $false)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { function ProcessNic { param ( $nic ) #See NSX API guide 'Attach or Detach a Virtual Machine from a Logical Switch' for #how to construct NIC id. $vmUuid = ($nic.parent | Get-View).config.instanceuuid $vnicUuid = "$vmUuid.$($nic.id.substring($nic.id.length-3))" #Construct XML $xmldoc = New-Object System.Xml.XmlDocument $xmlroot = $xmldoc.CreateElement("com.vmware.vshield.vsm.inventory.dto.VnicDto") $null = $xmldoc.AppendChild($xmlroot) Add-XmlElement -xmlRoot $xmlroot -xmlElementName "objectId" -xmlElementText $vnicUuid Add-XmlElement -xmlRoot $xmlroot -xmlElementName "vnicUuid" -xmlElementText $vnicUuid Add-XmlElement -xmlRoot $xmlroot -xmlElementName "portgroupId" -xmlElementText $LogicalSwitch.objectId #Do the post $body = $xmlroot.OuterXml $URI = "/api/2.0/vdn/virtualwires/vm/vnic" Write-Progress -Activity "Processing" -Status "Connecting $vnicuuid to logical switch $($LogicalSwitch.objectId)" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Processing" -Status "Connecting $vnicuuid to logical switch $($LogicalSwitch.objectId)" -Completed #api returns a task id. $job = [xml]$response.content $jobId = $job."com.vmware.vshield.vsm.vdn.dto.ui.ReconfigureVMTaskResultDto".jobId Wait-NsxGenericJob -JobId $JobID -Connection $Connection -WaitTimeout $WaitTimeout -FailOnTimeout:$FailOnTimeout } } process { switch ( $PSCmdlet.ParameterSetName ) { "VM" { foreach ( $vm in $VirtualMachine ) { [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop[]]$nics = $vm | Get-NetworkAdapter switch ($nics.count) { 0 { Write-Warning "Virtual Machine $($vm.name) ($($vm.extensiondata.moref.value)) has no network adapters. Nothing to do." } 1 { #do nothing } default { if ( -not $ConnectMultipleNics ) { Throw "Virtual Machine $($vm.name) ($($vm.extensiondata.moref.value)) has more than one network adapter. Specify -ConnectMultipleNics switch if this is really what you want." } } } foreach ( $nic in $nics ) { ProcessNic $nic } } } "NIC" { foreach ( $nic in $NetworkAdapter ) { ProcessNic $nic } } } } end {} } function Disconnect-NsxLogicalSwitch { <# .SYNOPSIS Disconnects a VM from a logical switch .DESCRIPTION An NSX Logical Switch provides L2 connectivity to VMs attached to it. A Logical Switch is 'bound' to a Transport Zone, and only hosts that are members of the Transport Zone are able to host VMs connected to a Logical Switch that is bound to it. Disconnect-NsxLogicalSwitch accepts either a VM or NIC and detaches it from the specified LogicalSwitch. #> [CmdLetBinding(DefaultParameterSetName = "VM")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param( [Parameter(Mandatory = $true, ParameterSetName = "VM", ValueFromPipeline = $true)] #VM or collection of VMs to attach to specified logical switch. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop[]]$VirtualMachine, [Parameter(Mandatory = $true, ParameterSetName = "NIC", ValueFromPipeline = $true)] #Network Adapter or collection of Network Adapters to attach to specified logical switch. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop[]]$NetworkAdapter, [Parameter(Mandatory = $false)] #If specified VM is multi homed, disconnect all NICs from the same network. Defaults to $false [switch]$DisconnectMultipleNics = $false, [Parameter(Mandatory = $false)] #Prompt for confirmation. [switch]$Confirm = $true, [Parameter(Mandatory = $false)] #If job reaches -WaitTimeout without failing or completing, do we prompt, or fail with error? [switch]$FailOnTimeout = $false, [Parameter(Mandatory = $false)] #Seconds to wait for connection job to complete. Defaults to 30 seconds. [int]$WaitTimeout = 30, [Parameter (Mandatory = $false)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { function ProcessNic { param ( $nic ) #See NSX API guide 'Attach or Detach a Virtual Machine from a Logical Switch' for #how to construct NIC id. $vmUuid = ($nic.parent | Get-View).config.instanceuuid $vnicUuid = "$vmUuid.$($nic.id.substring($nic.id.length-3))" #Construct XML $xmldoc = New-Object System.Xml.XmlDocument $xmlroot = $xmldoc.CreateElement("com.vmware.vshield.vsm.inventory.dto.VnicDto") $null = $xmldoc.AppendChild($xmlroot) Add-XmlElement -xmlRoot $xmlroot -xmlElementName "objectId" -xmlElementText $vnicUuid Add-XmlElement -xmlRoot $xmlroot -xmlElementName "vnicUuid" -xmlElementText $vnicUuid Add-XmlElement -xmlRoot $xmlroot -xmlElementName "portgroupId" -xmlElementText "" #Do the post $body = $xmlroot.OuterXml $URI = "/api/2.0/vdn/virtualwires/vm/vnic" if ( $confirm ) { $message = "Disconnecting $($nic.Parent.Name)'s network adapter from a logical switch will cause network connectivity loss." $question = "Proceed with disconnection?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Processing" -Status "Disconnecting $vnicuuid from logical switch" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Processing" -Status "Disconnecting $vnicuuid from logical switch" -Completed $job = [xml]$response.content $jobId = $job."com.vmware.vshield.vsm.vdn.dto.ui.ReconfigureVMTaskResultDto".jobId Wait-NsxGenericJob -JobId $JobID -Connection $Connection -WaitTimeout $WaitTimeout -FailOnTimeout:$FailOnTimeout } } } process { switch ( $PSCmdlet.ParameterSetName ) { "VM" { foreach ( $vm in $VirtualMachine ) { [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop[]]$nics = $vm | Get-NetworkAdapter switch ($nics.count) { 0 { Write-Warning "Virtual Machine $($vm.name) ($($vm.extensiondata.moref.value)) has no network adapters. Nothing to do." } 1 { #do nothing } default { if ( -not $DisconnectMultipleNics ) { Throw "Virtual Machine $($vm.name) ($($vm.extensiondata.moref.value)) has more than one network adapter. Specify -ConnectMultipleNics switch if this is really what you want." } } } foreach ( $nic in $nics ) { ProcessNic $nic } } } "NIC" { foreach ( $nic in $nics ) { ProcessNic $nic } } } } end {} } ######### ######### # Spoofguard related functions function Get-NsxSpoofguardPolicy { <# .SYNOPSIS Retrieves Spoofguard policy objects from NSX. .DESCRIPTION If a virtual machine has been compromised, its IP address can be spoofed and malicious transmissions can bypass firewall policies. You create a SpoofGuard policy for specific networks that allows you to authorize the IP addresses reported by VMware Tools and alter them if necessary to prevent spoofing. SpoofGuard inherently trusts the MAC addresses of virtual machines collected from the VMX files and vSphere SDK. Operating separately from Firewall rules, you can use SpoofGuard to block traffic determined to be spoofed. Use the Get-NsxSpoofguardPolicy cmdlet to retrieve existing SpoofGuard Policy objects from NSX. .EXAMPLE Get-NsxSpoofguardPolicy Get all Spoofguard policies .EXAMPLE Get-NsxSpoofguardPolicy Test Get a specific Spoofguard policy #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [ValidateNotNullorEmpty()] [String]$Name, [Parameter (Mandatory = $false, ParameterSetName = "ObjectId")] [ValidateNotNullorEmpty()] [string]$objectId, [Parameter (Mandatory = $false, ParameterSetName = "ObjectId")] [Parameter (Mandatory = $false, ParameterSetName = "Name")] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( $PsCmdlet.ParameterSetName -eq 'Name' ) { #All SG Policies $URI = "/api/4.0/services/spoofguard/policies/" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $response ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::spoofguardPolicies/spoofguardPolicy')) { if ( $Name ) { $polcollection = $response.spoofguardPolicies.spoofguardPolicy | Where-Object { $_.name -eq $Name } } else { $polcollection = $response.spoofguardPolicies.spoofguardPolicy } foreach ($pol in $polcollection ) { #Note that when you use the objectid URI, the NSX API actually reutrns additional information (statistics element), #so, without doing this, to the PowerNSX users, get-nsxsgpolicy would return a subset of info compared to #get-nsxsgpolicy which I dont like. $URI = "/api/4.0/services/spoofguard/policies/$($pol.policyId)" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $response ) { $response.spoofguardPolicy } else { throw "Unable to retrieve SpoofGuard policy $($pol.policyId)." } } } } } else { #Just getting a single SG Policy $URI = "/api/4.0/services/spoofguard/policies/$objectId" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $response ) { $response.spoofguardPolicy } } } end {} } function New-NsxSpoofguardPolicy { <# .SYNOPSIS Creates a new Spoofguard policy in NSX. .DESCRIPTION If a virtual machine has been compromised, its IP address can be spoofed and malicious transmissions can bypass firewall policies. You create a SpoofGuard policy for specific networks that allows you to authorize the IP addresses reported by VMware Tools and alter them if necessary to prevent spoofing. SpoofGuard inherently trusts the MAC addresses of virtual machines collected from the VMX files and vSphere SDK. Operating separately from Firewall rules, you can use SpoofGuard to block traffic determined to be spoofed. Use the New-NsxSpoofguardPolicy cmdlet to create a new SpoofGuard Policy in NSX. Policies are not published (enforced) automatically. Use the -publish switch to automatically publish a newly created policy. Note that this could impact VM communications depending on the policy settings. .EXAMPLE $ls = Get-NsxTransportZone | Get-NsxLogicalSwitch LSTemp New-NsxSpoofguardPolicy -Name Test -Description Testing -OperationMode tofu -Network $ls Create a new Trust on First Use Spoofguard policy protecting the Logical Switch LSTemp .EXAMPLE $vss_pg = Get-VirtualPortGroup -Name "VM Network" | select-object -First 1 $vds_pg = Get-VDPortgroup -Name "Internet" $ls = Get-NsxTransportZone | Get-NsxLogicalSwitch -Name LSTemp New-NsxSpoofguardPolicy -Name Test -Description Testing -OperationMode manual -Network $vss_pg, $vds_pg, $ls Create a new manual approval policy for three networks (a VSS PG, VDS PG and Logical switch) .EXAMPLE $ls = Get-NsxTransportZone | Get-NsxLogicalSwitch LSTemp New-NsxSpoofguardPolicy -Name Test -Description Testing -OperationMode tofu -Network $ls -publish Create a new Trust on First Use Spoofguard policy protecting the Logical Switch LSTemp and publish it immediately. Publishing causes the policy to be enforced on the data plane immediately (and potentially block all communication, so use with care!) .EXAMPLE $ls = Get-NsxTransportZone | Get-NsxLogicalSwitch LSTemp New-NsxSpoofguardPolicy -Name Test -Description Testing -OperationMode tofu -Network $ls -AllowLocalIps Create a new Trust on First Use Spoofguard policy protecting the Logical Switch LSTemp and allow local IPs to be approved (169.254/16 and fe80::/64) #> [CmdletBinding()] param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$Description, [Parameter (Mandatory = $true)] [ValidateSet("tofu", "manual", "disable")] [string]$OperationMode, [Parameter (Mandatory = $false)] [switch]$AllowLocalIps, [Parameter (Mandatory = $true)] [ValidateScript( { ValidateLogicalSwitchOrDistributedPortGroupOrStandardPortGroup $_ })] [object[]]$Network, [Parameter (Mandatory = $False)] [switch]$Publish = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("spoofguardPolicy") $xmlDoc.appendChild($xmlRoot) | Out-Null Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "operationMode" -xmlElementText $OperationMode.ToUpper() if ( $PSBoundParameters.ContainsKey('description')) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText $Description } if ( $PSBoundParameters.ContainsKey('AllowLocalIps')) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "allowLocalIPs" -xmlElementText $AllowLocalIps.ToString().ToLower() } foreach ( $Net in $Network) { [System.XML.XMLElement]$xmlEnforcementPoint = $XMLDoc.CreateElement("enforcementPoint") $xmlroot.appendChild($xmlEnforcementPoint) | Out-Null switch ( $Net ) { { $_ -is [System.Xml.XmlElement] } { $id = $_.objectId } { $_ -is [VMware.VimAutomation.ViCore.Interop.V1.Host.Networking.DistributedPortGroupInterop] } { $id = $_.ExtensionData.MoRef.Value } { $_ -is [VMware.VimAutomation.ViCore.Interop.V1.Host.Networking.VirtualPortGroupInterop] } { #Standard Port Group specified... Hope you appreciate this, coz the vSphere API and PowerCLI niceness dissapear a bit here. #and it took me a while to work out how to get around it. #You dont seem to be able to get a standard Moref outa the PowerCLI network object that represents a VSS PG. #You also dont seem to be able to do a get-view on it :| #So, I have get a hasthtable of all morefs that represent VSS based PGs and search it for the name of the PG the user specified. Im fairly (not 100%) sure this is safe as networkname should be unique at least within VSS portgroups... $StandardPgHash = Get-View -ViewType Network -Property Name | Where-Object { $_.Moref.Type -match 'Network' } | Select-Object name, moref | Sort-Object -Property Name -Unique | Group-Object -AsHashTable -Property Name $Item = $StandardPgHash.Item($_.name) if ( -not $item ) { throw "PortGroup $($_.name) not found." } $id = $Item.MoRef.Value } } Add-XmlElement -xmlRoot $xmlEnforcementPoint -xmlElementName "id" -xmlElementText $id } #Do the post $body = $xmlroot.OuterXml $URI = "/api/4.0/services/spoofguard/policies/" $policyId = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection #Now we Publish... if ( $publish ) { $URI = "/api/4.0/services/spoofguard/$($policyId)?action=publish" $null = Invoke-NsxWebRequest -method "post" -URI $URI -connection $connection } Get-NsxSpoofguardPolicy -objectId $policyId -Connection $connection } end {} } function Remove-NsxSpoofguardPolicy { <# .SYNOPSIS Removes the specified Spoofguard policy object from NSX. .DESCRIPTION If a virtual machine has been compromised, its IP address can be spoofed and malicious transmissions can bypass firewall policies. You create a SpoofGuard policy for specific networks that allows you to authorize the IP addresses reported by VMware Tools and alter them if necessary to prevent spoofing. SpoofGuard inherently trusts the MAC addresses of virtual machines collected from the VMX files and vSphere SDK. Operating separately from Firewall rules, you can use SpoofGuard to block traffic determined to be spoofed. Use the Remnove-NsxSpoofguardPolicy cmdlet to remove the specified SpoofGuard Policy from NSX. .EXAMPLE Get-NsxSpoofguardPolicy test | Remove-NsxSpoofguardPolicy Remove the policy Test. .EXAMPLE Get-NsxSpoofguardPolicy | Remove-NsxSpoofguardPolicy Remove all policies. .EXAMPLE Get-NsxSpoofguardPolicy test | Remove-NsxSpoofguardPolicy -confirm:$false Remove the policy Test without confirmation. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$SpoofguardPolicy, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( $SpoofguardPolicy.defaultPolicy -eq 'true') { Write-Warning "Cant delete the default Spoofguard policy" } else { if ( $confirm ) { $message = "Spoofguard Policy removal is permanent." $question = "Proceed with removal of Spoofguard Policy $($SpoofguardPolicy.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/4.0/services/spoofguard/policies/$($SpoofguardPolicy.policyId)" Write-Progress -Activity "Remove Spoofguard Policy $($SpoofguardPolicy.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Spoofguard Policy $($SpoofguardPolicy.Name)" -Completed } } } end {} } function Publish-NsxSpoofguardPolicy { <# .SYNOPSIS Publishes the specified Spoofguard policy object. .DESCRIPTION If a virtual machine has been compromised, its IP address can be spoofed and malicious transmissions can bypass firewall policies. You create a SpoofGuard policy for specific networks that allows you to authorize the IP addresses reported by VMware Tools and alter them if necessary to prevent spoofing. SpoofGuard inherently trusts the MAC addresses of virtual machines collected from the VMX files and vSphere SDK. Operating separately from Firewall rules, you can use SpoofGuard to block traffic determined to be spoofed. Use the Publish-NsxSpoofguardPolicy cmdlet to publish the specified SpoofGuard Policy. This causes it to be enforced. .EXAMPLE New-NsxSpoofguardPolicy -Name Test -Description Testing -OperationMode manual -Network $vss_pg, $vds_pg, $ls Get-NsxSpoofguardPolicy test | Publish-NsxSpoofguardPolicy Create and then separately publish a new policy. .EXAMPLE Get-NsxSpoofguardPolicy test | Get-NsxSpoofguardNic -NetworkAdapter (Get-Vm TestVm | get-NetworkAdapter | select-object -first 1) | Grant-NsxSpoofguardNicApproval -IpAddress 1.2.3.4 Get-NsxSpoofguardPolicy test | Publish-NsxSpoofguardPolicy Grant an approval to the first nic on the VM TestVM for ip 1.2.3.4 and publish it #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$SpoofguardPolicy, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( $confirm ) { $message = "Spoofguard Policy publishing will cause the current policy to be enforced." $question = "Proceed with publish operation on Spoofguard Policy $($SpoofguardPolicy.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/4.0/services/spoofguard/$($SpoofguardPolicy.policyId)?action=publish" Write-Progress -Activity "Publish Spoofguard Policy $($SpoofguardPolicy.Name)" Invoke-NsxRestMethod -method "post" -URI $URI -connection $connection | Out-Null Write-Progress -Activity "Publish Spoofguard Policy $($SpoofguardPolicy.Name)" -Completed Get-NsxSpoofguardPolicy -objectId $($SpoofguardPolicy.policyId) -Connection $connection } } end {} } function Get-NsxSpoofguardNic { <# .SYNOPSIS Retrieves Spoofguard NIC details for the specified Spoofguard policy. .DESCRIPTION If a virtual machine has been compromised, its IP address can be spoofed and malicious transmissions can bypass firewall policies. You create a SpoofGuard policy for specific networks that allows you to authorize the IP addresses reported by VMware Tools and alter them if necessary to prevent spoofing. SpoofGuard inherently trusts the MAC addresses of virtual machines collected from the VMX files and vSphere SDK. Operating separately from Firewall rules, you can use SpoofGuard to block traffic determined to be spoofed. Use the Get-NsxSpoofguardNic cmdlet to retrieve Spoofguard NIC details for the specified Spoofguard policy .EXAMPLE Get-NsxSpoofguardPolicy test | Get-NsxSpoofguardNic -NetworkAdapter (Get-vm evil-vm | Get-NetworkAdapter| select -First 1) Get the Spoofguard settings for the first NIC on vM Evil-Vm .EXAMPLE Get-NsxSpoofguardPolicy test | Get-NsxSpoofguardNic -VirtualMachine (Get-vm evil-vm) Get the Spoofguard settings for all nics on vM Evil-Vm .EXAMPLE Get-NsxSpoofguardPolicy test | Get-NsxSpoofguardNic -MacAddress 00:50:56:81:04:28 Get the Spoofguard settings for the MAC address 00:50:56:81:04:28 .EXAMPLE Get-NsxSpoofguardPolicy test | Get-NsxSpoofguardNic -Filter Inactive Get all Inactive spoofguard Nics #> [CmdLetBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "Default")] [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "MAC")] [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "VM")] [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "NIC")] [ValidateScript( { ValidateSpoofguardPolicy $_ } )] [System.xml.xmlElement]$SpoofguardPolicy, [Parameter (Mandatory = $false, ParameterSetName = "Default")] [Parameter (Mandatory = $false, ParameterSetName = "MAC")] [Parameter (Mandatory = $false, ParameterSetName = "VM")] [Parameter (Mandatory = $false, ParameterSetName = "NIC")] [Validateset("Active", "Inactive", "Published", "Unpublished", "Review_Pending", "Duplicate")] [string]$Filter, [Parameter (Mandatory = $false, ParameterSetName = "MAC")] [ValidateScript( { if ( $_ -notmatch "[a-f,A-F,0-9]{2}:[a-f,A-F,0-9]{2}:[a-f,A-F,0-9]{2}:[a-f,A-F,0-9]{2}:[a-f,A-F,0-9]{2}:[a-f,A-F,0-9]{2}" ) { throw "Specify a valid MAC address (0 must be specified as 00)" } $true })] [string]$MacAddress, [Parameter (Mandatory = $false, ParameterSetName = "VM")] #PowerCLI VirtualMachine object [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop]$VirtualMachine, [Parameter (Mandatory = $false, ParameterSetName = "NIC")] [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop]$NetworkAdapter, [Parameter (Mandatory = $false, ParameterSetName = "Default")] [Parameter (Mandatory = $false, ParameterSetName = "MAC")] [Parameter (Mandatory = $false, ParameterSetName = "VM")] [Parameter (Mandatory = $false, ParameterSetName = "NIC")] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( $PsBoundParameters.ContainsKey('Filter')) { $URI = "/api/4.0/services/spoofguard/$($SpoofguardPolicy.policyId)?list=$($Filter.ToUpper())" } else { #Not documented in the API guide but appears to work ;) $URI = "/api/4.0/services/spoofguard/$($SpoofguardPolicy.policyId)?list=ALL" } [system.xml.xmldocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::spoofguardList/spoofguard')) { switch ( $PsCmdlet.ParameterSetName ) { "MAC" { $outcollection = $response.spoofguardList.Spoofguard | Where-Object { $_.detectedMacAddress -eq $MacAddress } } "NIC" { $MacAddress = $NetworkAdapter.MacAddress $outcollection = $response.spoofguardList.Spoofguard | Where-Object { $_.detectedMacAddress -eq $MacAddress } } "VM" { foreach ( $Nic in ($virtualmachine | Get-NetworkAdapter )) { $MacAddress = $Nic.MacAddress $outcollection = $response.spoofguardList.Spoofguard | Where-Object { $_.detectedMacAddress -eq $MacAddress } } } default { $outcollection = $response.spoofguardList.Spoofguard } } #Add the policyId to the XML so we can pipline to grant/revoke cmdlets. foreach ( $out in $outcollection ) { Add-XmlElement -xmlRoot $out -xmlElementName "policyId" -xmlElementText $($SpoofguardPolicy.policyId) } $outcollection } else { Write-Debug "$($MyInvocation.MyCommand.Name) : No results found." } } end {} } function Grant-NsxSpoofguardNicApproval { <# .SYNOPSIS Approves a new IP for the specified Spoofguard NIC. .DESCRIPTION If a virtual machine has been compromised, its IP address can be spoofed and malicious transmissions can bypass firewall policies. You create a SpoofGuard policy for specific networks that allows you to authorize the IP addresses reported by VMware Tools and alter them if necessary to prevent spoofing. SpoofGuard inherently trusts the MAC addresses of virtual machines collected from the VMX files and vSphere SDK. Operating separately from Firewall rules, you can use SpoofGuard to block traffic determined to be spoofed. Use the Grant-NsxSpoofguardNicApproval cmdlet to add the specified IP to the list of approved IPs for the specified Spoofguard NIC. .EXAMPLE Get-NsxSpoofguardPolicy test | Get-NsxSpoofguardNic -NetworkAdapter (Get-vm evil-vm | Get-NetworkAdapter| select -First 1) | Grant-NsxSpoofguardNicApproval -IpAddress 1.2.3.4 -Publish Grant approval for the first NIC on VM Evil-VM to use the IP 1.2.3.4 and publish immediately .EXAMPLE Get-NsxSpoofguardPolicy test | Get-NsxSpoofguardNic -NetworkAdapter (Get-vm evil-vm | Get-NetworkAdapter| select -First 1) | Grant-NsxSpoofguardNicApproval --ApproveAllDetectedIps -Publish Grant approval for the first NIC on VM Evil-VM to use all IPs detected by whatever IP detction methods are available and publish immediately. Note: This *may* include 'local' IPs (such as fe80::/64) which may not be allowed if the policy is not enabled with 'AllowLocalIps'. In this case this operation will throw a cryptic error (Valid values are {2}) and not succeed. In this case you must either change the policy to allow local IPs, or manually approve the specific IPs you want. This issue affects the NSX UI as well. #> [CmdLetBinding(DefaultParameterSetName = "ipAddress")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateSpoofguardNic $_ } )] [System.xml.xmlElement]$SpoofguardNic, [Parameter (Mandatory = $True, ParameterSetName = "ipAddress")] [ValidateNotNullOrEmpty()] [string[]]$IpAddress, [Parameter (Mandatory = $True, ParameterSetName = "ApproveAll")] [ValidateNotNullOrEmpty()] [switch]$ApproveAllDetectedIps = $False, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$Publish = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #We need to modify the spoofguardNic XML element, so we need to clone it. $_SpoofguardNic = $SpoofguardNic.CloneNode($true) [System.XML.XMLDocument]$xmlDoc = $_SpoofguardNic.OwnerDocument [System.XML.XMLElement]$spoofguardList = $XMLDoc.CreateElement("spoofguardList") $spoofguardList.appendChild($_SpoofguardNic) | Out-Null #Get and Remove the policyId element we put there... $policyId = $_SpoofguardNic.policyId $_SpoofguardNic.RemoveChild((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SpoofguardNic -query 'descendant::policyId')) | Out-Null #if approvedIpAddress element does not exist, create it [system.xml.xmlElement]$approvedIpAddressNode = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SpoofguardNic -query 'descendant::approvedIpAddress') if ( -not $approvedIpAddressNode ) { [System.XML.XMLElement]$approvedIpAddressNode = $XMLDoc.CreateElement("approvedIpAddress") $_SpoofguardNic.appendChild($approvedIpAddressNode) | Out-Null } #If they are, Add the ip(s) specified if ( $PsBoundParameters.ContainsKey('ipAddress') ) { foreach ( $ip in $ipAddress ) { if ( (Invoke-XpathQuery -QueryMethod SelectNodes -Node $approvedIpAddressNode -query "descendant::ipAddress") | Where-Object { $_.'#Text' -eq $ip }) { Write-Warning "Not adding duplicate IP Address $ip as it is already added." } else { Add-XmlElement -xmlRoot $approvedIpAddressNode -xmlElementName "ipAddress" -xmlElementText $ip } } } #If there are IPs detected, and approve all is on, ensure user understands consequence. If ( $ApproveAllDetectedIps -and ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SpoofguardNic -query 'descendant::detectedIpAddress/ipAddress'))) { If ($confirm ) { $message = "Do you want to automatically approve all IP Addresses detected on the NIC $($_SpoofguardNic.nicName)?." $question = "Validate the detected IP addresses before continuing. Proceed?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { foreach ( $ip in $_SpoofguardNic.detectedIpAddress.ipAddress ) { #Have to ensure we dont add a duplicate here... if ( (Invoke-XpathQuery -QueryMethod SelectNodes -Node $approvedIpAddressNode -query "descendant::ipAddress") | Where-Object { $_.'#Text' -eq $ip }) { Write-Warning "Not adding duplicate IP Address $ip as it is already added." } else { Add-XmlElement -xmlRoot $approvedIpAddressNode -xmlElementName "ipAddress" -xmlElementText $ip } } } } # Had bad thoughts about allowing manual specification of MAC. I might come back to this... # if ( $PsCmdlet.ParameterSetName -eq "ManualMac" ) { # if ( -not ( (Invoke-XPathQuery -QueryMethod SelectSingleNode -Node $_SpoofguardNic -Query 'descendant::approvedMacAddress'))){ # Add-XmlElement -xmlRoot $_SpoofguardNic -xmlElementName "approvedMacAddress" -xmlElementText $MacAddress # } # else { # #Assume user wants to overwrite... should we confirm on this? # $_SpoofguardNic.approvedMacAddress = $MacAddress # } # } # else { # if ( -not ( (Invoke-XPathQuery -QueryMethod SelectSingleNode -Node $_SpoofguardNic -Query 'descendant::approvedMacAddress'))){ # Add-XmlElement -xmlRoot $_SpoofguardNic -xmlElementName "approvedMacAddress" -xmlElementText $_SpoofguardNic.detectedMacAddress # } # else { # #Assume user wants to overwrite... should we confirm on this? # $_SpoofguardNic.approvedMacAddress = $MacAddress # } # } #Do the post $body = $spoofguardList.OuterXml $URI = "/api/4.0/services/spoofguard/$($policyId)?action=approve" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection #Now we Publish... if ( $publish ) { $URI = "/api/4.0/services/spoofguard/$($policyId)?action=publish" $response = Invoke-NsxWebRequest -method "post" -URI $URI -connection $connection } Get-NsxSpoofguardPolicy -objectId $policyId -Connection $connection | Get-NsxSpoofguardNic -MAC $_SpoofguardNic.detectedMacAddress -Connection $connection } end {} } function Revoke-NsxSpoofguardNicApproval { <# .SYNOPSIS Removes an approved IP from the specified Spoofguard NIC. .DESCRIPTION If a virtual machine has been compromised, its IP address can be spoofed and malicious transmissions can bypass firewall policies. You create a SpoofGuard policy for specific networks that allows you to authorize the IP addresses reported by VMware Tools and alter them if necessary to prevent spoofing. SpoofGuard inherently trusts the MAC addresses of virtual machines collected from the VMX files and vSphere SDK. Operating separately from Firewall rules, you can use SpoofGuard to block traffic determined to be spoofed. Use the Revoke-NsxSpoofguardNicApproval cmdlet to remove the specified IP from the list of approved IPs for the specified Spoofguard NIC. .EXAMPLE Get-NsxSpoofguardPolicy test | Get-NsxSpoofguardNic -NetworkAdapter (Get-vm evil-vm | Get-NetworkAdapter| select -First 1) | Revoke-NsxSpoofguardNicApproval -RevokeAllApprovedIps -publish Revoke all approved IPs for vm evil-vm and immediately publish the policy. .EXAMPLE Get-NsxSpoofguardPolicy test | Get-NsxSpoofguardNic -NetworkAdapter (Get-vm evil-vm | Get-NetworkAdapter| select -First 1) | Revoke-NsxSpoofguardNicApproval -IpAddress 1.2.3.4 Revoke the approval for IP 1.2.3.4 from the first nic on vm evil-vm. #> [CmdLetBinding(DefaultParameterSetName = "IpList")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateSpoofguardNic $_ } )] [System.xml.xmlElement]$SpoofguardNic, [Parameter (Mandatory = $True, ParameterSetName = "IpList")] [ValidateNotNullOrEmpty()] [string[]]$IpAddress, [Parameter (Mandatory = $True, ParameterSetName = "RevokeAll")] [ValidateNotNullOrEmpty()] [switch]$RevokeAllApprovedIps, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$Publish = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #We need to modify the spoofguardNic XML element, so we need to clone it. $_SpoofguardNic = $SpoofguardNic.CloneNode($true) [System.XML.XMLDocument]$xmlDoc = $_SpoofguardNic.OwnerDocument [System.XML.XMLElement]$spoofguardList = $XMLDoc.CreateElement("spoofguardList") $spoofguardList.appendChild($_SpoofguardNic) | Out-Null #Get and Remove the policyId element we put there... $policyId = $_SpoofguardNic.policyId $_SpoofguardNic.RemoveChild((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SpoofguardNic -query 'descendant::policyId')) | Out-Null #if approvedIpAddress element does not exist, bail [system.xml.xmlElement]$approvedIpAddressNode = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SpoofguardNic -query 'descendant::approvedIpAddress') if ( -not $approvedIpAddressNode -or (-not ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $approvedIpAddressNode -query 'descendant::ipAddress')))) { Write-Warning "Nic $($_SpoofguardNic.NicName) has no approved IPs" } else { [system.xml.xmlElement]$publishedIpAddressNode = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SpoofguardNic -query 'descendant::publishedIpAddress') $approvedIpCollection = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $approvedIpAddressNode -query "descendant::ipAddress") $publishedIpCollection = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $publishedIpAddressNode -query "descendant::ipAddress") #If there are IPs detected, and revoke all is on, kill em all... If ( $PSCmdlet.ParameterSetName -eq "RevokeAll" ) { foreach ( $node in $approvedIpCollection ) { $approvedIpAddressNode.RemoveChild($node) | Out-Null } } else { #$IPAddress is mandatory... foreach ( $ip in $ipAddress ) { $currentApprovedIpNode = $approvedIpCollection | Where-Object { $_.'#Text' -eq $ip } $currentPublishedIpNode = $publishedIpCollection | Where-Object { $_.'#Text' -eq $ip } if ( -not $currentApprovedIpNode ) { Write-Warning "IP Address $ip is not currently approved on Nic $($_SpoofguardNic.NicName)." } else { $approvedIpAddressNode.RemoveChild($currentApprovedIpNode) | Out-Null if ( $currentPublishedIpNode ) { $publishedIpAddressNode.RemoveChild($currentPublishedIpNode) | Out-Null } } } } If ($confirm ) { $message = "Do you want to remove the specified IP Addresses from the approved list of the NIC $($_SpoofguardNic.nicName)?." $question = "Removal is permenant. Proceed?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { #Do the post $body = $spoofguardList.OuterXml $URI = "/api/4.0/services/spoofguard/$($policyId)?action=approve" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection #Now we Publish... if ( $publish) { $URI = "/api/4.0/services/spoofguard/$($policyId)?action=publish" $response = Invoke-NsxWebRequest -method "post" -URI $URI -connection $connection } Get-NsxSpoofguardPolicy -objectId $policyId -Connection $connection | Get-NsxSpoofguardNic -MAC $_SpoofguardNic.detectedMacAddress -Connection $connection } } } end {} } ######### ######### # Distributed Router functions function New-NsxLogicalRouterInterfaceSpec { <# .SYNOPSIS Creates a new NSX Logical Router Interface Spec. .DESCRIPTION NSX Logical Routers can host up to 1000 interfaces, each of which can be configured with multiple properties. In order to allow creation of Logical Routers with an arbitrary number of interfaces, a unique spec for each interface required must first be created. Logical Routers do support interfaces on VLAN backed portgroups, and this cmdlet will support a interface spec connected to a normal portgroup, however this is not noramlly a recommended scenario. .EXAMPLE PS C:\> $Uplink = New-NsxLogicalRouterinterfaceSpec -Name Uplink_interface -Type uplink -ConnectedTo (Get-NsxTransportZone | Get-NsxLogicalSwitch LS1) -PrimaryAddress 192.168.0.1 -SubnetPrefixLength 24 PS C:\> $Internal = New-NsxLogicalRouterinterfaceSpec -Name Internal-interface -Type internal -ConnectedTo (Get-NsxTransportZone | Get-NsxLogicalSwitch LS2) -PrimaryAddress 10.0.0.1 -SubnetPrefixLength 24 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true)] [ValidateSet ("internal", "uplink")] [string]$Type, [Parameter (Mandatory = $false)] [ValidateScript( { ValidateLogicalSwitchOrDistributedPortGroup $_ })] [object]$ConnectedTo, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$PrimaryAddress, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$SubnetPrefixLength, [Parameter (Mandatory = $false)] [ValidateRange(1, 9128)] [int]$MTU = 1500, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [switch]$Connected = $true, [Parameter (Mandatory = $false)] [ValidateRange(1, 1000)] [int]$Index ) begin { if ( $Connected -and ( -not $connectedTo ) ) { #Not allowed to be connected without a connected port group. throw "Interfaces that are connected must be connected to a distributed Portgroup or Logical Switch." } if (( $PsBoundParameters.ContainsKey("PrimaryAddress") -and ( -not $PsBoundParameters.ContainsKey("SubnetPrefixLength"))) -or (( -not $PsBoundParameters.ContainsKey("PrimaryAddress")) -and $PsBoundParameters.ContainsKey("SubnetPrefixLength"))) { #Not allowed to have subnet without primary or vice versa. throw "Interfaces with a Primary address must also specify a prefix length and vice versa." } } process { [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlVnic = $XMLDoc.CreateElement("interface") $xmlDoc.appendChild($xmlVnic) | Out-Null if ( $PsBoundParameters.ContainsKey("Name")) { Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "name" -xmlElementText $Name } if ( $PsBoundParameters.ContainsKey("Type")) { Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "type" -xmlElementText $type } if ( $PsBoundParameters.ContainsKey("Index")) { Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "index" -xmlElementText $Index } Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "mtu" -xmlElementText $MTU Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "isConnected" -xmlElementText $Connected switch ($ConnectedTo) { { $_ -is [VMware.VimAutomation.ViCore.Interop.V1.Host.Networking.DistributedPortGroupInterop] } { $PortGroupID = $_.ExtensionData.MoRef.Value } { $_ -is [System.Xml.XmlElement] } { $PortGroupID = $_.objectId } } if ( $PsBoundParameters.ContainsKey("ConnectedTo")) { Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "connectedToId" -xmlElementText $PortGroupID } if ( $PsBoundParameters.ContainsKey("PrimaryAddress")) { #For now, only supporting one addressgroup - will refactor later [System.XML.XMLElement]$xmlAddressGroups = $XMLDoc.CreateElement("addressGroups") $xmlVnic.appendChild($xmlAddressGroups) | Out-Null $AddressGroupParameters = @{ xmlAddressGroups = $xmlAddressGroups } if ( $PsBoundParameters.ContainsKey("PrimaryAddress" )) { $AddressGroupParameters.Add("PrimaryAddress", $PrimaryAddress) } if ( $PsBoundParameters.ContainsKey("SubnetPrefixLength" )) { $AddressGroupParameters.Add("SubnetPrefixLength", $SubnetPrefixLength) } AddNsxEdgeVnicAddressGroup @AddressGroupParameters } $xmlVnic } end {} } function Get-NsxLogicalRouter { <# .SYNOPSIS Retrieves a Logical Router object. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. This cmdlet returns Logical Router objects. .EXAMPLE PS C:\> Get-NsxLogicalRouter LR1 #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ParameterSetName = "objectId")] [string]$objectId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [string]$Name, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $pagesize = 10 switch ( $psCmdlet.ParameterSetName ) { "Name" { $URI = "/api/4.0/edges?pageSize=$pagesize&startIndex=00" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection #Edge summary XML is returned as paged data, means we have to handle it. #Then we have to query for full information on a per edge basis. $edgesummaries = @() $edges = @() $itemIndex = 0 $startingIndex = 0 $pagingInfo = $response.pagedEdgeList.edgePage.pagingInfo if ( [int]$paginginfo.totalCount -ne 0 ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Logical Router count non zero" do { Write-Debug "$($MyInvocation.MyCommand.Name) : In paging loop. PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" while (($itemindex -lt ([int]$paginginfo.pagesize + $startingIndex)) -and ($itemIndex -lt [int]$paginginfo.totalCount )) { Write-Debug "$($MyInvocation.MyCommand.Name) : In Item Processing Loop: ItemIndex: $itemIndex" Write-Debug "$($MyInvocation.MyCommand.Name) : $(@($response.pagedEdgeList.edgePage.edgeSummary)[($itemIndex - $startingIndex)].objectId)" #Need to wrap the edgesummary prop of the datapage in case we get exactly 1 item - #which powershell annoyingly unwraps to a single xml element rather than an array... $edgesummaries += @($response.pagedEdgeList.edgePage.edgeSummary)[($itemIndex - $startingIndex)] $itemIndex += 1 } Write-Debug "$($MyInvocation.MyCommand.Name) : Out of item processing - PagingInfo: PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" if ( [int]$paginginfo.totalcount -gt $itemIndex) { Write-Debug "$($MyInvocation.MyCommand.Name) : PagingInfo: PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" $startingIndex += $pagesize $URI = "/api/4.0/edges?pageSize=$pagesize&startIndex=$startingIndex" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $pagingInfo = $response.pagedEdgeList.edgePage.pagingInfo } } until ( [int]$paginginfo.totalcount -le $itemIndex ) Write-Debug "$($MyInvocation.MyCommand.Name) : Completed page processing: ItemIndex: $itemIndex" } #What we got here is...failure to communicate! In order to get full detail, we have to requery for each edgeid. #But... there is information in the SUmmary that isnt in the full detail. So Ive decided to add the summary as a node #to the returned edge detail. foreach ($edgesummary in $edgesummaries) { $URI = "/api/4.0/edges/$($edgesummary.objectID)" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $import = $response.edge.ownerDocument.ImportNode($edgesummary, $true) $response.edge.appendChild($import) | Out-Null $edges += $response.edge } if ( $name ) { $edges | Where-Object { $_.Type -eq 'distributedRouter' } | Where-Object { $_.name -eq $name } } else { $edges | Where-Object { $_.Type -eq 'distributedRouter' } } } "objectId" { $URI = "/api/4.0/edges/$objectId" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $edge = $response.edge $URI = "/api/4.0/edges/$objectId/summary" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $import = $edge.ownerDocument.ImportNode($($response.edgeSummary), $true) $edge.AppendChild($import) | Out-Null $edge } } } function New-NsxLogicalRouter { <# .SYNOPSIS Creates a new Logical Router object. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. This cmdlet creates a new Logical Router. A Logical router has many configuration options - not all are exposed with New-NsxLogicalRouter. Use Set-NsxLogicalRouter for other configuration. Interface configuration is handled by passing interface spec objects created by the New-NsxLogicalRouterInterfaceSpec cmdlet. A valid PowerCLI session is required to pass required objects as required by cluster/resourcepool and datastore parameters. .EXAMPLE Create a new LR with interfaces on existsing Logical switches (LS1,2,3 and Management interface on Mgmt) PS C:\> $ls1 = get-nsxtransportzone | get-nsxlogicalswitch LS1 PS C:\> $ls2 = get-nsxtransportzone | get-nsxlogicalswitch LS2 PS C:\> $ls3 = get-nsxtransportzone | get-nsxlogicalswitch LS3 PS C:\> $mgt = get-nsxtransportzone | get-nsxlogicalswitch Mgmt PS C:\> $vnic0 = New-NsxLogicalRouterInterfaceSpec -Type uplink -Name vNic0 -ConnectedTo $ls1 -PrimaryAddress 1.1.1.1 -SubnetPrefixLength 24 PS C:\> $vnic1 = New-NsxLogicalRouterInterfaceSpec -Type internal -Name vNic1 -ConnectedTo $ls2 -PrimaryAddress 2.2.2.1 -SubnetPrefixLength 24 PS C:\> $vnic2 = New-NsxLogicalRouterInterfaceSpec -Type internal -Name vNic2 -ConnectedTo $ls3 -PrimaryAddress 3.3.3.1 -SubnetPrefixLength 24 PS C:\> New-NsxLogicalRouter -Name testlr -ManagementPortGroup $mgt -Interface $vnic0,$vnic1,$vnic2 -Cluster (Get-Cluster) -Datastore (get-datastore) #> param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true)] [ValidateScript( { ValidateLogicalSwitchOrDistributedPortGroup $_ })] [object]$ManagementPortGroup, [Parameter (Mandatory = $true)] [ValidateScript( { ValidateLogicalRouterInterfaceSpec $_ })] [System.Xml.XmlElement[]]$Interface, [Parameter (Mandatory = $true, ParameterSetName = "ResourcePool")] [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ResourcePoolInterop]$ResourcePool, [Parameter (Mandatory = $true, ParameterSetName = "Cluster")] [ValidateScript( { if ( $_ -eq $null ) { throw "Must specify Cluster." } if ( -not $_.DrsEnabled ) { throw "Cluster is not DRS enabled." } $true })] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop]$Cluster, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.DatastoreManagement.DatastoreInterop]$Datastore, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [switch]$EnableHA = $false, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.DatastoreManagement.DatastoreInterop]$HADatastore = $datastore, [Parameter (Mandatory = $false)] #Set to deploy as a universal distributed logical router. [switch]$Universal = $false, [Parameter (Mandatory = $false)] #Create the universal logical router with Local Egress enabled. [switch]$EnableLocalEgress = $false, [Parameter (Mandatory = $false)] #Optional tenant string to be configured on the DLR. [ValidateNotNullOrEmpty()] [String]$Tenant, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("edge") $xmlDoc.appendChild($xmlRoot) | Out-Null Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "type" -xmlElementText "distributedRouter" if ($PSBoundParameters.ContainsKey("Tenant")) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "tenant" -xmlElementText $Tenant } switch ($ManagementPortGroup) { { $_ -is [VMware.VimAutomation.ViCore.Interop.V1.Host.Networking.DistributedPortGroupInterop] } { $PortGroupID = $_.ExtensionData.MoRef.Value } { $_ -is [System.Xml.XmlElement] } { $PortGroupID = $_.objectId } } [System.XML.XMLElement]$xmlMgmtIf = $XMLDoc.CreateElement("mgmtInterface") $xmlRoot.appendChild($xmlMgmtIf) | Out-Null Add-XmlElement -xmlRoot $xmlMgmtIf -xmlElementName "connectedToId" -xmlElementText $PortGroupID [System.XML.XMLElement]$xmlAppliances = $XMLDoc.CreateElement("appliances") $xmlRoot.appendChild($xmlAppliances) | Out-Null switch ($psCmdlet.ParameterSetName) { "Cluster" { $ResPoolId = $($cluster | Get-ResourcePool | Where-Object { $_.parent.id -eq $cluster.id }).extensiondata.moref.value } "ResourcePool" { $ResPoolId = $ResourcePool.extensiondata.moref.value } } [System.XML.XMLElement]$xmlAppliance = $XMLDoc.CreateElement("appliance") $xmlAppliances.appendChild($xmlAppliance) | Out-Null Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "resourcePoolId" -xmlElementText $ResPoolId Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "datastoreId" -xmlElementText $datastore.extensiondata.moref.value if ( $EnableHA ) { [System.XML.XMLElement]$xmlAppliance = $XMLDoc.CreateElement("appliance") $xmlAppliances.appendChild($xmlAppliance) | Out-Null Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "resourcePoolId" -xmlElementText $ResPoolId Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "datastoreId" -xmlElementText $HAdatastore.extensiondata.moref.value } [System.XML.XMLElement]$xmlVnics = $XMLDoc.CreateElement("interfaces") $xmlRoot.appendChild($xmlVnics) | Out-Null foreach ( $VnicSpec in $Interface ) { $import = $xmlDoc.ImportNode(($VnicSpec), $true) $xmlVnics.AppendChild($import) | Out-Null } if ( ( $EnableLocalEgress ) -and ( $universal ) ) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "localEgressEnabled" -xmlElementText "True" } #Do the post $body = $xmlroot.OuterXml $URI = "/api/4.0/edges?isUniversal=$universal" Write-Progress -Activity "Creating Logical Router $Name" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Creating Logical Router $Name" -Completed $edgeId = $response.Headers.Location.split("/")[$response.Headers.Location.split("/").GetUpperBound(0)] if ( $EnableHA ) { [System.XML.XMLElement]$xmlHA = $XMLDoc.CreateElement("highAvailability") Add-XmlElement -xmlRoot $xmlHA -xmlElementName "enabled" -xmlElementText "true" $body = $xmlHA.OuterXml $URI = "/api/4.0/edges/$edgeId/highavailability/config" Write-Progress -Activity "Enable HA on Logical Router $Name" $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Enable HA on Logical Router $Name" -Completed } Get-NsxLogicalRouter -objectId $edgeId -Connection $connection } end {} } function Remove-NsxLogicalRouter { <# .SYNOPSIS Deletes a Logical Router object. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. This cmdlet deletes the specified Logical Router object. .EXAMPLE Example1: Remove Logical Router LR1. PS C:\> Get-NsxLogicalRouter LR1 | Remove-NsxLogicalRouter Example2: No confirmation on delete. PS C:\> Get-NsxLogicalRouter LR1 | Remove-NsxLogicalRouter -confirm:$false #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLogicalRouter $_ })] [System.Xml.XmlElement]$LogicalRouter, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Logical Router removal is permanent." $question = "Proceed with removal of Logical Router $($LogicalRouter.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/4.0/edges/$($LogicalRouter.Edgesummary.ObjectId)" Write-Progress -Activity "Remove Logical Router $($LogicalRouter.Name)" Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection | Out-Null Write-Progress -Activity "Remove Logical Router $($LogicalRouter.Name)" -Completed } } end {} } function Set-NsxLogicalRouterInterface { <# .SYNOPSIS Configures an existing NSX LogicalRouter interface. .DESCRIPTION NSX Logical Routers can host up to 8 uplink and 1000 internal interfaces, each of which can be configured with multiple properties. Logical Routers support interfaces connected to either VLAN backed port groups or NSX Logical Switches, although connection to VLAN backed PortGroups is not a recommended configuration. Use Set-NsxLogicalRouterInterface to overwrite the configuration of an existing interface. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterInterface $_ })] [System.Xml.XmlElement]$Interface, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true)] [ValidateSet ("internal", "uplink")] [string]$Type, [Parameter (Mandatory = $true)] [ValidateScript( { ValidateLogicalSwitchOrDistributedPortGroup $_ })] [object]$ConnectedTo, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$PrimaryAddress, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$SubnetPrefixLength, [Parameter (Mandatory = $false)] [ValidateRange(1, 9128)] [int]$MTU = 1500, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [switch]$Connected = $true, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Check if there is already configuration if ( $confirm ) { $message = "Interface configuration will be overwritten." $question = "Proceed with reconfiguration for $($Interface.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) if ( $decision -eq 1 ) { return } } #generate the vnic XML $vNicSpecParams = @{ Index = $Interface.index Name = $name Type = $type ConnectedTo = $connectedTo MTU = $MTU Connected = $connected } if ( $PsBoundParameters.ContainsKey("PrimaryAddress" )) { $vNicSpecParams.Add("PrimaryAddress", $PrimaryAddress) } if ( $PsBoundParameters.ContainsKey("SubnetPrefixLength" )) { $vNicSpecParams.Add("SubnetPrefixLength", $SubnetPrefixLength) } if ( $PsBoundParameters.ContainsKey("SecondaryAddresses" )) { $vNicSpecParams.Add("SecondaryAddresses", $SecondaryAddresses) } $VnicSpec = New-NsxLogicalRouterInterfaceSpec @vNicSpecParams Write-Debug "$($MyInvocation.MyCommand.Name) : vNic Spec is $($VnicSpec.outerxml | Format-XML) " #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlVnics = $XMLDoc.CreateElement("interfaces") $import = $xmlDoc.ImportNode(($VnicSpec), $true) $xmlVnics.AppendChild($import) | Out-Null # #Do the post $body = $xmlVnics.OuterXml $URI = "/api/4.0/edges/$($Interface.logicalRouterId)/interfaces/?action=patch" Write-Progress -Activity "Updating Logical Router interface configuration for interface $($Interface.Index)." Invoke-NsxRestMethod -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Updating Logical Router interface configuration for interface $($Interface.Index)." -Completed } end {} } function New-NsxLogicalRouterInterface { <# .SYNOPSIS Configures an new NSX LogicalRouter interface. .DESCRIPTION NSX Logical Routers can host up to 8 uplink and 1000 internal interfaces, each of which can be configured with multiple properties. Logical Routers support interfaces connected to either VLAN backed port groups or NSX Logical Switches, although connection to VLAN backed PortGroups is not a recommended configuration. Use New-NsxLogicalRouterInterface to create a new Logical Router interface. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouter $_ })] [System.Xml.XmlElement]$LogicalRouter, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true)] [ValidateSet ("internal", "uplink")] [string]$Type, [Parameter (Mandatory = $true)] [ValidateScript( { ValidateLogicalSwitchOrDistributedPortGroup $_ })] [object]$ConnectedTo, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$PrimaryAddress, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$SubnetPrefixLength, [Parameter (Mandatory = $false)] [ValidateRange(1, 9128)] [int]$MTU = 1500, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [switch]$Connected = $true, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #generate the vnic XML $vNicSpecParams = @{ Name = $name Type = $type ConnectedTo = $connectedTo MTU = $MTU Connected = $connected } if ( $PsBoundParameters.ContainsKey("PrimaryAddress" )) { $vNicSpecParams.Add("PrimaryAddress", $PrimaryAddress) } if ( $PsBoundParameters.ContainsKey("SubnetPrefixLength" )) { $vNicSpecParams.Add("SubnetPrefixLength", $SubnetPrefixLength) } if ( $PsBoundParameters.ContainsKey("SecondaryAddresses" )) { $vNicSpecParams.Add("SecondaryAddresses", $SecondaryAddresses) } $VnicSpec = New-NsxLogicalRouterInterfaceSpec @vNicSpecParams Write-Debug "$($MyInvocation.MyCommand.Name) : vNic Spec is $($VnicSpec.outerxml | Format-XML) " #Construct the XML [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlVnics = $XMLDoc.CreateElement("interfaces") $import = $xmlDoc.ImportNode(($VnicSpec), $true) $xmlVnics.AppendChild($import) | Out-Null # #Do the post $body = $xmlVnics.OuterXml $URI = "/api/4.0/edges/$($LogicalRouter.Id)/interfaces/?action=patch" Write-Progress -Activity "Creating Logical Router interface." $response = Invoke-NsxRestMethod -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Creating Logical Router interface." -Completed $response.interfaces } end {} } function Remove-NsxLogicalRouterInterface { <# .SYNOPSIS Deletes an NSX Logical router interface. .DESCRIPTION NSX Logical Routers can host up to 8 uplink and 1000 internal interfaces, each of which can be configured with multiple properties. Logical Routers support interfaces connected to either VLAN backed port groups or NSX Logical Switches, although connection to VLAN backed PortGroups is not a recommended configuration. Use Remove-NsxLogicalRouterInterface to remove an existing Logical Router Interface. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterInterface $_ })] [System.Xml.XmlElement]$Interface, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Interface ($Interface.Name) will be deleted." $question = "Proceed with deletion of interface $($Interface.index)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) if ( $decision -eq 1 ) { return } } # #Do the delete $URI = "/api/4.0/edges/$($Interface.logicalRouterId)/interfaces/$($Interface.Index)" Write-Progress -Activity "Deleting interface $($Interface.Index) on logical router $($Interface.logicalRouterId)." $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Deleting interface $($Interface.Index) on logical router $($Interface.logicalRouterId)." -Completed } end {} } function Get-NsxLogicalRouterInterface { <# .SYNOPSIS Retrieves the specified interface configuration on a specified Logical Router. .DESCRIPTION NSX Logical Routers can host up to 8 uplink and 1000 internal interfaces, each of which can be configured with multiple properties. Logical Routers support interfaces connected to either VLAN backed port groups or NSX Logical Switches, although connection to VLAN backed PortGroups is not a recommended configuration. Use Get-NsxLogicalRouterInterface to retrieve the configuration of a interface. .EXAMPLE Get all Interfaces on a Logical Router. #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouter $_ })] [System.Xml.XmlElement]$LogicalRouter, [Parameter (Mandatory = $False, ParameterSetName = "Name", Position = 1)] [string]$Name, [Parameter (Mandatory = $True, ParameterSetName = "Index")] [ValidateRange(1, 1000)] [int]$Index, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( -not ($PsBoundParameters.ContainsKey("Index") )) { #All Interfaces on LR $URI = "/api/4.0/edges/$($LogicalRouter.Id)/interfaces/" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'child::interfaces/interface') { if ( $PsBoundParameters.ContainsKey("name") ) { $return = $response.interfaces.interface | Where-Object { $_.name -eq $name } if ( $return ) { Add-XmlElement -xmlDoc ([system.xml.xmldocument]$return.OwnerDocument) -xmlRoot $return -xmlElementName "logicalRouterId" -xmlElementText $($LogicalRouter.Id) } $return } else { $return = $response.interfaces.interface foreach ( $interface in $return ) { Add-XmlElement -xmlDoc ([system.xml.xmldocument]$interface.OwnerDocument) -xmlRoot $interface -xmlElementName "logicalRouterId" -xmlElementText $($LogicalRouter.Id) } $return } } } else { #Just getting a single named Interface $URI = "/api/4.0/edges/$($LogicalRouter.Id)/interfaces/$Index" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $response ) { $return = $response.interface Add-XmlElement -xmlDoc ([system.xml.xmldocument]$return.OwnerDocument) -xmlRoot $return -xmlElementName "logicalRouterId" -xmlElementText $($LogicalRouter.Id) $return } } } end {} } function Set-NsxLogicalRouter { <# .SYNOPSIS Configures an existing NSX Logical Router Raw configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Use the Set-NsxLogicalRouter to perform updates to the Raw XML config for a DLR to enable basic support for manipulating DLR features that arent supported by specific PowerNSX cmdlets. .EXAMPLE $dlr = Get-NsxLogicalRouter Dlr01 PS C:\>$dlr.features.firewall.enabled = "false" PS C:\>$dlr | Set-NsxLogicalRouter Disable the DLR Firewall on DLR Dlr01 #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] # Logical Router object as returned by Get-NsxLogicalRouter [ValidateScript( { ValidateLogicalRouter $_ })] [System.Xml.XmlElement]$LogicalRouter, [Parameter (Mandatory = $False)] # Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Clone the LogicalRouter Element so we can modify without barfing up the source object. $_LogicalRouter = $LogicalRouter.CloneNode($true) #Remove EdgeSummary... $edgeSummary = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouter -query 'descendant::edgeSummary') if ( $edgeSummary ) { $_LogicalRouter.RemoveChild($edgeSummary) | Out-Null } $URI = "/api/4.0/edges/$($_LogicalRouter.Id)" $body = $_LogicalRouter.OuterXml if ( $confirm ) { $message = "Logical Router update will modify existing Logical Router configuration." $question = "Proceed with Update of Logical Router $($LogicalRouter.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Logical Router $($LogicalRouter.Name)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Logical Router $($LogicalRouter.Name)" -Completed Get-NsxLogicalRouter -objectId $($LogicalRouter.Id) -Connection $connection } } end {} } ######## ######## # ESG related functions ###Private functions function AddNsxEdgeVnicAddressGroup { #Private function that Edge (ESG and LogicalRouter) VNIC creation leverages #To create valid address groups (primary and potentially secondary address) #and netmask. #ToDo - Implement IP address and netmask validation param ( [Parameter (Mandatory = $true)] [System.XML.XMLElement]$xmlAddressGroups, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$PrimaryAddress, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$SubnetPrefixLength, [Parameter (Mandatory = $false)] [string[]]$SecondaryAddresses = @() ) begin {} process { [System.XML.XMLDocument]$xmlDoc = $xmlAddressGroups.OwnerDocument [System.XML.XMLElement]$xmlAddressGroup = $xmlDoc.CreateElement("addressGroup") $xmlAddressGroups.appendChild($xmlAddressGroup) | Out-Null Add-XmlElement -xmlRoot $xmlAddressGroup -xmlElementName "primaryAddress" -xmlElementText $PrimaryAddress Add-XmlElement -xmlRoot $xmlAddressGroup -xmlElementName "subnetPrefixLength" -xmlElementText $SubnetPrefixLength if ( $SecondaryAddresses ) { [System.XML.XMLElement]$xmlSecondaryAddresses = $XMLDoc.CreateElement("secondaryAddresses") $xmlAddressGroup.appendChild($xmlSecondaryAddresses) | Out-Null foreach ($Address in $SecondaryAddresses) { Add-XmlElement -xmlRoot $xmlSecondaryAddresses -xmlElementName "ipAddress" -xmlElementText $Address } } } end {} } ###End Private functions function New-NsxAddressSpec { <# .SYNOPSIS Creates a new NSX Address Group Spec. .DESCRIPTION NSX ESGs and DLRs interfaces can be configured with multiple 'Address Groups'. This allows a single interface to have IP addresses defined in different subnets, each complete with their own Primary Address, Netmask and zero or more Secondary Addresses. In order to configure an interface in this way with PowerNSX, multiple 'AddressGroupSpec' objects can be created using New-NsxAddressSpec, and then specified when calling New/Set cmdlets for the associated interfaces. #> param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$PrimaryAddress, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [int]$SubnetPrefixLength, [Parameter (Mandatory = $false)] [string[]]$SecondaryAddresses = @() ) begin {} process { [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlAddressGroup = $xmlDoc.CreateElement("addressGroup") Add-XmlElement -xmlRoot $xmlAddressGroup -xmlElementName "primaryAddress" -xmlElementText $PrimaryAddress Add-XmlElement -xmlRoot $xmlAddressGroup -xmlElementName "subnetPrefixLength" -xmlElementText $SubnetPrefixLength.ToString() if ( $SecondaryAddresses ) { [System.XML.XMLElement]$xmlSecondaryAddresses = $XMLDoc.CreateElement("secondaryAddresses") $xmlAddressGroup.appendChild($xmlSecondaryAddresses) | Out-Null foreach ($Address in $SecondaryAddresses) { Add-XmlElement -xmlRoot $xmlSecondaryAddresses -xmlElementName "ipAddress" -xmlElementText $Address } } $xmlAddressGroup } end {} } function New-NsxEdgeInterfaceSpec { <# .SYNOPSIS Creates a new NSX Edge Service Gateway interface Spec. .DESCRIPTION NSX ESGs can host up to 10 interfaces and up to 200 subinterfaces, each of which can be configured with multiple properties. In order to allow creation of ESGs with an arbitrary number of interfaces, a unique spec for each interface required must first be created. ESGs support interfaces connected to either VLAN backed port groups or NSX Logical Switches. .EXAMPLE PS C:\> $Uplink = New-NsxEdgeInterfaceSpec -Name Uplink_interface -Type uplink -ConnectedTo (Get-NsxTransportZone | Get-NsxLogicalSwitch LS1) -PrimaryAddress 192.168.0.1 -SubnetPrefixLength 24 PS C:\> $Internal = New-NsxEdgeInterfaceSpec -Name Internal-interface -Type internal -ConnectedTo (Get-NsxTransportZone | Get-NsxLogicalSwitch LS2) -PrimaryAddress 10.0.0.1 -SubnetPrefixLength 24 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true)] [ValidateRange(0, 9)] [int]$Index, [Parameter (Mandatory = $false)] [string]$Name, [Parameter (Mandatory = $false)] [ValidateSet ("internal", "uplink", "trunk")] [string]$Type, [Parameter (Mandatory = $false)] [ValidateScript( { ValidateLogicalSwitchOrDistributedPortGroup $_ })] [object]$ConnectedTo, [Parameter (Mandatory = $false)] [string]$PrimaryAddress, [Parameter (Mandatory = $false)] [string]$SubnetPrefixLength, [Parameter (Mandatory = $false)] [string[]]$SecondaryAddresses = @(), [Parameter (Mandatory = $false)] [ValidateRange(1, 9128)] [int]$MTU = 1500, [Parameter (Mandatory = $false)] [switch]$EnableProxyArp = $false, [Parameter (Mandatory = $false)] [switch]$EnableSendICMPRedirects = $true, [Parameter (Mandatory = $false)] [switch]$Connected = $true ) begin { #toying with the idea of using dynamicParams for this, but decided on standard validation code for now. if ( ($Type -eq "trunk") -and ( $ConnectedTo -is [System.Xml.XmlElement])) { #Not allowed to have a trunk interface connected to a Logical Switch. throw "Interfaces of type Trunk must be connected to a distributed port group." } if ( $Connected -and ( -not $connectedTo ) ) { #Not allowed to be connected without a connected port group. throw "Interfaces that are connected must be connected to a distributed Portgroup or Logical Switch." } if (( $PsBoundParameters.ContainsKey("PrimaryAddress") -and ( -not $PsBoundParameters.ContainsKey("SubnetPrefixLength"))) -or (( -not $PsBoundParameters.ContainsKey("PrimaryAddress")) -and $PsBoundParameters.ContainsKey("SubnetPrefixLength"))) { #Not allowed to have subnet without primary or vice versa. throw "Interfaces with a Primary address must also specify a prefix length and vice versa." } } process { [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlVnic = $XMLDoc.CreateElement("vnic") $xmlDoc.appendChild($xmlVnic) | Out-Null if ( $PsBoundParameters.ContainsKey("Name")) { Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "name" -xmlElementText $Name } if ( $PsBoundParameters.ContainsKey("Index")) { Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "index" -xmlElementText $Index } if ( $PsBoundParameters.ContainsKey("Type")) { Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "type" -xmlElementText $Type } else { Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "type" -xmlElementText "internal" } Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "mtu" -xmlElementText $MTU Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "enableProxyArp" -xmlElementText $EnableProxyArp Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "enableSendRedirects" -xmlElementText $EnableSendICMPRedirects Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "isConnected" -xmlElementText $Connected if ( $PsBoundParameters.ContainsKey("ConnectedTo")) { switch ($ConnectedTo) { { $_ -is [VMware.VimAutomation.ViCore.Interop.V1.Host.Networking.DistributedPortGroupInterop] } { $PortGroupID = $_.ExtensionData.MoRef.Value } { $_ -is [System.Xml.XmlElement] } { $PortGroupID = $_.objectId } } Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "portgroupId" -xmlElementText $PortGroupID } [System.XML.XMLElement]$xmlAddressGroups = $XMLDoc.CreateElement("addressGroups") $xmlVnic.appendChild($xmlAddressGroups) | Out-Null if ( $PsBoundParameters.ContainsKey("PrimaryAddress")) { #Only supporting one addressgroup - User must use New-NsxAddressSpec to specify multiple. $AddressGroupParameters = @{ xmlAddressGroups = $xmlAddressGroups } if ( $PsBoundParameters.ContainsKey("PrimaryAddress" )) { $AddressGroupParameters.Add("PrimaryAddress", $PrimaryAddress) } if ( $PsBoundParameters.ContainsKey("SubnetPrefixLength" )) { $AddressGroupParameters.Add("SubnetPrefixLength", $SubnetPrefixLength) } if ( $PsBoundParameters.ContainsKey("SecondaryAddresses" )) { $AddressGroupParameters.Add("SecondaryAddresses", $SecondaryAddresses) } AddNsxEdgeVnicAddressGroup @AddressGroupParameters } $xmlVnic } end {} } function New-NsxEdgeSubInterfaceSpec { <# .SYNOPSIS Creates a new NSX Edge Service Gateway SubInterface Spec. .DESCRIPTION NSX ESGs can host up to 10 interfaces and up to 200 subinterfaces, each of which can be configured with multiple properties. In order to allow creation of ESGs with an arbitrary number of interfaces, a unique spec for each interface required must first be created. ESGs support Subinterfaces that specify either VLAN ID (VLAN Type) or NSX Logical Switch/Distributed Port Group (Network Type). #> [CmdLetBinding(DefaultParameterSetName = "None")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true)] [string]$Name, [Parameter (Mandatory = $true)] [ValidateRange(1, 4094)] [int]$TunnelId, [Parameter (Mandatory = $false, ParameterSetName = "Network")] [ValidateScript( { ValidateLogicalSwitchOrDistributedPortGroup $_ })] [object]$Network, [Parameter (Mandatory = $false, ParameterSetName = "VLAN")] [ValidateRange(0, 4094)] [int]$VLAN, [Parameter (Mandatory = $false)] [string]$PrimaryAddress, [Parameter (Mandatory = $false)] [string]$SubnetPrefixLength, [Parameter (Mandatory = $false)] [string[]]$SecondaryAddresses = @(), [Parameter (Mandatory = $false)] [ValidateRange(1, 9128)] [int]$MTU, [Parameter (Mandatory = $false)] [switch]$EnableSendICMPRedirects, [Parameter (Mandatory = $false)] [switch]$Connected = $true ) begin { if (( $PsBoundParameters.ContainsKey("PrimaryAddress") -and ( -not $PsBoundParameters.ContainsKey("SubnetPrefixLength"))) -or (( -not $PsBoundParameters.ContainsKey("PrimaryAddress")) -and $PsBoundParameters.ContainsKey("SubnetPrefixLength"))) { #Not allowed to have subnet without primary or vice versa. throw "Interfaces with a Primary address must also specify a prefix length and vice versa." } } process { [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlVnic = $XMLDoc.CreateElement("subInterface") $xmlDoc.appendChild($xmlVnic) | Out-Null Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "tunnelId" -xmlElementText $TunnelId Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "isConnected" -xmlElementText $Connected if ( $PsBoundParameters.ContainsKey("MTU")) { Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "mtu" -xmlElementText $MTU } if ( $PsBoundParameters.ContainsKey("EnableSendICMPRedirects")) { Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "enableSendRedirects" -xmlElementText $EnableSendICMPRedirects } if ( $PsBoundParameters.ContainsKey("Network")) { switch ($Network) { { $_ -is [VMware.VimAutomation.ViCore.Interop.V1.Host.Networking.DistributedPortGroupInterop] } { $PortGroupID = $_.ExtensionData.MoRef.Value } { $_ -is [System.Xml.XmlElement] } { $PortGroupID = $_.objectId } } #Even though the element name is logicalSwitchId, subinterfaces support VDPortGroup as well as Logical Switch. Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "logicalSwitchId" -xmlElementText $PortGroupID } if ( $PsBoundParameters.ContainsKey("VLAN")) { Add-XmlElement -xmlRoot $xmlVnic -xmlElementName "vlanId" -xmlElementText $VLAN } if ( $PsBoundParameters.ContainsKey("PrimaryAddress")) { #For now, only supporting one addressgroup - will refactor later [System.XML.XMLElement]$xmlAddressGroups = $XMLDoc.CreateElement("addressGroups") $xmlVnic.appendChild($xmlAddressGroups) | Out-Null $AddressGroupParameters = @{ xmlAddressGroups = $xmlAddressGroups } if ( $PsBoundParameters.ContainsKey("PrimaryAddress" )) { $AddressGroupParameters.Add("PrimaryAddress", $PrimaryAddress) } if ( $PsBoundParameters.ContainsKey("SubnetPrefixLength" )) { $AddressGroupParameters.Add("SubnetPrefixLength", $SubnetPrefixLength) } if ( $PsBoundParameters.ContainsKey("SecondaryAddresses" )) { $AddressGroupParameters.Add("SecondaryAddresses", $SecondaryAddresses) } AddNsxEdgeVnicAddressGroup @AddressGroupParameters } $xmlVnic } end {} } function Set-NsxEdgeInterface { <# .SYNOPSIS Conigures an NSX Edge Services Gateway Interface. .DESCRIPTION NSX ESGs can host up to 10 interfaces and up to 200 subinterfaces, each of which can be configured with multiple properties. ESGs support interfaces connected to either VLAN backed port groups or NSX Logical Switches. Use Set-NsxEdgeInterface to change (including overwriting) the configuration of an interface. .EXAMPLE $interface = Get-NsxEdge Edge01 | Get-NsxEdgeInterface -Index 4 $interface | Set-NsxEdgeInterface -Name "vNic4" -Type internal -ConnectedTo $ls4 -PrimaryAddress $ip4 -SubnetPrefixLength 24 Get an interface, then update it. .EXAMPLE $add1 = New-NsxAddressSpec -PrimaryAddress 11.11.11.11 -SubnetPrefixLength 24 -SecondaryAddresses 11.11.11.12, 11.11.11.13 $add2 = New-NsxAddressSpec -PrimaryAddress 22.22.22.22 -SubnetPrefixLength 24 -SecondaryAddresses 22.22.22.23 Get-NsxEdge Edge01 | Get-NsxEdgeInterface -index 5 | Set-NsxEdgeInterface -ConnectedTo $ls4 -AddressSpec $add1,$add2 -name "New Edge with Spec" -type internal Adds two addresses, precreated via New-AddressSpec to ESG Edge01 vnic 5 #> [CmdLetBinding(DefaultParameterSetName = "DirectAddress")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "AddressGroupSpec")] [ValidateScript( { ValidateEdgeInterface $_ })] [System.Xml.XmlElement]$Interface, [Parameter (Mandatory = $true, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $true, ParameterSetName = "AddressGroupSpec")] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $true, ParameterSetName = "AddressGroupSpec")] [ValidateSet ("internal", "uplink", "trunk")] [string]$Type, [Parameter (Mandatory = $true, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $true, ParameterSetName = "AddressGroupSpec")] [ValidateScript( { ValidateLogicalSwitchOrDistributedPortGroup $_ })] [object]$ConnectedTo, [Parameter (Mandatory = $false, ParameterSetName = "DirectAddress")] [ValidateNotNullOrEmpty()] [string]$PrimaryAddress, [Parameter (Mandatory = $false, ParameterSetName = "DirectAddress")] [ValidateNotNullOrEmpty()] [string]$SubnetPrefixLength, [Parameter (Mandatory = $false, ParameterSetName = "DirectAddress")] [string[]]$SecondaryAddresses = @(), [Parameter (Mandatory = $true, ParameterSetName = "AddressGroupSpec")] [ValidateScript( { ValidateAddressGroupSpec $_ })] [System.Xml.XmlElement[]]$AddressSpec, [Parameter (Mandatory = $false, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $false, ParameterSetName = "AddressGroupSpec")] [ValidateRange(1, 9128)] [int]$MTU = 1500, [Parameter (Mandatory = $false, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $false, ParameterSetName = "AddressGroupSpec")] [ValidateNotNullOrEmpty()] [switch]$EnableProxyArp = $false, [Parameter (Mandatory = $false, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $false, ParameterSetName = "AddressGroupSpec")] [ValidateNotNullOrEmpty()] [switch]$EnableSendICMPRedirects = $true, [Parameter (Mandatory = $false, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $false, ParameterSetName = "AddressGroupSpec")] [ValidateNotNullOrEmpty()] [switch]$Connected = $true, [Parameter (Mandatory = $false, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $false, ParameterSetName = "AddressGroupSpec")] [ValidateNotNullOrEmpty()] [switch]$Confirm = $true, [Parameter (Mandatory = $False, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $false, ParameterSetName = "AddressGroupSpec")] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Check if there is already configuration if ( $confirm ) { If ( ($Interface | Get-Member -MemberType properties PortGroupID ) -or ( $Interface.addressGroups ) ) { $message = "Interface $($Interface.Name) appears to already be configured. Config will be overwritten." $question = "Proceed with reconfiguration for $($Interface.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) if ( $decision -eq 1 ) { return } } } #generate the vnic XML $vNicSpecParams = @{ Index = $Interface.index Name = $name Type = $type ConnectedTo = $connectedTo MTU = $MTU EnableProxyArp = $EnableProxyArp EnableSendICMPRedirects = $EnableSendICMPRedirects Connected = $connected } if ( $PsBoundParameters.ContainsKey("PrimaryAddress" )) { $vNicSpecParams.Add("PrimaryAddress", $PrimaryAddress) } if ( $PsBoundParameters.ContainsKey("SubnetPrefixLength" )) { $vNicSpecParams.Add("SubnetPrefixLength", $SubnetPrefixLength) } if ( $PsBoundParameters.ContainsKey("SecondaryAddresses" )) { $vNicSpecParams.Add("SecondaryAddresses", $SecondaryAddresses) } $VnicSpec = New-NsxEdgeInterfaceSpec @vNicSpecParams Write-Debug "$($MyInvocation.MyCommand.Name) : vNic Spec is $($VnicSpec.outerxml | Format-XML) " #Construct the vnics XML Element [System.XML.XMLElement]$xmlVnics = $VnicSpec.OwnerDocument.CreateElement("vnics") $xmlVnics.AppendChild($VnicSpec) | Out-Null #Import any user specified address groups. if ( $PsBoundParameters.ContainsKey('AddressSpec')) { [System.Xml.XmlElement]$AddressGroups = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $VnicSpec -query 'descendant::addressGroups') foreach ( $spec in $AddressSpec ) { $import = $VnicSpec.OwnerDocument.ImportNode(($spec), $true) $AddressGroups.AppendChild($import) | Out-Null } } # #Do the post $body = $xmlVnics.OuterXml $URI = "/api/4.0/edges/$($Interface.edgeId)/vnics/?action=patch" Write-Progress -Activity "Updating Edge Services Gateway interface configuration for interface $($Interface.Index)." $null = Invoke-NsxRestMethod -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Updating Edge Services Gateway interface configuration for interface $($Interface.Index)." -Completed Write-Debug "$($MyInvocation.MyCommand.Name) : Getting updated interface" Get-NsxEdge -objectId $($Interface.edgeId) -Connection $connection | Get-NsxEdgeInterface -index "$($Interface.Index)" -connection $connection } end {} } function Clear-NsxEdgeInterface { <# .SYNOPSIS Clears the configuration on an NSX Edge Services Gateway interface. .DESCRIPTION NSX ESGs can host up to 10 interfaces and up to 200 subinterfaces, each of which can be configured with multiple properties. ESGs support itnerfaces connected to either VLAN backed port groups or NSX Logical Switches. Use Clear-NsxEdgeInterface to set the configuration of an interface back to default (disconnected, not attached to any portgroup, and no defined addressgroup). .EXAMPLE Get an interface and then clear its configuration. PS C:\> $interface = Get-NsxEdge $name | Get-NsxEdgeInterface "vNic4" PS C:\> $interface | Clear-NsxEdgeInterface -confirm:$false #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeInterface $_ })] [System.Xml.XmlElement]$Interface, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Interface $($Interface.Name) config will be cleared." $question = "Proceed with interface reconfiguration for interface $($interface.index)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) if ( $decision -eq 1 ) { return } } # #Do the delete $URI = "/api/4.0/edges/$($interface.edgeId)/vnics/$($interface.Index)" Write-Progress -Activity "Clearing Edge Services Gateway interface configuration for interface $($interface.Index)." $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Clearing Edge Services Gateway interface configuration for interface $($interface.Index)." -Completed } end {} } function Get-NsxEdgeInterface { <# .SYNOPSIS Retrieves the specified interface configuration on a specified Edge Services Gateway. .DESCRIPTION NSX ESGs can host up to 10 interfaces and up to 200 subinterfaces, each of which can be configured with multiple properties. ESGs support interfaces connected to either VLAN backed port groups or NSX Logical Switches. Use Get-NsxEdgeInterface to retrieve the configuration of an interface. .EXAMPLE Get all interface configuration for ESG named Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeInterface .EXAMPLE Get interface configuration for interface named vNic4 on ESG named Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeInterface vNic4 .EXAMPLE Get interface configuration for interface number 4 on ESG named Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeInterface -index 4 #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $False, ParameterSetName = "Name", Position = 1)] [string]$Name, [Parameter (Mandatory = $True, ParameterSetName = "Index")] [ValidateRange(0, 9)] [int]$Index, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( -not ($PsBoundParameters.ContainsKey("Index") )) { #All interfaces on Edge $URI = "/api/4.0/edges/$($Edge.Id)/vnics/" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $PsBoundParameters.ContainsKey("name") ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Getting vNic by Name" $return = $response.vnics.vnic | Where-Object { $_.name -eq $name } if ( $return ) { Add-XmlElement -xmlDoc ([system.xml.xmldocument]$return.OwnerDocument) -xmlRoot $return -xmlElementName "edgeId" -xmlElementText $($Edge.Id) } } else { Write-Debug "$($MyInvocation.MyCommand.Name) : Getting all vNics" $return = $response.vnics.vnic foreach ( $vnic in $return ) { Add-XmlElement -xmlDoc ([system.xml.xmldocument]$vnic.OwnerDocument) -xmlRoot $vnic -xmlElementName "edgeId" -xmlElementText $($Edge.Id) } } } else { Write-Debug "$($MyInvocation.MyCommand.Name) : Getting vNic by Index" #Just getting a single vNic by index $URI = "/api/4.0/edges/$($Edge.Id)/vnics/$Index" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $return = $response.vnic Add-XmlElement -xmlDoc ([system.xml.xmldocument]$return.OwnerDocument) -xmlRoot $return -xmlElementName "edgeId" -xmlElementText $($Edge.Id) } $return } end {} } function New-NsxEdgeSubInterface { <# .SYNOPSIS Adds a new subinterface to an existing NSX Edge Services Gateway trunk mode interface. .DESCRIPTION NSX ESGs can host up to 10 interfaces and up to 200 subinterfaces, each of which can be configured with multiple properties. ESGs support interfaces connected to either VLAN backed port groups or NSX Logical Switches. Use New-NsxEdgeSubInterface to add a new subinterface. .EXAMPLE Get an NSX Edge interface and configure a new subinterface in VLAN mode. PS C:\> $trunk = Get-NsxEdge $name | Get-NsxEdgeInterface "vNic3" PS C:\> $trunk | New-NsxEdgeSubinterface -Name "sub1" -PrimaryAddress $ip5 -SubnetPrefixLength 24 -TunnelId 1 -Vlan 123 .EXAMPLE Get an NSX Edge interface and configure a new subinterface in Network mode. PS C:\> $trunk = Get-NsxEdge $name | Get-NsxEdgeInterface "vNic3" PS C:\> $trunk | New-NsxEdgeSubinterface -Name "sub1" -PrimaryAddress $ip5 -SubnetPrefixLength 24 -TunnelId 1 -Network $LS2 #> [CmdLetBinding(DefaultParameterSetName = "None")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeInterface $_ })] [System.Xml.XmlElement]$Interface, [Parameter (Mandatory = $true)] [ValidateRange(1, 4094)] [int]$TunnelId, [Parameter (Mandatory = $false, ParameterSetName = "Network")] [ValidateScript( { ValidateLogicalSwitchOrDistributedPortGroup $_ })] [object]$Network, [Parameter (Mandatory = $false, ParameterSetName = "VLAN")] [ValidateRange(0, 4094)] [int]$VLAN, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$PrimaryAddress, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$SubnetPrefixLength, [Parameter (Mandatory = $false)] [string[]]$SecondaryAddresses = @(), [Parameter (Mandatory = $false)] [ValidateRange(1, 9128)] [int]$MTU, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [switch]$EnableSendICMPRedirects, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [switch]$Connected = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) #Validate interfaceindex is trunk if ( -not $Interface.type -eq 'trunk' ) { throw "Specified interface $($interface.Name) is of type $($interface.type) but must be of type trunk to host a subinterface. " } #Create private xml element $_Interface = $Interface.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_Interface.edgeId $NodetoRemove = $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Interface -query 'descendant::edgeId')) Write-Debug "Node to remove parent: $($nodetoremove.ParentNode | Format-XML)" $_Interface.RemoveChild( $NodeToRemove ) | Out-Null #Get or create the subinterfaces node. [System.XML.XMLDocument]$xmlDoc = $_Interface.OwnerDocument if ( $_Interface | Get-Member -MemberType Properties -Name subInterfaces) { [System.XML.XMLElement]$xmlSubInterfaces = $_Interface.subInterfaces } else { [System.XML.XMLElement]$xmlSubInterfaces = $xmlDoc.CreateElement("subInterfaces") $_Interface.AppendChild($xmlSubInterfaces) | Out-Null } #generate the vnic XML $vNicSpecParams = @{ TunnelId = $TunnelId Connected = $connected Name = $Name } switch ($psCmdlet.ParameterSetName) { "Network" { if ( $PsBoundParameters.ContainsKey("Network" )) { $vNicSpecParams.Add("Network", $Network) } } "VLAN" { if ( $PsBoundParameters.ContainsKey("VLAN" )) { $vNicSpecParams.Add("VLAN", $VLAN) } } "None" {} Default { throw "An invalid parameterset was found. This should never happen." } } if ( $PsBoundParameters.ContainsKey("PrimaryAddress" )) { $vNicSpecParams.Add("PrimaryAddress", $PrimaryAddress) } if ( $PsBoundParameters.ContainsKey("SubnetPrefixLength" )) { $vNicSpecParams.Add("SubnetPrefixLength", $SubnetPrefixLength) } if ( $PsBoundParameters.ContainsKey("SecondaryAddresses" )) { $vNicSpecParams.Add("SecondaryAddresses", $SecondaryAddresses) } if ( $PsBoundParameters.ContainsKey("MTU" )) { $vNicSpecParams.Add("MTU", $MTU) } if ( $PsBoundParameters.ContainsKey("EnableSendICMPRedirects" )) { $vNicSpecParams.Add("EnableSendICMPRedirects", $EnableSendICMPRedirects) } $VnicSpec = New-NsxEdgeSubInterfaceSpec @vNicSpecParams Write-Debug "$($MyInvocation.MyCommand.Name) : vNic Spec is $($VnicSpec.outerxml | Format-XML) " $import = $xmlDoc.ImportNode(($VnicSpec), $true) $xmlSubInterfaces.AppendChild($import) | Out-Null # #Do the post $body = $_Interface.OuterXml $URI = "/api/4.0/edges/$($EdgeId)/vnics/$($_Interface.Index)" Write-Progress -Activity "Updating Edge Services Gateway interface configuration for $($_Interface.Name)." Invoke-NsxRestMethod -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Updating Edge Services Gateway interface configuration for $($_Interface.Name)." -Completed } function Remove-NsxEdgeSubInterface { <# .SYNOPSIS Removes the specificed subinterface from an NSX Edge Services Gateway interface. .DESCRIPTION NSX ESGs can host up to 10 interfaces and up to 200 subinterfaces, each of which can be configured with multiple properties. ESGs support interfaces connected to either VLAN backed port groups or NSX Logical Switches. Use Remove-NsxEdgeSubInterface to remove a subinterface configuration from and ESG trunk interface. .EXAMPLE Get a subinterface and then remove it. PS C:\> $interface = Get-NsxEdge $name | Get-NsxEdgeInterface "vNic3" PS C:\> $interface | Get-NsxEdgeSubInterface "sub1" | Remove-NsxEdgeSubinterface #> [CmdLetBinding(DefaultParameterSetName = "None")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSubInterface $_ })] [System.Xml.XmlElement]$Subinterface, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) Begin {} Process { if ( $confirm ) { $message = "Interface $($Subinterface.Name) will be removed." $question = "Proceed with interface reconfiguration for interface $($Subinterface.index)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) if ( $decision -eq 1 ) { return } } #Get the vnic xml $ParentVnic = $(Get-NsxEdge -Connection $connection -objectId $SubInterface.edgeId).vnics.vnic | Where-Object { $_.index -eq $subInterface.vnicId } #Remove the node using xpath query. $NodeToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ParentVnic.subInterfaces -query "descendant::subInterface[index=$($subInterface.Index)]") Write-Debug "$($MyInvocation.MyCommand.Name) : XPath query for node to delete returned $($NodetoRemove.OuterXml | Format-XML)" $ParentVnic.Subinterfaces.RemoveChild($NodeToRemove) | Out-Null #Put the modified VNIC xml $body = $ParentVnic.OuterXml $URI = "/api/4.0/edges/$($SubInterface.edgeId)/vnics/$($ParentVnic.Index)" Write-Progress -Activity "Updating Edge Services Gateway interface configuration for interface $($ParentVnic.Name)." Invoke-NsxRestMethod -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Updating Edge Services Gateway interface configuration for interface $($ParentVnic.Name)." -Completed } End {} } function Get-NsxEdgeSubInterface { <# .SYNOPSIS Retrieves the subinterface configuration for the specified interface .DESCRIPTION NSX ESGs can host up to 10 interfaces and up to 200 subinterfaces, each of which can be configured with multiple properties. ESGs support interfaces connected to either VLAN backed port groups or NSX Logical Switches. Use Get-NsxEdgeSubInterface to retrieve the subinterface configuration of an interface. .EXAMPLE Get an NSX Subinterface called sub1 from any interface on ESG Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeInterface | Get-NsxEdgeSubInterface "sub1" #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeInterface $_ })] [System.Xml.XmlElement]$Interface, [Parameter (Mandatory = $False, ParameterSetName = "Name", Position = 1)] [string]$Name, [Parameter (Mandatory = $True, ParameterSetName = "Index")] [ValidateRange(10, 200)] [int]$Index ) begin {} process { #Not throwing error if no subinterfaces defined. If ( $interface | Get-Member -Name subInterfaces -MemberType Properties ) { if ($PsBoundParameters.ContainsKey("Index")) { $subint = $Interface.subInterfaces.subinterface | Where-Object { $_.index -eq $Index } if ( $subint ) { $_subint = $subint.CloneNode($true) Add-XmlElement -xmlDoc ([system.xml.xmldocument]$Interface.OwnerDocument) -xmlRoot $_subint -xmlElementName "edgeId" -xmlElementText $($Interface.edgeId) Add-XmlElement -xmlDoc ([system.xml.xmldocument]$Interface.OwnerDocument) -xmlRoot $_subint -xmlElementName "vnicId" -xmlElementText $($Interface.index) $_subint } } elseif ( $PsBoundParameters.ContainsKey("name")) { $subint = $Interface.subInterfaces.subinterface | Where-Object { $_.name -eq $name } if ($subint) { $_subint = $subint.CloneNode($true) Add-XmlElement -xmlDoc ([system.xml.xmldocument]$Interface.OwnerDocument) -xmlRoot $_subint -xmlElementName "edgeId" -xmlElementText $($Interface.edgeId) Add-XmlElement -xmlDoc ([system.xml.xmldocument]$Interface.OwnerDocument) -xmlRoot $_subint -xmlElementName "vnicId" -xmlElementText $($Interface.index) $_subint } } else { #All Subinterfaces on interface foreach ( $subint in $Interface.subInterfaces.subInterface ) { $_subint = $subint.CloneNode($true) Add-XmlElement -xmlDoc ([system.xml.xmldocument]$Interface.OwnerDocument) -xmlRoot $_subint -xmlElementName "edgeId" -xmlElementText $($Interface.edgeId) Add-XmlElement -xmlDoc ([system.xml.xmldocument]$Interface.OwnerDocument) -xmlRoot $_subint -xmlElementName "vnicId" -xmlElementText $($Interface.index) $_subint } } } } end {} } function Get-NsxEdgeInterfaceAddress { <# .SYNOPSIS Retrieves the address configuration for the specified interface .DESCRIPTION NSX ESGs interfaces can be configured with multiple 'Address Groups'. This allows a single interface to have IP addresses defined in different subnets, each complete with their own Primary Address, Netmask and zero or more Secondary Addresses. The Get-NsxEdgeInterfaceAddress cmdlet retrieves the addresses for the specific interface. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeInterface -Index 9 | Get-NsxEdgeInterfaceAddress Retrieves all the address groups defined on vNic 9 of the ESG Edge01. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeInterface -Index 9 | Get-NsxEdgeInterfaceAddress -PrimaryAddress 1.2.3.4 Retrieves the address config with primary address 1.2.3.4 defined on vNic 9 of the ESG Edge01. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeInterface $_ })] [System.Xml.XmlElement]$Interface, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$PrimaryAddress ) begin { } process { $_Interface = ($Interface.CloneNode($True)) [System.Xml.XmlElement]$AddressGroups = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Interface -query 'descendant::addressGroups') #Need to use an xpath query here, as dot notation will throw in strict mode if there is no childnode. If ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $AddressGroups -query 'descendant::addressGroup')) { $GroupCollection = $AddressGroups.addressGroup if ( $PsBoundParameters.ContainsKey('PrimaryAddress')) { $GroupCollection = $GroupCollection | Where-Object { $_.primaryAddress -eq $PrimaryAddress } } foreach ( $AddressGroup in $GroupCollection ) { Add-XmlElement -xmlRoot $AddressGroup -xmlElementName "edgeId" -xmlElementText $Interface.EdgeId Add-XmlElement -xmlRoot $AddressGroup -xmlElementName "interfaceIndex" -xmlElementText $Interface.Index } $GroupCollection } } end {} } function Add-NsxEdgeInterfaceAddress { <# .SYNOPSIS Adds a new address to the specified ESG interface .DESCRIPTION NSX ESGs interfaces can be configured with multiple 'Address Groups'. This allows a single interface to have IP addresses defined in different subnets, each complete with their own Primary Address, Netmask and zero or more Secondary Addresses. The Add-NsxEdgeInterfaceAddress cmdlet adds a new address to an existing ESG interface. .EXAMPLE get-nsxedge Edge01 | Get-NsxEdgeInterface -Index 9 | Add-NsxEdgeInterfaceAddress -PrimaryAddress 44.44.44.44 -SubnetPrefixLength 24 -SecondaryAddresses 44.44.44.45,44.44.44.46 Adds a new primary address and multiple secondary addresses to vNic 9 on edge Edge01 .EXAMPLE $add2 = New-NsxAddressSpec -PrimaryAddress 22.22.22.22 -SubnetPrefixLength 24 -SecondaryAddresses 22.22.22.23 $add3 = New-NsxAddressSpec -PrimaryAddress 33.33.33.33 -SubnetPrefixLength 24 -SecondaryAddresses 33.33.33.34 get-nsxedge Edge01 | Get-NsxEdgeInterface -Index 9 | Add-NsxEdgeInterfaceAddress -AddressSpec $add2,$add3 Adds two new addresses to Edge01's vnic9 using address specs. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "AddressGroupSpec")] [ValidateScript( { ValidateEdgeInterface $_ })] [System.Xml.XmlElement]$Interface, [Parameter (Mandatory = $true, ParameterSetName = "DirectAddress")] [ValidateNotNullOrEmpty()] [string]$PrimaryAddress, [Parameter (Mandatory = $true, ParameterSetName = "DirectAddress")] [ValidateNotNullOrEmpty()] [string]$SubnetPrefixLength, [Parameter (Mandatory = $false, ParameterSetName = "DirectAddress")] [string[]]$SecondaryAddresses = @(), [Parameter (Mandatory = $true, ParameterSetName = "AddressGroupSpec")] [ValidateScript( { ValidateAddressGroupSpec $_ })] [System.Xml.XmlElement[]]$AddressSpec, [Parameter (Mandatory = $False, ParameterSetName = "DirectAddress")] [Parameter (Mandatory = $false, ParameterSetName = "AddressGroupSpec")] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { [System.Xml.XmlElement]$_Interface = $Interface.CloneNode($True) #Store the edgeId and remove it from the XML as we need to put it... $edgeId = $_Interface.edgeId $NodetoRemove = $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Interface -query 'descendant::edgeId')) $_Interface.RemoveChild( $NodeToRemove ) | Out-Null [System.Xml.XmlElement]$AddressGroups = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Interface -query 'descendant::addressGroups') if ( $PSCmdlet.ParameterSetName -eq "DirectAddress") { if ( $PsBoundParameters.ContainsKey('SecondaryAddresses')) { AddNsxEdgeVnicAddressGroup -xmlAddressGroups $AddressGroups -PrimaryAddress $PrimaryAddress -SubnetPrefixLength $SubnetPrefixLength -SecondaryAddresses $SecondaryAddresses } else { AddNsxEdgeVnicAddressGroup -xmlAddressGroups $AddressGroups -PrimaryAddress $PrimaryAddress -SubnetPrefixLength $SubnetPrefixLength -SecondaryAddresses $SecondaryAddresses } } else { #Import any user specified address groups. foreach ( $spec in $AddressSpec ) { $import = $_Interface.OwnerDocument.ImportNode(($spec), $true) $AddressGroups.AppendChild($import) | Out-Null } } #Do the post $body = $_Interface.OuterXml $URI = "/api/4.0/edges/$($edgeId)/vnics/$($_Interface.Index)" Write-Progress -Activity "Updating Edge Services Gateway interface configuration for interface $($_Interface.Index)." $null = Invoke-NsxRestMethod -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Updating Edge Services Gateway interface configuration for interface $($_Interface.Index)." -Completed Write-Debug "$($MyInvocation.MyCommand.Name) : Getting updated interface" Get-NsxEdge -objectId $($edgeId) -Connection $connection | Get-NsxEdgeInterface -index "$($_Interface.Index)" -connection $connection } end {} } function Remove-NsxEdgeInterfaceAddress { <# .SYNOPSIS Removes the specified address configuration for the specified interface .DESCRIPTION NSX ESGs interfaces can be configured with multiple 'Address Groups'. This allows a single interface to have IP addresses defined in different subnets, each complete with their own Primary Address, Netmask and zero or more Secondary Addresses. The Remove-NsxEdgeInterfaceAddress cmdlet removes the address specified from the specified interface. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeInterface -Index 9 | Get-NsxEdgeInterfaceAddress -PrimaryAddress 1.2.3.4 | Remove-NsxEdgeInterfaceAddress Removes the address group with primary address 1.2.3.4 defined on vNic 9 of the ESG Edge01. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeInterfaceAddress $_ })] [System.Xml.XmlElement]$InterfaceAddress, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our Edge $edgeId = $InterfaceAddress.edgeId $InterfaceIndex = $InterfaceAddress.interfaceIndex $Edge = Get-NsxEdge -objectId $edgeId -Connection $connection $Interface = $Edge | Get-NsxEdgeInterface -index $InterfaceIndex -connection $connection if ( -not $Interface ) { Throw "Interface index $InterfaceIndex was not found on edge $edgeId." } #Remove the edgeId and interfaceIndex elements from the XML as we need to post it... $Interface.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $Interface -query 'descendant::edgeId')) ) | Out-Null #Need to do an xpath query here to query for an address that matches the one passed in. $xpathQuery = "//addressGroups/addressGroup[primaryAddress=`"$($InterfaceAddress.primaryAddress)`"]" Write-Debug "$($MyInvocation.MyCommand.Name) : XPath query for addressgroup nodes to remove is: $xpathQuery" $addressGroupToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $Interface -query $xpathQuery) if ( $addressGroupToRemove ) { Write-Debug "$($MyInvocation.MyCommand.Name) : addressGroupToRemove Element is: `n $($addressGroupToRemove.OuterXml | Format-XML) " $Interface.AddressGroups.RemoveChild($addressGroupToRemove) | Out-Null $URI = "/api/4.0/edges/$($EdgeId)/vnics/$InterfaceIndex" $body = $Interface.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed } } else { Throw "Address $($InterfaceAddress.PrimaryAddress) was not found in the configuration for interface $InterfaceIndex on Edge $edgeId" } } end {} } function Get-NsxEdge { <# .SYNOPSIS Retrieves an NSX Edge Service Gateway Object. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs support interfaces connected to either VLAN backed port groups or NSX Logical Switches. .EXAMPLE PS C:\> Get-NsxEdge #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ParameterSetName = "objectId")] [string]$objectId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [string]$Name, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $pagesize = 10 switch ( $psCmdlet.ParameterSetName ) { "Name" { $URI = "/api/4.0/edges?pageSize=$pagesize&startIndex=00" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection #Edge summary XML is returned as paged data, means we have to handle it. #Then we have to query for full information on a per edge basis. $edgesummaries = @() $edges = @() $itemIndex = 0 $startingIndex = 0 $pagingInfo = $response.pagedEdgeList.edgePage.pagingInfo if ( [int]$paginginfo.totalCount -ne 0 ) { Write-Debug "$($MyInvocation.MyCommand.Name) : ESG count non zero" do { Write-Debug "$($MyInvocation.MyCommand.Name) : In paging loop. PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" while (($itemindex -lt ([int]$paginginfo.pagesize + $startingIndex)) -and ($itemIndex -lt [int]$paginginfo.totalCount )) { Write-Debug "$($MyInvocation.MyCommand.Name) : In Item Processing Loop: ItemIndex: $itemIndex" Write-Debug "$($MyInvocation.MyCommand.Name) : $(@($response.pagedEdgeList.edgePage.edgeSummary)[($itemIndex - $startingIndex)].objectId)" #Need to wrap the edgesummary prop of the datapage in case we get exactly 1 item - #which powershell annoyingly unwraps to a single xml element rather than an array... $edgesummaries += @($response.pagedEdgeList.edgePage.edgeSummary)[($itemIndex - $startingIndex)] $itemIndex += 1 } Write-Debug "$($MyInvocation.MyCommand.Name) : Out of item processing - PagingInfo: PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" if ( [int]$paginginfo.totalcount -gt $itemIndex) { Write-Debug "$($MyInvocation.MyCommand.Name) : PagingInfo: PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" $startingIndex += $pagesize $URI = "/api/4.0/edges?pageSize=$pagesize&startIndex=$startingIndex" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $pagingInfo = $response.pagedEdgeList.edgePage.pagingInfo } } until ( [int]$paginginfo.totalcount -le $itemIndex ) Write-Debug "$($MyInvocation.MyCommand.Name) : Completed page processing: ItemIndex: $itemIndex" } #What we got here is...failure to communicate! In order to get full detail, we have to requery for each edgeid. #But... there is information in the SUmmary that isnt in the full detail. So Ive decided to add the summary as a node #to the returned edge detail. foreach ($edgesummary in $edgesummaries) { $URI = "/api/4.0/edges/$($edgesummary.objectID)" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $import = $response.edge.ownerDocument.ImportNode($edgesummary, $true) $response.edge.appendChild($import) | Out-Null $edges += $response.edge } if ( $name ) { $edges | Where-Object { $_.Type -eq 'gatewayServices' } | Where-Object { $_.name -eq $name } } else { $edges | Where-Object { $_.Type -eq 'gatewayServices' } } } "objectId" { $URI = "/api/4.0/edges/$objectId" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $edge = $response.edge $URI = "/api/4.0/edges/$objectId/summary" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $import = $edge.ownerDocument.ImportNode($($response.edgeSummary), $true) $edge.AppendChild($import) | Out-Null $edge } } } function New-NsxEdge { <# .SYNOPSIS Creates a new NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs support interfaces connected to either VLAN backed port groups or NSX Logical Switches. PowerCLI cmdlets such as Get-VDPortGroup and Get-Datastore require a valid PowerCLI session. .EXAMPLE Create interface specifications first for each interface that you want on the ESG PS C:\> $vnic0 = New-NsxEdgeInterfaceSpec -Index 0 -Name Uplink -Type Uplink -ConnectedTo (Get-VDPortgroup Corp) -PrimaryAddress "1.1.1.2" -SubnetPrefixLength 24 PS C:\> $vnic1 = New-NsxEdgeInterfaceSpec -Index 1 -Name Internal -Type Uplink -ConnectedTo $LogicalSwitch1 -PrimaryAddress "2.2.2.1" -SecondaryAddresses "2.2.2.2" -SubnetPrefixLength 24 Then create the Edge Services Gateway PS C:\> New-NsxEdge -name DMZ_Edge_2 -Cluster (get-cluster Cluster1) -Datastore (get-datastore Datastore1) -Interface $vnic0,$vnic1 -Password 'Pass' #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", "", Scope = "Function", Target = "*")] # Unable to remove without breaking backward compatibilty. param ( [Parameter (Mandatory = $true)] #Name of the edge appliance. [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true, ParameterSetName = "ResourcePool")] #Resource pool into which to deploy the Edge. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ResourcePoolInterop]$ResourcePool, [Parameter (Mandatory = $true, ParameterSetName = "Cluster")] #DRS Cluster into which to deploy the Edge. [ValidateScript( { if ( $_ -eq $null ) { throw "Must specify Cluster." } if ( -not $_.DrsEnabled ) { throw "Cluster is not DRS enabled." } $true })] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop]$Cluster, [Parameter (Mandatory = $true)] #Datastore onto which to deploy the edge appliance (If HA is enabled, use -HADatastore to specify an alternate location if desired.) [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.DatastoreManagement.DatastoreInterop]$Datastore, [Parameter (Mandatory = $false)] #Cli account username. [ValidateNotNullOrEmpty()] [String]$Username = "admin", [Parameter (Mandatory = $false)] #CLI account password [ValidateNotNullOrEmpty()] [String]$Password, [Parameter (Mandatory = $false)] #Datastore onto which to deploy the HA edge appliance (Best practice is to use an alternative datastore/array to the first edge appliance in a HA pair. Defaults to the same datastore as the first appliance.) [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.DatastoreManagement.DatastoreInterop]$HADatastore = $datastore, [Parameter (Mandatory = $false)] #Formfactor for the deploye dedge appliance. [ValidateSet ("compact", "large", "xlarge", "quadlarge")] [string]$FormFactor = "compact", [Parameter (Mandatory = $false)] #VI folder into which to place the edge in the VMs and Templates inventory. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.FolderInterop]$VMFolder, [Parameter (Mandatory = $false)] #Optional tenant string. [ValidateNotNullOrEmpty()] [String]$Tenant, [Parameter (Mandatory = $false)] #DNS hostname to configure on the edge appliance. Defaults to the edge name. [ValidateNotNullOrEmpty()] [String]$Hostname = $Name, [Parameter (Mandatory = $false)] #Enable SSH [ValidateNotNullOrEmpty()] [switch]$EnableSSH = $false, [Parameter (Mandatory = $false)] #Enable FIPs mode [ValidateNotNullOrEmpty()] [switch]$EnableFIPS = $false, [Parameter (Mandatory = $false)] #Enable autogeneration of edge firewall rules for enabled services. Defaults to $true [ValidateNotNullOrEmpty()] [switch]$AutoGenerateRules = $true, [Parameter (Mandatory = $false)] #Enable edge firewall. Defaults to $true. [switch]$FwEnabled = $true, [Parameter (Mandatory = $false)] #Set default firewall rule to allow. Defaults to $false. [switch]$FwDefaultPolicyAllow = $false, [Parameter (Mandatory = $false)] #Enable Firewall Logging. Defaults to $true. [switch]$FwLoggingEnabled = $true, [Parameter (Mandatory = $false)] #Enable HA on the deployed Edge. Defaults to $false. [ValidateNotNullOrEmpty()] [switch]$EnableHa = $false, [Parameter (Mandatory = $false)] #Configure the Edge Appliance Dead Time. [ValidateRange(3, 900)] [int]$HaDeadTime, [Parameter (Mandatory = $false)] #Configure the vNIC index used to send HA heartbeats. [ValidateRange(0, 9)] [int]$HaVnic, [Parameter (Mandatory = $false)] #Enable syslog. Defaults to $false. [switch]$EnableSyslog = $false, [Parameter (Mandatory = $false)] #Configure the syslog server. [ValidateNotNullOrEmpty()] [string[]]$SyslogServer, [Parameter (Mandatory = $false)] #Configure the syslog protocol. [ValidateSet("udp", "tcp", IgnoreCase = $true)] [string]$SyslogProtocol, [Parameter (Mandatory = $true)] #Define the Edge Interface configuration. Specify a collection of one or more interface specs as created by New-NsxEdgeInterfaceSpec. [ValidateScript( { ValidateEdgeInterfaceSpec $_ })] [System.Xml.XmlElement[]]$Interface, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("edge") $xmlDoc.appendChild($xmlRoot) | Out-Null Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "fqdn" -xmlElementText $Hostname #Enable FIPs mode if ( $EnableFIPS ) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "enableFips" -xmlElementText "true" } Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "type" -xmlElementText "gatewayServices" if ($PSBoundParameters.ContainsKey("Tenant")) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "tenant" -xmlElementText $Tenant } #Appliances element [System.XML.XMLElement]$xmlAppliances = $XMLDoc.CreateElement("appliances") $xmlRoot.appendChild($xmlAppliances) | Out-Null switch ($psCmdlet.ParameterSetName) { "Cluster" { $ResPoolId = $($cluster | Get-ResourcePool | Where-Object { $_.parent.id -eq $cluster.id }).extensiondata.moref.value } "ResourcePool" { $ResPoolId = $ResourcePool.extensiondata.moref.value } } Add-XmlElement -xmlRoot $xmlAppliances -xmlElementName "applianceSize" -xmlElementText $FormFactor [System.XML.XMLElement]$xmlAppliance = $XMLDoc.CreateElement("appliance") $xmlAppliances.appendChild($xmlAppliance) | Out-Null Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "resourcePoolId" -xmlElementText $ResPoolId Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "datastoreId" -xmlElementText $datastore.extensiondata.moref.value if ( $VMFolder ) { Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "vmFolderId" -xmlElementText $VMFolder.extensiondata.moref.value } #Create the features element. [System.XML.XMLElement]$xmlFeatures = $XMLDoc.CreateElement("features") $xmlRoot.appendChild($xmlFeatures) | Out-Null if ( $EnableHA ) { #Define the HA appliance [System.XML.XMLElement]$xmlAppliance = $XMLDoc.CreateElement("appliance") $xmlAppliances.appendChild($xmlAppliance) | Out-Null Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "resourcePoolId" -xmlElementText $ResPoolId Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "datastoreId" -xmlElementText $HAdatastore.extensiondata.moref.value if ( $VMFolder ) { Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "vmFolderId" -xmlElementText $VMFolder.extensiondata.moref.value } #configure HA [System.XML.XMLElement]$xmlHA = $XMLDoc.CreateElement("highAvailability") $xmlFeatures.appendChild($xmlHA) | Out-Null Add-XmlElement -xmlRoot $xmlHA -xmlElementName "enabled" -xmlElementText "true" if ( $PsBoundParameters.containsKey('HaDeadTime')) { Add-XmlElement -xmlRoot $xmlHA -xmlElementName "declareDeadTime" -xmlElementText $HaDeadTime.ToString() } if ( $PsBoundParameters.containsKey('HaVnic')) { Add-XmlElement -xmlRoot $xmlHA -xmlElementName "vnic" -xmlElementText $HaVnic.ToString() } } #Create the syslog element [System.XML.XMLElement]$xmlSyslog = $XMLDoc.CreateElement("syslog") $xmlFeatures.appendChild($xmlSyslog) | Out-Null Add-XmlElement -xmlRoot $xmlSyslog -xmlElementName "enabled" -xmlElementText $EnableSyslog.ToString().ToLower() if ( $PsBoundParameters.containsKey('SyslogProtocol')) { Add-XmlElement -xmlRoot $xmlSyslog -xmlElementName "protocol" -xmlElementText $SyslogProtocol.ToString() } if ( $PsBoundParameters.containsKey('SyslogServer')) { [System.XML.XMLElement]$xmlServerAddresses = $XMLDoc.CreateElement("serverAddresses") $xmlSyslog.appendChild($xmlServerAddresses) | Out-Null foreach ( $server in $SyslogServer ) { Add-XmlElement -xmlRoot $xmlServerAddresses -xmlElementName "ipAddress" -xmlElementText $server.ToString() } } #Create the fw element [System.XML.XMLElement]$xmlFirewall = $XMLDoc.CreateElement("firewall") $xmlFeatures.appendChild($xmlFirewall) | Out-Null Add-XmlElement -xmlRoot $xmlFirewall -xmlElementName "enabled" -xmlElementText $FwEnabled.ToString().ToLower() [System.XML.XMLElement]$xmlDefaultPolicy = $XMLDoc.CreateElement("defaultPolicy") $xmlFirewall.appendChild($xmlDefaultPolicy) | Out-Null Add-XmlElement -xmlRoot $xmlDefaultPolicy -xmlElementName "loggingEnabled" -xmlElementText $FwLoggingEnabled.ToString().ToLower() if ( $FwDefaultPolicyAllow ) { Add-XmlElement -xmlRoot $xmlDefaultPolicy -xmlElementName "action" -xmlElementText "accept" } else { Add-XmlElement -xmlRoot $xmlDefaultPolicy -xmlElementName "action" -xmlElementText "deny" } #Rule Autoconfiguration if ( $AutoGenerateRules ) { [System.XML.XMLElement]$xmlAutoConfig = $XMLDoc.CreateElement("autoConfiguration") $xmlRoot.appendChild($xmlAutoConfig) | Out-Null Add-XmlElement -xmlRoot $xmlAutoConfig -xmlElementName "enabled" -xmlElementText $AutoGenerateRules.ToString().ToLower() } #CLI Settings if ( $PsBoundParameters.ContainsKey('EnableSSH') -or $PSBoundParameters.ContainsKey('Password') ) { [System.XML.XMLElement]$xmlCliSettings = $XMLDoc.CreateElement("cliSettings") $xmlRoot.appendChild($xmlCliSettings) | Out-Null if ( $PsBoundParameters.ContainsKey('Password') ) { Add-XmlElement -xmlRoot $xmlCliSettings -xmlElementName "userName" -xmlElementText $UserName Add-XmlElement -xmlRoot $xmlCliSettings -xmlElementName "password" -xmlElementText $Password } if ( $PsBoundParameters.ContainsKey('EnableSSH') ) { Add-XmlElement -xmlRoot $xmlCliSettings -xmlElementName "remoteAccess" -xmlElementText $EnableSsh.ToString().ToLower() } } #DNS Settings if ( $PsBoundParameters.ContainsKey('PrimaryDnsServer') -or $PSBoundParameters.ContainsKey('SecondaryDNSServer') -or $PSBoundParameters.ContainsKey('DNSDomainName') ) { [System.XML.XMLElement]$xmlDnsClient = $XMLDoc.CreateElement("dnsClient") $xmlRoot.appendChild($xmlDnsClient) | Out-Null if ( $PsBoundParameters.ContainsKey('PrimaryDnsServer') ) { Add-XmlElement -xmlRoot $xmlDnsClient -xmlElementName "primaryDns" -xmlElementText $PrimaryDnsServer } if ( $PsBoundParameters.ContainsKey('SecondaryDnsServer') ) { Add-XmlElement -xmlRoot $xmlDnsClient -xmlElementName "secondaryDns" -xmlElementText $SecondaryDNSServer } if ( $PsBoundParameters.ContainsKey('DNSDomainName') ) { Add-XmlElement -xmlRoot $xmlDnsClient -xmlElementName "domainName" -xmlElementText $DNSDomainName } } #Nics [System.XML.XMLElement]$xmlVnics = $XMLDoc.CreateElement("vnics") $xmlRoot.appendChild($xmlVnics) | Out-Null foreach ( $VnicSpec in $Interface ) { $import = $xmlDoc.ImportNode(($VnicSpec), $true) $xmlVnics.AppendChild($import) | Out-Null } # #Do the post $body = $xmlroot.OuterXml $URI = "/api/4.0/edges" Write-Progress -Activity "Creating Edge Services Gateway $Name" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Creating Edge Services Gateway $Name" -Completed $edgeId = $response.Headers.Location.split("/")[$response.Headers.Location.split("/").GetUpperBound(0)] Get-NsxEdge -objectId $edgeId -Connection $connection } end {} } function Repair-NsxEdge { <# .SYNOPSIS Resyncs or Redploys the specified NSX Edge Services Gateway appliance. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. The Repair-NsxEdge cmdlet allows a Resync, Redploy or Upgrade operation to be performed on the specified Edge appliance. WARNING: Repair operations can cause connectivity loss. Use with caution. .EXAMPLE Get-NsxEdge Edge01 | Repair-NsxEdge -Operation Redeploy Redeploys the ESG Edge01. .EXAMPLE Get-NsxEdge Edge01 | Repair-NsxEdge -Operation ReSync -Confirm:$false Resyncs the ESG Edge01 without confirmation. .EXAMPLE Get-NsxEdge Edge01 | Repair-NsxEdge -Operation Upgrade -Confirm:$false Upgrade the ESG Edge01 to last release without confirmation. #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] #The Edge object to be repaired. Accepted on pipline [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $True)] #WARNING: This operation can potentially cause a datapath outage depending on the deployment architecture. #Specify the repair operation to be performed on the Edge. #If ForceSync - The edge appliance is rebooted #If Redeploy - The Edge is removed and redeployed (if the edge is HA this causes failover, otherwise, an outage.) #If Upgrade - The Edge is upgraded to latest release [ValidateSet("ForceSync", "Redeploy", "Upgrade")] [string]$Operation, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { $URI = "/api/4.0/edges/$($Edge.Id)?action=$($Operation.ToLower())" if ( $confirm ) { $message = "WARNING: An Edge Services Gateway $Operation is disruptive to Edge services and may cause connectivity loss depending on the deployment architecture." $question = "Proceed with Redeploy of Edge Services Gateway $($Edge.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Repairing Edge Services Gateway $($Edge.Name)" $null = Invoke-NsxWebRequest -method "post" -URI $URI -connection $connection Write-Progress -Activity "Reparing Edge Services Gateway $($Edge.Name)" -Completed Get-NsxEdge -objectId $($Edge.Id) -Connection $connection } } end {} } function Set-NsxEdge { <# .SYNOPSIS Configures an existing NSX Edge Services Gateway Raw configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs support interfaces connected to either VLAN backed port groups or NSX Logical Switches. Use the Set-NsxEdge to perform updates to the Raw XML config for an ESG to enable basic support for manipulating Edge features that arent supported by specific PowerNSX cmdlets. .EXAMPLE $edge = Get-NsxEdge Edge01 PS C:\>$edge.features.firewall.enabled = "false" PS C:\>$edge | Set-NsxEdge Disable the Edge Firewall on ESG Edge01 .EXAMPLE Get-NsxEdge Edge01 | Set-NsxEdge -password Vmware1!Vmware1! Change the SSH Password .EXAMPLE Get-NsxEdge Edge01 | Set-NsxEdge -remoteAccess:$true Enable the SSH on ESG (you can use also use Enable-NsxSSHEdgeSSH) .EXAMPLE Get-NsxEdge Edge01 | Set-NsxEdge -username powernsx -password Vmware1!Vmware1! Set the SSH username to PowerNSX (You need to change/set the password on the sametime) .EXAMPLE Get-NsxEdge Edge01 | Set-NsxEdge -sshLoginBannerText "My Login Banner" Change the SSH Login Banner .EXAMPLE Get-NsxEdge Edge01 | Set-NsxEdge -passwordExpiry 30 Change the SSH Password Expiration to 30 (days) #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, #cliSettings [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$userName, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$password, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [boolean]$remoteAccess, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [ValidateRange(1, 99999)] [int]$passwordExpiry, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [string]$sshLoginBannerText, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Clone the Edge Element so we can modify without barfing up the source object. $_Edge = $Edge.CloneNode($true) #Remove EdgeSummary... $edgeSummary = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge -query 'descendant::edgeSummary') if ( $edgeSummary ) { $_Edge.RemoveChild($edgeSummary) | Out-Null } #cliSettings if ( $PsBoundParameters.ContainsKey('userName') ) { if ( $PsBoundParameters.ContainsKey('password') ) { if ( Invoke-XpathQuery -Node $_Edge -QueryMethod SelectSingleNode -query "child::cliSettings/userName" ) { $_Edge.cliSettings.username = $userName } else { Add-XmlElement -xmlRoot $_Edge.cliSettings -xmlElementName "userName" -xmlElementText $userName } } else { throw "You need to specify a password for change username..." } } if ( $PsBoundParameters.ContainsKey('password') ) { if ( Invoke-XpathQuery -Node $_Edge -QueryMethod SelectSingleNode -query "child::cliSettings/password" ) { $_Edge.cliSettings.password = $password } else { Add-XmlElement -xmlRoot $_Edge.cliSettings -xmlElementName "password" -xmlElementText $password } } if ( $PsBoundParameters.ContainsKey('remoteAccess') ) { if ( Invoke-XpathQuery -Node $_Edge -QueryMethod SelectSingleNode -query "child::cliSettings/remoteAccess" ) { $_Edge.cliSettings.remoteAccess = $remoteAccess.ToString().ToLower() } else { Add-XmlElement -xmlRoot $_Edge.cliSettings -xmlElementName "remoteAccess" -xmlElementText $remoteAccess.ToString().ToLower() } } if ( $PsBoundParameters.ContainsKey('passwordExpiry') ) { if ( Invoke-XpathQuery -Node $_Edge -QueryMethod SelectSingleNode -query "child::cliSettings/passwordExpiry" ) { $_Edge.cliSettings.passwordExpiry = $passwordExpiry.ToString() } else { Add-XmlElement -xmlRoot $_Edge.cliSettings -xmlElementName "passwordExpiry" -xmlElementText $passwordExpiry.ToString() } } if ( $PsBoundParameters.ContainsKey('sshLoginBannerText') ) { if ( Invoke-XpathQuery -Node $_Edge -QueryMethod SelectSingleNode -query "child::cliSettings/sshLoginBannerText" ) { $_Edge.cliSettings.sshLoginBannerText = $sshLoginBannerText } else { Add-XmlElement -xmlRoot $_Edge.cliSettings -xmlElementName "sshLoginBannerText" -xmlElementText $sshLoginBannerText } } $URI = "/api/4.0/edges/$($_Edge.Id)" $body = $_Edge.OuterXml if ( $confirm ) { $message = "Edge Services Gateway update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($Edge.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($Edge.Name)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($Edge.Name)" -Completed Get-NsxEdge -objectId $($Edge.Id) -Connection $connection } } end {} } function Remove-NsxEdge { <# .SYNOPSIS Removes an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. .EXAMPLE Get-NsxEdge Edge01 | Remove-NsxEdge -confirm:$false This cmdlet removes the specified ESG. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Edge Services Gateway removal is permanent." $question = "Proceed with removal of Edge Services Gateway $($Edge.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/4.0/edges/$($Edge.Edgesummary.ObjectId)" Write-Progress -Activity "Remove Edge Services Gateway $($Edge.Name)" Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection | Out-Null Write-Progress -Activity "Remove Edge Services Gateway $($Edge.Name)" -Completed } } end {} } function Get-NsxEdgeStatus { <# .SYNOPSIS Retrieves NSX Edge status .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. This cmdlet retrieves NSX Edge Status. The edgeStatus has the following possible states: * GREEN: Health checks are successful, status is good. * YELLOW: Intermittent health check failure. If health check fails for five consecutive times for all appliances, status will turn RED. * GREY: unknown status. * RED: None of the appliances for this NSX Edge are in a serving state. Get also the status (UP / Down / Applied / Not Configured) of each ESG Service (DNS, Firewall, Routing, VPN...) .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeStatus Get NSX Edge Status .EXAMPLE ((Get-NsxEdge Edge01 | Get-NsxEdgeStatus).featureStatuses.featureStatus | where-object { $_.service -eq 'dns' }).status Get only status of DNS service #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $URI = "/api/4.0/edges/$($Edge.Id)/status" [system.xml.xmldocument]$response = Invoke-NsxRestMethod -method "GET" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query "child::edgeStatus")) { $response.edgeStatus } } end {} } function Enable-NsxEdgeSsh { <# .SYNOPSIS Enables the SSH server on an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. This cmdlet enables the ssh server on the specified Edge Services Gateway. If rule autogeneration is configured on the Edge, the Edge firewall is automatically configured to allow incoming connections. .EXAMPLE Get-NsxEdge Edge01 | Enable-NsxEdgeSsh Enable SSH on edge Edge01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { $URI = "/api/4.0/edges/$($Edge.Edgesummary.ObjectId)/cliremoteaccess?enable=true" Write-Progress -Activity "Enable SSH on Edge Services Gateway $($Edge.Name)" Invoke-NsxRestMethod -method "post" -URI $URI -connection $connection | Out-Null Write-Progress -Activity "Enable SSH on Edge Services Gateway $($Edge.Name)" -Completed } end {} } function Disable-NsxEdgeSsh { <# .SYNOPSIS Disables the SSH server on an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. This cmdlet disables the ssh server on the specified Edge Services Gateway. .EXAMPLE Get-NsxEdge Edge01 | Disable-NsxEdgeSsh Disable SSH on edge Edge01 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Disabling SSH will prevent remote SSH connections to this edge." $question = "Proceed with disabling SSH service on $($Edge.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/4.0/edges/$($Edge.Edgesummary.ObjectId)/cliremoteaccess?enable=false" Write-Progress -Activity "Disable SSH on Edge Services Gateway $($Edge.Name)" Invoke-NsxRestMethod -method "post" -URI $URI -connection $connection | Out-Null Write-Progress -Activity "Disable SSH on Edge Services Gateway $($Edge.Name)" -Completed } } end {} } function Enable-NsxEdgeFips { <# .SYNOPSIS Enables FIPS on an existing NSX Edge Services Gateway. .DESCRIPTION Enables FIPS on an existing NSX Edge Services Gateway. Changing the FIPS mode will reboot the NSX Edge appliance .EXAMPLE Get-NsxEdge Edge01 | Enable-NsxEdgeFips Enable FIPS mode on edge Edge01 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ($Edge.enableFips -eq "false") { if ( $confirm ) { $message = "Enabling FIPS mode will reboot the NSX Edge appliance." $question = "Proceed with enabling FIPS mode on Edge Services Gateway: $($Edge.Name) ($($Edge.Edgesummary.ObjectId))?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/4.0/edges/$($Edge.Edgesummary.ObjectId)/fips?enable=true" Write-Progress -Activity "Enabling FIPS mode on Edge Services Gateway: $($Edge.Name) ($($Edge.Edgesummary.ObjectId))" $null = Invoke-NsxWebRequest -method "post" -URI $URI -connection $connection Write-Progress -Activity "Enabling FIPS mode on Edge Services Gateway: $($Edge.Name) ($($Edge.Edgesummary.ObjectId))" -Completed } } } end {} } function Disable-NsxEdgeFips { <# .SYNOPSIS Disables FIPS mode on an existing NSX Edge Services Gateway. .DESCRIPTION Enables FIPS mode on an existing NSX Edge Services Gateway. Changing the FIPS mode will reboot the NSX Edge appliance .EXAMPLE Get-NsxEdge Edge01 | Disable-NsxEdgeFips Disable FIPS mode on edge Edge01 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ($Edge.enableFips -eq "true") { if ( $confirm ) { $message = "Disabling FIPS mode will reboot the NSX Edge appliance." $question = "Proceed with disabling FIPS mode on Edge Services Gateway: $($Edge.Name) ($($Edge.Edgesummary.ObjectId))?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/4.0/edges/$($Edge.Edgesummary.ObjectId)/fips?enable=false" Write-Progress -Activity "Disabling FIPS mode on Edge Services Gateway: $($Edge.Name) ($($Edge.Edgesummary.ObjectId))" $null = Invoke-NsxWebRequest -method "post" -URI $URI -connection $connection Write-Progress -Activity "Disabling FIPS mode on Edge Services Gateway: $($Edge.Name) ($($Edge.Edgesummary.ObjectId))" -Completed } } } end {} } ######### ######### # Edge NAT related functions function Set-NsxEdgeNat { <# .SYNOPSIS Configures global NAT configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. NSX Edge provides network address translation (NAT) service to protect the IP addresses of internal (private) networks from the public network. You can configure NAT rules to provide access to services running on privately addressed virtual machines. There are two types of NAT rules that can be configured: SNAT and DNAT. The Set-NsxEdgeNat cmdlet configures the global NAT configuration of the specified Edge Services Gateway. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeNat $_ })] [System.Xml.XmlElement]$EdgeNat, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$Enabled, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_EdgeNat = $EdgeNat.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeNat.edgeId $_EdgeNat.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeNat -query 'descendant::edgeId')) ) | Out-Null #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('Enabled') ) { if ( $Enabled ) { $_EdgeNat.enabled = 'true' } else { $_EdgeNat.enabled = 'false' } } $URI = "/api/4.0/edges/$($EdgeId)/nat/config" $body = $_EdgeNat.OuterXml if ( $confirm ) { $message = "Edge Services Gateway NAT update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeNat } } end {} } function Get-NsxEdgeNat { <# .SYNOPSIS Gets global NAT configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. NSX Edge provides network address translation (NAT) service to protect the IP addresses of internal (private) networks from the public network. You can configure NAT rules to provide access to services running on privately addressed virtual machines. There are two types of NAT rules that can be configured: SNAT and DNAT. The Get-NsxEdgeNat cmdlet retrieves the global NAT configuration of the specified Edge Services Gateway. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNat Retrieve the global NAT configuration from ESG Edge01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge ) begin { } process { #We append the Edge-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_EdgeNat = $Edge.features.nat.CloneNode($True) Add-XmlElement -xmlRoot $_EdgeNat -xmlElementName "edgeId" -xmlElementText $Edge.Id $_EdgeNat } end {} } function Get-NsxEdgeNatRule { <# .SYNOPSIS Retrieves NAT rules from the specified NSX Edge Services Gateway NAT configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. NSX Edge provides network address translation (NAT) service to protect the IP addresses of internal (private) networks from the public network. The Get-NsxEdgeNatRule cmdlet retrieves the nat rules from the nat configuration specified. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNat | Get-NsxEdgeNatRule Retrieve the NAT rules from ESG Edge01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeNat $_ })] [System.Xml.XmlElement]$EdgeNat, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$RuleId, [Parameter (Mandatory = $false)] [switch]$ShowInternal = $false ) begin { } process { #We append the Edge-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_EdgeNat = ($EdgeNat.CloneNode($True)) $_EdgeNatRules = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeNat -query 'descendant::natRules') #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called natRule. If ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeNatRules -query 'descendant::natRule')) { $RuleCollection = $_EdgeNatRules.natRule if ( $PsBoundParameters.ContainsKey('RuleId')) { $RuleCollection = $RuleCollection | Where-Object { $_.ruleId -eq $RuleId } } if ( -not $ShowInternal ) { $RuleCollection = $RuleCollection | Where-Object { $_.ruleType -eq 'user' } } foreach ( $Rule in $RuleCollection ) { Add-XmlElement -xmlRoot $Rule -xmlElementName "edgeId" -xmlElementText $EdgeNat.EdgeId } $RuleCollection } } end {} } function New-NsxEdgeNatRule { <# .SYNOPSIS Creates a new NAT rule and adds it to the specified ESGs NAT configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. NSX Edge provides network address translation (NAT) service to protect the IP addresses of internal (private) networks from the public network. The New-NsxEdgeNatRule cmdlet creates a new NAT rule in the nat configuration specified. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNat | New-NsxEdgeNatRule -action snat -OriginalAddress 192.168.44.0/24 -TranslatedAddress 198.51.100.1 Add Source NAT from Original Address 192.168.44.0/24 with Translated Address 198.51.100.1 .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNat | New-NsxEdgeNatRule -action snat -OriginalAddress 192.168.23.0/24 -TranslatedAddress 198.51.100.2 -vnic 0 -LoggingEnabled -Enabled Add Source NAT from Original Address 192.168.23.0/24 with Translated Address 198.51.100.2 on vnic 0 with Logging .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNat | New-NsxEdgeNatRule -action dnat -OriginalAddress 198.51.100.1 -TranslatedAddress 192.168.44.1 Add Destination NAT from Original Address 198.51.100.1 with Translated Address 192.168.44.1 (All ports) .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNat | New-NsxEdgeNatRule -action dnat -OriginalAddress 198.51.100.2 -TranslatedAddress 192.168.23.1 -Protocol tcp -OriginalPort 22 Add Destination NAT from Original Address 198.51.100.2 with Translated Address 192.168.23.1 with tcp port 22 .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNat | New-NsxEdgeNatRule -action dnat -OriginalAddress 198.51.100.3 -TranslatedAddress 192.168.23.2 -Protocol tcp -OriginalPort 2222 -TranslatedPort 22 Add Destination NAT from Original Address 198.51.100.3 with Translated Address 192.168.23.2 with tcp port 2222 to translated Port 22 .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNat | New-NsxEdgeNatRule -action dnat -OriginalAddress 198.51.100.4 -TranslatedAddress 192.168.23.4 -Protocol icmp -icmptype 8 -description "dnat with only icmptype 8" Add Destination NAT from Original Address 198.51.100.4 with Translated Address 192.168.23.4 with protocol icmp and icmp type 8 (icmp request) with a description .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNat | New-NsxEdgeNatRule -action snat -OriginalAddress 192.168.44.0/24 -TranslatedAddress 198.51.100.1 -protocol tcp -snatMatchDestinationAddress 192.168.23.0/24 -snatMatchDestinationPort 22 Add Source NAT from Original Address 192.168.44.0/24 with Translated Address 198.51.100.1 and Match Destination Address 192.168.23.0/24 on Match Destination Port 22 Need NSX >= 6.3.0 .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNat | new-nsxedgenatrule -action dnat -OriginalAddress 198.51.100.1 -TranslatedAddress 192.168.23.1 -protocol tcp -dnatMatchSourceAddress 192.168.44.0/24 -dnatMatchSourcePort 1024 Add Destination NAT from Original Address 198.51.100.1 with Translated Address 192.168.23.1 and Match Source Address 192.168.44.0/24 on Match Source Port 1024 Need NSX >= 6.3.0 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeNat $_ })] [System.Xml.XmlElement]$EdgeNat, [Parameter (Mandatory = $False)] [ValidateRange(0, 200)] [int]$Vnic, [Parameter (Mandatory = $True)] [string]$OriginalAddress, [Parameter (Mandatory = $True)] [string]$TranslatedAddress, [Parameter (Mandatory = $True)] [Validateset("dnat", "snat", ignorecase = $false)] [string]$action, [Parameter (Mandatory = $false)] [string]$Protocol, [Parameter (Mandatory = $False)] [string]$Description, [Parameter (Mandatory = $False)] [switch]$LoggingEnabled = $false, [Parameter (Mandatory = $False)] [switch]$Enabled = $true, [Parameter (Mandatory = $false)] [string]$OriginalPort, [Parameter (Mandatory = $false)] [string]$TranslatedPort, [Parameter (Mandatory = $false)] [string]$IcmpType, [Parameter (Mandatory = $false)] [string]$dnatMatchSourceAddress, [Parameter (Mandatory = $false)] [string]$snatMatchDestinationAddress, [Parameter (Mandatory = $false)] [string]$dnatMatchSourcePort, [Parameter (Mandatory = $false)] [string]$snatMatchDestinationPort, [Parameter (Mandatory = $false)] [int]$AboveRuleId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Store the edgeId and remove it from the XML as we need to post it... $EdgeId = $EdgeNat.edgeId #Create the new rules + rule element. [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument if ( -not $PsBoundParameters.ContainsKey('AboveRuleId') ) { $Rules = $xmlDoc.CreateElement('natRules') $Rule = $xmlDoc.CreateElement('natRule') $xmlDoc.AppendChild($Rules) | Out-Null $Rules.AppendChild($Rule) | Out-Null $URI = "/api/4.0/edges/$EdgeId/nat/config/rules" } else { $Rule = $xmlDoc.CreateElement('natRule') $xmlDoc.AppendChild($Rule) | Out-Null $URI = "/api/4.0/edges/$EdgeId/nat/config/rules?aboveRuleId=$($AboveRuleId.toString())" } #Append the mandatory props Add-XmlElement -xmlRoot $Rule -xmlElementName "vnic" -xmlElementText $Vnic.ToString() Add-XmlElement -xmlRoot $Rule -xmlElementName "originalAddress" -xmlElementText $OriginalAddress.ToString() Add-XmlElement -xmlRoot $Rule -xmlElementName "translatedAddress" -xmlElementText $TranslatedAddress.ToString() Add-XmlElement -xmlRoot $Rule -xmlElementName "action" -xmlElementText $Action.ToString() Add-XmlElement -xmlRoot $Rule -xmlElementName "loggingEnabled" -xmlElementText $LoggingEnabled.ToString().tolower() Add-XmlElement -xmlRoot $Rule -xmlElementName "enabled" -xmlElementText $Enabled.ToString().tolower() #Now the optional ones if ( $PsBoundParameters.ContainsKey("Protocol") ) { Add-XmlElement -xmlRoot $Rule -xmlElementName "protocol" -xmlElementText $Protocol.ToString() } if ( $PsBoundParameters.ContainsKey("Description") ) { Add-XmlElement -xmlRoot $Rule -xmlElementName "description" -xmlElementText $Description.ToString() } if ( $PsBoundParameters.ContainsKey("OriginalPort") ) { Add-XmlElement -xmlRoot $Rule -xmlElementName "originalPort" -xmlElementText $OriginalPort.ToString() } if ( $PsBoundParameters.ContainsKey("TranslatedPort") ) { Add-XmlElement -xmlRoot $Rule -xmlElementName "translatedPort" -xmlElementText $TranslatedPort.ToString() } if ( $PsBoundParameters.ContainsKey("IcmpType") ) { Add-XmlElement -xmlRoot $Rule -xmlElementName "icmpType" -xmlElementText $IcmpType.ToString() } if ( $PsBoundParameters.ContainsKey('dnatMatchSourceAddress') ) { if ( [version]$Connection.Version -lt [version]"6.3.0") { Write-Warning "The option dnatMatchSourceAddress requires at least NSX version 6.3.0" } else { Add-XmlElement -xmlRoot $Rule -xmlElementName "dnatMatchSourceAddress" -xmlElementText $dnatMatchSourceAddress.ToString() } } if ( $PsBoundParameters.ContainsKey('snatMatchDestinationAddress') ) { if ( [version]$Connection.Version -lt [version]"6.3.0") { Write-Warning "The option snatMatchDestinationAddress requires at least NSX version 6.3.0" } else { Add-XmlElement -xmlRoot $Rule -xmlElementName "snatMatchDestinationAddress" -xmlElementText $snatMatchDestinationAddress.ToString() } } if ( $PsBoundParameters.ContainsKey('dnatMatchSourcePort') ) { if ( [version]$Connection.Version -lt [version]"6.3.0") { Write-Warning "The option dnatMatchSourcePort requires at least NSX version 6.3.0" } else { Add-XmlElement -xmlRoot $Rule -xmlElementName "dnatMatchSourcePort" -xmlElementText $dnatMatchSourcePort.ToString() } } if ( $PsBoundParameters.ContainsKey('snatMatchDestinationPort') ) { if ( [version]$Connection.Version -lt [version]"6.3.0") { Write-Warning "The option snatMatchDestinationPort requires at least NSX version 6.3.0" } else { Add-XmlElement -xmlRoot $Rule -xmlElementName "snatMatchDestinationPort" -xmlElementText $snatMatchDestinationPort.ToString() } } if ( -not $PsBoundParameters.ContainsKey('AboveRuleId') ) { $body = $Rules.OuterXml } else { $body = $Rule.OuterXml } Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed $ruleid = $response.Headers.Location -replace "/api/4.0/edges/$edgeid/nat/config/rules/", "" Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeNat | Get-NsxEdgeNatRule -RuleId $ruleid } end {} } function Remove-NsxEdgeNatRule { <# .SYNOPSIS Removes a NAT Rule from the specified ESGs NAT configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. NSX Edge provides network address translation (NAT) service to protect the IP addresses of internal (private) networks from the public network. The Remove-NsxEdgeNatRule cmdlet removes a specific NAT rule from the NAT configuration of the specified Edge Services Gateway. Rules to be removed can be constructed via a PoSH pipline filter outputing rule objects as produced by Get-NsxEdgeNatRule and passing them on the pipeline to Remove-NsxEdgeNatRule. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNatRule | Remove-NsxEdgenatRule Remove all NAT rule with confirmation .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeNatRule | Remove-NsxEdgenatRule -confirm:$false Remove all NAT rule without confirmation .EXAMPLE $rule = get-NsxEdge Edge01 | get-NsxEdgeNat | get-NsxEdgeNatRule -RuleId 196614 PS C:\>$rule | Remove-NsxEdgeNatRule -confirm:$false Remove the NAT rule 196614 without confirmation #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeNatRule $_ })] [System.Xml.XmlElement]$NatRule, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the rule config for our Edge $edgeId = $NatRule.edgeId $ruleId = $NatRule.ruleId $URI = "/api/4.0/edges/$EdgeId/nat/config/rules/$ruleId" if ( $confirm ) { $message = "Edge Services Gateway nat rule update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $EdgeId" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Completed } } end {} } ######### ######### # Edge FW related functions function Set-NsxEdgeFirewall { <# .SYNOPSIS Configures global Firewall configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. The NSX Edge provides layer 3/4 firewall services to protect connected networks. the Edge firewall is separate, and can be used to complement the NSX distributed firewall The Set-NsxEdgeFirewall cmdlet configures the global FW configuration of the specified Edge Services Gateway. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeFirewall | Set-NsxEdgeFirewall -DefaultRuleAction deny Retrieve the current global FW configuration of Edge01 and set the action on the default rule to deny. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeFirewall | Set-NsxEdgeFirewall -DefaultRuleAction deny -NoConfirm Retrieve the current global FW configuration of Edge01 and set the action on the default rule to deny without prompting for confirmation. #> [CmdletBinding(DefaultParameterSetName = "Default")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeFw $_ })] [System.Xml.XmlElement]$EdgeFirewall, [Parameter (Mandatory = $False, ParameterSetName = "LegacyConfirm")] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False, ParameterSetName = "Default")] #Disable Prompt for confirmation. [switch]$NoConfirm, [Parameter (Mandatory = $False)] #Enable / Disable Edge Firewall [switch]$Enabled, [Parameter (Mandatory = $False)] #Default rule action [ValidateSet("accept", "deny", "reject", IgnoreCase = $False)] [string]$DefaultRuleAction, [Parameter (Mandatory = $False)] #Default rule logging configuration [switch]$DefaultRuleLoggingEnabled, [Parameter (Mandatory = $False)] #Edge Firewall global config option [switch]$tcpPickOngoingConnections, [Parameter (Mandatory = $False)] #Edge Firewall global config option [switch]$tcpAllowOutOfWindowPackets, [Parameter (Mandatory = $False)] #Edge Firewall global config option [switch]$tcpSendResetForClosedVsePorts, [Parameter (Mandatory = $False)] #Edge Firewall global config option [switch]$dropInvalidTraffic, [Parameter (Mandatory = $False)] #Edge Firewall global config option [switch]$logInvalidTraffic, [Parameter (Mandatory = $False)] #Edge Firewall global config option [int]$tcpTimeoutOpen, [Parameter (Mandatory = $False)] #Edge Firewall global config option [int]$tcpTimeoutEstablished, [Parameter (Mandatory = $False)] #Edge Firewall global config option [int]$tcpTimeoutClose, [Parameter (Mandatory = $False)] #Edge Firewall global config option [int]$udpTimeout, [Parameter (Mandatory = $False)] #Edge Firewall global config option [int]$icmpTimeout, [Parameter (Mandatory = $False)] #Edge Firewall global config option [int]$icmp6Timeout, [Parameter (Mandatory = $False)] #Edge Firewall global config option [int]$ipGenericTimeout, [Parameter (Mandatory = $False)] #Edge Firewall global config option [switch]$enableSynFloodProtection, [Parameter (Mandatory = $False)] #Edge Firewall global config option [switch]$logIcmpErrors, [Parameter (Mandatory = $False)] #Edge Firewall global config option [switch]$dropIcmpReplays, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { If ( $PSCmdlet.ParameterSetName -eq "LegacyConfirm") { Write-Warning "The -confirm switch is deprecated and will be removed in a future release. Use -NoConfirm instead." $NoConfirm = ( -not $confirm ) } } process { #Create private xml element $_EdgeFirewall = $EdgeFirewall.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $EdgeFirewall.edgeId $_EdgeFirewall.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeFirewall -query 'descendant::edgeId')) ) | Out-Null #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('Enabled') ) { $_EdgeFirewall.enabled = $enabled.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('DefaultRuleAction') ) { $_EdgeFirewall.defaultPolicy.action = $DefaultRuleAction.ToLower() } if ( $PsBoundParameters.ContainsKey('DefaultRuleLoggingEnabled') ) { $_EdgeFirewall.defaultPolicy.loggingEnabled = $DefaultRuleLoggingEnabled.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('tcpPickOngoingConnections') ) { $_EdgeFirewall.globalConfig.tcpPickOngoingConnections = $tcpPickOngoingConnections.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('tcpAllowOutOfWindowPackets') ) { $_EdgeFirewall.globalConfig.tcpAllowOutOfWindowPackets = $tcpAllowOutOfWindowPackets.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('tcpSendResetForClosedVsePorts') ) { $_EdgeFirewall.globalConfig.tcpSendResetForClosedVsePorts = $tcpSendResetForClosedVsePorts.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('dropInvalidTraffic') ) { $_EdgeFirewall.globalConfig.dropInvalidTraffic = $dropInvalidTraffic.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('logInvalidTraffic') ) { $_EdgeFirewall.globalConfig.logInvalidTraffic = $logInvalidTraffic.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('tcpTimeoutOpen') ) { $_EdgeFirewall.globalConfig.tcpTimeoutOpen = $tcpTimeoutOpen.ToString() } if ( $PsBoundParameters.ContainsKey('tcpTimeoutEstablished') ) { $_EdgeFirewall.globalConfig.tcpTimeoutEstablished = $tcpTimeoutEstablished.ToString() } if ( $PsBoundParameters.ContainsKey('tcpTimeoutClose') ) { $_EdgeFirewall.globalConfig.tcpTimeoutClose = $tcpTimeoutClose.ToString() } if ( $PsBoundParameters.ContainsKey('udpTimeout') ) { $_EdgeFirewall.globalConfig.udpTimeout = $udpTimeout.ToString() } if ( $PsBoundParameters.ContainsKey('icmpTimeout') ) { $_EdgeFirewall.globalConfig.icmpTimeout = $icmpTimeout.ToString() } if ( $PsBoundParameters.ContainsKey('icmp6Timeout') ) { $_EdgeFirewall.globalConfig.icmp6Timeout = $icmp6Timeout.ToString() } if ( $PsBoundParameters.ContainsKey('ipGenericTimeout') ) { $_EdgeFirewall.globalConfig.ipGenericTimeout = $ipGenericTimeout.ToString() } if ( $PsBoundParameters.ContainsKey('enableSynFloodProtection') ) { if ( [version]$Connection.Version -lt [version]"6.2.3") { Write-Warning "The option enableSynFloodProtection requires at least NSX version 6.2.3" } else { $_EdgeFirewall.globalConfig.enableSynFloodProtection = $enableSynFloodProtection.ToString().ToLower() } } if ( $PsBoundParameters.ContainsKey('logIcmpErrors') ) { if ( [version]$Connection.Version -lt [version]"6.3.0") { Write-Warning "The option logIcmpErrors requires at least NSX version 6.3.0" } else { $_EdgeFirewall.globalConfig.logIcmpErrors = $logIcmpErrors.ToString().ToLower() } } if ( $PsBoundParameters.ContainsKey('dropIcmpReplays') ) { if ( [version]$Connection.Version -lt [version]"6.3.0") { Write-Warning "The option dropIcmpReplays requires at least NSX version 6.3.0" } else { $_EdgeFirewall.globalConfig.dropIcmpReplays = $dropIcmpReplays.ToString().ToLower() } } $URI = "/api/4.0/edges/$($EdgeId)/firewall/config" $body = $_EdgeFirewall.OuterXml if ( -not ( $Noconfirm )) { $message = "Edge Services Gateway firewall configuration update will modify and existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeFirewall } } end {} } function Get-NsxEdgeFirewall { <# .SYNOPSIS Gets global Firewall configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. The NSX Edge provides layer 3/4 firewall services to protect connected networks. the Edge firewall is separate from, and can be used to complement the NSX distributed firewall. The Get-NsxEdgeFirewall cmdlet retrieves the global FW configuration of the specified Edge Services Gateway. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge ) begin { } process { #We append the Edge-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_EdgeFw = $Edge.features.firewall.CloneNode($True) Add-XmlElement -xmlRoot $_EdgeFw -xmlElementName "edgeId" -xmlElementText $Edge.Id $_EdgeFw } end {} } function Get-NsxEdgeFirewallRule { <# .SYNOPSIS Retrieves Firewall rules from the specified NSX Edge Services Gateway Firewall configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. The NSX Edge provides layer 3/4 firewall services to protect connected networks. the Edge firewall is separate from, and can be used to complement the NSX distributed firewall. The Get-NsxEdgeFirewallRule cmdlet retrieves configured firewall rules on the specified Edge Services Gateway. #> [CmdLetBinding (DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeFw $_ })] [System.Xml.XmlElement]$EdgeFirewall, [Parameter (Mandatory = $false, ParameterSetName = "RuleId")] [ValidateNotNullorEmpty()] [String]$RuleId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [ValidateNotNullorEmpty()] [String]$Name ) begin { } process { #We append the Edge-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_EdgeFirewall = ($EdgeFirewall.CloneNode($True)) $_EdgeFirewallRules = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeFirewall -query 'descendant::firewallRules') #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called natRule. If ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeFirewallRules -query 'descendant::firewallRule')) { $RuleCollection = $_EdgeFirewallRules.FirewallRule if ( $PsBoundParameters.ContainsKey('RuleId')) { $RuleCollection = $RuleCollection | Where-Object { $_.id -eq $RuleId } } elseif ($PsBoundParameters.ContainsKey("Name")) { $RuleCollection = $RuleCollection | Where-Object { $_.Name -eq $Name } } foreach ( $Rule in $RuleCollection ) { Add-XmlElement -xmlRoot $Rule -xmlElementName "edgeId" -xmlElementText $EdgeFirewall.EdgeId } $RuleCollection } } end {} } function New-NsxEdgeFirewallRule { <# .SYNOPSIS Creates a new NSX Edge firewall rule on the specified ESG. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. The NSX Edge provides layer 3/4 firewall services to protect connected networks. the Edge firewall is separate from, and can be used to complement the NSX distributed firewall. The New-NsxEdgeFirewallRule cmdlet configures new firewall rules on the specified Edge Services Gateway. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeFw $_ })] [System.Xml.XmlElement]$EdgeFireWall, [Parameter (Mandatory = $true)] # Name of the new rule [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] # Comment string for the new rule [string]$Comment = "", [Parameter (Mandatory = $true)] # Action of the rule - allow, deny or reject. [ValidateSet("accept", "deny", "reject")] [string]$Action, [Parameter (Mandatory = $false)] # Source(s) of traffic to hit the rule. IP4/6 members are specified as string, any other member as the appropriate VI or PowerNSX object. [ValidateScript( { ValidateFirewallRuleSourceDest $_ })] [object[]]$Source, [Parameter (Mandatory = $false)] # Source(s) vNics of traffic to hit the rule. Valid options are 0 - 9, internal, external, vse [ValidateSet("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "internal", "external", "vse")] [string[]]$SourceVnic, [Parameter (Mandatory = $false)] # Destination(s) vNics of traffic to hit the rule. Valid options are 0 - 9, internal, external, vse [ValidateSet("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "internal", "external", "vse")] [string[]]$DestinationVnic, [Parameter (Mandatory = $false)] # Negate the list of sources hit by the rule [ValidateNotNullOrEmpty()] [switch]$NegateSource, [Parameter (Mandatory = $false)] # Destination(s) of traffic to hit the rule. IP4/6 members are specified as string, any other member as the appropriate VI or PowerNSX object. [ValidateScript( { ValidateFirewallRuleSourceDest $_ })] [object[]]$Destination, [Parameter (Mandatory = $false)] # Negate the list of destinations hit by the rule [ValidateNotNullOrEmpty()] [switch]$NegateDestination, [Parameter (Mandatory = $false)] # Services to hit the rule. Services must be marked for inheritance in global scope, or defined directly within edge scope. [ValidateScript ( { ValidateEdgeFirewallRuleService $_ })] [object[]]$Service, [Parameter (Mandatory = $false)] # Rule is created as disabled [switch]$Disabled, [Parameter (Mandatory = $false)] # Rule logging is enabled [switch]$EnableLogging, [Parameter (Mandatory = $false)] # Existing RuleId above which to create new rule [int]$AboveRuleId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { function Add-NsxEdgeFirewallSrcDestMembers { param ( $memberlist, $SourceDestNode ) foreach ($member in $memberlist) { if ( ( $member -as [ipaddress]) -or ( ValidateIPRange -argument $member ) -or ( ValidateIPPrefix -argument $member ) ) { #Item is v4 or 6 address Write-Debug "$($MyInvocation.MyCommand.Name) : Building source/dest node for $member" Write-Debug "$($MyInvocation.MyCommand.Name) : Object $member is an ipaddress" Add-XmlElement -xmlRoot $SourceDestNode -xmlElementName "ipAddress" -xmlElementText $member } elseif ( $member -is [system.xml.xmlelement] ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Building source/dest node for $($member.name)" Write-Debug "$($MyInvocation.MyCommand.Name) : Object $($member.name) is specified as xml element" #XML representation of NSX object passed - ipset, sec group or logical switch #get appropritate name, value. Add-XmlElement -xmlRoot $SourceDestNode -xmlElementName "groupingObjectId" -xmlElementText $member.objectId } else { Write-Debug "$($MyInvocation.MyCommand.Name) : Building source/dest node for $($member.name)" Write-Debug "$($MyInvocation.MyCommand.Name) : Object $($member.name) is specified as supported powercli object" #Proper PowerCLI Object passed. We just need to grab details from the moref. Add-XmlElement -xmlRoot $SourceDestNode -xmlElementName "groupingObjectId" -xmlElementText $member.extensiondata.moref.value } } } } process { #Get the edgeId $EdgeId = $EdgeFirewall.edgeId #Create the new rules + rule element. [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument $Rule = $xmlDoc.CreateElement('firewallRule') #Append the mandatory props Add-XmlElement -xmlRoot $Rule -xmlElementName "name" -xmlElementText $Name.ToString() Add-XmlElement -xmlRoot $Rule -xmlElementName "action" -xmlElementText $Action.ToString() Add-XmlElement -xmlRoot $Rule -xmlElementName "loggingEnabled" -xmlElementText $EnableLogging.ToString().tolower() Add-XmlElement -xmlRoot $Rule -xmlElementName "enabled" -xmlElementText ( -not $Disabled ).ToString().tolower() if ( $PsBoundParameters.ContainsKey("Comment") ) { Add-XmlElement -xmlRoot $Rule -xmlElementName "description" -xmlElementText $Comment.ToString() } #Build Sources Node if ( $PsBoundParameters.ContainsKey("source") -or $PsBoundParameters.ContainsKey("sourcevnic") ) { #Build the Source node and handle negation if necessary $SourceNode = $xmlDoc.CreateElement('source') $null = $Rule.AppendChild($SourceNode) Add-XmlElement -xmlRoot $SourceNode -xmlElementName "exclude" -xmlElementText $NegateSource.ToString().tolower() } #Normal Sources if ( $PsBoundParameters.ContainsKey("source")) { Add-NsxEdgeFirewallSrcDestMembers -memberlist $Source -SourceDestNode $SourceNode } #Source vNICs if ( $PsBoundParameters.ContainsKey("sourcevnic")) { foreach ( $vnic in $Sourcevnic ) { switch -Regex ($vNic) { "^\d$" { $vNicSpecifier = "vnic-index-$vnic" } "^all$" { $vNicSpecifier = "vse" } default { $vNicSpecifier = $vnic.toLower() } } Add-XmlElement -xmlRoot $SourceNode -xmlElementName "vnicGroupId" -xmlElementText $vNicSpecifier } } #Destinations Node if ( $PsBoundParameters.ContainsKey("destination") -or $PsBoundParameters.ContainsKey("destinationvnic") ) { #Build the Destination node and handle negation if necessary $DestNode = $xmlDoc.CreateElement('destination') $null = $Rule.AppendChild($DestNode) Add-XmlElement -xmlRoot $DestNode -xmlElementName "exclude" -xmlElementText $NegateDestination.ToString().tolower() } if ( $PsBoundParameters.ContainsKey("destination")) { Add-NsxEdgeFirewallSrcDestMembers -memberlist $Destination -SourceDestNode $DestNode } #Destination vNICs if ( $PsBoundParameters.ContainsKey("destinationvnic")) { foreach ( $vnic in $Destinationvnic ) { switch -Regex ($vNic) { "^\d$" { $vNicSpecifier = "vnic-index-$vnic" } "^all$" { $vNicSpecifier = "vse" } default { $vNicSpecifier = $vnic.toLower() } } Add-XmlElement -xmlRoot $DestNode -xmlElementName "vnicGroupId" -xmlElementText $vNicSpecifier } } #Services if ( $service ) { New-NsxEdgeServiceNode -itemlist $service -xmlRule $Rule } if ( -not $PsBoundParameters.ContainsKey('AboveRuleId') ) { $URI = "/api/4.0/edges/$EdgeId/firewall/config/rules" $Rules = $xmlDoc.CreateElement('firewallRules') $null = $Rules.AppendChild($Rule) $body = $Rules.OuterXml } else { $URI = "/api/4.0/edges/$EdgeId/firewall/config/rules?aboveRuleId=$($AboveRuleId.toString())" $body = $Rule.OuterXml } Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed $ruleid = $response.Headers.Location -replace "/api/4.0/edges/$edgeid/firewall/config/rules/", "" Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieving ruleid $ruleid from API for $edgeid" $response = Invoke-NsxWebRequest -method "get" -URI "/api/4.0/edges/$EdgeId/firewall/config/rules/$ruleid" -connection $connection [system.xml.xmlDocument]$responserule = $response.content Add-XmlElement -xmlRoot $responserule.firewallRule -xmlElementName "edgeId" -xmlElementText $EdgeId $responserule.firewallRule } end {} } function Set-NsxEdgeFirewallRule { <# .SYNOPSIS Set configuration for a Firewall Rule from the specified ESGs FirewallRule configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. The NSX Edge provides layer 3/4 firewall services to protect connected networks. the Edge firewall is separate from, and can be used to complement the NSX distributed firewall. This cmdlet accepts a Edge firewall rule object returned from Get-NsxEdgeFirewallRule and set configuration (disabled, name, action...) .EXAMPLE Get-NsxEdge 01 | Get-NsxEdgeFirewall | Get-NsxEdgeFirewallRule -Ruleid 1007 | Set-NsxEdgeFirewallRule -enabled:$false Disabled the RuleId 1007 of NSX Edge 01 .EXAMPLE GGet-NsxEdge 01 | Get-NsxEdgeFirewall | Get-NsxEdgeFirewallRule -Ruleid 1007 | Set-NsxEdgeFirewallRule -loggingEnabled:$true Enable logging on the RuleId 1007 .EXAMPLE Get-NsxEdge 01 | Get-NsxEdgeFirewall | Get-NsxEdgeFirewallRule -Ruleid 1007 | Set-NsxEdgeFirewallRule -name "My Edge Firewall Rule" Set/Update the name of the RuleId 1007 .EXAMPLE Get-NsxEdge 01 | Get-NsxEdgeFirewall | Get-NsxEdgeFirewallRule -Ruleid 1007 | Set-NsxEdgeFirewallRule -comment "My comment on this Edge Firewall Rule" Set/Update the description of the RuleId 1007 .EXAMPLE Get-NsxEdge 01 | Get-NsxEdgeFirewall | Get-NsxEdgeFirewallRule -Ruleid 1007 | Set-NsxEdgeFirewallRule -action deny Change action to deny to RuleId 1007 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] # Edge FW rule as returned by Get-NsxEdgeFirewallRule / New-NsxEdgeFirewallRule [ValidateScript( { ValidateEdgeFwRule $_ })] [System.Xml.XmlElement]$FirewallRule, [Parameter (Mandatory = $false)] [boolean]$enabled, [Parameter (Mandatory = $false)] [boolean]$loggingEnabled, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$name, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$comment, [Parameter (Mandatory = $false)] [ValidateSet("Accept", "Deny", "Reject")] [string]$action, [Parameter (Mandatory = $false)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $ruleId = $FirewallRule.Id #Clone the xml so we dont modify source... $_FirewallRule = $FirewallRule.CloneNode($true) $edgeId = $FirewallRule.edgeId $_FirewallRule.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_FirewallRule -query 'descendant::edgeId')) ) | Out-Null if ( $PsBoundParameters.ContainsKey('enabled') ) { $_FirewallRule.enabled = $enabled.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('loggingEnabled') ) { $_FirewallRule.loggingEnabled = $loggingEnabled.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('name') ) { $_FirewallRule.name = $name } if ( $PsBoundParameters.ContainsKey('comment') ) { $_FirewallRule.description = $comment } if ( $PsBoundParameters.ContainsKey('action') ) { $_FirewallRule.action = $action } $URI = "/api/4.0/edges/$EdgeId/firewall/config/rules/$ruleId" try { $response = Invoke-NsxWebRequest -method put -URI $uri -body $_FirewallRule.OuterXml -connection $connection [xml]$ruleElem = $response.Content Get-NsxEdge -Object $EdgeId | Get-NsxEdgeFirewall | Get-NsxEdgeFirewallRule -RuleId $ruleId } catch { throw "Failed to modify the specified rule. $_" } } end {} } function Remove-NsxEdgeFirewallRule { <# .SYNOPSIS Removes a Firewall Rule from the specified ESGs FirewallRule configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. The NSX Edge provides layer 3/4 firewall services to protect connected networks. the Edge firewall is separate from, and can be used to complement the NSX distributed firewall. The Remove-NsxEdgeFirewallRule cmdlet removes the specified firewall rules from the specified Edge Services Gateway. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility [CmdletBinding (DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeFwRule $_ })] [System.Xml.XmlElement]$FirewallRule, [Parameter (Mandatory = $False, ParameterSetName = "LegacyConfirm")] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False, ParameterSetName = "Default")] #Disable Prompt for confirmation. [switch]$NoConfirm, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { If ( $PSCmdlet.ParameterSetName -eq "LegacyConfirm") { Write-Warning "The -confirm switch is deprecated and will be removed in a future release. Use -NoConfirm instead." $NoConfirm = ( -not $confirm ) } } process { #Get the rule config for our Edge $edgeId = $FirewallRule.edgeId $ruleId = $FirewallRule.Id $URI = "/api/4.0/edges/$EdgeId/firewall/config/rules/$ruleId" if ( -not $noConfirm ) { $message = "Edge Services Gateway firewall rule update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $EdgeId" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Completed } } end {} } ######### ######### # Edge Certificate related functions function Get-NsxEdgeCsr { <# .SYNOPSIS Gets SSL Certificate Signing Requests from an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. SSL Certificates are used by a variety of services within NSX, including SSL VPN and Load Balancing. Certificate Signing Requests define the subject details to be included in an SSL certificate and are the object that is signed by a Certificate Authority in order to provide a valid certificate The Get-NsxEdgeCsr cmdlet retrieves csr's definined on the specified Edge Services Gateway, or with the specified objectId #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "Edge")] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $true, ParameterSetName = "objectId")] [string]$objectId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( $PsBoundParameters.ContainsKey('objectId')) { #Just getting a single named csr by id group $URI = "/api/2.0/services/truststore/csr/$objectId" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $response ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::csr')) { $response.csr } } } else { $URI = "/api/2.0/services/truststore/csr/scope/$($Edge.Id)" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $response ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::csrs/csr')) { $response.csrs.csr } } } } end {} } function New-NsxEdgeCsr { <# .SYNOPSIS Creates a new SSL Certificate Signing Requests on an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. SSL Certificates are used by a variety of services within NSX, including SSL VPN and Load Balancing. Certificate Signing Requests define the subject details to be included in an SSL certificate and are the object that is signed by a Certificate Authority in order to provide a valid certificate The New-NsxEdgeCsr cmdlet creates a new csr on the specified Edge Services Gateway. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $True)] [string]$CommonName, [Parameter (Mandatory = $True)] [string]$Organisation, [Parameter (Mandatory = $True)] [string]$Country, [Parameter (Mandatory = $True)] [string]$OrganisationalUnit, [Parameter (Mandatory = $False)] [ValidateSet(2048, 3072)] [int]$Keysize = 2048, [Parameter (Mandatory = $False)] [ValidateSet("RSA", "DSA", IgnoreCase = $false )] [string]$Algorithm = "RSA", [Parameter (Mandatory = $False)] [string]$Description, [Parameter (Mandatory = $False)] [string]$Name, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { $edgeId = $Edge.Id #Create the new csr element and subject child element. [System.Xml.XmlDocument] $xmlDoc = New-Object System.Xml.XmlDocument $csr = $xmlDoc.CreateElement('csr') $subject = $xmlDoc.CreateElement('subject') $csr.AppendChild($subject) | Out-Null #Common Name $CnAttribute = $xmlDoc.CreateElement('attribute') $subject.AppendChild($CnAttribute) | Out-Null Add-XmlElement -xmlRoot $CnAttribute -xmlElementName "key" -xmlElementText "CN" Add-XmlElement -xmlRoot $CnAttribute -xmlElementName "value" -xmlElementText $CommonName.ToString() #Organisation $OAttribute = $xmlDoc.CreateElement('attribute') $subject.AppendChild($OAttribute) | Out-Null Add-XmlElement -xmlRoot $OAttribute -xmlElementName "key" -xmlElementText "O" Add-XmlElement -xmlRoot $OAttribute -xmlElementName "value" -xmlElementText $Organisation.ToString() #OU $OuAttribute = $xmlDoc.CreateElement('attribute') $subject.AppendChild($OuAttribute) | Out-Null Add-XmlElement -xmlRoot $OuAttribute -xmlElementName "key" -xmlElementText "OU" Add-XmlElement -xmlRoot $OuAttribute -xmlElementName "value" -xmlElementText $OrganisationalUnit.ToString() #Country $CAttribute = $xmlDoc.CreateElement('attribute') $subject.AppendChild($CAttribute) | Out-Null Add-XmlElement -xmlRoot $CAttribute -xmlElementName "key" -xmlElementText "C" Add-XmlElement -xmlRoot $CAttribute -xmlElementName "value" -xmlElementText $Country.ToString() #Algo Add-XmlElement -xmlRoot $csr -xmlElementName "algorithm" -xmlElementText $Algorithm.ToString() #KeySize Add-XmlElement -xmlRoot $csr -xmlElementName "keySize" -xmlElementText $Keysize.ToString() #Name if ( $PsBoundParameters.ContainsKey('Name')) { Add-XmlElement -xmlRoot $csr -xmlElementName "name" -xmlElementText $Name.ToString() } #Description if ( $PsBoundParameters.ContainsKey('Description')) { Add-XmlElement -xmlRoot $csr -xmlElementName "description" -xmlElementText $Description.ToString() } $URI = "/api/2.0/services/truststore/csr/$edgeId" $body = $csr.OuterXml Write-Progress -Activity "Update Edge Services Gateway $EdgeId" $response = Invoke-NsxRestMethod -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Completed $response.csr } end {} } function Remove-NsxEdgeCsr { <# .SYNOPSIS Remvoves the specificed SSL Certificate Signing Request from an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. SSL Certificates are used by a variety of services within NSX, including SSL VPN and Load Balancing. Certificate Signing Requests define the subject details to be included in an SSL certificate and are the object that is signed by a Certificate Authority in order to provide a valid certificate The Remove-NsxEdgeCsr cmdlet removes a csr from the specified Edge Services Gateway. CSRs to be removed can be constructed via a PoSH pipline filter outputing csr objects as produced by Get-NsxEdgeCsr and passing them on the pipeline to Remove-NsxEdgeCsr. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeCsr $_ })] [System.Xml.XmlElement]$Csr, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "CSR removal is permanent." $question = "Proceed with removal of CSR $($Csr.objectId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/2.0/services/truststore/csr/$($csr.objectId)" Write-Progress -Activity "Remove CSR $($Csr.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove CSR $($Csr.Name)" -Completed } } end {} } function Get-NsxEdgeCertificate { <# .SYNOPSIS Gets SSL Certificate from an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. SSL Certificates are used by a variety of services within NSX, including SSL VPN and Load Balancing. SSL Certificates are used to provide encyption and trust validation for the services that use them. The Get-NsxEdgeCertificate cmdlet retrieves certificates definined on the specified Edge Services Gateway, or with the specified objectId #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "Edge")] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $true, ParameterSetName = "objectId")] [string]$objectId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( $PsBoundParameters.ContainsKey('objectId')) { #Just getting a single named csr by id group $URI = "/api/2.0/services/truststore/certificate/$objectId" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $response ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::certificate')) { $response.certificate } } } else { $URI = "/api/2.0/services/truststore/certificate/scope/$($Edge.Id)" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $response ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::certificates/certificate')) { $response.certificates.certificate } } } } end {} } function New-NsxEdgeSelfSignedCertificate { <# .SYNOPSIS Signs an NSX Edge Certificate Signing Request on an existing NSX Edge Services Gateway to create a new Self Signed Certificate .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. SSL Certificates are used by a variety of services within NSX, including SSL VPN and Load Balancing. Certificate Signing Requests define the subject details to be included in an SSL certificate and are the object that is signed by a Certificate Authority in order to provide a valid certificate. The New-NsxEdgeCertificate cmdlet signs an existing csr on the specified Edge Services Gateway to create a Self Signed Certificate. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeCSR $_ })] [System.Xml.XmlElement]$CSR, [Parameter (Mandatory = $False)] [int]$NumberOfDays = 365, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { $edgeId = $Csr.Scope.Id $URI = "/api/2.0/services/truststore/csr/$($csr.objectId)?noOfDays=$NumberOfDays" Write-Progress -Activity "Update Edge Services Gateway $EdgeId" $response = Invoke-NsxRestMethod -method "Put" -URI $URI -connection $connection Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Completed $response.Certificate } end {} } function Remove-NsxEdgeCertificate { <# .SYNOPSIS Remvoves the specificed SSL Certificate from an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. SSL Certificates are used by a variety of services within NSX, including SSL VPN and Load Balancing. SSL Certificates are used to provide encyption and trust validation for the services that use them. The Remove-NsxEdgeCertificate cmdlet removes a certificate from the specified Edge Services Gateway. Certificates to be removed can be constructed via a PoSH pipeline filter outputing certificate objects as produced by Get-NsxEdgeCertificate and passing them on the pipeline to Remove-NsxEdgeCertificate. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeCertificate $_ })] [System.Xml.XmlElement]$Certificate, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Certificate removal is permanent." $question = "Proceed with removal of Certificate $($Certificate.objectId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/2.0/services/truststore/certificate/$($certificate.objectId)" Write-Progress -Activity "Remove Certificate $($Csr.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Certificate $($Csr.Name)" -Completed } } end {} } ######### ######### # Edge SSL VPN related functions function Get-NsxSslVpn { <# .SYNOPSIS Gets global SSLVPN configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. SSL VPN allows remote users to connect securely to private networks behind an NSX Edge Services gateway and access servers and applications in the private networks. The Get-NsxSslVpn cmdlet retrieves the global SSLVPN configuration of the specified Edge Services Gateway. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge ) begin { } process { #We append the Edge-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_EdgeSslVpn = $Edge.features.sslvpnConfig.CloneNode($True) Add-XmlElement -xmlRoot $_EdgeSslVpn -xmlElementName "edgeId" -xmlElementText $Edge.Id $_EdgeSslVpn } end {} } function Set-NsxSslVpn { #To do, portal customisation, server ip config... [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$Enabled, [Parameter (Mandatory = $False)] [switch]$EnableCompression, [Parameter (Mandatory = $False)] [switch]$ForceVirtualKeyboard, [Parameter (Mandatory = $False)] [switch]$RandomizeVirtualkeys, [Parameter (Mandatory = $False)] [switch]$PreventMultipleLogon, [Parameter (Mandatory = $False)] [string]$ClientNotification, [Parameter (Mandatory = $False)] [switch]$EnablePublicUrlAccess = $False, [Parameter (Mandatory = $False)] [int]$ForcedTimeout, [Parameter (Mandatory = $False)] [int]$SessionIdleTimeout, [Parameter (Mandatory = $False)] [switch]$ClientAutoReconnect, [Parameter (Mandatory = $False)] [switch]$ClientUpgradeNotification, [Parameter (Mandatory = $False)] [switch]$EnableLogging, [Parameter (Mandatory = $False)] [ValidateSet("emergency", "alert", "critical", "error", "warning", "notice", "info", "debug")] [string]$LogLevel, [Parameter (Mandatory = $False)] [ipaddress]$ServerAddress, [Parameter (Mandatory = $False)] [int]$ServerPort, [Parameter (Mandatory = $False)] [string]$CertificateID, [Parameter (Mandatory = $False)] [switch]$Enable_AES128_SHA, [Parameter (Mandatory = $False)] [switch]$Enable_AES256_SHA, [Parameter (Mandatory = $False)] [switch]$Enable_DES_CBC3_SHA, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { if (($EnablePublicUrlAccess -eq $True) -and ([version]$Connection.version -ge [version]"6.3.0")) { Write-Warning "PublicURL feature has been deprecated in the 6.3.X release. It has not been enabled." $EnablePublicUrlAccess = $False } } process { #Create private xml element $_EdgeSslVpn = $SslVpn.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeSslVpn.edgeId $_EdgeSslVpn.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeSslVpn -query 'descendant::edgeId')) ) | Out-Null #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('Enabled')) { $_EdgeSslVpn.enabled = $Enabled.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('EnableCompression')) { $_EdgeSslVpn.advancedConfig.enableCompression = $EnableCompression.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('ForceVirtualKeyboard')) { $_EdgeSslVpn.advancedConfig.ForceVirtualKeyboard = $ForceVirtualKeyboard.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('RandomizeVirtualkeys')) { $_EdgeSslVpn.advancedConfig.RandomizeVirtualkeys = $RandomizeVirtualkeys.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('PreventMultipleLogon')) { $_EdgeSslVpn.advancedConfig.PreventMultipleLogon = $PreventMultipleLogon.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('EnablePublicUrlAccess')) { $_EdgeSslVpn.advancedConfig.EnablePublicUrlAccess = $EnablePublicUrlAccess.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('ClientNotification')) { $_EdgeSslVpn.advancedConfig.ClientNotification = $ClientNotification.toString() } if ( $PsBoundParameters.ContainsKey('ForcedTimeout')) { $_EdgeSslVpn.advancedConfig.timeout.ForcedTimeout = $ForcedTimeout.ToString() } if ( $PsBoundParameters.ContainsKey('SessionIdleTimeout')) { $_EdgeSslVpn.advancedConfig.timeout.SessionIdleTimeout = $SessionIdleTimeout.ToString() } if ( $PsBoundParameters.ContainsKey('ClientAutoReconnect')) { $_EdgeSslVpn.clientConfiguration.AutoReconnect = $ClientAutoReconnect.ToString() } if ( $PsBoundParameters.ContainsKey('ClientUpgradeNotification')) { $_EdgeSslVpn.clientConfiguration.UpgradeNotification = $ClientUpgradeNotification.ToString().tolower() } if ( $PsBoundParameters.ContainsKey("EnableLogging")) { $_EdgeSslVpn.logging.enable = $EnableLogging.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("LogLevel")) { $_EdgeSslVpn.logging.logLevel = $LogLevel.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("ServerAddress") -or $PsBoundParameters.ContainsKey("ServerPort") -or $PsBoundParameters.ContainsKey("Enable_DES_CBC3_SHA") -or $PsBoundParameters.ContainsKey("Enable_AES128_SHA") -or $PsBoundParameters.ContainsKey("Enable_AES256_SHA")) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeSslVpn -query 'descendant::serverSettings') ) { [System.Xml.XmlElement]$serverSettings = $_EdgeSslVpn.ownerDocument.CreateElement('serverSettings') $_EdgeSslVpn.AppendChild($serverSettings) | Out-Null } else { [System.Xml.XmlElement]$ServerSettings = $_EdgeSslVpn.serverSettings } if ( $PsBoundParameters.ContainsKey("ServerAddress")) { #Set ServerAddress if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $serverSettings -query 'descendant::serverAddresses') ) { [System.Xml.XmlElement]$serverAddresses = $_EdgeSslVpn.ownerDocument.CreateElement('serverAddresses') $serverSettings.AppendChild($serverAddresses) | Out-Null } else { [System.Xml.XmlElement]$serverAddresses = $serverSettings.serverAddresses } if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $serverAddresses -query 'descendant::ipAddress') ) { Add-XmlElement -xmlRoot $serverAddresses -xmlElementName "ipAddress" -xmlElementText $($ServerAddress.IPAddresstoString) } else { $serverAddresses.ipAddress = [string]$ServerAddress.IPAddresstoString } } if ( $PsBoundParameters.ContainsKey("ServerPort")) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $serverSettings -query 'descendant::port') ) { Add-XmlElement -xmlRoot $serverSettings -xmlElementName "port" -xmlElementText $ServerPort.ToString() } else { $serverSettings.port = $ServerPort.ToString() } } if ( $PsBoundParameters.ContainsKey("CertificateID")) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $serverSettings -query 'descendant::certificateId') ) { Add-XmlElement -xmlRoot $serverSettings -xmlElementName "certificateId" -xmlElementText $CertificateID } else { $serverSettings.certificateId = $CertificateID } } if ( $PsBoundParameters.ContainsKey("Enable_DES_CBC3_SHA") -or $PsBoundParameters.ContainsKey("Enable_AES128_SHA") -or $PsBoundParameters.ContainsKey("Enable_AES256_SHA")) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ServerSettings -query 'descendant::cipherList') ) { [System.Xml.XmlElement]$cipherList = $serverSettings.ownerDocument.CreateElement('cipherList') $serverSettings.AppendChild($cipherList) | Out-Null } else { [System.Xml.XmlElement]$cipherList = $serverSettings.cipherList } if ( $PsBoundParameters.ContainsKey("Enable_DES_CBC3_SHA") ) { $cipher = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $cipherList -query "descendant::cipher") | Where-Object { $_.'#Text' -eq 'DES-CBC3-SHA' } if ( ( -not $cipher ) -and $Enable_DES_CBC3_SHA ) { Add-XmlElement -xmlRoot $cipherList -xmlElementName "cipher" -xmlElementText "DES-CBC3-SHA" } elseif ( $cipher -and ( -not $Enable_DES_CBC3_SHA )) { $cipherList.RemoveChild( $cipher ) | Out-Null } } if ( $PsBoundParameters.ContainsKey("Enable_AES128_SHA") ) { $cipher = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $cipherList -query "descendant::cipher") | Where-Object { $_.'#Text' -eq 'AES128-SHA' } if ( ( -not $cipher ) -and $Enable_AES128_SHA ) { Add-XmlElement -xmlRoot $cipherList -xmlElementName "cipher" -xmlElementText "AES128-SHA" } elseif ( $cipher -and ( -not $Enable_AES128_SHA )) { $CipherList.RemoveChild( $cipher ) | Out-Null } } if ( $PsBoundParameters.ContainsKey("Enable_AES256_SHA") ) { $cipher = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $cipherList -query "descendant::cipher") | Where-Object { $_.'#Text' -eq 'AES256-SHA' } if ( ( -not $cipher ) -and $Enable_AES256_SHA ) { Add-XmlElement -xmlRoot $cipherList -xmlElementName "cipher" -xmlElementText "AES256-SHA" } elseif ( $cipher -and ( -not $Enable_AES256_SHA )) { $CipherList.RemoveChild( $cipher ) | Out-Null } } } } $URI = "/api/4.0/edges/$EdgeId/sslvpn/config" $body = $_EdgeSslVpn.OuterXml if ( $confirm ) { $message = "Edge Services Gateway SSL VPN update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxSslVpn } } end {} } function New-NsxSslVpnAuthServer { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", "", Scope = "Function", Target = "*")] # Incorrect assertion by ScriptAnalyser. param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $False)] [ValidateRange(1, 63)] [int]$PasswordMinLength = 1, [Parameter (Mandatory = $False)] [ValidateRange(1, 63)] [int]$PasswordMaxLength = 63, [Parameter (Mandatory = $False)] [ValidateRange(1, 63)] [int]$PasswordMinAlphabet = 0, [Parameter (Mandatory = $False)] [ValidateRange(1, 63)] [int]$PasswordMinDigit = 0, [Parameter (Mandatory = $False)] [ValidateRange(1, 63)] [int]$PasswordMinSpecialChar = 0, [Parameter (Mandatory = $False)] [switch]$PasswordAllowUsernameInPassword = $false, [Parameter (Mandatory = $False)] [int]$PasswordLifetime = 30, [Parameter (Mandatory = $False)] [int]$PasswordExpiryNotificationTime = 25, [Parameter (Mandatory = $False)] [int]$PasswordLockoutRetryCount = 3, [Parameter (Mandatory = $False)] [int]$PasswordLockoutRetryDuration = 2, [Parameter (Mandatory = $False)] [int]$PasswordLockoutDuration = 2, [Parameter (Mandatory = $False)] [ValidateSet("Local")] [string]$ServerType = "Local", [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) Begin {} Process { #Create private xml element $_EdgeSslVpn = $SslVpn.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeSslVpn.edgeId $_EdgeSslVpn.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeSslVpn -query 'descendant::edgeId')) ) | Out-Null #Get the AuthServers node, and create a new PrimaryAuthServer in it. $PrimaryAuthServers = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeSslVpn -query 'descendant::authenticationConfiguration/passwordAuthentication/primaryAuthServers') Switch ( $ServerType ) { "Local" { #Like highlander, there can be only one! :) if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $PrimaryAuthServers -query 'descendant::localAuthServer') ) { throw "Local Authentication source already exists. Use Set-NsxEdgeSslVpnAuthServer to modify an existing server." } else { #Construct the Local Server XML Element. $AuthServer = $PrimaryAuthServers.ownerDocument.CreateElement('com.vmware.vshield.edge.sslvpn.dto.LocalAuthServerDto') $PrimaryAuthServers.AppendChild($AuthServer) | Out-Null $PasswordPolicy = $AuthServer.ownerDocument.CreateElement('passwordPolicy') $AccountLockoutPolicy = $AuthServer.ownerDocument.CreateElement('accountLockoutPolicy') $AuthServer.AppendChild($PasswordPolicy) | Out-Null $AuthServer.AppendChild($AccountLockoutPolicy) | Out-Null #No need to check if user specified as we are defaulting to the documented defaults for all props as per API guide. Add-XmlElement -xmlRoot $PasswordPolicy -xmlElementName "minLength" -xmlElementText $PasswordMinLength.ToString() Add-XmlElement -xmlRoot $PasswordPolicy -xmlElementName "maxLength" -xmlElementText $PasswordMaxLength.ToString() Add-XmlElement -xmlRoot $PasswordPolicy -xmlElementName "minAlphabets" -xmlElementText $PasswordMinAlphabet.ToString() Add-XmlElement -xmlRoot $PasswordPolicy -xmlElementName "minDigits" -xmlElementText $PasswordMinDigit.ToString() Add-XmlElement -xmlRoot $PasswordPolicy -xmlElementName "minSpecialChar" -xmlElementText $PasswordMinSpecialChar.ToString() Add-XmlElement -xmlRoot $PasswordPolicy -xmlElementName "allowUserIdWithinPassword" -xmlElementText $PasswordAllowUsernameInPassword.ToString() Add-XmlElement -xmlRoot $PasswordPolicy -xmlElementName "passwordLifeTime" -xmlElementText $PasswordLifetime.ToString() Add-XmlElement -xmlRoot $PasswordPolicy -xmlElementName "expiryNotification" -xmlElementText $PasswordExpiryNotificationTime.ToString() Add-XmlElement -xmlRoot $AccountLockoutPolicy -xmlElementName "retryCount" -xmlElementText $PasswordLockoutRetryCount.ToString() Add-XmlElement -xmlRoot $AccountLockoutPolicy -xmlElementName "retryDuration" -xmlElementText $PasswordLockoutRetryDuration.ToString() Add-XmlElement -xmlRoot $AccountLockoutPolicy -xmlElementName "lockoutDuration" -xmlElementText $PasswordLockoutDuration.ToString() } } default { Throw "Server type not supported." } } $URI = "/api/4.0/edges/$EdgeId/sslvpn/config" $body = $_EdgeSslVpn.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed #Totally cheating here while we only support local auth server. Will have to augment this later... Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxSslVpn | Get-NsxSslVpnAuthServer -ServerType local } end {} } function Get-NsxSslVpnAuthServer { <# .SYNOPSIS Gets SSLVPN Authentication Servers from an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. SSL VPN allows remote users to connect securely to private networks behind an NSX Edge Services gateway and access servers and applications in the private networks. Authentication Servers define how the SSL VPN server authenticates user connections The Get-NsxSslVpnAuthServer cmdlet retrieves the SSL VPN authentication sources configured on the specified Edge Services Gateway. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $false, Position = 1)] [ValidateSet("local", IgnoreCase = $false)] [string]$ServerType ) begin { } process { #We append the Edge-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_EdgeSslVpn = $SslVpn.CloneNode($True) $PrimaryAuthenticationServers = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_EdgeSslVpn -query 'descendant::authenticationConfiguration/passwordAuthentication/primaryAuthServers/*') if ( $PrimaryAuthenticationServers ) { foreach ( $Server in $PrimaryAuthenticationServers ) { Add-XmlElement -xmlRoot $Server -xmlElementName "edgeId" -xmlElementText $SslVpn.EdgeId if ( $PsBoundParameters.ContainsKey('ServerType')) { $Server | Where-Object { $_.authServerType -eq $ServerType } } else { $Server } } } $SecondaryAuthenticationServers = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_EdgeSslVpn -query 'descendant::authenticationConfiguration/passwordAuthentication/secondaryAuthServers/*') if ( $SecondaryAuthenticationServers ) { foreach ( $Server in $SecondaryAuthenticationServers ) { Add-XmlElement -xmlRoot $Server -xmlElementName "edgeId" -xmlElementText $SslVpn.EdgeId $Server } } } end {} } function New-NsxSslVpnUser { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", "", Scope = "Function", Target = "*")] # Unable to remove without breaking backward compatibilty. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $True)] [ValidateNotNullOrEmpty()] [string]$UserName, [Parameter (Mandatory = $True)] [ValidateNotNullOrEmpty()] [string]$Password, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$FirstName, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$LastName, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$Description, [Parameter (Mandatory = $False)] [switch]$DisableUser = $False, [Parameter (Mandatory = $False)] [switch]$PasswordNeverExpires = $False, [Parameter (Mandatory = $False)] [switch]$AllowPasswordChange = $True, [Parameter (Mandatory = $False)] [switch]$ForcePasswordChangeOnNextLogin, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) Begin {} Process { #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $SslVpn.edgeId #Create the user element $User = $SslVpn.ownerDocument.CreateElement('user') #Mandatory and defaults Add-XmlElement -xmlRoot $User -xmlElementName "userId" -xmlElementText $UserName.ToString() Add-XmlElement -xmlRoot $User -xmlElementName "password" -xmlElementText $Password.ToString() Add-XmlElement -xmlRoot $User -xmlElementName "disableUserAccount" -xmlElementText $DisableUser.ToString().ToLower() Add-XmlElement -xmlRoot $User -xmlElementName "passwordNeverExpires" -xmlElementText $PasswordNeverExpires.ToString().ToLower() if ( $AllowPasswordChange ) { $xmlAllowChangePassword = $User.OwnerDocument.CreateElement('allowChangePassword') $User.AppendChild($xmlAllowChangePassword) | Out-Null Add-XmlElement -xmlRoot $xmlAllowChangePassword -xmlElementName "changePasswordOnNextLogin" -xmlElementText $AllowPasswordChange.ToString().ToLower() } elseif ( $ForcePasswordChangeOnNextLogin ) { throw "Must enable allow password change to force user to change on next logon." } # Optionals... if ( $PsBoundParameters.ContainsKey('FirstName')) { Add-XmlElement -xmlRoot $User -xmlElementName "firstName" -xmlElementText $FirstName.ToString() } if ( $PsBoundParameters.ContainsKey('LastName')) { Add-XmlElement -xmlRoot $User -xmlElementName "lastName" -xmlElementText $LastName.ToString() } if ( $PsBoundParameters.ContainsKey('Description')) { Add-XmlElement -xmlRoot $User -xmlElementName "description" -xmlElementText $Description.ToString() } $URI = "/api/4.0/edges/$edgeId/sslvpn/config/auth/localserver/users/" $body = $User.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxSslVpn | Get-NsxSslVpnUser -UserName $UserName } } function Get-NsxSslVpnUser { param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $false, Position = 1)] [string]$UserName ) begin { } process { #We append the Edge-id to the associated XML to enable pipeline workflows and #consistent readable output $_EdgeSslVpn = $SslVpn.CloneNode($True) $Users = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_EdgeSslVpn -query 'descendant::users/*') if ( $Users ) { foreach ( $User in $Users ) { Add-XmlElement -xmlRoot $User -xmlElementName "edgeId" -xmlElementText $SslVpn.EdgeId if ( $PsBoundParameters.ContainsKey('UserName')) { $User | Where-Object { $_.UserId -eq $UserName } } else { $User } } } } end {} } function Remove-NsxSslVpnUser { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpnUser $_ })] [System.Xml.XmlElement]$SslVpnUser, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our Edge $edgeId = $SslVpnUser.edgeId $userId = $SslVpnUser.objectId $URI = "/api/4.0/edges/$edgeId/sslvpn/config/auth/localserver/users/$userId" if ( $confirm ) { $message = "User deletion is permanent." $question = "Proceed with deletion of user $($SslVpnUser.UserId) ($($userId)) from edge $($edgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Deleting user $($SslVpnUser.UserId) ($($userId)) from edge $edgeId" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Deleting user $($SslVpnUser.UserId) ($($userId)) from edge $edgeId" -Completed } } end {} } function New-NsxSslVpnIpPool { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $True)] [ValidateNotNullOrEmpty()] [string]$IpRange, [Parameter (Mandatory = $True)] [ValidateNotNullOrEmpty()] [ipaddress]$Netmask, [Parameter (Mandatory = $True)] [ValidateNotNullOrEmpty()] [ipaddress]$Gateway, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [ipAddress]$PrimaryDnsServer, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [ipAddress]$SecondaryDnsServer, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$DnsSuffix, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [ipAddress]$WinsServer, [Parameter (Mandatory = $False)] [switch]$Enabled = $True, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) Begin {} Process { #Store the edgeId. $edgeId = $SslVpn.edgeId #Create the ipAddressPool element $IpAddressPool = $SslVpn.ownerDocument.CreateElement('ipAddressPool') #Mandatory and defaults Add-XmlElement -xmlRoot $IpAddressPool -xmlElementName "ipRange" -xmlElementText $IpRange.ToString() Add-XmlElement -xmlRoot $IpAddressPool -xmlElementName "netmask" -xmlElementText $($NetMask.IpAddressToString) Add-XmlElement -xmlRoot $IpAddressPool -xmlElementName "gateway" -xmlElementText $($Gateway.IpAddressToString) # Optionals... if ( $PsBoundParameters.ContainsKey('Description')) { Add-XmlElement -xmlRoot $IpAddressPool -xmlElementName "description" -xmlElementText $Description.ToString() } if ( $PsBoundParameters.ContainsKey('PrimaryDNSServer')) { Add-XmlElement -xmlRoot $IpAddressPool -xmlElementName "primaryDns" -xmlElementText $($PrimaryDnsServer.IpAddressToString) } if ( $PsBoundParameters.ContainsKey('SecondaryDNSServer')) { Add-XmlElement -xmlRoot $IpAddressPool -xmlElementName "secondaryDns" -xmlElementText $($SecondaryDNSServer.IpAddressToString) } if ( $PsBoundParameters.ContainsKey('DnsSuffix')) { Add-XmlElement -xmlRoot $IpAddressPool -xmlElementName "dnsSuffix" -xmlElementText $DnsSuffix.ToString() } if ( $PsBoundParameters.ContainsKey('WinsServer')) { Add-XmlElement -xmlRoot $IpAddressPool -xmlElementName "winsServer" -xmlElementText $($WinsServer.IpAddressToString) } if ( -not $Enabled ) { Add-XmlElement -xmlRoot $IpAddressPool -xmlElementName "enabled" -xmlElementText "false" } $URI = "/api/4.0/edges/$edgeId/sslvpn/config/client/networkextension/ippools/" $body = $IpAddressPool.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxSslVpn | Get-NsxSslVpnIpPool -IpRange $IpRange } } function Get-NsxSslVpnIpPool { param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $false, Position = 1)] [string]$IpRange ) begin { } process { #We append the Edge-id to the associated XML to enable pipeline workflows and #consistent readable output $_EdgeSslVpn = $SslVpn.CloneNode($True) $IpPools = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_EdgeSslVpn -query 'descendant::ipAddressPools/*') if ( $IpPools ) { foreach ( $IpPool in $IpPools ) { Add-XmlElement -xmlRoot $IpPool -xmlElementName "edgeId" -xmlElementText $SslVpn.EdgeId if ( $PsBoundParameters.ContainsKey('IpRange')) { $IpPool | Where-Object { $_.ipRange -eq $IpRange } } else { $IpPool } } } } end {} } function Remove-NsxSslVpnIpPool { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpnIpPool $_ })] [System.Xml.XmlElement]$SslVpnIpPool, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { $edgeId = $SslVpnIpPool.edgeId $poolId = $SslVpnIpPool.objectId $URI = "/api/4.0/edges/$edgeId/sslvpn/config/client/networkextension/ippools/$poolId" if ( $confirm ) { $message = "Ip Pool deletion is permanent." $question = "Proceed with deletion of pool $($SslVpnIpPool.IpRange) ($($poolId)) from edge $($edgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Deleting pool $($SslVpnIpPool.IpRange) ($($poolId)) from edge $edgeId" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Deleting pool $($SslVpnIpPool.IpRange) ($($poolId)) from edge $edgeId" -Completed } } end {} } function New-NsxSslVpnPrivateNetwork { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $True)] [ValidateNotNullOrEmpty()] [string]$Network, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$Ports, [Parameter (Mandatory = $False)] [switch]$BypassTunnel = $False, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$Description, [Parameter (Mandatory = $False)] [switch]$OptimiseTcp = $True, [Parameter (Mandatory = $False)] [switch]$Enabled = $True, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) Begin {} Process { #Store the edgeId. $edgeId = $SslVpn.edgeId #Create the ipAddressPool element $PrivateNetwork = $SslVpn.ownerDocument.CreateElement('privateNetwork') #Mandatory and defaults Add-XmlElement -xmlRoot $PrivateNetwork -xmlElementName "network" -xmlElementText $Network.ToString() # Optionals... if ( $PsBoundParameters.ContainsKey('Description')) { Add-XmlElement -xmlRoot $PrivateNetwork -xmlElementName "description" -xmlElementText $Description.ToString() } if ( -not $BypassTunnel ) { [system.Xml.XmlElement]$sendOverTunnel = $PrivateNetwork.ownerDocument.CreateElement('sendOverTunnel') $PrivateNetwork.AppendChild($SendOverTunnel) | Out-Null Add-XmlElement -xmlRoot $SendOverTunnel -xmlElementName "optimize" -xmlElementText $OptimiseTcp.ToString().ToLower() if ( $PsBoundParameters.ContainsKey('Ports')) { Add-XmlElement -xmlRoot $SendOverTunnel -xmlElementName "ports" -xmlElementText $Ports.ToString() } } elseif ( $OptimiseTcp ) { Write-Warning "TCP Optimisation is not applicable when tunnel bypass is enabled." } elseif ( $PsBoundParameters.ContainsKey('Ports') ) { throw "Unable to specify ports when tunnel bypass is enabled." } if ( -not $Enabled ) { Add-XmlElement -xmlRoot $PrivateNetwork -xmlElementName "enabled" -xmlElementText "false" } $URI = "/api/4.0/edges/$edgeId/sslvpn/config/client/networkextension/privatenetworks" $body = $PrivateNetwork.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxSslVpn | Get-NsxSslVpnPrivateNetwork -Network $Network } } function Get-NsxSslVpnPrivateNetwork { param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $false, Position = 1)] [string]$Network ) begin { } process { #We append the Edge-id to the associated XML to enable pipeline workflows and #consistent readable output $_EdgeSslVpn = $SslVpn.CloneNode($True) $Networks = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_EdgeSslVpn -query 'descendant::privateNetworks/*') if ( $Networks ) { foreach ( $Net in $Networks ) { Add-XmlElement -xmlRoot $Net -xmlElementName "edgeId" -xmlElementText $SslVpn.EdgeId if ( $PsBoundParameters.ContainsKey('Network')) { $Net | Where-Object { $_.Network -eq $Network } } else { $Net } } } } end {} } function Remove-NsxSslVpnPrivateNetwork { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpnPrivateNetwork $_ })] [System.Xml.XmlElement]$SslVpnPrivateNetwork, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { $edgeId = $SslVpnPrivateNetwork.edgeId $networkId = $SslVpnPrivateNetwork.objectId $URI = "/api/4.0/edges/$edgeId/sslvpn/config/client/networkextension/privatenetworks/$networkId" if ( $confirm ) { $message = "Private network deletion is permanent." $question = "Proceed with deletion of network $($SslVpnPrivateNetwork.Network) ($($networkId)) from edge $($edgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Deleting network $($SslVpnPrivateNetwork.Network) ($($networkId)) from edge $edgeId" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Deleting network $($SslVpnPrivateNetwork.Network) ($($networkId)) from edge $edgeId" -Completed } } end {} } function New-NsxSslVpnClientInstallationPackage { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $True)] [string]$Name, [Parameter (Mandatory = $True)] [ipAddress[]]$Gateway, [Parameter (Mandatory = $False)] [ValidateRange(1, 65535)] [Int]$Port, [Parameter (Mandatory = $False)] [switch]$CreateLinuxClient, [Parameter (Mandatory = $False)] [switch]$CreateMacClient, [Parameter (Mandatory = $False)] [string]$Description, [Parameter (Mandatory = $False)] [switch]$StartClientOnLogon, [Parameter (Mandatory = $False)] [switch]$HideSystrayIcon, [Parameter (Mandatory = $False)] [switch]$RememberPassword, [Parameter (Mandatory = $False)] [switch]$SilentModeOperation, [Parameter (Mandatory = $False)] [switch]$SilentModeInstallation, [Parameter (Mandatory = $False)] [switch]$HideNetworkAdaptor, [Parameter (Mandatory = $False)] [switch]$CreateDesktopIcon, [Parameter (Mandatory = $False)] [switch]$EnforceServerSecurityCertValidation, [Parameter (Mandatory = $False)] [switch]$Enabled = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) Begin {} Process { #Store the edgeId. $edgeId = $SslVpn.edgeId #Create the ipAddressPool element $clientInstallPackage = $SslVpn.ownerDocument.CreateElement('clientInstallPackage') #gatewayList element [system.Xml.XmlElement]$gatewayList = $clientInstallPackage.ownerDocument.CreateElement('gatewayList') $clientInstallPackage.AppendChild($gatewayList) | Out-Null foreach ($gatewayitem in $gateway) { [system.Xml.XmlElement]$gatewayNode = $gatewayList.ownerDocument.CreateElement('gateway') $gatewayList.AppendChild($gatewayNode) | Out-Null Add-XmlElement -xmlRoot $gatewayNode -xmlElementName "hostName" -xmlElementText $gatewayitem if ( $PSBoundParameters.ContainsKey('port')) { Add-XmlElement -xmlRoot $gatewayNode -xmlElementName "port" -xmlElementText $Port } } #Mandatory and defaults Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "profileName" -xmlElementText $Name Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "enabled" -xmlElementText $Enabled.ToString().ToLower() # Optionals... if ( $PsBoundParameters.ContainsKey('StartClientOnLogon')) { Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "startClientOnLogon" -xmlElementText $StartClientOnLogon.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('hideSystrayIcon')) { Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "hideSystrayIcon" -xmlElementText $hideSystrayIcon.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('rememberPassword')) { Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "rememberPassword" -xmlElementText $rememberPassword.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('silentModeOperation')) { Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "silentModeOperation" -xmlElementText $silentModeOperation.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('silentModeInstallation')) { Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "silentModeInstallation" -xmlElementText $silentModeInstallation.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('hideNetworkAdaptor')) { Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "hideNetworkAdaptor" -xmlElementText $hideNetworkAdaptor.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('createDesktopIcon')) { Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "createDesktopIcon" -xmlElementText $createDesktopIcon.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('enforceServerSecurityCertValidation')) { Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "enforceServerSecurityCertValidation" -xmlElementText $enforceServerSecurityCertValidation.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('createLinuxClient')) { Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "createLinuxClient" -xmlElementText $createLinuxClient.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('createMacClient')) { Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "createMacClient" -xmlElementText $createMacClient.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('description')) { Add-XmlElement -xmlRoot $clientInstallPackage -xmlElementName "description" -xmlElementText $description.ToString().ToLower() } $URI = "/api/4.0/edges/$edgeId/sslvpn/config/client/networkextension/installpackages/" $body = $clientInstallPackage.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxSslVpn | Get-NsxSslVpnClientInstallationPackage -Name $Name } } function Get-NsxSslVpnClientInstallationPackage { param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $false, Position = 1)] [string]$Name ) begin { } process { #We append the Edge-id to the associated XML to enable pipeline workflows and #consistent readable output $_EdgeSslVpn = $SslVpn.CloneNode($True) $Packages = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_EdgeSslVpn -query 'descendant::clientInstallPackages/*') if ( $Packages ) { foreach ( $Package in $Packages ) { Add-XmlElement -xmlRoot $Package -xmlElementName "edgeId" -xmlElementText $SslVpn.EdgeId if ( $PsBoundParameters.ContainsKey('Name')) { $Package | Where-Object { $_.ProfileName -eq $Name } } else { $Package } } } } end {} } function Remove-NsxSslVpnClientInstallationPackage { [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeSslVpnClientPackage $_ })] [System.Xml.XmlElement]$EdgeSslVpnClientPackage, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { $edgeId = $EdgeSslVpnClientPackage.edgeId $packageId = $EdgeSslVpnClientPackage.objectId $URI = "/api/4.0/edges/$edgeId/sslvpn/config/client/networkextension/installpackages/$packageId" if ( $confirm ) { $message = "Installation Package deletion is permanent." $question = "Proceed with deletion of installation package $($EdgeSslVpnClientPackage.profileName) ($($packageId)) from edge $($edgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Deleting install package $($EdgeSslVpnClientPackage.profileName) ($($packageId)) from edge $edgeId" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Deleting install package $($EdgeSslVpnClientPackage.profileName) ($($packageId)) from edge $edgeId" -Completed } } end {} } function Remove-NsxSslVpn { <# .SYNOPSIS Remove the global SSLVPN configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. SSL VPN allows remote users to connect securely to private networks behind an NSX Edge Services gateway and access servers and applications in the private networks. The Remove-NsxSslVpn cmdlet unconfigures the global SSLVPN configuration of the specified Edge Services Gateway. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxSslVpn | Remove-NsxSslVpn Remove all NSX SSL VPN configuration with confirmation .EXAMPLE Get-NsxEdge Edge01 | Get-NsxSslVpn | Remove-NsxSslVpn -NoConfirm:$true Remove all NSX SSL VPN configuration without confirmation #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #NSX Edge SslVpn to remove [ValidateScript( { ValidateEdgeSslVpn $_ })] [System.Xml.XmlElement]$SslVpn, [Parameter (Mandatory = $False, ParameterSetName = "LegacyConfirm")] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False, ParameterSetName = "Default")] #Disable Prompt for confirmation. [switch]$NoConfirm, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { If ( $PSCmdlet.ParameterSetName -eq "LegacyConfirm") { Write-Warning "The -confirm switch is deprecated and will be removed in a future release. Use -NoConfirm instead." $NoConfirm = ( -not $confirm ) } } process { $edgeId = $SslVpn.edgeId if ( -not ( $Noconfirm )) { $message = "Edge SslVpn removal is permanent." $question = "Proceed with removal of Edge SslVpn $($EdgeId) ?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/4.0/edges/$($EdgeId)/sslvpn/config" Write-Progress -Activity "Remove SSL VPN for Edge $($EdgeId)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove SSL VPN for Edge $($EdgeId)" -Completed } } end {} } ######### ######### # Edge Routing related functions function Set-NsxEdgeRouting { <# .SYNOPSIS Configures global routing configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Set-NsxEdgeRouting cmdlet configures the global routing configuration of the specified Edge Services Gateway. .EXAMPLE Configure the default route of the ESG PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Set-NsxEdgeRouting -DefaultGatewayVnic 0 -DefaultGatewayAddress 10.0.0.101 .EXAMPLE Enable ECMP PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Set-NsxEdgeRouting -EnableECMP .EXAMPLE Enable OSPF PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Set-NsxEdgeRouting -EnableOSPF -RouterId 1.1.1.1 .EXAMPLE Enable BGP PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdge | Get-NsxEdgeRouting | Set-NsxEdgeRouting -EnableBGP -RouterId 1.1.1.1 -LocalAS 1234 .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Set-NsxEdgeRouting -EnableOspfRouteRedistribution:$false -Confirm:$false Disable OSPF Route Redistribution without confirmation. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$EnableOspf, [Parameter (Mandatory = $False)] [switch]$EnableBgp, [Parameter (Mandatory = $False)] [IpAddress]$RouterId, [Parameter (Mandatory = $False)] [ValidateRange(0, 65535)] [int]$LocalAS, [Parameter (Mandatory = $False)] [switch]$EnableEcmp, [Parameter (Mandatory = $False)] [switch]$EnableOspfRouteRedistribution, [Parameter (Mandatory = $False)] [switch]$EnableBgpRouteRedistribution, [Parameter (Mandatory = $False)] [switch]$EnableLogging, [Parameter (Mandatory = $False)] [ValidateSet("emergency", "alert", "critical", "error", "warning", "notice", "info", "debug")] [string]$LogLevel, [Parameter (Mandatory = $False)] [ValidateRange(0, 200)] [int]$DefaultGatewayVnic, [Parameter (Mandatory = $False)] [ValidateRange(0, 9128)] [int]$DefaultGatewayMTU, [Parameter (Mandatory = $False)] [string]$DefaultGatewayDescription, [Parameter (Mandatory = $False)] [ipAddress]$DefaultGatewayAddress, [Parameter (Mandatory = $False)] [ValidateRange(0, 255)] [int]$DefaultGatewayAdminDistance, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_EdgeRouting = $EdgeRouting.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeRouting.edgeId $_EdgeRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::edgeId')) ) | Out-Null #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('EnableOSPF') -or $PsBoundParameters.ContainsKey('EnableBGP') ) { $xmlGlobalConfig = $_EdgeRouting.routingGlobalConfig $xmlRouterId = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $xmlGlobalConfig -query 'descendant::routerId') if ( $EnableOSPF -or $EnableBGP ) { if ( -not ($xmlRouterId -or $PsBoundParameters.ContainsKey("RouterId"))) { #Existing config missing and no new value set... throw "RouterId must be configured to enable dynamic routing" } if ($PsBoundParameters.ContainsKey("RouterId")) { #Set Routerid... if ($xmlRouterId) { $xmlRouterId = $RouterId.IPAddresstoString } else { Add-XmlElement -xmlRoot $xmlGlobalConfig -xmlElementName "routerId" -xmlElementText $RouterId.IPAddresstoString } } } } if ( $PsBoundParameters.ContainsKey('EnableOSPF')) { $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::ospf') if ( -not $ospf ) { #ospf node does not exist. [System.XML.XMLElement]$ospf = $_EdgeRouting.ownerDocument.CreateElement("ospf") $_EdgeRouting.appendChild($ospf) | Out-Null } if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'descendant::enabled')) { #Enabled element exists. Update it. $ospf.enabled = $EnableOSPF.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "enabled" -xmlElementText $EnableOSPF.ToString().ToLower() } } if ( $PsBoundParameters.ContainsKey('EnableBGP')) { $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::bgp') if ( -not $bgp ) { #bgp node does not exist. [System.XML.XMLElement]$bgp = $_EdgeRouting.ownerDocument.CreateElement("bgp") $_EdgeRouting.appendChild($bgp) | Out-Null } if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::enabled')) { #Enabled element exists. Update it. $bgp.enabled = $EnableBGP.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "enabled" -xmlElementText $EnableBGP.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("LocalAS")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::localAS')) { #LocalAS element exists, update it. $bgp.localAS = $LocalAS.ToString() } else { #LocalAS element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "localAS" -xmlElementText $LocalAS.ToString() } } elseif ( (-not ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::localAS')) -and $EnableBGP )) { throw "Existing configuration has no Local AS number specified. Local AS must be set to enable BGP." } } if ( $PsBoundParameters.ContainsKey("EnableECMP")) { $_EdgeRouting.routingGlobalConfig.ecmp = $EnableECMP.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("EnableOspfRouteRedistribution")) { $_EdgeRouting.ospf.redistribution.enabled = $EnableOspfRouteRedistribution.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("EnableBgpRouteRedistribution")) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'child::bgp/redistribution/enabled') ) { throw "BGP must have been configured at least once to enable or disable BGP route redistribution. Enable BGP and try again." } $_EdgeRouting.bgp.redistribution.enabled = $EnableBgpRouteRedistribution.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("EnableLogging")) { $_EdgeRouting.routingGlobalConfig.logging.enable = $EnableLogging.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("LogLevel")) { $_EdgeRouting.routingGlobalConfig.logging.logLevel = $LogLevel.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("DefaultGatewayVnic") -or $PsBoundParameters.ContainsKey("DefaultGatewayAddress") -or $PsBoundParameters.ContainsKey("DefaultGatewayDescription") -or $PsBoundParameters.ContainsKey("DefaultGatewayMTU") -or $PsBoundParameters.ContainsKey("DefaultGatewayAdminDistance") ) { #Check for and create if required the defaultRoute element. first. if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting.staticRouting -query 'descendant::defaultRoute')) { #defaultRoute element does not exist $defaultRoute = $_EdgeRouting.ownerDocument.CreateElement('defaultRoute') $_EdgeRouting.staticRouting.AppendChild($defaultRoute) | Out-Null } else { #defaultRoute element exists $defaultRoute = $_EdgeRouting.staticRouting.defaultRoute } if ( $PsBoundParameters.ContainsKey("DefaultGatewayVnic") ) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $defaultRoute -query 'descendant::vnic')) { #element does not exist Add-XmlElement -xmlRoot $defaultRoute -xmlElementName "vnic" -xmlElementText $DefaultGatewayVnic.ToString() } else { #element exists $defaultRoute.vnic = $DefaultGatewayVnic.ToString() } } if ( $PsBoundParameters.ContainsKey("DefaultGatewayAddress") ) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $defaultRoute -query 'descendant::gatewayAddress')) { #element does not exist Add-XmlElement -xmlRoot $defaultRoute -xmlElementName "gatewayAddress" -xmlElementText $DefaultGatewayAddress.ToString() } else { #element exists $defaultRoute.gatewayAddress = $DefaultGatewayAddress.ToString() } } if ( $PsBoundParameters.ContainsKey("DefaultGatewayDescription") ) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $defaultRoute -query 'descendant::description')) { #element does not exist Add-XmlElement -xmlRoot $defaultRoute -xmlElementName "description" -xmlElementText $DefaultGatewayDescription } else { #element exists $defaultRoute.description = $DefaultGatewayDescription } } if ( $PsBoundParameters.ContainsKey("DefaultGatewayMTU") ) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $defaultRoute -query 'descendant::mtu')) { #element does not exist Add-XmlElement -xmlRoot $defaultRoute -xmlElementName "mtu" -xmlElementText $DefaultGatewayMTU.ToString() } else { #element exists $defaultRoute.mtu = $DefaultGatewayMTU.ToString() } } if ( $PsBoundParameters.ContainsKey("DefaultGatewayAdminDistance") ) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $defaultRoute -query 'descendant::adminDistance')) { #element does not exist Add-XmlElement -xmlRoot $defaultRoute -xmlElementName "adminDistance" -xmlElementText $DefaultGatewayAdminDistance.ToString() } else { #element exists $defaultRoute.adminDistance = $DefaultGatewayAdminDistance.ToString() } } } $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $_EdgeRouting.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeRouting } } end {} } function Get-NsxEdgeRouting { <# .SYNOPSIS Retrieves routing configuration for the specified NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Get-NsxEdgeRouting cmdlet retrieves the routing configuration of the specified Edge Services Gateway. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting Get routing configuration for the ESG Edge01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge ) begin { } process { #We append the Edge-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_EdgeRouting = $Edge.features.routing.CloneNode($True) Add-XmlElement -xmlRoot $_EdgeRouting -xmlElementName "edgeId" -xmlElementText $Edge.Id $_EdgeRouting } end {} } # Static Routing function Get-NsxEdgeStaticRoute { <# .SYNOPSIS Retrieves Static Routes from the specified NSX Edge Services Gateway Routing configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Get-NsxEdgeStaticRoute cmdlet retrieves the static routes from the routing configuration specified. .EXAMPLE Get-NsxEdge 01 | Get-NsxEdgeRouting | Get-NsxEdgeStaticRoute Get static routes defining on ESG Edge01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$Network, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [ipAddress]$NextHop ) begin { } process { #We append the Edge-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_EdgeStaticRouting = ($EdgeRouting.staticRouting.CloneNode($True)) $EdgeStaticRoutes = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeStaticRouting -query 'descendant::staticRoutes') #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called route. If ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $EdgeStaticRoutes -query 'descendant::route')) { $RouteCollection = $EdgeStaticRoutes.route if ( $PsBoundParameters.ContainsKey('Network')) { $RouteCollection = $RouteCollection | Where-Object { $_.network -eq $Network } } if ( $PsBoundParameters.ContainsKey('NextHop')) { $RouteCollection = $RouteCollection | Where-Object { $_.nextHop -eq $NextHop } } foreach ( $StaticRoute in $RouteCollection ) { Add-XmlElement -xmlRoot $StaticRoute -xmlElementName "edgeId" -xmlElementText $EdgeRouting.EdgeId } $RouteCollection } } end {} } function New-NsxEdgeStaticRoute { <# .SYNOPSIS Creates a new static route and adds it to the specified ESGs routing configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The New-NsxEdgeStaticRoute cmdlet adds a new static route to the routing configuration of the specified Edge Services Gateway. .EXAMPLE Add a new static route to ESG Edge01 for 1.1.1.0/24 via 10.0.0.200 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | New-NsxEdgeStaticRoute -Network 1.1.1.0/24 -NextHop 10.0.0.200 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [ValidateRange(0, 200)] [int]$Vnic, [Parameter (Mandatory = $False)] [ValidateRange(0, 9128)] [int]$MTU, [Parameter (Mandatory = $False)] [string]$Description, [Parameter (Mandatory = $True)] [ipAddress]$NextHop, [Parameter (Mandatory = $True)] [string]$Network, [Parameter (Mandatory = $False)] [ValidateRange(0, 255)] [int]$AdminDistance, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_EdgeRouting = $EdgeRouting.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeRouting.edgeId $_EdgeRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::edgeId')) ) | Out-Null #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. #Create the new route element. $Route = $_EdgeRouting.ownerDocument.CreateElement('route') #Need to do an xpath query here rather than use PoSH dot notation to get the static route element, #as it might be empty, and PoSH silently turns an empty element into a string object, which is rather not what we want... :| $StaticRoutes = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting.staticRouting -query 'descendant::staticRoutes') $StaticRoutes.AppendChild($Route) | Out-Null Add-XmlElement -xmlRoot $Route -xmlElementName "network" -xmlElementText $Network.ToString() Add-XmlElement -xmlRoot $Route -xmlElementName "nextHop" -xmlElementText $NextHop.ToString() if ( $PsBoundParameters.ContainsKey("Vnic") ) { Add-XmlElement -xmlRoot $Route -xmlElementName "vnic" -xmlElementText $Vnic.ToString() } if ( $PsBoundParameters.ContainsKey("MTU") ) { Add-XmlElement -xmlRoot $Route -xmlElementName "mtu" -xmlElementText $MTU.ToString() } if ( $PsBoundParameters.ContainsKey("Description") ) { Add-XmlElement -xmlRoot $Route -xmlElementName "description" -xmlElementText $Description.ToString() } if ( $PsBoundParameters.ContainsKey("AdminDistance") ) { Add-XmlElement -xmlRoot $Route -xmlElementName "adminDistance" -xmlElementText $AdminDistance.ToString() } $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $_EdgeRouting.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeRouting | Get-NsxEdgeStaticRoute -Network $Network -NextHop $NextHop } } end {} } function Remove-NsxEdgeStaticRoute { <# .SYNOPSIS Removes a static route from the specified ESGs routing configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Remove-NsxEdgeStaticRoute cmdlet removes a static route from the routing configuration of the specified Edge Services Gateway. Routes to be removed can be constructed via a PoSH pipline filter outputing route objects as produced by Get-NsxEdgeStaticRoute and passing them on the pipeline to Remove-NsxEdgeStaticRoute. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeStaticRoute | where-object { $_.network -eq '1.1.1.0/24' -and $_.nextHop -eq '10.0.0.100' } | Remove-NsxEdgeStaticRoute Remove a route to 1.1.1.0/24 via 10.0.0.100 from ESG Edge01 .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeStaticRoute | where-object { $_.network -eq '1.1.1.0/24' } | Remove-NsxEdgeStaticRoute Remove all routes to 1.1.1.0/24 from ESG Edge01 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeStaticRoute $_ })] [System.Xml.XmlElement]$StaticRoute, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our Edge $edgeId = $StaticRoute.edgeId $routing = Get-NsxEdge -objectId $edgeId -Connection $connection | Get-NsxEdgeRouting #Remove the edgeId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'descendant::edgeId')) ) | Out-Null #Need to do an xpath query here to query for a route that matches the one passed in. #Union of nextHop and network should be unique $xpathQuery = "//staticRoutes/route[nextHop=`"$($StaticRoute.nextHop)`" and network=`"$($StaticRoute.network)`"]" Write-Debug "XPath query for route nodes to remove is: $xpathQuery" $RouteToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing.staticRouting -query $xpathQuery) if ( $RouteToRemove ) { Write-Debug "RouteToRemove Element is: `n $($RouteToRemove.OuterXml | Format-XML) " $routing.staticRouting.staticRoutes.RemoveChild($RouteToRemove) | Out-Null $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed } } else { Throw "Route for network $($StaticRoute.network) via $($StaticRoute.nextHop) was not found in routing configuration for Edge $edgeId" } } end {} } # Prefixes function Get-NsxEdgePrefix { <# .SYNOPSIS Retrieves IP Prefixes from the specified NSX Edge Services Gateway Routing configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Get-NsxEdgePrefix cmdlet retrieves IP prefixes from the routing configuration specified. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgePrefix Retrieve prefixes from Edge Edge01 .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgePrefix -Network 1.1.1.0/24 Retrieve prefix 1.1.1.0/24 from Edge Edge01 .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgePrefix -Name CorpNet Retrieve prefix CorpNet from Edge Edge01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$Name, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$Network ) begin { } process { #We append the Edge-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_GlobalRoutingConfig = ($EdgeRouting.routingGlobalConfig.CloneNode($True)) $IpPrefixes = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_GlobalRoutingConfig -query 'child::ipPrefixes') #IPPrefixes may not exist... if ( $IPPrefixes ) { #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called ipPrefix. If ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $IpPrefixes -query 'child::ipPrefix')) { $PrefixCollection = $IPPrefixes.ipPrefix if ( $PsBoundParameters.ContainsKey('Network')) { $PrefixCollection = $PrefixCollection | Where-Object { $_.ipAddress -eq $Network } } if ( $PsBoundParameters.ContainsKey('Name')) { $PrefixCollection = $PrefixCollection | Where-Object { $_.name -eq $Name } } foreach ( $Prefix in $PrefixCollection ) { Add-XmlElement -xmlRoot $Prefix -xmlElementName "edgeId" -xmlElementText $EdgeRouting.EdgeId } $PrefixCollection } } } end {} } function New-NsxEdgePrefix { <# .SYNOPSIS Creates a new IP prefix and adds it to the specified ESGs routing configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The New-NsxEdgePrefix cmdlet adds a new IP prefix to the routing configuration of the specified Edge Services Gateway. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting | New-NsxEdgePrefix -Name test -Network 1.1.1.0/24 Create a new prefix called test for network 1.1.1.0/24 on ESG Edge01 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $False)] [ValidateNotNullorEmpty()] [switch]$Confirm = $true, [Parameter (Mandatory = $True)] [ValidateNotNullorEmpty()] [string]$Name, [Parameter (Mandatory = $True)] [ValidateNotNullorEmpty()] [string]$Network, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_EdgeRouting = $EdgeRouting.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeRouting.edgeId $_EdgeRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'child::edgeId')) ) | Out-Null #Need to do an xpath query here rather than use PoSH dot notation to get the IP prefix element, #as it might be empty or not exist, and PoSH silently turns an empty element into a string object, which is rather not what we want... :| $ipPrefixes = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting.routingGlobalConfig -query 'child::ipPrefixes') if ( -not $ipPrefixes ) { #Create the ipPrefixes element $ipPrefixes = $_EdgeRouting.ownerDocument.CreateElement('ipPrefixes') $_EdgeRouting.routingGlobalConfig.AppendChild($ipPrefixes) | Out-Null } #Create the new ipPrefix element. $ipPrefix = $_EdgeRouting.ownerDocument.CreateElement('ipPrefix') $ipPrefixes.AppendChild($ipPrefix) | Out-Null Add-XmlElement -xmlRoot $ipPrefix -xmlElementName "name" -xmlElementText $Name.ToString() Add-XmlElement -xmlRoot $ipPrefix -xmlElementName "ipAddress" -xmlElementText $Network.ToString() $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $_EdgeRouting.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeRouting | Get-NsxEdgePrefix -Network $Network -Name $Name } } end {} } function Remove-NsxEdgePrefix { <# .SYNOPSIS Removes an IP prefix from the specified ESGs routing configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Remove-NsxEdgePrefix cmdlet removes a IP prefix from the routing configuration of the specified Edge Services Gateway. Prefixes to be removed can be constructed via a PoSH pipline filter outputing prefix objects as produced by Get-NsxEdgePrefix and passing them on the pipeline to Remove-NsxEdgePrefix. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgePrefix -Network 1.1.1.0/24 | Remove-NsxEdgePrefix Remove any prefixes for network 1.1.1.0/24 from ESG Edge01 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgePrefix $_ })] [System.Xml.XmlElement]$Prefix, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our Edge $edgeId = $Prefix.edgeId $routing = Get-NsxEdge -objectId $edgeId -Connection $connection | Get-NsxEdgeRouting #Remove the edgeId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'child::edgeId')) ) | Out-Null #Need to do an xpath query here to query for a prefix that matches the one passed in. #Union of nextHop and network should be unique $xpathQuery = "/routingGlobalConfig/ipPrefixes/ipPrefix[name=`"$($Prefix.name)`" and ipAddress=`"$($Prefix.ipAddress)`"]" Write-Debug "XPath query for prefix nodes to remove is: $xpathQuery" $PrefixToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query $xpathQuery) if ( $PrefixToRemove ) { Write-Debug "PrefixToRemove Element is: `n $($PrefixToRemove.OuterXml | Format-XML) " $routing.routingGlobalConfig.ipPrefixes.RemoveChild($PrefixToRemove) | Out-Null $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed } } else { Throw "Prefix $($Prefix.Name) for network $($Prefix.network) was not found in routing configuration for Edge $edgeId" } } end {} } # BGP function Get-NsxEdgeBgp { <# .SYNOPSIS Retrieves BGP configuration for the specified NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Get-NsxEdgeBgp cmdlet retrieves the bgp configuration of the specified Edge Services Gateway. .EXAMPLE Get the BGP configuration for Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeBgp #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting ) begin { } process { #We append the Edge-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $EdgeRouting -query 'descendant::bgp')) { $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $EdgeRouting -query 'child::bgp').CloneNode($True) Add-XmlElement -xmlRoot $bgp -xmlElementName "edgeId" -xmlElementText $EdgeRouting.EdgeId $bgp } } end {} } function Set-NsxEdgeBgp { <# .SYNOPSIS Manipulates BGP specific base configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Set-NsxEdgeBgp cmdlet allows manipulation of the BGP specific configuration of a given ESG. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$EnableBGP, [Parameter (Mandatory = $False)] [IpAddress]$RouterId, [Parameter (Mandatory = $False)] [ValidateRange(0, 65535)] [int]$LocalAS, [Parameter (Mandatory = $False)] [switch]$GracefulRestart, [Parameter (Mandatory = $False)] [switch]$DefaultOriginate, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_EdgeRouting = $EdgeRouting.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeRouting.edgeId $_EdgeRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::edgeId')) ) | Out-Null #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. $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::bgp') if ( -not $bgp ) { #bgp node does not exist. [System.XML.XMLElement]$bgp = $_EdgeRouting.ownerDocument.CreateElement("bgp") $_EdgeRouting.appendChild($bgp) | Out-Null } # Check bgp enablement if ($PsBoundParameters.ContainsKey('EnableBGP')) { # BGP option is specified if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::enabled')) { #Enabled element exists. Update it. $bgp.enabled = $EnableBGP.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "enabled" -xmlElementText $EnableBGP.ToString().ToLower() } } elseif (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::enabled') { # BGP option is not specified but enabled if ( $bgp.enabled -eq 'true' ) { # Assume bgp is already enabled. } else { throw "EnableBGP is not specified or BGP is not enabled on edge $edgeID. Please specify option EnableBGP" } } else { throw "EnableBGP is not specified or BGP is not enabled on edge $edgeID. Please specify option EnableBGP" } $xmlGlobalConfig = $_EdgeRouting.routingGlobalConfig $xmlRouterId = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $xmlGlobalConfig -query 'descendant::routerId') if ( $EnableBGP ) { if ( -not ($xmlRouterId -or $PsBoundParameters.ContainsKey("RouterId"))) { #Existing config missing and no new value set... throw "RouterId must be configured to enable dynamic routing" } if ($PsBoundParameters.ContainsKey("RouterId")) { #Set Routerid... if ($xmlRouterId) { $xmlRouterId = $RouterId.IPAddresstoString } else { Add-XmlElement -xmlRoot $xmlGlobalConfig -xmlElementName "routerId" -xmlElementText $RouterId.IPAddresstoString } } } if ( $PsBoundParameters.ContainsKey("LocalAS")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::localAS')) { #LocalAS element exists, update it. $bgp.localAS = $LocalAS.ToString() } else { #LocalAS element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "localAS" -xmlElementText $LocalAS.ToString() } } elseif ( (-not ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::localAS')) -and $EnableBGP )) { throw "Existing configuration has no Local AS number specified. Local AS must be set to enable BGP." } if ( $PsBoundParameters.ContainsKey("GracefulRestart")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::gracefulRestart')) { #element exists, update it. $bgp.gracefulRestart = $GracefulRestart.ToString().ToLower() } else { #element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "gracefulRestart" -xmlElementText $GracefulRestart.ToString().ToLower() } } if ( $PsBoundParameters.ContainsKey("DefaultOriginate")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::defaultOriginate')) { #element exists, update it. $bgp.defaultOriginate = $DefaultOriginate.ToString().ToLower() } else { #element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "defaultOriginate" -xmlElementText $DefaultOriginate.ToString().ToLower() } } $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $_EdgeRouting.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeRouting | Get-NsxEdgeBgp } } end {} } function Get-NsxEdgeBgpNeighbour { <# .SYNOPSIS Returns BGP neighbours from the specified NSX Edge Services Gateway BGP configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Get-NsxEdgeBgpNeighbour cmdlet retrieves the BGP neighbours from the BGP configuration specified. .EXAMPLE Get all BGP neighbours defined on Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeBgpNeighbour .EXAMPLE Get BGP neighbour 1.1.1.1 defined on Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeBgpNeighbour -IpAddress 1.1.1.1 .EXAMPLE Get all BGP neighbours with Remote AS 1234 defined on Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeBgpNeighbour | where-object { $_.RemoteAS -eq '1234' } #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$Network, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [ipAddress]$IpAddress, [Parameter (Mandatory = $false)] [ValidateRange(0, 65535)] [int]$RemoteAS ) begin { } process { $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $EdgeRouting -query 'descendant::bgp') if ( $bgp ) { $_bgp = $bgp.CloneNode($True) $BgpNeighbours = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_bgp -query 'descendant::bgpNeighbours') #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called bgpNeighbour. if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $BgpNeighbours -query 'descendant::bgpNeighbour')) { $NeighbourCollection = $BgpNeighbours.bgpNeighbour if ( $PsBoundParameters.ContainsKey('IpAddress')) { $NeighbourCollection = $NeighbourCollection | Where-Object { $_.ipAddress -eq $IpAddress } } if ( $PsBoundParameters.ContainsKey('RemoteAS')) { $NeighbourCollection = $NeighbourCollection | Where-Object { $_.remoteAS -eq $RemoteAS } } foreach ( $Neighbour in $NeighbourCollection ) { #We append the Edge-id to the associated neighbour config XML to enable pipeline workflows and #consistent readable output Add-XmlElement -xmlRoot $Neighbour -xmlElementName "edgeId" -xmlElementText $EdgeRouting.EdgeId } $NeighbourCollection } } } end {} } function New-NsxEdgeBgpNeighbour { <# .SYNOPSIS Creates a new BGP neighbour and adds it to the specified ESGs BGP configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The New-NsxEdgeBgpNeighbour cmdlet adds a new BGP neighbour to the bgp configuration of the specified Edge Services Gateway. .EXAMPLE Add a new neighbour 1.2.3.4 with remote AS number 1234 with defaults. PS C:\> Get-NsxEdge | Get-NsxEdgeRouting | New-NsxEdgeBgpNeighbour -IpAddress 1.2.3.4 -RemoteAS 1234 .EXAMPLE Add a new neighbour 1.2.3.4 with remote AS number 22235 specifying weight, holddown and keepalive timers and dont prompt for confirmation. PowerCLI C:\> Get-NsxEdge | Get-NsxEdgeRouting | New-NsxEdgeBgpNeighbour -IpAddress 1.2.3.4 -RemoteAS 22235 -Confirm:$false -Weight 90 -HoldDownTimer 240 -KeepAliveTimer 90 -confirm:$false #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $true)] [ValidateNotNullorEmpty()] [ipAddress]$IpAddress, [Parameter (Mandatory = $true)] [ValidateRange(0, 65535)] [int]$RemoteAS, [Parameter (Mandatory = $false)] [ValidateRange(0, 65535)] [int]$Weight, [Parameter (Mandatory = $false)] [ValidateRange(2, 65535)] [int]$HoldDownTimer, [Parameter (Mandatory = $false)] [ValidateRange(1, 65534)] [int]$KeepAliveTimer, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [string]$Password, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_EdgeRouting = $EdgeRouting.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeRouting.edgeId $_EdgeRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::edgeId')) ) | Out-Null #Create the new bgpNeighbour element. $Neighbour = $_EdgeRouting.ownerDocument.CreateElement('bgpNeighbour') #Need to do an xpath query here rather than use PoSH dot notation to get the bgp element, #as it might not exist which wil cause PoSH to throw in stric mode. $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::bgp') if ( $bgp ) { (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::bgpNeighbours').AppendChild($Neighbour) | Out-Null Add-XmlElement -xmlRoot $Neighbour -xmlElementName "ipAddress" -xmlElementText $IpAddress.ToString() Add-XmlElement -xmlRoot $Neighbour -xmlElementName "remoteAS" -xmlElementText $RemoteAS.ToString() #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("Weight") ) { Add-XmlElement -xmlRoot $Neighbour -xmlElementName "weight" -xmlElementText $Weight.ToString() } if ( $PsBoundParameters.ContainsKey("HoldDownTimer") ) { Add-XmlElement -xmlRoot $Neighbour -xmlElementName "holdDownTimer" -xmlElementText $HoldDownTimer.ToString() } if ( $PsBoundParameters.ContainsKey("KeepAliveTimer") ) { Add-XmlElement -xmlRoot $Neighbour -xmlElementName "keepAliveTimer" -xmlElementText $KeepAliveTimer.ToString() } if ( $PsBoundParameters.ContainsKey("Password") ) { Add-XmlElement -xmlRoot $Neighbour -xmlElementName "password" -xmlElementText $Password.ToString() } $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $_EdgeRouting.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeRouting | Get-NsxEdgeBgpNeighbour -IpAddress $IpAddress -RemoteAS $RemoteAS } } else { throw "BGP is not enabled on edge $edgeID. Enable BGP using Set-NsxEdgeRouting or Set-NsxEdgeBGP first." } } end {} } function Remove-NsxEdgeBgpNeighbour { <# .SYNOPSIS Removes a BGP neigbour from the specified ESGs BGP configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Remove-NsxEdgeBgpNeighbour cmdlet removes a BGP neighbour route from the bgp configuration of the specified Edge Services Gateway. Neighbours to be removed can be constructed via a PoSH pipline filter outputing neighbour objects as produced by Get-NsxEdgeBgpNeighbour and passing them on the pipeline to Remove-NsxEdgeBgpNeighbour. .EXAMPLE Remove the BGP neighbour 1.1.1.2 from the the edge Edge01's bgp configuration PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeBgpNeighbour | where-object { $_.ipaddress -eq '1.1.1.2' } | Remove-NsxEdgeBgpNeighbour #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeBgpNeighbour $_ })] [System.Xml.XmlElement]$BgpNeighbour, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our Edge $edgeId = $BgpNeighbour.edgeId $routing = Get-NsxEdge -objectId $edgeId | Get-NsxEdgeRouting #Remove the edgeId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'descendant::edgeId')) ) | Out-Null #Validate the BGP node exists on the edge if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'descendant::bgp')) { throw "BGP is not enabled on ESG $edgeId. Enable BGP and try again." } #Need to do an xpath query here to query for a bgp neighbour that matches the one passed in. #Union of ipaddress and remote AS should be unique (though this is not enforced by the API, #I cant see why having duplicate neighbours with same ip and AS would be useful...maybe #different filters?) #Will probably need to include additional xpath query filters here in the query to include #matching on filters to better handle uniquness amongst bgp neighbours with same ip and remoteAS $xpathQuery = "//bgpNeighbours/bgpNeighbour[ipAddress=`"$($BgpNeighbour.ipAddress)`" and remoteAS=`"$($BgpNeighbour.remoteAS)`"]" Write-Debug "XPath query for neighbour nodes to remove is: $xpathQuery" $NeighbourToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing.bgp -query $xpathQuery) if ( $NeighbourToRemove ) { Write-Debug "NeighbourToRemove Element is: `n $($NeighbourToRemove.OuterXml | Format-XML) " $routing.bgp.bgpNeighbours.RemoveChild($NeighbourToRemove) | Out-Null $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed } } else { Throw "Neighbour $($BgpNeighbour.ipAddress) with Remote AS $($BgpNeighbour.RemoteAS) was not found in routing configuration for Edge $edgeId" } } end {} } # OSPF function Get-NsxEdgeOspf { <# .SYNOPSIS Retrieves OSPF configuration for the specified NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Get-NsxEdgeOspf cmdlet retrieves the OSPF configuration of the specified Edge Services Gateway. .EXAMPLE Get the OSPF configuration for Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeOspf #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting ) begin { } process { #We append the Edge-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $EdgeRouting -query 'descendant::ospf')) { $ospf = $EdgeRouting.ospf.CloneNode($True) Add-XmlElement -xmlRoot $ospf -xmlElementName "edgeId" -xmlElementText $EdgeRouting.EdgeId $ospf } } end {} } function Set-NsxEdgeOspf { <# .SYNOPSIS Manipulates OSPF specific base configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Set-NsxEdgeOspf cmdlet allows manipulation of the OSPF specific configuration of a given ESG. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$EnableOSPF, [Parameter (Mandatory = $False)] [IpAddress]$RouterId, [Parameter (Mandatory = $False)] [switch]$GracefulRestart, [Parameter (Mandatory = $False)] [switch]$DefaultOriginate, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_EdgeRouting = $EdgeRouting.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeRouting.edgeId $_EdgeRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::edgeId')) ) | Out-Null #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. $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::ospf') if ( -not $ospf ) { #ospf node does not exist. [System.XML.XMLElement]$ospf = $_EdgeRouting.ownerDocument.CreateElement("ospf") $_EdgeRouting.appendChild($ospf) | Out-Null } # Check ospf enablemant if ($PsBoundParameters.ContainsKey('EnableOSPF')) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'descendant::enabled')) { #Enabled element exists. Update it. $ospf.enabled = $EnableOSPF.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "enabled" -xmlElementText $EnableOSPF.ToString().ToLower() } } elseif (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'descendant::enabled') { # OSPF option is not specified but enabled if ( $ospf.enabled -eq 'true' ) { # Assume ospf is already enabled. } else { throw "EnableOSPF is not specified or BGP is not enabled on edge $edgeID. Please specify option EnableOSPF" } } else { throw "EnableOSPF is not specified or BGP is not enabled on edge $edgeID. Please specify option EnableOSFP" } $xmlGlobalConfig = $_EdgeRouting.routingGlobalConfig $xmlRouterId = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $xmlGlobalConfig -query 'descendant::routerId') if ( $EnableOSPF ) { if ( -not ($xmlRouterId -or $PsBoundParameters.ContainsKey("RouterId"))) { #Existing config missing and no new value set... throw "RouterId must be configured to enable dynamic routing" } if ($PsBoundParameters.ContainsKey("RouterId")) { #Set Routerid... if ($xmlRouterId) { $xmlRouterId = $RouterId.IPAddresstoString } else { Add-XmlElement -xmlRoot $xmlGlobalConfig -xmlElementName "routerId" -xmlElementText $RouterId.IPAddresstoString } } } if ( $PsBoundParameters.ContainsKey("GracefulRestart")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'descendant::gracefulRestart')) { #element exists, update it. $ospf.gracefulRestart = $GracefulRestart.ToString().ToLower() } else { #element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "gracefulRestart" -xmlElementText $GracefulRestart.ToString().ToLower() } } if ( $PsBoundParameters.ContainsKey("DefaultOriginate")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'descendant::defaultOriginate')) { #element exists, update it. $ospf.defaultOriginate = $DefaultOriginate.ToString().ToLower() } else { #element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "defaultOriginate" -xmlElementText $DefaultOriginate.ToString().ToLower() } } $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $_EdgeRouting.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeRouting | Get-NsxEdgeOspf } } end {} } function Get-NsxEdgeOspfArea { <# .SYNOPSIS Returns OSPF Areas defined in the specified NSX Edge Services Gateway OSPF configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Get-NsxEdgeOspfArea cmdlet retrieves the OSPF Areas from the OSPF configuration specified. .EXAMPLE Get all areas defined on Edge01. PS C:\> C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeOspfArea #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $false)] [ValidateRange(0, 4294967295)] [int]$AreaId ) begin { } process { $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $EdgeRouting -query 'descendant::ospf') if ( $ospf ) { $_ospf = $ospf.CloneNode($True) $OspfAreas = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ospf -query 'descendant::ospfAreas') #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called ospfArea. if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $OspfAreas -query 'descendant::ospfArea')) { $AreaCollection = $OspfAreas.ospfArea if ( $PsBoundParameters.ContainsKey('AreaId')) { $AreaCollection = $AreaCollection | Where-Object { $_.areaId -eq $AreaId } } foreach ( $Area in $AreaCollection ) { #We append the Edge-id to the associated Area config XML to enable pipeline workflows and #consistent readable output Add-XmlElement -xmlRoot $Area -xmlElementName "edgeId" -xmlElementText $EdgeRouting.EdgeId } $AreaCollection } } } end {} } function Remove-NsxEdgeOspfArea { <# .SYNOPSIS Removes an OSPF area from the specified ESGs OSPF configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Remove-NsxEdgeOspfArea cmdlet removes a BGP neighbour route from the bgp configuration of the specified Edge Services Gateway. Areas to be removed can be constructed via a PoSH pipline filter outputing area objects as produced by Get-NsxEdgeOspfArea and passing them on the pipeline to Remove-NsxEdgeOspfArea. .EXAMPLE Remove area 51 from ospf configuration on Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeOspfArea -AreaId 51 | Remove-NsxEdgeOspfArea #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeOspfArea $_ })] [System.Xml.XmlElement]$OspfArea, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our Edge $edgeId = $OspfArea.edgeId $routing = Get-NsxEdge -objectId $edgeId -Connection $connection | Get-NsxEdgeRouting #Remove the edgeId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'descendant::edgeId')) ) | Out-Null #Validate the OSPF node exists on the edge if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'descendant::ospf')) { throw "OSPF is not enabled on ESG $edgeId. Enable OSPF and try again." } if ( -not ($routing.ospf.enabled -eq 'true') ) { throw "OSPF is not enabled on ESG $edgeId. Enable OSPF and try again." } $xpathQuery = "//ospfAreas/ospfArea[areaId=`"$($OspfArea.areaId)`"]" Write-Debug "XPath query for area nodes to remove is: $xpathQuery" $AreaToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing.ospf -query $xpathQuery) if ( $AreaToRemove ) { Write-Debug "AreaToRemove Element is: `n $($AreaToRemove.OuterXml | Format-XML) " $routing.ospf.ospfAreas.RemoveChild($AreaToRemove) | Out-Null $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed } } else { Throw "Area $($OspfArea.areaId) was not found in routing configuration for Edge $edgeId" } } end {} } function New-NsxEdgeOspfArea { <# .SYNOPSIS Creates a new OSPF Area and adds it to the specified ESGs OSPF configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The New-NsxEdgeOspfArea cmdlet adds a new OSPF Area to the ospf configuration of the specified Edge Services Gateway. .EXAMPLE Create area 50 as a normal type on ESG Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | New-NsxEdgeOspfArea -AreaId 50 .EXAMPLE Create area 10 as a nssa type on ESG Edge01 with password authentication PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | New-NsxEdgeOspfArea -AreaId 10 -Type password -Password "Secret" #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $true)] [ValidateRange(0, 4294967295)] [uint32]$AreaId, [Parameter (Mandatory = $false)] [ValidateSet("normal", "nssa", IgnoreCase = $false)] [string]$Type, [Parameter (Mandatory = $false)] [ValidateSet("none", "password", "md5", IgnoreCase = $false)] [string]$AuthenticationType = "none", [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [string]$Password, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_EdgeRouting = $EdgeRouting.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeRouting.edgeId $_EdgeRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::edgeId')) ) | Out-Null #Create the new ospfArea element. $Area = $_EdgeRouting.ownerDocument.CreateElement('ospfArea') #Need to do an xpath query here rather than use PoSH dot notation to get the ospf element, #as it might not exist which wil cause PoSH to throw in stric mode. $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::ospf') if ( $ospf ) { (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'descendant::ospfAreas').AppendChild($Area) | Out-Null Add-XmlElement -xmlRoot $Area -xmlElementName "areaId" -xmlElementText $AreaId.ToString() #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("Type") ) { Add-XmlElement -xmlRoot $Area -xmlElementName "type" -xmlElementText $Type.ToString() } if ( $PsBoundParameters.ContainsKey("AuthenticationType") -or $PsBoundParameters.ContainsKey("Password") ) { switch ($AuthenticationType) { "none" { if ( $PsBoundParameters.ContainsKey('Password') ) { throw "Authentication type must be other than none to specify a password." } #Default value - do nothing } default { if ( -not ( $PsBoundParameters.ContainsKey('Password')) ) { throw "Must specify a password if Authentication type is not none." } $Authentication = $Area.ownerDocument.CreateElement("authentication") $Area.AppendChild( $Authentication ) | Out-Null Add-XmlElement -xmlRoot $Authentication -xmlElementName "type" -xmlElementText $AuthenticationType Add-XmlElement -xmlRoot $Authentication -xmlElementName "value" -xmlElementText $Password } } } $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $_EdgeRouting.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeRouting | Get-NsxEdgeOspfArea -AreaId $AreaId } } else { throw "OSPF is not enabled on edge $edgeID. Enable OSPF using Set-NsxEdgeRouting or Set-NsxEdgeOSPF first." } } end {} } function Get-NsxEdgeOspfInterface { <# .SYNOPSIS Returns OSPF Interface mappings defined in the specified NSX Edge Services Gateway OSPF configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Get-NsxEdgeOspfInterface cmdlet retrieves the OSPF Area to interfaces mappings from the OSPF configuration specified. .EXAMPLE Get all OSPF Area to Interface mappings on ESG Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeOspfInterface .EXAMPLE Get OSPF Area to Interface mapping for Area 10 on ESG Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeOspfInterface -AreaId 10 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $false)] [ValidateRange(0, 4294967295)] [int]$AreaId, [Parameter (Mandatory = $false)] [ValidateRange(0, 200)] [int]$vNicId ) begin { } process { $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $EdgeRouting -query 'descendant::ospf') if ( $ospf ) { $_ospf = $ospf.CloneNode($True) $OspfInterfaces = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ospf -query 'descendant::ospfInterfaces') #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called ospfArea. if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $OspfInterfaces -query 'descendant::ospfInterface')) { $InterfaceCollection = $OspfInterfaces.ospfInterface if ( $PsBoundParameters.ContainsKey('AreaId')) { $InterfaceCollection = $InterfaceCollection | Where-Object { $_.areaId -eq $AreaId } } if ( $PsBoundParameters.ContainsKey('vNicId')) { $InterfaceCollection = $InterfaceCollection | Where-Object { $_.vnic -eq $vNicId } } foreach ( $Interface in $InterfaceCollection ) { #We append the Edge-id to the associated Area config XML to enable pipeline workflows and #consistent readable output Add-XmlElement -xmlRoot $Interface -xmlElementName "edgeId" -xmlElementText $EdgeRouting.EdgeId } $InterfaceCollection } } } end {} } function Remove-NsxEdgeOspfInterface { <# .SYNOPSIS Removes an OSPF Interface from the specified ESGs OSPF configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Remove-NsxEdgeOspfInterface cmdlet removes a BGP neighbour route from the bgp configuration of the specified Edge Services Gateway. Interfaces to be removed can be constructed via a PoSH pipline filter outputing interface objects as produced by Get-NsxEdgeOspfInterface and passing them on the pipeline to Remove-NsxEdgeOspfInterface. .EXAMPLE Remove Interface to Area mapping for area 51 from ESG Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeOspfInterface -AreaId 51 | Remove-NsxEdgeOspfInterface .EXAMPLE Remove all Interface to Area mappings from ESG Edge01 without confirmation. PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeOspfInterface | Remove-NsxEdgeOspfInterface -confirm:$false #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeOspfInterface $_ })] [System.Xml.XmlElement]$OspfInterface, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our Edge $edgeId = $OspfInterface.edgeId $routing = Get-NsxEdge -objectId $edgeId -Connection $connection | Get-NsxEdgeRouting #Remove the edgeId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'descendant::edgeId')) ) | Out-Null #Validate the OSPF node exists on the edge if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'descendant::ospf')) { throw "OSPF is not enabled on ESG $edgeId. Enable OSPF and try again." } if ( -not ($routing.ospf.enabled -eq 'true') ) { throw "OSPF is not enabled on ESG $edgeId. Enable OSPF and try again." } $xpathQuery = "//ospfInterfaces/ospfInterface[areaId=`"$($OspfInterface.areaId)`"]" Write-Debug "XPath query for interface nodes to remove is: $xpathQuery" $InterfaceToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing.ospf -query $xpathQuery) if ( $InterfaceToRemove ) { Write-Debug "InterfaceToRemove Element is: `n $($InterfaceToRemove.OuterXml | Format-XML) " $routing.ospf.ospfInterfaces.RemoveChild($InterfaceToRemove) | Out-Null $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed } } else { Throw "Interface $($OspfInterface.areaId) was not found in routing configuration for Edge $edgeId" } } end {} } function New-NsxEdgeOspfInterface { <# .SYNOPSIS Creates a new OSPF Interface to Area mapping and adds it to the specified ESGs OSPF configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The New-NsxEdgeOspfInterface cmdlet adds a new OSPF Area to Interface mapping to the ospf configuration of the specified Edge Services Gateway. .EXAMPLE Add a mapping for Area 10 to Interface 0 on ESG Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | New-NsxEdgeOspfInterface -AreaId 10 -Vnic 0 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $true)] [ValidateRange(0, 4294967295)] [uint32]$AreaId, [Parameter (Mandatory = $true)] [ValidateRange(0, 200)] [int]$Vnic, [Parameter (Mandatory = $false)] [ValidateRange(1, 255)] [int]$HelloInterval, [Parameter (Mandatory = $false)] [ValidateRange(1, 65535)] [int]$DeadInterval, [Parameter (Mandatory = $false)] [ValidateRange(0, 255)] [int]$Priority, [Parameter (Mandatory = $false)] [ValidateRange(1, 65535)] [int]$Cost, [Parameter (Mandatory = $false)] [switch]$IgnoreMTU, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_EdgeRouting = $EdgeRouting.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeRouting.edgeId $_EdgeRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::edgeId')) ) | Out-Null #Create the new ospfInterface element. $Interface = $_EdgeRouting.ownerDocument.CreateElement('ospfInterface') #Need to do an xpath query here rather than use PoSH dot notation to get the ospf element, #as it might not exist which wil cause PoSH to throw in stric mode. $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'descendant::ospf') if ( $ospf ) { (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'descendant::ospfInterfaces').AppendChild($Interface) | Out-Null Add-XmlElement -xmlRoot $Interface -xmlElementName "areaId" -xmlElementText $AreaId.ToString() Add-XmlElement -xmlRoot $Interface -xmlElementName "vnic" -xmlElementText $Vnic.ToString() #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("HelloInterval") ) { Add-XmlElement -xmlRoot $Interface -xmlElementName "helloInterval" -xmlElementText $HelloInterval.ToString() } if ( $PsBoundParameters.ContainsKey("DeadInterval") ) { Add-XmlElement -xmlRoot $Interface -xmlElementName "deadInterval" -xmlElementText $DeadInterval.ToString() } if ( $PsBoundParameters.ContainsKey("Priority") ) { Add-XmlElement -xmlRoot $Interface -xmlElementName "priority" -xmlElementText $Priority.ToString() } if ( $PsBoundParameters.ContainsKey("Cost") ) { Add-XmlElement -xmlRoot $Interface -xmlElementName "cost" -xmlElementText $Cost.ToString() } if ( $PsBoundParameters.ContainsKey("IgnoreMTU") ) { Add-XmlElement -xmlRoot $Interface -xmlElementName "mtuIgnore" -xmlElementText $IgnoreMTU.ToString().ToLower() } $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $_EdgeRouting.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeRouting | Get-NsxEdgeOspfInterface -AreaId $AreaId } } else { throw "OSPF is not enabled on edge $edgeID. Enable OSPF using Set-NsxEdgeRouting or Set-NsxEdgeOSPF first." } } end {} } # Redistribution Rules function Get-NsxEdgeRedistributionRule { <# .SYNOPSIS Returns dynamic route redistribution rules defined in the specified NSX Edge Services Gateway routing configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Get-NsxEdgeRedistributionRule cmdlet retrieves the route redistribution rules defined in the ospf and bgp configurations for the specified ESG. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeRedistributionRule -Learner ospf Get all Redistribution rules for ospf on ESG Edge01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $false)] [ValidateSet("ospf", "bgp")] [string]$Learner, [Parameter (Mandatory = $false)] [int]$Id ) begin { } process { #Rules can be defined in either ospf or bgp (isis as well, but who cares huh? :) ) if ( ( -not $PsBoundParameters.ContainsKey('Learner')) -or ($PsBoundParameters.ContainsKey('Learner') -and $Learner -eq 'ospf')) { $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $EdgeRouting -query 'child::ospf') if ( $ospf ) { $_ospf = $ospf.CloneNode($True) if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ospf -query 'child::redistribution/rules/rule') ) { $OspfRuleCollection = $_ospf.redistribution.rules.rule foreach ( $rule in $OspfRuleCollection ) { #We append the Edge-id to the associated rule config XML to enable pipeline workflows and #consistent readable output Add-XmlElement -xmlRoot $rule -xmlElementName "edgeId" -xmlElementText $EdgeRouting.EdgeId #Add the learner to be consistent with the view the UI gives Add-XmlElement -xmlRoot $rule -xmlElementName "learner" -xmlElementText "ospf" } if ( $PsBoundParameters.ContainsKey('Id')) { $OspfRuleCollection = $OspfRuleCollection | Where-Object { $_.id -eq $Id } } $OspfRuleCollection } } } if ( ( -not $PsBoundParameters.ContainsKey('Learner')) -or ($PsBoundParameters.ContainsKey('Learner') -and $Learner -eq 'bgp')) { $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $EdgeRouting -query 'child::bgp') if ( $bgp ) { $_bgp = $bgp.CloneNode($True) if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_bgp -query 'child::redistribution/rules/rule') ) { $BgpRuleCollection = $_bgp.redistribution.rules.rule foreach ( $rule in $BgpRuleCollection ) { #We append the Edge-id to the associated rule config XML to enable pipeline workflows and #consistent readable output Add-XmlElement -xmlRoot $rule -xmlElementName "edgeId" -xmlElementText $EdgeRouting.EdgeId #Add the learner to be consistent with the view the UI gives Add-XmlElement -xmlRoot $rule -xmlElementName "learner" -xmlElementText "bgp" } if ( $PsBoundParameters.ContainsKey('Id')) { $BgpRuleCollection = $BgpRuleCollection | Where-Object { $_.id -eq $Id } } $BgpRuleCollection } } } } end {} } function Remove-NsxEdgeRedistributionRule { <# .SYNOPSIS Removes a route redistribution rule from the specified ESGs configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The Remove-NsxEdgeRedistributionRule cmdlet removes a route redistribution rule from the configuration of the specified Edge Services Gateway. Interfaces to be removed can be constructed via a PoSH pipline filter outputing interface objects as produced by Get-NsxEdgeRedistributionRule and passing them on the pipeline to Remove-NsxEdgeRedistributionRule. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeRedistributionRule -Learner ospf | Remove-NsxEdgeRedistributionRule Remove all ospf redistribution rules from Edge01 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeRedistributionRule $_ })] [System.Xml.XmlElement]$RedistributionRule, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our Edge $edgeId = $RedistributionRule.edgeId $routing = Get-NsxEdge -objectId $edgeId -Connection $connection | Get-NsxEdgeRouting #Remove the edgeId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'child::edgeId')) ) | Out-Null #Validate the learner protocol node exists on the edge if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query "child::$($RedistributionRule.learner)")) { throw "Rule learner protocol $($RedistributionRule.learner) is not enabled on ESG $edgeId. Use Get-NsxEdge | Get-NsxEdgerouting | Get-NsxEdgeRedistributionRule to get the rule you want to remove." } #Make XPath do all the hard work... Wish I was able to just compare the from node, but id doesnt appear possible with xpath 1.0 $xpathQuery = "child::$($RedistributionRule.learner)/redistribution/rules/rule[action=`"$($RedistributionRule.action)`"" $xPathQuery += " and from/connected=`"$($RedistributionRule.from.connected)`" and from/static=`"$($RedistributionRule.from.static)`"" $xPathQuery += " and from/ospf=`"$($RedistributionRule.from.ospf)`" and from/bgp=`"$($RedistributionRule.from.bgp)`"" if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $RedistributionRule -query 'child::from/isis')) { $xPathQuery += " and from/isis=`"$($RedistributionRule.from.isis)`"" } if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $RedistributionRule -query 'child::prefixName')) { $xPathQuery += " and prefixName=`"$($RedistributionRule.prefixName)`"" } $xPathQuery += "]" Write-Debug "XPath query for rule node to remove is: $xpathQuery" $RuleToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query $xpathQuery) if ( $RuleToRemove ) { Write-Debug "RuleToRemove Element is: `n $($RuleToRemove | Format-XML) " $routing.$($RedistributionRule.Learner).redistribution.rules.RemoveChild($RuleToRemove) | Out-Null $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed } } else { Throw "Rule Id $($RedistributionRule.Id) was not found in the $($RedistributionRule.Learner) routing configuration for Edge $edgeId" } } end {} } function New-NsxEdgeRedistributionRule { <# .SYNOPSIS Creates a new route redistribution rule and adds it to the specified ESGs configuration. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. ESGs perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF, ISIS and BGP. The New-NsxEdgeRedistributionRule cmdlet adds a new route redistribution rule to the configuration of the specified Edge Services Gateway. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxEdgeRouting | New-NsxEdgeRedistributionRule -PrefixName test -Learner ospf -FromConnected -FromStatic -Action permit Create a new permit Redistribution Rule for prefix test (note, prefix must already exist, and is case sensistive) for ospf. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdgeRouting $_ })] [System.Xml.XmlElement]$EdgeRouting, [Parameter (Mandatory = $True)] [ValidateSet("ospf", "bgp", IgnoreCase = $false)] [String]$Learner, [Parameter (Mandatory = $false)] [String]$PrefixName, [Parameter (Mandatory = $false)] [switch]$FromConnected, [Parameter (Mandatory = $false)] [switch]$FromStatic, [Parameter (Mandatory = $false)] [switch]$FromOspf, [Parameter (Mandatory = $false)] [switch]$FromBgp, [Parameter (Mandatory = $False)] [ValidateSet("permit", "deny", IgnoreCase = $false)] [String]$Action = "permit", [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_EdgeRouting = $EdgeRouting.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_EdgeRouting.edgeId $_EdgeRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query 'child::edgeId')) ) | Out-Null #Need to do an xpath query here rather than use PoSH dot notation to get the protocol element, #as it might not exist which wil cause PoSH to throw in stric mode. $ProtocolElement = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_EdgeRouting -query "child::$Learner") if ( (-not $ProtocolElement) -or ($ProtocolElement.Enabled -ne 'true')) { throw "The $Learner protocol is not enabled on Edge $edgeId. Enable it and try again." } else { #Create the new rule element. $Rule = $_EdgeRouting.ownerDocument.CreateElement('rule') (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ProtocolElement -query 'child::redistribution/rules').AppendChild($Rule) | Out-Null Add-XmlElement -xmlRoot $Rule -xmlElementName "action" -xmlElementText $Action if ( $PsBoundParameters.ContainsKey("PrefixName") ) { Add-XmlElement -xmlRoot $Rule -xmlElementName "prefixName" -xmlElementText $PrefixName.ToString() } #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('FromConnected') -or $PsBoundParameters.ContainsKey('FromStatic') -or $PsBoundParameters.ContainsKey('FromOspf') -or $PsBoundParameters.ContainsKey('FromBgp') ) { $FromElement = $Rule.ownerDocument.CreateElement('from') $Rule.AppendChild($FromElement) | Out-Null if ( $PsBoundParameters.ContainsKey("FromConnected") ) { Add-XmlElement -xmlRoot $FromElement -xmlElementName "connected" -xmlElementText $FromConnected.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("FromStatic") ) { Add-XmlElement -xmlRoot $FromElement -xmlElementName "static" -xmlElementText $FromStatic.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("FromOspf") ) { Add-XmlElement -xmlRoot $FromElement -xmlElementName "ospf" -xmlElementText $FromOspf.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("FromBgp") ) { Add-XmlElement -xmlRoot $FromElement -xmlElementName "bgp" -xmlElementText $FromBgp.ToString().ToLower() } } $URI = "/api/4.0/edges/$($EdgeId)/routing/config" $body = $_EdgeRouting.OuterXml if ( $confirm ) { $message = "Edge Services Gateway routing update will modify existing Edge configuration." $question = "Proceed with Update of Edge Services Gateway $($EdgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed (Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxEdgeRouting | Get-NsxEdgeRedistributionRule -Learner $Learner)[-1] } } } end {} } ######### ######### # DLR Routing related functions function Set-NsxLogicalRouterRouting { <# .SYNOPSIS Configures global routing configuration of an existing NSX Logical Router .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Set-NsxLogicalRouterRouting cmdlet configures the global routing configuration of the specified LogicalRouter. .EXAMPLE Get-NsxLogicalRouter | Get-NsxLogicalRouterRouting | Set-NsxLogicalRouterRouting -DefaultGatewayVnic 0 -DefaultGatewayAddress 10.0.0.101 Configure the default route of the LogicalRouter. .EXAMPLE Get-NsxLogicalRouter | Get-NsxLogicalRouterRouting | Set-NsxLogicalRouterRouting -EnableECMP Enable ECMP .EXAMPLE Get-NsxLogicalRouter | Get-NsxLogicalRouterRouting | Set-NsxLogicalRouterRouting -EnableOSPF -RouterId 1.1.1.1 -ForwardingAddress 1.1.1.1 -ProtocolAddress 1.1.1.2 Enable OSPF .EXAMPLE Get-NsxLogicalRouter | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouter | Get-NsxLogicalRouterRouting | Set-NsxLogicalRouterRouting -EnableBGP -RouterId 1.1.1.1 -LocalAS 1234 Enable BGP .EXAMPLE Get-NsxLogicalRouter | Get-NsxLogicalRouterRouting | Set-NsxLogicalRouterRouting -EnableOspfRouteRedistribution:$false -Confirm:$false Disable OSPF Route Redistribution without confirmation. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$EnableOspf, [Parameter (Mandatory = $False)] [ValidateNotNullorEmpty()] [ipAddress]$ProtocolAddress, [Parameter (Mandatory = $False)] [ValidateNotNullorEmpty()] [ipAddress]$ForwardingAddress, [Parameter (Mandatory = $False)] [switch]$EnableBgp, [Parameter (Mandatory = $False)] [IpAddress]$RouterId, [Parameter (MAndatory = $False)] [ValidateRange(0, 65535)] [int]$LocalAS, [Parameter (Mandatory = $False)] [switch]$EnableEcmp, [Parameter (Mandatory = $False)] [switch]$EnableOspfRouteRedistribution, [Parameter (Mandatory = $False)] [switch]$EnableBgpRouteRedistribution, [Parameter (Mandatory = $False)] [switch]$EnableLogging, [Parameter (Mandatory = $False)] [ValidateSet("emergency", "alert", "critical", "error", "warning", "notice", "info", "debug")] [string]$LogLevel, [Parameter (Mandatory = $False)] [ValidateRange(0, 200)] [int]$DefaultGatewayVnic, [Parameter (Mandatory = $False)] [ValidateRange(0, 9128)] [int]$DefaultGatewayMTU, [Parameter (Mandatory = $False)] [string]$DefaultGatewayDescription, [Parameter (Mandatory = $False)] [ipAddress]$DefaultGatewayAddress, [Parameter (Mandatory = $False)] [ValidateRange(0, 255)] [int]$DefaultGatewayAdminDistance, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LogicalRouterRouting = $LogicalRouterRouting.CloneNode($true) #Store the logicalrouterId and remove it from the XML as we need to post it... $logicalrouterId = $_LogicalRouterRouting.logicalrouterId $_LogicalRouterRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::logicalrouterId')) ) | Out-Null #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('EnableOSPF') -or $PsBoundParameters.ContainsKey('EnableBGP') ) { $xmlGlobalConfig = $_LogicalRouterRouting.routingGlobalConfig $xmlRouterId = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $xmlGlobalConfig -query 'child::routerId') if ( $EnableOSPF -or $EnableBGP ) { if ( -not ($xmlRouterId -or $PsBoundParameters.ContainsKey("RouterId"))) { #Existing config missing and no new value set... throw "RouterId must be configured to enable dynamic routing" } if ($PsBoundParameters.ContainsKey("RouterId")) { #Set Routerid... if ($xmlRouterId) { $xmlRouterId = $RouterId.IPAddresstoString } else { Add-XmlElement -xmlRoot $xmlGlobalConfig -xmlElementName "routerId" -xmlElementText $RouterId.IPAddresstoString } } } } if ( $PsBoundParameters.ContainsKey('EnableOSPF')) { $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::ospf') if ( -not $ospf ) { #ospf node does not exist. [System.XML.XMLElement]$ospf = $_LogicalRouterRouting.ownerDocument.CreateElement("ospf") $_LogicalRouterRouting.appendChild($ospf) | Out-Null } if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::enabled')) { #Enabled element exists. Update it. $ospf.enabled = $EnableOSPF.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "enabled" -xmlElementText $EnableOSPF.ToString().ToLower() } if ( $EnableOSPF -and (-not ($ProtocolAddress -or ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::protocolAddress'))))) { throw "ProtocolAddress and ForwardingAddress are required to enable OSPF" } if ( $EnableOSPF -and (-not ($ForwardingAddress -or ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::forwardingAddress'))))) { throw "ProtocolAddress and ForwardingAddress are required to enable OSPF" } if ( $PsBoundParameters.ContainsKey('ProtocolAddress') ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::protocolAddress')) { # element exists. Update it. $ospf.protocolAddress = $ProtocolAddress.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "protocolAddress" -xmlElementText $ProtocolAddress.ToString().ToLower() } } if ( $PsBoundParameters.ContainsKey('ForwardingAddress') ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::forwardingAddress')) { # element exists. Update it. $ospf.forwardingAddress = $ForwardingAddress.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "forwardingAddress" -xmlElementText $ForwardingAddress.ToString().ToLower() } } } if ( $PsBoundParameters.ContainsKey('EnableBGP')) { $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::bgp') if ( -not $bgp ) { #bgp node does not exist. [System.XML.XMLElement]$bgp = $_LogicalRouterRouting.ownerDocument.CreateElement("bgp") $_LogicalRouterRouting.appendChild($bgp) | Out-Null } if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'child::enabled')) { #Enabled element exists. Update it. $bgp.enabled = $EnableBGP.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "enabled" -xmlElementText $EnableBGP.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("LocalAS")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'child::localAS')) { #LocalAS element exists, update it. $bgp.localAS = $LocalAS.ToString() } else { #LocalAS element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "localAS" -xmlElementText $LocalAS.ToString() } } elseif ( (-not ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'child::localAS')) -and $EnableBGP )) { throw "Existing configuration has no Local AS number specified. Local AS must be set to enable BGP." } } if ( $PsBoundParameters.ContainsKey("EnableECMP")) { $_LogicalRouterRouting.routingGlobalConfig.ecmp = $EnableECMP.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("EnableOspfRouteRedistribution")) { $_LogicalRouterRouting.ospf.redistribution.enabled = $EnableOspfRouteRedistribution.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("EnableBgpRouteRedistribution")) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::bgp/redistribution/enabled') ) { throw "BGP must have been configured at least once to enable/disable BGP route redistribution. Enable BGP and try again." } $_LogicalRouterRouting.bgp.redistribution.enabled = $EnableBgpRouteRedistribution.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("EnableLogging")) { $_LogicalRouterRouting.routingGlobalConfig.logging.enable = $EnableLogging.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("LogLevel")) { $_LogicalRouterRouting.routingGlobalConfig.logging.logLevel = $LogLevel.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("DefaultGatewayVnic") -or $PsBoundParameters.ContainsKey("DefaultGatewayAddress") -or $PsBoundParameters.ContainsKey("DefaultGatewayDescription") -or $PsBoundParameters.ContainsKey("DefaultGatewayMTU") -or $PsBoundParameters.ContainsKey("DefaultGatewayAdminDistance") ) { #Check for and create if required the defaultRoute element. first. if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting.staticRouting -query 'child::defaultRoute')) { #defaultRoute element does not exist $defaultRoute = $_LogicalRouterRouting.ownerDocument.CreateElement('defaultRoute') $_LogicalRouterRouting.staticRouting.AppendChild($defaultRoute) | Out-Null } else { #defaultRoute element exists $defaultRoute = $_LogicalRouterRouting.staticRouting.defaultRoute } if ( $PsBoundParameters.ContainsKey("DefaultGatewayVnic") ) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $defaultRoute -query 'child::vnic')) { #element does not exist Add-XmlElement -xmlRoot $defaultRoute -xmlElementName "vnic" -xmlElementText $DefaultGatewayVnic.ToString() } else { #element exists $defaultRoute.vnic = $DefaultGatewayVnic.ToString() } } if ( $PsBoundParameters.ContainsKey("DefaultGatewayAddress") ) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $defaultRoute -query 'child::gatewayAddress')) { #element does not exist Add-XmlElement -xmlRoot $defaultRoute -xmlElementName "gatewayAddress" -xmlElementText $DefaultGatewayAddress.ToString() } else { #element exists $defaultRoute.gatewayAddress = $DefaultGatewayAddress.ToString() } } if ( $PsBoundParameters.ContainsKey("DefaultGatewayDescription") ) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $defaultRoute -query 'child::description')) { #element does not exist Add-XmlElement -xmlRoot $defaultRoute -xmlElementName "description" -xmlElementText $DefaultGatewayDescription } else { #element exists $defaultRoute.description = $DefaultGatewayDescription } } if ( $PsBoundParameters.ContainsKey("DefaultGatewayMTU") ) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $defaultRoute -query 'child::mtu')) { #element does not exist Add-XmlElement -xmlRoot $defaultRoute -xmlElementName "mtu" -xmlElementText $DefaultGatewayMTU.ToString() } else { #element exists $defaultRoute.mtu = $DefaultGatewayMTU.ToString() } } if ( $PsBoundParameters.ContainsKey("DefaultGatewayAdminDistance") ) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $defaultRoute -query 'child::adminDistance')) { #element does not exist Add-XmlElement -xmlRoot $defaultRoute -xmlElementName "adminDistance" -xmlElementText $DefaultGatewayAdminDistance.ToString() } else { #element exists $defaultRoute.adminDistance = $DefaultGatewayAdminDistance.ToString() } } } $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $_LogicalRouterRouting.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed Get-NsxLogicalRouter -objectId $LogicalRouterId -Connection $connection | Get-NsxLogicalRouterRouting } } end {} } function Get-NsxLogicalRouterRouting { <# .SYNOPSIS Retrieves routing configuration for the specified NSX LogicalRouter. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Get-NsxLogicalRouterRouting cmdlet retrieves the routing configuration of the specified LogicalRouter. .EXAMPLE Get routing configuration for the LogicalRouter LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLogicalRouter $_ })] [System.Xml.XmlElement]$LogicalRouter ) begin { } process { #We append the LogicalRouter-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_LogicalRouterRouting = $LogicalRouter.features.routing.CloneNode($True) Add-XmlElement -xmlRoot $_LogicalRouterRouting -xmlElementName "logicalrouterId" -xmlElementText $LogicalRouter.Id $_LogicalRouterRouting } end {} } # Static Routing function Get-NsxLogicalRouterStaticRoute { <# .SYNOPSIS Retrieves Static Routes from the specified NSX LogicalRouter Routing configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Get-NsxLogicalRouterStaticRoute cmdlet retrieves the static routes from the routing configuration specified. .EXAMPLE Get static routes defining on LogicalRouter LogicalRouter01 PS C:\> Get-NsxLogicalRouter | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterStaticRoute #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$Network, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [ipAddress]$NextHop ) begin { } process { #We append the LogicalRouter-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_LogicalRouterStaticRouting = ($LogicalRouterRouting.staticRouting.CloneNode($True)) $LogicalRouterStaticRoutes = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterStaticRouting -query 'child::staticRoutes') #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called route. If ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LogicalRouterStaticRoutes -query 'child::route')) { $RouteCollection = $LogicalRouterStaticRoutes.route if ( $PsBoundParameters.ContainsKey('Network')) { $RouteCollection = $RouteCollection | Where-Object { $_.network -eq $Network } } if ( $PsBoundParameters.ContainsKey('NextHop')) { $RouteCollection = $RouteCollection | Where-Object { $_.nextHop -eq $NextHop } } foreach ( $StaticRoute in $RouteCollection ) { Add-XmlElement -xmlRoot $StaticRoute -xmlElementName "logicalrouterId" -xmlElementText $LogicalRouterRouting.LogicalRouterId } $RouteCollection } } end {} } function New-NsxLogicalRouterStaticRoute { <# .SYNOPSIS Creates a new static route and adds it to the specified ESGs routing configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The New-NsxLogicalRouterStaticRoute cmdlet adds a new static route to the routing configuration of the specified LogicalRouter. .EXAMPLE PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | New-NsxLogicalRouterStaticRoute -Network 1.1.1.0/24 -NextHop 10.0.0.200 Add a new static route to LogicalRouter LogicalRouter01 for 1.1.1.0/24 via 10.0.0.200 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [ValidateRange(0, 200)] [int]$Vnic, [Parameter (Mandatory = $False)] [ValidateRange(0, 9128)] [int]$MTU, [Parameter (Mandatory = $False)] [string]$Description, [Parameter (Mandatory = $True)] [ipAddress]$NextHop, [Parameter (Mandatory = $True)] [string]$Network, [Parameter (Mandatory = $False)] [ValidateRange(0, 255)] [int]$AdminDistance, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LogicalRouterRouting = $LogicalRouterRouting.CloneNode($true) #Store the logicalrouterId and remove it from the XML as we need to post it... $logicalrouterId = $_LogicalRouterRouting.logicalrouterId $_LogicalRouterRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::logicalrouterId')) ) | Out-Null #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. #Create the new route element. $Route = $_LogicalRouterRouting.ownerDocument.CreateElement('route') #Need to do an xpath query here rather than use PoSH dot notation to get the static route element, #as it might be empty, and PoSH silently turns an empty element into a string object, which is rather not what we want... :| $StaticRoutes = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting.staticRouting -query 'child::staticRoutes') $StaticRoutes.AppendChild($Route) | Out-Null Add-XmlElement -xmlRoot $Route -xmlElementName "network" -xmlElementText $Network.ToString() Add-XmlElement -xmlRoot $Route -xmlElementName "nextHop" -xmlElementText $NextHop.ToString() if ( $PsBoundParameters.ContainsKey("Vnic") ) { Add-XmlElement -xmlRoot $Route -xmlElementName "vnic" -xmlElementText $Vnic.ToString() } if ( $PsBoundParameters.ContainsKey("MTU") ) { Add-XmlElement -xmlRoot $Route -xmlElementName "mtu" -xmlElementText $MTU.ToString() } if ( $PsBoundParameters.ContainsKey("Description") ) { Add-XmlElement -xmlRoot $Route -xmlElementName "description" -xmlElementText $Description.ToString() } if ( $PsBoundParameters.ContainsKey("AdminDistance") ) { Add-XmlElement -xmlRoot $Route -xmlElementName "adminDistance" -xmlElementText $AdminDistance.ToString() } $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $_LogicalRouterRouting.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed Get-NsxLogicalRouter -objectId $LogicalRouterId -Connection $connection | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterStaticRoute -Network $Network -NextHop $NextHop } } end {} } function Remove-NsxLogicalRouterStaticRoute { <# .SYNOPSIS Removes a static route from the specified ESGs routing configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Remove-NsxLogicalRouterStaticRoute cmdlet removes a static route from the routing configuration of the specified LogicalRouter. Routes to be removed can be constructed via a PoSH pipline filter outputing route objects as produced by Get-NsxLogicalRouterStaticRoute and passing them on the pipeline to Remove-NsxLogicalRouterStaticRoute. .EXAMPLE Remove a route to 1.1.1.0/24 via 10.0.0.100 from LogicalRouter LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterStaticRoute | where-object { $_.network -eq '1.1.1.0/24' -and $_.nextHop -eq '10.0.0.100' } | Remove-NsxLogicalRouterStaticRoute .EXAMPLE Remove all routes to 1.1.1.0/24 from LogicalRouter LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterStaticRoute | where-object { $_.network -eq '1.1.1.0/24' } | Remove-NsxLogicalRouterStaticRoute #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterStaticRoute $_ })] [System.Xml.XmlElement]$StaticRoute, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our LogicalRouter $logicalrouterId = $StaticRoute.logicalrouterId $routing = Get-NsxLogicalRouter -objectId $logicalrouterId -Connection $connection | Get-NsxLogicalRouterRouting #Remove the logicalrouterId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'child::logicalrouterId')) ) | Out-Null #Need to do an xpath query here to query for a route that matches the one passed in. #Union of nextHop and network should be unique $xpathQuery = "//staticRoutes/route[nextHop=`"$($StaticRoute.nextHop)`" and network=`"$($StaticRoute.network)`"]" Write-Debug "XPath query for route nodes to remove is: $xpathQuery" $RouteToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing.staticRouting -query $xpathQuery) if ( $RouteToRemove ) { Write-Debug "RouteToRemove Element is: `n $($RouteToRemove.OuterXml | Format-XML) " $routing.staticRouting.staticRoutes.RemoveChild($RouteToRemove) | Out-Null $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed } } else { Throw "Route for network $($StaticRoute.network) via $($StaticRoute.nextHop) was not found in routing configuration for LogicalRouter $logicalrouterId" } } end {} } # Prefixes function Get-NsxLogicalRouterPrefix { <# .SYNOPSIS Retrieves IP Prefixes from the specified NSX LogicalRouter Routing configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Get-NsxLogicalRouterPrefix cmdlet retrieves IP prefixes from the routing configuration specified. .EXAMPLE Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterPrefix Retrieve prefixes from LogicalRouter LogicalRouter01 .EXAMPLE Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterPrefix -Network 1.1.1.0/24 Retrieve prefix 1.1.1.0/24 from LogicalRouter LogicalRouter01 .EXAMPLE Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterPrefix -Name CorpNet Retrieve prefix CorpNet from LogicalRouter LogicalRouter01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$Name, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$Network ) begin { } process { #We append the LogicalRouter-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output $_GlobalRoutingConfig = ($LogicalRouterRouting.routingGlobalConfig.CloneNode($True)) $IpPrefixes = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_GlobalRoutingConfig -query 'child::ipPrefixes') #IPPrefixes may not exist... if ( $IPPrefixes ) { #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called ipPrefix. If ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $IpPrefixes -query 'child::ipPrefix')) { $PrefixCollection = $IPPrefixes.ipPrefix if ( $PsBoundParameters.ContainsKey('Network')) { $PrefixCollection = $PrefixCollection | Where-Object { $_.ipAddress -eq $Network } } if ( $PsBoundParameters.ContainsKey('Name')) { $PrefixCollection = $PrefixCollection | Where-Object { $_.name -eq $Name } } foreach ( $Prefix in $PrefixCollection ) { Add-XmlElement -xmlRoot $Prefix -xmlElementName "logicalrouterId" -xmlElementText $LogicalRouterRouting.LogicalRouterId } $PrefixCollection } } } end {} } function New-NsxLogicalRouterPrefix { <# .SYNOPSIS Creates a new IP prefix and adds it to the specified ESGs routing configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The New-NsxLogicalRouterPrefix cmdlet adds a new IP prefix to the routing configuration of the specified LogicalRouter . #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $False)] [ValidateNotNullorEmpty()] [switch]$Confirm = $true, [Parameter (Mandatory = $True)] [ValidateNotNullorEmpty()] [string]$Name, [Parameter (Mandatory = $True)] [ValidateNotNullorEmpty()] [string]$Network, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LogicalRouterRouting = $LogicalRouterRouting.CloneNode($true) #Store the logicalrouterId and remove it from the XML as we need to post it... $logicalrouterId = $_LogicalRouterRouting.logicalrouterId $_LogicalRouterRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::logicalrouterId')) ) | Out-Null #Need to do an xpath query here rather than use PoSH dot notation to get the IP prefix element, #as it might be empty or not exist, and PoSH silently turns an empty element into a string object, which is rather not what we want... :| $ipPrefixes = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting.routingGlobalConfig -query 'child::ipPrefixes') if ( -not $ipPrefixes ) { #Create the ipPrefixes element $ipPrefixes = $_LogicalRouterRouting.ownerDocument.CreateElement('ipPrefixes') $_LogicalRouterRouting.routingGlobalConfig.AppendChild($ipPrefixes) | Out-Null } #Create the new ipPrefix element. $ipPrefix = $_LogicalRouterRouting.ownerDocument.CreateElement('ipPrefix') $ipPrefixes.AppendChild($ipPrefix) | Out-Null Add-XmlElement -xmlRoot $ipPrefix -xmlElementName "name" -xmlElementText $Name.ToString() Add-XmlElement -xmlRoot $ipPrefix -xmlElementName "ipAddress" -xmlElementText $Network.ToString() $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $_LogicalRouterRouting.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed Get-NsxLogicalRouter -objectId $LogicalRouterId -Connection $connection | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterPrefix -Network $Network -Name $Name } } end {} } function Remove-NsxLogicalRouterPrefix { <# .SYNOPSIS Removes an IP prefix from the specified ESGs routing configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Remove-NsxLogicalRouterPrefix cmdlet removes a IP prefix from the routing configuration of the specified LogicalRouter . Prefixes to be removed can be constructed via a PoSH pipline filter outputing prefix objects as produced by Get-NsxLogicalRouterPrefix and passing them on the pipeline to Remove-NsxLogicalRouterPrefix. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterPrefix $_ })] [System.Xml.XmlElement]$Prefix, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our LogicalRouter $logicalrouterId = $Prefix.logicalrouterId $routing = Get-NsxLogicalRouter -objectId $logicalrouterId -Connection $connection | Get-NsxLogicalRouterRouting #Remove the logicalrouterId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'child::logicalrouterId')) ) | Out-Null #Need to do an xpath query here to query for a prefix that matches the one passed in. #Union of nextHop and network should be unique $xpathQuery = "/routingGlobalConfig/ipPrefixes/ipPrefix[name=`"$($Prefix.name)`" and ipAddress=`"$($Prefix.ipAddress)`"]" Write-Debug "XPath query for prefix nodes to remove is: $xpathQuery" $PrefixToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query $xpathQuery) if ( $PrefixToRemove ) { Write-Debug "PrefixToRemove Element is: `n $($PrefixToRemove.OuterXml | Format-XML) " $routing.routingGlobalConfig.ipPrefixes.RemoveChild($PrefixToRemove) | Out-Null $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed } } else { Throw "Prefix $($Prefix.Name) for network $($Prefix.network) was not found in routing configuration for LogicalRouter $logicalrouterId" } } end {} } # BGP function Get-NsxLogicalRouterBgp { <# .SYNOPSIS Retrieves BGP configuration for the specified NSX LogicalRouter. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Get-NsxLogicalRouterBgp cmdlet retrieves the bgp configuration of the specified LogicalRouter. .EXAMPLE Get the BGP configuration for LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterBgp #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting ) begin { } process { #We append the LogicalRouter-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LogicalRouterRouting -query 'child::bgp')) { $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LogicalRouterRouting -query 'child::bgp').CloneNode($True) Add-XmlElement -xmlRoot $bgp -xmlElementName "logicalrouterId" -xmlElementText $LogicalRouterRouting.LogicalRouterId $bgp } } end {} } function Set-NsxLogicalRouterBgp { <# .SYNOPSIS Manipulates BGP specific base configuration of an existing NSX LogicalRouter. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Set-NsxLogicalRouterBgp cmdlet allows manipulation of the BGP specific configuration of a given ESG. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$EnableBGP, [Parameter (Mandatory = $False)] [IpAddress]$RouterId, [Parameter (Mandatory = $False)] [ValidateRange(0, 65535)] [int]$LocalAS, [Parameter (Mandatory = $False)] [switch]$GracefulRestart, [Parameter (Mandatory = $False)] [switch]$DefaultOriginate, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { if ( $DefaultOriginate ) { if ( -not $connection.version ) { Write-Warning "Setting defaultOriginate on a logical router is not supported NSX 6.3.0 or later and current NSX version could not be determined." } elseif ( [version]$connection.version -ge [version]"6.3.0") { throw "Setting defaultOriginate on a logical router is not supported NSX 6.3.0 or later." } } } process { #Create private xml element $_LogicalRouterRouting = $LogicalRouterRouting.CloneNode($true) #Store the logicalrouterId and remove it from the XML as we need to post it... $logicalrouterId = $_LogicalRouterRouting.logicalrouterId $_LogicalRouterRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::logicalrouterId')) ) | Out-Null #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. $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::bgp') if ( -not $bgp ) { #bgp node does not exist. [System.XML.XMLElement]$bgp = $_LogicalRouterRouting.ownerDocument.CreateElement("bgp") $_LogicalRouterRouting.appendChild($bgp) | Out-Null } # Check bgp enablement if ($PsBoundParameters.ContainsKey('EnableBGP')) { # BGP option is specified if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::enabled')) { #Enabled element exists. Update it. $bgp.enabled = $EnableBGP.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "enabled" -xmlElementText $EnableBGP.ToString().ToLower() } } elseif (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'descendant::enabled') { # BGP option is not specified but enabled if ( $bgp.enabled -eq 'true' ) { # Assume bgp is already enabled. } else { throw "EnableBGP is not specified or BGP is not enabled on logicalrouter $logicalrouterID. Please specify option EnableBGP" } } else { throw "EnableBGP is not specified or BGP is not enabled on logicalrouter $logicalrouterID. Please specify option EnableBGP" } $xmlGlobalConfig = $_LogicalRouterRouting.routingGlobalConfig $xmlRouterId = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $xmlGlobalConfig -query 'child::routerId') if ( $EnableBGP ) { if ( -not ($xmlRouterId -or $PsBoundParameters.ContainsKey("RouterId"))) { #Existing config missing and no new value set... throw "RouterId must be configured to enable dynamic routing" } if ($PsBoundParameters.ContainsKey("RouterId")) { #Set Routerid... if ($xmlRouterId) { $xmlRouterId = $RouterId.IPAddresstoString } else { Add-XmlElement -xmlRoot $xmlGlobalConfig -xmlElementName "routerId" -xmlElementText $RouterId.IPAddresstoString } } } if ( $PsBoundParameters.ContainsKey("LocalAS")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'child::localAS')) { #LocalAS element exists, update it. $bgp.localAS = $LocalAS.ToString() } else { #LocalAS element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "localAS" -xmlElementText $LocalAS.ToString() } } elseif ( (-not ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'child::localAS')) -and $EnableBGP )) { throw "Existing configuration has no Local AS number specified. Local AS must be set to enable BGP." } if ( $PsBoundParameters.ContainsKey("GracefulRestart")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'child::gracefulRestart')) { #element exists, update it. $bgp.gracefulRestart = $GracefulRestart.ToString().ToLower() } else { #element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "gracefulRestart" -xmlElementText $GracefulRestart.ToString().ToLower() } } if ( $PsBoundParameters.ContainsKey("DefaultOriginate")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'child::defaultOriginate')) { #element exists, update it. $bgp.defaultOriginate = $DefaultOriginate.ToString().ToLower() } else { #element does not exist... Add-XmlElement -xmlRoot $bgp -xmlElementName "defaultOriginate" -xmlElementText $DefaultOriginate.ToString().ToLower() } } $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $_LogicalRouterRouting.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed Get-NsxLogicalRouter -objectId $LogicalRouterId -Connection $connection | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterBgp } } end {} } function Get-NsxLogicalRouterBgpNeighbour { <# .SYNOPSIS Returns BGP neighbours from the specified NSX LogicalRouter BGP configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Get-NsxLogicalRouterBgpNeighbour cmdlet retrieves the BGP neighbours from the BGP configuration specified. .EXAMPLE Get all BGP neighbours defined on LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterBgpNeighbour .EXAMPLE Get BGP neighbour 1.1.1.1 defined on LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterBgpNeighbour -IpAddress 1.1.1.1 .EXAMPLE Get all BGP neighbours with Remote AS 1234 defined on LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterBgpNeighbour | where-object { $_.RemoteAS -eq '1234' } #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [String]$Network, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [ipAddress]$IpAddress, [Parameter (Mandatory = $false)] [ValidateRange(0, 65535)] [int]$RemoteAS ) begin { } process { $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LogicalRouterRouting -query 'child::bgp') if ( $bgp ) { $_bgp = $bgp.CloneNode($True) $BgpNeighbours = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_bgp -query 'child::bgpNeighbours') #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called bgpNeighbour. if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $BgpNeighbours -query 'child::bgpNeighbour')) { $NeighbourCollection = $BgpNeighbours.bgpNeighbour if ( $PsBoundParameters.ContainsKey('IpAddress')) { $NeighbourCollection = $NeighbourCollection | Where-Object { $_.ipAddress -eq $IpAddress } } if ( $PsBoundParameters.ContainsKey('RemoteAS')) { $NeighbourCollection = $NeighbourCollection | Where-Object { $_.remoteAS -eq $RemoteAS } } foreach ( $Neighbour in $NeighbourCollection ) { #We append the LogicalRouter-id to the associated neighbour config XML to enable pipeline workflows and #consistent readable output Add-XmlElement -xmlRoot $Neighbour -xmlElementName "logicalrouterId" -xmlElementText $LogicalRouterRouting.LogicalRouterId } $NeighbourCollection } } } end {} } function New-NsxLogicalRouterBgpNeighbour { <# .SYNOPSIS Creates a new BGP neighbour and adds it to the specified ESGs BGP configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The New-NsxLogicalRouterBgpNeighbour cmdlet adds a new BGP neighbour to the bgp configuration of the specified LogicalRouter. .EXAMPLE Add a new neighbour 1.2.3.4 with remote AS number 1234 with defaults. PS C:\> Get-NsxLogicalRouter | Get-NsxLogicalRouterRouting | New-NsxLogicalRouterBgpNeighbour -IpAddress 1.2.3.4 -RemoteAS 1234 -ForwardingAddress 1.2.3.1 -ProtocolAddress 1.2.3.2 .EXAMPLE Add a new neighbour 1.2.3.4 with remote AS number 22235 specifying weight, holddown and keepalive timers and dont prompt for confirmation. PS C:\> Get-NsxLogicalRouter | Get-NsxLogicalRouterRouting | New-NsxLogicalRouterBgpNeighbour -IpAddress 1.2.3.4 -RemoteAS 22235 -ForwardingAddress 1.2.3.1 -ProtocolAddress 1.2.3.2 -Confirm:$false -Weight 90 -HoldDownTimer 240 -KeepAliveTimer 90 -confirm:$false #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $true)] [ValidateNotNullorEmpty()] [ipAddress]$IpAddress, [Parameter (Mandatory = $true)] [ValidateRange(0, 65535)] [int]$RemoteAS, [Parameter (Mandatory = $true)] [ValidateNotNullorEmpty()] [ipAddress]$ForwardingAddress, [Parameter (Mandatory = $true)] [ValidateNotNullorEmpty()] [ipAddress]$ProtocolAddress, [Parameter (Mandatory = $false)] [ValidateRange(0, 65535)] [int]$Weight, [Parameter (Mandatory = $false)] [ValidateRange(2, 65535)] [int]$HoldDownTimer, [Parameter (Mandatory = $false)] [ValidateRange(1, 65534)] [int]$KeepAliveTimer, [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [string]$Password, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LogicalRouterRouting = $LogicalRouterRouting.CloneNode($true) #Store the logicalrouterId and remove it from the XML as we need to post it... $logicalrouterId = $_LogicalRouterRouting.logicalrouterId $_LogicalRouterRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::logicalrouterId')) ) | Out-Null #Create the new bgpNeighbour element. $Neighbour = $_LogicalRouterRouting.ownerDocument.CreateElement('bgpNeighbour') #Need to do an xpath query here rather than use PoSH dot notation to get the bgp element, #as it might not exist which wil cause PoSH to throw in stric mode. $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::bgp') if ( $bgp ) { (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bgp -query 'child::bgpNeighbours').AppendChild($Neighbour) | Out-Null Add-XmlElement -xmlRoot $Neighbour -xmlElementName "ipAddress" -xmlElementText $IpAddress.ToString() Add-XmlElement -xmlRoot $Neighbour -xmlElementName "remoteAS" -xmlElementText $RemoteAS.ToString() Add-XmlElement -xmlRoot $Neighbour -xmlElementName "forwardingAddress" -xmlElementText $ForwardingAddress.ToString() Add-XmlElement -xmlRoot $Neighbour -xmlElementName "protocolAddress" -xmlElementText $ProtocolAddress.ToString() #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("Weight") ) { Add-XmlElement -xmlRoot $Neighbour -xmlElementName "weight" -xmlElementText $Weight.ToString() } if ( $PsBoundParameters.ContainsKey("HoldDownTimer") ) { Add-XmlElement -xmlRoot $Neighbour -xmlElementName "holdDownTimer" -xmlElementText $HoldDownTimer.ToString() } if ( $PsBoundParameters.ContainsKey("KeepAliveTimer") ) { Add-XmlElement -xmlRoot $Neighbour -xmlElementName "keepAliveTimer" -xmlElementText $KeepAliveTimer.ToString() } if ( $PsBoundParameters.ContainsKey("Password") ) { Add-XmlElement -xmlRoot $Neighbour -xmlElementName "password" -xmlElementText $Password.ToString() } $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $_LogicalRouterRouting.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed Get-NsxLogicalRouter -objectId $LogicalRouterId -Connection $connection | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterBgpNeighbour -IpAddress $IpAddress -RemoteAS $RemoteAS } } else { throw "BGP is not enabled on logicalrouter $logicalrouterID. Enable BGP using Set-NsxLogicalRouterRouting or Set-NsxLogicalRouterBGP first." } } end {} } function Remove-NsxLogicalRouterBgpNeighbour { <# .SYNOPSIS Removes a BGP neigbour from the specified ESGs BGP configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Remove-NsxLogicalRouterBgpNeighbour cmdlet removes a BGP neighbour route from the bgp configuration of the specified LogicalRouter Services Gateway. Neighbours to be removed can be constructed via a PoSH pipline filter outputing neighbour objects as produced by Get-NsxLogicalRouterBgpNeighbour and passing them on the pipeline to Remove-NsxLogicalRouterBgpNeighbour. .EXAMPLE Remove the BGP neighbour 1.1.1.2 from the the logicalrouter LogicalRouter01's bgp configuration PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterBgpNeighbour | where-object { $_.ipaddress -eq '1.1.1.2' } | Remove-NsxLogicalRouterBgpNeighbour #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterBgpNeighbour $_ })] [System.Xml.XmlElement]$BgpNeighbour, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our LogicalRouter $logicalrouterId = $BgpNeighbour.logicalrouterId $routing = Get-NsxLogicalRouter -objectId $logicalrouterId -Connection $connection | Get-NsxLogicalRouterRouting #Remove the logicalrouterId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'child::logicalrouterId')) ) | Out-Null #Validate the BGP node exists on the logicalrouter if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'child::bgp')) { throw "BGP is not enabled on ESG $logicalrouterId. Enable BGP and try again." } #Need to do an xpath query here to query for a bgp neighbour that matches the one passed in. #Union of ipaddress and remote AS should be unique (though this is not enforced by the API, #I cant see why having duplicate neighbours with same ip and AS would be useful...maybe #different filters?) #Will probably need to include additional xpath query filters here in the query to include #matching on filters to better handle uniquness amongst bgp neighbours with same ip and remoteAS $xpathQuery = "//bgpNeighbours/bgpNeighbour[ipAddress=`"$($BgpNeighbour.ipAddress)`" and remoteAS=`"$($BgpNeighbour.remoteAS)`"]" Write-Debug "XPath query for neighbour nodes to remove is: $xpathQuery" $NeighbourToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing.bgp -query $xpathQuery) if ( $NeighbourToRemove ) { Write-Debug "NeighbourToRemove Element is: `n $($NeighbourToRemove.OuterXml | Format-XML) " $routing.bgp.bgpNeighbours.RemoveChild($NeighbourToRemove) | Out-Null $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed } } else { Throw "Neighbour $($BgpNeighbour.ipAddress) with Remote AS $($BgpNeighbour.RemoteAS) was not found in routing configuration for LogicalRouter $logicalrouterId" } } end {} } # OSPF function Get-NsxLogicalRouterOspf { <# .SYNOPSIS Retrieves OSPF configuration for the specified NSX LogicalRouter. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Get-NsxLogicalRouterOspf cmdlet retrieves the OSPF configuration of the specified LogicalRouter. .EXAMPLE Get the OSPF configuration for LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterOspf #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting ) begin { } process { #We append the LogicalRouter-id to the associated Routing config XML to enable pipeline workflows and #consistent readable output if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LogicalRouterRouting -query 'child::ospf')) { $ospf = $LogicalRouterRouting.ospf.CloneNode($True) Add-XmlElement -xmlRoot $ospf -xmlElementName "logicalrouterId" -xmlElementText $LogicalRouterRouting.LogicalRouterId $ospf } } end {} } function Set-NsxLogicalRouterOspf { <# .SYNOPSIS Manipulates OSPF specific base configuration of an existing NSX LogicalRouter. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Set-NsxLogicalRouterOspf cmdlet allows manipulation of the OSPF specific configuration of a given ESG. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$EnableOSPF, [Parameter (Mandatory = $False)] [ValidateNotNullorEmpty()] [ipAddress]$ProtocolAddress, [Parameter (Mandatory = $False)] [ValidateNotNullorEmpty()] [ipAddress]$ForwardingAddress, [Parameter (Mandatory = $False)] [IpAddress]$RouterId, [Parameter (Mandatory = $False)] [switch]$GracefulRestart, [Parameter (Mandatory = $False)] [switch]$DefaultOriginate, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { if ( $DefaultOriginate ) { if ( -not $connection.version ) { Write-Warning "Setting defaultOriginate on a logical router is not supported NSX 6.3.0 or later and current NSX version could not be determined." } elseif ( [version]$connection.version -ge [version]"6.3.0") { throw "Setting defaultOriginate on a logical router is not supported NSX 6.3.0 or later." } } } process { #Create private xml element $_LogicalRouterRouting = $LogicalRouterRouting.CloneNode($true) #Store the logicalrouterId and remove it from the XML as we need to post it... $logicalrouterId = $_LogicalRouterRouting.logicalrouterId $_LogicalRouterRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::logicalrouterId')) ) | Out-Null #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. $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::ospf') if ( -not $ospf ) { #ospf node does not exist. [System.XML.XMLElement]$ospf = $_LogicalRouterRouting.ownerDocument.CreateElement("ospf") $_LogicalRouterRouting.appendChild($ospf) | Out-Null } if ( $PsBoundParameters.ContainsKey('EnableOSPF') ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::enabled')) { #Enabled element exists. Update it. $ospf.enabled = $EnableOSPF.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "enabled" -xmlElementText $EnableOSPF.ToString().ToLower() } } elseif ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::enabled')) { # OSPF option is not specified but enabled if ( $ospf.enabled -eq 'true' ) { # Assume ospf is already enabled. } else { throw "EnableOSPF is not specified or OSPF is not enabled on logicalrouter $logicalrouterID. Please specify option EnableOSPF" } } else { throw "EnableOSPF is not specified or OSPF is not enabled on logicalrouter $logicalrouterID. Please specify option EnableOSPF" } if ( $EnableOSPF -and (-not ($ProtocolAddress -or ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::protocolAddress'))))) { throw "ProtocolAddress and ForwardingAddress are required to enable OSPF" } if ( $EnableOSPF -and (-not ($ForwardingAddress -or ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::forwardingAddress'))))) { throw "ProtocolAddress and ForwardingAddress are required to enable OSPF" } if ( $PsBoundParameters.ContainsKey('ProtocolAddress') ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::protocolAddress')) { # element exists. Update it. $ospf.protocolAddress = $ProtocolAddress.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "protocolAddress" -xmlElementText $ProtocolAddress.ToString().ToLower() } } if ( $PsBoundParameters.ContainsKey('ForwardingAddress') ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::forwardingAddress')) { # element exists. Update it. $ospf.forwardingAddress = $ForwardingAddress.ToString().ToLower() } else { #Enabled element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "forwardingAddress" -xmlElementText $ForwardingAddress.ToString().ToLower() } } $xmlGlobalConfig = $_LogicalRouterRouting.routingGlobalConfig $xmlRouterId = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $xmlGlobalConfig -query 'child::routerId') if ( $EnableOSPF ) { if ( -not ($xmlRouterId -or $PsBoundParameters.ContainsKey("RouterId"))) { #Existing config missing and no new value set... throw "RouterId must be configured to enable dynamic routing" } if ($PsBoundParameters.ContainsKey("RouterId")) { #Set Routerid... if ($xmlRouterId) { $xmlRouterId = $RouterId.IPAddresstoString } else { Add-XmlElement -xmlRoot $xmlGlobalConfig -xmlElementName "routerId" -xmlElementText $RouterId.IPAddresstoString } } } if ( $PsBoundParameters.ContainsKey("GracefulRestart")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::gracefulRestart')) { #element exists, update it. $ospf.gracefulRestart = $GracefulRestart.ToString().ToLower() } else { #element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "gracefulRestart" -xmlElementText $GracefulRestart.ToString().ToLower() } } if ( $PsBoundParameters.ContainsKey("DefaultOriginate")) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::defaultOriginate')) { #element exists, update it. $ospf.defaultOriginate = $DefaultOriginate.ToString().ToLower() } else { #element does not exist... Add-XmlElement -xmlRoot $ospf -xmlElementName "defaultOriginate" -xmlElementText $DefaultOriginate.ToString().ToLower() } } $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $_LogicalRouterRouting.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed Get-NsxLogicalRouter -objectId $LogicalRouterId -Connection $connection | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterOspf } } end {} } function Get-NsxLogicalRouterOspfArea { <# .SYNOPSIS Returns OSPF Areas defined in the specified NSX LogicalRouter OSPF configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Get-NsxLogicalRouterOspfArea cmdlet retrieves the OSPF Areas from the OSPF configuration specified. .EXAMPLE Get all areas defined on LogicalRouter01. PS C:\> C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterOspfArea #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $false)] [ValidateRange(0, 4294967295)] [int]$AreaId ) begin { } process { $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LogicalRouterRouting -query 'child::ospf') if ( $ospf ) { $_ospf = $ospf.CloneNode($True) $OspfAreas = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ospf -query 'child::ospfAreas') #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called ospfArea. if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $OspfAreas -query 'child::ospfArea')) { $AreaCollection = $OspfAreas.ospfArea if ( $PsBoundParameters.ContainsKey('AreaId')) { $AreaCollection = $AreaCollection | Where-Object { $_.areaId -eq $AreaId } } foreach ( $Area in $AreaCollection ) { #We append the LogicalRouter-id to the associated Area config XML to enable pipeline workflows and #consistent readable output Add-XmlElement -xmlRoot $Area -xmlElementName "logicalrouterId" -xmlElementText $LogicalRouterRouting.LogicalRouterId } $AreaCollection } } } end {} } function Remove-NsxLogicalRouterOspfArea { <# .SYNOPSIS Removes an OSPF area from the specified ESGs OSPF configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Remove-NsxLogicalRouterOspfArea cmdlet removes a BGP neighbour route from the bgp configuration of the specified LogicalRouter. Areas to be removed can be constructed via a PoSH pipline filter outputing area objects as produced by Get-NsxLogicalRouterOspfArea and passing them on the pipeline to Remove-NsxLogicalRouterOspfArea. .EXAMPLE Remove area 51 from ospf configuration on LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterOspfArea -AreaId 51 | Remove-NsxLogicalRouterOspfArea #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterOspfArea $_ })] [System.Xml.XmlElement]$OspfArea, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our LogicalRouter $logicalrouterId = $OspfArea.logicalrouterId $routing = Get-NsxLogicalRouter -objectId $logicalrouterId -Connection $connection | Get-NsxLogicalRouterRouting #Remove the logicalrouterId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'child::logicalrouterId')) ) | Out-Null #Validate the OSPF node exists on the logicalrouter if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'child::ospf')) { throw "OSPF is not enabled on ESG $logicalrouterId. Enable OSPF and try again." } if ( -not ($routing.ospf.enabled -eq 'true') ) { throw "OSPF is not enabled on ESG $logicalrouterId. Enable OSPF and try again." } $xpathQuery = "//ospfAreas/ospfArea[areaId=`"$($OspfArea.areaId)`"]" Write-Debug "XPath query for area nodes to remove is: $xpathQuery" $AreaToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing.ospf -query $xpathQuery) if ( $AreaToRemove ) { Write-Debug "AreaToRemove Element is: `n $($AreaToRemove.OuterXml | Format-XML) " $routing.ospf.ospfAreas.RemoveChild($AreaToRemove) | Out-Null $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed } } else { Throw "Area $($OspfArea.areaId) was not found in routing configuration for LogicalRouter $logicalrouterId" } } end {} } function New-NsxLogicalRouterOspfArea { <# .SYNOPSIS Creates a new OSPF Area and adds it to the specified ESGs OSPF configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The New-NsxLogicalRouterOspfArea cmdlet adds a new OSPF Area to the ospf configuration of the specified LogicalRouter. .EXAMPLE Create area 50 as a normal type on ESG LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | New-NsxLogicalRouterOspfArea -AreaId 50 .EXAMPLE Create area 10 as a nssa type on ESG LogicalRouter01 with password authentication PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | New-NsxLogicalRouterOspfArea -AreaId 10 -Type password -Password "Secret" #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $true)] [ValidateRange(0, 4294967295)] [uint32]$AreaId, [Parameter (Mandatory = $false)] [ValidateSet("normal", "nssa", IgnoreCase = $false)] [string]$Type, [Parameter (Mandatory = $false)] [ValidateSet("none", "password", "md5", IgnoreCase = $false)] [string]$AuthenticationType = "none", [Parameter (Mandatory = $false)] [ValidateNotNullorEmpty()] [string]$Password, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LogicalRouterRouting = $LogicalRouterRouting.CloneNode($true) #Store the logicalrouterId and remove it from the XML as we need to post it... $logicalrouterId = $_LogicalRouterRouting.logicalrouterId $_LogicalRouterRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::logicalrouterId')) ) | Out-Null #Create the new ospfArea element. $Area = $_LogicalRouterRouting.ownerDocument.CreateElement('ospfArea') #Need to do an xpath query here rather than use PoSH dot notation to get the ospf element, #as it might not exist which wil cause PoSH to throw in stric mode. $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::ospf') if ( $ospf ) { (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::ospfAreas').AppendChild($Area) | Out-Null Add-XmlElement -xmlRoot $Area -xmlElementName "areaId" -xmlElementText $AreaId.ToString() #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("Type") ) { Add-XmlElement -xmlRoot $Area -xmlElementName "type" -xmlElementText $Type.ToString() } if ( $PsBoundParameters.ContainsKey("AuthenticationType") -or $PsBoundParameters.ContainsKey("Password") ) { switch ($AuthenticationType) { "none" { if ( $PsBoundParameters.ContainsKey('Password') ) { throw "Authentication type must be other than none to specify a password." } #Default value - do nothing } default { if ( -not ( $PsBoundParameters.ContainsKey('Password')) ) { throw "Must specify a password if Authentication type is not none." } $Authentication = $Area.ownerDocument.CreateElement("authentication") $Area.AppendChild( $Authentication ) | Out-Null Add-XmlElement -xmlRoot $Authentication -xmlElementName "type" -xmlElementText $AuthenticationType Add-XmlElement -xmlRoot $Authentication -xmlElementName "value" -xmlElementText $Password } } } $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $_LogicalRouterRouting.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed Get-NsxLogicalRouter -objectId $LogicalRouterId -Connection $connection | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterOspfArea -AreaId $AreaId } } else { throw "OSPF is not enabled on logicalrouter $logicalrouterID. Enable OSPF using Set-NsxLogicalRouterRouting or Set-NsxLogicalRouterOSPF first." } } end {} } function Get-NsxLogicalRouterOspfInterface { <# .SYNOPSIS Returns OSPF Interface mappings defined in the specified NSX LogicalRouter OSPF configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Get-NsxLogicalRouterOspfInterface cmdlet retrieves the OSPF Area to interfaces mappings from the OSPF configuration specified. .EXAMPLE Get all OSPF Area to Interface mappings on LogicalRouter LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterOspfInterface .EXAMPLE Get OSPF Area to Interface mapping for Area 10 on LogicalRouter LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterOspfInterface -AreaId 10 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $false)] [ValidateRange(0, 4294967295)] [int]$AreaId, [Parameter (Mandatory = $false)] [ValidateRange(0, 200)] [int]$vNicId ) begin { } process { $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LogicalRouterRouting -query 'child::ospf') if ( $ospf ) { $_ospf = $ospf.CloneNode($True) $OspfInterfaces = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ospf -query 'child::ospfInterfaces') #Need to use an xpath query here, as dot notation will throw in strict mode if there is not childnode called ospfArea. if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $OspfInterfaces -query 'child::ospfInterface')) { $InterfaceCollection = $OspfInterfaces.ospfInterface if ( $PsBoundParameters.ContainsKey('AreaId')) { $InterfaceCollection = $InterfaceCollection | Where-Object { $_.areaId -eq $AreaId } } if ( $PsBoundParameters.ContainsKey('vNicId')) { $InterfaceCollection = $InterfaceCollection | Where-Object { $_.vnic -eq $vNicId } } foreach ( $Interface in $InterfaceCollection ) { #We append the LogicalRouter-id to the associated Area config XML to enable pipeline workflows and #consistent readable output Add-XmlElement -xmlRoot $Interface -xmlElementName "logicalrouterId" -xmlElementText $LogicalRouterRouting.LogicalRouterId } $InterfaceCollection } } } end {} } function Remove-NsxLogicalRouterOspfInterface { <# .SYNOPSIS Removes an OSPF Interface from the specified ESGs OSPF configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Remove-NsxLogicalRouterOspfInterface cmdlet removes a BGP neighbour route from the bgp configuration of the specified LogicalRouter. Interfaces to be removed can be constructed via a PoSH pipline filter outputing interface objects as produced by Get-NsxLogicalRouterOspfInterface and passing them on the pipeline to Remove-NsxLogicalRouterOspfInterface. .EXAMPLE Remove Interface to Area mapping for area 51 from LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterOspfInterface -AreaId 51 | Remove-NsxLogicalRouterOspfInterface .EXAMPLE Remove all Interface to Area mappings from LogicalRouter01 without confirmation. PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterOspfInterface | Remove-NsxLogicalRouterOspfInterface -confirm:$false #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterOspfInterface $_ })] [System.Xml.XmlElement]$OspfInterface, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our LogicalRouter $logicalrouterId = $OspfInterface.logicalrouterId $routing = Get-NsxLogicalRouter -objectId $logicalrouterId -Connection $connection | Get-NsxLogicalRouterRouting #Remove the logicalrouterId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'child::logicalrouterId')) ) | Out-Null #Validate the OSPF node exists on the logicalrouter if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'child::ospf')) { throw "OSPF is not enabled on ESG $logicalrouterId. Enable OSPF and try again." } if ( -not ($routing.ospf.enabled -eq 'true') ) { throw "OSPF is not enabled on ESG $logicalrouterId. Enable OSPF and try again." } $xpathQuery = "//ospfInterfaces/ospfInterface[areaId=`"$($OspfInterface.areaId)`"]" Write-Debug "XPath query for interface nodes to remove is: $xpathQuery" $InterfaceToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing.ospf -query $xpathQuery) if ( $InterfaceToRemove ) { Write-Debug "InterfaceToRemove Element is: `n $($InterfaceToRemove.OuterXml | Format-XML) " $routing.ospf.ospfInterfaces.RemoveChild($InterfaceToRemove) | Out-Null $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed } } else { Throw "Interface $($OspfInterface.areaId) was not found in routing configuration for LogicalRouter $logicalrouterId" } } end {} } function New-NsxLogicalRouterOspfInterface { <# .SYNOPSIS Creates a new OSPF Interface to Area mapping and adds it to the specified LogicalRouters OSPF configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The New-NsxLogicalRouterOspfInterface cmdlet adds a new OSPF Area to Interface mapping to the ospf configuration of the specified LogicalRouter. .EXAMPLE Add a mapping for Area 10 to Interface 0 on ESG LogicalRouter01 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | New-NsxLogicalRouterOspfInterface -AreaId 10 -Vnic 0 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $true)] [ValidateRange(0, 4294967295)] [uint32]$AreaId, [Parameter (Mandatory = $true)] [ValidateRange(0, 200)] [int]$Vnic, [Parameter (Mandatory = $false)] [ValidateRange(1, 255)] [int]$HelloInterval, [Parameter (Mandatory = $false)] [ValidateRange(1, 65535)] [int]$DeadInterval, [Parameter (Mandatory = $false)] [ValidateRange(0, 255)] [int]$Priority, [Parameter (Mandatory = $false)] [ValidateRange(1, 65535)] [int]$Cost, [Parameter (Mandatory = $false)] [switch]$IgnoreMTU, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LogicalRouterRouting = $LogicalRouterRouting.CloneNode($true) #Store the logicalrouterId and remove it from the XML as we need to post it... $logicalrouterId = $_LogicalRouterRouting.logicalrouterId $_LogicalRouterRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::logicalrouterId')) ) | Out-Null #Create the new ospfInterface element. $Interface = $_LogicalRouterRouting.ownerDocument.CreateElement('ospfInterface') #Need to do an xpath query here rather than use PoSH dot notation to get the ospf element, #as it might not exist which wil cause PoSH to throw in stric mode. $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::ospf') if ( $ospf ) { (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ospf -query 'child::ospfInterfaces').AppendChild($Interface) | Out-Null Add-XmlElement -xmlRoot $Interface -xmlElementName "areaId" -xmlElementText $AreaId.ToString() Add-XmlElement -xmlRoot $Interface -xmlElementName "vnic" -xmlElementText $Vnic.ToString() #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("HelloInterval") ) { Add-XmlElement -xmlRoot $Interface -xmlElementName "helloInterval" -xmlElementText $HelloInterval.ToString() } if ( $PsBoundParameters.ContainsKey("DeadInterval") ) { Add-XmlElement -xmlRoot $Interface -xmlElementName "deadInterval" -xmlElementText $DeadInterval.ToString() } if ( $PsBoundParameters.ContainsKey("Priority") ) { Add-XmlElement -xmlRoot $Interface -xmlElementName "priority" -xmlElementText $Priority.ToString() } if ( $PsBoundParameters.ContainsKey("Cost") ) { Add-XmlElement -xmlRoot $Interface -xmlElementName "cost" -xmlElementText $Cost.ToString() } if ( $PsBoundParameters.ContainsKey("IgnoreMTU") ) { Add-XmlElement -xmlRoot $Interface -xmlElementName "mtuIgnore" -xmlElementText $IgnoreMTU.ToString().ToLower() } $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $_LogicalRouterRouting.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed Get-NsxLogicalRouter -objectId $LogicalRouterId -Connection $connection | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterOspfInterface -AreaId $AreaId } } else { throw "OSPF is not enabled on logicalrouter $logicalrouterID. Enable OSPF using Set-NsxLogicalRouterRouting or Set-NsxLogicalRouterOSPF first." } } end {} } # Redistribution Rules function Get-NsxLogicalRouterRedistributionRule { <# .SYNOPSIS Returns dynamic route redistribution rules defined in the specified NSX LogicalRouter routing configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Get-NsxLogicalRouterRedistributionRule cmdlet retrieves the route redistribution rules defined in the ospf and bgp configurations for the specified LogicalRouter. .EXAMPLE Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterRedistributionRule -Learner ospf Get all Redistribution rules for ospf on LogicalRouter LogicalRouter01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $false)] [ValidateSet("ospf", "bgp")] [string]$Learner, [Parameter (Mandatory = $false)] [int]$Id ) begin { } process { #Rules can be defined in either ospf or bgp (isis as well, but who cares huh? :) ) if ( ( -not $PsBoundParameters.ContainsKey('Learner')) -or ($PsBoundParameters.ContainsKey('Learner') -and $Learner -eq 'ospf')) { $ospf = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LogicalRouterRouting -query 'child::ospf') if ( $ospf ) { $_ospf = $ospf.CloneNode($True) if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ospf -query 'child::redistribution/rules/rule') ) { $OspfRuleCollection = $_ospf.redistribution.rules.rule foreach ( $rule in $OspfRuleCollection ) { #We append the LogicalRouter-id to the associated rule config XML to enable pipeline workflows and #consistent readable output Add-XmlElement -xmlRoot $rule -xmlElementName "logicalrouterId" -xmlElementText $LogicalRouterRouting.LogicalRouterId #Add the learner to be consistent with the view the UI gives Add-XmlElement -xmlRoot $rule -xmlElementName "learner" -xmlElementText "ospf" } if ( $PsBoundParameters.ContainsKey('Id')) { $OspfRuleCollection = $OspfRuleCollection | Where-Object { $_.id -eq $Id } } $OspfRuleCollection } } } if ( ( -not $PsBoundParameters.ContainsKey('Learner')) -or ($PsBoundParameters.ContainsKey('Learner') -and $Learner -eq 'bgp')) { $bgp = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LogicalRouterRouting -query 'child::bgp') if ( $bgp ) { $_bgp = $bgp.CloneNode($True) if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_bgp -query 'child::redistribution/rules/rule') ) { $BgpRuleCollection = $_bgp.redistribution.rules.rule foreach ( $rule in $BgpRuleCollection ) { #We append the LogicalRouter-id to the associated rule config XML to enable pipeline workflows and #consistent readable output Add-XmlElement -xmlRoot $rule -xmlElementName "logicalrouterId" -xmlElementText $LogicalRouterRouting.LogicalRouterId #Add the learner to be consistent with the view the UI gives Add-XmlElement -xmlRoot $rule -xmlElementName "learner" -xmlElementText "bgp" } if ( $PsBoundParameters.ContainsKey('Id')) { $BgpRuleCollection = $BgpRuleCollection | Where-Object { $_.id -eq $Id } } $BgpRuleCollection } } } } end {} } function Remove-NsxLogicalRouterRedistributionRule { <# .SYNOPSIS Removes a route redistribution rule from the specified LogicalRouters configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The Remove-NsxLogicalRouterRedistributionRule cmdlet removes a route redistribution rule from the configuration of the specified LogicalRouter. Interfaces to be removed can be constructed via a PoSH pipline filter outputing interface objects as produced by Get-NsxLogicalRouterRedistributionRule and passing them on the pipeline to Remove-NsxLogicalRouterRedistributionRule. .EXAMPLE Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterRedistributionRule -Learner ospf | Remove-NsxLogicalRouterRedistributionRule Remove all ospf redistribution rules from LogicalRouter01 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterRedistributionRule $_ })] [System.Xml.XmlElement]$RedistributionRule, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our LogicalRouter $logicalrouterId = $RedistributionRule.logicalrouterId $routing = Get-NsxLogicalRouter -objectId $logicalrouterId -Connection $connection | Get-NsxLogicalRouterRouting #Remove the logicalrouterId element from the XML as we need to post it... $routing.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query 'child::logicalrouterId')) ) | Out-Null #Validate the learner protocol node exists on the logicalrouter if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query "child::$($RedistributionRule.learner)")) { throw "Rule learner protocol $($RedistributionRule.learner) is not enabled on LogicalRouter $logicalrouterId. Use Get-NsxLogicalRouter | Get-NsxLogicalRouterrouting | Get-NsxLogicalRouterRedistributionRule to get the rule you want to remove." } #Make XPath do all the hard work... Wish I was able to just compare the from node, but id doesnt appear possible with xpath 1.0 $xpathQuery = "child::$($RedistributionRule.learner)/redistribution/rules/rule[action=`"$($RedistributionRule.action)`"" $xPathQuery += " and from/connected=`"$($RedistributionRule.from.connected)`" and from/static=`"$($RedistributionRule.from.static)`"" $xPathQuery += " and from/ospf=`"$($RedistributionRule.from.ospf)`" and from/bgp=`"$($RedistributionRule.from.bgp)`"" if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $RedistributionRule -query 'child::from/isis')) { $xPathQuery += " and from/isis=`"$($RedistributionRule.from.isis)`"" } if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $RedistributionRule -query 'child::prefixName')) { $xPathQuery += " and prefixName=`"$($RedistributionRule.prefixName)`"" } $xPathQuery += "]" Write-Debug "XPath query for rule node to remove is: $xpathQuery" $RuleToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $routing -query $xpathQuery) if ( $RuleToRemove ) { Write-Debug "RuleToRemove Element is: `n $($RuleToRemove | Format-XML) " $routing.$($RedistributionRule.Learner).redistribution.rules.RemoveChild($RuleToRemove) | Out-Null $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $routing.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed } } else { Throw "Rule Id $($RedistributionRule.Id) was not found in the $($RedistributionRule.Learner) routing configuration for LogicalRouter $logicalrouterId" } } end {} } function New-NsxLogicalRouterRedistributionRule { <# .SYNOPSIS Creates a new route redistribution rule and adds it to the specified LogicalRouters configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers perform ipv4 and ipv6 routing functions for connected networks and support both static and dynamic routing via OSPF and BGP. The New-NsxLogicalRouterRedistributionRule cmdlet adds a new route redistribution rule to the configuration of the specified LogicalRouter. .EXAMPLE Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | New-NsxLogicalRouterRedistributionRule -PrefixName test -Learner ospf -FromConnected -FromStatic -Action permit Create a new permit Redistribution Rule for prefix test (note, prefix must already exist, and is case sensistive) for ospf. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalRouterRouting $_ })] [System.Xml.XmlElement]$LogicalRouterRouting, [Parameter (Mandatory = $True)] [ValidateSet("ospf", "bgp", IgnoreCase = $false)] [String]$Learner, [Parameter (Mandatory = $false)] [String]$PrefixName, [Parameter (Mandatory = $false)] [switch]$FromConnected, [Parameter (Mandatory = $false)] [switch]$FromStatic, [Parameter (Mandatory = $false)] [switch]$FromOspf, [Parameter (Mandatory = $false)] [switch]$FromBgp, [Parameter (Mandatory = $False)] [ValidateSet("permit", "deny", IgnoreCase = $false)] [String]$Action = "permit", [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LogicalRouterRouting = $LogicalRouterRouting.CloneNode($true) #Store the logicalrouterId and remove it from the XML as we need to post it... $logicalrouterId = $_LogicalRouterRouting.logicalrouterId $_LogicalRouterRouting.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query 'child::logicalrouterId')) ) | Out-Null #Need to do an xpath query here rather than use PoSH dot notation to get the protocol element, #as it might not exist which wil cause PoSH to throw in stric mode. $ProtocolElement = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterRouting -query "child::$Learner") if ( (-not $ProtocolElement) -or ($ProtocolElement.Enabled -ne 'true')) { throw "The $Learner protocol is not enabled on LogicalRouter $logicalrouterId. Enable it and try again." } else { #Create the new rule element. $Rule = $_LogicalRouterRouting.ownerDocument.CreateElement('rule') (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ProtocolElement -query 'child::redistribution/rules').AppendChild($Rule) | Out-Null Add-XmlElement -xmlRoot $Rule -xmlElementName "action" -xmlElementText $Action if ( $PsBoundParameters.ContainsKey("PrefixName") ) { Add-XmlElement -xmlRoot $Rule -xmlElementName "prefixName" -xmlElementText $PrefixName.ToString() } #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('FromConnected') -or $PsBoundParameters.ContainsKey('FromStatic') -or $PsBoundParameters.ContainsKey('FromOspf') -or $PsBoundParameters.ContainsKey('FromBgp') ) { $FromElement = $Rule.ownerDocument.CreateElement('from') $Rule.AppendChild($FromElement) | Out-Null if ( $PsBoundParameters.ContainsKey("FromConnected") ) { Add-XmlElement -xmlRoot $FromElement -xmlElementName "connected" -xmlElementText $FromConnected.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("FromStatic") ) { Add-XmlElement -xmlRoot $FromElement -xmlElementName "static" -xmlElementText $FromStatic.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("FromOspf") ) { Add-XmlElement -xmlRoot $FromElement -xmlElementName "ospf" -xmlElementText $FromOspf.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("FromBgp") ) { Add-XmlElement -xmlRoot $FromElement -xmlElementName "bgp" -xmlElementText $FromBgp.ToString().ToLower() } } $URI = "/api/4.0/edges/$($LogicalRouterId)/routing/config" $body = $_LogicalRouterRouting.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed (Get-NsxLogicalRouter -objectId $LogicalRouterId -Connection $connection | Get-NsxLogicalRouterRouting | Get-NsxLogicalRouterRedistributionRule -Learner $Learner)[-1] } } } end {} } # Bridging function Get-NsxLogicalRouterBridging { <# .SYNOPSIS Retrieves bridging configuration for the specified NSX LogicalRouter. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers act as the configuration entity for enabling layer 2 bridging within a NSX environment. Although the Logical Router control VM is not part of the datapath, it does control which hypervisor is active for a given bridge instance. A Bridge is configured between a single VD Port Group and a single Logical Switch Each Logical Router can define the configuration of multiple bridges. The Get-NsxLogicalRouterBridging cmdlet retrieves the bridge configuration of the specified LogicalRouter. .EXAMPLE Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterBridging Retrieve the bridging configuration for LogicalRouter01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLogicalRouter $_ })] [System.Xml.XmlElement]$LogicalRouter ) begin { } process { #We append the LogicalRouter-id to the associated Bridging config XML to enable pipeline workflows and #consistent readable output $_LogicalRouterBridging = $LogicalRouter.features.bridges.CloneNode($True) Add-XmlElement -xmlRoot $_LogicalRouterBridging -xmlElementName "logicalrouterId" -xmlElementText $LogicalRouter.Id $_LogicalRouterBridging } end {} } function Set-NsxLogicalRouterBridging { <# .SYNOPSIS Configures bridging configuration for the specified NSX LogicalRouter. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers act as the configuration entity for enabling layer 2 bridging within a NSX environment. Although the Logical Router control VM is not part of the datapath, it does control which hypervisor is active for a given bridge instance. A Bridge is configured between a single VD Port Group and a single Logical Switch Each Logical Router can define the configuration of multiple bridges. The Set-NsxLogicalRouterBridging cmdlet configures the bridge configuration of the specified LogicalRouter. .EXAMPLE Get-NsxLogicalRouter BridgeRouter | Get-NsxLogicalRouterBridging | Set-NsxLogicalRouterBridging -Enabled Enable bridging on the LogicalRouter called BridgeRouter #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #LogicalRouter Bridging object as retrieved by Get-NsxLogicalRouterBridging [ValidateScript( { ValidateLogicalRouterBridging $_ })] [System.Xml.XmlElement]$LogicalRouterBridging, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $True)] #Enable Bridge support. [switch]$Enabled, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LogicalRouterBridging = $LogicalRouterBridging.CloneNode($true) #Store the logicalrouterId and remove it from the XML as we need to post it... $logicalrouterId = $_LogicalRouterBridging.logicalrouterId $_LogicalRouterBridging.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterBridging -query 'child::logicalrouterId')) ) | Out-Null #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('Enabled')) { $_LogicalRouterBridging.Enabled = $Enabled.ToString().ToLower() } $URI = "/api/4.0/edges/$($LogicalRouterId)/bridging/config" $body = $_LogicalRouterBridging.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed Get-NsxLogicalRouter -objectId $LogicalRouterId -Connection $connection | Get-NsxLogicalRouterBridging } } end {} } function New-NsxLogicalRouterBridge { <# .SYNOPSIS Creates a new static route and adds it to the specified ESGs routing configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers act as the configuration entity for enabling layer 2 bridging within a NSX environment. Although the Logical Router control VM is not part of the datapath, it does control which hypervisor is active for a given bridge instance. A Bridge is configured between a single VD Port Group and a single Logical Switch Each Logical Router can define the configuration of multiple bridges. The New-NsxLogicalRouterBridge cmdlet creates a new bridge instance configured via the specifid logical router. .EXAMPLE Get-NsxLogicalRouter BridgeRouter | Get-NsxLogicalRouterBridging | New-NsxLogicalRouterBridge -Name "bridge1" -PortGroup $bridgepg1 -LogicalSwitch $bridgels1 Create a bridge between vdportgroup $bridgepg1 and logical switch $bridgels1 on logicalrouter BridgeRouter. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLogicalRouterBridging $_ })] [System.Xml.XmlElement]$LogicalRouterBridging, [Parameter (Mandatory = $True)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $True)] [VMware.VimAutomation.ViCore.Interop.V1.Host.Networking.DistributedPortGroupInterop]$PortGroup, [Parameter (Mandatory = $True)] [ValidateScript( { ValidateLogicalSwitchOrDistributedPortGroup $_ } )] [System.Xml.XmlElement]$LogicalSwitch, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LogicalRouterBridging = $LogicalRouterBridging.CloneNode($true) #Store the logicalrouterId and remove it from the XML as we need to post it... $logicalrouterId = $_LogicalRouterBridging.logicalrouterId $_LogicalRouterBridging.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LogicalRouterBridging -query 'child::logicalrouterId')) ) | Out-Null #Create the new bridge element. $Bridge = $_LogicalRouterBridging.ownerDocument.CreateElement('bridge') #Need to do an xpath query here rather than use PoSH dot notation to get the static route element, #as it might be empty, and PoSH silently turns an empty element into a string object, which is rather not what we want... :| $_LogicalRouterBridging.AppendChild($Bridge) | Out-Null Add-XmlElement -xmlRoot $Bridge -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $Bridge -xmlElementName "virtualWire" -xmlElementText $LogicalSwitch.objectId Add-XmlElement -xmlRoot $Bridge -xmlElementName "dvportGroup" -xmlElementText $PortGroup.ExtensionData.Moref.Value $URI = "/api/4.0/edges/$($LogicalRouterId)/bridging/config" $body = $_LogicalRouterBridging.OuterXml Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed Get-NsxLogicalRouter -objectId $LogicalRouterId -Connection $connection | Get-NsxLogicalRouterBridging | Get-NsxLogicalRouterBridge -Name $Name } end {} } function Get-NsxLogicalRouterBridge { <# .SYNOPSIS Retrieves bridge instances from the specified NSX LogicalRouter Bridging configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers act as the configuration entity for enabling layer 2 bridging within a NSX environment. Although the Logical Router control VM is not part of the datapath, it does control which hypervisor is active for a given bridge instance. A Bridge is configured between a single VD Port Group and a single Logical Switch Each Logical Router can define the configuration of multiple bridges. The Get-NsxLogicalRouterBridge cmdlet retrieves the bridge instances configured within the specified LogicalRouter Bridging Configuration. .EXAMPLE Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterBridging | Get-NSxLogicalRouterBridge #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #Logical Router Bridging Configuration as returned by Get-NsxLogicalRouterBridging [ValidateScript( { ValidateLogicalRouterBridging $_ })] [System.Xml.XmlElement]$LogicalRouterBridging, [Parameter(Mandatory = $False, ParameterSetName = "Name")] #Bridge instance name [string]$Name, [Parameter(Mandatory = $False, ParameterSetName = "BridgeId")] #Bridge Instance Id [int]$BridgeId ) begin { } process { $logicalrouterId = $LogicalRouterBridging.logicalrouterId if ( Invoke-XpathQuery -Node $LogicalRouterBridging -QueryMethod SelectNodes -query "child::bridge") { #Add LogicalRouterId so we can easily retrieve later in a remove pipeline. foreach ( $bridge in $LogicalRouterBridging.bridge ) { Add-XmlElement -xmlRoot $Bridge -xmlElementName "logicalrouterId" -xmlElementText $logicalrouterId } if ( $PSBoundParameters.ContainsKey("Name")) { $LogicalRouterBridging.bridge | Where-Object { $_.Name -eq $Name } } elseif ( $PSBoundParameters.ContainsKey("BridgeId")) { $LogicalRouterBridging.bridge | Where-Object { $_.bridgeId -eq $BridgeId } } else { $LogicalRouterBridging.bridge } } } end {} } function Remove-NsxLogicalRouterBridge { <# .SYNOPSIS Removes a bridge instances from the specified Logical Routers bridging configuration. .DESCRIPTION An NSX Logical Router is a distributed routing function implemented within the ESXi kernel, and optimised for east west routing. Logical Routers act as the configuration entity for enabling layer 2 bridging within a NSX environment. Although the Logical Router control VM is not part of the datapath, it does control which hypervisor is active for a given bridge instance. A Bridge is configured between a single VD Port Group and a single Logical Switch Each Logical Router can define the configuration of multiple bridges. The Remove-NsxLogicalRouterBridge cmdlet removes the specified bridge instance from its associated LogicalRouter Bridging Configuration. .EXAMPLE Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterBridging | Get-NSxLogicalRouterBridge -Name Bridge1 | Remove-NsxLogicalRouterBridge Remove the bridge Bridge1 on LogicalRouter01 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] #The bridge instance to remove as retrieved by Get-NsxLogicalRouterBridge. [ValidateScript( { ValidateLogicalRouterBridge $_ })] [System.Xml.XmlElement]$BridgeInstance, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get the routing config for our LogicalRouter $logicalrouterId = $BridgeInstance.logicalrouterId $bridging = Get-NsxLogicalRouter -objectId $logicalrouterId -Connection $connection | Get-NsxLogicalRouterBridging #Remove the logicalrouterId element from the XML as we need to post it... $bridging.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bridging -query 'child::logicalrouterId')) ) | Out-Null #Need to do an xpath query here to query for a bridge that matches the one passed in. $BridgeToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $bridging -query "child::bridge[bridgeId=`"$($BridgeInstance.bridgeId)`"]" ) if ( $BridgeToRemove ) { Write-Debug "$($MyInvocation.MyCommand.Name) : BridgeToRemove Element is: `n $($BridgeToRemove.OuterXml | Format-XML) " $bridging.RemoveChild($BridgeToRemove) | Out-Null $URI = "/api/4.0/edges/$($LogicalRouterId)/bridging/config" $body = $bridging.OuterXml if ( $confirm ) { $message = "LogicalRouter routing update will modify existing LogicalRouter configuration." $question = "Proceed with Update of LogicalRouter $($LogicalRouterId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update LogicalRouter $($LogicalRouterId)" -Completed } } else { Throw "Bridge $($BridgeInstance.Name) ($($BridgeInstance.BridgeId)) was not found in bridge configuration for LogicalRouter $logicalrouterId" } } end {} } ######### ######### # Grouping related Collections function Get-NsxSecurityGroup { <# .SYNOPSIS Retrieves NSX Security Groups .DESCRIPTION An NSX Security Group is a grouping construct that provides a powerful grouping function that can be used in DFW Firewall Rules and the NSX Service Composer. This cmdlet returns Security Groups objects. .EXAMPLE PS C:\> Get-NsxSecurityGroup TestSG #> [CmdLetBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $false, ParameterSetName = "objectId")] #Get SecurityGroups by objectid [string]$objectId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [Parameter (Mandatory = $false, ParameterSetName = "UniversalOnly", Position = 1)] [Parameter (Mandatory = $false, ParameterSetName = "LocalOnly", Position = 1)] #Get SecurityGroups by name [string]$name, [Parameter (Mandatory = $false)] #ScopeId of IPSet. Can define multiple scopeIds in a list to iterate accross scopes. [string[]]$scopeId, [Parameter (Mandatory = $true, ParameterSetName = "UniversalOnly")] #Return only Universal objects [switch]$UniversalOnly, [Parameter (Mandatory = $true, ParameterSetName = "LocalOnly")] #Return only Locally scoped objects [switch]$LocalOnly, [Parameter (Mandatory = $true, ParameterSetName = "VirtualMachine", ValueFromPipeLine = $true)] #Virtual Machine to check for group membership [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop]$VirtualMachine, [Parameter (Mandatory = $false)] #Include default system security group [switch]$IncludeSystem = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { if (-not $PsBoundParameters.ContainsKey("scopeId") ) { switch ( $PSCmdlet.ParameterSetName ) { "UniversalOnly" { $scopeid = "universalroot-0" } "LocalOnly" { $scopeid = "globalroot-0" } Default { $scopeId = "globalroot-0", "universalroot-0" } } } } process { if ( $PSBoundParameters.ContainsKey("VirtualMachine")) { $VMMoRef = $VirtualMachine.ExtensionData.Moref.Value $uri = "/api/2.0/services/securitygroup/lookup/virtualmachine/$VMMoRef" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::securitygroup')) { $response.securitygroups.securityGroups.securityGroup } } elseif ( -not $objectId ) { $sg = @() foreach ($scope in $scopeid ) { #All Security Groups $URI = "/api/2.0/services/securitygroup/scope/$scope" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::list/securitygroup')) { if ( $Name ) { $sg += $response.list.securitygroup | Where-Object { $_.name -eq $name } } else { $sg += $response.list.securitygroup } } } #Filter default if switch not set if ( -not $IncludeSystem ) { $sg | Where-Object { ( $_.objectId -ne 'securitygroup-1') } } else { $sg } } else { #Just getting a single Security group $URI = "/api/2.0/services/securitygroup/$objectId" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::securitygroup')) { $sg = $response.securitygroup } #Filter default if switch not set if ( -not $IncludeSystem ) { $sg | Where-Object { ( $_.objectId -ne 'securitygroup-1') } } else { $sg } } } end {} } function New-NsxSecurityGroup { <# .SYNOPSIS Creates a new NSX Security Group. .DESCRIPTION An NSX Security Group is a grouping construct that provides a powerful grouping function that can be used in DFW Firewall Rules and the NSX Service Composer. This cmdlet creates a new NSX Security Group. A Security Group can consist of Static Includes and Excludes as well as dynamic matching properties. At this time, this cmdlet supports only static include/exclude members. A valid PowerCLI session is required to pass certain types of objects supported by the IncludeMember and ExcludeMember parameters. .EXAMPLE Example1: Create a new SG and include App01 and App02 VMs (get-vm requires a valid PowerCLI session) PS C:\> New-NsxSecurityGroup -Name TestSG -Description "Test creating an NSX SecurityGroup" -IncludeMember (get-vm app01),(get-vm app02) Example2: Create a new SG and include cluster1 except for App01 and App02 VMs (get-vm and get-cluster requires a valid PowerCLI session) PS C:\> New-NsxSecurityGroup -Name TestSG -Description "Test creating an NSX SecurityGroup" -IncludeMember (get-cluster cluster1) -ExcludeMember (get-vm app01),(get-vm app02) #> [CmdletBinding()] param ( [Parameter (Mandatory = $true)] #Name of the Security Group [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] #Optional description for the new Security Group [ValidateNotNull()] [string]$Description = "", [Parameter (Mandatory = $false)] #Static include membership [ValidateScript( { ValidateSecurityGroupMember $_ })] [object[]]$IncludeMember, [Parameter (Mandatory = $false)] #Static exclude membership [ValidateScript( { ValidateSecurityGroupMember $_ })] [object[]]$ExcludeMember, [Parameter (Mandatory = $false)] #Scope of object. For universal object creation, use the -Universal switch. [ValidateScript( { if ($_ -match "^globalroot-0$|universalroot-0$|^edge-\d+$") { $True } else { Throw "$_ is not a valid scope. Valid options are: globalroot-0 | universalroot-0 | edge-id" } })] [string]$scopeId = "globalroot-0", [Parameter (Mandatory = $false)] #Create the IPSet as Universal object. [switch]$Universal = $false, [Parameter (Mandatory = $false)] #Return only an object ID, not the full object. [switch]$ReturnObjectIdOnly = $false, [Parameter (Mandatory = $False)] #Flag to allow static membership of Universal Security Tags and dynamic membership via VM Name. See https://blogs.vmware.com/networkvirtualization/2017/02/nsx-6-3-cross-vc-nsx-security-enhancements.html/ [switch]$ActiveStandbyDeployment = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { if ( (-not $Universal) -and ( $ActiveStandbyDeployment) ) { throw "SecurityGroup must be of universal scope for Active Standby flag to be specified." } } process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("securitygroup") $xmlDoc.appendChild($xmlRoot) | Out-Null Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText $Description if ( $includeMember ) { foreach ( $Member in $IncludeMember) { [System.XML.XMLElement]$xmlMember = $XMLDoc.CreateElement("member") $xmlroot.appendChild($xmlMember) | Out-Null #This is probably not safe - need to review all possible input types to confirm. if ($Member -is [System.Xml.XmlElement] ) { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "objectId" -xmlElementText $member.objectId } else { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "objectId" -xmlElementText $member.ExtensionData.MoRef.Value } } } if ( $excludeMember ) { foreach ( $Member in $ExcludeMember) { [System.XML.XMLElement]$xmlMember = $XMLDoc.CreateElement("excludeMember") $xmlroot.appendChild($xmlMember) | Out-Null #This is probably not safe - need to review all possible input types to confirm. if ($Member -is [System.Xml.XmlElement] ) { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "objectId" -xmlElementText $member.objectId } else { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "objectId" -xmlElementText $member.ExtensionData.MoRef.Value } } } if (( $ActiveStandbyDeployment ) -and ( $Universal )) { [System.XML.XMLElement]$xmlMember = $XMLDoc.CreateElement("extendedAttributes") $xmlroot.appendChild($xmlMember) | Out-Null [System.XML.XMLElement]$xmlsubMember = $XMLDoc.CreateElement("extendedAttribute") Add-XmlElement -xmlRoot $xmlSubMember -xmlElementName "name" -xmlElementText "localMembersOnly" Add-XmlElement -xmlRoot $xmlSubMember -xmlElementName "value" -xmlElementText "true" $xmlmember.appendChild($xmlsubMember) | Out-Null } #Do the post $body = $xmlroot.OuterXml if ( $universal ) { $scopeId = "universalroot-0" } $URI = "/api/2.0/services/securitygroup/bulk/$($scopeId.ToLower())" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection if ( $ReturnObjectIdOnly) { $response.content } else { Get-NsxSecurityGroup -objectId $response.content -Connection $connection } } end {} } function Remove-NsxSecurityGroup { <# .SYNOPSIS Removes the specified NSX Security Group. .DESCRIPTION An NSX Security Group is a grouping construct that provides a powerful grouping function that can be used in DFW Firewall Rules and the NSX Service Composer. This cmdlet deletes a specified Security Groups object. If the object is currently in use the api will return an error. Use -force to override but be aware that the firewall rulebase will become invalid and will need to be corrected before publish operations will succeed again. .EXAMPLE Get-NsxSecurityGroup TestSG | Remove-NsxSecurityGroup Remove the SecurityGroup TestSG .EXAMPLE $sg | Remove-NsxSecurityGroup -confirm:$false Remove the SecurityGroup $sg without confirmation. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #SecurityGroup object as returned by get-nsxsecuritygroup [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$SecurityGroup, [Parameter (Mandatory = $False)] #Disable confirmation prompt [switch]$confirm = $true, [Parameter (Mandatory = $False)] #Force deletion of in use or system objects [switch]$force = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if (($SecurityGroup.ObjectId -eq 'securitygroup-1') -and ( -not $force)) { Write-Warning "Not removing $($SecurityGroup.Name) as it is a default SecurityGroup. Use -Force to force deletion." } else { if ( $confirm ) { $message = "Security Group removal is permanent." $question = "Proceed with removal of Security group $($SecurityGroup.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { if ( $force ) { $URI = "/api/2.0/services/securitygroup/$($SecurityGroup.objectId)?force=true" } else { $URI = "/api/2.0/services/securitygroup/$($SecurityGroup.ObjectId)?force=false" } Write-Progress -Activity "Remove Security Group $($SecurityGroup.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Security Group $($SecurityGroup.Name)" -Completed } } } end {} } function Get-NsxSecurityGroupMemberTypes { <# .SYNOPSIS Retrieves all potential NSX Security Group Member Types .DESCRIPTION An NSX Security Group is a grouping construct that provides a powerful grouping function that can be used in DFW Firewall Rules and the NSX Service Composer. This cmdlet queries the NSX API to determine all the applicable member types that can be added to a Security Group. .EXAMPLE Get-NsxSecurityGroupMemberTypes IPSet ClusterComputeResource VirtualMachine VirtualWire SecurityGroup DirectoryGroup VirtualApp ResourcePool DistributedVirtualPortgroup Datacenter Network Vnic SecurityTag MACSet #> param ( [Parameter (Mandatory = $false)] #Scopeid - default globalroot-0 [string]$scopeId = "globalroot-0", [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { $URI = "/api/2.0/services/securitygroup/scope/$scopeId/memberTypes" $response = Invoke-NsxWebRequest -method "get" -URI $URI -connection $connection [xml]$Members = $response.Content $members.list.objecttype.typeName } end {} } function Add-NsxSecurityGroupMember { <# .SYNOPSIS Adds a new member to an existing NSX Security Group. .DESCRIPTION An NSX Security Group is a grouping construct that provides a powerful grouping function that can be used in DFW Firewall Rules and the NSX Service Composer. This cmdlet adds a new member to an existing NSX Security Group. A Security Group can consist of Static Includes and Excludes as well as dynamic matching properties. At this time, this cmdlet supports only static include/exclude members. A valid PowerCLI session is required to pass certain types of objects supported by the IncludeMember and ExcludeMember parameters. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #SecurityGroup whose membership is to be modified. [ValidateNotNullOrEmpty()] [object]$SecurityGroup, [Parameter (Mandatory = $False)] #Throw an error if the member already exists (by default will ignore) [switch]$FailIfExists = $false, [Parameter (Mandatory = $False)] #The specified members are to be added to the security group as exclusions [switch]$MemberIsExcluded = $false, [Parameter (Mandatory = $true)] #The member(s) to be added [ValidateScript( { ValidateSecurityGroupMember $_ })] [object[]]$Member, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { #Populate the global membertype cache if not already done #Using the API rather than hardcoding incase this changes with versions of NSX if ( -not (Test-Path Variable:\NsxMemberTypes) ) { $script:NsxMemberTypes = Get-NsxSecurityGroupMemberTypes } } process { #Get our internal SG object and id. The internal obejct is used to modify and put for bulk update. if ( $SecurityGroup -is [System.Xml.XmlElement] ) { $SecurityGroupId = $securityGroup.objectId $_SecurityGroup = $SecurityGroup.cloneNode($true) } elseif ( ($securityGroup -is [string]) -and ($SecurityGroup -match "securitygroup-\d+")) { $SecurityGroupId = $securityGroup $_SecurityGroup = Get-NsxSecurityGroup -objectId $SecurityGroupId -Connection $connection } else { throw "Invalid SecurityGroup specified. Specify a PowerNSX SecurityGroup object or a valid securitygroup objectid." } if ( $PsBoundParameters.ContainsKey('Member') ) { foreach ( $_Member in $Member) { if ($_Member -is [System.Xml.XmlElement] ) { $MemberMoref = $_Member.objectId } elseif ( ($_Member -is [string]) -and ($_Member -match "^vm-\d+$|^resgroup-\d+$|^dvportgroup-\d+$|^directory_group-\d+$|^domain-c\d+$" )) { $MemberMoref = $_Member } elseif ( ($_Member -is [string] ) -and ( [guid]::tryparse(($_Member -replace ".\d{3}$", ""), [ref][guid]::Empty)) ) { $MemberMoref = $_Member } elseif (( $_Member -is [string]) -and ( $NsxMemberTypes -contains ($_Member -replace "-\d+$") ) ) { $MemberMoref = $_Member } elseif ( $_Member -is [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop] ) { #See NSX API guide 'Attach or Detach a Virtual Machine from a Logical Switch' for #how to construct NIC id. $vmUuid = ($_Member.parent | Get-View).config.instanceuuid $MemberMoref = "$vmUuid.$($_Member.id.substring($_Member.id.length-3))" } elseif (( $_Member -is [VMware.VimAutomation.ViCore.Interop.V1.VIObjectInterop]) -and ( $NsxMemberTypes -contains $_Member.ExtensionData.MoRef.Type)) { $MemberMoref = $_Member.ExtensionData.MoRef.Value } else { throw "Invalid member specified $($_Member)" } if ( $FailIfExists) { #Need to check before adding the member, because we are now using bulk update, the API doesnt support detecting duplicates. #To support the prior functionality of FailIfExists, we have to check ourselves... if ( Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SecurityGroup -query "child::member[objectId=`"$MemberMoref`"]" ) { throw "Member $($_Member.Name) ($MemberMoref) is already a member of the specified SecurityGroup." } } #Create a new member node if ( $MemberIsExcluded ) { $null = $memberxml = $_SecurityGroup.OwnerDocument.CreateElement("excludeMember") } else { $null = $memberxml = $_SecurityGroup.OwnerDocument.CreateElement("member") } $null = $_SecurityGroup.AppendChild($memberxml) Add-XmlElement -xmlRoot $memberxml -xmlElementName "objectId" -xmlElementText $MemberMoref } $URI = "/api/2.0/services/securitygroup/bulk/$($SecurityGroupId)" Write-Progress -Activity "Updating membership of Security Group $SecurityGroupId" $null = Invoke-NsxWebRequest -method "put" -URI $URI -connection $connection -body $_SecurityGroup.OuterXml Write-Progress -Activity "Updating membership of Security Group $SecurityGroupId" -Completed } #Get-NsxSecurityGroup -objectId $SecurityGroup.objectId -connection $connection } end {} } function Remove-NsxSecurityGroupMember { <# .SYNOPSIS Removes a member from an existing NSX Security Group. .DESCRIPTION An NSX Security Group is a grouping construct that provides a powerful grouping function that can be used in DFW Firewall Rules and the NSX Service Composer. This cmdlet removes a member from an existing NSX Security Group. A Security Group can consist of Static Includes and Excludes as well as dynamic matching properties. At this time, this cmdlet supports only static include/exclude members. A valid PowerCLI session is required to pass certain types of objects supported by the IncludeMember and ExcludeMember parameters. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateNotNullOrEmpty()] [object]$SecurityGroup, [Parameter (Mandatory = $False)] [switch]$FailIfAbsent = $true, [Parameter (Mandatory = $False)] #The specified exclude members are to be removed from the security group [switch]$MemberIsExcluded = $false, [Parameter (Mandatory = $true)] [ValidateScript( { ValidateSecurityGroupMember $_ })] [object[]]$Member, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { #Populate the global membertype cache if not already done #Using the API rather than hardcoding incase this changes with versions of NSX if ( -not (Test-Path Variable:\NsxMemberTypes) ) { $script:NsxMemberTypes = Get-NsxSecurityGroupMemberTypes } } process { $modified = $False #Get our internal SG object and id. The internal obejct is used to modify and put for bulk update. if ( $SecurityGroup -is [System.Xml.XmlElement] ) { $SecurityGroupId = $securityGroup.objectId $_SecurityGroup = $SecurityGroup.cloneNode($true) } elseif ( ($securityGroup -is [string]) -and ($SecurityGroup -match "securitygroup-\d+")) { $SecurityGroupId = $securityGroup $_SecurityGroup = Get-NsxSecurityGroup -objectId $SecurityGroupId -Connection $connection } else { throw "Invalid SecurityGroup specified. Specify a PowerNSX SecurityGroup object or a valid securitygroup objectid." } if ( $PsBoundParameters.ContainsKey('Member') ) { foreach ( $_Member in $Member) { if ($_Member -is [System.Xml.XmlElement] ) { $MemberMoref = $_Member.objectId } elseif ( ($_Member -is [string]) -and ($_Member -match "^vm-\d+$|^resgroup-\d+$|^dvportgroup-\d+$|^directory_group-\d+$|^domain-c\d+$" )) { $MemberMoref = $_Member } elseif ( ($_Member -is [string] ) -and ( [guid]::tryparse(($_Member -replace ".\d{3}$", ""), [ref][guid]::Empty)) ) { $MemberMoref = $_Member } elseif (( $_Member -is [string]) -and ( $NsxMemberTypes -contains ($_Member -replace "-\d+$") ) ) { $MemberMoref = $_Member } elseif ( $_Member -is [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop] ) { #See NSX API guide 'Attach or Detach a Virtual Machine from a Logical Switch' for #how to construct NIC id. $vmUuid = ($_Member.parent | Get-View).config.instanceuuid $MemberMoref = "$vmUuid.$($_Member.id.substring($_Member.id.length-3))" } elseif (( $_Member -is [VMware.VimAutomation.ViCore.Interop.V1.VIObjectInterop]) -and ( $NsxMemberTypes -contains $_Member.ExtensionData.MoRef.Type)) { $MemberMoref = $_Member.ExtensionData.MoRef.Value } else { throw "Invalid member specified $($_Member)" } # Check for the correct member type (inclue or exclude member) if ( $MemberIsExcluded ) { $existingMember = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SecurityGroup -query "child::excludeMember[objectId=`"$MemberMoref`"]" ) } else { $existingMember = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SecurityGroup -query "child::member[objectId=`"$MemberMoref`"]" ) } if ( $FailIfAbsent) { #To support the prior functionality of failIfAbsent, we have to check ourselves... if ( $null -eq $existingMember ) { throw "Member $(if ($_Member | Get-Member -MemberType Properties -Name Name) {$_member.name}) ($MemberMoref) is not a member of the specified SecurityGroup." } } #Need to check before removing the member, because we are now using bulk update, the API doesnt do this for us. if ($existingMember) { $null = $_SecurityGroup.Removechild($existingMember) $modified = $True } } # There is no reason to just blindly update the configuration as # there may be no changes required, so we only do it if we find the # member/excludeMember object via the xPath query if ($modified) { $URI = "/api/2.0/services/securitygroup/bulk/$($SecurityGroupId)" Write-Progress -Activity "Updating membership of Security Group $SecurityGroupId" $null = Invoke-NsxWebRequest -method "put" -URI $URI -connection $connection -body $_SecurityGroup.OuterXml Write-Progress -Activity "Updating membership of Security Group $SecurityGroupId" -Completed } } #Get-NsxSecurityGroup -objectId $SecurityGroup.objectId -connection $connection } end {} } ######### ######### # Dynamic Membership function New-NsxDynamicCriteriaSpec { <# .SYNOPSIS Creates a new Security Group Dynamic Membership Criteria Spec. .DESCRIPTION NSX Security Groups can have 3 types of membership configured, Dynamic Criteria, Static Members and Exclude Members. One or more Dynamic Criteria combine to make a Dynamic Member Set, and one or more Dynamic Member Sets combine to define the Dynamic Membership of a given security group. In order to allow the configuration of a security groups Dynamic Membership with an aritrary number of Dynamic Criteria Member Sets that contain an arbitrary number of Dynamic Criteria in a flexible way, PowerNSX provides the following abstractions. Creation of individual Dynamic Criteria is accomplished with New-NsxDynamicCriteriaSpec. One or more Dynamic Criteria can be added to a Dynamic Member Set at creation time with Add-NsxDynamicMemberSet and specifying the required Dynamic Criteria Spec objects at creation time. One or more Dynamic Criteria can be added to an existing Dynamic Member Set after the fact with Add-NsxDynamicCriteria or removed with Remove-NsxDynamicCriteria. One or more Dynamic Member sets can be added to a security groups overall Dynamic Membership definition using Add-NsxDynamicMemberSet or removed using Remove-NsxDynamicMemberSet A Security Groups Dynamic Member definition can include multiple Dynamic Member Sets in an logical AND/OR arrangement, and for each of the Dynamic Member Sets, a match operator of ALL or ANY can be specified that determines how multiple Dynamic Criteria within the set combine to define a match. Dynamic Criteria consist of the following three elements: The Key: This is the attribute that is to be evaluated. The list of Keys available (along with their UI representation) are as follows: Key UI Name ----------------------- -------- OSName Computer OS Name ComputerName Computer Name VmName VM Name SecurityTag Security Tag The condition: This is the criteria that will be used to evaluate the provided value. The possible options for condition are as follows: Condition UI Name --------------- --------------------------- contains Contains ends_with Ends with starts_with Starts with equals Equals to notequals Not Equals to regex Matches regular expression The Value: This is the string of text that is required to be matched against the Key provided using the condition specified. It is also possible to specify an object to use as part of a Dynamic Criteria Spec. To do this, a valid PowerCLI or PowerNSX object must be specified using the entity parameter. Using the entity parameter is the equivalant of statically including the object within the Dynamic Criteria Spec. A valid PowerCLI session is required to pass certain types of objects when specifying an entity. .EXAMPLE $criteriaSpec11 = New-NsxDynamicCriteriaSpec -key VmName -condition contains -value "VM" Match all VMs where the VM name contains the string "VM" VM Name Matched --------------- ------- Test-VM-01 Yes Test-VM-42 Yes Test-VM-142 Yes Prod-VM-01 Yes Prod-PCI-VM-01 Yes Test-VM-01-Template Yes WIN-DC-01 No .EXAMPLE $criteriaSpec12 = New-NsxDynamicCriteriaSpec -key VmName -condition equals -value "Test-VM-01" Match all VMs where the VM name is equal to the string "Test-VM-01" VM Name Matched --------------- ------- Test-VM-01 Yes Test-VM-42 No Test-VM-142 No Prod-VM-01 No Prod-PCI-VM-01 No Test-VM-01-Template No WIN-DC-01 No .EXAMPLE $criteriaSpec13 = New-NsxDynamicCriteriaSpec -key VmName -condition notequals -value "Test-VM-01" Match all VMs where the VM name is NOT equal to the string "Test-VM-01" VM Name Matched --------------- ------- Test-VM-01 No Test-VM-42 Yes Test-VM-142 Yes Prod-VM-01 Yes Prod-PCI-VM-01 Yes Test-VM-01-Template Yes WIN-DC-01 Yes .EXAMPLE $criteriaSpec14 = New-NsxDynamicCriteriaSpec -key VmName -condition starts_with -value "Test" Match all VMs where the VM name starts with the string "Test". VM Name Matched --------------- ------- Test-VM-01 Yes Test-VM-42 Yes Test-VM-142 Yes Prod-VM-01 No Prod-PCI-VM-01 No Test-VM-01-Template Yes WIN-DC-01 No .EXAMPLE $criteriaSpec15 = New-NsxDynamicCriteriaSpec -key VmName -condition ends_with -value "01" Match all VMs where the VM name ends with the string "01". VM Name Matched --------------- ------- Test-VM-01 Yes Test-VM-42 No Test-VM-142 No Prod-VM-01 Yes Prod-PCI-VM-01 Yes Test-VM-01-Template No WIN-DC-01 Yes .EXAMPLE $criteriaSpec16 = New-NsxDynamicCriteriaSpec -key VmName -condition regex -value "^Test-VM-[0-9]{2}$" Match all VMs where the VM name matches the supplied regular expression. VM Name Matched --------------- ------- Test-VM-01 Yes Test-VM-42 Yes Test-VM-142 No Prod-VM-01 No Prod-PCI-VM-01 No Test-VM-01-Template No WIN-DC-01 No .EXAMPLE $criteriaSpec21 = New-NsxDynamicCriteriaSpec -entity (Get-NsxLogicalSwitch DMZ-LS-1) Statically specify the NSX Logical Switch called DMZ-LS-1 to be included as part of the dynamic criteria .EXAMPLE $criteriaSpec22 = New-NsxDynamicCriteriaSpec -operator AND -entity $(Get-NsxSecurityGroup SG-PCI-Machines) Statically specify the NSX Security Group called SG-PCI-Machines to be included as part of the dynamic criteria #> [CmdletBinding(DefaultParameterSetName = "search")] param ( [Parameter(Mandatory = $true, ParameterSetName = "search")] # The attribute that is to be evaluated. The list of keys is described in the help description. [ ValidateSet("VMName", "ComputerName", "OSName", "SecurityTag") ] [String]$Key, [Parameter(Mandatory = $true, ParameterSetName = "search")] # The condition used to evaluate the criteria value against the its key [ ValidateSet("contains", "ends_with", "starts_with", "equals", "notequals", "regex") ] [String]$Condition, [Parameter(Mandatory = $true, ParameterSetName = "search")] # The value of the criteria to be evaluated. [ ValidateNotNullOrEmpty() ] [String]$Value, [Parameter(Mandatory = $true, ParameterSetName = "entity")] # The Entity to be matched. This can be a Valie PowerNSX such as logical switch or PowerCLI object such as VM. [ ValidateNotNullOrEmpty() ] [object]$Entity ) begin { $criteria = ConvertTo-NsxApiCriteriaCondition $Condition $_key = ConvertTo-NsxApiCriteriaKey $key #Populate the global membertype cache if not already done #Using the API rather than hardcoding incase this changes with versions of NSX if ( -not (Test-Path Variable:\NsxMemberTypes) ) { $script:NsxMemberTypes = Get-NsxSecurityGroupMemberTypes } # TODO: [DC] Maybe in the future making the cmdlet aware of a number of # entities being and creating the corresponding number of specs for it. # [NB] I would expect the user to loop creation of multiple specs rather than # include in the cmdlet itself. $entityCount = @($entity).count if ( $entityCount -ne 1 ) { throw "Multiple ($entityCount) entities specified . Only 1 is allowed." } } process { [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlDynamicCriteria = $XMLDoc.CreateElement("dynamicCriteria") $xmlDoc.appendChild($xmlDynamicCriteria) | Out-Null if ($PSCmdlet.ParameterSetName -eq "entity") { # if ($_Member -is [System.Xml.XmlElement] ) { if ($entity -is [System.Xml.XmlElement] ) { $EntityObjectId = $entity.objectId } elseif ( ($entity -is [string]) -and ($entity -match "^vm-\d+$|^resgroup-\d+$|^dvportgroup-\d+$|^directory_group-\d+$" )) { $EntityObjectId = $entity } # Match NIC identifier specified by user (eg UUID.000) elseif ( ($entity -is [string] ) -and ( [guid]::tryparse(($entity -replace ".\d{3}$", ""), [ref][guid]::Empty)) ) { $EntityObjectId = $entity } elseif (( $entity -is [string]) -and ( $NsxMemberTypes -contains ($entity -replace "-\d+$") ) ) { $EntityObjectId = $entity } elseif ( $entity -is [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop] ) { #See NSX API guide 'Attach or Detach a Virtual Machine from a Logical Switch' for #how to construct NIC id. $vmUuid = ($entity.parent | Get-View).config.instanceuuid $EntityObjectId = "$vmUuid.$($entity.id.substring($entity.id.length-3))" } elseif (( $entity -is [VMware.VimAutomation.ViCore.Interop.V1.VIObjectInterop]) -and ( $NsxMemberTypes -contains $entity.ExtensionData.MoRef.Type)) { $EntityObjectId = $entity.ExtensionData.MoRef.Value } else { throw "Invalid member specified $($entity)" } Add-XmlElement -xmlRoot $xmlDynamicCriteria -xmlElementName "key" -xmlElementText "ENTITY" Add-XmlElement -xmlRoot $xmlDynamicCriteria -xmlElementName "criteria" -xmlElementText "belongs_to" Add-XmlElement -xmlRoot $xmlDynamicCriteria -xmlElementName "value" -xmlElementText $EntityObjectId } elseif ($PSCmdlet.ParameterSetName -eq "search") { Add-XmlElement -xmlRoot $xmlDynamicCriteria -xmlElementName "key" -xmlElementText $_key.ToUpper() Add-XmlElement -xmlRoot $xmlDynamicCriteria -xmlElementName "criteria" -xmlElementText $criteria.ToLower() Add-XmlElement -xmlRoot $xmlDynamicCriteria -xmlElementName "value" -xmlElementText $value } $xmlDynamicCriteria } end {} } function Add-NsxDynamicMemberSet { <# .SYNOPSIS Adds a new dynamic member set to an existing NSX Security Group. .DESCRIPTION NSX Security Groups can have 3 types of membership configured, Dynamic Criteria, Static Members and Exclude Members. One or more Dynamic Criteria combine to make a Dynamic Member Set, and one or more Dynamic Member Sets combine to define the Dynamic Membership of a given security group. In order to allow the configuration of a security groups Dynamic Membership with an aritrary number of Dynamic Criteria Member Sets that contain an arbitrary number of Dynamic Criteria in a flexible way, PowerNSX provides the following abstractions. Creation of individual Dynamic Criteria is accomplished with New-NsxDynamicCriteriaSpec. One or more Dynamic Criteria can be added to a Dynamic Member Set at creation time with Add-NsxDynamicMemberSet and specifying the required Dynamic Criteria Spec objects at creation time. One or more Dynamic Criteria can be added to an existing Dynamic Member Set after the fact with Add-NsxDynamicCriteria or removed with Remove-NsxDynamicCriteria. One or more Dynamic Member sets can be added to a security groups overall Dynamic Membership definition using Add-NsxDynamicMemberSet or removed using Remove-NsxDynamicMemberSet A Security Groups Dynamic Member definition can include multiple Dynamic Member Sets in an logical AND/OR arrangement, and for each of the Dynamic Member Sets, a match operator of ALL or ANY can be specified that determines how multiple Dynamic Criteria combine within the set to define a match. The Add-NsxDynamicMemberSet cmdlet is used to create a new Dynamic Member Set and add it to an existing Security Groups Dynamic Member Definition. .EXAMPLE $criteria1Spec = New-NsxDynamicCriteriaSpec -key VM.name -condition contains -value "PROD" $criteria2Spec = New-NsxDynamicCriteriaSpec -key VM.GUEST_OS_FULL_NAME -condition contains -value "Win" $sg1 = New-NsxSecurityGroup -Name "SG-Production-Windows" Get-NsxSecurityGroup "SG-Production-Windows" | Add-NsxDynamicMemberSet -SetOperator OR -CriteriaOperator ANY -DynamicCriteriaSpec $criteria1Spec,$criteria2Spec .EXAMPLE $criteria3Spec = New-NsxDynamicCriteriaSpec -key VM.SECURITY_TAG -condition starts_with -value "ST_PCI" $criteria4Spec = New-NsxDynamicCriteriaSpec -entity $(Get-Cluster DMZ) $sg2 = New-NsxSecurityGroup -Name "SG-DMZ-PCI" Get-NsxSecurityGroup "SG-DMZ-PCI" | Add-NsxDynamicMemberSet -SetOperator AND -CriteriaOperator ALL -DynamicCriteriaSpec $criteria3Spec,$criteria4Spec .EXAMPLE $criteria5Spec = New-NsxDynamicCriteriaSpec -key VM.SECURITY_TAG -condition starts_with -value "ST_Backup" $criteria6Spec = New-NsxDynamicCriteriaSpec -entity $(Get-Cluster Dev-CL-01) $criteria7Spec = New-NsxDynamicCriteriaSpec -entity $(Get-NsxLogicalSwitch LS-Backup-Net) $criteria8Spec = New-NsxDynamicCriteriaSpec -key VM.NAME -condition contains -value "PROD" $sg3 = New-NsxSecurityGroup -Name "SG-Backup-Clients" $sg3.objectid | Add-NsxDynamicMemberSet -SetOperator OR -CriteriaOperator ANY -DynamicCriteriaSpec $criteria5Spec,$criteria6Spec $sg3.objectid | Add-NsxDynamicMemberSet -SetOperator OR -CriteriaOperator ANY -DynamicCriteriaSpec $criteria7Spec,$criteria8Spec #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1) ] # SecurityGroup whose membership is to be modified. [ValidateNotNullOrEmpty()] [object]$SecurityGroup, [Parameter (Mandatory = $false) ] # Dynamic Criteria Set operator BETWEEN sets. In the UI, this is the AND/OR drop down displayed between member sets. # This value is ignored if the set being added is the first set being added to the Dynamic Member Definition of a Security Group [ValidateSet("OR", "AND")] [String]$SetOperator, [Parameter (Mandatory = $true) ] # Dynamic Criteria operator for criteria WITHIN the set being added. In the UI, this is the Match: ANY/ALL drop down displayed at the top of each Dynamic Member Set. [ValidateSet("ANY", "ALL")] [String]$CriteriaOperator, [Parameter (Mandatory = $true) ] # Dynamic criteria spec/s as generated by New-NsxDynamicCriteriaSpec [ValidateScript( { ValidateDynamicCriteriaSpec $_ })] [System.Xml.XmlElement[]]$DynamicCriteriaSpec, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { # Get our internal SG object and id. The internal object is used later # to modify and put for bulk update. if ( $SecurityGroup -is [System.Xml.XmlElement] ) { $SecurityGroupId = $securityGroup.objectId $_SecurityGroup = $SecurityGroup.cloneNode($true) } elseif ( ($securityGroup -is [string]) -and ($SecurityGroup -match "securitygroup-\d+")) { $SecurityGroupId = $securityGroup $_SecurityGroup = Get-NsxSecurityGroup -objectId $SecurityGroupId -Connection $connection } else { throw "Invalid SecurityGroup specified. Specify a PowerNSX SecurityGroup object or a valid securitygroup objectid." } # First we need to verify if the Security Group object passed in via the # pipeline already has the dynamicMemberDefinition element created. If # not, then create the required XML structure $dynamicMemberDefinitionElement = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SecurityGroup -query 'child::dynamicMemberDefinition' if ( -not $dynamicMemberDefinitionElement ) { Add-XmlElement -xmlRoot $_SecurityGroup -xmlElementName "dynamicMemberDefinition" $dynamicMemberDefinitionElement = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SecurityGroup -query 'child::dynamicMemberDefinition' #Default the set operator for the first criteria set in a dynamic membership defintion to be 'OR' as the UI does. if ( $PSBoundParameters.ContainsKey("SetOperator")) { Write-Warning "A Set Operator is not defined for the first dynamic membership set defined on a security group. The Set Operator value has been ignored." } $SetOperator = "OR" } else { #Require that sets added after the initial one have an operator defined. If ( -not $PSBoundParameters.ContainsKey("SetOperator")) { throw "A Set Operator is required to define additional membership criteria sets." } } $_CriteriaOperator = ConvertTo-NsxApiCriteriaOperator $CriteriaOperator # Now lets add the dynamic criteria (DynamicSets) [System.Xml.XmlElement]$xmlDynamicMemberDefinition = $dynamicMemberDefinitionElement [System.XML.XMLElement]$xmlRoot = $xmlDynamicMemberDefinition.ownerDocument.CreateElement("dynamicSet") Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "operator" -xmlElementText $SetOperator.ToUpper() foreach ( $spec in $DynamicCriteriaSpec) { $specImport = $xmlRoot.ownerDocument.ImportNode($spec, $true) #Add the criteria operator to the spec elem Add-XmlElement -xmlRoot $specImport -xmlElementName "operator" -xmlElementText $_CriteriaOperator $xmlRoot.appendChild($specImport) | Out-Null } $xmlDynamicMemberDefinition.appendChild($xmlRoot) | Out-Null #Do the post $body = $_SecurityGroup.OuterXml $URI = "/api/2.0/services/securitygroup/bulk/$($SecurityGroupId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection } end {} } function Get-NsxDynamicMemberSet { <# .SYNOPSIS Retrieves Dynamic Member Sets from the specified security group. .DESCRIPTION NSX Security Groups can have 3 types of membership configured, Dynamic Criteria, Static Members and Exclude Members. One or more Dynamic Criteria combine to make a Dynamic Member Set, and one or more Dynamic Member Sets combine to define the Dynamic Membership of a given security group. In order to allow the configuration of a security groups Dynamic Membership with an aritrary number of Dynamic Criteria Member Sets that contain an arbitrary number of Dynamic Criteria in a flexible way, PowerNSX provides the following abstractions. Creation of individual Dynamic Criteria is accomplished with New-NsxDynamicCriteriaSpec. One or more Dynamic Criteria can be added to a Dynamic Member Set at creation time with Add-NsxDynamicMemberSet and specifying the required Dynamic Criteria Spec objects at creation time. One or more Dynamic Criteria can be added to an existing Dynamic Member Set after the fact with Add-NsxDynamicCriteria or removed with Remove-NsxDynamicCriteria. One or more Dynamic Member sets can be added to a security groups overall Dynamic Membership definition using Add-NsxDynamicMemberSet or removed using Remove-NsxDynamicMemberSet A Security Groups Dynamic Member definition can include multiple Dynamic Member Sets in an logical AND/OR arrangement, and for each of the Dynamic Member Sets, a match operator of ALL or ANY can be specified that determines how multiple Dynamic Criteria combine within the set to define a match. This cmdlet returns the existing Dynamic Member Sets from the given security group. .EXAMPLE Get-NsxSecurityGroup Prod-WindowsServer | Get-NsxDynamicMemberSet Retrieves the Dynamic Member Sets that make up the Dynamic Membership specification of the security group Prod-WindowsServer .EXAMPLE Get-NsxSecurityGroup Prod-WindowsServer | Get-NsxDynamicMemberSet -Index 3 Retrieves the third Member Set from the Dynamic Membership specification of the security group Prod-WindowsServer. This is primarly intended to return an object suitable to pass to Get-NsxDynamicCriteria or Add-NsxDynamicCriteria. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1) ] # SecurityGroup to retrieve Dynamic Sets from. [ValidateNotNullOrEmpty()] [object]$SecurityGroup, [Parameter (Mandatory = $false)] #Get Member Set by index [ValidateNotNullOrEmpty()] [string]$Index, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) process { if (Invoke-XpathQuery -Node $SecurityGroup -QueryMethod SelectSingleNode -query "child::dynamicMemberDefinition/dynamicSet") { $SetCount = 0 foreach ( $CriteriaSet in $SecurityGroup.dynamicMemberDefinition.dynamicSet) { $ResultObj = @{} $SetCount++ #Use the first element in the set to determine the criteria operator #All criteria members of a set MUST have the same operator, despite #the fact that it is defined for each criteria member. #Obfustcating it here to a per dynamic criteria set setting aligns #with how the UI operates. $CriteriaOperator = ConvertFrom-NsxApiCriteriaOperator (Invoke-XpathQuery -Node $CriteriaSet -QueryMethod SelectSingleNode -query "child::dynamicCriteria").Operator #Bash together an output string that reflects what the user would see in the UI... $CriteriaString = "Match: $CriteriaOperator" $CriteriaCollection = @() foreach ( $Criteria in $CriteriaSet.dynamicCriteria ) { $CriteriaString += ", $(ConvertFrom-NsxApiCriteriaKey $Criteria.Key) $(ConvertFrom-NsxApiCriteriaCondition $Criteria.Criteria) $($Criteria.value)" $CriteriaObj = [pscustomobject]@{ "Index" = $CriteriaCollection.Length + 1 "Match" = $CriteriaOperator "Key" = ConvertFrom-NsxApiCriteriaKey $Criteria.Key "Condition" = ConvertFrom-NsxApiCriteriaCondition $Criteria.Criteria "Value" = $Criteria.value } $CriteriaCollection += $CriteriaObj } #SecurityGroup Name is just useful output for user. $ResultObj.Add("SecurityGroupName", $SecurityGroup.Name) #We have to generate an index on the fly so user has easy method to #select a set to operate on for subsequent modification. $ResultObj.Add("Index", $SetCount) #Supress the display of the set operator for the first set to align with #what the user sees in the UI if ( $SetCount -eq 1) { $ResultObj.Add("SetOperator", "") } else { $ResultObj.Add("SetOperator", $CriteriaSet.Operator) } $ResultObj.Add("CriteriaString", $CriteriaString) #SecurityGroup is supressed in output by default, but it is the #actual xml object that is modified and put back during modification #events like remove/add criteria. $ResultObj.Add("SecurityGroup", $SecurityGroup) #Likewise, CriteriaObj is suppressed, as the String representation of it is more readable, but we use the object in get/remove criteria pipelines $ResultObj.Add("Criteria", $CriteriaCollection) $output = [pscustomobject]$ResultObj #Manipulating which output properties are displayed to supress SecurityGroup [string[]]$DefaultProperties = 'Index', 'SecurityGroupName', 'SetOperator', 'CriteriaString' # Add the PSStandardMembers.DefaultDisplayPropertySet member $ddps = New-Object System.Management.Automation.PSPropertySet DefaultDisplayPropertySet, $DefaultProperties $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]$ddps # Attach default display property set and output $output | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers if ( $PSBoundParameters.ContainsKey("Index") ) { $output | Where-Object { $_.index -eq $Index } } else { $output } } } } end {} } function Remove-NsxDynamicMemberSet { <# .SYNOPSIS Removes the specified Dynamic Member Set from a security groups Dynamic Membership definition. .DESCRIPTION NSX Security Groups can have 3 types of membership configured, Dynamic Criteria, Static Members and Exclude Members. One or more Dynamic Criteria combine to make a Dynamic Member Set, and one or more Dynamic Member Sets combine to define the Dynamic Membership of a given security group. In order to allow the configuration of a security groups Dynamic Membership with an aritrary number of Dynamic Criteria Member Sets that contain an arbitrary number of Dynamic Criteria in a flexible way, PowerNSX provides the following abstractions. Creation of individual Dynamic Criteria is accomplished with New-NsxDynamicCriteriaSpec. One or more Dynamic Criteria can be added to a Dynamic Member Set at creation time with Add-NsxDynamicMemberSet and specifying the required Dynamic Criteria Spec objects at creation time. One or more Dynamic Criteria can be added to an existing Dynamic Member Set after the fact with Add-NsxDynamicCriteria or removed with Remove-NsxDynamicCriteria. One or more Dynamic Member sets can be added to a security groups overall Dynamic Membership definition using Add-NsxDynamicMemberSet or removed using Remove-NsxDynamicMemberSet A Security Groups Dynamic Member definition can include multiple Dynamic Member Sets in an logical AND/OR arrangement, and for each of the Dynamic Member Sets, a match operator of ALL or ANY can be specified that determines how multiple Dynamic Criteria combine within the set to define a match. This cmdlet removes the specified Dynamic Member Set as retrieved by Get-NsxDynamicMemberSet from the Security Group it is defined within. .EXAMPLE Get-NsxSecurityGroup Prod-WindowsServer | Get-NsxDynamicMemberSet Index SecurityGroupName SetOperator CriteriaString ----- ----------------- ----------- -------------- 1 Prod-WindowsServer Match: ANY, VMName contains Windows, ComputerName regex *win* 2 Prod-WindowsServer OR Match: ANY, OSName contains Win PS C:\> Get-NsxSecurityGroup Prod-WindowsServer | Get-NsxDynamicMemberSet -index 1 | Remove-NsxDynamicMemberSet Removes the first dynamic member set from the dynamic member definition of the security group Prod-WindowsServer #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility [CmdletBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1) ] # Dynamic member set to remove. [ValidateScript( { ValidateDynamicMemberSet $_ })] [object]$DynamicMemberSet, [Parameter (Mandatory = $False, ParameterSetName = "LegacyConfirm")] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False, ParameterSetName = "Default")] #Disable Prompt for confirmation. [switch]$NoConfirm, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) #We cant actually modify the dynamic set list in the process block as normal #as objects come down the pipeline, as the index used to define a set to #remove is ephemeral and based only on a sets position in the overal definition #and modifying the set on the fly will impact the position of subsequent elements. #So we setup a tracking hashtable per security group in the begin block #(we are assuming the pipeline could send us more than one SG to modify), #and use the process block to build a collection of XMLNodes that will be #removed all at once in the end block prior to the API put to persist the #actual change. Begin { If ( $PSCmdlet.ParameterSetName -eq "LegacyConfirm") { Write-Warning "The -confirm switch is deprecated and will be removed in a future release. Use -NoConfirm instead." $NoConfirm = ( -not $confirm ) } #Setup tracking hashtable. key is sg id. value is pscustomobject with following keys: # - SecurityGroup - the actual SG XML. This allows us to honour the # intent of revisioning to avoid pushing an out of date change. # - NodesToRemove - a collection of XMLNodes added to in the process block. $SGsToModify = @{} } Process { if ( -not ($SgsToModify.ContainsKey($DynamicMemberSet.SecurityGroup.objectId))) { #We havent seen this SG before, add it to our tracking hashtable. We have to clone the node to avoid modifying the input object compoenent that is XML. $SGsToModify.Add($DynamicMemberSet.SecurityGroup.objectId, [pscustomobject]@{"SecurityGroup" = $DynamicMemberSet.SecurityGroup.CloneNode($True); "NodesToRemove" = @() }) } #Get the SG XML from our tracking hashtable to search on. $SecurityGroup = $SGsToModify[$DynamicMemberSet.SecurityGroup.objectId].SecurityGroup $NodeToRemove = Invoke-XpathQuery -Node $SecurityGroup.dynamicMemberDefinition -QueryMethod SelectSingleNode "child::dynamicSet[$($DynamicMemberSet.Index)]" if ( -not $NodeToRemove ) { throw "The Dynamic Member Set index $($DynamicMemberSet.Index) does not exist in the security group $($SecurityGroup.Name) ($($SecurityGroup.objectId)). This should not occur and indicates a fault in PowerNSX. Please report this bug at github.com/vmware/PowerNSX" } #Add the node to remove to the tracking collection for this SG. $SGsToModify[$DynamicMemberSet.SecurityGroup.objectId].NodesToRemove += $NodeToRemove } End { #Now we do the actual modification work. foreach ( $SGToModify in $SGsToModify.Values) { foreach ( $Node in $SgToModify.NodesToRemove ) { $null = $SgToModify.SecurityGroup.dynamicMemberDefinition.RemoveChild($Node) } #Post the updated SG XML. $uri = "/api/2.0/services/securitygroup/bulk/$($SgToModify.SecurityGroup.objectId)" $body = $SgToModify.SecurityGroup.outerXml if ( -not ( $Noconfirm )) { $message = "Removal of dynamic member sets from Security Group $($SGToModify.SecurityGroup.Name) will result in a change in security posture." $question = "Are you sure you want to proceed with the update of Security Group $($SGToModify.SecurityGroup.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Security Group $($SGToModify.SecurityGroup.Name)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Security Group $($SGToModify.SecurityGroup.Name)" -Completed } } } } function Add-NsxDynamicCriteria { <# .SYNOPSIS Adds a Dynamic Criteria to the specified Dynamic Member Set. .DESCRIPTION NSX Security Groups can have 3 types of membership configured, Dynamic Criteria, Static Members and Exclude Members. One or more Dynamic Criteria combine to make a Dynamic Member Set, and one or more Dynamic Member Sets combine to define the Dynamic Membership of a given security group. In order to allow the configuration of a security groups Dynamic Membership with an aritrary number of Dynamic Criteria Member Sets that contain an arbitrary number of Dynamic Criteria in a flexible way, PowerNSX provides the following abstractions. Creation of individual Dynamic Criteria is accomplished with New-NsxDynamicCriteriaSpec. One or more Dynamic Criteria can be added to a Dynamic Member Set at creation time with Add-NsxDynamicMemberSet and specifying the required Dynamic Criteria Spec objects at creation time. One or more Dynamic Criteria can be added to an existing Dynamic Member Set after the fact with Add-NsxDynamicCriteria or removed with Remove-NsxDynamicCriteria. One or more Dynamic Member sets can be added to a security groups overall Dynamic Membership definition using Add-NsxDynamicMemberSet or removed using Remove-NsxDynamicMemberSet A Security Groups Dynamic Member definition can include multiple Dynamic Member Sets in an logical AND/OR arrangement, and for each of the Dynamic Member Sets, a match operator of ALL or ANY can be specified that determines how multiple Dynamic Criteria combine within the set to define a match. Add-NsxDynamicCriteria adds a new Dynamic Member Criteria to the specified Dynamic Member Set as retrieved by Get-NsxDynamicMemberSet. You can pass a Dynamic Member Spec as created by New-NsxDynamicMemberSpec, or explicitly specify the key, condition and value of the new Dynamic Criteria. .EXAMPLE Get-NsxSecurityGroup WebApp | Get-NsxDynamicMemberSet -Index 1 | Add-NsxDynamicCriteria -Entity (Get-VM app01) Index SecurityGroupName SetOperator CriteriaString ----- ----------------- ----------- -------------- 1 WebApp Match: ANY, VMName contains WebApp, ComputerName regex *webapp*, ENTITY belongs_to vm-1234 Adds a new Dynamic Criteria for a static inclusion of the VM app01 to the existing first Dynamic Member Set of the Security Group WebApp .EXAMPLE $spec1 = New-NsxDynamicCriteriaSpec -key SecurityTag -condition equals -value "webapp" PS C:\> Get-NsxSecurityGroup WebApp | Get-NsxDynamicMemberSet -Index 1 | Add-NsxDynamicCriteria -DynamicCriteriaSpec $spec1 Index SecurityGroupName SetOperator CriteriaString ----- ----------------- ----------- -------------- 1 WebApp Match: ANY, VMName contains WebApp, ComputerName regex *webapp*, ENTITY belongs_to vm-1234, SecurityTag equals webapp Adds a new Dynamic Criteria based on the precreated criteria spec $spec1 to the existing first Dynamic Member Set of the Security Group WebApp .EXAMPLE Get-NsxSecurityGroup WebApp | Get-NsxDynamicMemberSet -Index 1 | Add-NsxDynamicCriteria -key SecurityTag -condition equals -value "webapp" Index SecurityGroupName SetOperator CriteriaString ----- ----------------- ----------- -------------- 1 WebApp Match: ANY, VMName contains WebApp, ComputerName regex *webapp*, ENTITY belongs_to vm-1234, SecurityTag equals webapp Adds a new Dynamic Criteria based on the key/condition/value specified to the existing first Dynamic Member Set of the Security Group WebApp #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1) ] # SecurityGroup to retrieve Dynamic Sets from. [ValidateScript( { ValidateDynamicMemberSet $_ })] [object]$DynamicMemberSet, [Parameter (Mandatory = $true, ParameterSetName = "spec") ] # Dynamic criteria spec/s as generated by New-NsxDynamicCriteriaSpec [ValidateScript( { ValidateDynamicCriteriaSpec $_ })] [System.Xml.XmlElement]$DynamicCriteriaSpec, [Parameter (Mandatory = $true, ParameterSetName = "search")] # Dynamic Criteria Key [ ValidateSet("VMName", "ComputerName", "OSName", "SecurityTag") ] [String]$Key, [Parameter (Mandatory = $true, ParameterSetName = "search")] # Dynamic Criteria Condition [ ValidateSet("contains", "ends_with", "starts_with", "equals", "notequals", "regex") ] [String]$Condition, [Parameter (Mandatory = $true, ParameterSetName = "search")] # Dynamic Criteria Value to be matched against the key using the condition. [ ValidateNotNullOrEmpty() ] [String]$Value, [Parameter (Mandatory = $true, ParameterSetName = "entity")] # A specific entity to match against. [ ValidateNotNullOrEmpty() ] [object]$Entity, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { switch ( $PSCmdlet.ParameterSetName ) { "search" { $spec = New-NsxDynamicCriteriaSpec -Key $Key -Condition $Condition -Value $Value } "entity" { $spec = New-NsxDynamicCriteriaSpec -Entity $entity } "spec" { $spec = $DynamicCriteriaSpec } } # Now lets add the dynamic criteria. Clone the input node so modifying XML doesnt affect the source. $SecurityGroupXML = $DynamicMemberSet.SecurityGroup.CloneNode($true) #Now get the specific set elem user has passed from the contained SG XML elem... $dynamicMemberSetElement = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $SecurityGroupXML -query "child::dynamicMemberDefinition/dynamicSet[$($DynamicMemberSet.Index)]" if ( -not $dynamicMemberSetElement ) { #this shouldnt happen if we get a valid Dynamic Member Set throw "The specified Dynamic Member Set is not valid. This is not expected, please report this issue on the PowerNSX Github issues page - github.com/vmware/powernsx/issues" } $specImport = $dynamicMemberSetElement.ownerDocument.ImportNode($spec, $true) #Add the criteria operator to the spec elem. All Criteria must share the same operator, so we just grab the first one and copy it. Add-XmlElement -xmlRoot $specImport -xmlElementName "operator" -xmlElementText (ConvertTo-NsxApiCriteriaOperator $DynamicMemberSet.Criteria[0].Match).ToUpper() $dynamicMemberSetElement.appendChild($specImport) | Out-Null #Do the post $body = $SecurityGroupXML.OuterXml $URI = "/api/2.0/services/securitygroup/bulk/$($SecurityGroupXML.objectId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Get-NsxSecurityGroup -objectId $SecurityGroupXML.objectId -Connection $Connection | Get-NsxDynamicMemberSet -Index $DynamicMemberSet.Index -Connection $Connection } end { } } function Get-NsxDynamicCriteria { <# .SYNOPSIS Retrieves Dynamic Member Criteria from the specified Dynamic Member Set. .DESCRIPTION NSX Security Groups can have 3 types of membership configured, Dynamic Criteria, Static Members and Exclude Members. One or more Dynamic Criteria combine to make a Dynamic Member Set, and one or more Dynamic Member Sets combine to define the Dynamic Membership of a given security group. In order to allow the configuration of a security groups Dynamic Membership with an aritrary number of Dynamic Criteria Member Sets that contain an arbitrary number of Dynamic Criteria in a flexible way, PowerNSX provides the following abstractions. Creation of individual Dynamic Criteria is accomplished with New-NsxDynamicCriteriaSpec. One or more Dynamic Criteria can be added to a Dynamic Member Set at creation time with Add-NsxDynamicMemberSet and specifying the required Dynamic Criteria Spec objects at creation time. One or more Dynamic Criteria can be added to an existing Dynamic Member Set after the fact with Add-NsxDynamicCriteria or removed with Remove-NsxDynamicCriteria. One or more Dynamic Member sets can be added to a security groups overall Dynamic Membership definition using Add-NsxDynamicMemberSet or removed using Remove-NsxDynamicMemberSet A Security Groups Dynamic Member definition can include multiple Dynamic Member Sets in an logical AND/OR arrangement, and for each of the Dynamic Member Sets, a match operator of ALL or ANY can be specified that determines how multiple Dynamic Criteria combine within the set to define a match. Get-NsxDynamicCriteria retrieves Dynamic Member Criteria from the specified Dynamic Member Set as retrieved by Get-NsxDynamicMemberSet. While Get-NsxDynamicMemberSet displays a text representation of the Dynamic Criteria that belong to it, this cmdlet outputs individual objects representing each criteria such that they can be filtered, and passed to Remove-NsxDynamicCriteria. .EXAMPLE Get-NsxSecurityGroup webapp | Get-NsxDynamicMemberSet | Get-NsxDynamicCriteria | ft Index MemberSetIndex SecurityGroupName Key Condition Value ----- -------------- ----------------- --- --------- ----- 1 1 webapp VMName contains webapp 2 1 webapp SecurityTag contains webapp 3 1 webapp ComputerName contains webapp 1 2 webapp ENTITY belongs_to vm-3964 2 2 webapp SecurityTag equals webapp 1 3 webapp ENTITY belongs_to vm-3961 Retrieves all Dynamic Criteria from ALL Dynamic Member Sets of the security group webapp. This is probably not what you want to do. Output is formatted as a table. .EXAMPLE Get-NsxSecurityGroup webapp | Get-NsxDynamicMemberSet -index 1 | Get-NsxDynamicCriteria | ft Index MemberSetIndex SecurityGroupName Key Condition Value ----- -------------- ----------------- --- --------- ----- 1 1 webapp VMName contains webapp 2 1 webapp SecurityTag contains webapp 3 1 webapp ComputerName contains webapp Retrieves all Dynamic Criteria from the first Dynamic Member Set of the security group webapp. This probably IS what you want to do! :) Output is formatted as a table. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1) ] # Dynamic Member Set to retrieve Dynamic Criteria from. [ValidateScript( { ValidateDynamicMemberSet $_ })] [object]$DynamicMemberSet, [Parameter (Mandatory = $false)] #Get Criteria Member by index [ValidateNotNullOrEmpty()] [string]$Index, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) process { foreach ( $Criteria in $DynamicMemberSet.Criteria) { $output = [pscustomobject]@{ "Index" = $Criteria.Index "MemberSetIndex" = $DynamicMemberSet.Index "SecurityGroupName" = $DynamicMemberSet."SecurityGroupName" "Key" = $Criteria.Key "Condition" = $Criteria.Condition "Value" = $Criteria.Value "SecurityGroup" = $DynamicMemberSet.SecurityGroup } #Manipulating which output properties are displayed to supress SecurityGroup [string[]]$DefaultProperties = "Index", "MemberSetIndex", "SecurityGroupName", "Key", "Condition", "Value" # Add the PSStandardMembers.DefaultDisplayPropertySet member $ddps = New-Object System.Management.Automation.PSPropertySet DefaultDisplayPropertySet, $DefaultProperties $PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]$ddps # Attach default display property set and output $output | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers if ( $PSBoundParameters.ContainsKey("Index") ) { $output | Where-Object { $_.index -eq $Index } } else { $output } } } end {} } function Remove-NsxDynamicCriteria { <# .SYNOPSIS Removes the specified Dynamic Criteria from the specified Dynamic Member Set. .DESCRIPTION NSX Security Groups can have 3 types of membership configured, Dynamic Criteria, Static Members and Exclude Members. One or more Dynamic Criteria combine to make a Dynamic Member Set, and one or more Dynamic Member Sets combine to define the Dynamic Membership of a given security group. In order to allow the configuration of a security groups Dynamic Membership with an aritrary number of Dynamic Criteria Member Sets that contain an arbitrary number of Dynamic Criteria in a flexible way, PowerNSX provides the following abstractions. Creation of individual Dynamic Criteria is accomplished with New-NsxDynamicCriteriaSpec. One or more Dynamic Criteria can be added to a Dynamic Member Set at creation time with Add-NsxDynamicMemberSet and specifying the required Dynamic Criteria Spec objects at creation time. One or more Dynamic Criteria can be added to an existing Dynamic Member Set after the fact with Add-NsxDynamicCriteria or removed with Remove-NsxDynamicCriteria. One or more Dynamic Member sets can be added to a security groups overall Dynamic Membership definition using Add-NsxDynamicMemberSet or removed using Remove-NsxDynamicMemberSet A Security Groups Dynamic Member definition can include multiple Dynamic Member Sets in an logical AND/OR arrangement, and for each of the Dynamic Member Sets, a match operator of ALL or ANY can be specified that determines how multiple Dynamic Criteria combine within the set to define a match. This cmdlet removes the specified Dynamic Criteria as retrieved by Get-NsxDynamicCriteria from the given Dymanic Member Set of which it is a member. .EXAMPLE Get-NsxSecurityGroup webapp | Get-NsxDynamicMemberSet | Get-NsxDynamicCriteria -index 1 | Remove-NsxDynamicCriteria Index MemberSetIndex SecurityGroupName Key Condition Value ----- -------------- ----------------- --- --------- ----- 1 1 webapp VMName contains webapp 2 1 webapp SecurityTag contains webapp 3 1 webapp ComputerName contains webapp 1 2 webapp ENTITY belongs_to vm-3964 2 2 webapp SecurityTag equals webapp 1 3 webapp ENTITY belongs_to vm-3961 Removes the first Dynamic Criteria from ALL Dynamic Member Sets of the security group webapp. This is probably not what you want to do. .EXAMPLE Get-NsxSecurityGroup webapp | Get-NsxDynamicMemberSet -index 1 | Get-NsxDynamicCriteria -index 1 | Remove-NsxDynamicCriteria Index MemberSetIndex SecurityGroupName Key Condition Value ----- -------------- ----------------- --- --------- ----- 1 1 webapp VMName contains webapp 2 1 webapp SecurityTag contains webapp 3 1 webapp ComputerName contains webapp Removes the first Dynamic Criteria from the first Dynamic Member Set of the security group webapp. This probably IS what you want to do! :) #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility [CmdletBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1) ] # SecurityGroup to retrieve Dynamic Sets from. [ValidateScript( { ValidateDynamicCriteria $_ })] [object]$DynamicCriteria, [Parameter (Mandatory = $False, ParameterSetName = "LegacyConfirm")] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False, ParameterSetName = "Default")] #Disable Prompt for confirmation. [switch]$NoConfirm, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) #We cant actually modify the dynamic set list in the process block as normal #as objects come down the pipeline, as the index used to define a set to #remove is ephemeral and based only on a sets position in the overal definition #and modifying the set on the fly will impact the position of subsequent elements. #So we setup a tracking hashtable per security group in the begin block #(we are assuming the pipeline could send us more than one SG and/or criteriaset to modify), #and use the process block to build a collection of XMLNodes that will be #removed all at once in the end block prior to the API put to persist the #actual change. Begin { If ( $PSCmdlet.ParameterSetName -eq "LegacyConfirm") { Write-Warning "The -confirm switch is deprecated and will be removed in a future release. Use -NoConfirm instead." $NoConfirm = ( -not $confirm ) } #Setup tracking hashtable. key is sg id. value is pscustomobject with following keys: # - SecurityGroup - the actual SG XML. This allows us to honour the # intent of revisioning to avoid pushing an out of date change. # - NodesToRemove - a collection of XMLNodes added to in the process block. $SGsToModify = @{} } Process { if ( -not ($SgsToModify.ContainsKey($DynamicCriteria.SecurityGroup.objectId))) { #We havent seen this SG before, add it to our tracking hashtable. We have to clone the node to avoid modifying the input object compoenent that is XML. $SGsToModify.Add($DynamicCriteria.SecurityGroup.objectId, [pscustomobject]@{"SecurityGroup" = $DynamicCriteria.SecurityGroup.CloneNode($True); "NodesToRemove" = @() }) } #Get the SG XML from our tracking hashtable to search on. $SecurityGroup = $SGsToModify[$DynamicCriteria.SecurityGroup.objectId].SecurityGroup $NodeToRemove = Invoke-XpathQuery -Node $SecurityGroup.dynamicMemberDefinition -QueryMethod SelectSingleNode -query "child::dynamicSet[$($DynamicCriteria.MemberSetIndex)]/dynamicCriteria[$($DynamicCriteria.Index)]" if ( -not $NodeToRemove ) { throw "The Dynamic Criteria index $($DynamicCriteria.Index) within the Dynamic Member set index $($DynamicCriteria.MemberSetIndex) does not exist in the security group $($SecurityGroup.Name) ($($SecurityGroup.objectId)). This should not occur and indicates a fault in PowerNSX. Please report this bug at github.com/vmware/PowerNSX" } #Add the node to remove to the tracking collection for this SG. We need to store the memberset index too so we can select it laster during the removal. $SGsToModify[$DynamicCriteria.SecurityGroup.objectId].NodesToRemove += [pscustomobject]@{ "MemberSetIndex" = $DynamicCriteria.MemberSetIndex "NodeToRemove" = $NodeToRemove } } End { #Now we do the actual modification work. foreach ( $SGToModify in $SGsToModify.Values) { foreach ( $Node in $SgToModify.NodesToRemove ) { $null = (Invoke-XpathQuery -Node $SecurityGroup.dynamicMemberDefinition -QueryMethod SelectSingleNode -query "child::dynamicSet[$($Node.MemberSetIndex)]").RemoveChild($Node.NodeToRemove) } #Post the updated SG XML. $uri = "/api/2.0/services/securitygroup/bulk/$($SgToModify.SecurityGroup.objectId)" $body = $SgToModify.SecurityGroup.outerXml if ( -not ( $Noconfirm )) { $message = "Removal of dynamic criteria from the Dynamic Member set of a Security Group $($SGToModify.SecurityGroup.Name) will result in a change in security posture." $question = "Are you sure you want to proceed with the update of Security Group $($SGToModify.SecurityGroup.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Security Group $($SGToModify.SecurityGroup.Name)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Security Group $($SGToModify.SecurityGroup.Name)" -Completed } } } } ######## function New-NsxSecurityTag { <# .SYNOPSIS Creates a new NSX Security Tag .DESCRIPTION A NSX Security Tag is a arbitrary string. It is used in other functions of NSX such as Security Groups match criteria. Security Tags are applied to a Virtual Machine. This cmdlet creates a new NSX Security Tag .EXAMPLE PS C:\> New-NSXSecurityTag -name ST-Web-DMZ -description Security Tag for the Web Tier #> param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] [string]$Description, [Parameter (Mandatory = $false)] #This marks the tag as a universal object within the constructs of NSX [switch]$Universal, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { if ( $universal ) { if ( -not $connection.version ) { Write-Warning "Universal security tags are not supported on NSX versions less than 6.3.0 and current NSX version could not be determined." } elseif ( [version]$connection.version -lt [version]"6.3.0") { throw "Universal security tags are not supported on NSX versions less than 6.3.0" } } } process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("securityTag") [System.XML.XMLElement]$XmlNodes = $Xmldoc.CreateElement("type") $xmlDoc.appendChild($xmlRoot) | Out-Null $xmlRoot.appendChild($xmlnodes) | Out-Null #Mandatory fields Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "objectTypeName" -xmlElementText "SecurityTag" Add-XmlElement -xmlRoot $xmlnodes -xmlElementName "typeName" -xmlElementText "SecurityTag" Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name #Optional fields if ( $PsBoundParameters.ContainsKey('Description')) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText "$Description" } if ($Universal) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "isUniversal" -xmlElementText $Universal.toString().ToLower() } #Do the post $body = $xmlroot.OuterXml $URI = "/api/2.0/services/securitytags/tag" $null = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection #Return our shiny new tag... Get-NsxSecurityTag -Name $Name -Connection $connection } end {} } function Get-NsxSecurityTag { <# .SYNOPSIS Retrieves an NSX Security Tag .DESCRIPTION A NSX Security Tag is a arbitrary string. It is used in other functions of NSX such as Security Groups match criteria. Security Tags are applied to a Virtual Machine. This cmdlet retrieves existing NSX Security Tags .EXAMPLE Get-NSXSecurityTag Gets all Security Tags .EXAMPLE Get-NSXSecurityTag -name ST-Web-DMZ Gets a specific Security Tag by name #> param ( [Parameter (Mandatory = $false, Position = 1)] #Get Security Tag by name [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] #Get security tag by objectId [string]$objectId, [Parameter (Mandatory = $false)] #Include system security tags [switch]$IncludeSystem = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) process { if ( -not $PsBoundParameters.ContainsKey('objectId')) { #either all or by name $URI = "/api/2.0/services/securitytags/tag" [System.Xml.XmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $securityTags = @() if ($response.securityTags | Get-Member -MemberType Properties -Name pagingInfo) { $defaultPageSize = 1024 $itemIndex = 0 $startingIndex = 0 $pagingInfo = $response.securityTags.pagingInfo if ( [int]$paginginfo.totalCount -ne 0 ) { Write-Debug "$($MyInvocation.MyCommand.Name) : SecurityTag count non zero" do { Write-Debug "$($MyInvocation.MyCommand.Name) : In paging loop. PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" while (($itemindex -lt ([int]$paginginfo.pagesize + $startingIndex)) -and ($itemIndex -lt [int]$paginginfo.totalCount )) { Write-Debug "$($MyInvocation.MyCommand.Name) : In Item Processing Loop: ItemIndex: $itemIndex" Write-Debug "$($MyInvocation.MyCommand.Name) : $(@($response.securityTags.securityTag)[($itemIndex - $startingIndex)].objectId)" $securityTags += @($response.securityTags.securityTag)[($itemIndex - $startingIndex)] $itemIndex += 1 } Write-Debug "$($MyInvocation.MyCommand.Name) : Out of item processing - PagingInfo: PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" if ( [int]$paginginfo.totalcount -gt $itemIndex) { Write-Debug "$($MyInvocation.MyCommand.Name) : PagingInfo: PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" $startingIndex += $defaultPageSize $URI = "/api/2.0/services/securitytags/tag?pageSize=$defaultPageSize&startIndex=$startingIndex" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $pagingInfo = $response.securityTags.pagingInfo } } until ( [int]$paginginfo.totalcount -le $itemIndex ) Write-Debug "$($MyInvocation.MyCommand.Name) : Completed page processing: ItemIndex: $itemIndex" } } else { $securityTags = $response.securityTags.securityTag } if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::securityTags/securityTag')) { if ( $PsBoundParameters.ContainsKey('Name')) { $tags = $securityTags | Where-Object { $_.name -eq $name } } else { $tags = $securityTags } if ( -not $IncludeSystem ) { $tags | Where-Object { ( $_.systemResource -ne 'true') } } else { $tags } } } else { #Just getting a single Security group by object id $URI = "/api/2.0/services/securitytags/tag/$objectId" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::securityTag')) { $tags = $response.securitytag } if ( -not $IncludeSystem ) { $tags | Where-Object { ( $_.systemResource -ne 'true') } } else { $tags } } } end {} } function Remove-NsxSecurityTag { <# .SYNOPSIS Removes the specified NSX Security Tag. .DESCRIPTION A NSX Security Tag is a arbitrary string. It is used in other functions of NSX such as Security Groups match criteria. Security Tags are applied to a Virtual Machine. This cmdlet removes the specified NSX Security Tag If the object is currently in use the api will return an error. Use -force to override but be aware that the firewall rulebase will become invalid and will need to be corrected before publish operations will succeed again. .EXAMPLE PS C:\> Get-NsxSecurityTag TestSecurityTag | Remove-NsxSecurityTag #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateSecurityTag $_ })] [System.Xml.XmlElement]$SecurityTag, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$force = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if (($SecurityTag.systemResource -eq 'true') -and ( -not $force)) { Write-Warning "Not removing $($SecurityTag.Name) as it is a default SecurityTag. Use -Force to force deletion." } else { if ( $confirm ) { $message = "Removal of Security Tags may impact desired Security Posture and expose your infrastructure. Please understand the impact of this change" $question = "Proceed with removal of Security Tag $($SecurityTag.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/2.0/services/securitytags/tag/$($SecurityTag.objectId)?force=$($Force.ToString().ToLower())" Write-Progress -Activity "Remove Security Tag $($SecurityTag.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Security Tag $($SecurityTag.Name)" -Completed } } } end {} } function Get-NsxSecurityTagAssignment { <# .SYNOPSIS This cmdlet is used to retrive a list of virtual machines assigned a particular NSX Security Tag. .DESCRIPTION A NSX Security Tag is a arbitrary string. It is used in other functions of NSX such as Security Groups match criteria. Security Tags are applied to a Virtual Machine. This cmdlet is used to retrive a list of virtual machines assigned a particular NSX Security Tag. .EXAMPLE Get-NsxSecurityTag ST-Web-DMZ | Get-NsxSecurityTagAssignment Specify a single security tag to find all virtual machines the tag is assigned to. .EXAMPLE Get-NsxSecurityTag | where-object { $_.name -like "*dmz*" } | Get-NsxSecurityTagAssignment Retrieve all virtual machines that are assigned a security tag containing 'dmz' in the security tag name .EXAMPLE Get-VM Web-01 | Get-NsxSecurityTagAssignment Specify a virtual machine to retrieve all the assigned security tags #> [CmdLetBinding(DefaultParameterSetName = "Tag")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "Tag")] [ValidateScript( { ValidateSecurityTag $_ })] [System.Xml.XmlElement]$SecurityTag, [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "VirtualMachine")] [ValidateScript( { ValidateVirtualMachineOrTemplate $_ })] [object[]]$VirtualMachine, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { switch ( $PSCmdlet.ParameterSetName ) { 'Tag' { $URI = "/api/2.0/services/securitytags/tag/$($SecurityTag.objectId)/vm" [System.Xml.XmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::basicinfolist/basicinfo') ) { $nodes = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $response -query 'descendant::basicinfolist/basicinfo') foreach ($node in $nodes) { # Get the VI VM object... # But seems that NSX allows you to apply a security tag to a VM Template. # So if a tag has been applied to a template we need to look for # it via Get-VM and also Get-Template. # If after trying both commands it still can't find the VM object, I'm # buggered if I know where to look for it. Considering it exists somewhere # because its being returned as a valid object via the NSX API. try { $vm = Get-VM -Server $Connection.VIConnection -Id "VirtualMachine-$($node.objectId)" -ErrorAction stop } catch { try { $vm = Get-Template -Server $Connection.VIConnection -Id "VirtualMachine-$($node.objectId)" -ErrorAction stop } catch { throw "Could not find object with MoRef $($node.objectId) using Get-VM or Get-Template." } } [pscustomobject]@{ "SecurityTag" = $SecurityTag; "VirtualMachine" = $vm } } } } 'VirtualMachine' { ## for each VM object, get the NSX Security Tag(s) assigned to it, if any $VirtualMachine | ForEach-Object { $oThisVM = $_ Write-Progress -Activity "Fetching Security Tags assigned to Virtual Machine '$oThisVM'" ## make the URI to use; leverage the value of the top-level property ".Id", for minor speed improvement over accessing .ExtensionData; this REST method was introduced in NSX v6.3.0 $URI = "/api/2.0/services/securitytags/vm/{0}" -f ($oThisVM.Id -replace "^VirtualMachine-", "") [System.Xml.XmlDocument]$oRestResponse = Invoke-NsxRestMethod -method "GET" -URI $URI -connection $connection ## for each SecurityTag object in .securityTags property of the response (if any), return a new object with SecurityTag and VirtualMachine properties (in the same way that the by-Tag parameterset behaves) if (-not [System.String]::IsNullOrEmpty($oRestResponse.securityTags)) { $oRestResponse.securityTags.securityTag | ForEach-Object { [pscustomobject]@{ "SecurityTag" = $_ "VirtualMachine" = $oThisVM } ## end new-object } ## end new-object } ## end if } ## end foreach-object } ## end case } } end {} } function New-NsxSecurityTagAssignment { <# .SYNOPSIS This cmdlet assigns is used to assign NSX Security Tags to a virtual machine. .DESCRIPTION A NSX Security Tag is an arbitrary string. It is used in other functions of NSX such as Security Groups match criteria. Security Tags are applied to a Virtual Machine. This cmdlet is used to assign NSX Security Tags to a virtual machine. .EXAMPLE Get-VM Web-01 | New-NsxSecurityTagAssignment -ApplyTag -SecurityTag (Get-NsxSecurityTag ST-Web-DMZ) Assign a single security tag to a virtual machine .EXAMPLE Get-NsxSecurityTag ST-Web-DMZ | New-NsxSecurityTagAssignment -ApplyToVm -VirtualMachine (Get-VM Web-01) Assign a single security tag to a virtual machine .EXAMPLE Get-VM Web-01 | New-NsxSecurityTagAssignment -ApplyTag -SecurityTag $( Get-NsxSecurityTag | where-object {$_.name -like "*prod*"} ) Assign all security tags containing "prod" in the name to a virtual machine .EXAMPLE Get-NsxSecurityTag | where-object { $_.name -like "*dmz*" } | New-NsxSecurityTagAssignment -ApplyToVm -VirtualMachine (Get-VM web01,app01,db01) Assign all security tags containing "DMZ" in the name to multiple virtual machines #> [CmdLetBinding(DefaultParameterSetName = "VirtualMachine")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "VirtualMachine")] [Parameter (Mandatory = $true, Position = 1, ParameterSetName = "SecurityTag")] [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop[]]$VirtualMachine, [Parameter (Mandatory = $true, Position = 1, ParameterSetName = "VirtualMachine")] [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "SecurityTag")] [ValidateScript( { ValidateSecurityTag $_ })] [System.Xml.XmlElement[]]$SecurityTag, [Parameter (Mandatory = $true, ParameterSetName = "VirtualMachine")] [switch]$ApplyTag, [Parameter (Mandatory = $true, ParameterSetName = "SecurityTag")] [switch]$ApplyToVm, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { foreach ( $tag in $SecurityTag) { $TagIdentifierString = $Tag.objectid foreach ( $vm in $VirtualMachine) { $vmMoid = $vm.ExtensionData.MoRef.Value $URI = "/api/2.0/services/securitytags/tag/$($TagIdentifierString)/vm/$($vmMoid)" Write-Progress -Activity "Adding Security Tag $($TagIdentifierString) to Virtual Machine $($vmMoid)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -connection $connection Write-Progress -Activity "Adding Security Tag $TagIdentifierString to Virtual Machine $($vmMoid)" -Completed } } } end {} } function Remove-NsxSecurityTagAssignment { <# .SYNOPSIS This cmdlet is used to remove NSX Security Tags assigned to a virtual machine .DESCRIPTION A NSX Security Tag is a arbitrary string. It is used in other functions of NSX such as Security Groups match criteria. Security Tags are applied to a Virtual Machine. This cmdlet assigns is used to remove NSX Security Tags assigned to a virtual machine .EXAMPLE Get-NsxSecurityTag ST-WEB-DMZ | Get-NsxSecurityTagAssignment | Remove-NsxSecurityTagAssignment Gets all assignment of Security Tag ST-WEB-DMZ and removes its assignment from all VMs with confirmation. .EXAMPLE Get-VM Web01 | Get-NsxSecurityTagAssignment | Remove-NsxSecurityTagAssignment Removes all security tags assigned to Web01 virtual machine. #> [CmdLetBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript ( { ValidateTagAssignment $_ })] [PSCustomObject]$TagAssignment, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( $confirm ) { $message = "Removing Security Tag $($TagAssignment.SecurityTag.Name) from $($TagAssignment.VirtualMachine.name) may impact desired Security Posture and expose your infrastructure." $question = "Proceed with removal of Security Tag?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/2.0/services/securitytags/tag/$($TagAssignment.SecurityTag.ObjectId)/vm/$($TagAssignment.VirtualMachine.ExtensionData.Moref.Value)" Write-Progress -Activity "Removing Security Tag $($TagAssignment.SecurityTag.ObjectId) to Virtual Machine $($TagAssignment.VirtualMachine.ExtensionData.Moref.Value)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Adding Security Tag $($TagAssignment.SecurityTag.ObjectId) to Virtual Machine $($TagAssignment.VirtualMachine.ExtensionData.Moref.Value)" -Completed } } end {} } function Get-NsxIpSet { <# .SYNOPSIS Retrieves NSX IPSets .DESCRIPTION An NSX IPSet is a grouping construct that allows for grouping of IP adresses, ranges and/or subnets in a sigle container that can be used either in DFW Firewall Rules or as members of a security group. This cmdlet returns IP Set objects. .EXAMPLE Get-NsxIpSet TestIPSet Retrieves the IPSet named TestIPSet .EXAMPLE Get-NsxIpSet Retrieves all ipsets. Includes locally and universally scoped ipsets. .EXAMPLE Get-NsxIpSet -LocalOnly Retrieves all locally scoped ipsets .EXAMPLE Get-NsxIpSet -UniversalOnly Retrieves only Universally scoped IPSets. .EXAMPLE Get-NSXIpSet TestEsgeIPSet -scopeId edge-1 Returns all locally configured IP Sets on the specified edge. #> [CmdLetBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $true, ParameterSetName = "objectId")] #Objectid of IPSet [string]$objectId, [Parameter (Mandatory = $true, ParameterSetName = "Name", Position = 1)] [Parameter (Mandatory = $false, ParameterSetName = "UniversalOnly", Position = 1)] [Parameter (Mandatory = $false, ParameterSetName = "LocalOnly", Position = 1)] #Name of IPSet [string]$Name, [Parameter (Mandatory = $false)] #ScopeId of IPSet. Can define multiple scopeIds in a list to iterate accross scopes. [string[]]$scopeId, [Parameter (Mandatory = $false)] #Return 'Readonly' (system) ipsets as well [switch]$IncludeReadOnly = $false, [Parameter (Mandatory = $true, ParameterSetName = "UniversalOnly")] #Return only Universal objects [switch]$UniversalOnly, [Parameter (Mandatory = $true, ParameterSetName = "LocalOnly")] #Return only Locally scoped objects [switch]$LocalOnly, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { if ( -not $PsBoundParameters.ContainsKey("scopeId") ) { switch ( $PSCmdlet.ParameterSetName ) { "UniversalOnly" { $scopeid = "universalroot-0" } "LocalOnly" { $scopeid = "globalroot-0" } Default { $scopeId = "globalroot-0", "universalroot-0" } } } } process { if ( -not $objectID ) { $ipsets = @() foreach ($scope in $scopeid ) { #All IPSets $URI = "/api/2.0/services/ipset/scope/$scope" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::list/ipset')) { if ( $name ) { $ipsets += $response.list.ipset | Where-Object { $_.name -eq $name } } else { $ipsets += $response.list.ipset } } } if ( $ipsets -and ( -not $IncludeReadOnly )) { $ipsets | Where-Object { -not ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ -query "descendant::extendedAttributes/extendedAttribute[name=`"isReadOnly`" and value=`"true`"]")) } } elseif ( $ipsets ) { $ipsets } } else { #Just getting a single named Security group $URI = "/api/2.0/services/ipset/$objectId" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::ipset')) { $ipsets = $response.ipset } if ( -not $IncludeReadOnly ) { $ipsets | Where-Object { -not ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ -query "descendant::extendedAttributes/extendedAttribute[name=`"isReadOnly`" and value=`"true`"]")) } } else { $ipsets } } } end {} } function New-NsxIpSet { <# .SYNOPSIS Creates a new NSX IPSet. .DESCRIPTION An NSX IPSet is a grouping construct that allows for grouping of IP adresses, ranges and/or subnets in a sigle container that can be used either in DFW Firewall Rules or as members of a security group. This cmdlet creates a new IP Set with the specified parameters. IPAddress is a string that can contain 1 or more of the following separated by commas IP address: (eg, 1.2.3.4) IP Range: (eg, 1.2.3.4-1.2.3.10) IP Subnet: (eg, 1.2.3.0/24) .EXAMPLE PS C:\> New-NsxIPSet -Name TestIPSet -Description "Testing IP Set Creation" -IPAddress "1.2.3.4,1.2.3.0/24" Creates a new IP Set in the scope globalroot-0. .EXAMPLE PS C:\> New-NsxIPSet -Name UniversalIPSet -Description "Testing Universal" -IPAddress "1.2.3.4,1.2.3.0/24" -Universal Creates a new Universal IP Set. .EXAMPLE PS C:\> New-NsxIPSet -Name EdgeIPSet -Description "Testing Edge IP Sets" -IPAddress "1.2.3.4,1.2.3.0/24" -scopeId edge-1 Creates a new IP Set on the specified edge.. #> [CmdletBinding()] param ( [Parameter (Mandatory = $true)] #Name of the IpSet. [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] #Descript of the IPSet. [ValidateNotNull()] [string]$Description = "", [Parameter (Mandatory = $false)] #Single string of comma separated ipaddresses, or a collection of ip address strings. [Alias ("IPAddresses")] [string[]]$IPAddress, [Parameter (Mandatory = $false)] #Scope of object. For universal object creation, use the -Universal switch. [ValidateScript( { if ($_ -match "^globalroot-0$|universalroot-0$|^edge-\d+$") { $True } else { Throw "$_ is not a valid scope. Valid options are: globalroot-0 | universalroot-0 | edge-id" } })] [string]$scopeId = "globalroot-0", [Parameter (Mandatory = $false)] #Create the IPSet as Universal object. [switch]$Universal = $false, [Parameter (Mandatory = $false)] #Create the IPSet with the inheritance set. Allows the IP Set to be used at a lower scope. [switch]$EnableInheritance = $false, [Parameter (Mandatory = $false)] #Return the objectid as a string rather than the whole XML object. [switch]$ReturnObjectIdOnly = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("ipset") $xmlDoc.appendChild($xmlRoot) | Out-Null Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText $Description if ( $IPAddress ) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "value" -xmlElementText ($IPAddress -join ",") } if ( ( $EnableInheritance ) -and ( -not ( $universal ) ) ) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "inheritanceAllowed" -xmlElementText "True" } #Do the post if ( $universal ) { $scopeId = "universalroot-0" } $body = $xmlroot.OuterXml $URI = "/api/2.0/services/ipset/$($scopeId.ToLower())" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection if ( $ReturnObjectIdOnly) { $response.content } else { Get-NsxIpSet -objectId $response.content -Connection $connection } } end {} } function Remove-NsxIpSet { <# .SYNOPSIS Removes the specified NSX IPSet. .DESCRIPTION An NSX IPSet is a grouping construct that allows for grouping of IP adresses, ranges and/or subnets in a sigle container that can be used either in DFW Firewall Rules or as members of a security group. This cmdlet removes the specified IP Set. If the object is currently in use the api will return an error. Use -force to override but be aware that the firewall rulebase will become invalid and will need to be corrected before publish operations will succeed again. .EXAMPLE PS C:\> Get-NsxIPSet TestIPSet | Remove-NsxIPSet #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$IPSet, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$force = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ipset -query "descendant::extendedAttributes/extendedAttribute[name=`"isReadOnly`" and value=`"true`"]") -and ( -not $force)) { Write-Warning "Not removing $($Ipset.Name) as it is set as read-only. Use -Force to force deletion." } else { if ( $confirm ) { $message = "IPSet removal is permanent." $question = "Proceed with removal of IP Set $($IPSet.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { if ( $force ) { $URI = "/api/2.0/services/ipset/$($IPSet.objectId)?force=true" } else { $URI = "/api/2.0/services/ipset/$($IPSet.objectId)?force=false" } Write-Progress -Activity "Remove IP Set $($IPSet.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove IP Set $($IPSet.Name)" -Completed } } } end {} } function Add-NsxIpSetMember { <# .SYNOPSIS Adds a new member to an existing IP Set. .DESCRIPTION An NSX IPSet is a grouping construct that allows for grouping of IP adresses, ranges and/or subnets in a sigle container that can be used either in DFW Firewall Rules or as members of a security group. This cmdlet adds a new member to the specified IP Set. IPAddress is a collection of strings, each of which can contain 1 only of the following IP address: (eg, 1.2.3.4) IP Range: (eg, 1.2.3.4-1.2.3.10) IP Subnet: (eg, 1.2.3.0/24) .EXAMPLE get-nsxipset test | Add-NsxIpSetMember -IPAddress 5.4.3.2 Adds the ip address 5.4.3.2 to the existing ipset test. .EXAMPLE get-nsxipset test | Add-NsxIpSetMember -IPAddress 5.4.3.0/24 Adds the cidr 5.4.3.0/24 to the existing ipset test .EXAMPLE get-nsxipset test | Add-NsxIpSetMember -IPAddress 5.4.3.2,1.2.3.0/24 Adds the ip address 5.4.3.2 and the cidr 1.2.3.0/24 to the existing ipset test #> [CmdletBinding()] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #Existing IPSet PowerNSX object to be modified. [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$IPSet, [Parameter (Mandatory = $true)] #Collection of ip addresses/ranges and/or CIDR's to be added to the ipset. [ValidateNotNullOrEmpty()] [string[]]$IPAddress, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $_ipset = $ipset.clonenode($true) if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ipset -query "child::value")) { Add-XmlElement -xmlRoot $_ipset -xmlElementName "value" -xmlElementText "" } $modified = $false foreach ( $value in $IPAddress ) { if ( $_ipset.value -eq "" ) { $modified = $true $_ipset.value = $value } else { if ( $_ipset.value -split "," -contains $value ) { Write-Warning "Value $value is already a member of the IPSet $($ipset.name)" } else { $modified = $true $_ipset.value += "," + $value } } } if ( $modified ) { #Do the post $body = $_ipset.OuterXml $URI = "/api/2.0/services/ipset/$($_ipset.objectId)" $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection try { [system.xml.xmldocument]$ipsetdoc = $response.content $ipsetdoc.ipset } catch { throw "Unable to interpret response content from NSX API as XML. Response: $response" } } } end {} } function Remove-NsxIpSetMember { <# .SYNOPSIS Removes a member from an existing IP Set. .DESCRIPTION An NSX IPSet is a grouping construct that allows for grouping of IP adresses, ranges and/or subnets in a sigle container that can be used either in DFW Firewall Rules or as members of a security group. This cmdlet adds removes a member IPAddress from the specified IP Set. IPAddress is a collection of strings, each of which can contain 1 only of the following IP address: (eg, 1.2.3.4) IP Range: (eg, 1.2.3.4-1.2.3.10) IP Subnet: (eg, 1.2.3.0/24) .EXAMPLE PS C:\> Get-NsxIpset Test-IPSet | Remove-NsxIpSetMember -IPAddress 3.3.3.3 Removes the address 3.3.3.3 and 3.3.3.3/32 from the given NSX IPSet .EXAMPLE PS C:\> Get-NsxIpset Test-IPSet | Remove-NsxIpSetMember -IPAddress 3.3.3.3/32 Removes the address 3.3.3.3 and 3.3.3.3/32 from the given NSX IPSet .EXAMPLE PS C:\> Get-NsxIpset Test-IPSet | Remove-NsxIpSetMember -IPAddress 10.0.0.0/8 Removes the network 10.0.0.0/8 from the given NSX IPSet .EXAMPLE PS C:\> Get-NsxIpset Test-IPSet | Remove-NsxIpSetMember -IPAddress 192.168.1.1-192.168.1.254 Removes the range 192.168.1.1-192.168.1.254 from the given NSX IPSet .EXAMPLE PS C:\> Get-NsxIpset Test-IPSet | Remove-NsxIpSetMember -IPAddress 3.3.3.3,10.0.0.0/8,192.168.1.1-192.168.1.254 Removes the given IP Addresses, Networks and Ranges from the NSX IPSet #> [CmdletBinding()] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #Existing IPSet PowerNSX object to be modified. [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$IPSet, [Parameter (Mandatory = $true)] #Collection of ip addresses/ranges and/or CIDR's to be removed from the ipset. [ValidateNotNullOrEmpty()] [string[]]$IPAddress, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $_ipset = $ipset.clonenode($true) $modified = $false if ( ( $_ipset.value -eq "" ) -or ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ipset -query "child::value")) ) { Write-Warning "IPSet $($ipset.name) ($($ipset.objectid)): No members found." } else { [system.collections.arraylist]$ValCollection = $_ipset.value -split "," foreach ( $value in $IPAddress ) { # An IPSET allows the users to enter a host as either 1.1.1.1 or # 1.1.1.1/32. So if the users specifies that they want to remove # 1.1.1.1 we need to look for both 1.1.1.1 AND 1.1.1.1/32 to remove. if ( ValidateIPHost $value ) { if ( $value -as [ipaddress] ) { if ( ( -not ( $valcollection -contains $value ) ) -and ( -not ( $valcollection -contains "$($value)/32" ) ) ) { Write-Warning "IPSet $($ipset.name) ($($ipset.objectid)): $Value is not a member of IPSet" } else { $modified = $true $ValCollection.Remove($value) $ValCollection.Remove("$($value)/32") } } else { if ( ( -not ( $valcollection -contains $value ) ) -and ( -not ( $valcollection -contains "$(($value -split "/")[0])" ) ) ) { Write-Warning "IPSet $($ipset.name) ($($ipset.objectid)): $Value is not a member of IPSet" } else { $modified = $true $ValCollection.Remove($value) $ValCollection.Remove("$(($value -split "/")[0])") } } } else { if ( ( -not ( $valcollection -contains $value ) ) ) { Write-Warning "IPSet $($ipset.name) ($($ipset.objectid)): $Value is not a member of IPSet" } else { $modified = $true $ValCollection.Remove($value) } } } # Aparently the API chucks a wobbly and returns a 400 error if you # try to remove the last IP Address from an IP Set resulting in a blank # value. But it will allow you to create one with no value set... go figure. if ( $ValCollection.count -eq 0 ) { throw "IPSet $($ipset.name) ($($ipset.objectid)): Operation not executed as it will result in an empty IP Set and the API will throw a 400 error." } } if ( $modified ) { $_ipset.value = $ValCollection -join "," #Do the post $body = $_ipset.OuterXml $URI = "/api/2.0/services/ipset/$($_ipset.objectId)" $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection try { [system.xml.xmldocument]$ipsetdoc = $response.content $ipsetdoc.ipset } catch { throw "Unable to interpret response content from NSX API as XML. Response: $response" } } } } function Remove-NsxIpPool { <# .SYNOPSIS Removes the specified NSX IPPool. .DESCRIPTION An IP Pool is a simple IPAM construct in NSX that simplifies automated IP address asignment for multiple NSX technologies including VTEP interfaces NSX Controllers. This cmdlet removes the specified IP Pool. If the object has current IP Address allocations the api will return an error. Use -force to override. .EXAMPLE PS C:\> Get-NsxIPPool TestIPPool | Remove-NsxIPPool #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #IPPool object to be removed. [ValidateScript( { ValidateIpPool $_ })] [System.Xml.XmlElement]$IPPool, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #Force removal of the ippool, even if it has current allocations. [switch]$force = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $IPPool -query "descendant::usedAddressCount[. != 0]") -and ( -not $force)) { Write-Warning "Not removing $($IPPool.Name) because it currently has allocated addresses. Use -force to override." } else { if ( $confirm ) { $message = "IP Pool removal is permanent." $question = "Proceed with removal of IP Pool $($IPPool.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/2.0/services/ipam/pools/$($IPPool.objectId)?force=$($force.tostring().tolower())" Write-Progress -Activity "Remove IP Pool $($IPPool.Name)" Invoke-NsxRestMethod -method "delete" -URI $URI -connection $connection | Out-Null Write-Progress -Activity "Remove IP Pool $($IPPool.Name)" -Completed } } } end {} } function Get-NsxMacSet { <# .SYNOPSIS Retrieves NSX MACSets .DESCRIPTION An NSX MACSet is a grouping construct that allows for grouping of MAC Addresses in a sigle container that can be used either in DFW Firewall Rules or as members of a security group. This cmdlet returns MAC Set objects. .EXAMPLE Retrieves all NSX MAC Sets Get-NsxMacSet .EXAMPLE Retrieves NSX MAC Set by name Get-NsxMacSet TEST_MAC_SET #> [CmdLetBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $false, ParameterSetName = "objectId")] #Get Mac sets by objectid [string]$objectId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [Parameter (Mandatory = $false, ParameterSetName = "UniversalOnly", Position = 1)] [Parameter (Mandatory = $false, ParameterSetName = "LocalOnly", Position = 1)] #Get mac sets by name [string]$Name, [Parameter (Mandatory = $false)] #ScopeId of MacSet. Can define multiple scopeIds in a list to iterate accross scopes. [string[]]$scopeId, [Parameter (Mandatory = $true, ParameterSetName = "UniversalOnly")] #Return only Universal objects [switch]$UniversalOnly, [Parameter (Mandatory = $true, ParameterSetName = "LocalOnly")] #Return only Locally scoped objects [switch]$LocalOnly, [Parameter (Mandatory = $false)] #Include mac sets with readonly attribute [switch]$IncludeReadOnly = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { if ( -not $PsBoundParameters.ContainsKey("scopeId") ) { switch ( $PSCmdlet.ParameterSetName ) { "UniversalOnly" { $scopeid = "universalroot-0" } "LocalOnly" { $scopeid = "globalroot-0" } Default { $scopeId = "globalroot-0", "universalroot-0" } } } } process { if ( -not $objectID ) { $MacSets = @() foreach ($scope in $scopeid ) { #All IPSets $URI = "/api/2.0/services/macset/scope/$scope" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::list/macset')) { if ( $name ) { $macsets += $response.list.macset | Where-Object { $_.name -eq $name } } else { $macsets += $response.list.macset } } } #Filter readonly if switch not set if ( $macsets -and (-not $IncludeReadOnly )) { $macsets | Where-Object { -not ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ -query "descendant::extendedAttributes/extendedAttribute[name=`"isReadOnly`" and value=`"true`"]")) } } else { $macsets } } else { #Just getting a single named MACset $URI = "/api/2.0/services/macset/$objectId" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::macset')) { $macsets = $response.macset } #Filter readonly if switch not set if ( -not $IncludeReadOnly ) { $macsets | Where-Object { -not ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ -query "descendant::extendedAttributes/extendedAttribute[name=`"isReadOnly`" and value=`"true`"]")) } } else { $macsets } } } end {} } function New-NsxMacSet { <# .SYNOPSIS Creates a new NSX MACSet. .DESCRIPTION An NSX MACSet is a grouping construct that allows for grouping of MAC Addresses in a sigle container that can be used either in DFW Firewall Rules or as members of a security group. This cmdlet creates a new MAC Set with the specified parameters. MacAddresses is a string that can contain 1 or more MAC Addresses the following separated by commas Mac address: (eg, 00:00:00:00:00:00) .EXAMPLE new-nsxmacset -name MAC_SET_TEST -Description "A sample MAC" -MacAddresses "BE:EF:CA:FE:DE:AD" Creates a MAC Set with the MAC address BEEF:CAFE:DEAD .EXAMPLE new-nsxmacset -name MAC_SET_TEST -Description "A sample MAC" -MacAddresses "BE:EF:CA:FE:DE:AD" -Universal Creates a MAC Set in the universal scope #> [CmdletBinding()] param ( [Parameter (Mandatory = $true)] #Name of the MacSet [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] #Description of the MacSet [ValidateNotNull()] [string]$Description = "", [Parameter (Mandatory = $false)] #Single string accepting comma separated Mac Addresses [string]$MacAddresses, [Parameter (Mandatory = $false)] #Scope of object. For universal object creation, use the -Universal switch. [ValidateScript( { if ($_ -match "^globalroot-0$|universalroot-0$|^edge-\d+$") { $True } else { Throw "$_ is not a valid scope. Valid options are: globalroot-0 | universalroot-0 | edge-id" } })] [string]$scopeId = "globalroot-0", [Parameter (Mandatory = $false)] #Create the MacSet as Universal object. [switch]$Universal = $false, [Parameter (Mandatory = $false)] #Create the MacSet with the inheritance set. Allows the MacSet to be used at a lower scope. [switch]$EnableInheritance = $false, [Parameter (Mandatory = $false)] #Return the objectid as a string rather than the whole XML object. [switch]$ReturnObjectIdOnly = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("macset") $xmlDoc.appendChild($xmlRoot) | Out-Null Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText $Description if ( $MacAddresses ) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "value" -xmlElementText $MacAddresses } if ( ( $EnableInheritance ) -and ( -not ( $universal ) ) ) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "inheritanceAllowed" -xmlElementText "True" } #Do the post $body = $xmlroot.OuterXml if ( $universal ) { $scopeId = "universalroot-0" } $URI = "/api/2.0/services/macset/$($scopeId.tolower())" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection if ( $ReturnObjectIdOnly) { $response.content } else { Get-NsxMacSet -objectId $response.content -Connection $connection } } end {} } function Remove-NsxMacSet { <# .SYNOPSIS Removes the specified NSX MacSet. .DESCRIPTION An NSX MacSet is a grouping construct that allows for grouping of Mac Addresses in a sigle container that can be used either in DFW Firewall Rules or as members of a security group. This cmdlet removes the specified MAC Set. If the object is currently in use the api will return an error. Use -force to override but be aware that the firewall rulebase will become invalid and will need to be corrected before publish operations will succeed again. .EXAMPLE This will remove a MAC Set by name. Get-NsxMacSet MAC_SET_TEST | Remove-NsxMacSet -confirm:$false can be used to avoid being prompted. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] #Macset as retrieved by get-nsxmacset to remove [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$MacSet, [Parameter (Mandatory = $False)] #Set to false to disable prompt on deletion [switch]$confirm = $true, [Parameter (Mandatory = $False)] #Enable force to remove objects in use, or set to readonly (system) [switch]$force = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $macset -query "descendant::extendedAttributes/extendedAttribute[name=`"isReadOnly`" and value=`"true`"]") -and ( -not $force)) { Write-Warning "Not removing $($MacSet.Name) as it is set as read-only. Use -Force to force deletion." } else { if ( $confirm ) { $message = "MACSet removal is permanent." $question = "Proceed with removal of MAC Set $($MACSet.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { if ( $force ) { $URI = "/api/2.0/services/macset/$($MACSet.objectId)?force=true" } else { $URI = "/api/2.0/services/macset/$($MACSet.objectId)?force=false" } Write-Progress -Activity "Remove MAC Set $($MACSet.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove MAC Set $($MACSet.Name)" -Completed } } } end {} } function Get-NsxService { <# .SYNOPSIS Retrieves NSX Services (aka Applications). .DESCRIPTION An NSX Service defines a service as configured in the NSX Distributed Firewall. This cmdlet retrieves existing services as defined within NSX. It also supports searching for services by TCP/UDP port number and will locate services that contain the specified port within a range definition as well as those explicitly configured with the given port. .EXAMPLE Example1: Get Service by name PS C:\> Get-NsxService -Name TestService Example2: Get Service by port (will match services that include the specified port within a range as well as those explicitly configured with the given port.) PS C:\> Get-NsxService -port 1234 #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $false, ParameterSetName = "objectId")] #Return service by objectId [string]$objectId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [Parameter (Mandatory = $false, ParameterSetName = "UniversalOnly", Position = 1)] [Parameter (Mandatory = $false, ParameterSetName = "LocalOnly", Position = 1)] #Return service by name [string]$Name, [Parameter (Mandatory = $false, ParameterSetName = "Port", Position = 1)] #Return services that have a either a matching port, or are defiuned by a range into which the specified port falls [int]$Port, [Parameter (Mandatory = $false)] #ScopeId of Service Group. Can define multiple scopeIds in a list to iterate accross scopes. [string[]]$scopeId, [Parameter (Mandatory = $false)] #Include services with readonly attribute [switch]$IncludeReadOnly = $false, [Parameter (Mandatory = $true, ParameterSetName = "UniversalOnly")] #Return only Universal objects [switch]$UniversalOnly, [Parameter (Mandatory = $true, ParameterSetName = "LocalOnly")] #Return only Locally scoped objects [switch]$LocalOnly, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { if (-not $PsBoundParameters.ContainsKey("scopeId") ) { switch ( $PSCmdlet.ParameterSetName ) { "UniversalOnly" { $scopeid = "universalroot-0" } "LocalOnly" { $scopeid = "globalroot-0" } Default { $scopeId = "globalroot-0", "universalroot-0" } } } } process { switch ( $PSCmdlet.ParameterSetName ) { "objectId" { #Just getting a single named service group $URI = "/api/2.0/services/application/$objectId" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::application')) { $svcs = $response.application #Filter readonly if switch not set if ( -not $IncludeReadOnly ) { $svcs | Where-Object { -not ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ -query "descendant::extendedAttributes/extendedAttribute[name=`"isReadOnly`" and value=`"true`"]")) } } else { $svcs } } } "Port" { # Service by port foreach ($scope in $scopeid ) { $application = $null $URI = "/api/2.0/services/application/scope/$scope" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::list/application')) { foreach ( $application in $response.list.application ) { if ( $application | Get-Member -MemberType Properties -Name element ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Testing service $($application.name) with ports: $($application.element.value)" #The port configured on a service is stored in element.value and can be #either an int, range (expressed as inta-intb, or a comma separated list of ints and/or ranges #So we split the value on comma, the replace the - with .. in a range, and wrap parentheses arount it #Then, lean on PoSH native range handling to force the lot into an int array... switch -regex ( $application.element.value ) { "^[\d,-]+$" { [string[]]$valarray = $application.element.value.split(",") foreach ($val in $valarray) { Write-Debug "$($MyInvocation.MyCommand.Name) : Converting range expression and expanding: $val" [int[]]$ports = Invoke-Expression ( $val -replace '^(\d+)-(\d+)$', '($1..$2)' ) #Then test if the port int array contains what we are looking for... if ( $ports.contains($port) ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Matched Service $($Application.name)" #Filter readonly if switch not set if ( -not $IncludeReadOnly ) { $application | Where-Object { -not ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ -query "descendant::extendedAttributes/extendedAttribute[name=`"isReadOnly`" and value=`"true`"]")) } } else { $application } break } } } default { #do nothing, port number is not numeric.... Write-Debug "$($MyInvocation.MyCommand.Name) : Ignoring $($application.name) - non numeric element: $($application.element | Format-XML)" } } } else { Write-Debug "$($MyInvocation.MyCommand.Name) : Ignoring $($application.name) - element not defined" } } } } } Default { $svcs = @() foreach ($scope in $scopeid ) { #All Services $URI = "/api/2.0/services/application/scope/$scope" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query 'descendant::list/application')) { if ( $name ) { $svcs += $response.list.application | Where-Object { $_.name -eq $name } } else { $svcs += $response.list.application } } } #Filter readonly if switch not set if ( -not $IncludeReadOnly ) { $svcs | Where-Object { -not ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_ -query "descendant::extendedAttributes/extendedAttribute[name=`"isReadOnly`" and value=`"true`"]")) } } else { $svcs } } } } end {} } function New-NsxService { <# .SYNOPSIS Creates a new NSX Service (aka Application). .DESCRIPTION An NSX Service defines a service as configured in the NSX Distributed Firewall. This cmdlet creates a new service of the specified configuration. .EXAMPLE PS C:\> New-NsxService -Name TestService -Description "Test creation of a service" -Protocol TCP -port 1234 #> [CmdletBinding()] param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] [ValidateNotNull()] [string]$Description = "", [Parameter (Mandatory = $true)] [ValidateSet ( "AARP", "AH", "ARPATALK", "ATMFATE", "ATMMPOA", "BPQ", "CUST", "DEC", "DIAG", "DNA_DL", "DNA_RC", "DNA_RT", "ESP", "FR_ARP", "FTP", "GRE", "ICMP", "IEEE_802_1Q", "IGMP", "IPCOMP", "IPV4", "IPV6", "IPV6FRAG", "IPV6ICMP", "IPV6NONXT", "IPV6OPTS", "IPV6ROUTE", "IPX", "L2_OTHERS", "L2TP", "L3_OTHERS", "LAT", "LLC", "LOOP", "MS_RPC_TCP", "MS_RPC_UDP", "NBDG_BROADCAST", "NBNS_BROADCAST", "NETBEUI", "ORACLE_TNS", "PPP", "PPP_DISC", "PPP_SES", "RARP", "RAW_FR", "RSVP", "SCA", "SCTP", "SUN_RPC_TCP", "SUN_RPC_UDP", "TCP", "UDP", "X25" )] [string]$Protocol, [Parameter (Mandatory = $false)] [string]$port, [Parameter (Mandatory = $false)] [string]$SourcePort, [Parameter (Mandatory = $false)] #Scope of object. For universal object creation, use the -Universal switch. [ValidateScript( { if ($_ -match "^globalroot-0$|universalroot-0$|^edge-\d+$") { $True } else { Throw "$_ is not a valid scope. Valid options are: globalroot-0 | universalroot-0 | edge-id" } })] [string]$scopeId = "globalroot-0", [Parameter (Mandatory = $false)] #Create the Service as Universal object. [switch]$Universal = $false, [Parameter (Mandatory = $false)] #Create the Service with the inheritance set. Allows the Service to be used at a lower scope. [switch]$EnableInheritance = $false, [Parameter (Mandatory = $false)] [switch]$ReturnObjectIdOnly = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { #Cant do all this in Param validation due to fact that port must not be mandatory and issues with binding order when splatting... if (( $AllServicesRequiringPort -contains $protocol ) -and ( -not $PSBoundParameters.ContainsKey("Port")) ) { throw "Specified protocol requires a port value to be specified." } if ( $PSBoundParameters.ContainsKey("Port")) { if (( @("TCP", "UDP") -contains $protocol ) -and ( $port -notmatch "^[\d,-]+$" )) { throw "TCP or UDP port numbers must be either an integer, range (nn-nn) or commma separated integers or ranges." } if ( ( @("FTP", "MS_RPC_TCP", "MS_RPC_UDP", "NBDG_BROADCAST", "NBNS_BROADCAST", "ORACLE_TNS", "SUN_RPC_TCP", "SUN_RPC_UDP") -contains $Protocol ) -and (-not ( ($port -as [int]) -and ( (1..65535) -contains $port )))) { throw "Valid port numbers must be an integer between 1-65535." } if (( $protocol -eq "ICMP") -and ( $AllValidIcmpTypes -notcontains $port )) { throw "Invalid ICMP protocol $port. Specify one of $($AllValidIcmpTypes -join ", ")" } if (($protocol -eq "L2_OTHERS") -and ( $port -notmatch "0x[0-9A-Fa-f]{4}" )) { throw "L2_OTHER protocoltype `'port`' must specify a valid ethertype in hex (eg. 0x0800)" } if (($protocol -eq "L3_OTHERS") -and ( (1..255) -notcontains $port )) { throw "L3_OTHER protocoltype `'port`' must specify a valid IP protocol number in the range 1-255" } if ($PSBoundParameters.ContainsKey("Port") -and (($protocol -notmatch "ICMP|TCP|UDP") -and ( $AllServicesNotRequiringPort -contains $Protocol ))) { #Validation is only executed if user specified a value for port... ICMP, UDP and TCP are special in that you can, but dont have to specify a 'port'. throw "Specified protocol does not allow a port value to be specified." } } if ($PSBoundParameters.ContainsKey("SourcePort")) { if (( @("TCP", "UDP") -contains $protocol ) -and ( $SourcePort -notmatch "^[\d,-]+$" )) { throw "TCP or UDP source port numbers must be either an integer, range (nn-nn) or commma separated integers or ranges." } if ( ( @("FTP", "MS_RPC_TCP", "MS_RPC_UDP", "NBDG_BROADCAST", "NBNS_BROADCAST", "ORACLE_TNS", "SUN_RPC_TCP", "SUN_RPC_UDP") -contains $Protocol ) -and (-not ( ($SourcePort -as [int]) -and ( (1..65535) -contains $SourcePort )))) { throw "Valid source port numbers must be an integer between 1-65535." } if ( $AllServicesValidSourcePort -notcontains $protocol ) { throw "Specified protocol does not allow a source port value to be specified" } } } process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("application") $xmlDoc.appendChild($xmlRoot) | Out-Null Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText $Description #Create the 'element' element ??? :) [System.XML.XMLElement]$xmlElement = $XMLDoc.CreateElement("element") $xmlRoot.appendChild($xmlElement) | Out-Null Add-XmlElement -xmlRoot $xmlElement -xmlElementName "applicationProtocol" -xmlElementText $Protocol.ToUpper() if ( $PSBoundParameters.ContainsKey("Port")) { Add-XmlElement -xmlRoot $xmlElement -xmlElementName "value" -xmlElementText $Port } if ( $PSBoundParameters.ContainsKey("SourcePort")) { Add-XmlElement -xmlRoot $xmlElement -xmlElementName "sourcePort" -xmlElementText $SourcePort } if ( ( $EnableInheritance ) -and ( -not ( $universal ) ) ) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "inheritanceAllowed" -xmlElementText "True" } #Do the post $body = $xmlroot.OuterXml if ( $universal ) { $scopeId = "universalroot-0" } $URI = "/api/2.0/services/application/$($scopeId.tolower())" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection if ( $ReturnObjectIdOnly) { $response.content } else { Get-NsxService -objectId $response.content -Connection $connection } } end {} } function Remove-NsxService { <# .SYNOPSIS Removes the specified NSX Service (aka Application). .DESCRIPTION An NSX Service defines a service as configured in the NSX Distributed Firewall. This cmdlet removes the NSX service specified. .EXAMPLE Get-NsxService -Name TestService | Remove-NsxService Removes the service TestService #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$Service, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$force = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $Service -query "descendant::extendedAttributes/extendedAttribute[name=`"isReadOnly`" and value=`"true`"]") -and ( -not $force)) { Write-Warning "Not removing $($Service.Name) as it is set as read-only. Use -Force to force deletion." } else { if ( $confirm ) { $message = "Service removal is permanent." $question = "Proceed with removal of Service $($Service.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { if ( $force ) { $URI = "/api/2.0/services/application/$($Service.objectId)?force=true" } else { $URI = "/api/2.0/services/application/$($Service.objectId)?force=false" } Write-Progress -Activity "Remove Service $($Service.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Service $($Service.Name)" -Completed } } } end {} } Function Get-NsxServiceGroup { <# .SYNOPSIS Retrieves a list of NSX Service Groups. .DESCRIPTION Lists all created NSX Service Groups. Service groups contain a mixture of selected ports to represent a potential grouping of like ports. This cmdlet retrieves the service group of the specified configuration. .EXAMPLE Get-NsxServiceGroup Retrieves all NSX Service Groups .EXAMPLE Get-NsxServiceGroup Heartbeat Retrieves the default NSX Service Group called Heartbeat .EXAMPLE Get-NsxServiceGroup | where-object {$_.name -match ("Exchange")} | select-object name Retrieves all Services Groups that have the string "Exchange" in their name property e.g: ---- Microsoft Exchange 2003 MS Exchange 2007 Transport Servers MS Exchange 2007 Unified Messaging Centre MS Exchange 2007 Client Access Server Microsoft Exchange 2007 MS Exchange 2007 Mailbox Servers Microsoft Exchange 2010 MS Exchange 2010 Client Access Servers MS Exchange 2010 Transport Servers MS Exchange 2010 Mailbox Servers MS Exchange 2010 Unified Messaging Server #> [CmdLetBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $true, ParameterSetName = "objectId")] #Objectid of Service Group [string]$objectId, [Parameter (Mandatory = $true, Position = 1, ParameterSetName = "Name")] [Parameter (Mandatory = $false, ParameterSetName = "UniversalOnly", Position = 1)] [Parameter (Mandatory = $false, ParameterSetName = "LocalOnly", Position = 1)] # Name of the Service Group [ValidateNotNullorEmpty()] [string]$Name, [Parameter (Mandatory = $false)] #ScopeId of Service Group. Can define multiple scopeIds in a list to iterate accross scopes. [string[]]$scopeId, [Parameter (Mandatory = $true, ParameterSetName = "UniversalOnly")] #Return only Universal objects [switch]$UniversalOnly, [Parameter (Mandatory = $true, ParameterSetName = "LocalOnly")] #Return only Locally scoped objects [switch]$LocalOnly, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { if ( -not $PsBoundParameters.ContainsKey("scopeId") ) { switch ( $PSCmdlet.ParameterSetName ) { "UniversalOnly" { $scopeid = "universalroot-0" } "LocalOnly" { $scopeid = "globalroot-0" } Default { $scopeId = "globalroot-0", "universalroot-0" } } } } process { if ( -not $objectId ) { #All Sections $servicegroup = @() foreach ($scope in $scopeid ) { $URI = "/api/2.0/services/applicationgroup/scope/$scope" [system.xml.xmlDocument]$response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query "child::list/applicationGroup")) { $servicegroup += $response.list.applicationGroup } } if ($PsBoundParameters.ContainsKey("Name")) { $servicegroup | Where-Object { $_.name -eq $name } } else { $servicegroup } } else { $URI = "/api/2.0/services/applicationgroup/$objectid" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $response.applicationGroup } } end {} } function Get-NsxServiceGroupMember { <# .SYNOPSIS Retrieves a list of services within an NSX Service Groups. .DESCRIPTION Lists all serivces associated to an NSX Service Groups. Service groups contain a mixture of selected ports to represent a potential grouping of like ports. This cmdlet retrieves the member services within a Service Group for specific or all Service Groups .EXAMPLE Get-NsxServiceGroup | Get-NsxServiceGroupMember Retrieves all members of all Service Groups. You are brave. .EXAMPLE Get-NsxServiceGroup Heartbeat | Get-NsxServiceGroupMember Retrieves all members of the Service Group Heartbeat e.g: objectId : application-70 objectTypeName : Application vsmUuid : 42019B98-63EC-995F-6CBB-FF738D027F92 nodeId : 0dd7c0dd-a194-4df1-a14b-56a1617c2f0f revision : 2 type : type name : Vmware-VCHeartbeat scope : scope clientHandle : extendedAttributes : isUniversal : false universalRevision : 0 objectId : application-180 objectTypeName : Application vsmUuid : 42019B98-63EC-995F-6CBB-FF738D027F92 nodeId : 0dd7c0dd-a194-4df1-a14b-56a1617c2f0f revision : 2 type : type name : Vmware-Heartbeat-PrimarySecondary scope : scope clientHandle : extendedAttributes : isUniversal : false universalRevision : 0 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateServiceOrServiceGroup $_ })] [System.Xml.XmlElement]$ServiceGroup, [Parameter (Mandatory = $false)] [string]$scopeId = "globalroot-0", [Parameter (Mandatory = $false)] [string]$objectId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ServiceGroup -query "child::member")) { $ServiceGroup.member } } end {} } function Remove-NsxServiceGroup { <# .SYNOPSIS Removes the specified NSX Service Group. .DESCRIPTION A service group is a container that includes Services and other Service Groups. These Service Groups are used by the NSX Distributed Firewall when creating firewall rules. They can also be referenced by Service Composer's Security Policies. This cmdlet removes the specified Service Group. .EXAMPLE Get-NsxServiceGroup Heartbeat | Remove-NsxServiceGroup This will remove the Service Group Heartbeat. All members of the Service Group are not affected. .EXAMPLE Get-NsxServiceGroup | Remove-NsxServiceGroup -confirm:$false This will retrieve and remove ALL Service Groups without confirmation prompt. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateServiceGroup $_ })] [System.Xml.XmlElement]$ServiceGroup, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$force = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Service Group removal is permanent." $question = "Proceed with removal of Service group $($ServiceGroup.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { if ( $force ) { $URI = "/api/2.0/services/applicationgroup/$($ServiceGroup.objectid)?force=true" } else { $URI = "/api/2.0/services/applicationgroup/$($ServiceGroup.objectid)?force=false" } Write-Progress -Activity "Remove Service Group $($ServiceGroup.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Service Group $($ServiceGroup.Name)" -Completed } } end {} } function New-NsxServiceGroup { <# .SYNOPSIS Creates a new Service Group to which new Services or Service Groups can be added. .DESCRIPTION A service group is a container that includes Services and other Service Groups. These Service Groups are used by the NSX Distributed Firewall when creating firewall rules. They can also be referenced by Service Composer's Security Policies. .EXAMPLE New-NsxServiceGroup PowerNSX-SVG Creates a new Service Group called PowerNSX-SVG objectId : applicationgroup-53 objectTypeName : ApplicationGroup vsmUuid : 42019B98-63EC-995F-6CBB-FF738D027F92 nodeId : 0dd7c0dd-a194-4df1-a14b-56a1617c2f0f revision : 1 type : type name : PowerNSX-SVG description : scope : scope clientHandle : extendedAttributes : isUniversal : false universalRevision : 0 inheritanceAllowed : false #> [CmdletBinding()] param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] [ValidateNotNull()] [string]$Description = "", [Parameter (Mandatory = $false)] #Scope of object. For universal object creation, use the -Universal switch. [ValidateScript( { if ($_ -match "^globalroot-0$|universalroot-0$|^edge-\d+$") { $True } else { Throw "$_ is not a valid scope. Valid options are: globalroot-0 | universalroot-0 | edge-id" } })] [string]$scopeId = "globalroot-0", [Parameter (Mandatory = $false)] #Create the Service Group as Universal object. [switch]$Universal = $false, [Parameter (Mandatory = $false)] #Create the Service Group with the inheritance set. Allows the Service Group to be used at a lower scope. [switch]$EnableInheritance = $false, [Parameter (Mandatory = $false)] [switch]$ReturnObjectIdOnly = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("applicationGroup") $xmlDoc.appendChild($xmlRoot) | Out-Null Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText $Description if ( ( $EnableInheritance ) -and ( -not ( $universal ) ) ) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "inheritanceAllowed" -xmlElementText "True" } if ( $universal ) { $scopeId = "universalroot-0" } $body = $xmlroot.OuterXml $uri = "/api/2.0/services/applicationgroup/$($scopeId.ToLower())" $response = Invoke-NsxWebRequest -URI $uri -method "post" -body $body -connection $connection if ( $ReturnObjectIdOnly) { $response.content } else { Get-NsxServiceGroup -objectId $response.content -Connection $connection } } end {} } function Add-NsxServiceGroupMember { <# .SYNOPSIS Adds a single Service, numerous Services, or a Service Group to a Service Group .DESCRIPTION Adds the defined Service or Service Group to an NSX Service Groups. Service groups contain a mixture of selected ports to represent a potential grouping of like ports. This cmdlet adds the defined Services or Service Groups within a Service Group for specific or all Service Groups .EXAMPLE $Service1 = Get-NsxService http -LocalOnly PS C:\> Get-NsxServiceGroup SG_PowerNSX | Add-NsxServiceGroupMember -Member $Service1 Add service http to Service Group SG_PowerNSX .EXAMPLE $Service1 = Get-NsxService http -LocalOnly PS C:\> $Service2 = Get-NsxService https -LocalOnly PS C:\> Get-NsxServiceGroup SG_PowerNSX2 | Add-NsxServiceGroupMember $Service1, $Service2 Add service http and https to Service Group SG_PowerNSX2 #> param ( #Mastergroup added from Get-NsxServiceGroup [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateServiceGroup $_ })] [System.Xml.XmlElement]$ServiceGroup, [Parameter (Mandatory = $true, Position = 1)] [ValidateScript( { ValidateServiceOrServiceGroup $_ })] #The [] in XmlElement means it can expect more than one object! [System.Xml.XmlElement[]]$Member, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { foreach ($Mem in $Member) { $URI = "/api/2.0/services/applicationgroup/$($ServiceGroup.objectId)/members/$($Mem.objectId)" $null = Invoke-NsxWebRequest -method "PUT" -URI $URI -connection $connection Write-Progress -Activity "Adding Service or Service Group $($Mem) to Service Group $($ServiceGroup)" } } end {} } function Get-NsxApplicableMember { <# .SYNOPSIS Retrieves a list of applicable members for either Security Groups or Service Groups .DESCRIPTION Security Groups and Service Groups can contain members of specific types. Basic information about all valid (applicable) members can be retrieved using a simple API call which is typically much less expensive than the alternative of retrieving the complete configuration from the API for a specific type of object. This cmdlet also exposes 'shortcut' functionality that lets you retrieve object name to objectId mapping of many object types in NSX that can improve the performance of scripts in high scale environments. Hat tip to Dale Coghlan (sneauku.com) for pointing out the usefulness of this API in large scale environments. See http://www.sneaku.com/2016/07/13/how-to-find-object-ids-for-almost-everything/ for more information. .EXAMPLE Get-NsxApplicableMember -SecurityGroupApplicableMembers -MemberType VirtualMachine Get the virtual machine applicable member list .EXAMPLE Get-NsxApplicableMember -ServiceGroupApplicableMembers Get the applicable member list for ServiceGroup membership. .EXAMPLE Get-NsxApplicableMember -SecurityGroupApplicableMembers -MemberType IPSet -Universal Get the Universal IP Set applicable member list .EXAMPLE Get-NsxApplicableMember -ServiceGroupApplicableMembers -Universal Get the applicable member list for Universal ServiceGroup membership. #> [CmdLetBinding(DefaultParameterSetName = "securitygroup")] param ( [Parameter (Mandatory = $false)] [ValidateScript( { if ($_ -match "^globalroot-0$|universalroot-0$|^edge-\d+$") { $True } else { Throw "$_ is not a valid scope. Valid options are: globalroot-0 | universalroot-0 | edge-id" } })] [string]$scopeId = "globalroot-0", [Parameter (Mandatory = $true, ParameterSetName = "securitygroup" )] [switch]$SecurityGroupApplicableMembers, [Parameter (Mandatory = $true, ParameterSetName = "applicationgroup" )] [switch]$ServiceGroupApplicableMembers, [Parameter (Mandatory = $true, ParameterSetName = "securitygroup" )] [ValidateSet("IPSet", "ClusterComputeResource", "VirtualWire", "VirtualMachine", "DirectoryGroup", "SecurityGroup", "VirtualApp", "ResourcePool", "DistributedVirtualPortgroup", "Datacenter", "Network", "Vnic", "SecurityTag", "MACSet", IgnoreCase = $false)] [string]$MemberType, [Parameter (Mandatory = $false)] [switch]$Universal = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $universal ) { $scopeId = "universalroot-0" } if ( $PSCmdlet.ParameterSetName -eq "securitygroup") { $URI = "/api/2.0/services/securitygroup/scope/$($scopeId.ToLower())/members/$MemberType" } else { $URI = "/api/2.0/services/applicationgroup/scope/$($scopeId.ToLower())/members/" } try { $response = Invoke-NsxWebRequest -URI $Uri -method Get -connection $connection } catch { throw "Failed retrieving applicable members. $_" } if ( $response | Get-Member -MemberType Property -Name Content ) { try { [xml]$content = $response.Content if ( Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $content -query "child::list/basicinfo") { $content.list.basicInfo } } catch { throw "Content returned from NSX API could not be parsed as applicable member XML." } } else { throw "No Content returned from NSX API call." } } end {} } ######### ######### # Firewall related functions ###Private functions function Add-NsxSourceDestNode { param ( [system.xml.xmlelement]$Rule, [ValidateSet ("sources", "destinations", IgnoreCase = $false)] [string]$NodeType, [switch]$negated ) #Create the parent sources element $XmlDoc = $Rule.OwnerDocument [System.XML.XMLElement]$xmlNode = $XMLDoc.CreateElement($NodeType) $Rule.AppendChild($xmlNode) | Out-Null #The excluded attribute indicates negation $xmlNegated = $xmlDoc.createAttribute("excluded") $xmlNode.Attributes.Append($xmlNegated) | Out-Null $xmlNegated.value = $Negated.ToString().ToLower() } function Add-NsxSourceDestMember { #Internal function - Handles building the source/dest xml node for a given object. # Updates NB 05/17 -> Modified for Add-NSxFirewallRuleMember cmdlet use. # - Accepts rule (rather than doc) object now # - Returns modified rule, rather than just the source/dest node. # - Renamed to reflect 'member' terminology # - Removed negation logic (moved back to new-rule due to logic not being applicable to individual member instances, function to be duplicated in set-rule cmdlet to allow flipping of negation (and other functions)) param ( [Parameter (Mandatory = $true)] [ValidateSet ("source", "destination", IgnoreCase = $false)] [string]$membertype, [object[]]$memberlist, [System.Xml.XmlElement]$rule ) # Get Doc object from passed rule $xmlDoc = $rule.OwnerDocument # Get SrcDestNode parent element. Have to use xpath here as the elem may be empty and powershell unhelpfully turns that into a string for us :| if ( $membertype -eq "Source" ) { [System.Xml.XmlElement]$xmlSrcDestNode = Invoke-XpathQuery -query "child::sources" -QueryMethod SelectSingleNode -Node $rule } else { [System.Xml.XmlElement]$xmlSrcDestNode = Invoke-XpathQuery -query "child::destinations" -QueryMethod SelectSingleNode -Node $rule } #Loop the memberlist and create appropriate element in the srcdest node. foreach ($member in $memberlist) { if ( ( $member -as [ipaddress]) -or ( ValidateIPRange -argument $member ) -or ( ValidateIPPrefix -argument $member ) ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Building source/dest node for $member" } else { Write-Debug "$($MyInvocation.MyCommand.Name) : Building source/dest node for $($member.name)" } #Build the return XML element and append to our srcdestnode [System.XML.XMLElement]$xmlMember = $XMLDoc.CreateElement($memberType) $xmlSrcDestNode.appendChild($xmlMember) | Out-Null if ( ( $member -as [ipaddress]) -or ( ValidateIPRange -argument $member ) -or ( ValidateIPPrefix -argument $member ) ) { #Item is v4 or 6 address Write-Debug "$($MyInvocation.MyCommand.Name) : Object $member is an ipaddress" Add-XmlElement -xmlRoot $xmlMember -xmlElementName "value" -xmlElementText $member Add-XmlElement -xmlRoot $xmlMember -xmlElementName "type" -xmlElementText "Ipv4Address" } elseif ( $member -is [system.xml.xmlelement] ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Object $($member.name) is specified as xml element" #XML representation of NSX object passed - ipset, sec group or logical switch #get appropritate name, value. Add-XmlElement -xmlRoot $xmlMember -xmlElementName "value" -xmlElementText $member.objectId Add-XmlElement -xmlRoot $xmlMember -xmlElementName "name" -xmlElementText $member.name Add-XmlElement -xmlRoot $xmlMember -xmlElementName "type" -xmlElementText $member.objectTypeName } else { Write-Debug "$($MyInvocation.MyCommand.Name) : Object $($member.name) is specified as supported powercli object" #Proper PowerCLI Object passed #If passed object is a NIC, we have to do some more digging if ( $member -is [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop] ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Object $($member.name) is vNic" #Naming based on DFW UI standard Add-XmlElement -xmlRoot $xmlMember -xmlElementName "name" -xmlElementText "$($member.parent.name) - $($member.name)" Add-XmlElement -xmlRoot $xmlMember -xmlElementName "type" -xmlElementText "Vnic" $vmUuid = ($member.parent | Get-View).config.instanceuuid $MemberMoref = "$vmUuid.$($member.id.substring($member.id.length-3))" Add-XmlElement -xmlRoot $xmlMember -xmlElementName "value" -xmlElementText $MemberMoref } else { #any other accepted PowerCLI object, we just need to grab details from the moref. Add-XmlElement -xmlRoot $xmlMember -xmlElementName "name" -xmlElementText $member.name Add-XmlElement -xmlRoot $xmlMember -xmlElementName "type" -xmlElementText $member.extensiondata.moref.type Add-XmlElement -xmlRoot $xmlMember -xmlElementName "value" -xmlElementText $member.extensiondata.moref.value } } } } function New-NsxServiceNode { #Internal function - Handles building the appliedto xml node for a given object. param ( [object[]]$itemlist, [System.XML.XMLDocument]$xmlDoc ) [System.XML.XMLElement]$xmlReturn = $XMLDoc.CreateElement("services") foreach ($item in $itemlist) { # Check to see if a protocol AND port are specified if ( ($item -is [string]) -and ($item -match "/") ) { $itemSplit = $item -split "/" [System.XML.XMLElement]$xmlItem = $XMLDoc.CreateElement("service") Add-XmlElement -xmlRoot $xmlItem -xmlElementName "protocolName" -xmlElementText $itemSplit[0].ToUpper() Add-XmlElement -xmlRoot $xmlItem -xmlElementName "destinationPort" -xmlElementText $itemSplit[1] Write-Debug "$($MyInvocation.MyCommand.Name) : Building service node for $($item)" } # Otherwise we assume its just a Protocol with no port specified elseif ($item -is [string]) { [System.XML.XMLElement]$xmlItem = $XMLDoc.CreateElement("service") Add-XmlElement -xmlRoot $xmlItem -xmlElementName "protocolName" -xmlElementText $item.ToUpper() Write-Debug "$($MyInvocation.MyCommand.Name) : Building service node for $($item)" } # or its either an XML object, or a collection of objects (already verified as XML objects through validation script) elseif ( ( $item -is [System.Xml.XmlElement] ) -or ( $item -is [System.Object] ) ) { foreach ( $serviceitem in $item ) { [System.XML.XMLElement]$xmlItem = $XMLDoc.CreateElement("service") Add-XmlElement -xmlRoot $xmlItem -xmlElementName "value" -xmlElementText $serviceItem.objectId $xmlReturn.appendChild($xmlItem) | Out-Null Write-Debug "$($MyInvocation.MyCommand.Name) : Building service node for $($item.name)" } } $xmlReturn.appendChild($xmlItem) | Out-Null } $xmlReturn } function New-NsxEdgeServiceNode { #Internal function - Handles building the Edge fw service xml node for a given object. param ( [Parameter (Mandatory = $true)] [object[]]$itemlist, [Parameter (Mandatory = $true)] [System.XML.XMLElement]$xmlRule ) $xmlDoc = $xmlRule.OwnerDocument $Application = $XmlDoc.CreateElement("application") $null = $xmlrule.AppendChild($Application) foreach ($item in $itemlist) { # Check to see if a protocol AND port are specified if ( ($item -is [string]) -and ($item -match "/") ) { $itemSplit = $item -split "/" $svc = $XMLDoc.CreateElement("service") $null = $Application.AppendChild($svc) Add-XmlElement -xmlRoot $svc -xmlElementName "protocol" -xmlElementText $itemSplit[0].ToUpper() Add-XmlElement -xmlRoot $svc -xmlElementName "port" -xmlElementText $itemSplit[1] Write-Debug "$($MyInvocation.MyCommand.Name) : Building protocol/port service node for $($item)" } # Otherwise we assume its just a Protocol with no port specified elseif ($item -is [string]) { $svc = $XMLDoc.CreateElement("service") $null = $Application.AppendChild($svc) Add-XmlElement -xmlRoot $svc -xmlElementName "protocol" -xmlElementText $item.ToUpper() Write-Debug "$($MyInvocation.MyCommand.Name) : Building protocol service node for $($item)" } # or its either an XML object, or a collection of objects (already verified as XML objects through validation script) else { Add-XmlElement -xmlRoot $Application -xmlElementName "applicationId" -xmlElementText $item.objectId Write-Debug "$($MyInvocation.MyCommand.Name) : Building application service node for $($item.name)" } } } function New-NsxAppliedToListNode { #Internal function - Handles building the appliedto xml node for a given object. param ( [object[]]$itemlist, [System.XML.XMLDocument]$xmlDoc, [switch]$ApplyToDFW, [switch]$ApplyToAllEdges ) [System.XML.XMLElement]$xmlReturn = $XMLDoc.CreateElement("appliedToList") #Iterate the appliedTo passed and build appliedTo nodes. #$xmlRoot.appendChild($xmlReturn) | out-null foreach ($item in $itemlist) { Write-Debug "$($MyInvocation.MyCommand.Name) : Building appliedTo node for $($item.name)" #Build the return XML element [System.XML.XMLElement]$xmlItem = $XMLDoc.CreateElement("appliedTo") if ( $item -is [system.xml.xmlelement] ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Object $($item.name) is specified as xml element" if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $item -query 'descendant::edgeSummary')) { Write-Debug "$($MyInvocation.MyCommand.Name) : Object $($item.name) is an edge object" if ( $ApplyToAllEdges ) { #Apply to all edges is default off, so this means the user asked for something stupid throw "Cant specify Edge Object in applied to list and ApplyToAllEdges simultaneously." } #We have an edge, and edges have the details we need in their EdgeSummary element: Add-XmlElement -xmlRoot $xmlItem -xmlElementName "value" -xmlElementText $item.edgeSummary.objectId Add-XmlElement -xmlRoot $xmlItem -xmlElementName "name" -xmlElementText $item.edgeSummary.name Add-XmlElement -xmlRoot $xmlItem -xmlElementName "type" -xmlElementText $item.edgeSummary.objectTypeName } else { #Something specific passed in applied to list, turn off Apply to DFW. $ApplyToDFW = $false #XML representation of NSX object passed - ipset, sec group or logical switch #get appropritate name, value. Add-XmlElement -xmlRoot $xmlItem -xmlElementName "value" -xmlElementText $item.objectId Add-XmlElement -xmlRoot $xmlItem -xmlElementName "name" -xmlElementText $item.name Add-XmlElement -xmlRoot $xmlItem -xmlElementName "type" -xmlElementText $item.objectTypeName } } else { #Something specific passed in applied to list, turn off Apply to DFW. $ApplyToDFW = $false Write-Debug "$($MyInvocation.MyCommand.Name) : Object $($item.name) is specified as supported powercli object" #Proper PowerCLI Object passed #If passed object is a NIC, we have to do some more digging if ( $item -is [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop] ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Object $($item.name) is vNic" #Naming based on DFW UI standard Add-XmlElement -xmlRoot $xmlItem -xmlElementName "name" -xmlElementText "$($item.parent.name) - $($item.name)" Add-XmlElement -xmlRoot $xmlItem -xmlElementName "type" -xmlElementText "Vnic" $vmUuid = ($item.parent | Get-View).config.instanceuuid $MemberMoref = "$vmUuid.$($item.id.substring($item.id.length-3))" Add-XmlElement -xmlRoot $xmlItem -xmlElementName "value" -xmlElementText $MemberMoref } else { #any other accepted PowerCLI object, we just need to grab details from the moref. Add-XmlElement -xmlRoot $xmlItem -xmlElementName "name" -xmlElementText $item.name Add-XmlElement -xmlRoot $xmlItem -xmlElementName "type" -xmlElementText $item.extensiondata.moref.type Add-XmlElement -xmlRoot $xmlItem -xmlElementName "value" -xmlElementText $item.extensiondata.moref.value } } $xmlReturn.appendChild($xmlItem) | Out-Null } if ( $ApplyToDFW ) { [System.XML.XMLElement]$xmlAppliedTo = $XMLDoc.CreateElement("appliedTo") $xmlReturn.appendChild($xmlAppliedTo) | Out-Null Add-XmlElement -xmlRoot $xmlAppliedTo -xmlElementName "name" -xmlElementText "DISTRIBUTED_FIREWALL" Add-XmlElement -xmlRoot $xmlAppliedTo -xmlElementName "type" -xmlElementText "DISTRIBUTED_FIREWALL" Add-XmlElement -xmlRoot $xmlAppliedTo -xmlElementName "value" -xmlElementText "DISTRIBUTED_FIREWALL" } if ( $ApplyToAllEdges ) { [System.XML.XMLElement]$xmlAppliedTo = $XMLDoc.CreateElement("appliedTo") $xmlReturn.appendChild($xmlAppliedTo) | Out-Null Add-XmlElement -xmlRoot $xmlAppliedTo -xmlElementName "name" -xmlElementText "ALL_EDGES" Add-XmlElement -xmlRoot $xmlAppliedTo -xmlElementName "type" -xmlElementText "ALL_EDGES" Add-XmlElement -xmlRoot $xmlAppliedTo -xmlElementName "value" -xmlElementText "ALL_EDGES" } $xmlReturn } ###End Private Functions function Get-NsxFirewallSection { <# .SYNOPSIS Retrieves the specified NSX Distributed Firewall Section. .DESCRIPTION An NSX Distributed Firewall Section is a named portion of the firewall rule set that contains firewall rules. This cmdlet retrieves the specified NSX Distributed Firewall Section. .EXAMPLE PS C:\> Get-NsxFirewallSection TestSection #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $false, ParameterSetName = "ObjectId")] [string]$objectId, [Parameter (Mandatory = $false)] [string]$scopeId = "globalroot-0", [Parameter (Mandatory = $false, Position = 1, ParameterSetName = "Name")] [string]$Name, [Parameter (Mandatory = $false)] [ValidateSet("layer3sections", "layer2sections", "layer3redirectsections", ignorecase = $false)] [string]$sectionType = "layer3sections", [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( -not $objectID ) { #All Sections $URI = "/api/4.0/firewall/$scopeID/config" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $return = $response.firewallConfiguration.$sectiontype.section if ($name) { $return | Where-Object { $_.name -eq $name } } else { $return } } else { $URI = "/api/4.0/firewall/$scopeID/config/$sectionType/$objectId" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $response.section } } end {} } function New-NsxFirewallSection { <# .SYNOPSIS Creates a new NSX Distributed Firewall Section. .DESCRIPTION An NSX Distributed Firewall Section is a named portion of the firewall rule set that contains firewall rules. This cmdlet create the specified NSX Distributed Firewall Section. By default this cmdlet creates a section at the top of the ruleset. It is possible to create the section at the top or bottom (before default) of the ruleset by using the position parameter. The position parameter can also be used to specify the section be created before or after an existing section. The existing section Id will need to be supplied as the anchor Id. .EXAMPLE PS> New-NsxFirewallSection -Name TestSection Creates a new Layer 3 firewall section at the top of the rulebase .EXAMPLE PS> New-NsxFirewallSection -Name TestL2Section -sectionType layer2sections Creates a new Layer 2 firewall section at the top of the rulebase. .EXAMPLE PS> New-NsxFirewallSection -Name TestL3RedirectSection -sectionType layer3redirectsections Creates a new Layer 3 redirect firewall section at the top of the rulebase. .EXAMPLE PS> New-NsxFirewallSection -Name TestAtBottom -position bottom Creates a new Layer 3 firewall section before the default section. .EXAMPLE PS> New-NsxFirewallSection -Name TestAtTop -position top Creates a new Layer 3 firewall section at the top of the rulebase. .EXAMPLE PS> New-NsxFirewallSection -Name TestBeforeExisting -position before -anchorId 1024 Creates a new Layer 3 firewall section before the existing section with an ID of 1024. .EXAMPLE PS> New-NsxFirewallSection -Name TestAfterExisting -position after -anchorId 1024 Creates a new Layer 3 firewall section after the existing section with an ID of 1024. .EXAMPLE PS> $section = Get-NsxFirewallSection blah PS> New-NsxFirewallSection -Name TestBeforeExisting -position before -anchorId $section.id Creates a new Layer 3 firewall section before the existing section named blah. #> [CmdletBinding()] param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] [ValidateSet("layer3sections", "layer2sections", "layer3redirectsections", ignorecase = $false)] [string]$sectionType = "layer3sections", [Parameter (Mandatory = $false)] [ValidateScript( { if ($_ -match "^globalroot-0$|^edge-\d+$") { $True } else { Throw "$_ is not a valid scope. Valid options are: globalroot-0 | edge-id" } })] [string]$scopeId = "globalroot-0", [Parameter (Mandatory = $false)] #Marks the firewall section to be universal or not [switch]$Universal, [Parameter (Mandatory = $false)] #Identifies where to insert the newly created section. after & before must specify an existing section id as the anchor. [ValidateSet("top", "bottom", "after", "before", ignorecase = $false)] [string]$position = "top", [Parameter (Mandatory = $False)] #ID of an existing section to use as an anchor for the new section. [ValidateNotNullOrEmpty()] [string]$anchorId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { $requiresAnchor = @("before", "after") if (( $requiresAnchor -contains $position ) -AND (-not ($PSBoundParameters.ContainsKey("anchorID")) ) ) { throw "An anchor ID must be supplied when specifying insert_before or insert_after as the operation" } if ( ($universal) -AND ($position -eq "bottom") ) { throw "Cannot specify -universal and -position bottom together. Instead specify -position after and the appropriate anchorId to add a universal section after the last universal section." } } process { #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("section") $xmlDoc.appendChild($xmlRoot) | Out-Null #Mandatory Fields Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name #Optional Fields if ($Universal) { #Create XML for universal object Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "managedBy" -xmlElementText "universalroot-0" } #Do the post $body = $xmlroot.OuterXml switch ($position) { { $requiresAnchor -contains $position } { $URI = "/api/4.0/firewall/$($scopeId.ToLower())/config/$sectionType`?operation=$(ConvertTo-NsxApiSectionOperation $position)`&anchorId=$anchorId" } "bottom" { $URI = "/api/4.0/firewall/$($scopeId.ToLower())/config/$sectionType`?operation=$(ConvertTo-NsxApiSectionOperation $position)" } default { $URI = "/api/4.0/firewall/$($scopeId.ToLower())/config/$sectionType" } } $response = Invoke-NsxRestMethod -method "post" -URI $URI -body $body -connection $connection $response.section } end {} } function Remove-NsxFirewallSection { <# .SYNOPSIS Removes the specified NSX Distributed Firewall Section. .DESCRIPTION An NSX Distributed Firewall Section is a named portion of the firewall rule set that contains firewall rules. This cmdlet removes the specified NSX Distributed Firewall Section. If the section contains rules, the removal attempt fails. Specify -force to override this, but be aware that all firewall rules contained within the section are removed along with it. .EXAMPLE PS C:\> New-NsxFirewallSection -Name TestSection #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateNotNull()] [System.Xml.XmlElement]$Section, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$force = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Firewall Section removal is permanent and cannot be reversed." $question = "Proceed with removal of Section $($Section.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { if ( $Section.Name -match 'Default Section' ) { Write-Warning "Will not delete $($Section.Name)." } else { #Changed to avoid need for traversal to parent XML node to determine section type which fails in some scenarios. switch ( $Section.Type) { "LAYER3" { $sectiontype = "layer3sections" } "LAYER2" { $Sectiontype = "layer2sections" } "L3REDIRECT" { $sectiontype = "layer3redirectsections" } } if ( $force ) { $URI = "/api/4.0/firewall/globalroot-0/config/$sectiontype/$($Section.Id)" } else { if ( $section | Get-Member -MemberType Properties -Name rule ) { throw "Section $($section.name) contains rules. Specify -force to delete this section" } else { $URI = "/api/4.0/firewall/globalroot-0/config/$sectiontype/$($Section.Id)" } } Write-Progress -Activity "Remove Section $($Section.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Section $($Section.Name)" -Completed } } } end {} } function Get-NsxFirewallRule { <# .SYNOPSIS Retrieves the specified NSX Distributed Firewall Rule. .DESCRIPTION An NSX Distributed Firewall Rule defines a typical 5 tuple rule and is enforced on each hypervisor at the point where the VMs NIC connects to the portgroup or logical switch. Additionally, the 'applied to' field allow additional flexibility about where (as in VMs, networks, hosts etc) the rule is actually applied. This cmdlet retrieves the specified NSX Distributed Firewall Rule. It is also effective used in conjunction with an NSX firewall section as returned by Get-NsxFirewallSection being passed on the pipeline to retrieve all the rules defined within the given section. .EXAMPLE PS C:\> Get-NsxFirewallSection TestSection | Get-NsxFirewallRule #> [CmdletBinding(DefaultParameterSetName = "Filter")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "Section")] [ValidateNotNull()] [System.Xml.XmlElement]$Section, [Parameter (Mandatory = $false, Position = 1, ParameterSetName = "Filter")] [Parameter (Mandatory = $false, Position = 1, ParameterSetName = "Section")] [ValidateNotNullorEmpty()] [string]$Name, [Parameter (Mandatory = $true, ParameterSetName = "RuleId")] [ValidateNotNullOrEmpty()] [string]$RuleId, [Parameter (Mandatory = $false)] [string]$ScopeId = "globalroot-0", [Parameter (Mandatory = $false, ParameterSetName = "Section")] [Parameter (Mandatory = $false, ParameterSetName = "RuleId")] [ValidateSet("layer3sections", "layer2sections", "layer3redirectsections", ignorecase = $false)] [string]$RuleType = "layer3sections", [Parameter (Mandatory = $False, ParameterSetName = "Filter")] [ValidateScript( { ValidateFwSourceDestFilter $_ })] [object]$Source, [Parameter (Mandatory = $False, ParameterSetName = "Filter")] [ValidateScript( { ValidateFwSourceDestFilter $_ })] [object]$Destination, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { if ( ( $PSCmdlet.ParameterSetName -eq "Section" ) -and $PSBoundParameters.ContainsKey('RuleType') ) { Write-Warning "The -RuleType parameter is no longer required (and will be ignored) when passing a section along the pipeline. This will be deprecated and removed in a future release." } } process { if ( $PSCmdlet.ParameterSetName -eq "Section" ) { $URI = "/api/4.0/firewall/$scopeID/config/$(ConvertTo-NsxApiSectionType $section.type)/$($Section.Id)" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $response | Get-Member -Name Section -MemberType Properties) { if ( $response.Section | Get-Member -Name Rule -MemberType Properties ) { if ( $PsBoundParameters.ContainsKey("Name") ) { $response.section.rule | Where-Object { $_.name -eq $Name } } else { $response.section.rule } } } } elseif ( $PSCmdlet.ParameterSetName -eq "Filter" ) { Switch ( $Source ) { { $_ -as [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop] } { $SourceString = $_.id -replace "virtualmachine-" } default { #either a vmmoid or ipaddress. $SourceString = $Source } } Switch ( $Destination ) { { $_ -as [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop] } { $DestinationString = $_.id -replace "virtualmachine-" } default { #either a vmmoid or ipaddress. $DestinationString = $Destination } } $URI = "/api/4.0/firewall/$ScopeId/config?ruleType=LAYER3&source=$SourceString&destination=$DestinationString&name=$Name" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( Invoke-XpathQuery -QueryMethod SelectSingleNode -query "descendant::filteredfirewallConfiguration/layer3Sections/section/rule" -Node $response ) { $response.filteredfirewallConfiguration.layer3Sections.Section.rule } } else { #SpecificRule - returned xml is firewallconfig -> layer3sections -> section. #In our infinite wisdom, we use a different string here for the section type :| #Kinda considering searching each section type here and returning result regardless of section #type if user specifies ruleid... The I dont have to make the user specify the ruletype... switch ($ruleType) { "layer3sections" { $URI = "/api/4.0/firewall/$scopeID/config?ruleType=LAYER3&ruleId=$RuleId" } "layer2sections" { $URI = "/api/4.0/firewall/$scopeID/config?ruleType=LAYER2&ruleId=$RuleId" } "layer3redirectsections" { $URI = "/api/4.0/firewall/$scopeID/config?ruleType=L3REDIRECT&ruleId=$RuleId" } default { throw "Invalid rule type" } } #NSX 6.2 introduced a change in the API wheras the element returned #for a query such as we are doing here is now called #'filteredfirewallConfiguration'. Why? :| $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( $response | Get-Member -Name firewallConfiguration -MemberType Properties ) { if ( $PsBoundParameters.ContainsKey("Name") ) { $response.firewallConfiguration.layer3Sections.Section.rule | Where-Object { $_.name -eq $Name } } else { $response.firewallConfiguration.layer3Sections.Section.rule } } elseif ( $response | Get-Member -Name filteredfirewallConfiguration -MemberType Properties ) { if ( $PsBoundParameters.ContainsKey("Name") ) { $response.filteredfirewallConfiguration.layer3Sections.Section.rule | Where-Object { $_.name -eq $Name } } else { $response.filteredfirewallConfiguration.layer3Sections.Section.rule } } else { throw "Invalid response from NSX API. $response" } } } end {} } function New-NsxFirewallRule { <# .SYNOPSIS Creates a new NSX Distributed Firewall Rule. .DESCRIPTION An NSX Distributed Firewall Rule defines a typical 5 tuple rule and is enforced on each hypervisor at the point where the VMs NIC connects to the portgroup or logical switch. Additionally, the 'applied to' field allows flexibility about where (as in VMs, networks, hosts etc) the rule is actually applied. This cmdlet creates the specified NSX Distributed Firewall Rule. The section in which to create the rule is mandatory. .EXAMPLE PS> Get-NsxFirewallSection TestSection | New-NsxFirewallRule -Name TestRule -Source $LS1 -Destination $LS1 -Action allow -service (Get-NsxService HTTP) -AppliedTo $LS1 -EnableLogging -Comment "Testing Rule Creation" Add a new Layer 3 rule to the section called TestSection. By default, the rule will be inserted at the top of the section. .EXAMPLE PS> Get-NsxFirewallSection TestL2Section | New-NsxFirewallRule -Name TestRule -Source $VM1 -Destination $VM1 -Action allow -AppliedTo $VM1 -EnableLogging -Comment "Testing L2 Rule Creation" Add a new Layer 2 rule to the section called TestL2Section. By default, the rule will be inserted at the top of the section. .EXAMPLE PS> Get-NsxFirewallSection TestSection | New-NsxFirewallRule -Name TestRule -Source $LS1 -Destination $LS1 -Action allow -service (Get-NsxService HTTP) -AppliedTo $LS1 -EnableLogging -Comment "Testing creating a disabled rule" -DisableRule Add a new Layer 3 disabled rule to the section called TestSection .EXAMPLE PS> Get-NsxFirewallSection TestSection | New-NsxFirewallRule -Name TestRule -Source $LS1 -Destination $LS1 -Action allow -service (Get-NsxService HTTP) -AppliedTo $LS1 -EnableLogging -Comment "Testing creating a rule at the bottom of the section" -Position bottom Add a new Layer 3 rule to the bottom of the section called TestSection .EXAMPLE PS> Get-NsxFirewallSection TestSection | New-NsxFirewallRule -Name TestRule -Source $LS1 -Destination $LS1 -Action allow -service (Get-NsxService HTTP) -AppliedTo $LS1 -EnableLogging -Comment "Testing creating a rule before an existing rule" -Position before -anchorId 1024 Add a new Layer 3 rule immediatley before rule id 1024 in the section called TestSection .EXAMPLE PS> Get-NsxFirewallSection TestSection | New-NsxFirewallRule -Name TestRule -Source $LS1 -Destination $LS1 -Action allow -service (Get-NsxService HTTP) -AppliedTo $LS1 -EnableLogging -Comment "Testing creating a rule after an existing rule" -Position after -anchorId 1024 Add a new Layer 3 rule immediatley after rule id 1024 in the section called TestSection #> [CmdletBinding()] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "Section")] # Section in which the new rule should be created [ValidateNotNull()] [System.Xml.XmlElement]$Section, [Parameter (Mandatory = $true)] # Name of the new rule [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true)] # Action of the rule - allow, deny or reject. [ValidateSet("allow", "deny", "reject")] [string]$Action, [Parameter (Mandatory = $false)] # Direction of traffic to hit the rule - in, out or inout (Default inout) [ValidateSet("inout", "in", "out")] [string]$Direction = "inout", [Parameter (Mandatory = $false)] # Source(s) of traffic to hit the rule. IP4/6 members are specified as string, any other member as the appropriate VI or PowerNSX object. [ValidateScript( { ValidateFirewallRuleSourceDest $_ })] [object[]]$Source, [Parameter (Mandatory = $false)] # Negate the list of sources hit by the rule [ValidateNotNullOrEmpty()] [switch]$NegateSource, [Parameter (Mandatory = $false)] # Destination(s) of traffic to hit the rule. IP4/6 members are specified as string, any other member as the appropriate VI or PowerNSX object. [ValidateScript( { ValidateFirewallRuleSourceDest $_ })] [object[]]$Destination, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [switch]$NegateDestination, [Parameter (Mandatory = $false)] # Negate the list of destinations hit by the rule [ValidateScript ( { ValidateFirewallRuleService $_ })] [object[]]$Service, [Parameter (Mandatory = $false)] # Comment string for the new rule [string]$Comment = "", [Parameter (Mandatory = $false)] # Rule is created as disabled [switch]$Disabled, [Parameter (Mandatory = $false)] # Rule logging is enabled [switch]$EnableLogging, [Parameter (Mandatory = $false)] # Specific Object(s) to which the rule will be applied. [ValidateScript( { ValidateFirewallAppliedTo $_ })] [object[]]$AppliedTo, [Parameter (Mandatory = $false)] # Enable application of the rule to 'DISTRIBUTED_FIREWALL' (ie, to all VNICs present on NSX prepared hypervisors. This does NOT include NSX Edges) [switch]$ApplyToDfw = $true, [Parameter (Mandatory = $false)] # Enable application of the rule to all NSX edges [switch]$ApplyToAllEdges = $false, [Parameter (Mandatory = $false)] # Rule type [ValidateSet("layer3sections", "layer2sections", "layer3redirectsections", ignorecase = $false)] [string]$RuleType = "layer3sections", [Parameter (Mandatory = $false)] # Create the new rule at the specified position of the section (Top or Bottom, Default - Top) [ValidateSet("Top", "Bottom", "before", "after")] [string]$Position = "Top", [Parameter (Mandatory = $False)] #ID of an existing rule to use as an anchor for the new rule. [ValidateNotNullOrEmpty()] [string]$anchorId, [Parameter (Mandatory = $false)] # Tag to be configured on the new rule. Tag is an arbitrary string attached to the rule that does not affect application of the rule, but is included in logged output of rule hits if logging is enabled for the rule. [ValidateNotNullorEmpty()] [string]$Tag, [Parameter (Mandatory = $false)] # Scope of the created rule. [string]$ScopeId = "globalroot-0", [Parameter (Mandatory = $false)] # Specifies that New-NsxFirewall rule will return the actual rule that was created rather than the deprecated behaviour of returning the complete containing section # This option exists to allow existing scripts that use this function to be easily updated to set it to $false and continue working (For now!). # This option is deprecated and will be removed in a future version. [switch]$ReturnRule = $true, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { $requiresAnchor = @("before", "after") if (( $requiresAnchor -contains $position ) -AND (-not ($PSBoundParameters.ContainsKey("anchorID")) ) ) { throw "An anchor ID must be supplied when specifying before or after as the operation" } } process { # Check to see if the section that has been passed along the pipeline contains # a "default rule". A default rule in a section is typically the last rule in # the section and has a node/element of default # and if you try to add a rule below this default rule, the API responds with # a criptic errror msg which can only be decrypted if your a part of the Goa'uld if ( ($position -eq "bottom") -AND (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $Section -query "child::rule[precedence=`"default`"][last()]") ) { throw "Cannot insert rule at the bottom of the section $($section.id) ($($section.name)) as the last rule is a system defined default rule" } $generationNumber = $section.generationNumber Write-Debug "$($MyInvocation.MyCommand.Name) : Preparing rule for section $($section.Name) with generationId $generationNumber" #Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRule = $XMLDoc.CreateElement("rule") $xmlDoc.appendChild($xmlRule) | Out-Null Add-XmlElement -xmlRoot $xmlRule -xmlElementName "name" -xmlElementText $Name #Add-XmlElement -xmlRoot $xmlRule -xmlElementName "sectionId" -xmlElementText $($section.Id) Add-XmlElement -xmlRoot $xmlRule -xmlElementName "notes" -xmlElementText $Comment Add-XmlElement -xmlRoot $xmlRule -xmlElementName "action" -xmlElementText $action Add-XmlElement -xmlRoot $xmlRule -xmlElementName "direction" -xmlElementText $Direction if ( $EnableLogging ) { #Enable Logging attribute $xmlAttrLog = $xmlDoc.createAttribute("logged") $xmlAttrLog.value = "true" $xmlRule.Attributes.Append($xmlAttrLog) | Out-Null } if ( $Disabled ) { #Disable (rule) attribute $xmlAttrDisabled = $xmlDoc.createAttribute("disabled") $xmlAttrDisabled.value = "true" $xmlRule.Attributes.Append($xmlAttrDisabled) | Out-Null } #Build Sources Node if ( $source ) { Add-NsxSourceDestNode -Rule $xmlRule -Nodetype "sources" -negated:$NegateSource #Add the source members Add-NsxSourceDestMember -membertype "source" -memberlist $source -rule $xmlRule } #Destinations Node if ( $destination ) { Add-NsxSourceDestNode -Rule $xmlRule -Nodetype "destinations" -negated:$NegateDestination #Add the destination members Add-NsxSourceDestMember -membertype "destination" -memberlist $destination -rule $xmlRule } #Services if ( $service ) { $xmlservices = New-NsxServiceNode -itemType "service" -itemlist $service -xmlDoc $xmlDoc $xmlRule.appendChild($xmlservices) | Out-Null } #Applied To if ( -not $PsBoundParameters.ContainsKey('AppliedTo')) { $xmlAppliedToList = New-NsxAppliedToListNode -xmlDoc $xmlDoc -ApplyToDFW:$ApplyToDfw -ApplyToAllEdges:$ApplyToAllEdges } else { $xmlAppliedToList = New-NsxAppliedToListNode -itemlist $AppliedTo -xmlDoc $xmlDoc -ApplyToDFW:$ApplyToDfw -ApplyToAllEdges:$ApplyToAllEdges } $xmlRule.appendChild($xmlAppliedToList) | Out-Null #Tag if ( $tag ) { Add-XmlElement -xmlRoot $xmlRule -xmlElementName "tag" -xmlElementText $tag } #GetThe existing rule Ids and store them - we check for a rule that isnt contained here in the response so we can presnet back to user with rule id if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $Section -query "child::rule") ) { $ExistingIds = @($Section.rule.id) } else { $ExistingIds = @() } #Append the new rule to the section $xmlrule = $Section.ownerDocument.ImportNode($xmlRule, $true) switch ($Position) { "Top" { $Section.prependchild($xmlRule) | Out-Null } "Bottom" { $Section.appendchild($xmlRule) | Out-Null } { ($_ -eq "before") -or ($_ -eq "after") } { $anchorRule = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $Section -query "child::rule[@id=`"$anchorId`"]" if (-not ($anchorRule)) { throw "Anchor rule id $anchorId does not exist in section $($section.id) ($($section.name))" } else { switch ($Position) { "before" { $section.insertBefore($xmlrule, $anchorRule) | Out-Null } "after" { $section.insertAfter($xmlrule, $anchorRule) | Out-Null } } } } } #Do the post $body = $Section.OuterXml $URI = "/api/4.0/firewall/$scopeId/config/$(ConvertTo-NsxApiSectionType $section.type)/$($section.Id)" #Need the IfMatch header to specify the current section generation id $IfMatchHeader = @{"If-Match" = $generationNumber } $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -extraheader $IfMatchHeader -connection $connection try { [system.xml.xmldocument]$content = $response.content } catch { throw "API call to NSX was successful, but was unable to interpret NSX API response as xml." } if ( $ReturnRule ) { $content.section.rule | Where-Object { ( -not ($ExistingIds.Contains($_.id))) } } else { $content.section Write-Warning 'The -ReturnRule:$false option is deprecated and will be removed in a future version. Please update your scripts so that they accept the return object of New-NsxFirewallRule to be the newly created rule rather than the full section.' } } end {} } function Set-NsxFirewallRule { <# .SYNOPSIS Set configuration for a NSX Distributed Firewall Rule. .DESCRIPTION An NSX Distributed Firewall Rule defines a typical 5 tuple rule and is enforced on each hypervisor at the point where the VMs NIC connects to the portgroup or logical switch. This cmdlet accepts a firewall rule object returned from Get-NsxFirewallRule and set configuration (disabled, name, action...) .EXAMPLE Get-NsxFirewallRule -Ruleid 1007 | Set-NsxFirewallRule -disabled:$true Disabled the RuleId 1007 .EXAMPLE Get-NsxFirewallRule -Ruleid 1007 | Set-NsxFirewallRule -logged:$true Enable logging on the RuleId 1007 .EXAMPLE Get-NsxFirewallRule -Ruleid 1007 | Set-NsxFirewallRule -name "My Distributed Firewall Rule" Set/Update the description of the RuleId 1007 .EXAMPLE Get-NsxFirewallRule -Ruleid 1007 | Set-NsxFirewallRule -action deny Change action to deny to RuleId 1007 .EXAMPLE Get-NsxFirewallRule -Ruleid 1007 | Set-NsxFirewallRule -comment "My Comment" Set/update the comment of the RuleId 1007 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] # DFW rule as returned by Get-NsxFirewallRule / New-NsxFirewallRule [ValidateScript( { ValidateFirewallRule $_ })] [System.Xml.XmlElement]$FirewallRule, [Parameter (Mandatory = $false)] [boolean]$disabled, [Parameter (Mandatory = $false)] [boolean]$logged, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$name, [Parameter (Mandatory = $false)] [ValidateSet("Allow", "Deny", "Reject")] [string]$action, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$comment, [Parameter (Mandatory = $false)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $sectionId = $FirewallRule.ParentNode.Id $RuleId = $FirewallRule.id $generationNumber = $FirewallRule.ParentNode.generationnumber #Clone the xml so we dont modify source... $_FirewallRule = $FirewallRule.CloneNode($true) if ( $PsBoundParameters.ContainsKey('disabled') ) { $_FirewallRule.disabled = $disabled.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('logged') ) { $_FirewallRule.logged = $logged.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey('name') ) { $_FirewallRule.name = $name } if ( $PsBoundParameters.ContainsKey('action') ) { $_FirewallRule.action = $action } if ( $PsBoundParameters.ContainsKey('comment') ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_FirewallRule -query 'descendant::notes')) { $_FirewallRule.notes = $comment.ToString() } else { Add-XmlElement -xmlRoot $_FirewallRule -xmlElementName "notes" -xmlElementText $comment.ToString() } } $uri = "/api/4.0/firewall/globalroot-0/config/layer3sections/$sectionId/rules/$Ruleid" #Need the IfMatch header to specify the current section generation id $IfMatchHeader = @{"If-Match" = $generationNumber } try { $response = Invoke-NsxWebRequest -method put -URI $uri -body $_FirewallRule.OuterXml -extraheader $IfMatchHeader -connection $connection [xml]$ruleElem = $response.Content Get-NsxFirewallRule -RuleId $ruleElem.rule.id } catch { throw "Failed to modify the specified rule. $_" } } end {} } function Remove-NsxFirewallRule { <# .SYNOPSIS Removes the specified NSX Distributed Firewall Rule. .DESCRIPTION An NSX Distributed Firewall Rule defines a typical 5 tuple rule and is enforced on each hypervisor at the point where the VMs NIC connects to the portgroup or logical switch. This cmdlet removes the specified NSX Distributed Firewall Rule. .EXAMPLE PS C:\> Get-NsxFirewallRule -RuleId 1144 | Remove-NsxFirewallRule -confirm:$false #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateNotNull()] [System.Xml.XmlElement]$Rule, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] [switch]$force = $false, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( $confirm ) { $message = "Firewall Rule removal is permanent and cannot be reversed." $question = "Proceed with removal of Rule $($Rule.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $section = Get-NsxFirewallSection $Rule.parentnode.name -Connection $connection $generationNumber = $section.generationNumber $IfMatchHeader = @{"If-Match" = $generationNumber } $URI = "/api/4.0/firewall/globalroot-0/config/$($Section.ParentNode.name.tolower())/$($Section.Id)/rules/$($Rule.id)" Write-Progress -Activity "Remove Rule $($Rule.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -extraheader $IfMatchHeader -connection $connection Write-Progress -Activity "Remove Rule $($Rule.Name)" -Completed } } end {} } function Get-NsxFirewallExclusionListMember { <# .SYNOPSIS Gets the virtual machines that are excluded from the distributed firewall .DESCRIPTION The 'Exclusion List' is a list of virtual machines which are excluded from the distributed firewall rules. They are not protected and/or limited by it. If a virtual machine has multiple vNICs, all of them are excluded from protection. VMware recommends that you place the following service virtual machines in the Exclusion List * vCenter Server. * Partner service virtual machines. * Virtual machines that require promiscuous mode. This cmdlet retrieves all VMs on the exclusion list and returns PowerCLI VM objects. .EXAMPLE Get-NsxFirewallExclusionListMember Retrieves the entire contents of the exclusion list .EXAMPLE Get-NsxFirewallExclusionListMember | where-object { $_.name -match 'myvm'} Retrieves a specific vm from the exclusion list if it exists. #> param ( [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { # Build URL and catch response into XML format $URI = "/api/2.1/app/excludelist" [System.Xml.XmlDocument]$response = Invoke-NsxRestMethod -method "GET" -URI $URI -connection $Connection # If there are any VMs found, iterate and return them #Martijn - I removed the array build here, as: #### a) I preferred to just output VM objects so that the get- | remove- pipline works #### b) outputting the VM obj immediately works nicer in a pipeline (object appears immediately) #### as opposed to building the array internally where the whole pipeline has to be processed before the user gets any output. #### c) Its also less lines :) $nodes = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $response -query 'descendant::VshieldAppConfiguration/excludeListConfiguration/excludeMember') if ($nodes) { foreach ($node in $nodes) { # output the VI VM object... Get-VM -Server $Connection.VIConnection -Id "VirtualMachine-$($node.member.objectId)" } } } end {} } function Add-NsxFirewallExclusionListMember { <# .SYNOPSIS Adds a virtual machine to the exclusion list, which are excluded from the distributed firewall .DESCRIPTION The 'Exclusion List' is a list of virtual machines which are excluded from the distributed firewall rules. They are not protected and/or limited by it. If a virtual machine has multiple vNICs, all of them are excluded from protection. VMware recommends that you place the following service virtual machines in the Exclusion List * vCenter Server. * Partner service virtual machines. * Virtual machines that require promiscuous mode. This cmdlet adds a VM to the exclusion list .EXAMPLE Add-NsxFirewallExclusionListMember -VirtualMachine (Get-VM -Name myVM) Adds the VM myVM to the exclusion list .EXAMPLE Get-VM | where-object { $_.name -match 'mgt'} | Add-NsxFirewallExclusionListMember Adds all VMs with mgt in their name to the exclusion list. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop]$VirtualMachine, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { # Get VM MOID $vmMoid = $VirtualMachine.ExtensionData.MoRef.Value # Build URL $URI = "/api/2.1/app/excludelist/$vmMoid" try { $null = Invoke-NsxRestMethod -method "PUT" -URI $URI -connection $connection } catch { Throw "Unable to add VM $VirtualMachine to Exclusion list. $_" } } end {} } function Remove-NsxFirewallExclusionListMember { <# .SYNOPSIS Removes a virtual machine from the exclusion list, which are excluded from the distributed firewall .DESCRIPTION The 'Exclusion List' is a list of virtual machines which are excluded from the distributed firewall rules. They are not protected and/or limited by it. If a virtual machine has multiple vNICs, all of them are excluded from protection. VMware recommends that you place the following service virtual machines in the Exclusion List * vCenter Server. * Partner service virtual machines. * Virtual machines that require promiscuous mode. This cmdlet removes a VM to the exclusion list .EXAMPLE Remove-NsxFirewallExclusionListMember -VirtualMachine (Get-VM -Name myVM) Removes the VM myVM from the exclusion list .EXAMPLE Get-NsxFirewallExclusionListMember | Remove-NsxFirewallExclusionlistMember Removes all vms from the exclusion list. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop]$VirtualMachine, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { # Get VM MOID $vmMoid = $VirtualMachine.ExtensionData.MoRef.Value # Build URL $URI = "/api/2.1/app/excludelist/$vmMoid" try { $null = Invoke-NsxWebRequest -method "DELETE" -URI $URI -connection $connection } catch { Throw "Unable to remove VM $VirtualMachine from Exclusion list. $_" } } end {} } function Get-NsxFirewallSavedConfiguration { <# .SYNOPSIS Retrieves saved Distributed Firewall configuration. .DESCRIPTION Retireves saved Distributed Firewall configuration. A copy of every published configuration is also saved as a draft. A maximum of 100 configurations can be saved at a time. 90 out of these 100 can be auto saved configurations from a publish operation. When the limit is reached,the oldest configuration that is not marked for preserve is purged to make way for a new one. .EXAMPLE Get-NsxFirewallSavedConfiguration Retrieves all saved Distributed Firewall configurations .EXAMPLE Get-NsxFirewallSavedConfiguration TestBackup Retrieves all Distributed Firewall configurations with the name specified .EXAMPLE Get-NsxFirewallSavedConfiguration -Name TestBackup Retrieves all Distributed Firewall configurations with the name specified .EXAMPLE Get-NsxFirewallSavedConfiguration -ObjectId 403 Retrieves a Distributed Firewall configuration by ObjectId #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $false, ParameterSetName = "ObjectId")] # ID of a saved Distributed Firewall Configuration [ValidateNotNullOrEmpty()] [string]$ObjectId, [Parameter (Mandatory = $false, Position = 1, ParameterSetName = "Name")] # Name of a saved Distributed Firewall Configuration [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $False)] # PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { if ( -not ($PsBoundParameters.ContainsKey("ObjectId"))) { # All Sections $URI = "/api/4.0/firewall/globalroot-0/drafts" [system.xml.xmldocument]$Response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query "child::firewallDrafts/*")) { $Return = $Response if ($PsBoundParameters.ContainsKey("Name")) { $namedResults = $Return.firewallDrafts.firewallDraft | Where-Object { $_.name -eq $Name } foreach ($config in $namedResults) { Get-NsxFirewallSavedConfiguration -ObjectId $config.id -Connection $connection } } else { $Return.firewallDrafts.firewallDraft } } } else { $URI = "/api/4.0/firewall/globalroot-0/drafts/$ObjectId" [system.xml.xmldocument]$Response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $Response -query "child::firewallDraft")) { $Response.firewallDraft } } } end {} } function New-NsxFirewallSavedConfiguration { <# .SYNOPSIS Creates a manually saved Distributed Firewall configuration. .DESCRIPTION Creates a manually saved Distributed Firewall configuration. A copy of every published configuration is automatically saved as a draft. A maximum of 100 configurations can be saved at a time. 90 out of these 100 can be auto saved configurations from a publish operation. When the limit is reached,the oldest configuration that is not marked for preserve is purged to make way for a new one. .EXAMPLE New-NsxFirewallSavedConfiguration -Name Change123 -Description "Backup taken prior to change 123" Creates a saved Distributed Firewall Configuration .EXAMPLE New-NsxFirewallSavedConfiguration -Name Change123 -Description "Backup taken prior to change 123" -Preserve:$false Creates a saved Distributed Firewall Configuration and does not set the preserve flag. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, Position = 1)] # Name to call the saved configuration [string]$Name, [Parameter (Mandatory = $false)] # A meaningful description for the saved configuration [string]$Description, [Parameter (Mandatory = $false)] # Specifies whether to preserve the saved configuration to prevent it being deleted automatically. Default = $True [switch]$Preserve = $true, [Parameter (Mandatory = $False)] # PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { # Create the XMLRoot [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlRoot = $XMLDoc.CreateElement("firewallDraft") # Set the name attribute $xmlDoc.appendChild($xmlRoot) | Out-Null $xmlAttrName = $xmlDoc.createAttribute("name") $xmlAttrName.value = $Name $xmlRoot.Attributes.Append($xmlAttrName) | Out-Null Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "preserve" -xmlElementText $Preserve Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "mode" -xmlElementText "userdefined" if ( $PsBoundParameters.ContainsKey("Description") ) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText $Description } else { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" } #Go and grab the complete firewall configuration to backup $URI = "/api/4.0/firewall/globalroot-0/config" [system.xml.xmlDocument]$config = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if (-not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $config -query "child::firewallConfiguration")) { throw "Cannot retrieve complete Distributed Firewall configuration." } [System.XML.XMLElement]$xmlConfigNode = $xmlRoot.OwnerDocument.CreateElement("config") $xmlRoot.AppendChild($xmlConfigNode) | Out-Null foreach ($node in $config.firewallConfiguration.ChildNodes) { $xmlConfigBackup = $xmlroot.OwnerDocument.ImportNode($node, $true) $xmlConfigNode.AppendChild($xmlConfigBackup) | Out-Null } $body = $xmlroot.OuterXml $URI = "/api/4.0/firewall/globalroot-0/drafts" Write-Progress -Activity "Creating firewall saved configuration." $response = Invoke-NsxRestMethod -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Creating firewall saved configuration." -Completed Get-NsxFirewallSavedConfiguration -ObjectId $response.firewalldraft.id -Connection $connection } end {} } function Remove-NsxFirewallSavedConfiguration { <# .SYNOPSIS Removes a saved Distributed Firewall configuration. .DESCRIPTION The Remove-NsxFirewallSavedConfiguration will remove a given saved configuration. A maximum of 100 configurations can be saved at a time. 90 out of these 100 can be auto saved configurations from a publish operation. When the limit is reached,the oldest configuration that is not marked for preserve is purged to make way for a new one. .EXAMPLE Get-NsxFirewallSavedConfiguration -Name Change123 | Remove-NsxFirewallSavedConfiguration Remove all saved Distributed Firewall Configurations named Change123 .EXAMPLE Get-NsxFirewallSavedConfiguration -Name Change123 | Remove-NsxFirewallSavedConfiguration -Confirm:$false Remove all saved Distributed Firewall Configurations named Change123 without prompting for confirmation .EXAMPLE Get-NsxFirewallSavedConfiguration -ObjectId 3278 | Remove-NsxFirewallSavedConfiguration Remove saved Distributed Firewall Configuration with the ID 3278 #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] # A valid saved configuration from Get-NsxFirewallSavedConfiguration [ValidateScript( { ValidateFirewallDraft $_ })] [System.Xml.XmlElement]$SavedConfig, [Parameter (Mandatory = $False)] # Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ( $confirm ) { $message = "Removal of a saved Distributed Firewall Configuration is permanent." $question = "Proceed with removal of Saved Distributed Firewall Configuration ($($SavedConfig.id) - $($SavedConfig.Name))?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/4.0/firewall/globalroot-0/drafts/$($SavedConfig.id)" Write-Progress -Activity "Remove Saved Distributed Firewall Configuration $($SavedConfig.id) - $($SavedConfig.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Saved Distributed Firewall Configuration $($SavedConfig.id) - $($SavedConfig.Name)" -Completed } } end {} } function Set-NsxFirewallSavedConfiguration { <# .SYNOPSIS Update a saved Distributed Firewall configuration. .DESCRIPTION Update a saved Distributed Firewall configuration. A copy of every published configuration is automatically saved as a draft. A maximum of 100 configurations can be saved at a time. 90 out of these 100 can be auto saved configurations from a publish operation. When the limit is reached,the oldest configuration that is not marked for preserve is purged to make way for a new one. .EXAMPLE Get-NsxFirewallSavedConfiguration -Name Change123 | Set-NsxFirewallSavedConfiguration -Preserve Set the Preserve flag to true on an existing saved Distributed Firewall Configuration .EXAMPLE Get-NsxFirewallSavedConfiguration -Name Change123 | Set-NsxFirewallSavedConfiguration -Name Change123NewName -Description "Now With Description" Update the name and description on an existing saved Distributed Firewall Configuration .EXAMPLE Get-NsxFirewallSavedConfiguration -Name Change123 | Set-NsxFirewallSavedConfiguration -Preserve Set the Preserve flag to true on an existing saved Distributed Firewall Configuration #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] # A valid saved configuration from Get-NsxFirewallSavedConfiguration [ValidateScript( { ValidateFirewallSavedConfiguration $_ })] [System.Xml.XmlElement]$SavedConfig, [Parameter (Mandatory = $False)] # Specifies whether to preserve the saved configuration to prevent it being deleted automatically. Default = $True [switch]$Preserve, [Parameter (Mandatory = $False)] # Name to call the saved configuration [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $False)] # A meaningful description for the saved configuration [string]$Description, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Create private xml element $_SavedConfig = $SavedConfig.CloneNode($true) if ( $PsBoundParameters.ContainsKey('Name') ) { $_SavedConfig.Name = $Name } if ( $PsBoundParameters.ContainsKey('Description') ) { $_SavedConfig.Description = $Description } if ( $PsBoundParameters.ContainsKey('Preserve') ) { $_SavedConfig.preserve = [string]$preserve } $URI = "/api/4.0/firewall/globalroot-0/drafts/$($SavedConfig.id)" $body = $_SavedConfig.OuterXml $null = Invoke-NsxRestMethod -method "put" -URI $URI -body $body -connection $connection Get-NsxFirewallSavedConfiguration -ObjectId $SavedConfig.id -Connection $connection } end {} } 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 {} process { #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. # Must convert all ToString due to Windows issues. macOS and Linux are fine without. if ( $PsBoundParameters.ContainsKey('Cpu') ) { $currentthreshold.cpu.percentValue = $Cpu.ToString() } if ( $PsBoundParameters.ContainsKey('Memory') ) { $currentthreshold.memory.percentValue = $Memory.ToString() } if ( $PsBoundParameters.ContainsKey('ConnectionsPerSecond') ) { $currentthreshold.connectionsPerSecond.value = $ConnectionsPerSecond.ToString() } $uri = "/api/4.0/firewall/stats/eventthresholds" $body = $currentthreshold.outerXml Invoke-NsxWebRequest -method "PUT" -URI $uri -body $body | Out-Null Get-NsxFirewallThreshold } end {} } function Get-NsxFirewallRuleMember { <# .SYNOPSIS Retrieves the specified members from specified NSX Distributed Firewall Rule. .DESCRIPTION An NSX Distributed Firewall Rule defines a typical 5 tuple rule and is enforced on each hypervisor at the point where the VMs NIC connects to the portgroup or logical switch. This cmdlet accepts a firewall rule object returned from Get-NsxFirewallRule and returns the specified source and/or destination members of the rule. Its primary use is to provide a source object for the Remove-NsxFirewallRuleMember cmdlet. .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Get-NsxFirewallRuleMember | format-table RuleId SectionId MemberType Name Value Type isValid ------ --------- ---------- ---- ----- ---- ------- 5441 3717 Source 1.2.3.4 Ipv4Address true 5441 3717 Destination test ipset-309 IPSet true 5441 3717 Destination Web02 vm-1266 VirtualMachine true 5441 3717 Destination 1.2.3.4 Ipv4Address true Get all members from rule id 5441 and format output as table. .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Get-NsxFirewallRuleMember -MemberType Source -Member 1.2.3.4 RuleId : 5441 SectionId : 3717 MemberType : Source Name : Value : 1.2.3.4 Type : Ipv4Address isValid : true Get just the source member 1.2.3.4 from rule id 5441 .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Get-NsxFirewallRuleMember -Member 1.2.3.4 | Format-Table RuleId SectionId MemberType Name Value Type isValid ------ --------- ---------- ---- ----- ---- ------- 5441 3717 Source 1.2.3.4 Ipv4Address true 5441 3717 Destination 1.2.3.4 Ipv4Address true Get member 1.2.3.4 in either source or destination of rule 5441. Matching by string .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Get-NsxFirewallRuleMember -Member web\d+ | Format-Table RuleId SectionId MemberType Name Value Type isValid ------ --------- ---------- ---- ----- ---- ------- 5441 3717 Source Web01 vm-1270 VirtualMachine true 5441 3717 Destination Web02 vm-1266 VirtualMachine true Get any member of rule 5441 with a name matching the regular expression web\d+ (the string web followed by 1 or more digit) .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Get-NsxFirewallRuleMember -Member (get-vm web01) | Format-Table RuleId SectionId MemberType Name Value Type isValid ------ --------- ---------- ---- ----- ---- ------- 5441 3717 Source Web01 vm-1270 VirtualMachine true Get any member of rule 5441 that is the VM web01. Matching by PowerCLI object .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Get-NsxFirewallRuleMember -Member (get-nsxipset test) | Format-Table RuleId SectionId MemberType Name Value Type isValid ------ --------- ---------- ---- ----- ---- ------- 5441 3717 Destination test ipset-309 IPSet true Get any member of the rule 5441 that is the NSX IPSet called test. Matching by PowerNSX object .EXAMPLE get-nsxfirewallrule | Get-NsxFirewallRuleMember -Member (get-nsxipset test) | Format-Table RuleId SectionId MemberType Name Value Type isValid ------ --------- ---------- ---- ----- ---- ------- 5441 3717 Destination test ipset-309 IPSet true Get any member of the rule 5441 that is the NSX IPSet called test. Matching by PowerNSX object .EXAMPLE get-nsxfirewallrule | Get-NsxFirewallRuleMember -Member (get-vm web01) | Format-Table RuleId SectionId MemberType Name Value Type isValid ------ --------- ---------- ---- ----- ---- ------- 5441 3717 Source Web01 vm-1270 VirtualMachine true 4332 3717 Source Web01 vm-1270 VirtualMachine true Get any member of any rule that is the VM object web01. Matching accross all rules by PowerCLI object #> [CmdletBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] # DFW rule as returned by Get-NsxFirewallRule / New-NsxFirewallRule [ValidateScript( { ValidateFirewallRule $_ })] [System.Xml.XmlElement]$FirewallRule, [Parameter (Mandatory = $false, Position = 1)] # Member(s) to return. Can specify as a string or VI / NSX Object (VM, Logical Switch etc)). String match is processed as regex (eg: web\d{2} is supported) [ValidateScript( { ValidateFirewallRuleMember $_ })] [object[]]$Member = ".*", [Parameter (Mandatory = $false)] # MemberType to return. Source, Destination or All (Default) [ValidateSet("Source", "Destination", "All")] [string]$MemberType = "All" ) begin {} process { Write-Debug "$($MyInvocation.MyCommand.Name) : Rule $($FirewallRule.id)" foreach ( $_Member in $Member ) { if ( $_Member -is [string] ) { if ( $Membertype -match "Source|All" ) { if ( Invoke-XpathQuery -Node $FirewallRule -query "child::sources/source" -QueryMethod SelectSingleNode ) { foreach ($source in $FirewallRule.Sources.Source ) { #check member type - ipv4/6 addresses dont have a 'name' property, but user will expect operation against 'value' property. Sigh... why is our API so f*@#$&n inconsistent... if ( $source.type -match "ipv4Address|ipv6Address" ) { if ( $source.value -match $_Member ) { [pscustomobject]@{"RuleId" = $FirewallRule.id; "SectionId" = $FirewallRule.SectionId; "MemberType" = "Source"; "Name" = $null; "Value" = $source.Value; "Type" = $source.Type; "isValid" = $source.isValid } } } elseif ( $source.name -match $_Member ) { [pscustomobject]@{"RuleId" = $FirewallRule.id; "SectionId" = $FirewallRule.SectionId; "MemberType" = "Source"; "Name" = $source.Name; "Value" = $source.Value; "Type" = $source.Type; "isValid" = $source.isValid } } } } } if ( $Membertype -match "Destination|All" ) { if ( Invoke-XpathQuery -Node $FirewallRule -query "child::destinations/destination" -QueryMethod SelectSingleNode ) { foreach ($destination in $FirewallRule.Destinations.Destination ) { #check member type - ipv4/6 addresses dont have a 'name' property, but user will expect operation against 'value' property. if ( $destination.type -match "ipv4Address|ipv6Address" ) { if ( $destination.value -match $_Member ) { [pscustomobject]@{"RuleId" = $FirewallRule.id; "SectionId" = $FirewallRule.SectionId; "MemberType" = "Destination"; "Name" = $null; "Value" = $destination.Value; "Type" = $destination.Type; "isValid" = $destination.isValid } } } elseif ( $Destination.name -match $_Member ) { [pscustomobject]@{"RuleId" = $FirewallRule.id; "SectionId" = $FirewallRule.SectionId; "MemberType" = "Destination"; "Name" = $destination.Name; "Value" = $destination.Value; "Type" = $destination.Type; "isValid" = $destination.isValid } } } } } } elseif ( $_Member -is [system.xml.xmlelement] ) { #XML representation of NSX object passed - ipset, sec group or logical switch. match on value (objectId) if ( $Membertype -match "Source|All" ) { if ( Invoke-XpathQuery -Node $FirewallRule -query "child::sources/source" -QueryMethod SelectSingleNode ) { foreach ($source in $FirewallRule.Sources.Source ) { #ignore any ip4/6 rules - user can only match them on string based search so wont hit here... if (( $source.type -notmatch "ipv4Address|ipv6Address" ) -and ( $source.value -match $_Member.objectId )) { [pscustomobject]@{"RuleId" = $FirewallRule.id; "SectionId" = $FirewallRule.SectionId; "MemberType" = "Source"; "Name" = $source.Name; "Value" = $source.Value; "Type" = $source.Type; "isValid" = $source.isValid } } } } } if ( $Membertype -match "Destination|All" ) { if ( Invoke-XpathQuery -Node $FirewallRule -query "child::destinations/destination" -QueryMethod SelectSingleNode ) { foreach ($destination in $FirewallRule.Destinations.Destination ) { #ignore any ip4/6 rules - user can only match them on string based search so wont hit here... if (( $destination.type -notmatch "ipv4Address|ipv6Address" ) -and ( $destination.value -match $_Member.objectId )) { [pscustomobject]@{"RuleId" = $FirewallRule.id; "SectionId" = $FirewallRule.SectionId; "MemberType" = "Destination"; "Name" = $destination.Name; "Value" = $destination.Value; "Type" = $destination.Type; "isValid" = $destination.isValid } } } } } } else { #Proper PowerCLI Object passed if ( $_Member -is [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop] ) { #If passed object is a NIC, its easiest to match on name. $NicName = "$($_Member.parent.name) - $($Member.name)" if ( $Membertype -match "Source|All" ) { if ( Invoke-XpathQuery -Node $FirewallRule -query "child::sources/source" -QueryMethod SelectSingleNode ) { foreach ($source in $FirewallRule.Sources.Source ) { if (( $source.type -match "Vnic" ) -and ( $source.name -match $NicName )) { [pscustomobject]@{"RuleId" = $FirewallRule.id; "SectionId" = $FirewallRule.SectionId; "MemberType" = "Source"; "Name" = $source.Name; "Value" = $source.Value; "Type" = $source.Type; "isValid" = $source.isValid } } } } } if ( $Membertype -match "Destination|All" ) { if ( Invoke-XpathQuery -Node $FirewallRule -query "child::destinations/destination" -QueryMethod SelectSingleNode ) { foreach ($destination in $FirewallRule.Destinations.Destination ) { if (( $destination.type -match "Vnic" ) -and ( $destination.name -match $NicName )) { [pscustomobject]@{"RuleId" = $FirewallRule.id; "SectionId" = $FirewallRule.SectionId; "MemberType" = "Destination"; "Name" = $Destination.Name; "Value" = $Destination.Value; "Type" = $Destination.Type; "isValid" = $Destination.isValid } } } } } } else { #any other accepted PowerCLI object, we just need to grab details from the moref. if ( $Membertype -match "Source|All" ) { if ( Invoke-XpathQuery -Node $FirewallRule -query "child::sources/source" -QueryMethod SelectSingleNode ) { foreach ($source in $FirewallRule.Sources.Source ) { if (( $source.type -notmatch "ipv4Address|ipv6Address" ) -and ( $source.value -match $_Member.extensiondata.moref.value )) { [pscustomobject]@{"RuleId" = $FirewallRule.id; "SectionId" = $FirewallRule.SectionId; "MemberType" = "Source"; "Name" = $source.Name; "Value" = $source.Value; "Type" = $source.Type; "isValid" = $source.isValid } } } } } if ( $Membertype -match "Destination|All" ) { if ( Invoke-XpathQuery -Node $FirewallRule -query "child::destinations/destination" -QueryMethod SelectSingleNode ) { foreach ($destination in $FirewallRule.Destinations.Destination ) { if (( $destination.type -notmatch "ipv4Address|ipv6Address" ) -and ( $destination.value -match $_Member.extensiondata.moref.value )) { [pscustomobject]@{"RuleId" = $FirewallRule.id; "SectionId" = $FirewallRule.SectionId; "MemberType" = "Destination"; "Name" = $Destination.Name; "Value" = $Destination.Value; "Type" = $Destination.Type; "isValid" = $Destination.isValid } } } } } } } } } end {} } function Add-NsxFirewallRuleMember { <# .SYNOPSIS Adds a new source or destination member to the specified NSX Distributed Firewall Rule. .DESCRIPTION An NSX Distributed Firewall Rule defines a typical 5 tuple rule and is enforced on each hypervisor at the point where the VMs NIC connects to the portgroup or logical switch. This cmdlet accepts a firewall rule object returned from Get-NsxFirewallRule and adds the specified source and/or destination members to the rule. .EXAMPLE get-nsxfirewallrule -RuleId 5441 | add-NsxFirewallRuleMember -MemberType Source -Member (get-vm web01) | Format-Table RuleId SectionId MemberType Name Value Type isValid ------ --------- ---------- ---- ----- ---- ------- 5441 3717 Source Web01 vm-1270 VirtualMachine true 5441 3717 Source 1.2.3.4 Ipv4Address true 5441 3717 Destination test ipset-309 IPSet true 5441 3717 Destination Web02 vm-1266 VirtualMachine true Add the vm web01 as a source member of rule 5441 - output as table. .EXAMPLE get-nsxfirewallrule -RuleId 5441 | add-NsxFirewallRuleMember -MemberType Destination -Member "1.2.3.4" | Format-Table RuleId SectionId MemberType Name Value Type isValid ------ --------- ---------- ---- ----- ---- ------- 5441 3717 Source Web01 vm-1270 VirtualMachine true 5441 3717 Source 1.2.3.4 Ipv4Address true 5441 3717 Destination test ipset-309 IPSet true 5441 3717 Destination Web02 vm-1266 VirtualMachine true 5441 3717 Destination 1.2.3.4 Ipv4Address true Add the ip 1.2.3.4 to the destinations of rule 5441 - output as table. .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Add-NsxFirewallRuleMember -MemberType Destination -Member (get-vm web02),"1.2.3.4",$IPSetTest | Format-Table RuleId SectionId MemberType Name Value Type isValid ------ --------- ---------- ---- ----- ---- ------- 5441 3717 Source Web01 vm-1270 VirtualMachine true 5441 3717 Source 1.2.3.4 Ipv4Address true 5441 3717 Destination test ipset-309 IPSet true 5441 3717 Destination Web02 vm-1266 VirtualMachine true 5441 3717 Destination 1.2.3.4 Ipv4Address true Add 1.2.3.4, the vm web02 and the nsx ipset stored in $ipsettest to the rule 5441 - output as table. #> [CmdletBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] # DFW rule as returned by Get-NsxFirewallRule / New-NsxFirewallRule [ValidateScript( { ValidateFirewallRule $_ })] [System.Xml.XmlElement]$FirewallRule, [Parameter (Mandatory = $True, Position = 1)] # Member(s) to add. specify ipv4/6 addresses as a string or other member types as VI / NSX Object (VM, Logical Switch etc)). [ValidateScript( { ValidateFirewallRuleSourceDest $_ })] [object[]]$Member, [Parameter (Mandatory = $true)] # MemberType to add. Source, Destination or Both [ValidateSet("Source", "Destination", "Both")] [string]$MemberType, [Parameter (Mandatory = $false)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $sectionId = $FirewallRule.ParentNode.Id $RuleId = $FirewallRule.id $generationNumber = $FirewallRule.ParentNode.generationnumber #Clone the xml so we dont modify source... $_FirewallRule = $FirewallRule.CloneNode($true) if ( $MemberType -eq "Both" ) { # We are defaulting negation to false here as negation applied to ALL members. If we allow user to specify in this cmdlet, then we have to catch the scenario where the rule has existing sources (and we shouldnt override the existing negation setting). Prefer to require user to explicitly use separate set-nsxfirewallrule -negatesource / -negatedestination? if ( -not ( Invoke-XpathQuery -QueryMethod SelectSingleNode -query "child::sources" -Node $_FirewallRule)) { Add-NsxSourceDestNode -Rule $_FirewallRule -Nodetype "sources" -negated:$false } if ( -not ( Invoke-XpathQuery -QueryMethod SelectSingleNode -query "child::destinations" -Node $_FirewallRule)) { Add-NsxSourceDestNode -Rule $_FirewallRule -Nodetype "destinations" -negated:$false } Add-NsxSourceDestMember -membertype "source" -memberlist $member -rule $_FirewallRule Add-NsxSourceDestMember -membertype "destination" -memberlist $member -rule $_FirewallRule } else { if ( -not ( Invoke-XpathQuery -QueryMethod SelectSingleNode -query "child::$($Membertype.ToLower())s" -Node $_FirewallRule)) { Add-NsxSourceDestNode -Rule $_FirewallRule -Nodetype "$($Membertype.ToLower())s" -negated:$false } Add-NsxSourceDestMember -membertype $MemberType.ToLower() -memberlist $member -rule $_FirewallRule } $uri = "/api/4.0/firewall/globalroot-0/config/layer3sections/$sectionId/rules/$Ruleid" #Need the IfMatch header to specify the current section generation id $IfMatchHeader = @{"If-Match" = $generationNumber } try { $response = Invoke-NsxWebRequest -method put -URI $uri -body $_FirewallRule.OuterXml -extraheader $IfMatchHeader -connection $connection [xml]$ruleElem = $response.Content Get-NsxFirewallRule -RuleId $ruleElem.rule.id | Get-NsxFirewallRuleMember } catch { throw "Failed to add member to specified rule. $_" } } end {} } function Remove-NsxFirewallRuleMember { <# .SYNOPSIS Removes the specified source or destination member from the specified NSX Distributed Firewall Rule. .DESCRIPTION An NSX Distributed Firewall Rule defines a typical 5 tuple rule and is enforced on each hypervisor at the point where the VMs NIC connects to the portgroup or logical switch. This cmdlet accepts a firewall rule member object returned from Get-NsxFirewallRuleMember and removes it from its parent rule. .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Get-NsxFirewallRuleMember -MemberType Source -Member 1.2.3.4 | Remove-NsxFirewallRuleMember Removal of a firewall rule member is permanent and will modify your security posture. Proceed with removal of member 1.2.3.4 from the Source list of firewallrule 5441 in section 3717? [Y] Yes [N] No [?] Help (default is "N"): y Remove the source 1.2.3.4 from firewall rule 5441 .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Get-NsxFirewallRuleMember -MemberType Source -Member 1.2.3.4 | Remove-NsxFirewallRuleMember Removal of a firewall rule member is permanent and will modify your security posture. Proceed with removal of member 1.2.3.4 from the Source list of firewallrule 5441 in section 3717? [Y] Yes [N] No [?] Help (default is "N"): y Remove the source 1.2.3.4 from firewall rule 5441 .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Get-NsxFirewallRuleMember -MemberType Source -Member 1.2.3.4 | Remove-NsxFirewallRuleMember -confirm:$false Remove the source 1.2.3.4 from firewall rule 5441 with no confirmation. .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Get-NsxFirewallRuleMember -MemberType Source | Remove-NsxFirewallRuleMember Removal of a firewall rule member is permanent and will modify your security posture. Proceed with removal of member 1.2.3.4 from the Source list of firewallrule 5441 in section 3717? [Y] Yes [N] No [?] Help (default is "N"): y Removal of a firewall rule member is permanent and will modify your security posture. Proceed with removal of member vm-1270 from the Source list of firewallrule 5441 in section 3717? [Y] Yes [N] No [?] Help (default is "N"): y The source member vm-1270 of rule 5441 in section 3717 is the last source member in this rule. Its removal will cause this rule to match ANY Source Confirm rule 5441 to match Source ANY? [Y] Yes [N] No [?] Help (default is "N"): y WARNING: The source member vm-1270 of rule 5441 in section 3717 was the last member in this rule. Its removal has caused this rule to now match ANY Source. Remove ALL sources from the firewall rule 5441. Note the extra prompt AND warning that you are about to make this rule match on ANY source. .EXAMPLE get-nsxfirewallrule -RuleId 5441 | Get-NsxFirewallRuleMember -MemberType Source | Remove-NsxFirewallRuleMember -Confirm:$false The source member vm-1270 of rule 5441 in section 3717 is the last source member in this rule. Its removal will cause this rule to match ANY Source Confirm rule 5441 to match Source ANY? [Y] Yes [N] No [?] Help (default is "N"): y Remove ALL sources from the firewall rule 5441 with no confirmation prompt. Note the remaining prompt AND warning that you are about to make this rule match on ANY source. #> [CmdletBinding(DefaultParameterSetName = "Default")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] # DFW rule member as returned by Get-NsxFirewallRuleMember [ValidateScript( { ValidateFirewallRuleMemberObject $_ })] [Object]$FirewallRuleMember, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #Override confirmation of removal of last source or destination member - effectively converting rule to match ANY in the appropriate field (source or destination). Specify as -SayHello2Heaven to disable confirmation prompt. RIP Chris Cornell, 17 May 2017 [switch]$SayHello2Heaven = $false, [Parameter (Mandatory = $false)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { # We process all member modifications offline as part of pipeline processing, then we put the updated sections to the api in the end{} section to optimise multiple edits to the same rule. # Save modified rules in a hash table keyed by ruleid $ModifiedSections = @{} } process { if ( $confirm ) { $message = "Removal of a firewall rule member is permanent and will modify your security posture." $question = "Proceed with removal of member $($FirewallRuleMember.Value) from the $($FirewallRuleMember.MemberType) list of firewallrule $($FirewallRuleMember.RuleId) in section $($FirewallRuleMember.SectionId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ( $decision -eq 0 ) { if ( $ModifiedSections.ContainsKey($FirewallRuleMember.SectionId )) { # Section has already been updated in this pipeline, so we modify the already updated xml. $SectionXml = $ModifiedSections[$FirewallRuleMember.SectionId] } else { # We havent touched the section yet, so we have to get it $SectionXml = Get-NsxFirewallSection -objectId $FirewallRuleMember.SectionId $ModifiedSections.Add($FirewallRuleMember.SectionId, $SectionXml) } $Query = "descendant::rule[@id=`"$($FirewallRuleMember.RuleId.ToString())`"]/*/$($FirewallRuleMember.MemberType.ToString().ToLower())[value=`"$($FirewallRuleMember.Value.ToString())`"]" Write-Debug "$($MyInvocation.MyCommand.Name) : Executing xpath query to locate member in section: $query" $XmlMember = Invoke-XpathQuery -QueryMethod SelectNodes -query $Query -Node $SectionXml if ( @($XmlMember).Count -ne 1) { throw "Xpath query for member $($FirewallRuleMember.Name) did not result in exactly one member being returned. $(@($XmlMember).count) members were matched. Please report this issue at https://github.com/vmware/powernsx/issues/ and include steps to reproduce." } if ( $XmlMember.ParentNode.ParentNode.id -ne $FirewallRuleMember.RuleId ) { throw "Xpath query for member $($FirewallRuleMember.Name) returned a member with a non matching ruleid: $($XmlMember.ParentNode.ParentNode.id) -ne $($FirewallRuleMember.RuleId). Please report this issue at https://github.com/vmware/powernsx/issues/ and include steps to reproduce." } # Assuming our xpath query is correct, we can simply remove the child element from its parent node $ParentNode = $XmlMember.ParentNode $AllChildNodes = Invoke-XpathQuery -Node $ParentNode -QueryMethod SelectNodes -query "child::$($FirewallRuleMember.MemberType.ToString().ToLower())" if ( @($AllChildNodes).count -eq 1 ) { # We have about to remove the last member from the sources or destinations element. API will reject and empty sources elem, so we need to remove it. # We also should warn the user that this just became an any rule! # Also - when Im doing this 'get my parent and call its removechild method to remove myself' kinda circular operation, it always reminds me of the Lorax lifting himself by the seat of his pants and disappearing... if ( -not $SayHello2Heaven ) { $message = "The $($FirewallRuleMember.MemberType.ToLower()) member $($FirewallRuleMember.Value) of rule $($FirewallRuleMember.RuleId) in section $($FirewallRuleMember.SectionId) is the last $($FirewallRuleMember.MemberType.ToLower()) member in this rule. Its removal will cause this rule to match ANY $($FirewallRuleMember.MemberType)" $question = "Confirm rule $($FirewallRuleMember.RuleId) to match $($FirewallRuleMember.MemberType) ANY?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ( $decision -eq 0 ) { Write-Warning "The $($FirewallRuleMember.MemberType.ToLower()) member $($FirewallRuleMember.Value) of rule $($FirewallRuleMember.RuleId) in section $($FirewallRuleMember.SectionId) was the last member in this rule. Its removal has caused this rule to now match ANY $($FirewallRuleMember.MemberType)." $ParentNode.RemoveChild($XmlMember) | Out-Null $ParentNode.ParentNode.RemoveChild($ParentNode) | Out-Null } } else { $ParentNode.RemoveChild($XmlMember) | Out-Null } } } end { # Section XML is updated in process block. End block fires at end of pipeline processing and is where we push the updated sections to reduce the number of API roundtrips... foreach ( $Section in $ModifiedSections.Values ) { switch ( $Section.Type ) { "LAYER3" { $SectionType = "layer3sections" } "LAYER2" { $SectionType = "layer2sections" } "L3REDIRECT" { $SectionType = "layer3redirectsections" } } $uri = "/api/4.0/firewall/globalroot-0/config/$SectionType/$($section.Id)" # Need the IfMatch header to specify the current section generation id $IfMatchHeader = @{"If-Match" = $Section.generationNumber } try { $response = Invoke-NsxWebRequest -method put -URI $uri -body $Section.outerxml -extraheader $IfMatchHeader -connection $connection [xml]$Section = $response.Content } catch { throw "Failed to post updated NSX Firewall Section $($Section.Id). $_" } } } } function Get-NsxFirewallGlobalConfiguration { <# .SYNOPSIS Retrieves the Distributed Firewall global configuration options. .DESCRIPTION The global firewalll configuration options can be used to modify firewall performance. This command will retrieve the current configuration options for the distributed firewall .EXAMPLE PS /> Get-NsxFirewallGlobalConfiguration layer3RuleOptimize layer2RuleOptimize tcpStrictOption ------------------ ------------------ --------------- false true false #> param ( [Parameter (Mandatory = $false)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { $URI = "/api/4.0/firewall/config/globalconfiguration" $response = Invoke-NsxWebRequest -method "get" -URI $URI -connection $connection try { [system.xml.xmldocument]$globalConfigurationDoc = $response.content $globalConfigurationDoc.globalConfiguration } catch { Throw "Unexpected API response $_" } } end {} } function Set-NsxFirewallGlobalConfiguration { <# .SYNOPSIS Sets the Distributed Firewall global configuration options. .DESCRIPTION The global firewalll configuration options can be used to modify the distributed firewall. .EXAMPLE PS /> Get-NsxFirewallGlobalConfiguration | Set-NsxFirewallGlobalConfiguration -EnableTcpStrict -DisableAutoDraft layer3RuleOptimize layer2RuleOptimize tcpStrictOption autoDraftDisabled ------------------ ------------------ --------------- ----------------- false true true true #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$GlobalConfiguration, [Parameter (Mandatory = $False)] [switch]$EnableTcpStrict, [Parameter (Mandatory = $False)] [switch]$DisableAutoDraft, [Parameter (Mandatory = $false)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Capture existing options $_GlobalConfiguration = $GlobalConfiguration.CloneNode($True) #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('EnableTcpStrict') ) { $_GlobalConfiguration.tcpStrictOption = [string]$EnableTcpStrict } if ( $PsBoundParameters.ContainsKey('DisableAutoDraft') ) { # Check to see if the element already exists if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_GlobalConfiguration -query 'descendant::autoDraftDisabled')) { $_GlobalConfiguration.autoDraftDisabled = [string]$DisableAutoDraft } else { Add-XmlElement -xmlRoot $_GlobalConfiguration -xmlElementName "autoDraftDisabled" -xmlElementText $DisableAutoDraft } } $uri = "/api/4.0/firewall/config/globalconfiguration" $body = $_GlobalConfiguration.outerXml Invoke-NsxWebRequest -method "PUT" -URI $uri -body $body | Out-Null Get-NsxFirewallGlobalConfiguration } end {} } function Get-NsxFirewallPublishStatus { <# .SYNOPSIS Retrieves the Distributed Firewall publish status showing per cluster generation number and status. .DESCRIPTION An NSX Distributed Firewall Rule defines a typical 5 tuple rule and is enforced on each hypervisor at the point where the VMs NIC connects to the portgroup or logical switch. The Get-NsxFirewallPublishStatus cmdet retrieves the current publishign status for each DFW enabled cluster. .EXAMPLE Get-NsxFirewallPublishStatus #> param ( [Parameter (Mandatory = $false)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { $URI = "/api/4.0/firewall/globalroot-0/status" $response = Invoke-NsxWebRequest -method "get" -URI $URI -connection $connection [system.xml.xmldocument]$Content = $response.content if ( Invoke-XpathQuery -Node $content -QueryMethod SelectSingleNode -query "child::firewallStatus" ) { $Content.firewallStatus } } end {} } ######## ######## # Load Balancing function Get-NsxLoadBalancer { <# .SYNOPSIS Retrieves the LoadBalancer configuration from a specified Edge. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. This cmdlet retrieves the LoadBalancer configuration from a specified Edge. .EXAMPLE PS C:\> Get-NsxEdge Edge01 | Get-NsxLoadBalancer #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge ) begin {} process { #We append the Edge-id to the associated LB XML to enable pipeline workflows and #consistent readable output (PSCustom object approach results in 'edge and #LoadBalancer' props of the output which is not pretty for the user) $_LoadBalancer = $Edge.features.loadBalancer.CloneNode($True) Add-XmlElement -xmlRoot $_LoadBalancer -xmlElementName "edgeId" -xmlElementText $Edge.Id $_LoadBalancer } } function Set-NsxLoadBalancer { <# .SYNOPSIS Configures an NSX LoadBalancer. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. This cmdlet sets the basic LoadBalancer configuration of an NSX Load Balancer. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Set-NsxLoadBalancer -Enabled Enabled the LoadBalancer feature on Edge. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Set-NsxLoadBalancer -Enabled:$false Disabled the LoadBalancer feature on Edge. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Set-NsxLoadBalancer -EnableAcceleration Enabled the Acceleration feature (uses the faster L4 LB engine rather than L7 LB engine) on Load Balancer. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Set-NsxLoadBalancer -EnableLogging Enabled Load Balancer collects traffic logs. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Set-NsxLoadBalancer -LogLevel debug Choose the log level (emergency, alert, critical, error, warning, notice, info, debug) of Load Balancer traffic logs. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancer $_ })] [System.Xml.XmlElement]$LoadBalancer, [Parameter (Mandatory = $False)] [switch]$Enabled, [Parameter (Mandatory = $False)] [switch]$EnableAcceleration, [Parameter (Mandatory = $False)] [switch]$EnableLogging, [Parameter (Mandatory = $False)] [ValidateSet("emergency", "alert", "critical", "error", "warning", "notice", "info", "debug")] [string]$LogLevel, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LoadBalancer = $LoadBalancer.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_LoadBalancer.edgeId $_LoadBalancer.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LoadBalancer -query 'descendant::edgeId')) ) | Out-Null #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('Enabled') ) { if ( $Enabled ) { $_LoadBalancer.enabled = "true" } else { $_LoadBalancer.enabled = "false" } } if ( $PsBoundParameters.ContainsKey('EnableAcceleration') ) { if ( $EnableAcceleration ) { $_LoadBalancer.accelerationEnabled = "true" } else { $_LoadBalancer.accelerationEnabled = "false" } } if ( $PsBoundParameters.ContainsKey('EnableLogging') ) { if ( $EnableLogging ) { $_LoadBalancer.logging.enable = "true" } else { $_LoadBalancer.logging.enable = "false" } } if ( $PsBoundParameters.ContainsKey('LogLevel') ) { $_LoadBalancer.logging.logLevel = $LogLevel } $URI = "/api/4.0/edges/$($edgeId)/loadbalancer/config" $body = $_LoadBalancer.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" -Completed Get-NsxEdge -objectId $($edgeId) -Connection $connection | Get-NsxLoadBalancer } end { } } function Get-NsxLoadBalancerMonitor { <# .SYNOPSIS Retrieves the LoadBalancer Monitors from a specified LoadBalancer. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. Load Balancer Monitors are the method by which a Load Balancer determines the health of pool members. This cmdlet retrieves the LoadBalancer Monitors from a specified LoadBalancer. .EXAMPLE PS C:\> $LoadBalancer | Get-NsxLoadBalancerMonitor default_http_monitor #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancer $_ })] [PSCustomObject]$LoadBalancer, [Parameter (Mandatory = $true, ParameterSetName = "monitorId")] [string]$monitorId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [string]$Name ) begin {} process { if ( $Name) { $Monitors = $loadbalancer.monitor | Where-Object { $_.name -eq $Name } } elseif ( $monitorId ) { $Monitors = $loadbalancer.monitor | Where-Object { $_.monitorId -eq $monitorId } } else { $Monitors = $loadbalancer.monitor } foreach ( $Monitor in $Monitors ) { $_Monitor = $Monitor.CloneNode($True) Add-XmlElement -xmlRoot $_Monitor -xmlElementName "edgeId" -xmlElementText $LoadBalancer.edgeId $_Monitor } } end { } } function New-NsxLoadBalancerMonitor { <# .SYNOPSIS Creates a new LoadBalancer Service Monitor on the specified Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. Service monitors define health check parameters for a particular type of network traffic. When you associate a service monitor with a pool, the pool members are monitored according to the service monitor parameters. This cmdlet creates a new LoadBalancer Service monitor on a specified Load Balancer .EXAMPLE PS C:\> Get-NsxEdge Edge01 | Get-NsxLoadBalancer | New-NsxLoadBalancerMonitor -Name Web-Monitor -interval 10 -Timeout 10 -MaxRetries 3 -Type HTTPS -Method GET -Url "/WAPI/api/status" -Expected "200 OK" #> [CmdLetBinding(DefaultParameterSetName = "HTTP")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "HTTP")] [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "HTTPS")] [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "TCP")] [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "ICMP")] [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "UDP")] [ValidateScript( { ValidateLoadBalancer $_ })] [System.Xml.XmlElement]$LoadBalancer, [Parameter (Mandatory = $true, ParameterSetName = "HTTP")] [switch]$TypeHttp, [Parameter (Mandatory = $true, ParameterSetName = "HTTPS")] [switch]$TypeHttps, [Parameter (Mandatory = $true, ParameterSetName = "TCP")] [switch]$TypeTcp, [Parameter (Mandatory = $true, ParameterSetName = "ICMP")] [switch]$TypeIcmp, [Parameter (Mandatory = $true, ParameterSetName = "UDP")] [switch]$TypeUdp, [Parameter (Mandatory = $true, ParameterSetName = "HTTP")] [Parameter (Mandatory = $true, ParameterSetName = "HTTPS")] [Parameter (Mandatory = $true, ParameterSetName = "TCP")] [Parameter (Mandatory = $true, ParameterSetName = "ICMP")] [Parameter (Mandatory = $true, ParameterSetName = "UDP")] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true, ParameterSetName = "HTTP")] [Parameter (Mandatory = $true, ParameterSetName = "HTTPS")] [Parameter (Mandatory = $true, ParameterSetName = "TCP")] [Parameter (Mandatory = $true, ParameterSetName = "ICMP")] [Parameter (Mandatory = $true, ParameterSetName = "UDP")] [ValidateNotNullOrEmpty()] [string]$Interval, [Parameter (Mandatory = $true, ParameterSetName = "HTTP")] [Parameter (Mandatory = $true, ParameterSetName = "HTTPS")] [Parameter (Mandatory = $true, ParameterSetName = "TCP")] [Parameter (Mandatory = $true, ParameterSetName = "ICMP")] [Parameter (Mandatory = $true, ParameterSetName = "UDP")] [ValidateNotNullOrEmpty()] [string]$Timeout, [Parameter (Mandatory = $true, ParameterSetName = "HTTP")] [Parameter (Mandatory = $true, ParameterSetName = "HTTPS")] [Parameter (Mandatory = $true, ParameterSetName = "TCP")] [Parameter (Mandatory = $true, ParameterSetName = "ICMP")] [Parameter (Mandatory = $true, ParameterSetName = "UDP")] [ValidateNotNullOrEmpty()] [string]$MaxRetries, [Parameter (Mandatory = $true, ParameterSetName = "HTTP")] [Parameter (Mandatory = $true, ParameterSetName = "HTTPS")] [ValidateSet("GET", "POST", "OPTIONS", IgnoreCase = $False)] [string]$Method, [Parameter (Mandatory = $true, ParameterSetName = "HTTP")] [Parameter (Mandatory = $true, ParameterSetName = "HTTPS")] [ValidateNotNullOrEmpty()] [string]$Url, [Parameter (Mandatory = $false, ParameterSetName = "HTTP")] [Parameter (Mandatory = $false, ParameterSetName = "HTTPS")] [ValidateNotNullOrEmpty()] [string]$Expected, [Parameter (Mandatory = $false, ParameterSetName = "HTTP")] [Parameter (Mandatory = $false, ParameterSetName = "HTTPS")] [Parameter (Mandatory = $false, ParameterSetName = "TCP")] [Parameter (Mandatory = $false, ParameterSetName = "ICMP")] [Parameter (Mandatory = $false, ParameterSetName = "UDP")] [ValidateNotNullOrEmpty()] [string]$Send, [Parameter (Mandatory = $false, ParameterSetName = "HTTP")] [Parameter (Mandatory = $false, ParameterSetName = "HTTPS")] [Parameter (Mandatory = $false, ParameterSetName = "TCP")] [Parameter (Mandatory = $false, ParameterSetName = "ICMP")] [Parameter (Mandatory = $false, ParameterSetName = "UDP")] [ValidateNotNullOrEmpty()] [string]$Receive, [Parameter (Mandatory = $false, ParameterSetName = "HTTP")] [Parameter (Mandatory = $false, ParameterSetName = "HTTPS")] [Parameter (Mandatory = $false, ParameterSetName = "TCP")] [Parameter (Mandatory = $false, ParameterSetName = "ICMP")] [Parameter (Mandatory = $false, ParameterSetName = "UDP")] [ValidateNotNullOrEmpty()] [string]$Extension, [Parameter (Mandatory = $false, ParameterSetName = "HTTP")] [Parameter (Mandatory = $false, ParameterSetName = "HTTPS")] [Parameter (Mandatory = $false, ParameterSetName = "TCP")] [Parameter (Mandatory = $false, ParameterSetName = "ICMP")] [Parameter (Mandatory = $false, ParameterSetName = "UDP")] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Store the edgeId $edgeId = $LoadBalancer.edgeId if ( -not $LoadBalancer.enabled -eq 'true' ) { Write-Warning "Load Balancer feature is not enabled on edge $($edgeId). Use Set-NsxLoadBalancer -Enabled to enable." } [System.XML.XMLElement]$xmlmonitor = $LoadBalancer.OwnerDocument.CreateElement("monitor") #Common Elements Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "interval" -xmlElementText $Interval Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "timeout" -xmlElementText $Timeout Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "maxRetries" -xmlElementText $MaxRetries #Optional if ( $PSBoundParameters.ContainsKey('Send')) { Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "send" -xmlElementText $Send } if ( $PSBoundParameters.ContainsKey('Receive')) { Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "receive" -xmlElementText $Receive } if ( $PSBoundParameters.ContainsKey('Extension')) { Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "extension" -xmlElementText $Extension } #Type specific switch -regex ( $PsCmdlet.ParameterSetName ) { "HTTP" { #will match both HTTP and HTTPS due to regex switch handling... if ( $TypeHttp ) { Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "type" -xmlElementText "http" } else { Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "type" -xmlElementText "https" } if ( $PSBoundParameters.ContainsKey('Method')) { Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "method" -xmlElementText $Method } if ( $PSBoundParameters.ContainsKey('Url')) { Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "url" -xmlElementText $Url } if ( $PSBoundParameters.ContainsKey('Expected')) { Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "expected" -xmlElementText $Expected } } "ICMP" { Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "type" -xmlElementText "icmp" } "TCP" { Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "type" -xmlElementText "tcp" } "UDP" { Add-XmlElement -xmlRoot $xmlmonitor -xmlElementName "type" -xmlElementText "udp" } } $URI = "/api/4.0/edges/$edgeId/loadbalancer/config/monitors" $body = $xmlmonitor.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" -Status "Load Balancer Monitor Config" $null = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" -Completed Get-NsxEdge -objectId $edgeId -Connection $connection | Get-NsxLoadBalancer | Get-NsxLoadBalancerMonitor -Name $Name } end {} } function Remove-NsxLoadBalancerMonitor { <# .SYNOPSIS Removes the specified LoadBalancer Monitor. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. Service monitors define health check parameters for a particular type of network traffic. When you associate a service monitor with a pool, the pool members are monitored according to the service monitor parameters. This cmdlet removes the specified LoadBalancer Monitor. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLoadBalancerMonitor $_ })] [System.Xml.XmlElement]$Monitor, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $Monitor.edgeId $MonitorId = $Monitor.monitorId $URI = "/api/4.0/edges/$edgeId/loadbalancer/config/monitors/$MonitorId" if ( $confirm ) { $message = "Monitor removal is permanent." $question = "Proceed with removal of Load Balancer Monitor $($MonitorId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Status "Removing Monitor $MonitorId" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Completed } } end {} } function Get-NsxLoadBalancerApplicationProfile { <# .SYNOPSIS Retrieves LoadBalancer Application Profiles from a specified LoadBalancer. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. Application profiles define the behavior of a particular type of network traffic. After configuring a profile, you associate the profile with a virtual server. The virtual server then processes traffic according to the values specified in the profile. Using profiles enhances your control over managing network traffic, and makes traffic‐management tasks easier and more efficient. This cmdlet retrieves the LoadBalancer Application Profiles from a specified LoadBalancer. .EXAMPLE PS C:\> Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Get-NsxLoadBalancerApplicationProfile HTTP #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancer $_ })] [System.Xml.XmlElement]$LoadBalancer, [Parameter (Mandatory = $true, ParameterSetName = "applicationProfileId")] [alias("applicationProfileId")] [string]$objectId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [string]$Name ) begin {} process { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LoadBalancer -query 'descendant::applicationProfile')) { if ( $PsBoundParameters.ContainsKey('Name')) { $AppProfiles = $loadbalancer.applicationProfile | Where-Object { $_.name -eq $Name } } elseif ( $PsBoundParameters.ContainsKey('objectId') ) { $AppProfiles = $loadbalancer.applicationProfile | Where-Object { $_.applicationProfileId -eq $objectId } } else { $AppProfiles = $loadbalancer.applicationProfile } foreach ( $AppProfile in $AppProfiles ) { $_AppProfile = $AppProfile.CloneNode($True) Add-XmlElement -xmlRoot $_AppProfile -xmlElementName "edgeId" -xmlElementText $LoadBalancer.edgeId $_AppProfile } } } end { } } function New-NsxLoadBalancerApplicationProfile { <# .SYNOPSIS Creates a new LoadBalancer Application Profile on the specified Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. Application profiles define the behavior of a particular type of network traffic. After configuring a profile, you associate the profile with a virtual server. The virtual server then processes traffic according to the values specified in the profile. Using profiles enhances your control over managing network traffic, and makes traffic‐management tasks easier and more efficient. This cmdlet creates a new LoadBalancer Application Profile on a specified Load Balancer #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancer $_ })] [System.Xml.XmlElement]$LoadBalancer, [Parameter (Mandatory = $True)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $True)] [ValidateSet("TCP", "UDP", "HTTP", "HTTPS")] [string]$Type, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [switch]$InsertXForwardedFor = $false, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [switch]$SslPassthrough = $false, [Parameter (Mandatory = $False)] [ValidateSet("ssl_sessionid", "cookie", "sourceip", "msrdp", IgnoreCase = $false)] [string]$PersistenceMethod, [Parameter (Mandatory = $False)] [int]$PersistenceExpiry, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$CookieName, [Parameter (Mandatory = $False)] [ValidateSet("insert", "prefix", "app")] [string]$CookieMode, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) # Still a bit to do here - need cert selection... # Also - There are many combinations of valid (and invalid) options. Unfortunately. # the NSX API does not perform the validation of these combinations (It will # accept combinations of params that the UI will not), the NSX UI does # So I need to be doing validation in here as well - this is still to be done, but required # so user has sane experience... begin { } process { #Create private xml element $_LoadBalancer = $LoadBalancer.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_LoadBalancer.edgeId $_LoadBalancer.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LoadBalancer -query 'descendant::edgeId')) ) | Out-Null if ( -not $_LoadBalancer.enabled -eq 'true' ) { Write-Warning "Load Balancer feature is not enabled on edge $($edgeId). Use Set-NsxLoadBalancer -Enabled to enable." } [System.XML.XMLElement]$xmlapplicationProfile = $_LoadBalancer.OwnerDocument.CreateElement("applicationProfile") $_LoadBalancer.appendChild($xmlapplicationProfile) | Out-Null #Mandatory Params and those with Default values Add-XmlElement -xmlRoot $xmlapplicationProfile -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlapplicationProfile -xmlElementName "template" -xmlElementText $Type Add-XmlElement -xmlRoot $xmlapplicationProfile -xmlElementName "insertXForwardedFor" -xmlElementText $insertXForwardedFor Add-XmlElement -xmlRoot $xmlapplicationProfile -xmlElementName "sslPassthrough" -xmlElementText $SslPassthrough #Optionals. If ( $PsBoundParameters.ContainsKey('PersistenceMethod')) { [System.XML.XMLElement]$xmlPersistence = $_LoadBalancer.OwnerDocument.CreateElement("persistence") $xmlapplicationProfile.appendChild($xmlPersistence) | Out-Null Add-XmlElement -xmlRoot $xmlPersistence -xmlElementName "method" -xmlElementText $PersistenceMethod If ( $PsBoundParameters.ContainsKey('CookieName')) { Add-XmlElement -xmlRoot $xmlPersistence -xmlElementName "cookieName" -xmlElementText $CookieName } If ( $PsBoundParameters.ContainsKey('CookieMode')) { Add-XmlElement -xmlRoot $xmlPersistence -xmlElementName "cookieMode" -xmlElementText $CookieMode } If ( $PsBoundParameters.ContainsKey('PersistenceExpiry')) { Add-XmlElement -xmlRoot $xmlPersistence -xmlElementName "expire" -xmlElementText $PersistenceExpiry } } $URI = "/api/4.0/edges/$edgeId/loadbalancer/config" $body = $_LoadBalancer.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" -Status "Load Balancer Config" $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" -Completed $updatedEdge = Get-NsxEdge -objectId $($edgeId) -Connection $connection $applicationProfiles = $updatedEdge.features.loadbalancer.applicationProfile foreach ($applicationProfile in $applicationProfiles) { #6.1 Bug? NSX API creates an object ID format that it does not accept back when put. We have to change on the fly to the 'correct format'. Write-Debug "$($MyInvocation.MyCommand.Name) : Checking for stupidness in $($applicationProfile.applicationProfileId)" $applicationProfile.applicationProfileId = $applicationProfile.applicationProfileId.replace("edge_load_balancer_application_profiles", "applicationProfile-") } $body = $updatedEdge.features.loadbalancer.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" -Status "Load Balancer Config" $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" -Completed #filter output for our newly created app profile - name is safe as it has to be unique. $return = $updatedEdge.features.loadbalancer.applicationProfile | Where-Object { $_.name -eq $name } Add-XmlElement -xmlRoot $return -xmlElementName "edgeId" -xmlElementText $edgeId $return } end {} } function Remove-NsxLoadBalancerApplicationProfile { <# .SYNOPSIS Removes the specified LoadBalancer Application Profile. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. Application profiles define the behavior of a particular type of network traffic. After configuring a profile, you associate the profile with a virtual server. The virtual server then processes traffic according to the values specified in the profile. Using profiles enhances your control over managing network traffic, and makes traffic‐management tasks easier and more efficient. This cmdlet removes the specified LoadBalancer Application Profile. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLoadBalancerApplicationProfile $_ })] [System.Xml.XmlElement]$ApplicationProfile, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $ApplicationProfile.edgeId $AppProfileId = $ApplicationProfile.applicationProfileId $URI = "/api/4.0/edges/$edgeId/loadbalancer/config/applicationprofiles/$AppProfileId" if ( $confirm ) { $message = "Application Profile removal is permanent." $question = "Proceed with removal of Application Profile $($AppProfileId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Status "Removing Application Profile $AppProfileId" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Completed } } end {} } function New-NsxLoadBalancerMemberSpec { <# .SYNOPSIS Creates a new LoadBalancer Pool Member specification to be used when updating or creating a LoadBalancer Pool .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A pool manages load balancer distribution methods and has a service monitor attached to it for health check parameters. Each Pool has one or more members. Prior to creating or updating a pool to add a member, a member spec describing the member needs to be created. This cmdlet creates a new LoadBalancer Pool Member specification. .EXAMPLE $WebMember1 = New-NsxLoadBalancerMemberSpec -name Web01 -IpAddress 192.168.200.11 -Port 80 Creates a new member spec for a member called Web01, ipaddress 192.168.200.11 and port 80 .EXAMPLE $WebMember2 = New-NsxLoadBalancerMemberSpec -name Web02 -IpAddress 192.168.200.12 -Port 80 -MonitorPort 8080 -MaximumConnections 100 Creates a new member spec with an alternate MonitorPort. .EXAMPLE $WebMember3 = New-NsxLoadBalancerMemberSpec -Name Web03 -Member (Get-VM Web03) -port 80 Creates a new member spec based on VM object for Web03. .EXAMPLE $WebMember3 = New-NsxLoadBalancerMemberSpec -Name Web03 -Member (Get-NsxLogicalSwitch WebLS) -port 80 Creates a new member spec based on NSX Logical Switch. #> [CmdLetBinding(DefaultParameterSetName = "IpAddress")] param ( [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true, ParameterSetName = "IpAddress")] [ValidateNotNullOrEmpty()] [IpAddress]$IpAddress, [Parameter (Mandatory = $true, ParameterSetName = "GroupingObject")] [ValidateScript( { ValidateSecurityGroupMember $_ })] [object]$Member, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [int]$Weight = 1, [Parameter (Mandatory = $false)] [ValidateRange(1, 65535)] [int]$Port, [Parameter (Mandatory = $false)] [ValidateRange(1, 65535)] [int]$MonitorPort = $Port, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [int]$MinimumConnections = 0, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [int]$MaximumConnections = 0 ) begin {} process { [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$xmlMember = $XMLDoc.CreateElement("member") $xmlDoc.appendChild($xmlMember) | Out-Null Add-XmlElement -xmlRoot $xmlMember -xmlElementName "name" -xmlElementText $Name if ( $PSCmdlet.ParameterSetName -eq "ipaddress" ) { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "ipAddress" -xmlElementText $IpAddress } else { if ($Member -is [System.Xml.XmlElement] ) { $MemberMoref = $Member.objectId $MemberName = $Member.name } elseif ( ($Member -is [string]) -and ($Member -match "^vm-\d+$|^resgroup-\d+$|^dvportgroup-\d+$" )) { $MemberMoref = $Member $MemberName = $Member } elseif ( ($Member -is [string] ) -and ( [guid]::tryparse(($Member -replace ".\d{3}$", ""), [ref][guid]::Empty)) ) { $MemberMoref = $Member $MemberName = $Member } elseif (( $Member -is [string]) -and ( $NsxMemberTypes -contains ($Member -replace "-\d+$") ) ) { $MemberMoref = $Member $MemberName = $Member } elseif ( $Member -is [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop] ) { #See NSX API guide 'Attach or Detach a Virtual Machine from a Logical Switch' for #how to construct NIC id. $vmUuid = ($Member.parent | Get-View).config.instanceuuid $MemberMoref = "$vmUuid.$($Member.id.substring($Member.id.length-3))" $MemberName = $Member.Name } elseif (( $Member -is [VMware.VimAutomation.ViCore.Interop.V1.VIObjectInterop]) -and ( $NsxMemberTypes -contains $Member.ExtensionData.MoRef.Type)) { $MemberMoref = $Member.ExtensionData.MoRef.Value $MemberName = $Member.Name } else { throw "Invalid member specified $($Member)" } #Create a new member node Add-XmlElement -xmlRoot $xmlMember -xmlElementName "groupingObjectId" -xmlElementText $MemberMoref Add-XmlElement -xmlRoot $xmlMember -xmlElementName "groupingObjectName" -xmlElementText $MemberName } Add-XmlElement -xmlRoot $xmlMember -xmlElementName "weight" -xmlElementText $Weight Add-XmlElement -xmlRoot $xmlMember -xmlElementName "port" -xmlElementText $Port Add-XmlElement -xmlRoot $xmlMember -xmlElementName "monitorPort" -xmlElementText $MonitorPort Add-XmlElement -xmlRoot $xmlMember -xmlElementName "minConn" -xmlElementText $MinimumConnections Add-XmlElement -xmlRoot $xmlMember -xmlElementName "maxConn" -xmlElementText $MaximumConnections $xmlMember } end {} } function New-NsxLoadBalancerPool { <# .SYNOPSIS Creates a new LoadBalancer Pool on the specified ESG. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A pool manages load balancer distribution methods and has a service monitor attached to it for health check parameters. Each Pool has one or more members. Prior to creating or updating a pool to add a member, a member spec describing the member needs to be created. This cmdlet creates a new LoadBalancer Pool on the specified ESG. .EXAMPLE Example1: Need to create member specs for each of the pool members first PS C:\> $WebMember1 = New-NsxLoadBalancerMemberSpec -name Web01 -IpAddress 192.168.200.11 -Port 80 PS C:\> $WebMember2 = New-NsxLoadBalancerMemberSpec -name Web02 -IpAddress 192.168.200.12 -Port 80 -MonitorPort 8080 -MaximumConnections 100 PS C:\> $WebPool = Get-NsxEdge Edge01 | New-NsxLoadBalancerPool -Name WebPool -Description "WebServer Pool" -Transparent:$false -Algorithm round-robin -Monitor $monitor -MemberSpec $WebMember1,$WebMember2 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancer $_ })] [System.Xml.XmlElement]$LoadBalancer, [Parameter (Mandatory = $True)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $False)] [ValidateNotNull()] [string]$Description = "", [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [switch]$Transparent = $false, [Parameter (Mandatory = $True)] [ValidateSet("round-robin", "ip-hash", "uri", "leastconn")] [string]$Algorithm, [Parameter (Mandatory = $false)] [ValidateScript( { ValidateLoadBalancerMonitor $_ })] [System.Xml.XmlElement]$Monitor, [Parameter (Mandatory = $false)] [ValidateScript( { ValidateLoadBalancerMemberSpec $_ })] [System.Xml.XmlElement[]]$MemberSpec, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LoadBalancer = $LoadBalancer.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_LoadBalancer.edgeId $_LoadBalancer.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LoadBalancer -query 'descendant::edgeId')) ) | Out-Null if ( -not $_LoadBalancer.enabled -eq 'true' ) { Write-Warning "Load Balancer feature is not enabled on edge $($edgeId). Use Set-NsxLoadBalancer -Enabled to enable." } [System.XML.XMLElement]$xmlPool = $_LoadBalancer.OwnerDocument.CreateElement("pool") $_LoadBalancer.appendChild($xmlPool) | Out-Null Add-XmlElement -xmlRoot $xmlPool -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlPool -xmlElementName "description" -xmlElementText $Description Add-XmlElement -xmlRoot $xmlPool -xmlElementName "transparent" -xmlElementText $Transparent Add-XmlElement -xmlRoot $xmlPool -xmlElementName "algorithm" -xmlElementText $algorithm if ( $PsBoundParameters.ContainsKey('Monitor')) { Add-XmlElement -xmlRoot $xmlPool -xmlElementName "monitorId" -xmlElementText $Monitor.monitorId } if ( $PSBoundParameters.ContainsKey('MemberSpec')) { foreach ( $Member in $MemberSpec ) { $xmlmember = $xmlPool.OwnerDocument.ImportNode($Member, $true) $xmlPool.AppendChild($xmlmember) | Out-Null } } $URI = "/api/4.0/edges/$EdgeId/loadbalancer/config" $body = $_LoadBalancer.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Status "Load Balancer Config" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed $UpdatedEdge = Get-NsxEdge -objectId $($EdgeId) -Connection $connection $return = $UpdatedEdge.features.loadBalancer.pool | Where-Object { $_.name -eq $Name } Add-XmlElement -xmlRoot $return -xmlElementName "edgeId" -xmlElementText $edgeId $return } end {} } function Get-NsxLoadBalancerPool { <# .SYNOPSIS Retrieves LoadBalancer Pools Profiles from the specified LoadBalancer. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A pool manages load balancer distribution methods and has a service monitor attached to it for health check parameters. Each Pool has one or more members. Prior to creating or updating a pool to add a member, a member spec describing the member needs to be created. This cmdlet retrieves LoadBalancer pools from the specified LoadBalancer. .EXAMPLE PS C:\> Get-NsxEdge | Get-NsxLoadBalancer | Get-NsxLoadBalancerPool #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancer $_ })] [System.Xml.XmlElement]$LoadBalancer, [Parameter (Mandatory = $true, ParameterSetName = "poolId")] [string]$PoolId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [string]$Name ) begin {} process { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $loadbalancer -query 'child::pool')) { if ( $PsBoundParameters.ContainsKey('Name')) { $pools = $loadbalancer.pool | Where-Object { $_.name -eq $Name } } elseif ( $PsBoundParameters.ContainsKey('PoolId')) { $pools = $loadbalancer.pool | Where-Object { $_.poolId -eq $PoolId } } else { $pools = $loadbalancer.pool } foreach ( $Pool in $Pools ) { $_Pool = $Pool.CloneNode($True) Add-XmlElement -xmlRoot $_Pool -xmlElementName "edgeId" -xmlElementText $LoadBalancer.edgeId $_Pool } } } end { } } function Remove-NsxLoadBalancerPool { <# .SYNOPSIS Removes a Pool from the specified Load Balancer. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A pool manages load balancer distribution methods and has a service monitor attached to it for health check parameters. Each Pool has one or more members. This cmdlet removes the specified pool from the Load Balancer pool and returns the updated LoadBalancer. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLoadBalancerPool $_ })] [System.Xml.XmlElement]$LoadBalancerPool, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $LoadBalancerPool.edgeId $poolId = $LoadBalancerPool.poolId #Get and remove the edgeId element $LoadBalancer = Get-NsxEdge -objectId $edgeId -Connection $connection | Get-NsxLoadBalancer $LoadBalancer.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LoadBalancer -query 'child::edgeId')) ) | Out-Null $PoolToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LoadBalancer -query "child::pool[poolId=`"$poolId`"]") if ( -not $PoolToRemove ) { throw "Pool $poolId is not defined on Load Balancer $edgeid." } $LoadBalancer.RemoveChild( $PoolToRemove ) | Out-Null $URI = "/api/4.0/edges/$edgeId/loadbalancer/config" $body = $LoadBalancer.OuterXml if ( $confirm ) { $message = "Pool removal is permanent." $question = "Proceed with removal of Pool $($poolId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Status "Removing pool $poolId" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Completed Get-NsxEdge -objectId $edgeId -Connection $connection | Get-NsxLoadBalancer } } end {} } function Get-NsxLoadBalancerPoolMember { <# .SYNOPSIS Retrieves the members of the specified LoadBalancer Pool. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A pool manages load balancer distribution methods and has a service monitor attached to it for health check parameters. Each Pool has one or more members. Prior to creating or updating a pool to add a member, a member spec describing the member needs to be created. This cmdlet retrieves the members of the specified LoadBalancer Pool. #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancerPool $_ })] [System.Xml.XmlElement]$LoadBalancerPool, [Parameter (Mandatory = $true, ParameterSetName = "MemberId")] [string]$MemberId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [string]$Name ) begin {} process { if ( $PsBoundParameters.ContainsKey('Name')) { $Members = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $LoadBalancerPool -query 'descendant::member') | Where-Object { $_.name -eq $Name } } elseif ( $PsBoundParameters.ContainsKey('MemberId')) { $Members = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $LoadBalancerPool -query 'descendant::member') | Where-Object { $_.memberId -eq $MemberId } } else { $Members = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $LoadBalancerPool -query 'descendant::member') } foreach ( $Member in $Members ) { $_Member = $Member.CloneNode($True) Add-XmlElement -xmlRoot $_Member -xmlElementName "edgeId" -xmlElementText $LoadBalancerPool.edgeId Add-XmlElement -xmlRoot $_Member -xmlElementName "poolId" -xmlElementText $LoadBalancerPool.poolId $_Member } } end { } } function Set-NsxLoadBalancerPoolMember { <# .SYNOPSIS Configures the state of the specified LoadBalancer Pool Member. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A pool manages load balancer distribution methods and has a service monitor attached to it for health check parameters. Each Pool has one or more members. Prior to creating or updating a pool to add a member, a member spec describing the member needs to be created. This cmdlet configures the state of the specified LoadBalancer Pool Member. .EXAMPLE Get-NsxEdge testedge | Get-NsxLoadBalancer | Get-NsxLoadBalancerPool pool1 | Get-NsxLoadBalancerPoolMember web01 | Set-NsxLoadBalancerPoolMember -state disabled Disable member web01 of pool1 on edge testedge .EXAMPLE Get-NsxEdge testedge | Get-NsxLoadBalancer | Get-NsxLoadBalancerPool pool1 | Get-NsxLoadBalancerPoolMember web01 | Set-NsxLoadBalancerPoolMember -state enabled Enable member web01 of pool1 on edge testedge .EXAMPLE Get-NsxEdge testedge | Get-NsxLoadBalancer | Get-NsxLoadBalancerPool pool1 | Get-NsxLoadBalancerPoolMember web01 | Set-NsxLoadBalancerPoolMember -state drain Drain member web01 of pool1 on edge testedge (Supported only on NSX 6.3.0 and above) .EXAMPLE Get-NsxEdge testedge | Get-NsxLoadBalancer | Get-NsxLoadBalancerPool | Get-NsxLoadBalancerPoolMember | Set-NsxLoadBalancerPoolMember -Weight 10 Set all members of all pools on edge testedge for weight 10 #> [CmdLetBinding(DefaultParameterSetName = "Default")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] #Pool member to be configured [ValidateScript( { ValidateLoadBalancerPoolMember $_ })] [System.Xml.XmlElement]$LoadBalancerPoolMember, [Parameter (Mandatory = $False, ParameterSetName = "LegacyConfirm")] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False, ParameterSetName = "Default")] #Disable Prompt for confirmation. [switch]$NoConfirm, [Parameter (Mandatory = $false)] [ValidateSet("enabled", "disabled", "drain")] [string]$state, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [int]$Weight, [Parameter (Mandatory = $false)] [ValidateRange(1, 65535)] [int]$Port, [Parameter (Mandatory = $false)] [ValidateRange(1, 65535)] [int]$MonitorPort, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [int]$MinimumConnections, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [int]$MaximumConnections, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { If ( $PSCmdlet.ParameterSetName -eq "LegacyConfirm") { Write-Warning "The -confirm switch is deprecated and will be removed in a future release. Use -NoConfirm instead." $NoConfirm = ( -not $confirm ) } if ($PSBoundParameters.ContainsKey("state") -and ($state -eq "drain") -and ([version]$Connection.version -lt [version]"6.3.0")) { throw "Setting a member state to drain requires NSX 6.3.0 or above." } } process { $edgeid = $LoadBalancerPoolMember.edgeId $poolid = $loadBalancerPoolMember.poolId $memberId = $LoadBalancerPoolMember.memberId $response = Invoke-NsxWebRequest -method "get" -URI "/api/4.0/edges/$edgeid/loadbalancer/config/pools/$poolid" [xml]$pool = $response.Content $member = Invoke-XpathQuery -QueryMethod SelectSingleNode -query "child::member[memberId=`"$memberId`"]" -Node $pool.pool if ($PSBoundParameters.ContainsKey("state")) { $member.condition = $state.toLower() } if ($PSBoundParameters.ContainsKey("weight")) { $member.weight = $weight.toString() } if ($PSBoundParameters.ContainsKey("port")) { $member.port = $port.toString() } if ($PSBoundParameters.ContainsKey("monitorPort")) { $member.monitorPort = $monitorPort.toString() } if ($PSBoundParameters.ContainsKey("MinimumConnections")) { $member.minConn = $MinimumConnections.ToString() } if ($PSBoundParameters.ContainsKey("MaximumConnections")) { $member.maxConn = $MaximumConnections.ToString() } #ToDo: Missing Confirm!!! $response = Invoke-NsxWebRequest -method "put" -URI "/api/4.0/edges/$edgeid/loadbalancer/config/pools/$poolid" -body $pool.outerxml $response = Invoke-NsxWebRequest -method "get" -URI "/api/4.0/edges/$edgeid/loadbalancer/config/pools/$poolid" [xml]$pool = $response.Content $member = Invoke-XpathQuery -QueryMethod SelectSingleNode -query "child::member[memberId=`"$memberId`"]" -Node $pool.pool $member } end { } } function Add-NsxLoadBalancerPoolMember { <# .SYNOPSIS Adds a new Pool Member to the specified Load Balancer Pool. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A pool manages load balancer distribution methods and has a service monitor attached to it for health check parameters. Each Pool has one or more members. This cmdlet adds a new member to the specified LoadBalancer Pool and returns the updated Pool. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | get-nsxloadbalancerpool pool1 | Add-NsxLoadBalancerPoolMember -Name test -ipaddress 1.2.3.4 -Port 80 Adds the ipaddress to LB pool1 on edge Edge01 .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | get-nsxloadbalancerpool pool1 | Add-NsxLoadBalancerPoolMember -Name test -Member (get-vm web01) -Port 80 Adds the vm object web01 to LB pool1 on edge Edge01 .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | get-nsxloadbalancerpool pool1 | Add-NsxLoadBalancerPoolMember -Name test -Member (get-logicalswitch WebLS) -Port 80 Adds the NSX object WebLS LB pool1 on edge Edge01 #> [CmdLetBinding(DefaultParameterSetName = "IpAddress")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancerPool $_ })] [System.Xml.XmlElement]$LoadBalancerPool, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true, ParameterSetName = "IpAddress")] [ValidateNotNullOrEmpty()] [IpAddress]$IpAddress, [Parameter (Mandatory = $true, ParameterSetName = "Member")] [ValidateScript( { ValidateSecurityGroupMember $_ })] [object]$Member, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [int]$Weight = 1, [Parameter (Mandatory = $true)] [ValidateRange(1, 65535)] [int]$Port, [Parameter (Mandatory = $false)] [ValidateRange(1, 65535)] [int]$MonitorPort = $port, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [int]$MinimumConnections = 0, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [int]$MaximumConnections = 0, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Create private xml element $_LoadBalancerPool = $LoadBalancerPool.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_LoadBalancerPool.edgeId $_LoadBalancerPool.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LoadBalancerPool -query 'descendant::edgeId')) ) | Out-Null [System.XML.XMLElement]$xmlMember = $_LoadBalancerPool.OwnerDocument.CreateElement("member") $_LoadBalancerPool.appendChild($xmlMember) | Out-Null Add-XmlElement -xmlRoot $xmlMember -xmlElementName "name" -xmlElementText $Name if ( $PSCmdlet.ParameterSetName -eq "ipaddress" ) { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "ipAddress" -xmlElementText $IpAddress } else { if ($Member -is [System.Xml.XmlElement] ) { $MemberMoref = $Member.objectId $MemberName = $Member.name } elseif ( ($Member -is [string]) -and ($Member -match "^vm-\d+$|^resgroup-\d+$|^dvportgroup-\d+$" )) { $MemberMoref = $Member $MemberName = $Member } elseif ( ($Member -is [string] ) -and ( [guid]::tryparse(($Member -replace ".\d{3}$", ""), [ref][guid]::Empty)) ) { $MemberMoref = $Member $MemberName = $Member } elseif (( $Member -is [string]) -and ( $NsxMemberTypes -contains ($Member -replace "-\d+$") ) ) { $MemberMoref = $Member $MemberName = $Member } elseif ( $Member -is [VMware.VimAutomation.ViCore.Interop.V1.VirtualDevice.NetworkAdapterInterop] ) { #See NSX API guide 'Attach or Detach a Virtual Machine from a Logical Switch' for #how to construct NIC id. $vmUuid = ($Member.parent | Get-View).config.instanceuuid $MemberMoref = "$vmUuid.$($Member.id.substring($Member.id.length-3))" $MemberName = $Member } elseif (( $Member -is [VMware.VimAutomation.ViCore.Interop.V1.VIObjectInterop]) -and ( $NsxMemberTypes -contains $Member.ExtensionData.MoRef.Type)) { $MemberMoref = $Member.ExtensionData.MoRef.Value $MemberName = $Member.name } else { throw "Invalid member specified $($Member)" } #Create a new member node Add-XmlElement -xmlRoot $xmlMember -xmlElementName "groupingObjectId" -xmlElementText $MemberMoref Add-XmlElement -xmlRoot $xmlMember -xmlElementName "groupingObjectName" -xmlElementText $MemberName } Add-XmlElement -xmlRoot $xmlMember -xmlElementName "weight" -xmlElementText $Weight Add-XmlElement -xmlRoot $xmlMember -xmlElementName "port" -xmlElementText $port Add-XmlElement -xmlRoot $xmlMember -xmlElementName "monitorPort" -xmlElementText $monitorPort Add-XmlElement -xmlRoot $xmlMember -xmlElementName "minConn" -xmlElementText $MinimumConnections Add-XmlElement -xmlRoot $xmlMember -xmlElementName "maxConn" -xmlElementText $MaximumConnections $URI = "/api/4.0/edges/$edgeId/loadbalancer/config/pools/$($_LoadBalancerPool.poolId)" $body = $_LoadBalancerPool.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Status "Pool config for $($_LoadBalancerPool.poolId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed #Get updated pool $URI = "/api/4.0/edges/$edgeId/loadbalancer/config/pools/$($_LoadBalancerPool.poolId)" Write-Progress -Activity "Retrieving Updated Pool for $($EdgeId)" -Status "Pool $($_LoadBalancerPool.poolId)" $return = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $Pool = $return.pool Add-XmlElement -xmlRoot $Pool -xmlElementName "edgeId" -xmlElementText $edgeId $Pool } end {} } function Remove-NsxLoadBalancerPoolMember { <# .SYNOPSIS Removes a Pool Member from the specified Load Balancer Pool. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A pool manages load balancer distribution methods and has a service monitor attached to it for health check parameters. Each Pool has one or more members. This cmdlet removes the specified member from the specified pool and returns the updated Pool. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLoadBalancerPoolMember $_ })] [System.Xml.XmlElement]$LoadBalancerPoolMember, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Store the edgeId and remove it from the XML as we need to post it... $MemberId = $LoadBalancerPoolMember.memberId $edgeId = $LoadBalancerPoolMember.edgeId $poolId = $LoadBalancerPoolMember.poolId #Get and remove the edgeId and poolId elements $LoadBalancer = Get-NsxEdge -objectId $edgeId -Connection $connection | Get-NsxLoadBalancer $LoadBalancer.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LoadBalancer -query 'child::edgeId')) ) | Out-Null $LoadBalancerPool = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $loadbalancer -query "child::pool[poolId=`"$poolId`"]") $MemberToRemove = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LoadBalancerPool -query "child::member[memberId=`"$MemberId`"]") if ( -not $MemberToRemove ) { throw "Member $MemberId is not a member of pool $PoolId." } $LoadBalancerPool.RemoveChild( $MemberToRemove ) | Out-Null $URI = "/api/4.0/edges/$edgeId/loadbalancer/config" $body = $LoadBalancer.OuterXml if ( $confirm ) { $message = "Pool Member removal is permanent." $question = "Proceed with removal of Pool Member $($memberId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Status "Pool config for $poolId" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Completed Get-NsxEdge -objectId $edgeId -Connection $connection | Get-NsxLoadBalancer | Get-NsxLoadBalancerPool -PoolId $poolId } } end {} } function Get-NsxLoadBalancerVip { <# .SYNOPSIS Retrieves the Virtual Servers configured on the specified LoadBalancer. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A Virtual Server binds an IP address (must already exist on an ESG iNterface as either a Primary or Secondary Address) and a port to a LoadBalancer Pool and Application Profile. This cmdlet retrieves the configured Virtual Servers from the specified Load Balancer. #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancer $_ })] [System.Xml.XmlElement]$LoadBalancer, [Parameter (Mandatory = $true, ParameterSetName = "VirtualServerId")] [string]$VirtualServerId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] [string]$Name ) begin {} process { if ( $PsBoundParameters.ContainsKey('Name')) { $Vips = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $LoadBalancer -query 'descendant::virtualServer') | Where-Object { $_.name -eq $Name } } elseif ( $PsBoundParameters.ContainsKey('MemberId')) { $Vips = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $LoadBalancer -query 'descendant::virtualServer') | Where-Object { $_.virtualServerId -eq $VirtualServerId } } else { $Vips = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $LoadBalancer -query 'descendant::virtualServer') } foreach ( $Vip in $Vips ) { $_Vip = $VIP.CloneNode($True) Add-XmlElement -xmlRoot $_Vip -xmlElementName "edgeId" -xmlElementText $LoadBalancer.edgeId $_Vip } } end { } } function Add-NsxLoadBalancerVip { <# .SYNOPSIS Adds a new LoadBalancer Virtual Server to the specified ESG. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A Virtual Server binds an IP address (must already exist on an ESG iNterface as either a Primary or Secondary Address) and a port to a LoadBalancer Pool and Application Profile. This cmdlet creates a new Load Balancer VIP. .EXAMPLE Example1: Need to create member specs for each of the pool members first PS C:\> $WebVip = Get-NsxEdge Edge01 | New-NsxLoadBalancerVip -Name WebVip -Description "Test Creating a VIP" -IpAddress $edge_uplink_ip -Protocol http -Port 80 -ApplicationProfile $AppProfile -DefaultPool $WebPool -AccelerationEnabled #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancer $_ })] [System.Xml.XmlElement]$LoadBalancer, [Parameter (Mandatory = $True)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $False)] [ValidateNotNull()] [string]$Description = "", [Parameter (Mandatory = $True)] [ValidateNotNullOrEmpty()] [IpAddress]$IpAddress, [Parameter (Mandatory = $True)] [ValidateSet("http", "https", "tcp", "udp")] [string]$Protocol, [Parameter (Mandatory = $True)] [ValidateRange(1, 65535)] [int]$Port, [Parameter (Mandatory = $False)] [ValidateNotNullorEmpty()] [switch]$Enabled = $true, [Parameter (Mandatory = $true)] [ValidateScript( { ValidateLoadBalancerApplicationProfile $_ })] [System.Xml.XmlElement]$ApplicationProfile, [Parameter (Mandatory = $true)] [ValidateScript( { ValidateLoadBalancerPool $_ })] [System.Xml.XmlElement]$DefaultPool, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [switch]$AccelerationEnabled = $True, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [int]$ConnectionLimit = 0, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [int]$ConnectionRateLimit = 0, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LoadBalancer = $LoadBalancer.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_LoadBalancer.edgeId $_LoadBalancer.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LoadBalancer -query 'descendant::edgeId')) ) | Out-Null if ( -not $_LoadBalancer.enabled -eq 'true' ) { Write-Warning "Load Balancer feature is not enabled on edge $($edgeId). Use Set-NsxLoadBalancer -Enabled to enable." } [System.XML.XMLElement]$xmlVIip = $_LoadBalancer.OwnerDocument.CreateElement("virtualServer") $_LoadBalancer.appendChild($xmlVIip) | Out-Null Add-XmlElement -xmlRoot $xmlVIip -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlVIip -xmlElementName "description" -xmlElementText $Description Add-XmlElement -xmlRoot $xmlVIip -xmlElementName "enabled" -xmlElementText $Enabled Add-XmlElement -xmlRoot $xmlVIip -xmlElementName "ipAddress" -xmlElementText $IpAddress Add-XmlElement -xmlRoot $xmlVIip -xmlElementName "protocol" -xmlElementText $Protocol Add-XmlElement -xmlRoot $xmlVIip -xmlElementName "port" -xmlElementText $Port Add-XmlElement -xmlRoot $xmlVIip -xmlElementName "connectionLimit" -xmlElementText $ConnectionLimit Add-XmlElement -xmlRoot $xmlVIip -xmlElementName "connectionRateLimit" -xmlElementText $ConnectionRateLimit Add-XmlElement -xmlRoot $xmlVIip -xmlElementName "applicationProfileId" -xmlElementText $ApplicationProfile.applicationProfileId Add-XmlElement -xmlRoot $xmlVIip -xmlElementName "defaultPoolId" -xmlElementText $DefaultPool.poolId Add-XmlElement -xmlRoot $xmlVIip -xmlElementName "accelerationEnabled" -xmlElementText $AccelerationEnabled $URI = "/api/4.0/edges/$($EdgeId)/loadbalancer/config" $body = $_LoadBalancer.OuterXml Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Status "Load Balancer Config" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $EdgeId" -Completed $UpdatedLB = Get-NsxEdge -objectId $EdgeId -Connection $connection | Get-NsxLoadBalancer $UpdatedLB } end {} } function Remove-NsxLoadBalancerVip { <# .SYNOPSIS Removes a VIP from the specified Load Balancer. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A Virtual Server binds an IP address (must already exist on an ESG iNterface as either a Primary or Secondary Address) and a port to a LoadBalancer Pool and Application Profile. This cmdlet remove a VIP from the specified Load Balancer. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLoadBalancerVip $_ })] [System.Xml.XmlElement]$LoadBalancerVip, [Parameter (Mandatory = $False)] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Store the virtualserverid and edgeId $VipId = $LoadBalancerVip.VirtualServerId $edgeId = $LoadBalancerVip.edgeId $URI = "/api/4.0/edges/$edgeId/loadbalancer/config/virtualservers/$VipId" if ( $confirm ) { $message = "VIP removal is permanent." $question = "Proceed with removal of VIP $VipID on Edge $($edgeId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Status "Removing VIP $VipId" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed } } end {} } function Get-NsxLoadBalancerStats { <# .SYNOPSIS Retrieves NSX Edge Load Balancer statistics for the specified load balancer .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. This cmdlet retrieves NSX Edge Load Balancer statistics from the specified enabled NSX loadbalancer. .EXAMPLE Get-nsxedge edge01 | Get-NsxLoadBalancer | Get-NsxLoadBalancerStats Retrieves the LB stats for the LB service on Edge01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] #Load Balancer from which to retrieve stats. Must be enabled. [ValidateScript( { ValidateLoadBalancer $_ })] [System.Xml.XmlElement]$LoadBalancer, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Test that LB is enabled (otherwise there are no results.) if ( $LoadBalancer.Enabled -ne 'true' ) { Throw "Load balancer feature is not enabled on $($LoadBalancer.EdgeId)" } $URI = "/api/4.0/edges/$($LoadBalancer.EdgeId)/loadbalancer/statistics" [system.xml.xmldocument]$response = Invoke-NsxRestMethod -method "GET" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query "child::loadBalancerStatusAndStats")) { $response.loadBalancerStatusAndStats } } end {} } function Get-NsxLoadBalancerApplicationRule { <# .SYNOPSIS Retrieves LoadBalancer Application Rules from the specified LoadBalancer. .DESCRIPTION Retrieves LoadBalancer Application Rules from the specified LoadBalancer. You can write an application rule to directly manipulate and manage IP application traffic. .EXAMPLE Get-NsxEdge | Get-NsxLoadBalancer | Get-NsxLoadBalancerApplicationRule Retrieves all Application Rules across all NSX Edges. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Get-NsxLoadBalancerApplicationRule Retrieves all Application Rules the NSX Edge named Edge01. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Get-NsxLoadBalancerApplicationRule -name AR-Redirect-VMware Retrieves the Application Rule named AR-Redirect-VMware on NSX Edge named Edge01. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Get-NsxLoadBalancerApplicationRule -objectId applicationRule-2 Retrieves the Application Rule on NSX Edge with the objectId of applicationRule-2. #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancer $_ })] [System.Xml.XmlElement]$LoadBalancer, [Parameter (Mandatory = $false, ParameterSetName = "ObjectId")] [string]$ObjectId, [Parameter (Mandatory = $false, Position = 1, ParameterSetName = "Name")] [string]$Name ) begin { } process { if ( -not ($PsBoundParameters.ContainsKey("ObjectId"))) { if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LoadBalancer -query "child::applicationRule")) { if ($PsBoundParameters.ContainsKey("Name")) { $LoadBalancer.applicationRule | Where-Object { $_.name -eq $Name } } else { $LoadBalancer.applicationRule } } } else { if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $LoadBalancer -query "child::applicationRule/applicationRuleId")) { $LoadBalancer.applicationRule | Where-Object { $_.applicationRuleId -eq $ObjectId } } } } end {} } function New-NsxLoadBalancerApplicationRule { <# .SYNOPSIS Retrieves LoadBalancer Application Rules from the specified LoadBalancer. .DESCRIPTION Retrieves LoadBalancer Application Rules from the specified LoadBalancer. You can write an application rule to directly manipulate and manage IP application traffic. .EXAMPLE Get-NsxEdge | Get-NsxLoadBalancer | New-NsxLoadBalancerApplicationRule -name AR-Redirect-VMware -script $script Applies a new Application Rule across all NSX Edges. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | New-NsxLoadBalancerApplicationRule -name AR-Redirect-VMware -script $script Applies a new Application Rule to the defined NSX Edge. #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancer $_ })] [System.Xml.XmlElement]$LoadBalancer, [Parameter (Mandatory = $True)] [string]$Script, [Parameter (Mandatory = $True, Position = 1)] [string]$Name, [Parameter (Mandatory = $False)] #PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $LoadBalancer.edgeId if ( -not $LoadBalancer.enabled -eq 'true' ) { write-warning "Load Balancer feature is not enabled on edge $($edgeId). Use Set-NsxLoadBalancer -EnableLoadBalancing to enable." } #Create a new XML document. Use applicationRule as root. [System.XML.XmlDocument]$xmldoc = New-Object System.XML.XmlDocument [System.XML.XMLElement]$xmlAr = $xmldoc.CreateElement("applicationRule") [void]$xmldoc.appendChild($xmlAr) # Create children and add to $xmlXR Add-XmlElement -xmlRoot $xmlAr -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlAr -xmlElementName "script" -xmlElementText $Script #Construct Rest Call $URI = "/api/4.0/edges/$($EdgeId)/loadbalancer/config/applicationrules" $body = $xmlAr.OuterXml $Response = Invoke-NsxWebRequest -method "POST" -URI $URI -body $body -connection $Connection [System.XML.XmlDocument]$ApplicationRule = Invoke-NsxRestMethod -method "GET" -URI $Response.Headers.Location if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $ApplicationRule -query "child::applicationRule")) { $ApplicationRule.applicationRule } } end {} } ######## ######## # Service Composer functions function Get-NsxSecurityPolicyHighestUsedPrecedence { <# .SYNOPSIS Retrieves the highest precedence number defined on any security policy. .DESCRIPTION An NSX Security Policy is a set of Endpoint, firewall, and network introspection services that can be applied to a security group. This cmdlet returns the highest precedence number defined on any Security Policy. This is primarily useful when creating a new policy. .EXAMPLE Get-NsxSecurityPolicyHighestUsedPrecedence Precedence ---------- 3300 Retrieves the highest precedence number used. Convention is to add 1000 to this when creating a new policy to leave 'gaps' in to which future policy could be inserted. #> [CmdLetBinding()] param ( [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) $URI = "/api/2.0/services/policy/securitypolicy/maxprecedence" $return = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( -not ($return -match "\d*")) { throw "Unexpected result $return from call to get highest used precedence. Return value should be a number." } [pscustomobject]@{"Precedence" = [int]$return } } function Get-NsxSecurityPolicy { <# .SYNOPSIS Retrieves an NSX Security Policy. .DESCRIPTION An NSX Security Policy is a set of Endpoint, firewall, and network introspection services that can be applied to a security group. This cmdlet returns Security Policy objects. .EXAMPLE Get-NsxSecurityPolicy SecPolicy_WebServers Retrieves the security policy called SecPolicy_WebServers .EXAMPLE Get-NsxSecurityGroup WebApp1WebServers | Get-NsxSecurityPolicy Retrieves all security policies applied to the security Group WeApp1WebServers #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ParameterSetName = "objectId")] # Set Security Policies by objectId [string]$ObjectId, [Parameter (Mandatory = $false, ParameterSetName = "Name", Position = 1)] # Get Security Policies by name [string]$Name, [Parameter (Mandatory = $true, ParameterSetName = "SecurityGroup", ValueFromPipeline = $true)] # Get Security Policies applied to the specified Security Group [ValidateScript( { ValidateSecurityGroup $_ })] [System.Xml.XmlElement]$SecurityGroup, [Parameter (Mandatory = $false)] # Include the readonly (system) Security Policies in results. [alias("ShowHidden")] [switch]$IncludeHidden = $False, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { switch ( $PSCmdlet.ParameterSetName ) { "Name" { #Get all Security Policies and optionally filter on Name $URI = "/api/2.0/services/policy/securitypolicy/all" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $securityPolicies = @() if ($response.securityPolicies | Get-Member -MemberType Properties -Name pagingInfo) { $defaultPageSize = 1024 $itemIndex = 0 $startingIndex = 0 $pagingInfo = $response.securityPolicies.pagingInfo if ( [int]$paginginfo.totalCount -ne 0 ) { Write-Debug "$($MyInvocation.MyCommand.Name) : SecurityPolicy count non zero" do { Write-Debug "$($MyInvocation.MyCommand.Name) : In paging loop. PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" while (($itemindex -lt ([int]$paginginfo.pagesize + $startingIndex)) -and ($itemIndex -lt [int]$paginginfo.totalCount )) { Write-Debug "$($MyInvocation.MyCommand.Name) : In Item Processing Loop: ItemIndex: $itemIndex" Write-Debug "$($MyInvocation.MyCommand.Name) : $(@($response.securityPolicies.securityPolicy)[($itemIndex - $startingIndex)].objectId)" $securityPolicies += @($response.securityPolicies.securityPolicy)[($itemIndex - $startingIndex)] $itemIndex += 1 } Write-Debug "$($MyInvocation.MyCommand.Name) : Out of item processing - PagingInfo: PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" if ( [int]$paginginfo.totalcount -gt $itemIndex) { Write-Debug "$($MyInvocation.MyCommand.Name) : PagingInfo: PageSize: $($pagingInfo.PageSize), StartIndex: $($paginginfo.startIndex), TotalCount: $($paginginfo.totalcount)" $startingIndex += $defaultPageSize $URI = "/api/2.0/services/policy/securitypolicy/all?pageSize=$defaultPageSize&startIndex=$startingIndex" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $pagingInfo = $response.securityPolicies.pagingInfo } } until ( [int]$paginginfo.totalcount -le $itemIndex ) Write-Debug "$($MyInvocation.MyCommand.Name) : Completed page processing: ItemIndex: $itemIndex" } } else { $securityPolicies = $response.securityPolicies.securitypolicy } if ( $PSBoundParameters.ContainsKey("Name") ) { $FinalSecPol = $securityPolicies | Where-Object { $_.name -eq $Name } } else { $FinalSecPol = $securityPolicies } } "objectId" { #Just getting a single Security policy $URI = "/api/2.0/services/policy/securitypolicy/$objectId" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $FinalSecPol = $response.securityPolicy } "SecurityGroup" { $URI = "/api/2.0/services/policy/securitygroup/$($SecurityGroup.objectId)/securitypolicies" $response = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection if ( Invoke-XpathQuery -Node $response -QueryMethod selectSingleNode -query "child::securityPolicies/securityPolicy" ) { $FinalSecPol = $response.securityPolicies.SecurityPolicy } else { $FinalSecPol = $null } } } if ( -not $IncludeHidden ) { foreach ( $CurrSecPol in $FinalSecPol ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $CurrSecPol -query 'child::extendedAttributes/extendedAttribute')) { $hiddenattr = $CurrSecPol.extendedAttributes.extendedAttribute | Where-Object { $_.name -eq 'isHidden' } if ( -not ($hiddenAttr.Value -eq 'true')) { $CurrSecPol } } else { $CurrSecPol } } } else { $FinalSecPol } } end {} } function New-NsxSecurityPolicy { <# .SYNOPSIS Create a new NSX Security Policy. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The three categories of rules that can be included in a Security Policy are: - Guest Introspection - data security, anti-virus, and vulnerability management and rules based on third party Guest Introspection capability. - Firewall rules - creates appropriate distributed firewall rules when the policy is applied to a security group. - Network introspection services - Thirdparty firewall, IPS/IDS etc. New-NsxSecurityPolicy enables creation of a security policy that includes rules from any of the three categories. For Network Introspection, and some Guest Introspection rules, the appropriate service defintion and service policies must already be defined within NSX to allow this. .EXAMPLE New-NsxSecurityPolicy -Name EmptyPolicy Creates an empty Security Policy with no rules. .EXAMPLE $sg1 = Get-NsxSecurityGroup "All Management Servers" PS C:\> $http = Get-NsxService -Localonly | Where { $_.name -eq 'HTTP' } PS C:\> $https = Get-NsxService -Localonly | Where { $_.name -eq 'HTTPS' } PS C:\> $ssh = Get-NsxService -Localonly | Where { $_.name -eq 'SSH' } PS C:\> $inboundwebrule = New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow Inbound Web" ` -Description "Allow inbound web traffic" ` -Service $http,$https -Source Any -EnableLogging -Action allow PS C:\> $inboundsshrule = New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow SSH from Management" ` -Description "Allow inbound ssh traffic from management servers" ` -Service $ssh -Source $sg1 -EnableLogging -Action allow PS C:\> New-NsxSecurityPolicy -Name WebServers -Description "Generic Web Server Policy" ` -FirewallRuleSpec $inboundwebrule, $inboundsshrule Creates a security policy called WebServers that defines two firewall rules. The specific steps to accomplish this are as follows: - Retrieves an existing security group that represents management servers from which SSH traffic will originate. - Retrieves existing NSX services defining HTTP, HTTPS and SSH and stores them in appropriate variables. - Creates two FirewallRule Specs that use the group and services collected above and stores them in appropriate variables. - Creates a Security Policy using the two precreated firewall rule specs. .EXAMPLE $ServiceDefinition = Get-NsxServiceDefinition -Name "MyThirdPartyFirewall" PS C:\> $ServicePolicy = $ServiceDefinition | Get-NsxServiceProfile "FirewallProfile" PS C:\> $https = Get-NsxService -Localonly | Where { $_.name -eq 'HTTPS' } PS C:\> $RedirectRule = New-NsxSecurityPolicyNetworkIntrospectionSpec -Name "MyThirdPartyRedirectRule" ` -ServiceProfile $ServicePolicy -Service $https -source Any PS C:\> New-NsxSecurityPolicy -Name HTTPSRedirect -Description "Redirect HTTPS to ThirdParty Firewall" ` -NetworkIntrospectionSpec $RedirectRule Creates a security policy called ThirdPartyRedirect that defines a single network introspection rule to redirect traffic to a thirdparty firewall service. The specific steps to accomplish this are as follows: - Retrieves an existing Service Policy that is defined as part of the third party firewall production integration with NSX. - Retrieves an existing NSX service defining HTTPS and stores it in an appropriate variable. - Creates a Network Introspection rule spec that uses the policy collected above, that matches HTTPS traffic from any source and stores it in an appropriate variable. - Creates a Security Policy using the precreated network introspection rule spec. .EXAMPLE $ServiceDefinition = Get-NsxServiceDefinition -Name "MyThirdPartyEndpoint" PS C:\> $ServicePolicy = $ServiceDefinition | Get-NsxServiceProfile "Profile1" PS C:\> $Endpointrule = New-NsxSecurityPolicyGuestIntrospectionSpec -Name "MyThirdPartyEndpointRule" ` -ServiceDefinition $ServiceDefinition -ServiceProfile $ServicePolicy PS C:\> New-NsxSecurityPolicy -Name ThirdPartyEndpoint -Description "Apply ThirdParty Introspection" ` -GuestIntrospection $EndpointRule Creates a security policy called ThirdPartyEndpoint that defines a single guest introspection rule to apply. The specific steps to accomplish this are as follows: - Retrieves an existing Service Policy that is defined as part of the third party endpoint integration with NSX. - Creates a Guest Introspection rule spec that uses the policy collected above and stores it in an appropriate variable. - Creates a Security Policy using the precreated guest introspection rule spec. .EXAMPLE PS C:\> $Endpointrule = New-NsxSecurityPolicyGuestIntrospectionSpec -Name "MyThirdPartyEndpointRule" ` -Servicetype AntiVirus PS C:\> New-NsxSecurityPolicy -Name AntiVirusEndpoint -Description "Antivirus Endpoint" ` -GuestIntrospection $EndpointRule Creates a security policy called AntiVirusEndpoint that defines a single AntiVirus guest introspection rule to apply. The specific steps to accomplish this are as follows: - Creates a Guest Introspection AntiVirus rule spec and stores it in an appropriate variable. - Creates a Security Policy using the precreated guest introspection rule spec. .EXAMPLE PS C:\> $Endpointrule = New-NsxSecurityPolicyGuestIntrospectionSpec -Name "MyThirdPartyEndpointRule" ` -Servicetype FileIntegrityMonitoring PS C:\> New-NsxSecurityPolicy -Name FileIntegrityEndpoint -Description "FileIntegrity Endpoint" ` -GuestIntrospection $EndpointRule Creates a security policy called FileIntegrityEndpoint that defines a single FileIntegrity guest introspection rule to apply. The specific steps to accomplish this are as follows: - Creates a Guest Introspection FileIntegrity rule spec and stores it in an appropriate variable. - Creates a Security Policy using the precreated guest introspection rule spec. .EXAMPLE PS C:\> $Endpointrule = New-NsxSecurityPolicyGuestIntrospectionSpec -Name "MyThirdPartyEndpointRule" ` -Servicetype VulnerabilityManagement PS C:\> New-NsxSecurityPolicy -Name VulnerabilityMgmtEndpoint -Description "VulnMgmt Endpoint" ` -GuestIntrospection $EndpointRule Creates a security policy called VulnerabilityMgmtEndpoint that defines a single VulnerabilityManagementt guest introspection rule to apply. The specific steps to accomplish this are as follows: - Creates a Guest Introspection VulnerabilityManagement rule spec and stores it in an appropriate variable. - Creates a Security Policy using the precreated guest introspection rule spec. #> [CmdletBinding()] param ( [Parameter (Mandatory = $true)] # The name of the newly created policy [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] # The description of the newly created policy [ValidateNotNull()] [string]$Description, [Parameter (Mandatory = $false)] # Security Policy Firewall Rule Spec as created by New-NsxSecurityPolicyFirewallRuleSpec [ValidateScript( { ValidateSecPolFwSpec $_ })] [System.Xml.XmlElement[]]$FirewallRuleSpec, [Parameter (Mandatory = $false)] # Guest Introspection Rule Spec as created by New-NsxSecurityPolicyGuestIntrospectionSpec [ValidateScript( { ValidateSecPolGiSpec $_ })] [System.Xml.XmlElement[]]$GuestIntrospectionSpec, [Parameter (Mandatory = $false)] # Network Introspection Rule Spec as created by New-NsxSecurityPolicyNetworkIntrospectionSpec [ValidateScript( { ValidateSecPolNiSpec $_ })] [System.Xml.XmlElement[]]$NetworkIntrospectionSpec, [Parameter (Mandatory = $false)] # Return only the objectId of the newly create policy (avoids an aditional get to the API to retrieve the newly created object) [switch]$ReturnObjectIdOnly = $false, [Parameter (Mandatory = $false)] # Manually define the precedence number of the newly created policy. This defaults to the highest currently inuse precedence + 1000 (like the UI) [int]$Precedence, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { #Get current precedence and add 1000 if ( -not ($PSBoundParameters.ContainsKey("Precedence"))) { $Precedence = (Get-NsxSecurityPolicyHighestUsedPrecedence).Precedence + 1000 } } process { #Creating the XML Document and root elem for Security Policy [System.XML.XMLDocument]$xmlDoc = New-Object System.XML.XMLDocument [System.XML.XMLElement]$SecurityPolicy = $xmlDoc.CreateElement("securityPolicy") $null = $xmlDoc.appendChild($SecurityPolicy) Add-XmlElement -xmlRoot $SecurityPolicy -xmlElementName "name" -xmlElementText $Name if ( $PSBoundPArameters.ContainsKey("Description")) { Add-XmlElement -xmlRoot $SecurityPolicy -xmlElementName "description" -xmlElementText $Description } Add-XmlElement -xmlRoot $SecurityPolicy -xmlElementName "precedence" -xmlElementText $Precedence #Create the firewall category actionsByCategory Elem if required if ($PSBoundParameters.ContainsKey("FirewallRuleSpec")) { $xmlFwActionsByCategory = $xmlDoc.CreateElement("actionsByCategory") $null = $SecurityPolicy.appendChild($xmlFwActionsByCategory) Add-XmlElement -xmlRoot $xmlFwActionsByCategory -xmlElementName "category" -xmlElementText "firewall" foreach ($rule in $FirewallRuleSpec) { #Import the new fw node $null = $xmlFwActionsByCategory.AppendChild($xmlFwActionsByCategory.OwnerDocument.ImportNode($rule, $true)) } } #Create the endpointSecurityAction actionsByCategory Elem if required. if ( $PSBoundParameters.ContainsKey("GuestIntrospectionSpec")) { $xmlEndpointActionsByCategory = $xmlDoc.CreateElement("actionsByCategory") $null = $SecurityPolicy.appendChild($xmlEndpointActionsByCategory) Add-XmlElement -xmlRoot $xmlEndpointActionsByCategory -xmlElementName "category" -xmlElementText "endpoint" foreach ($rule in $GuestIntrospectionSpec) { #Import the new GI node $null = $xmlEndpointActionsByCategory.AppendChild($xmlEndpointActionsByCategory.OwnerDocument.ImportNode($rule, $true)) } } #Create the trafficSteeringSecurityAction actionsByCategory Elem if required. if ( $PSBoundParameters.ContainsKey("NetworkIntrospectionSpec")) { $xmlNetworkIntrospectionActionsByCategory = $xmlDoc.CreateElement("actionsByCategory") $null = $SecurityPolicy.appendChild($xmlNetworkIntrospectionActionsByCategory) Add-XmlElement -xmlRoot $xmlNetworkIntrospectionActionsByCategory -xmlElementName "category" -xmlElementText "traffic_steering" foreach ($rule in $NetworkIntrospectionSpec) { #Import the new NI node $null = $xmlNetworkIntrospectionActionsByCategory.AppendChild($xmlNetworkIntrospectionActionsByCategory.OwnerDocument.ImportNode($rule, $true)) } } #Do the post $body = $SecurityPolicy.OuterXml $URI = "/api/2.0/services/policy/securitypolicy" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection if ($response.StatusCode -eq "201") { if ($ReturnObjectIdOnly) { $response.content } else { Get-NsxSecurityPolicy -ObjectId $response.content -Connection $connection } } } end {} } function Set-NsxSecurityPolicy { <# .SYNOPSIS Updates the specified NSX Security Policy. .DESCRIPTION An NSX Security Policy is a set of Endpoint, firewall, and network introspection services that can be applied to a security group. This cmdlet re-configures the specified security policy. .EXAMPLE Get-NsxSecurityPolicy TestSP | Set-NsxSecurityPolicy -weight 10000 Reconfigure the weight of an existing policy. .EXAMPLE Get-NsxSecurityPolicy TestSP | Set-NsxSecurityPolicy -name NewPol Reconfigure the name of an existing policy. .EXAMPLE $sp = Get-NsxSecurityPolicy UberPol Get-NsxSecurityPolicy TestSP | Set-NsxSecurityPolicy -InheritPolicy $sp Configure TestSP to inherit the policy UberPol. .EXAMPLE Get-NsxSecurityPolicy TestSP | Set-NsxSecurityPolicy -DisableInheritance Disable policy inheritance on TestSP. .EXAMPLE $sp = Get-NsxSecurityPolicy TestSP PS C:\> $sp.description = "New description" PS C:\> Set-NsxSecurityPolicy -Policy $sp Retrieve and existing policy, update an XML element manually and put the updated XML back. Any valid XML changes can be pushed this way. #> [CmdletBinding(DefaultParameterSetName = "XML")] param ( [Parameter(Mandatory = $true, ValueFromPipeLine = $true)] # Security Policy object to update $Policy, [Parameter()] # Disable confirmation prompt [switch]$NoConfirm, [Parameter(ParameterSetName = "Default")] # Configure the policies name [ValidateNotNullorEmpty()] [string]$Name, [Parameter(ParameterSetName = "Default")] # Configure the policies description [ValidateNotNullorEmpty()] [string]$Description, [Parameter(ParameterSetName = "Default")] # Configure inheritance for the specified policy [ValidateScript( { ValidateSecurityPolicy $_ })] [object]$InheritPolicy, [Parameter(ParameterSetName = "Default")] # Disable inheritance for the specified policy [switch]$DisableInheritance, [Parameter(ParameterSetName = "Default")] # Configure the policies weight (precedence) [ValidateNotNullorEmpty()] [Alias("Precedence")] [string]$Weight, [Parameter(Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) Begin {} Process { #Clone the node to avoid modifying the original $_Policy = $Policy.CloneNode($true) # Update Name if ( $PSBoundParameters.ContainsKey("Name")) { if ( Invoke-XpathQuery -Node $_Policy -QueryMethod SelectSingleNode -query "child::name" ) { $_Policy.Name = $name } else { Add-XmlElement -xmlRoot $_Policy -xmlElementName "name" -xmlElementText $name } } # Update Description if ( $PSBoundParameters.ContainsKey("Description")) { if ( Invoke-XpathQuery -Node $_Policy -QueryMethod SelectSingleNode -query "child::description" ) { $_Policy.description = $description } else { Add-XmlElement -xmlRoot $_Policy -xmlElementName "description" -xmlElementText $description } } # Update Weight (precedence) if ( $PSBoundParameters.ContainsKey("Weight")) { if ( Invoke-XpathQuery -Node $_Policy -QueryMethod SelectSingleNode -query "child::precedence" ) { $_Policy.precedence = $weight } else { Add-XmlElement -xmlRoot $_Policy -xmlElementName "precedence" -xmlElementText $weight } } # Disable inheritance if ( $DisableInheritance) { $Parentnode = Invoke-XpathQuery -Node $_Policy -QueryMethod SelectSingleNode -query "child::parent" if ( $Parentnode ) { $null = $_Policy.RemoveChild($Parentnode) } else { Write-Warning "Specified policy does not have inheritance enabled" } } # Update inheritance if ( $PSBoundParameters.ContainsKey("InheritPolicy")) { $ParentNode = Invoke-XpathQuery -Node $_Policy -QueryMethod SelectSingleNode -query "child::parent" if ( $ParentNode ) { $null = $_Policy.RemoveChild($Parentnode) } $ParentNode = $_Policy.OwnerDocument.CreateElement("parent") $null = $_Policy.appendChild($ParentNode) Add-XmlElement -xmlRoot $ParentNode -xmlElementName "objectId" -xmlElementText $InheritPolicy.objectId } if ( -Not $NoConfirm ) { $message = "Modification of the specified policy will affect the security posture of all Security Groups that have it applied." $question = "Proceed with update of policy $($_Policy.objectId)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { Write-Debug "$($MyInvocation.MyCommand.Name) : Putting updated policy from the policy cache for $($_Policy.objectId)" #Do the post $body = $_Policy.OuterXml $URI = "/api/2.0/services/policy/securitypolicy/$($_Policy.objectId)" $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection if ($response.StatusCode -eq "200") { [System.Xml.XmlDocument]$Doc = $response.content $Doc.securityPolicy } } } End {} } function Remove-NsxSecurityPolicy { <# .SYNOPSIS Removes the specified NSX Security Policy. .DESCRIPTION An NSX Security Policy is a set of Endpoint, firewall, and network introspection services that can be applied to a security group. This cmdlet removes the specified Security Policy object. .EXAMPLE Example1: Remove the SecurityPolicy TestSP PS C:\> Get-NsxSecurityPolicy TestSP | Remove-NsxSecurityPolicy Example2: Remove the SecurityPolicy $sp without confirmation. PS C:\> $sp | Remove-NsxSecurityPolicy -confirm:$false #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] # Security Policy to Remove. [ValidateScript( { ValidateSecurityPolicy $_ })] [System.Xml.XmlElement]$SecurityPolicy, [Parameter (Mandatory = $False)] # Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False)] # Force removal, even if the policy is in use. [switch]$force = $false, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { if ((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $SecurityPolicy -query "descendant::extendedAttributes/extendedAttribute[name=`"isHidden`" and value=`"true`"]") -and ( -not $force)) { Write-Warning "Not removing $($SecurityPolicy.Name) as it is set as hidden. Use -Force to force deletion." } else { if ( $confirm ) { $message = "Security Policy removal is permanent." $question = "Proceed with removal of Security Policy $($SecurityPolicy.Name)?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { if ( $force ) { $URI = "/api/2.0/services/policy/securitypolicy/$($SecurityPolicy.objectId)?force=true" } else { $URI = "/api/2.0/services/policy/securitypolicy/$($SecurityPolicy.ObjectId)?force=false" } Write-Progress -Activity "Remove Security Policy $($SecurityPolicy.Name)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove Security Policy $($SecurityPolicy.Name)" -Completed } } } end {} } function New-NsxSecurityPolicyFirewallRuleSpec { <# .SYNOPSIS Creates a Security Policy Firewall Rule spec approriate for use in New-NsxSecurityPolicy or Add-NsxSecurityPolicyRule. .DESCRIPTION This cmdlet does not actually communicate with the NSX API, but merely constructs the appropriate XML element to define a single firewall rule that can subsequently be used in the New-NsxSecurityPolicy and Add-NsxSecurityPolicyRule cmdlets. It can operate in one of two modes. Mode 1 will be familiar to typical NSX administrators that are familiar with the concept of 'Policies Security Group' and the role it plays when defining a Security Policy Firewall Rule. It requires specification of both source and destination of the rule, at least one of which (and potentially both) must be Policies Security Group. The other can be 'Any', or a specific Security Group. Mode 2 reflects the way the API represents the firewall rule definition, and is arguably clearer than the way Security Policy Rules are modeled in the UI. It requires specification of a direction (inbound outbound or intra) and for inbound/outbound directions, a specific securitygroup may be specified. If no Security Group is specified, the source/destination is 'Any'. The two modes are equivalent in operation. Users familiar with the NSX UI and related concepts should use Mode 1. .EXAMPLE New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow All" ` -Description "Allow all inbound traffic" ` -Action allow Defines an enabled rule allowing traffic sourced from "Any" to "Policies Security Group". .EXAMPLE $sg1 = Get-NsxSecurityGroup "SG App Servers" PS C:\> $http = Get-NsxService HTTP PS C:\> $https = Get-NsxService HTTPS PS C:\> New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow Web to Demo VM" ` -Description "Allow inbound web traffic" ` -Service $http,$https -Source $sg1 -EnableLogging -Action allow Defines an enabled rule allowing traffic sourced from Security Group "SG App Servers" to "Policies Security Group" on port 80/443 with logging enabled. .EXAMPLE $sg1 = Get-NsxSecurityGroup "SG App Servers" PS C:\> $http = Get-NsxService HTTP PS C:\> $https = Get-NsxService HTTPS PS C:\> New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow Web to Demo VM" -Description "Allow Inbound Web traffic" -Service $http,$https -securityGroup $sg1 -Direction inbound -EnableLogging -Action allow Defines an enabled rule allowing traffic sourced from Security Group "SG App Servers" to "Policies Security Group" on port 80/443 with logging enabled. This results in an identical rule to Example 2 .EXAMPLE New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow All Intra Group Traffic" ` -Description "Allow all traffic within PSG" ` -Action allow Defines an enabled rule allowing traffic sourced from "Policies Security Group" to "Policies Security Group" with logging enabled. Source and Destination default to "Policies Security Group". .EXAMPLE New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow All Intra Group Traffic" ` -Description "Allow all traffic within PSG" ` -Direction Intra -Action allow Defines an enabled rule allowing traffic sourced from "Policies Security Group" to "Policies Security Group" with logging enabled. Security Group defaults to 'Any'. #> [CmdletBinding(DefaultParameterSetName = "SrcDest")] param ( [Parameter (Mandatory = $true)] # Name of the newly created firewall rule [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] # Description of the newly created firewall rule [ValidateNotNull()] [string]$Description, [Parameter (Mandatory = $false)] # Specify -disabled to create a rule as disabled. Rules default to enabled. [switch]$Disabled, [Parameter (Mandatory = $false, ParameterSetName = "Direction")] # Security Group that defines the source or destination of the rule (depending on -Direction). Security Group is mandatory if direction is Inbound or Outbound. [ValidateScript( { ValidateSecurityGroup $_ })] [object[]]$SecurityGroup, [Parameter (Mandatory = $true, ParameterSetName = "Direction")] # Direction that dictates if the specified security group is the source or destination of the rule. Inbound : Security Group defines the source. Outbound : Security Group defines the destination. [ValidateSet("Inbound", "Outbound", "Intra")] [string]$Direction, [Parameter (Mandatory = $false, ParameterSetName = "SrcDest")] # Source of the rule. Can be 'Any', 'PoliciesSecurityGroup', or a valid PowerNSX securitygroup object. At least one of source or destination MUST be 'PoliciesSecurityGroup'. Defaults to 'PoliciesSecurityGroup' [ValidateScript( { ValidateSPFirewallSrcDest $_ })] [object[]]$Source = "PoliciesSecurityGroup", [Parameter (Mandatory = $false, ParameterSetName = "SrcDest")] # Destination of the rule. Can be 'Any', 'PoliciesSecurityGroup', or a valid PowerNSX securitygroup object. At least one of source or destination MUST be 'PoliciesSecurityGroup'. Defaults to 'PoliciesSecurityGroup' [ValidateScript( { ValidateSPFirewallSrcDest $_ })] [object[]]$Destination = "PoliciesSecurityGroup", [Parameter (Mandatory = $false)] # Service defined by the rule. Defaults to 'any'. Can be any valid PowerNSX Service object. [ValidateScript( { ValidateServiceOrServiceGroup $_ })] [object[]]$Service, [Parameter (Mandatory = $false)] # Enable logging. Defaults to disabled. [switch]$EnableLogging, [Parameter (Mandatory = $false)] # Rule action. Defaults to Allow [ValidateSet("Allow", "Block", "Reject")] [string]$Action = "Allow" ) begin { switch ($PSCmdlet.ParameterSetName) { "SrcDest" { #Need some advanced input val here. Check user has specified PSG in at least one of Source or Dest. #Note : Checking for array size of one in ValidateScript is not possible due to each member being passed to validation sctript individually. if ( (($Source -contains "PoliciesSecurityGroup") -and ($source.count -ne 1)) -or (($Destination -contains "PoliciesSecurityGroup") -and ($Destination.count -ne 1)) ) { Throw "$($MyInvocation.MyCommand.Name) : Cannot validate argument on parameters 'Source' and 'Destination'. If specifying 'PoliciesSecurityGroup' it must be the only value specified." } if ( (($Source -contains "Any") -and ($source.count -ne 1)) -or (($Destination -contains "Any") -and ($Destination.count -ne 1)) ) { Throw "$($MyInvocation.MyCommand.Name) : Cannot validate argument on parameters 'Source' and 'Destination'. If specifying 'Any' it must be the only value specified." } if ( -not (( $Source[0] -eq "PoliciesSecurityGroup") -or ( $Destination[0] -eq "PoliciesSecurityGroup")) ) { Throw "$($MyInvocation.MyCommand.Name) : Cannot validate argument on parameters 'Source' and 'Destination'. At least one must specify 'PoliciesSecurityGroup'. Supply a valid argument and try the command again." } #Init SecurityGroup as an array - we need to iterate on it later, so at least need an empty array. $SecurityGroup = @() #Now we turn Source/Dest into direction based vars... if (( $Source[0] -eq "PoliciesSecurityGroup") -and ( $Destination[0] -eq "PoliciesSecurityGroup")) { $direction = "Intra" } elseif ( $Source[0] -eq "PoliciesSecurityGroup" ) { $direction = "Outbound" #User must have specified destination otherwise condition above would have hit... if ( -not ( $Destination[0] -eq "Any")) { $SecurityGroup = $Destination } } else { $direction = "Inbound" #User must have specified source otherwise conditions above would have hit... if ( -not ( $Source[0] -eq "Any")) { $SecurityGroup = $Source } } } "Direction" { #Do nothing for now. } } } process { #Create the doc and root elem. We are only defining the action elem and down. $xmlDoc = New-Object System.XML.XMLDocument $xmlRoot = $xmlDoc.CreateElement("action") $xmlDoc.appendChild($xmlRoot) | Out-Null $xmlRoot.SetAttribute("class", "firewallSecurityAction") #Basic elements Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "category" -xmlElementText "firewall" Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "logged" -xmlElementText $EnableLogging.ToString().ToLower() Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "action" -xmlElementText $Action.ToLower() Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "direction" -xmlElementText $Direction.ToLower() Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "isEnabled" -xmlElementText ( -not $Disabled ) if ( $PSBoundParameters.ContainsKey("description")) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText $Description } #Iterate securitygroups. 'PoliciesSecurityGroups' and 'Any' are taken care of in begin block. SecurityGroups is only popoulated if we really do have some to walk. foreach ( $Group in $SecurityGroup) { $xmlSecurityGroup = $xmlDoc.CreateElement("secondarySecurityGroup") $xmlRoot.appendChild($xmlSecurityGroup) | Out-Null Add-XmlElement -xmlRoot $xmlSecurityGroup -xmlElementName "objectId" -xmlElementText $group.objectId } #Iterate over services. if ( $PsBoundParameters.ContainsKey('Service') ) { $xmlApplications = $xmlDoc.CreateElement("applications") $xmlRoot.appendChild($xmlApplications) | Out-Null foreach ( $svc in $service) { switch ($svc.objectTypeName) { "application" { $xmlElementName = "application" } "applicationgroup" { $xmlElementName = "applicationGroup" } } $xmlApplicationsObject = $xmlDoc.CreateElement($xmlElementName) $xmlApplications.appendChild($xmlApplicationsObject) | Out-Null Add-XmlElement -xmlRoot $xmlApplicationsObject -xmlElementName "objectId" -xmlElementText $svc.objectId } } #Just emit the resulting xml. $xmlRoot } end {} } function New-NsxSecurityPolicyNetworkIntrospectionSpec { <# .SYNOPSIS Creates a Security Policy Network Introspection Rule spec approriate for use in New-NsxSecurityPolicy or Add-NsxSecurityPolicyRule. .DESCRIPTION This cmdlet does not actually communicate with the NSX API, but merely constructs the appropriate XML element to define a single rule that can subsequently be used in the New-NsxSecurityPolicy and Add-NsxSecurityPolicyRule cmdlets. It can operate in one of two modes. Mode 1 will be familiar to typical NSX administrators that are familiar with the concept of 'Policies Security Group' and the role it plays when defining a Security Policy Firewall Rule. It requires specification of both source and destination of the rule, at least one of which (and potentially both) must be Policies Security Group. The other can be 'Any', or a specific Security Group. Mode 2 reflects the way the API represents the rule definition, and is arguably clearer than the way Security Policy Rules are modeled in the UI. It requires specification of a direction (inbound outbound or intra) and for inbound/outbound directions, a specific securitygroup may be specified. If no Security Group is specified, the source/destination is 'Any'. The two modes are equivalent in operation. Users familiar with the NSX UI and related concepts should use Mode 1. .EXAMPLE New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow All" ` -Description "Allow all inbound traffic" ` -Action allow Defines an enabled rule allowing traffic sourced from "Any" to "Policies Security Group". .EXAMPLE $sg1 = Get-NsxSecurityGroup "SG App Servers" PS C:\> $http = Get-NsxService HTTP PS C:\> $https = Get-NsxService HTTPS PS C:\> New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow Web to Demo VM" ` -Description "Allow inbound web traffic" ` -Service $http,$https -Source $sg1 -EnableLogging -Action allow Defines an enabled rule allowing traffic sourced from Security Group "SG App Servers" to "Policies Security Group" on port 80/443 with logging enabled. .EXAMPLE $sg1 = Get-NsxSecurityGroup "SG App Servers" PS C:\> $http = Get-NsxService HTTP PS C:\> $https = Get-NsxService HTTPS PS C:\> New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow Web to Demo VM" -Description "Allow Inbound Web traffic" -Service $http,$https -securityGroup $sg1 -Direction inbound -EnableLogging -Action allow Defines an enabled rule allowing traffic sourced from Security Group "SG App Servers" to "Policies Security Group" on port 80/443 with logging enabled. This results in an identical rule to Example 2 .EXAMPLE New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow All Intra Group Traffic" ` -Description "Allow all traffic within PSG" ` -Action allow Defines an enabled rule allowing traffic sourced from "Policies Security Group" to "Policies Security Group" with logging enabled. Source and Destination default to "Policies Security Group". .EXAMPLE New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow All Intra Group Traffic" ` -Description "Allow all traffic within PSG" ` -Direction Intra -Action allow Defines an enabled rule allowing traffic sourced from "Policies Security Group" to "Policies Security Group" with logging enabled. Security Group defaults to 'Any'. #> [CmdletBinding(DefaultParameterSetName = "SrcDest")] param ( [Parameter (Mandatory = $true)] # Name of the newly created network introspection rule [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] # Description of the newly created network introspection rule [ValidateNotNull()] [string]$Description, [Parameter (Mandatory = $false)] # Specify -disabled to create a rule as disabled. Rules default to enabled. [switch]$Disabled, [Parameter (Mandatory = $false, ParameterSetName = "Direction")] # Security Group that defines the source or destination of the rule (depending on -Direction). Security Group is mandatory if direction is Inbound or Outbound. [ValidateScript( { ValidateSecurityGroup $_ })] [object[]]$SecurityGroup, [Parameter (Mandatory = $true, ParameterSetName = "Direction")] # Direction that dictates if the specified security group is the source or destination of the rule. Inbound : Security Group defines the source. Outbound : Security Group defines the destination. [ValidateSet("Inbound", "Outbound", "Intra")] [string]$Direction, [Parameter (Mandatory = $false, ParameterSetName = "SrcDest")] # Source of the rule. Can be 'Any', 'PoliciesSecurityGroup', or a valid PowerNSX securitygroup object. At least one of source or destination MUST be 'PoliciesSecurityGroup'. Defaults to 'PoliciesSecurityGroup' [ValidateScript( { ValidateSPFirewallSrcDest $_ })] [object[]]$Source = "PoliciesSecurityGroup", [Parameter (Mandatory = $false, ParameterSetName = "SrcDest")] # Destination of the rule. Can be 'Any', 'PoliciesSecurityGroup', or a valid PowerNSX securitygroup object. At least one of source or destination MUST be 'PoliciesSecurityGroup'. Defaults to 'PoliciesSecurityGroup' [ValidateScript( { ValidateSPFirewallSrcDest $_ })] [object[]]$Destination = "PoliciesSecurityGroup", [Parameter (Mandatory = $false)] # Service defined by the rule. Defaults to 'any'. Can be any valid PowerNSX Service object. [ValidateScript( { ValidateService $_ })] [object[]]$Service, [Parameter (Mandatory = $true)] # Service Profile object as retrieved using Get-NsxServiceProfile (as defined in Service Profile section of a specific Service Definition in the NSX UI). [ValidateScript( { ValidateServiceProfile $_ })] [System.Xml.XmlElement]$ServiceProfile, [Parameter (Mandatory = $false)] # Enable logging. Defaults to disabled. [switch]$EnableLogging, [Parameter (Mandatory = $false)] # Disable redirection for this rule. Defaults to $false (Rule is created with redirection enabled). [switch]$DisableRedirection ) begin { switch ($PSCmdlet.ParameterSetName) { "SrcDest" { #Need some advanced input val here. Check user has specified PSG in at least one of Source or Dest. #Note : Checking for array size of one in ValidateScript is not possible due to each member being passed to validation sctript individually. if ( (($Source -contains "PoliciesSecurityGroup") -and ($source.count -ne 1)) -or (($Destination -contains "PoliciesSecurityGroup") -and ($Destination.count -ne 1)) ) { Throw "$($MyInvocation.MyCommand.Name) : Cannot validate argument on parameters 'Source' and 'Destination'. If specifying 'PoliciesSecurityGroup' it must be the only value specified." } if ( (($Source -contains "Any") -and ($source.count -ne 1)) -or (($Destination -contains "Any") -and ($Destination.count -ne 1)) ) { Throw "$($MyInvocation.MyCommand.Name) : Cannot validate argument on parameters 'Source' and 'Destination'. If specifying 'Any' it must be the only value specified." } if ( -not (( $Source[0] -eq "PoliciesSecurityGroup") -or ( $Destination[0] -eq "PoliciesSecurityGroup")) ) { Throw "$($MyInvocation.MyCommand.Name) : Cannot validate argument on parameters 'Source' and 'Destination'. At least one must specify 'PoliciesSecurityGroup'. Supply a valid argument and try the command again." } #Init SecurityGroup as an array - we need to iterate on it later, so at least need an empty array. $SecurityGroup = @() #Now we turn Source/Dest into direction based vars... if (( $Source[0] -eq "PoliciesSecurityGroup") -and ( $Destination[0] -eq "PoliciesSecurityGroup")) { $direction = "Intra" } elseif ( $Source[0] -eq "PoliciesSecurityGroup" ) { $direction = "Outbound" #User must have specified destination otherwise condition above would have hit... if ( -not ( $Destination[0] -eq "Any")) { $SecurityGroup = $Destination } } else { $direction = "Inbound" #User must have specified source otherwise conditions above would have hit... if ( -not ( $Source[0] -eq "Any")) { $SecurityGroup = $Source } } } "Direction" { #Do nothing for now. } } } process { #Create the doc and root elem. We are only defining the action elem and down. $xmlDoc = New-Object System.XML.XMLDocument $xmlRoot = $xmlDoc.CreateElement("action") $xmlDoc.appendChild($xmlRoot) | Out-Null $xmlRoot.SetAttribute("class", "trafficSteeringSecurityAction") #Basic elements Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "category" -xmlElementText "traffic_steering" Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "logged" -xmlElementText $EnableLogging.ToString().ToLower() Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "direction" -xmlElementText $Direction.ToLower() Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "isEnabled" -xmlElementText ( -not $Disabled ) Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "redirect" -xmlElementText ( -not $DisableRedirection ) if ( $PSBoundParameters.ContainsKey("description")) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText $Description } $xmlServiceProfile = $xmlDoc.CreateElement("serviceProfile") $null = $xmlRoot.AppendChild($xmlServiceProfile) Add-XmlElement -xmlRoot $xmlServiceProfile -xmlElementName "objectId" $ServiceProfile.objectId #Iterate securitygroups. 'PoliciesSecurityGroups' and 'Any' are taken care of in begin block. SecurityGroups is only popoulated if we really do have some to walk. foreach ( $Group in $SecurityGroup) { $xmlSecurityGroup = $xmlDoc.CreateElement("secondarySecurityGroup") $xmlRoot.appendChild($xmlSecurityGroup) | Out-Null Add-XmlElement -xmlRoot $xmlSecurityGroup -xmlElementName "objectId" -xmlElementText $group.objectId } #Iterate over services. if ( $PsBoundParameters.ContainsKey('Service') ) { $xmlApplications = $xmlDoc.CreateElement("applications") $xmlRoot.appendChild($xmlApplications) | Out-Null foreach ( $svc in $service) { switch ($svc.objectTypeName) { "application" { $xmlElementName = "application" } "applicationgroup" { $xmlElementName = "applicationGroup" } } $xmlApplicationsObject = $xmlDoc.CreateElement($xmlElementName) $xmlApplications.appendChild($xmlApplicationsObject) | Out-Null Add-XmlElement -xmlRoot $xmlApplicationsObject -xmlElementName "objectId" -xmlElementText $svc.objectId } } #Just emit the resulting xml. $xmlRoot } end {} } function New-NsxSecurityPolicyGuestIntrospectionSpec { <# .SYNOPSIS Creates a Security Policy Guest Introspection Rule spec approriate for use in New-NsxSecurityPolicy or Add-NsxSecurityPolicyGuestIntrospectionRule. .DESCRIPTION This cmdlet does not actually communicate with the NSX API, but merely constructs the appropriate XML element to define a single guest introspection rule that can subsequently be used in the New-NsxSecurityPolicy and Add-NsxSecurityPolicyRule cmdlets. It can operate in one of two modes. Mode 1 (action apply) - Allows the creation of a guest introspection rule that applies a preconfigured Service Definition and optional Service Profile. Apply mode is typically used to apply guest introspection rules associated with third party solutions integrated with NSX. Mode 2 (action block) - Allows the creation of a guest introspection rule that blocks based on AV, Vulnerability Management, or File Integrity Monitoring. .EXAMPLE $gispec = New-NsxSecurityPolicyGuestIntrospectionSpec -ServiceType AntiVirus -description "AV GI Rule" Create a new Guest Introspection 'Block' AntiVirus rule. .EXAMPLE $gispec = New-NsxSecurityPolicyGuestIntrospectionSpec -ServiceType AntiVirus -description "AV GI Rule" Create a new Guest Introspection 'Block' AntiVirus rule. .EXAMPLE $sd = Get-NsxServiceDefinition "ServiceDefinition" PS C:\> $sp = $sd | Get-NsxServiceProfile "Profile1" PS C:\> $gispec = New-NsxSecurityPolicyGuestIntrospectionSpec -ServiceDefinition $sd -ServiceProfile $sp -name GIRule-Profile1 -description "Custom GI Rule" Create a new Guest Introspection 'Apply' rule based on a Service Definition called ServiceDefinition1, and Service Profile Profile1 #> [CmdletBinding(DefaultParameterSetName = "Apply")] param ( [Parameter (Mandatory = $false)] # Name of the newly created GI rule. [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] # Description of the newly created rule. [ValidateNotNull()] [string]$Description, [Parameter (Mandatory = $false)] # Create the rule as disabled. [switch]$Disabled, [Parameter (Mandatory = $false)] # Create the rule as Enforced (Rule is not enforced by default as per UI default) [switch]$Enforced, [Parameter (Mandatory = $true, ParameterSetName = "Block")] # Service Type of the Block rule. Accepts AntiVirus, VulnerabilityManagement or FileIntegrityMonitoring [ValidateSet("AntiVirus", "VulnerabilityManagement", "FileIntegrityMonitoring")] [string]$ServiceType, [Parameter (Mandatory = $true, ParameterSetName = "Apply")] # Service Definition object as retrieved using Get-NsxServiceDefinition (as defined in Service Definitions section of the NSX UI). [ValidateScript( { ValidateServiceDefinition $_ })] [System.Xml.XmlElement]$ServiceDefinition, [Parameter (Mandatory = $false, ParameterSetName = "Apply")] # Service Profile object as retrieved using Get-NsxServiceProfile (as defined in Service Profile section of a specific Service Definition in the NSX UI). [ValidateScript( { ValidateServiceProfile $_ })] [System.Xml.XmlElement]$ServiceProfile, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Create the doc and root elem. We are only defining the action elem and down. $xmlDoc = New-Object System.XML.XMLDocument $xmlRoot = $xmlDoc.CreateElement("action") $xmlDoc.appendChild($xmlRoot) | Out-Null $xmlRoot.SetAttribute("class", "endpointSecurityAction") #Basic mandatory elements Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "category" -xmlElementText "endpoint" Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "isEnabled" -xmlElementText ( -not $Disabled ) Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "isActionEnforced" -xmlElementText $Enforced #Optional elements if ( $PSBoundParameters.ContainsKey("name")) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "name" -xmlElementText $Name } if ( $PSBoundParameters.ContainsKey("description")) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "description" -xmlElementText $Description } #Are we in 'apply' or block mode. If block, we specify the action type. If apply, we specifiy the service and optional service profile. switch ( $PSCmdlet.ParameterSetName ) { "Apply" { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "serviceId" -xmlElementText $ServiceDefinition.objectId if ( $PSBoundParameters.ContainsKey("ServiceProfile") ) { $xmlServiceProfile = $xmlDoc.CreateElement("serviceProfile") $null = $xmlRoot.AppendChild($xmlServiceProfile) Add-XmlElement -xmlRoot $xmlServiceProfile -xmlElementName "objectId" $ServiceProfile.objectId } } "Block" { $actionType = ConvertTo-NsxApiActionType $ServiceType Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "actionType" -xmlElementText $actionType } } #Just emit the resulting xml. $xmlRoot } end {} } function Get-NsxServiceDefinition { <# .SYNOPSIS Retrieves Service Definitions from NSX. .DESCRIPTION A Service Definition describes the integration of some default internal and registered thirdparty services such as load balancing or layer 7 firewalling. This cmdlet retrieves existing Service Definitions from NSX. .EXAMPLE Get-NsxServiceDefinition Retrieve all Service Definitions. .EXAMPLE Get-NsxServiceDefinition -ObjectId service-7 Retrieve the service definition with the specified objectId .EXAMPLE Get-NsxServiceDefinition -Name MyServiceDefinition Retrieve the service definition with the specified Name. #> [CmdletBinding(DefaultParameterSetName = "Name")] param ( [Parameter(Mandatory = $false, ParameterSetName = "Name", Position = 1)] # Name of the Service Definition to retrieve. [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(Mandatory = $false, ParameterSetName = "ObjectId")] # ObjectId of the Service Definition to retrieve. [ValidateNotNullOrEmpty()] [string]$ObjectId, [Parameter (Mandatory = $False)] # PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) switch ( $PSCmdlet.ParameterSetName ) { "ObjectId" { $URI = "/api/2.0/si/service/$objectId" try { $response = Invoke-NsxWebRequest -method Get -URI $URI -connection $connection if ( $response ) { [xml]$return = $response.content $return.service } } catch { Throw "An unknown error occured retrieving Service Definitions. $_" } } "Name" { $URI = "/api/2.0/si/services" try { $response = Invoke-NsxWebRequest -method Get -URI $URI -connection $connection if ( $response ) { [xml]$return = $response.content if ( $PSBoundParameters.ContainsKey("Name")) { $return.services.service | Where-Object { $_.name -eq $Name } } else { $return.services.service } } } catch { Throw "An unknown error occured retrieving Service Definitions. $_" } } } } function Get-NsxServiceProfile { <# .SYNOPSIS Retrieves Service Profiles associated with Service Definitions. .DESCRIPTION A Service Definition describes the integration of some default internal and registered thirdparty services such as load balancing or layer 7 firewalling. A Service Definition can have one or more Service Profiles defined that expose functionality that can be leveraged in a service instance when defining a Guest Introspection rule in a Security Policy. This cmdlet retrieves Service Profile objects. .EXAMPLE Get-NsxServiceDefinitionProfile Get all Service Profiles defined. .EXAMPLE Get-NsxServiceDefinition "ThirdPartySi" | Get-NsxServiceDefinitionProfile Get all Service Profiles for the specified Service Definition. .EXAMPLE Get-NsxServiceDefinitionProfile -ObjectID "serviceprofile-3" Get the Service Profile with objectid serviceprofile-3 .EXAMPLE Get-NsxServiceDefinitionProfile MyAvService Get the specified Service Profile by name #> [CmdletBinding(DefaultParameterSetName = "Name")] param ( [Parameter(Mandatory = $false, ValueFromPipeline = $true, ParameterSetName = "ServiceDefinition")] # Service Definition as returned by Get-NsxServiceDefinition. [ValidateScript( { ValidateServiceDefinition $_ })] [System.Xml.XmlElement]$ServiceDefinition, [Parameter(Mandatory = $false, ParameterSetName = "Name", Position = 1)] # Name of the Service Profile to retrieve. [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(Mandatory = $false, ParameterSetName = "ObjectId")] # ObjectId of the Service Profile to retrieve. [ValidateNotNullOrEmpty()] [string]$ObjectId, [Parameter (Mandatory = $False)] # PowerNSX Connection object. [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) Begin {} Process { switch ( $PSCmdlet.ParameterSetName ) { "ObjectId" { $URI = "/api/2.0/si/serviceprofile/$objectId" try { $response = Invoke-NsxWebRequest -method Get -URI $URI -connection $connection if ( $response ) { [xml]$return = $response.content $return.serviceProfile } } catch { Throw "An error occured retrieving Service Profiles. $_" } } Default { $URI = "/api/2.0/si/serviceprofiles" try { $response = Invoke-NsxWebRequest -method Get -URI $URI -connection $connection if ( $response ) { [xml]$return = $response.content if ( Invoke-XpathQuery -Node $return -QueryMethod SelectSingleNode -query ("child::serviceProfiles/serviceProfile")) { if ( $PSBoundParameters.ContainsKey("Name")) { $return.serviceProfiles.serviceProfile | Where-Object { $_.name -eq $Name } } elseif ( $PSCmdlet.ParameterSetName -eq "ServiceDefinition") { $return.serviceProfiles.serviceProfile | Where-Object { $_.service.objectId -eq $ServiceDefinition.objectId } } else { $return.serviceProfiles.serviceProfile } } } } catch { Throw "An error occured retrieving Service Definitions. $_" } } } } End {} } function New-NsxSecurityPolicyAssignment { <# .SYNOPSIS Applies a Security Policy to the specified Security Group. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The New-NsxSecurityPolicyAssignment cmdlet applies the specified Security Policy to the specified Security Group. .EXAMPLE $sg = Get-NsxSecurityGroup WebApp1WebServers PS C:\> Get-NsxSecurityPolicy WebServers | New-NsxSecurityPolicyAssignment -SecurityGroup $sg Applies the Security Policy WebServers to the Security Group WebApp1WebServers. .EXAMPLE $AllSecurityGroups = Get-NsxSecurityGroup PS C:\> Get-NsxSecurityPolicy Mandatory | New-NsxSecurityPolicyAssignment -SecurityGroup $AllSecurityGroups Applies the Security Policy Mandatory to all defined Security Groups. #> [CmdletBinding()] param ( [Parameter (Mandatory = $True, ValueFromPipeline = $True)] # Security Policy to be applied. [ValidateScript( { ValidateSecurityPolicy $_ })] [System.Xml.XmlElement]$SecurityPolicy, [Parameter (Mandatory = $false)] # Security Group to which to apply the specified policy. Can specify a collection of security groups to perform assignment of policy to multiple groups. [ValidateScript( { ValidateSecurityGroup $_ })] [System.Xml.XmlElement[]]$SecurityGroup, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Clone the existing SP so we dont modify the input object. $_SecurityPolicy = $SecurityPolicy.CloneNode($True) #Iterate SecurityGroup collection foreach ($Group in $SecurityGroup) { $BindingNode = $_SecurityPolicy.OwnerDocument.CreateElement("securityGroupBinding") $null = $_SecurityPolicy.AppendChild($BindingNode) Add-XmlElement -xmlRoot $BindingNode -xmlElementName "objectId" -xmlElementText $Group.objectId } #Do the post $body = $_SecurityPolicy.OuterXml $URI = "/api/2.0/services/policy/securitypolicy/$($_SecurityPolicy.objectId)" Write-Progress -Activity "Updating SecurityGroup bindings for Security Policy $($SecurityPolicy.Name)" $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Updating SecurityGroup bindings for Security Policy $($SecurityPolicy.Name)" -Completed [xml]$return = $response.content $return.securityPolicy } end {} } function Remove-NsxSecurityPolicyAssignment { <# .SYNOPSIS Removes the applied Security Policy from specified Security Group(s). .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The Remove-NsxSecurityPolicyAssignment cmdlet removes the specified Security Policy from the specified Security Group. .EXAMPLE $sg = Get-NsxSecurityGroup WebApp1WebServers PS C:\>Get-NsxSecurityPolicy WebServers | Remove-NsxSecurityPolicyAssignment -SecurityGroup $sg Removes the Security Policy Webservers from the applied policies list of the WebApp1WebSevers Security Group. #> [CmdletBinding()] param ( [Parameter (Mandatory = $True, ValueFromPipeline = $True)] # Security Policy whose application will be removed from the specified Security Group [ValidateScript( { ValidateSecurityPolicy $_ })] [System.Xml.XmlElement]$SecurityPolicy, [Parameter (Mandatory = $true)] # Security Group to remove the specified Security Policy from its applied policies list. [ValidateScript( { ValidateSecurityGroup $_ })] [System.Xml.XmlElement[]]$SecurityGroup, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Track if we actually modify the SP - no point in going back to the API if we dont actually have anything to say. $ModifiedSP = $false #Check that the Sp we are processing is applied to at least one group. if (Invoke-XpathQuery -Node $SecurityPolicy -QueryMethod SelectSingleNode -query "child::securityGroupBinding") { #Clone the node to avoid modifying input object. $_SecurityPolicy = $SecurityPolicy.CloneNode($true) #Iterate SecurityGroups, find and remove the ones that match the current SecurityGroup foreach ($Group in $SecurityGroup) { $CurrGroupBindingNode = Invoke-XpathQuery -Node $_SecurityPolicy -QueryMethod SelectSingleNode -query "child::securityGroupBinding[objectId=`'$($Group.objectId)`']" if ($CurrGroupBindingNode) { $null = $_SecurityPolicy.RemoveChild($CurrGroupBindingNode) $ModifiedSP = $true } else { #Let the user know there was nothing to do, but dont throw...want to make sure the pipeline continues. Write-Warning "Security Policy $($SecurityPolicy.Name) ($($SecurityPolicy.objectId)) is not applied to Security Group $($Group.Name) ($($Group.objectId))" } } } Else { #Again, we dont throw, want to make sure the pipeline continues. Write-Warning "No SecurityGroups are assoicated with SecurityPolicy: $($SecurityPolicy.name)" } #Do the post if ( $ModifiedSP ) { $body = $_SecurityPolicy.OuterXml $URI = "/api/2.0/services/policy/securitypolicy/$($_SecurityPolicy.objectId)" $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection if ($response.StatusCode -eq "200") { [xml]$return = $response.content $return.securityPolicy } } } end {} } function Get-NsxSecurityPolicyRule { <# .SYNOPSIS Retrieves rules defined on the specified Security Policy. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The three categories of rules that can be included in a Security Policy are: - Guest Introspection - data security, anti-virus, and vulnerability management and rules based on third party Guest Introspection capability. - Firewall rules - creates appropriate distributed firewall rules when the policy is applied to a security group. - Network introspection services - Thirdparty firewall, IPS/IDS etc. Get-NsxSecurityPolicyRule retrieves firewall, guest introspection and network introspection rules defined on the specified policy. .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule Retrieves all defined rules from the security policy SecPol01 .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall Retrieves all defined firewall rules from the security policy SecPol01 .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Network Retrieves all defined network introspection rules from the security policy SecPol01 .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Guest Retrieves all defined guest introspection rules from the security policy SecPol01 .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -Name TestRule Retrieves the rule called TestRule from the security policy SecPol01 .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -ObjectId firewallpolicyaction-10 Retrieves the specified from the security policy SecPol01 #> [CmdLetBinding(DefaultParameterSetName = "securitygroup")] param ( [Parameter(Mandatory = $true, ValueFromPipeLine)] #Security Policy to retrieve rules from. [ValidateScript( { ValidateSecurityPolicy $_ })] [System.Xml.XmlElement]$SecurityPolicy, [Parameter()] #Type of rule to retrieve. Defaults to all. [ValidateSet("All", "Firewall", "Network", "Guest")] [String]$RuleType = "All", [Parameter(Position = 1)] #Name of rule to retrieve. [ValidateNotNullOrEmpty()] [String]$Name, [Parameter()] #Name of rule to retrieve. [ValidateNotNullOrEmpty()] [string]$ObjectId, [Parameter(Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Define the XPATH search criteria based on RuleType. Switch ($RuleType) { "All" { $Query = "actionsByCategory/action" } "Firewall" { $Query = "actionsByCategory/action[@class='firewallSecurityAction']" } "Network" { $Query = "actionsByCategory/action[@class='trafficSteeringSecurityAction']" } "Guest" { $Query = "actionsByCategory/action[@class='endpointSecurityAction']" } } if ($PSBoundParameters.ContainsKey("Name")) { $Query += "[name=`'$Name`']" } if ($PSBoundParameters.ContainsKey("ObjectId")) { $Query += "[objectId=`'$ObjectId`']" } Write-Debug "Using xpath query $Query" Invoke-XpathQuery -Node $SecurityPolicy -QueryMethod SelectNodes -query $Query } end {} } function Move-NsxSecurityPolicyRule { <# .SYNOPSIS Moves the specified rule to a new location within its parent Security Policy. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The three categories of rules that can be included in a Security Policy are: - Guest Introspection - data security, anti-virus, and vulnerability management and rules based on third party Guest Introspection capability. - Firewall rules - creates appropriate distributed firewall rules when the policy is applied to a security group. - Network introspection services - Thirdparty firewall, IPS/IDS etc. Move-NsxSecurityPolicyRule moves the specified rule to a new location within it's parent Security Policy. Allowed destinations are 'Top', 'Bottom' or 'ToPosition', where -position must specified the desired location. .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Move-NsxSecurityPolicyRule -Destination Top Moves the specified the firewall rule called AdminSsh to the top of the security policy SecPol01's configured firewall rules. .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Move-NsxSecurityPolicyRule -Destination Bottom Moves the specified the firewall rule called AdminSsh to the bottom of the security policy SecPol01's configured firewall rules. .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Move-NsxSecurityPolicyRule -Destination 3 Moves the specified the firewall rule called AdminSsh to be the third of security policy SecPol01's configured firewall rules. .EXAMPLE $dodgyrule = Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name DodgyRule Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Move-NsxSecurityPolicyRule -Destination $dodgyrule.executionOrder Moves the specified the firewall rule called AdminSsh to be above the rule called DodgyRule within security policy SecPol01's configured firewall rules. #> [CmdletBinding()] param ( [Parameter (Mandatory = $True, ValueFromPipeline = $True)] # Security Policy Rule to reconfigure [ValidateScript( { ValidateSecPolRule $_ })] [System.Xml.XmlElement]$Rule, [Parameter (Mandatory = $true)] # Move the specified rule. Destination parameter must be used to specify the desired location. [ValidateScript( { switch -regex ($_) { "^Top$" { $true; break } "^Bottom$" { $true; break } "^\d.*$" { $true; break } default { throw "Specify position as 'Top', 'Bottom', or an integer to specify a new position. Use an existing rules property executionOrder to move relative to an existing rule." } } })] [string]$Destination, [Parameter ()] # Disable confirmation prompt [switch]$NoConfirm, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { # We process all rule modifications offline as part of pipeline processing, then we put the updated policies to the api in the end{} block to avoid overwriting changes to different rules in the same policy.. # Save modified policies in a hash table keyed by id Write-Debug "$($MyInvocation.MyCommand.Name) : Initialising Policy cache" $ModifiedPolicies = @{} $ModifiedRules = @() } process { $ParentPolicyObjectId = $Rule.ParentNode.ParentNode.objectId #The Rule the user specified is only used the first time we edit a given policy. Otherwise, it is just a key to the cached copy - in which case, we modify the rule as it exists in the cached copy of the policy. if ( $ModifiedPolicies.ContainsKey($ParentPolicyObjectId )) { #Doesnt make sense to move multiple rules at the same time. throw "Moving multiple rules within a single policy at the same time is not supported. Ensure only a single rule is specified to be moved." } else { # We havent touched the policy yet, so we have to get it. We clone to avoid modifying the original. $PolicyXml = $Rule.ParentNode.ParentNode.CloneNode($true) $ModifiedPolicies.Add($ParentPolicyObjectId, $PolicyXml) Write-Debug "$($MyInvocation.MyCommand.Name) : Specified rules parent policy not found in policy cache so it has been added." } #Now get our xml from the cached policy. $_Rule = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $PolicyXml -query "actionsByCategory/action[objectId=`'$($Rule.objectId)`']" if ( -not $_Rule ) { #This should never happen throw "An unexpected error occured retrieving a rule from cache. Please report this as a bug at https://github.com/vmware/powernsx" } Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved rule from cache: $( $_Rule | Format-XML )" #And do our updates... $CurrentRuleClass = $_Rule.class #Get a ref node for the destination of the move. try { switch -regex ( $Destination ) { "Top" { $refnode = Invoke-XpathQuery -Node $PolicyXml -QueryMethod SelectSingleNode -query "actionsByCategory/action[@class=`'$CurrentRuleClass`'][1]" break } "Bottom" { $refnode = Invoke-XpathQuery -Node $PolicyXml -QueryMethod SelectSingleNode -query "actionsByCategory/action[@class=`'$CurrentRuleClass`'][last()]" break } "\d.*" { $refnode = Invoke-XpathQuery -Node $PolicyXml -QueryMethod SelectSingleNode -query "actionsByCategory/action[@class=`'$CurrentRuleClass`'][$Destination]" break } } if ( -not $refnode ) { throw "No existing rule found at number $Destination." } } catch { throw "Specified destination is invalid. Specify a valid destination and try again. $_" } Write-Debug "$($MyInvocation.MyCommand.Name) : Ref node for new destination is $($refnode.objectId) )" #Check user specified an actual move. if ( $_Rule.objectId -eq $refnode.objectId ) { throw "Rule $($_Rule.objectId) is already at the specified location." } #This logic is predicated on the fact that the executionOrder text elem value reflects the xml ordering. #Move the node. If we are moving it down, then we insert after the refnode. If moving up, we insert before... $Parent = $_Rule.ParentNode if ( $_rule.executionOrder -lt $refnode.executionOrder) { $null = $Parent.InsertAfter($_Rule, $refnode) } else { $null = $Parent.InsertBefore($_Rule, $refnode) } Write-Debug "$($MyInvocation.MyCommand.Name) : Rule processing complete. Updated policy xml is : $( $PolicyXML | Format-XML )" $ModifiedRules += $_Rule.objectId } end { foreach ( $policy in $ModifiedPolicies.Values ) { $UpdatedPolicy = Set-NsxSecurityPolicy -Policy $policy -NoConfirm:$NoConfirm if ( $UpdatedPolicy) { $AllPolicyRules = Invoke-XpathQuery -QueryMethod SelectNodes -Node $UpdatedPolicy -query "actionsByCategory/action" $AllPolicyRules | Where-Object { $ModifiedRules -contains $_.objectId } } } } } function Remove-NsxSecurityPolicyRule { <# .SYNOPSIS Removes the specified rule from its parent Security Policy. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The three categories of rules that can be included in a Security Policy are: - Guest Introspection - data security, anti-virus, and vulnerability management and rules based on third party Guest Introspection capability. - Firewall rules - creates appropriate distributed firewall rules when the policy is applied to a security group. - Network introspection services - Thirdparty firewall, IPS/IDS etc. Remove-NsxSecurityPolicyRule removes the specified rule from its parent Security Policy. .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Remove-NsxSecurityPolicyRule Remove the rule called AdminSsh from the Security Policy SecPol01 .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Remove-NsxSecurityPolicyRule -NoConfirm Remove the rule called AdminSsh from the Security Policy SecPol01 with no confirmation prompt. #> [CmdletBinding()] param ( [Parameter (Mandatory = $True, ValueFromPipeline = $True)] # Security Policy Rule to reconfigure [ValidateScript( { ValidateSecPolRule $_ })] [System.Xml.XmlElement]$Rule, [Parameter()] # Disable confirmation prompt [switch]$NoConfirm, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { # We process all rule modifications offline as part of pipeline processing, then we put the updated policies to the api in the end{} block to avoid overwriting changes to different rules in the same policy.. # Save modified policies in a hash table keyed by id Write-Debug "$($MyInvocation.MyCommand.Name) : Initialising Policy cache" $ModifiedPolicies = @{} } process { $ParentPolicyObjectId = $Rule.ParentNode.ParentNode.objectId #The Rule the user specified is only used the first time we edit a given policy. Otherwise, it is just a key to the cached copy - in which case, we modify the rule as it exists in the cached copy of the policy. if ( $ModifiedPolicies.ContainsKey($ParentPolicyObjectId )) { # Policy has already been updated in this pipeline, so we modify the already updated xml. $PolicyXml = $ModifiedPolicies[$ParentPolicyObjectId] Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved specified rules parent policy from the policy cache. Policy XML is : $($PolicyXML | Format-XML)" } else { # We havent touched the policy yet, so we have to get it. We clone to avoid modifying the original. $PolicyXml = $Rule.ParentNode.ParentNode.CloneNode($true) $ModifiedPolicies.Add($ParentPolicyObjectId, $PolicyXml) Write-Debug "$($MyInvocation.MyCommand.Name) : Specified rules parent policy not found in policy cache so it has been added." } #Now get our xml from the cached policy. $_Rule = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $PolicyXml -query "actionsByCategory/action[objectId=`'$($Rule.objectId)`']" if ( -not $_Rule ) { #This should never happen throw "An unexpected error occured retrieving a rule from cache. Please report this as a bug at https://github.com/vmware/powernsx" } Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved rule from cache: $( $_Rule | Format-XML )" #And do our updates... $null = $_Rule.ParentNode.RemoveChild($_Rule) Write-Debug "$($MyInvocation.MyCommand.Name) : Rule processing complete. Updated policy xml is : $( $PolicyXML | Format-XML )" } end { foreach ( $policy in $ModifiedPolicies.Values ) { $null = Set-NsxSecurityPolicy -Policy $policy -NoConfirm:$NoConfirm } } } function Add-NsxSecurityPolicyRule { <# .SYNOPSIS Adds a new NSX Security Policy Rule to an existing policy. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The three categories of rules that can be included in a Security Policy are: - Guest Introspection - data security, anti-virus, and vulnerability management and rules based on third party Guest Introspection capability. - Firewall rules - creates appropriate distributed firewall rules when the policy is applied to a security group. - Network introspection services - Thirdparty firewall, IPS/IDS etc. Add-NsxSecurityPolicyRule allows the addition of a new rule to an existing security policy. For Network Introspection, and some Guest Introspection rules, the appropriate service defintion and service policies must already be defined within NSX to allow this. .EXAMPLE $sg1 = Get-NsxSecurityGroup "All Management Servers" PS C:\> $http = Get-NsxService -Localonly | Where { $_.name -eq 'HTTP' } PS C:\> $https = Get-NsxService -Localonly | Where { $_.name -eq 'HTTPS' } PS C:\> $ssh = Get-NsxService -Localonly | Where { $_.name -eq 'SSH' } PS C:\> $inboundwebrule = New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow Inbound Web" ` -Description "Allow inbound web traffic" ` -Service $http,$https -Source Any -EnableLogging -Action allow PS C:\> $inboundsshrule = New-NsxSecurityPolicyFirewallRuleSpec -Name "Allow SSH from Management" ` -Description "Allow inbound ssh traffic from management servers" ` -Service $ssh -Source $sg1 -EnableLogging -Action allow PS C:\> Get-NsxSecurityPolicy -Name WebServers | Add-NsxSecurityPolicyRule ` -FirewallRuleSpec $inboundwebrule, $inboundsshrule Gets a security policy called WebServers and addes two firewall rules to it. The specific steps to accomplish this are as follows: - Retrieves an existing security group that represents management servers from which SSH traffic will originate. - Retrieves existing NSX services defining HTTP, HTTPS and SSH and stores them in appropriate variables. - Creates two FirewallRule Specs that use the group and services collected above and stores them in appropriate variables. - Retrieves a Security Policy using its name and adds the two precreated firewall rules. .EXAMPLE $ServiceDefinition = Get-NsxServiceDefinition -Name "MyThirdPartyFirewall" PS C:\> $ServicePolicy = $ServiceDefinition | Get-NsxServiceProfile "FirewallProfile" PS C:\> $https = Get-NsxService -Localonly | Where { $_.name -eq 'HTTPS' } PS C:\> $RedirectRule = New-NsxSecurityPolicyNetworkIntrospectionSpec -Name "MyThirdPartyRedirectRule" ` -ServiceProfile $ServicePolicy -Service $https -source Any PS C:\> Get-NsxSecurityPolicy -Name ThirdPartyRedirect | Add-NsxSecurityPolicyRule ` -NetworkIntrospectionSpec $RedirectRule Retrieves a security policy called ThirdPartyRedirect and adds a single network introspection rule to redirect traffic to a thirdparty firewall service. The specific steps to accomplish this are as follows: - Retrieves an existing Service Policy that is defined as part of the third party firewall production integration with NSX. - Retrieves an existing NSX service defining HTTPS and stores it in an appropriate variable. - Creates a Network Introspection rule spec that uses the policy collected above, that matches HTTPS traffic from any source and stores it in an appropriate variable. - Retrieves a Security Policy using its name and adds the precreated network introspection rule. .EXAMPLE $ServiceDefinition = Get-NsxServiceDefinition -Name "MyThirdPartyEndpoint" PS C:\> $ServicePolicy = $ServiceDefinition | Get-NsxServiceProfile "Profile1" PS C:\> $Endpointrule = New-NsxSecurityPolicyGuestIntrospectionSpec -Name "MyThirdPartyEndpointRule" ` -ServiceDefinition $ServiceDefinition -ServiceProfile $ServicePolicy PS C:\> Get-NsxSecurityPolicy -Name ThirdPartyEndpoint | Add-NsxSecurityPolicyRule ` -GuestIntrospection $EndpointRule Retrieves a security policy called ThirdPartyEndpoint and adds a single guest introspection rule to it. The specific steps to accomplish this are as follows: - Retrieves an existing Service Policy that is defined as part of the third party endpoint integration with NSX. - Creates a Guest Introspection rule spec that uses the policy collected above and stores it in an appropriate variable. - Retrieves a Security Policy and adds the precreated guest introspection rule. .EXAMPLE $Endpointrule = New-NsxSecurityPolicyGuestIntrospectionSpec -Name "MyThirdPartyEndpointRule" ` -Servicetype AntiVirus PS C:\> Get-NsxSecurityPolicy -Name AntiVirusEndpoint | Add-NsxSecurityPolicyRule ` -GuestIntrospection $EndpointRule Retieves a security policy called AntiVirusEndpoint and adds a single AntiVirus guest introspection rule to it. The specific steps to accomplish this are as follows: - Creates a Guest Introspection AntiVirus rule spec and stores it in an appropriate variable. - Retrieves a Security Policy using its name and adds the precreated guest introspection rule. .EXAMPLE $Endpointrule = New-NsxSecurityPolicyGuestIntrospectionSpec -Name "MyThirdPartyEndpointRule" ` -Servicetype FileIntegrityMonitoring PS C:\> Get-NsxSecurityPolicy -Name FileIntegrityEndpoint | Add-NsxSecurityPolicyRule ` -GuestIntrospection $EndpointRule Retrieves a security policy called FileIntegrityEndpoint and adds a single FileIntegrity guest introspection rule to it. The specific steps to accomplish this are as follows: - Creates a Guest Introspection FileIntegrity rule spec and stores it in an appropriate variable. - Retrieves a Security Policy using its name and adds the guest introspection rule. .EXAMPLE $Endpointrule = New-NsxSecurityPolicyGuestIntrospectionSpec -Name "MyThirdPartyEndpointRule" ` -Servicetype VulnerabilityManagement PS C:\> Get-NsxSecurityPolicy -Name VulnerabilityMgmtEndpoint | Add-NsxSecurityPolicyRule ` -GuestIntrospection $EndpointRule Retrieves a security policy called VulnerabilityMgmtEndpoint and adds a single VulnerabilityManagement guest introspection rule to it. The specific steps to accomplish this are as follows: - Creates a Guest Introspection VulnerabilityManagement rule spec and stores it in an appropriate variable. - Retrieves a Security Policy using its name and adds the precreated guest introspection rule. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeLine)] #Security Policy to retrieve rules from. [ValidateScript( { ValidateSecurityPolicy $_ })] [System.Xml.XmlElement]$SecurityPolicy, [Parameter (Mandatory = $false)] # Security Policy Firewall Rule Spec as created by New-NsxSecurityPolicyFirewallRuleSpec [ValidateScript( { ValidateSecPolFwSpec $_ })] [System.Xml.XmlElement[]]$FirewallRuleSpec, [Parameter (Mandatory = $false)] # Guest Introspection Rule Spec as created by New-NsxSecurityPolicyGuestIntrospectionSpec [ValidateScript( { ValidateSecPolGiSpec $_ })] [System.Xml.XmlElement[]]$GuestIntrospectionSpec, [Parameter (Mandatory = $false)] # Network Introspection Rule Spec as created by New-NsxSecurityPolicyNetworkIntrospectionSpec [ValidateScript( { ValidateSecPolNiSpec $_ })] [System.Xml.XmlElement[]]$NetworkIntrospectionSpec, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Creating the XML Document and root elem for Security Policy $_SecurityPolicy = $SecurityPolicy.CloneNode($true) $xmlDoc = $_SecurityPolicy.OwnerDocument #Create the firewall category actionsByCategory Elem if required if ($PSBoundParameters.ContainsKey("FirewallRuleSpec")) { $xmlFwActionsByCategory = Invoke-XpathQuery -Node $_SecurityPolicy -QueryMethod SelectSingleNode -query "actionsByCategory[category='firewall']" if ( -not $xmlFwActionsByCategory ) { $xmlFwActionsByCategory = $xmlDoc.CreateElement("actionsByCategory") $null = $_SecurityPolicy.appendChild($xmlFwActionsByCategory) Add-XmlElement -xmlRoot $xmlFwActionsByCategory -xmlElementName "category" -xmlElementText "firewall" } foreach ($rule in $FirewallRuleSpec) { #Import the new fw node $null = $xmlFwActionsByCategory.AppendChild($xmlFwActionsByCategory.OwnerDocument.ImportNode($rule, $true)) } } #Create the endpointSecurityAction actionsByCategory Elem if required. if ( $PSBoundParameters.ContainsKey("GuestIntrospectionSpec")) { $xmlEndpointActionsByCategory = Invoke-XpathQuery -Node $_SecurityPolicy -QueryMethod SelectSingleNode -query "actionsByCategory[category='endpoint']" if ( -not $xmlEndpointActionsByCategory ) { $xmlEndpointActionsByCategory = $xmlDoc.CreateElement("actionsByCategory") $null = $_SecurityPolicy.appendChild($xmlEndpointActionsByCategory) Add-XmlElement -xmlRoot $xmlEndpointActionsByCategory -xmlElementName "category" -xmlElementText "endpoint" } foreach ($rule in $GuestIntrospectionSpec) { #Import the new GI node $null = $xmlEndpointActionsByCategory.AppendChild($xmlEndpointActionsByCategory.OwnerDocument.ImportNode($rule, $true)) } } #Create the trafficSteeringSecurityAction actionsByCategory Elem if required. if ( $PSBoundParameters.ContainsKey("NetworkIntrospectionSpec")) { $xmlNetworkIntrospectionActionsByCategory = Invoke-XpathQuery -Node $_SecurityPolicy -QueryMethod SelectSingleNode -query "actionsByCategory[category='traffic_steering']" if ( -not $xmlNetworkIntrospectionActionsByCategory ) { $xmlNetworkIntrospectionActionsByCategory = $xmlDoc.CreateElement("actionsByCategory") $null = $_SecurityPolicy.appendChild($xmlNetworkIntrospectionActionsByCategory) Add-XmlElement -xmlRoot $xmlNetworkIntrospectionActionsByCategory -xmlElementName "category" -xmlElementText "traffic_steering" } foreach ($rule in $NetworkIntrospectionSpec) { #Import the new NI node $null = $xmlNetworkIntrospectionActionsByCategory.AppendChild($xmlNetworkIntrospectionActionsByCategory.OwnerDocument.ImportNode($rule, $true)) } } #Do the post $body = $_SecurityPolicy.OuterXml $URI = "/api/2.0/services/policy/securitypolicy/$($_SecurityPolicy.objectId)" $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection if ($response.StatusCode -eq "200") { [xml]$xmldoc = $response.content $xmldoc.securityPolicy } } end {} } # We are only doing set for Firewall rule for the moment. Will do GI and NI if rcustomers request. function Set-NsxSecurityPolicyFirewallRule { <# .SYNOPSIS Modifies the configuration of an existing Security Policy Rule. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The three categories of rules that can be included in a Security Policy are: - Guest Introspection - data security, anti-virus, and vulnerability management and rules based on third party Guest Introspection capability. - Firewall rules - creates appropriate distributed firewall rules when the policy is applied to a security group. - Network introspection services - Thirdparty firewall, IPS/IDS etc. Set-NsxSecurityPolicyRule modifies an existing firewall, guest introspection or network introspection rule as retrieved by Get-NsxSecurityPolicyRule It is possible to use Set-NsxSecurityPolicyFirewallRule to modify the 'direction' of a given rule. (From Policies SecurityGroup, To Policies SecurityGroup, or to and from Policies SecurityGroup ) The concept of 'direction', reflects the way the API represents the firewall rule definition rather than the UI represendation of Policies Security Group but is functionality equivalent. It requires specification of a direction (inbound outbound or intra) and for inbound/outbound directions, specific securitygroups may be specified. If no Security Group is specified, the source/destination is 'Any'. Refer to Get-Help documentation in New-NsxSecurityPolicyFirewallRuleSpec for more information. .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Set-NsxSecurityPolicyFirewallRule -Action Allow Sets the action to allow on the firewall rule called AdminSsh within the security policy SecPol01 .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Set-NsxSecurityPolicyFirewallRule -Logging Enabled Enables logging on the firewall rule called AdminSsh within the security policy SecPol01 .EXAMPLE Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Set-NsxSecurityPolicyFirewallRule #> [CmdletBinding()] param ( [Parameter (Mandatory = $True, ValueFromPipeline = $True)] # Security Policy Rule to reconfigure [ValidateScript( { ValidateSecPolRule $_ if ( $_.class -ne "firewallSecurityAction" ) { throw "Specified rule is not a firewall rule." } })] [System.Xml.XmlElement]$Rule, [Parameter ()] # Set the name of the specified rule [ValidateNotNullOrEmpty()] [String]$Name, [Parameter ()] # Set the description of the specified rule [ValidateNotNullOrEmpty()] [String]$Description, [Parameter ()] # Set the Action of the specified rule [ValidateSet("Allow", "Block", "Reject")] [String]$Action, [Parameter ()] # Configure logging behaviour for the specified rule [Boolean]$LoggingEnabled, [Parameter ()] # Enable or disable the specified rule. [Boolean]$Enabled, [Parameter ()] # Modify the 'direction' of the rule. Refer to mode '2' operation of New-NsxSecurityPolicyFirewallRuleSpec for more information. [Validateset("Inbound", "Outbound", "Intra")] [String]$Direction, [Parameter ()] # Disable confirmation prompt [switch]$NoConfirm, [Parameter (Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { # We process all rule modifications offline as part of pipeline processing, then we put the updated policies to the api in the end{} block to avoid overwriting changes to different rules in the same policy.. # Save modified policies in a hash table keyed by id Write-Debug "$($MyInvocation.MyCommand.Name) : Initialising Policy cache" $ModifiedPolicies = @{} $ModifiedRules = @() } process { $ParentPolicyObjectId = $Rule.ParentNode.ParentNode.objectId #The Rule the user specified is only used the first time we edit a given policy. Otherwise, it is just a key to the cached copy - in which case, we modify the rule as it exists in the cached copy of the policy. if ( $ModifiedPolicies.ContainsKey($ParentPolicyObjectId )) { # Policy has already been updated in this pipeline, so we modify the already updated xml. $PolicyXml = $ModifiedPolicies[$ParentPolicyObjectId] Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved specified rules parent policy from the policy cache. Policy XML is : $($PolicyXML | Format-XML)" } else { # We havent touched the policy yet, so we have to get it. We clone to avoid modifying the original. $PolicyXml = $Rule.ParentNode.ParentNode.CloneNode($true) $ModifiedPolicies.Add($ParentPolicyObjectId, $PolicyXml) Write-Debug "$($MyInvocation.MyCommand.Name) : Specified rules parent policy not found in policy cache so it has been added." } #Now get our xml from the cached policy. $_Rule = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $PolicyXml -query "actionsByCategory/action[objectId=`'$($Rule.objectId)`']" if ( -not $_Rule ) { #This should never happen throw "An unexpected error occured retrieving a rule from cache. Please report this as a bug at https://github.com/vmware/powernsx" } Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved rule from cache: $( $_Rule | Format-XML )" #And do our updates... if ( $PSBoundParameters.ContainsKey("Name")) { if ( -not ( Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Rule -query "child::name" )) { Add-XmlElement -xmlRoot $_Rule -xmlElementName "name" -xmlElementText $Name } else { $_Rule.name = $Name } } if ( $PSBoundParameters.ContainsKey("Description")) { if ( -not ( Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Rule -query "child::description" )) { Add-XmlElement -xmlRoot $_Rule -xmlElementName "description" -xmlElementText $description } else { $_Rule.description = $description } } if ( $PSBoundParameters.ContainsKey("Action")) { $_Rule.action = $Action.tolower() } if ( $PSBoundParameters.ContainsKey("LoggingEnabled")) { $_Rule.logged = $LoggingEnabled.ToString().ToLower() } if ( $PSBoundParameters.ContainsKey("Enabled")) { $_Rule.isEnabled = $Enabled.ToString().ToLower() } if ( $PSBoundParameters.ContainsKey("Direction")) { if ( $_Rule.direction -ne $Direction.ToString().ToLower()) { # We dont expect that users will do this much as it's hard to concieve of an operational reason to do so # other than fat fingers. Still - its easy to implement, so we provide it, and just warn. if ( $Direction -eq "Intra" ) { $SecondaryGroups = $null $secondaryGroups = Invoke-XpathQuery -QueryMethod SelectNodes -Node $_Rule -query "child::secondarySecurityGroup" if ( $SecondaryGroups) { Write-Warning "Specified rule specifies an explicit source or destination group. Converting the rule to direction 'Intra' will REMOVE all existing source and destination groups." foreach ( $groupnode in $SecondaryGroups ) { $null = $_Rule.RemoveChild($groupnode) } } } elseif ( $_Rule.direction -eq 'intra' ) { if ( $Direction -eq 'inbound') { $SrcDest = "source" } else { $SrcDest = "destination" } Write-Warning "Changing the direction of a rule from intra to $($Direction.toLower()) will set the $srcdest to 'any'." } else { Write-Warning "Changing the direction of a rule from $($_Rule.Direction) to $($Direction.toLower()) is equivalent to swapping it's source and destination." } } $_Rule.direction = $Direction.ToString().ToLower() } Write-Debug "$($MyInvocation.MyCommand.Name) : Rule processing complete. Updated rule xml is : $($_Rule.OuterXml)" $ModifiedRules += $_Rule.objectId } end { foreach ( $policy in $ModifiedPolicies.Values ) { $UpdatedPolicy = Set-NsxSecurityPolicy -Policy $policy -NoConfirm:$NoConfirm if ( $UpdatedPolicy) { $AllPolicyRules = Invoke-XpathQuery -QueryMethod SelectNodes -Node $UpdatedPolicy -query "actionsByCategory/action" $AllPolicyRules | Where-Object { $ModifiedRules -contains $_.objectId } } } } } function Set-NsxLoadBalancerPool { <# .SYNOPSIS Modified a LoadBalancer Pool on the specified ESG. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. A pool manages load balancer distribution methods and has a service monitor attached to it for health check parameters. Each Pool has one or more members. Prior to creating or updating a pool to add a member, a member spec describing the member needs to be created. This cmdlet modified LoadBalancer Pool on the specified ESG. .EXAMPLE $MyLBPool = Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Get-NsxLoadBalancerPool $MyLBPool | Set-NsxLoadBalancerPool -Name WebPool -Description "WebServer Pool" Update Name and Description of LoadBalancer Pool .EXAMPLE $MyLBPool = Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Get-NsxLoadBalancerPool $MyLBPool | Set-NsxLoadBalancerPool -Transparent Enable transparent mode in Load Balancer Pool .EXAMPLE $MyLBPool = Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Get-NsxLoadBalancerPool $MyLBPool | Set-NsxLoadBalancerPool -Transparent:$false Disable transparent mode in Load Balancer Pool .EXAMPLE $MyLBPool = Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Get-NsxLoadBalancerPool $MyLBPool | Set-NsxLoadBalancerPool -Algorithm ip-hash Choose the algorithm emergency (round-robin, ip-hash, uri, leastconn) of Load Balancer Pool #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateLoadBalancerPool $_ })] [System.Xml.XmlElement]$LoadBalancerPool, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $False)] [ValidateNotNull()] [string]$Description = "", [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [switch]$Transparent, [Parameter (Mandatory = $false)] [ValidateSet("round-robin", "ip-hash", "uri", "leastconn")] [string]$Algorithm, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_LoadBalancerPool = $LoadBalancerPool.CloneNode($true) #Store the poolId $poolId = $_LoadBalancerPool.poolId $poolname = $_LoadBalancerPool.name #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_LoadBalancerPool.edgeId $_LoadBalancerPool.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_LoadBalancerPool -query 'descendant::edgeId')) ) | Out-Null if ( $PsBoundParameters.ContainsKey('name') ) { $_LoadBalancerPool.name = $name $poolname = $name } if ( $PsBoundParameters.ContainsKey('Description') ) { $_LoadBalancerPool.description = $Description } if ( $PsBoundParameters.ContainsKey('Transparent') ) { if ( $Transparent ) { $_LoadBalancerPool.Transparent = "true" } else { $_LoadBalancerPool.Transparent = "false" } } if ( $PsBoundParameters.ContainsKey('Algorithm') ) { $_LoadBalancerPool.Algorithm = $Algorithm } $URI = "/api/4.0/edges/$EdgeId/loadbalancer/config/pools/$poolId" $body = $_LoadBalancerPool.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Status "Load Balancer Pool Config" $response = Invoke-NsxWebRequest -method "PUT" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed $UpdatedEdge = Get-NsxEdge -objectId $($EdgeId) -Connection $connection $return = $UpdatedEdge.features.loadBalancer.pool | Where-Object { $_.name -eq $poolname } Add-XmlElement -xmlRoot $return -xmlElementName "edgeId" -xmlElementText $edgeId $return } end {} } function Add-NsxSecurityPolicyRuleGroup { <# .SYNOPSIS Modifies the configuration of an existing Security Policy Firewall or Network Introspection Rule to add a source or destination group. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The three categories of rules that can be included in a Security Policy are: - Guest Introspection - data security, anti-virus, and vulnerability management and rules based on third party Guest Introspection capability. - Firewall rules - creates appropriate distributed firewall rules when the policy is applied to a security group. - Network introspection services - Thirdparty firewall, IPS/IDS etc. Add-NsxSecurityPolicyRuleGroup modifies the configuration of an existing Security Policy Firewall or Network Introspection Rule to add a source or destination group. Note: Whether the group is added to the source or destination of a rule is a function of its configured direction. It is only meaningful to modify the source groups of a rule whose direction is 'inbound' (Destination = 'Policies Security Group'), or the destination groups of a rule whose direction is 'outbound' (Source = 'Policies Security Group'), and it is never meaningful to modify the source or destination groups of a rule whose direction is 'intra' (Source and Destination = 'Policies Security Group'). You can use Set-NsxSecurityPolicyRule to change the direction of a rule if necessary. Refer to Get-Help documentation in New-NsxSecurityPolicyFirewallRuleSpec for more information on direction as it relates to 'Policies Security Group'. Adding a security group to an existing rule whose current source/destination is 'any' makes the rule MORE restrictive in what traffic it applies to than it currently is, but adding subsequent groups to a rule whose current source or destination already specifies a group makes it LESS restrictive. As Dale would say... 'Think about it Kohei!' :) .EXAMPLE $grp = New-NsxSecurityGroup MySpecialServers -IncludeMember (Get-VM specialvm*) Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Add-NsxSecurityPolicyRuleGroup -Group $grp Creates a new group called MySpecialServers with static membership of any vm whose name starts with the string 'specialvm' and adds it to the source or destination of the Firewall rule AdminSsh within the Security Policy SecPol01 #> [CmdletBinding()] param ( [Parameter(Mandatory = $True, ValueFromPipeline = $True)] # Security Policy Rule to reconfigure [ValidateScript( { ValidateSecPolRule $_ if ( ($_.class -ne "firewallSecurityAction") -and ($_.class -ne "trafficSteeringSecurityAction") ) { throw "Specified rule is not a firewall or network introspection rule" } })] [System.Xml.XmlElement]$Rule, [Parameter(Mandatory = $true)] # Group(s) to be added to source or destination of specified rule. Depends on currently configured direction of the rule. [ValidateScript( { ValidateSecurityGroup $_ })] [System.Xml.XmlElement[]]$SecurityGroup, [Parameter()] # Disable confirmation prompt [switch]$NoConfirm, [Parameter(Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { # We process all rule modifications offline as part of pipeline processing, then we put the updated policies to the api in the end{} block to avoid overwriting changes to different rules in the same policy.. # Save modified policies in a hash table keyed by id Write-Debug "$($MyInvocation.MyCommand.Name) : Initialising Policy cache" $ModifiedPolicies = @{} $ModifiedRules = @() } process { if ( $Rule.direction -eq "intra") { throw "Unable to add groups to rule $($_Rule.Name) ($($_Rule.objectId)) because it's source and destination are Policies Security Group (direction intra)" } $ParentPolicyObjectId = $Rule.ParentNode.ParentNode.objectId #The Rule the user specified is only used the first time we edit a given policy. Otherwise, it is just a key to the cached copy - in which case, we modify the rule as it exists in the cached copy of the policy. if ( $ModifiedPolicies.ContainsKey($ParentPolicyObjectId )) { # Policy has already been updated in this pipeline, so we modify the already updated xml. $PolicyXml = $ModifiedPolicies[$ParentPolicyObjectId] Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved specified rules parent policy from the policy cache. Policy XML is : $($PolicyXML | Format-XML)" } else { # We havent touched the policy yet, so we have to get it. We clone to avoid modifying the original. $PolicyXml = $Rule.ParentNode.ParentNode.CloneNode($true) $ModifiedPolicies.Add($ParentPolicyObjectId, $PolicyXml) Write-Debug "$($MyInvocation.MyCommand.Name) : Specified rules parent policy not found in policy cache so it has been added." } #Now get our xml from the cached policy. $_Rule = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $PolicyXml -query "actionsByCategory/action[objectId=`'$($Rule.objectId)`']" if ( -not $_Rule ) { #This should never happen throw "An unexpected error occured retrieving a rule from cache. Please report this as a bug at https://github.com/vmware/powernsx" } Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved rule from cache: $( $_Rule | Format-XML )" #Iterate securitygroups. foreach ( $Group in $SecurityGroup) { if ( Invoke-XpathQuery -Node $_Rule -QueryMethod SelectSingleNode -query "child::secondarySecurityGroup[objectId=`'$($Group.objectId)`']" ) { Write-Warning "Group $($Group.name) ($($Group.objectId)) is already configured in rule $($_Rule.name) ($($_Rule.objectId))." } else { $xmlSecurityGroup = $_Rule.OwnerDocument.CreateElement("secondarySecurityGroup") $_Rule.appendChild($xmlSecurityGroup) | Out-Null Add-XmlElement -xmlRoot $xmlSecurityGroup -xmlElementName "objectId" -xmlElementText $group.objectId Write-Debug "$($MyInvocation.MyCommand.Name) : Added group $($group.objectId) to rule $($_Rule.Name)" } } Write-Debug "$($MyInvocation.MyCommand.Name) : Rule processing complete. Updated rule xml is : $($_Rule.OuterXml | Format-XML)" $ModifiedRules += $_Rule.objectId } end { foreach ( $policy in $ModifiedPolicies.Values ) { $UpdatedPolicy = Set-NsxSecurityPolicy -Policy $policy -NoConfirm:$NoConfirm if ( $UpdatedPolicy) { $AllPolicyRules = Invoke-XpathQuery -QueryMethod SelectNodes -Node $UpdatedPolicy -query "actionsByCategory/action" $AllPolicyRules | Where-Object { $ModifiedRules -contains $_.objectId } } } } } function Remove-NsxSecurityPolicyRuleGroup { <# .SYNOPSIS Modifies the configuration of an existing Security Policy Firewall or Network Introspection Rule to remove a source or destination group. Note: If the group to be removed is the last one defined, then the source or destination of the rule becomes ANY. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The three categories of rules that can be included in a Security Policy are: - Guest Introspection - data security, anti-virus, and vulnerability management and rules based on third party Guest Introspection capability. - Firewall rules - creates appropriate distributed firewall rules when the policy is applied to a security group. - Network introspection services - Thirdparty firewall, IPS/IDS etc. Remove-NsxSecurityPolicyRuleGroup modifies the configuration of an existing Security Policy Firewall or Network Introspection Rule to add a source or destination group. Note: Whether the group is removed from the source or destination of a rule is a function of its configured direction. It is only meaningful to modify the source groups of a rule whose direction is 'inbound' (Destination = 'Policies Security Group'), or the destination groups of a rule whose direction is 'outbound' (Source = 'Policies Security Group'), and it is never meaningful to modify the source or destination groups of a rule whose direction is 'intra' (Source and Destination = 'Policies Security Group'). You can use Set-NsxSecurityPolicyRule to change the direction of a rule if necessary. Refer to Get-Help documentation in New-NsxSecurityPolicyFirewallRuleSpec for more information on direction as it relates to 'Policies Security Group'. Adding a security group to an existing rule whose current source/destination is 'any' makes the rule MORE restrictive in what traffic it applies to than it currently is, but adding subsequent groups to a rule whose current source or destination already specifies a group makes it LESS restrictive. As Dale would say... 'Think about it Kohei!' :) .EXAMPLE $grp = New-NsxSecurityGroup MySpecialServers -IncludeMember (Get-VM specialvm*) Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Add-NsxSecurityPolicyRuleGroup -Group $grp Creates a new group called MySpecialServers with static membership of any vm whose name starts with the string 'specialvm' and adds it to the source or destination of the Firewall rule AdminSsh within the Security Policy SecPol01 #> [CmdletBinding()] param ( [Parameter(Mandatory = $True, ValueFromPipeline = $True)] # Security Policy Rule to reconfigure [ValidateScript( { ValidateSecPolRule $_ if ( ($_.class -ne "firewallSecurityAction") -and ($_.class -ne "trafficSteeringSecurityAction") ) { throw "Specified rule is not a firewall or network introspection rule" } })] [System.Xml.XmlElement]$Rule, [Parameter(Mandatory = $true)] # Group(s) to be added to source or destination of specified rule. Depends on currently configured direction of the rule. [ValidateScript( { ValidateSecurityGroup $_ })] [System.Xml.XmlElement[]]$SecurityGroup, [Parameter()] # Disable confirmation prompt [switch]$NoConfirm, [Parameter()] # Disable confirmation prompt for removal of last group - effectively converting rule to match ANY in the configured source or destination. [switch]$NoConfirmOnLastGroupRemoval, [Parameter(Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { # We process all rule modifications offline as part of pipeline processing, then we put the updated policies to the api in the end{} block to avoid overwriting changes to different rules in the same policy.. # Save modified policies in a hash table keyed by id Write-Debug "$($MyInvocation.MyCommand.Name) : Initialising Policy cache" $ModifiedPolicies = @{} $ModifiedRules = @() } process { if ( $Rule.direction -eq "intra") { # We don't throw to avoid killing pipeline processing, but we warn that we cant do diddly to this particular rule. Write-Warning "Unable to remove groups from rule $($_Rule.Name) ($($_Rule.objectId)) because it's source and destination are Policies Security Group (direction intra)" break } if ( -not (Invoke-XpathQuery -Node $Rule -QueryMethod selectsinglenode -query "child::secondarySecurityGroup" )) { Write-Warning "Unable to remove groups from rule $($_Rule.Name) ($($_Rule.objectId)) because it is configured with source or destination of 'any'." break } $ParentPolicyObjectId = $Rule.ParentNode.ParentNode.objectId #The Rule the user specified is only used the first time we edit a given policy. Otherwise, it is just a key to the cached copy - in which case, we modify the rule as it exists in the cached copy of the policy. if ( $ModifiedPolicies.ContainsKey($ParentPolicyObjectId )) { # Policy has already been updated in this pipeline, so we modify the already updated xml. $PolicyXml = $ModifiedPolicies[$ParentPolicyObjectId] Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved specified rules parent policy from the policy cache. Policy XML is : $($PolicyXML | Format-XML)" } else { # We havent touched the policy yet, so we have to get it. We clone to avoid modifying the original. $PolicyXml = $Rule.ParentNode.ParentNode.CloneNode($true) $ModifiedPolicies.Add($ParentPolicyObjectId, $PolicyXml) Write-Debug "$($MyInvocation.MyCommand.Name) : Specified rules parent policy not found in policy cache so it has been added." } #Now get our xml from the cached policy. $_Rule = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $PolicyXml -query "actionsByCategory/action[objectId=`'$($Rule.objectId)`']" if ( -not $_Rule ) { #This should never happen throw "An unexpected error occured retrieving a rule from cache. Please report this as a bug at https://github.com/vmware/powernsx" } Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved rule from cache: $( $_Rule | Format-XML )" #Iterate securitygroups. foreach ( $Group in $SecurityGroup) { #Catch the special case of removal of the last group - the makes the rule apply to ALL traffic - need to ensure its what we want. if (((Invoke-XpathQuery -Node $_Rule -QueryMethod selectNodes -query "child::secondarySecurityGroup" ) | Measure-Object ).count -eq 1 ) { if ( -Not $NoConfirmOnLastGroupRemoval ) { $message = "The last security group configured on rule $($_Rule.Name) ($($_Rule.objectId)) in policy $ParentPolicyObjectId is being removed. This will result in the rules source or destination being configured as 'any'." $question = "Are you sure this is what you want?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -ne 0) { throw "Aborting on user request." } } $xmlSecurityGroup = Invoke-XpathQuery -Node $_Rule -QueryMethod SelectSingleNode -query "child::secondarySecurityGroup[objectId=`'$($Group.objectId)`']" if ( -not $xmlSecurityGroup ) { Write-Warning "Group $($Group.name) ($($Group.objectId)) not configured in rule $($_Rule.name) ($($_Rule.objectId))." } else { $_Rule.removeChild($xmlSecurityGroup) | Out-Null Write-Debug "$($MyInvocation.MyCommand.Name) : Removed group $($group.objectId) from rule $($_Rule.Name)" } } Write-Debug "$($MyInvocation.MyCommand.Name) : Rule processing complete. Updated rule xml is : $($_Rule.OuterXml | Format-XML)" $ModifiedRules += $_Rule.objectId } end { foreach ( $policy in $ModifiedPolicies.Values ) { $UpdatedPolicy = Set-NsxSecurityPolicy -Policy $policy -NoConfirm:$NoConfirm if ( $UpdatedPolicy) { $AllPolicyRules = Invoke-XpathQuery -QueryMethod SelectNodes -Node $UpdatedPolicy -query "actionsByCategory/action" $AllPolicyRules | Where-Object { $ModifiedRules -contains $_.objectId } } } } } function Add-NsxSecurityPolicyRuleService { <# .SYNOPSIS Modifies the configuration of an existing Security Policy Firewall or Network Introspection Rule to add a service. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The three categories of rules that can be included in a Security Policy are: - Guest Introspection - data security, anti-virus, and vulnerability management and rules based on third party Guest Introspection capability. - Firewall rules - creates appropriate distributed firewall rules when the policy is applied to a security group. - Network introspection services - Thirdparty firewall, IPS/IDS etc. Add-NsxSecurityPolicyRuleService modifies the configuration of an existing Security Policy Firewall or Network Introspection Rule to add a service. .EXAMPLE $svc = New-NsxService -Name AltSsh -Protocol TCP -port 2222 Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Add-NsxSecurityPolicyRuleservice -Service $svc Creates a new service called AltSsh and adds it to the Firewall rule AdminSsh within the Security Policy SecPol01 #> [CmdletBinding()] param ( [Parameter(Mandatory = $True, ValueFromPipeline = $True)] # Security Policy Rule to reconfigure [ValidateScript( { ValidateSecPolRule $_ if ( ($_.class -ne "firewallSecurityAction") -and ($_.class -ne "trafficSteeringSecurityAction") ) { throw "Specified rule is not a firewall or network introspection rule" } })] [System.Xml.XmlElement]$Rule, [Parameter(Mandatory = $true)] # Group(s) to be added to source or destination of specified rule. Depends on currently configured direction of the rule. [ValidateScript( { ValidateService $_ })] [System.Xml.XmlElement[]]$Service, [Parameter()] # Disable confirmation prompt [switch]$NoConfirm, [Parameter(Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { # We process all rule modifications offline as part of pipeline processing, then we put the updated policies to the api in the end{} block to avoid overwriting changes to different rules in the same policy.. # Save modified policies in a hash table keyed by id Write-Debug "$($MyInvocation.MyCommand.Name) : Initialising Policy cache" $ModifiedPolicies = @{} $ModifiedRules = @() } process { $ParentPolicyObjectId = $Rule.ParentNode.ParentNode.objectId #The Rule the user specified is only used the first time we edit a given policy. Otherwise, it is just a key to the cached copy - in which case, we modify the rule as it exists in the cached copy of the policy. if ( $ModifiedPolicies.ContainsKey($ParentPolicyObjectId )) { # Policy has already been updated in this pipeline, so we modify the already updated xml. $PolicyXml = $ModifiedPolicies[$ParentPolicyObjectId] Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved specified rules parent policy from the policy cache. Policy XML is : $($PolicyXML | Format-XML)" } else { # We havent touched the policy yet, so we have to get it. We clone to avoid modifying the original. $PolicyXml = $Rule.ParentNode.ParentNode.CloneNode($true) $ModifiedPolicies.Add($ParentPolicyObjectId, $PolicyXml) Write-Debug "$($MyInvocation.MyCommand.Name) : Specified rules parent policy not found in policy cache so it has been added." } #Now get our xml from the cached policy. $_Rule = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $PolicyXml -query "actionsByCategory/action[objectId=`'$($Rule.objectId)`']" if ( -not $_Rule ) { #This should never happen throw "An unexpected error occured retrieving a rule from cache. Please report this as a bug at https://github.com/vmware/powernsx" } Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved rule from cache: $( $_Rule | Format-XML )" #Iterate services. foreach ( $Svc in $Service) { #Make sure we have the applications parent node. $ApplicationsNode = Invoke-XpathQuery -Node $_Rule -QueryMethod SelectSingleNode -query "child::applications" if ( -not ($applicationsNode)) { $ApplicationsNode = $_Rule.OwnerDocument.CreateElement("applications") $null = $_Rule.appendChild($ApplicationsNode) } if ( Invoke-XpathQuery -Node $_Rule -QueryMethod SelectSingleNode -query "child::applications/application[objectId=`'$($Svc.objectId)`']" ) { Write-Warning "Service $($Svc.name) ($($Svc.objectId)) is already configured in rule $($_Rule.name) ($($_Rule.objectId))." } else { $Application = $_Rule.OwnerDocument.CreateElement("application") $null = $ApplicationsNode.appendChild($Application) Add-XmlElement -xmlRoot $Application -xmlElementName "objectId" -xmlElementText $Svc.objectId Write-Debug "$($MyInvocation.MyCommand.Name) : Added service $($Svc.objectId) to rule $($_Rule.Name)" } } Write-Debug "$($MyInvocation.MyCommand.Name) : Rule processing complete. Updated rule xml is : $($_Rule.OuterXml | Format-XML)" $ModifiedRules += $_Rule.objectId } end { foreach ( $policy in $ModifiedPolicies.Values ) { $UpdatedPolicy = Set-NsxSecurityPolicy -Policy $policy -NoConfirm:$NoConfirm if ( $UpdatedPolicy) { $AllPolicyRules = Invoke-XpathQuery -QueryMethod SelectNodes -Node $UpdatedPolicy -query "actionsByCategory/action" $AllPolicyRules | Where-Object { $ModifiedRules -contains $_.objectId } } } } } function Remove-NsxSecurityPolicyRuleService { <# .SYNOPSIS Modifies the configuration of an existing Security Policy Firewall or Network Introspection Rule to remove a service. Note: If the service to be removed is the last one defined, then the matching service for the rule becomes ANY. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The three categories of rules that can be included in a Security Policy are: - Guest Introspection - data security, anti-virus, and vulnerability management and rules based on third party Guest Introspection capability. - Firewall rules - creates appropriate distributed firewall rules when the policy is applied to a security group. - Network introspection services - Thirdparty firewall, IPS/IDS etc. Remove-NsxSecurityPolicyRuleService modifies the configuration of an existing Security Policy Firewall or Network Introspection Rule to remove a service. .EXAMPLE $svc = Get-NsxService -Name AltSsh Get-NsxSecurityPolicy SecPol01 | Get-NsxSecurityPolicyRule -RuleType Firewall -Name AdminSsh | Remove-NsxSecurityPolicyRuleservice -Service $svc Gets the service called AltSsh and removes it from the Firewall rule AdminSsh within the Security Policy SecPol01 #> [CmdletBinding()] param ( [Parameter(Mandatory = $True, ValueFromPipeline = $True)] # Security Policy Rule to reconfigure [ValidateScript( { ValidateSecPolRule $_ if ( ($_.class -ne "firewallSecurityAction") -and ($_.class -ne "trafficSteeringSecurityAction") ) { throw "Specified rule is not a firewall or network introspection rule" } })] [System.Xml.XmlElement]$Rule, [Parameter(Mandatory = $true)] # Services(s) to be removed from the specified rule. Depends on currently configured direction of the rule. [ValidateScript( { ValidateService $_ })] [System.Xml.XmlElement[]]$Service, [Parameter()] # Disable confirmation prompt [switch]$NoConfirm, [Parameter()] # Disable confirmation prompt for removal of last service - effectively converting rule to match ANY service. [switch]$NoConfirmOnLastServiceRemoval, [Parameter(Mandatory = $False)] # PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { # We process all rule modifications offline as part of pipeline processing, then we put the updated policies to the api in the end{} block to avoid overwriting changes to different rules in the same policy.. # Save modified policies in a hash table keyed by id Write-Debug "$($MyInvocation.MyCommand.Name) : Initialising Policy cache" $ModifiedPolicies = @{} $ModifiedRules = @() } process { if ( -not (Invoke-XpathQuery -Node $Rule -QueryMethod selectsinglenode -query "child::applications" )) { Write-Warning "Unable to remove service from rule $($Rule.Name) ($($Rule.objectId)) because it is configured with service of 'any'." break } $ParentPolicyObjectId = $Rule.ParentNode.ParentNode.objectId #The Rule the user specified is only used the first time we edit a given policy. Otherwise, it is just a key to the cached copy - in which case, we modify the rule as it exists in the cached copy of the policy. if ( $ModifiedPolicies.ContainsKey($ParentPolicyObjectId )) { # Policy has already been updated in this pipeline, so we modify the already updated xml. $PolicyXml = $ModifiedPolicies[$ParentPolicyObjectId] Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved specified rules parent policy from the policy cache. Policy XML is : $($PolicyXML | Format-XML)" } else { # We havent touched the policy yet, so we have to get it. We clone to avoid modifying the original. $PolicyXml = $Rule.ParentNode.ParentNode.CloneNode($true) $ModifiedPolicies.Add($ParentPolicyObjectId, $PolicyXml) Write-Debug "$($MyInvocation.MyCommand.Name) : Specified rules parent policy not found in policy cache so it has been added." } #Now get our xml from the cached policy. $_Rule = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $PolicyXml -query "actionsByCategory/action[objectId=`'$($Rule.objectId)`']" if ( -not $_Rule ) { #This should never happen throw "An unexpected error occured retrieving a rule from cache. Please report this as a bug at https://github.com/vmware/powernsx" } Write-Debug "$($MyInvocation.MyCommand.Name) : Retrieved rule from cache: $( $_Rule | Format-XML )" #Iterate securitygroups. foreach ( $Svc in $Service) { $ServiceXml = Invoke-XpathQuery -Node $_Rule -QueryMethod SelectSingleNode -query "child::applications/application[objectId=`'$($Svc.objectId)`']" if ( -not $ServiceXml ) { Write-Warning "Service $($Svc.name) ($($Svc.objectId)) not configured in rule $($_Rule.name) ($($_Rule.objectId))." } else { #Catch the special case of removal of the last service - the makes the rule apply to ALL traffic - need to ensure its what we want. if (((Invoke-XpathQuery -Node $_Rule -QueryMethod selectNodes -query "child::applications/application" ) | Measure-Object ).count -eq 1 ) { if ( -Not $NoConfirmOnLastServiceRemoval ) { $message = "The last service configured on rule $($_Rule.Name) ($($_Rule.objectId)) in policy $ParentPolicyObjectId is being removed. This will result in the rule matching 'any' service." $question = "Are you sure this is what you want?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -ne 0) { throw "Aborting on user request." } } $_Rule.applications.removeChild($ServiceXml) | Out-Null Write-Debug "$($MyInvocation.MyCommand.Name) : Removed service $($Svc.objectId) from rule $($_Rule.Name)" } } Write-Debug "$($MyInvocation.MyCommand.Name) : Rule processing complete. Updated rule xml is : $($_Rule.OuterXml | Format-XML)" $ModifiedRules += $_Rule.objectId } end { foreach ( $policy in $ModifiedPolicies.Values ) { $UpdatedPolicy = Set-NsxSecurityPolicy -Policy $policy -NoConfirm:$NoConfirm if ( $UpdatedPolicy) { $AllPolicyRules = Invoke-XpathQuery -QueryMethod SelectNodes -Node $UpdatedPolicy -query "actionsByCategory/action" $AllPolicyRules | Where-Object { $ModifiedRules -contains $_.objectId } } } } } function Get-NsxApplicableSecurityAction { <# .SYNOPSIS Retrieves Security Policy actions (rules) associated with NSX Security Groups, Security Policies, or Virtual Machines. .DESCRIPTION A security policy is a policy construct that can define one or more rules in several different categories, that can then be applied to an arbitrary number of Security Groups in order to enforce the defined policy. The three categories of rules that can be included in a Security Policy are: - Guest Introspection - data security, anti-virus, and vulnerability management and rules based on third party Guest Introspection capability. - Firewall rules - creates appropriate distributed firewall rules when the policy is applied to a security group. - Network introspection services - Thirdparty firewall, IPS/IDS etc. Get-NsxApplicableFSecurityAction retrieves the security actions applicable to a given object. Actions may be firewall, traffic redirection or guest introspection. .EXAMPLE $SG_Test = Get-NsxSecurityGroup "SG_Test" PS C:\> $SG_Test | Get-NsxApplicableSecurityAction .EXAMPLE $VM_Test = Get-VM -name "VM_Test" PS C:\> $VM_Test | Get-NsxApplicableSecurityAction .EXAMPLE $SP_Test = Get-NsxSecurityPolicy -name "SP_Test" PS C:\> Get-NsxApplicableSecurityAction -SecurityPolicy $SP_Test #> [CmdLetBinding()] param ( [Parameter (Mandatory = $True, ValueFromPipeline = $true)] # Object(s) to retrieve applicable rules for. Can be a SecurityGroup, Security Policy or Virtual Machine [ValidateScript( { $arg = $_ try { ValidateSecurityGroup $arg } catch { try { ValidateSecurityPolicy $arg } catch { try { ValidateVirtualMachine $arg } catch { throw "Object specified is not a SecurityGroup, SecurityPolicy or Virtual Machine. $($arg.gettype())" } } } })] [object[]]$Object, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { foreach ( $obj in $object ) { # Work out what type of object we have. if ( $obj -is [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop] ) { $URI = "/api/2.0/services/policy/virtualmachine/$($obj.ExtensionData.MoRef.Value)/securityactions" } else { if ( $obj.ObjectTypeName -eq "SecurityGroup" ) { $URI = "/api/2.0/services/policy/securitygroup/$($obj.objectId)/securityactions" } elseif ( $obj.ObjectTypeName -eq "Policy" ) { $URI = "/api/2.0/services/policy/securitypolicy/$($obj.objectId)/securityactions" } else { throw "Unsupported objecttype specified." } } #Make the call try { $response = Invoke-NsxRestMethod -URI $Uri -method Get -connection $connection $ApplicableActions = Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query "child::securityActionsByCategoryMap/actionsByCategory/action" if ( $ApplicableActions ) { $response.securityActionsByCategoryMap.actionsByCategory.action } } catch { throw "Failed retrieving applicable actions. $_" } } } end {} } ###### # IPsec function Get-NsxIPsecStats { <# .SYNOPSIS Retrieves NSX Edge VPN IPsec statistics .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge load balancer enables network traffic to follow multiple paths to a specific destination. It distributes incoming service requests evenly among multiple servers in such a way that the load distribution is transparent to users. Load balancing thus helps in achieving optimal resource utilization, maximizing throughput, minimizing response time, and avoiding overload. NSX Edge provides load balancing up to Layer 7. This cmdlet retrieves NSX Edge VPN IPSec statistics .EXAMPLE Get-NsxEdge edge01 | Get-NsxIPsecStats Retrieves the VPN IPsec stats on Edge01 #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $URI = "/api/4.0/edges/$($Edge.Id)/ipsec/statistics" [system.xml.xmldocument]$response = Invoke-NsxRestMethod -method "GET" -URI $URI -connection $connection if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $response -query "child::ipsecStatusAndStats")) { $response.ipsecStatusAndStats } } end {} } ######## ######## # Extra functions - here we try to extend on the capability of the base API, rather than just exposing it... function Get-NsxSecurityGroupEffectiveMember { <# .SYNOPSIS Determines the effective memebership of a security group. .DESCRIPTION An NSX SecurityGroup can contain members (VMs, IP Addresses, MAC Addresses or interfaces) by virtue of direct, or indirect membership (nested security groups), and either by static or dynamic inclusion. In addition, direct or indirect exclusions can also modify membership. This cmdlet uses the NSX 'Translation APIs' to determine the 'Effective Membership' of a given security group. The membership output by Get-NsxSecurityGroupEffectiveMember is determined by NSX itself. Note: In order for IPAddress membership to be accurate, IP Discovery of virtual machines must be operational (as it must for the dataplane to function as well.) If IPAddress membership is not accurately represented here, verify that an appropriate IP discovery mechanism is operational, and NSX 'detects' the ip addresses you are expecting. Using the Get-NsxSpoofguardNic cmdlet will allow visibility of the detection state of a given nic or VM. Note: Previous versions of this cmdlet included direct static inclusions (only) which is not useful in the context of determining 'effective membership' and has been removed. If you wish to know how a given SG is configured with respect to inclusions/exclusions, use the Get-NsxSecurityGroup cmdlet. Return properties have also been renamed to make their function clearer, and the cmdlet renamed to be consistent with PowerShell naming convention (singular). Note: In addition to this cmdlet, four individual wrapper cmdlets exist that allow a translation query for a specific object type (ie vms only) to be executed, and whose output is easier to parse for intelligent monkeys. Review Get-NsxSecurityGroupEffectiveVirtualMachine, Get-NsxSecurityGroupEffectiveIpAddress, Get-NsxSecurityGroupEffectiveMacAddress, Get-NsxSecurityGroupEffectiveVnic for more information. .EXAMPLE Get-NsxSecurityGroup TestSG | Get-NsxSecurityGroupEffectiveMembers Retrieve the effective membership of the securitygroup testsg by passing the securitygroup object on the pipline. .EXAMPLE Get-NsxSecurityGroupEffectiveMembers -SecurityGroupId securitygroup-1234 Retrieve the effective membership of a securitygroup by passing the securitygroup objectid. .EXAMPLE $testSG | Get-NsxSecurityGroupEffectiveMembers -ReturnTypes -ReturnTypes VirtualMachine, Vnic Retrieve just the VM and VNIC effective membership of the SecurityGroup stored in $testSG #> [CmdLetBinding(DefaultParameterSetName = "object")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "object")] [ValidateNotNull()] [System.Xml.XmlElement]$SecurityGroup, [Parameter (Mandatory = $true, Position = 1, ParameterSetName = "objectid" )] [ValidateScript ( { if ( -not $_ -match 'securitygroup-\d+') { throw "Specify a valid SecurityGroup id" } else { $true } })] [string]$SecurityGroupId, [Parameter (Mandatory = $false)] [ValidateSet("All", "VirtualMachine", "IpAddress", "MacAddress", "Vnic")] [string[]]$ReturnTypes = "All", [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { $EffectiveVMNodes = $null $EffectiveIPNodes = $null $EffectiveMACNodes = $null $EffectiveVNICNodes = $null if ( $PSCmdlet.ParameterSetName -eq "object" ) { $sgid = $SecurityGroup.ObjectId } else { $sgid = $SecurityGroupId } if ( ($ReturnTypes -eq "All") -or ($ReturnTypes -eq "VirtualMachine")) { Write-Debug "$($MyInvocation.MyCommand.Name) : Getting effective VM membership for Security Group $sgid" $URI = "/api/2.0/services/securitygroup/$sgid/translation/virtualmachines" $response = Invoke-NsxWebRequest -method "get" -URI $URI -connection $connection if ( $response.content -as [system.xml.xmldocument] ) { Write-Debug "$($MyInvocation.MyCommand.Name) : got xml response from api" [system.xml.xmldocument]$body = $response.content if ( $body.GetElementsByTagName("vmnodes").haschildnodes) { $EffectiveVMNodes = $body.GetElementsByTagName("vmnodes") } } } if ( ($ReturnTypes -eq "All") -or ($ReturnTypes -eq "IpAddress")) { Write-Debug "$($MyInvocation.MyCommand.Name) : Getting effective ipaddress membership for Security Group $sgid" $URI = "/api/2.0/services/securitygroup/$sgid/translation/ipaddresses" $response = Invoke-NsxWebRequest -method "get" -URI $URI -connection $connection if ( $response.content -as [system.xml.xmldocument] ) { Write-Debug "$($MyInvocation.MyCommand.Name) : got xml response from api" [system.xml.xmldocument]$body = $response.content if ( $body.GetElementsByTagName("ipNodes").haschildnodes) { $EffectiveIPNodes = $body.GetElementsByTagName("ipNodes") } } } if ( ($ReturnTypes -eq "All") -or ($ReturnTypes -eq "MacAddress")) { Write-Debug "$($MyInvocation.MyCommand.Name) : Getting effective macaddress membership for Security Group $sgid" $URI = "/api/2.0/services/securitygroup/$sgid/translation/macaddresses" $response = Invoke-NsxWebRequest -method "get" -URI $URI -connection $connection if ( $response.content -as [system.xml.xmldocument] ) { Write-Debug "$($MyInvocation.MyCommand.Name) : got xml response from api" [system.xml.xmldocument]$body = $response.content if ( $body.GetElementsByTagName("macNodes").haschildnodes) { $EffectiveMACNodes = $body.GetElementsByTagName("macNodes") } } } if ( ($ReturnTypes -eq "All") -or ($ReturnTypes -eq "Vnic")) { Write-Debug "$($MyInvocation.MyCommand.Name) : Getting effective vnic membership for Security Group $sgid" $URI = "/api/2.0/services/securitygroup/$sgid/translation/vnics" $response = Invoke-NsxWebRequest -method "get" -URI $URI -connection $connection if ( $response.content -as [system.xml.xmldocument] ) { Write-Debug "$($MyInvocation.MyCommand.Name) : got xml response from api" [system.xml.xmldocument]$body = $response.content if ( $body.GetElementsByTagName("vnicNodes").haschildnodes) { $EffectiveVNICNodes = $body.GetElementsByTagName("vnicNodes") } } } [pscustomobject]@{ "VirtualMachine" = $EffectiveVMNodes "IpAddress" = $EffectiveIPNodes "MacAddress" = $EffectiveMACNodes "Vnic" = $EffectiveVNICNodes } } end {} } function Get-NsxSecurityGroupEffectiveVirtualMachine { <# .SYNOPSIS Determines the effective VM membership of a security group. .DESCRIPTION An NSX SecurityGroup can contain members (VMs, IP Addresses, MAC Addresses or interfaces) by virtue of direct, or indirect membership (nested security groups), and either by static or dynamic inclusion. In addition, direct or indirect exclusions can also modify membership. This cmdlet uses the NSX 'Translation APIs' to determine the 'Effective VM Membership' of a given security group. The membership output by this cmdlet is determined by NSX itself. .EXAMPLE Get-NsxSecurityGroup testSG | Get-NsxSecurityGroupEffectiveVirtualMachine VmName VmId ------ ---- Web01 vm-1270 Determine the effective VM membership of testSG .EXAMPLE Get-NsxSecurityGroupEffectiveVirtualMachine -SecurityGroupId securitygroup-1234 VmName VmId ------ ---- Web01 vm-1270 Determine the effective VM membership of a security group by object id. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "object")] [ValidateNotNull()] [System.Xml.XmlElement]$SecurityGroup, [Parameter (Mandatory = $true, Position = 1, ParameterSetName = "objectid" )] [ValidateScript ( { if ( -not $_ -match 'securitygroup-\d+') { throw "Specify a valid SecurityGroup id" } else { $true } })] [string]$SecurityGroupId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { Get-NsxSecurityGroupEffectiveMember @PSBoundParameters -ReturnTypes VirtualMachine | Select-Object @{ "n" = "VmName"; "e" = { $_.virtualmachine.vmnode.vmname } }, @{ "n" = "VmId"; "e" = { $_.virtualmachine.vmnode.VmId } } } end {} } function Get-NsxSecurityGroupEffectiveIpAddress { <# .SYNOPSIS Determines the effective VM membership of a security group. .DESCRIPTION An NSX SecurityGroup can contain members (VMs, IP Addresses, MAC Addresses or interfaces) by virtue of direct, or indirect membership (nested security groups), and either by static or dynamic inclusion. In addition, direct or indirect exclusions can also modify membership. This cmdlet uses the NSX 'Translation APIs' to determine the 'Effective VM Membership' of a given security group. The membership output by this cmdlet is determined by NSX itself. Note: In order for IPAddress membership to be accurate, IP Discovery of virtual machines must be operational (as it must for the dataplane to function as well.) If IPAddress membership is not accurately represented here, verify that an appropriate IP discovery mechanism is operational, and NSX 'detects' the ip addresses you are expecting. Using the Get-NsxSpoofguardNic cmdlet will allow visibility of the detection state of a given nic or VM. .EXAMPLE Get-NsxSecurityGroup TestSG | Get-Get-NsxSecurityGroupEffectiveIpAddress IpAddress --------- fe80::250:56ff:fe80:3e20 Determine the effective ipaddress membership of securitygroup .EXAMPLE Get-NsxSecurityGroup TestSG | Get-Get-NsxSecurityGroupEffectiveIpAddress IpAddress --------- fe80::250:56ff:fe80:3e20 Determine the effective ipaddress membership of a security group by objectid #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "object")] [ValidateNotNull()] [System.Xml.XmlElement]$SecurityGroup, [Parameter (Mandatory = $true, Position = 1, ParameterSetName = "objectid" )] [ValidateScript ( { if ( -not $_ -match 'securitygroup-\d+') { throw "Specify a valid SecurityGroup id" } else { $true } })] [string]$SecurityGroupId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { Get-NsxSecurityGroupEffectiveMember @PSBoundParameters -ReturnTypes IpAddress | Select-Object @{ "n" = "IpAddress"; "e" = { $_.ipaddress.ipnode.ipaddresses.string } } } end {} } function Get-NsxSecurityGroupEffectiveMacAddress { <# .SYNOPSIS Determines the effective Mac Address membership of a security group. .DESCRIPTION An NSX SecurityGroup can contain members (VMs, IP Addresses, MAC Addresses or interfaces) by virtue of direct, or indirect membership (nested security groups), and either by static or dynamic inclusion. In addition, direct or indirect exclusions can also modify membership. This cmdlet uses the NSX 'Translation APIs' to determine the 'Effective MAC Address Membership' of a given security group. The membership output by this cmdlet is determined by NSX itself. .EXAMPLE Get-NsxSecurityGroup testSG | Get-NsxSecurityGroupEffectiveMacAddress MacAddress ---------- {00:50:56:80:3e:20, 00:50:56:80:5f:d0} Determine the effective MAC Address membership of testSG. .EXAMPLE Get-NsxSecurityGroupEffectiveMacAddress -SecurityGroupId securitygroup-1234 MacAddress ---------- {00:50:56:80:3e:20, 00:50:56:80:5f:d0} Determine the effective Mac Address membership of a security group by object id. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "object")] [ValidateNotNull()] [System.Xml.XmlElement]$SecurityGroup, [Parameter (Mandatory = $true, Position = 1, ParameterSetName = "objectid" )] [ValidateScript ( { if ( -not $_ -match 'securitygroup-\d+') { throw "Specify a valid SecurityGroup id" } else { $true } })] [string]$SecurityGroupId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { Get-NsxSecurityGroupEffectiveMember @PSBoundParameters -ReturnTypes MacAddress | Select-Object @{ "n" = "MacAddress"; "e" = { $_.macaddress.macnode.macaddress } } } end {} } function Get-NsxSecurityGroupEffectiveVnic { <# .SYNOPSIS Determines the effective VNIC Address membership of a security group. .DESCRIPTION An NSX SecurityGroup can contain members (VMs, IP Addresses, MAC Addresses or interfaces) by virtue of direct, or indirect membership (nested security groups), and either by static or dynamic inclusion. In addition, direct or indirect exclusions can also modify membership. This cmdlet uses the NSX 'Translation APIs' to determine the 'Effective VNIC Address Membership' of a given security group. The membership output by this cmdlet is determined by NSX itself. Note: The IPAddress listed against a vnic via the VNIC translation API may NOT reflect true IPAddress membership of the group as exclusions are not taken into account. Use the Get-NsxSecurityGroupEffectiveIpAddress cmdlet for accurate IP address determination. .EXAMPLE Get-NsxSecurityGroup testSG | Get-NsxSecurityGroupEffectiveVnic Uuid IpAddresses MacAddress ---- ----------- ---------- {50005aa9-a365-5d39-5e73-ab1239eb997e.000, 50004328-f0f5-1115-eb45-1de4261748a1.001} {fe80::250:56ff:fe80:3e20, 10.0.1.11} {00:50:56:80:3e:20, 00:50:56:80:5f:d0} Determine the effective VNIC membership of testSG. .EXAMPLE Get-NsxSecurityGroupEffectiveVnic -SecurityGroupId securitygroup-1234 Uuid IpAddresses MacAddress ---- ----------- ---------- {50005aa9-a365-5d39-5e73-ab1239eb997e.000, 50004328-f0f5-1115-eb45-1de4261748a1.001} {fe80::250:56ff:fe80:3e20, 10.0.1.11} {00:50:56:80:3e:20, 00:50:56:80:5f:d0} Determine the effective VNIC membership of a security group by object id. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = "object")] [ValidateNotNull()] [System.Xml.XmlElement]$SecurityGroup, [Parameter (Mandatory = $true, Position = 1, ParameterSetName = "objectid" )] [ValidateScript ( { if ( -not $_ -match 'securitygroup-\d+') { throw "Specify a valid SecurityGroup id" } else { $true } })] [string]$SecurityGroupId, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { Get-NsxSecurityGroupEffectiveMember @PSBoundParameters -ReturnTypes Vnic | Select-Object @{ "n" = "Uuid"; "e" = { $_.Vnic.vnicnode.uuid } }, @{ "n" = "IpAddresses"; "e" = { $_.Vnic.vnicnode.IpAddresses.string } }, @{ "n" = "MacAddress"; "e" = { $_.Vnic.vnicnode.MacAddress } } } end {} } function Find-NsxWhereVMUsed { <# .SYNOPSIS Determines what what NSX Security Groups or Firewall Rules a given VM is defined in. .DESCRIPTION Determining what NSX Security Groups or Firewall Rules a given VM is defined in is difficult from the UI. This cmdlet provides this simple functionality. .EXAMPLE PS C:\> Get-VM web01 | Where-NsxVMUsed #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop]$VM, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Get Firewall rules $L3FirewallRules = Get-NsxFirewallSection -Connection $connection | Get-NsxFirewallRule -Connection $connection $L2FirewallRules = Get-NsxFirewallSection -sectionType layer2sections -Connection $connection | Get-NsxFirewallRule -RuleType layer2sections -Connection $connection #Get all SGs $securityGroups = Get-NsxSecurityGroup -Connection $connection $MatchedSG = @() $MatchedFWL3 = @() $MatchedFWL2 = @() foreach ( $SecurityGroup in $securityGroups ) { $Members = $securityGroup | Get-NsxSecurityGroupEffectiveMember -Connection $connection -ReturnTypes VirtualMachine Write-Debug "$($MyInvocation.MyCommand.Name) : Checking securitygroup $($securitygroup.name) for VM $($VM.name)" If ( $members.VirtualMachine ) { foreach ( $member in $members.VirtualMachine) { if ( $member.vmnode.vmid -eq $VM.ExtensionData.MoRef.Value ) { $MatchedSG += $SecurityGroup } } } } Write-Debug "$($MyInvocation.MyCommand.Name) : Checking L3 FirewallRules for VM $($VM.name)" foreach ( $FirewallRule in $L3FirewallRules ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Checking rule $($FirewallRule.Id) for VM $($VM.name)" If ( $FirewallRule | Get-Member -MemberType Properties -Name Sources) { foreach ( $Source in $FirewallRule.Sources.Source) { if ( $Source.value -eq $VM.ExtensionData.MoRef.Value ) { $MatchedFWL3 += $FirewallRule } } } If ( $FirewallRule | Get-Member -MemberType Properties -Name Destinations ) { foreach ( $Dest in $FirewallRule.Destinations.Destination) { if ( $Dest.value -eq $VM.ExtensionData.MoRef.Value ) { $MatchedFWL3 += $FirewallRule } } } If ( $FirewallRule | Get-Member -MemberType Properties -Name AppliedToList) { foreach ( $AppliedTo in $FirewallRule.AppliedToList.AppliedTo) { if ( $AppliedTo.value -eq $VM.ExtensionData.MoRef.Value ) { $MatchedFWL3 += $FirewallRule } } } } Write-Debug "$($MyInvocation.MyCommand.Name) : Checking L2 FirewallRules for VM $($VM.name)" foreach ( $FirewallRule in $L2FirewallRules ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Checking rule $($FirewallRule.Id) for VM $($VM.name)" If ( $FirewallRule | Get-Member -MemberType Properties -Name Sources) { foreach ( $Source in $FirewallRule.Sources.Source) { if ( $Source.value -eq $VM.ExtensionData.MoRef.Value ) { $MatchedFWL2 += $FirewallRule } } } If ( $FirewallRule | Get-Member -MemberType Properties -Name Destinations ) { foreach ( $Dest in $FirewallRule.Destinations.Destination) { if ( $Dest.value -eq $VM.ExtensionData.MoRef.Value ) { $MatchedFWL2 += $FirewallRule } } } If ( $FirewallRule | Get-Member -MemberType Properties -Name AppliedToList) { foreach ( $AppliedTo in $FirewallRule.AppliedToList.AppliedTo) { if ( $AppliedTo.value -eq $VM.ExtensionData.MoRef.Value ) { $MatchedFWL2 += $FirewallRule } } } } $return = New-Object psobject $return | Add-Member -MemberType NoteProperty -Name "MatchedSecurityGroups" -Value $MatchedSG $return | Add-Member -MemberType NoteProperty -Name "MatchedL3FirewallRules" -Value $MatchedFWL3 $return | Add-Member -MemberType NoteProperty -Name "MatchedL2FirewallRules" -Value $MatchedFWL2 $return } end {} } function Get-NsxBackingPortGroup { <# .SYNOPSIS Gets the PortGroups backing an NSX Logical Switch. .DESCRIPTION NSX Logical switches are backed by one or more Virtual Distributed Switch portgroups that are the connection point in vCenter for VMs that connect to the logical switch. In simpler environments, a logical switch may only be backed by a single portgroup on a single Virtual Distributed Switch, but the scope of a logical switch is governed by the transport zone it is created in. The transport zone may span multiple vSphere clusters that have hosts that belong to multiple different Virtual Distributed Switches and in this situation, a logical switch would be backed by a unique portgroup on each Virtual Distributed Switch. This cmdlet requires an active and correct PowerCLI connection to the vCenter server that is registered to NSX. It returns PowerCLI VDPortgroup objects for each backing portgroup. .EXAMPLE #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalSwitch $_ })] [object]$LogicalSwitch ) begin { if ( -not ( $global:DefaultVIServer.IsConnected )) { throw "This cmdlet requires a valid PowerCLI connection. Use Connect-VIServer to connect to vCenter and try again." } } process { $BackingVDS = $_.vdsContextWithBacking foreach ( $vDS in $BackingVDS ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Backing portgroup id $($vDS.backingValue)" try { Get-VDPortgroup -Id "DistributedVirtualPortgroup-$($vDS.backingValue)" } catch { throw "VDPortgroup not found on connected vCenter $($global:DefaultVIServer.Name). $_" } } } end {} } function Get-NsxBackingDVSwitch { <# .SYNOPSIS Gets the Virtual Distributed Switches backing an NSX Logical Switch. .DESCRIPTION NSX Logical switches are backed by one or more Virtual Distributed Switch portgroups that are the connection point in vCenter for VMs that connect to the logical switch. In simpler environments, a logical switch may only be backed by a single portgroup on a single Virtual Distributed Switch, but the scope of a logical switch is governed by the transport zone it is created in. The transport zone may span multiple vSphere clusters that have hosts that belong to multiple different Virtual Distributed Switches and in this situation, a logical switch would be backed by a unique portgroup on each Virtual Distributed Switch. This cmdlet requires an active and correct PowerCLI connection to the vCenter server that is registered to NSX. It returns PowerCLI VDSwitch objects for each backing VDSwitch. .EXAMPLE #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] [ValidateScript( { ValidateLogicalSwitch $_ })] [object]$LogicalSwitch ) begin { if ( -not ( $global:DefaultVIServer.IsConnected )) { throw "This cmdlet requires a valid PowerCLI connection. Use Connect-VIServer to connect to vCenter and try again." } } process { $BackingVDS = $_.vdsContextWithBacking foreach ( $vDS in $BackingVDS ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Backing vDS id $($vDS.switch.objectId)" try { Get-VDSwitch -Id "VmwareDistributedVirtualSwitch-$($vDS.switch.objectId)" } catch { throw "VDSwitch not found on connected vCenter $($global:DefaultVIServer.Name). $_" } } } end {} } ######## ######## # IPsec VPN function Get-NsxIPsec { <# .SYNOPSIS Retrieves the IPsec configuration from a specified Edge. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN IPsec, load balancing, and high availability. The NSX supports site-to-site IPSec VPN between an NSX Edge instance and remote sites. Certificate authentication, preshared key mode, IP unicast traffic, and no dynamic routing protocol are supported between the NSX Edge instance and remote VPN routers. This cmdlet retrieves the IPsec configuration from a specified Edge. .EXAMPLE PS C:\> Get-NsxEdge Edge01 | Get-NsxIPsec #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge ) begin {} process { $_IPsec = $Edge.features.ipsec.CloneNode($True) Add-XmlElement -xmlRoot $_IPsec -xmlElementName "edgeId" -xmlElementText $Edge.Id $_IPsec } end {} } function Set-NsxIPsec { <# .SYNOPSIS Configures an NSX (VPN) IPsec. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN IPsec, load balancing, and high availability. The NSX supports site-to-site IPSec VPN between an NSX Edge instance and remote sites. Certificate authentication, preshared key mode, IP unicast traffic, and no dynamic routing protocol are supported between the NSX Edge instance and remote VPN routers. This cmdlet sets the basic IPsec configuration of an NSX ESG. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxIPsec | Set-NsxIPsec -Enabled Enabled the IPsec feature on Edge (Need to add IPsec Site before). .EXAMPLE Get-NsxEdge Edge01 | Get-NsxIPsec | Set-NsxIPsec -Enabled:$false Disabled the IPsec feature on Edge. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxIPsec| Set-NsxIPsec -EnableLogging Enabled IPsec collects traffic logs. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxIPsec | Set-NsxIPsec -LogLevel debug Choose the log level (emergency, alert, critical, error, warning, notice, info, debug) of IPsec traffic logs. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxIPsec | Set-NsxIPsec -psk VMWare1! Specify a "global" PSK for IPsec tunnel .EXAMPLE Get-NsxEdge Edge01 | Get-NsxIPsec | Set-NsxIPsec -serviceCertificate certificate-1 Choose a (service)Certificate for IPsec tunnel #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateIPsec $_ })] [System.Xml.XmlElement]$IPsec, [Parameter (Mandatory = $False)] [switch]$Enabled, [Parameter (Mandatory = $False)] [switch]$EnableLogging, [Parameter (Mandatory = $False)] [ValidateSet("emergency", "alert", "critical", "error", "warning", "notice", "info", "debug")] [string]$LogLevel, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$psk, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [string]$serviceCertificate, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_IPsec = $IPsec.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_IPsec.edgeId $_IPsec.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_IPsec -query 'descendant::edgeId')) ) | Out-Null #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('Enabled') ) { $_IPsec.enabled = $Enabled.ToString().ToLower(); } if ( $PsBoundParameters.ContainsKey('EnableLogging') ) { $_IPsec.logging.enable = $EnableLogging.ToString().ToLower(); } if ( $PsBoundParameters.ContainsKey('LogLevel') ) { $_IPsec.logging.logLevel = $LogLevel } #Global Settings if ( $PsBoundParameters.ContainsKey('psk') ) { $_IPsec.global.psk = $psk } if ( $PsBoundParameters.ContainsKey('serviceCertificate') ) { if ( Invoke-XpathQuery -Node $_IPsec -QueryMethod SelectSingleNode -query "child::global/serviceCertificate" ) { $_IPsec.global.serviceCertificate = $serviceCertificate } else { Add-XmlElement -xmlRoot $_IPsec.global -xmlElementName "serviceCertificate" -xmlElementText $serviceCertificate } } $URI = "/api/4.0/edges/$($edgeId)/ipsec/config" $body = $_IPsec.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" -Completed Get-NsxEdge -objectId $($edgeId) -Connection $connection | Get-NsxIPsec } end {} } function Remove-NsxIPsec { <# .SYNOPSIS Remove the global IPsec configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN IPsec, load balancing, and high availability. The NSX supports site-to-site IPSec VPN between an NSX Edge instance and remote sites. Certificate authentication, preshared key mode, IP unicast traffic, and no dynamic routing protocol are supported between the NSX Edge instance and remote VPN routers. The Remove-NsxIPsec cmdlet unconfigures the global IPsec configuration of the specified Edge Services Gateway. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxIPsec | Remove-NsxIPsec Remove all NSX IPsec configuration with confirmation .EXAMPLE Get-NsxEdge Edge01 | Get-NsxIPsec | Remove-NsxIPsec -NoConfirm:$true Remove all NSX IPsec configuration without confirmation #> [CmdLetBinding(DefaultParameterSetName = "Default")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #NSX Edge DNS to remove [ValidateScript( { ValidateIPsec $_ })] [System.Xml.XmlElement]$IPsec, [Parameter (Mandatory = $False, ParameterSetName = "LegacyConfirm")] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False, ParameterSetName = "Default")] #Disable Prompt for confirmation. [switch]$NoConfirm, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { If ( $PSCmdlet.ParameterSetName -eq "LegacyConfirm") { Write-Warning "The -confirm switch is deprecated and will be removed in a future release. Use -NoConfirm instead." $NoConfirm = ( -not $confirm ) } } process { $edgeId = $IPsec.edgeId if ( -not ( $Noconfirm )) { $message = "Edge IPsec removal is permanent." $question = "Proceed with removal of Edge IPsec $($EdgeId) ?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/4.0/edges/$($EdgeId)/ipsec/config" Write-Progress -Activity "Remove IPsec for Edge $($EdgeId)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove IPsec for Edge $($EdgeId)" -Completed } } end {} } function Add-NsxIPsecSite { <# .SYNOPSIS Add the IPsec Site configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN IPsec, load balancing, and high availability. The NSX supports site-to-site IPSec VPN between an NSX Edge instance and remote sites. Certificate authentication, preshared key mode, IP unicast traffic, and no dynamic routing protocol are supported between the NSX Edge instance and remote VPN routers. The Add-NsxIPsecSite cmdlet configures the site IPsec configuration of the specified Edge Services Gateway. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxIPsec | Add-NsxIPsecSite -localID localid -localIP 1.1.1.1 -localSubnet 192.168.23.0/24 -peerId peerid -peerIP 2.2.2.2 -peerSubnet 192.168.44.0/24 -psk VMware1! Add a IPsec Site using PSK and default settings .EXAMPLE Get-NsxEdge Edge01 | Get-NsxIPsec | Add-NsxIPsecSite -localID localid -localIP 1.1.1.1 -localSubnet 192.168.23.0/24 -peerId peerid -peerIP 2.2.2.2 -peerSubnet 192.168.44.0/24 -authenticationMode x.509 Add a IPsec Site using Certificate and default settings Need to have enable Certificate on IPsec Global .EXAMPLE Get-NsxEdge Edge01 | Get-NsxIPsec | Add-NsxIPsecSite -localID localid -localIP 1.1.1.1 -localSubnet 192.168.23.0/24 -peerId peerid -peerIP 2.2.2.2 -peerSubnet 192.168.44.0/24 -psk VMware1! -dhgroup dh4 -encryptionAlgorithm AES256 Add a IPsec Site using PSK and custom settings (use dhgroup dh14 and encryption AES256) #> [CmdLetBinding(DefaultParameterSetName = "IpAddress")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateIPsec $_ })] [System.Xml.XmlElement]$IPsec, [Parameter (Mandatory = $false)] [switch]$Enabled = $true, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$Description, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$localId, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [ipaddress]$localIp, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string[]]$localSubnet, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$peerId, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$peerIp, [Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()] [string[]]$peerSubnet, [Parameter (Mandatory = $false)] [ValidateSet("AES", "AES256", "3DES", "AES-GCM")] [string]$encryptionAlgorithm = "AES", [Parameter (Mandatory = $false)] [ValidateSet("PSK", "x.509")] [string]$authenticationMode = "PSK", [Parameter (Mandatory = $false)] [switch]$enablepfs = $true, [Parameter (Mandatory = $false)] [ValidateSet("dh2", "dh5", "dh14", "dh15", "dh16")] [string]$dhgroup = "dh14", [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$psk, [Parameter (Mandatory = $false)] [ValidateNotNullOrEmpty()] [string]$extension, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Create private xml element $_IPsec = $IPsec.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_IPsec.edgeId $_IPsec.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_IPsec -query 'descendant::edgeId')) ) | Out-Null #For first site, you need to recreate sites field (by default a empty System.Object) if ($_IPsec.sites.gettype().basetype -eq [System.Object]) { $_IPsec.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_IPsec -query 'descendant::sites')) ) | Out-Null [System.XML.XMLElement]$xmlMemberSites = $_IPsec.OwnerDocument.CreateElement("sites") $_IPsec.appendChild($xmlMemberSites) | Out-Null [System.XML.XMLElement]$xmlMember = $_IPsec.OwnerDocument.CreateElement("site") $xmlMemberSites.appendChild($xmlMember) | Out-Null } else { [System.XML.XMLElement]$xmlMember = $_IPsec.OwnerDocument.CreateElement("site") $_IPsec.Sites.appendChild($xmlMember) | Out-Null } Add-XmlElement -xmlRoot $xmlMember -xmlElementName "enabled" -xmlElementText $Enabled.ToString().ToLower() if ( $PsBoundParameters.ContainsKey("name") ) { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "name" -xmlElementText $Name } if ( $PsBoundParameters.ContainsKey("description") ) { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "description" -xmlElementText $description } Add-XmlElement -xmlRoot $xmlMember -xmlElementName "localId" -xmlElementText $localId Add-XmlElement -xmlRoot $xmlMember -xmlElementName "localIp" -xmlElementText $localIp Add-XmlElement -xmlRoot $xmlMember -xmlElementName "peerId" -xmlElementText $peerId Add-XmlElement -xmlRoot $xmlMember -xmlElementName "peerIp" -xmlElementText $peerIp if ( $PsBoundParameters.ContainsKey("encryptionAlgorithm") ) { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "encryptionAlgorithm" -xmlElementText $encryptionAlgorithm } [System.XML.XMLElement]$xmllocalsubnet = $xmlMember.OwnerDocument.CreateElement('localSubnets') $xmlMember.Appendchild($xmllocalsubnet) | Out-Null foreach ($subnet in $localSubnet) { Add-XmlElement -xmlRoot $xmllocalsubnet -xmlElementName "subnet" -xmlElementText $subnet.ToString() } [System.XML.XMLElement]$xmlpeersubnet = $xmlMember.OwnerDocument.CreateElement('peerSubnets') $xmlMember.Appendchild($xmlpeersubnet) | Out-Null foreach ($subnet in $peerSubnet) { Add-XmlElement -xmlRoot $xmlpeersubnet -xmlElementName "subnet" -xmlElementText $subnet.ToString() } Add-XmlElement -xmlRoot $xmlMember -xmlElementName "authenticationMode" -xmlElementText $authenticationMode if ( $authenticationMode -eq "PSK" ) { if ( $PsBoundParameters.ContainsKey("psk") ) { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "psk" -xmlElementText $psk } else { #throw "You need to specify a PSK (-psk)" } } if ( $PsBoundParameters.ContainsKey("enablePfs") ) { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "enablePfs" -xmlElementText $enablePfs } if ( $PsBoundParameters.ContainsKey("dhGroup") ) { Add-XmlElement -xmlRoot $xmlMember -xmlElementName "dhGroup" -xmlElementText $dhGroup } $URI = "/api/4.0/edges/$edgeId/ipsec/config" $body = $_IPsec.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Status "IPsec config for $($EdgeId)" $null = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($EdgeId)" -Completed #Get updated ipsec $URI = "/api/4.0/edges/$edgeId/ipsec/config" $return = Invoke-NsxRestMethod -method "get" -URI $URI -connection $connection $return.ipsec #Add-XmlElement -xmlroot $Pool -xmlElementName "edgeId" -xmlElementText $edgeId #$Pool } end {} } function Copy-NsxEdge { <# .SYNOPSIS Creates a new NSX Edge Services Gateway based on the configuration of an existing one. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. Each NSX Edge virtual appliance can have a total of ten uplink and internal network interfaces and up to 200 subinterfaces. Multiple external IP addresses can be configured for load balancer, site‐to‐site VPN, and NAT services. This cmdlet creates a new Nsx Edge Services Gateway based on the configuration of an existing one. There are numerous properties that are not possible to clone, and must be either configured in the call to Copy-NsxEdge (such as interface IPs), or will need to be manually configured on the new NSX Edge after the fact (such as external certificate configuration). Note that this operation does not strictly clone the Edge, internal object identifiers such as NAT and FW rule ids etc. will not be consistent between source and duplicated Edges. This is a limitation imposed by the NSX API. An attempt is made to make sensible 'fixups' to the duplicated edge to allow it to function as expected. Most of these fixups can be disabled with param switches to Copy-NsxEdge, but in some cases, this will prevent the duplication of certain features (for instance, disabling local object fixups will prevent user defined firewall rules from being configured on the duplicate edge.) Fixups for the following are currently in place and enabled by default: - Any Self Signed certificates are 'regenerated' on the duplicated edge Note: Externally signed certificates cannot be migrated and must be manually configured on the duplicated edge if required. Regenerated Self Signed certificates will have the fqdn of the edge as their CN. Alternatively, the user can specify a CN explicitly via parameter to Copy-NsxEdge. All certificates will have the same CN currently. - Any services using certificates that have been regenerated will be configured to use the corresponding regenerated cert. - Any listening services (LB VIPs, SSL VPN, IPSec VPN etc) bound to interface addresses will be updated to use the corresponding address on the duplicated edge. - Any NAT rules that specify a local interface address in either the Original Address or Translated Address field will be updated to specify the corresponding replacement interface address on the duplicated edge. - Any locally defined grouping objects (IPSets, Services or Service Groups) will be recreated on the duplicated edge. This includes fixups for any service groups that contain other local services or service groups to be updated to include their corresponding recreated local object on the duplicated edge. - Any User defined local firewall rules that reference local objects in source, destination or service fields are updated to reference the corresponding recreated local object on the duplicated edge. - Any IPSec Pre Shared Keys defined will be randomised. These can be manually updated after the fact as required. - If a router ID is configured on the source edge, and references an interface address, it is updated to reference the corresponding address on the duplicated edge. This is an experimental function for now and involves a lot of heavy lifting. Please report any limitations or issues using it via the project github page so it can be improved. .EXAMPLE Get-NsxEdge Edge01 | Copy-NsxEdge -name Edge02 -Password VMware1!VMware1! Creates a duplicated edge based on the source-edge Edge01. Any interface addresses found on Edge01 will be interactively prompted for replacement. Note that the subnet (network and mask) of each primary or secondary adderess specified must match that of the source edge, and all addresses found on the source must be updated. .EXAMPLE $uplink = New-NsxEdgeInterfaceSpec -Index 0 -Name Uplink -Type uplink -ConnectedTo (get-vdportgroup internal) -PrimaryAddress 192.168.100.202 -SubnetPrefixLength 24 -SecondaryAddresses 192.168.100.203,192.168.100.204,192.168.100.205 PS C:\>$transit = New-NsxEdgeInterfaceSpec -Index 1 -Name Transit -Type internal -ConnectedTo (Get-NsxLogicalSwitch transit) -PrimaryAddress 172.16.1.11 -SubnetPrefixLength 24 -SecondaryAddresses 172.16.1.12 PS C:\>Get-NsxEdge Edge01 | Copy-NsxEdge -name Edge02 -Password VMware1!VMware1! -Interface $Uplink,$Transit Creates two interface specs and creates a duplicated edge based on the source-edge Edge01. Note that the subnet (network and mask) of each primary or secondary adderess specified in each spec, as well as the number of addresses, and the interface indexes specified, must match that of the source edge. #> [CmdletBinding(DefaultParameterSetName = "Default")] [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")] # Unable to remove without breaking backward compatibilty. Alternate credential parameter exists. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingUserNameAndPassWordParams", "", Scope = "Function", Target = "*")] # Unable to remove without breaking backward compatibilty. [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true)] #PowerNSX Edge Object as retrieved with Get-NsxEdge representing the source edge to duplicate. [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge, [Parameter (Mandatory = $true)] #Duplicated Edge Name (base of appliance name and default for fqdn) [ValidateNotNullOrEmpty()] [string]$Name, [Parameter (Mandatory = $true, ParameterSetName = "ResourcePool")] #PowerCLI Resource Pool object representing vSphere Resource Pool to which duplicated edge appliances are deployed. If Resource Pool and Cluster are not specified, Copy-NsxEdge places the duplicated edge appliances in the same location as the source edge. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ResourcePoolInterop]$ResourcePool, [Parameter (Mandatory = $true, ParameterSetName = "Cluster")] #PowerCLI Cluster object representing vSphere Cluster to which duplicated edge appliances are deployed. If Resource Pool and Cluster are not specified, Copy-NsxEdge places the duplicated edge appliances in the same location as the source edge. [ValidateScript( { if ( $_ -eq $null ) { throw "Must specify Cluster." } if ( -not $_.DrsEnabled ) { throw "Cluster is not DRS enabled." } $true })] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.ClusterInterop]$Cluster, [Parameter (Mandatory = $false)] #PowerCLI Datastore object representing vSphere datastore to which the primary duplicated edge appliance is deployed. Defaults to the same location as the source edge. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.DatastoreManagement.DatastoreInterop]$Datastore, [Parameter (Mandatory = $false)] #Edge CLI user name. Defaults to 'admin' [ValidateNotNullOrEmpty()] [String]$Username = "admin", [Parameter (Mandatory = $true)] #Edge CLI password [ValidateNotNullOrEmpty()] [String]$Password, [Parameter (Mandatory = $false)] #PowerCLI Datastore object representing vSphere datastore to which the secondary edge appliance is deployed (requires HA). Defaults to the same location as the source edge. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.DatastoreManagement.DatastoreInterop]$HADatastore, [Parameter (Mandatory = $false)] #Edge Appliance Form Factor. See NSX Documentation for appliance form factor details and recommendations. Defaults to the source edge form factor. [ValidateSet("compact", "large", "xlarge", "quadlarge", IgnoreCase = $false)] [string]$FormFactor, [Parameter (Mandatory = $false)] #PowerCLI Folder object representing the vSphere VM inventory folder in which the appliances should be deployed. Defaults to the source edge location. [ValidateNotNullOrEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.FolderInterop]$VMFolder, [Parameter (Mandatory = $false)] #Tenant name used in appliance naming and API references. Defaults to the source edge tenant. [ValidateNotNullOrEmpty()] [String]$Tenant, [Parameter (Mandatory = $false)] #FQDN of Edge. Defaults to $name (undotted). [ValidateNotNullOrEmpty()] [String]$Hostname = $Name, [Parameter (Mandatory = $false)] #Enable SSH on the duplicated Edge. Defaults to source edge setting. [ValidateNotNullOrEmpty()] [switch]$EnableSSH, [Parameter (Mandatory = $false)] #Enable autogenerated firewall rules on the duplicated Edge. Defaults to source edge setting. [ValidateNotNullOrEmpty()] [switch]$AutoGenerateRules, [Parameter (Mandatory = $false)] #Enable firewall on the duplicated Edge. Defaults to source edge setting. [switch]$FwEnabled, [Parameter (Mandatory = $false)] #Configure default firewall policy on the duplicated Edge. Defaults to source edge setting. [switch]$FwDefaultPolicyAllow, [Parameter (Mandatory = $false)] #Configure default firewall action logging on the duplicated Edge. Defaults to source edge setting. [switch]$FwLoggingEnabled, [Parameter (Mandatory = $false)] #Configure HA on the duplicated Edge. Defaults to source edge setting. [ValidateNotNullOrEmpty()] [switch]$EnableHa, [Parameter (Mandatory = $false)] #Configure HA dead time on the duplicated Edge. Defaults to source edge setting. [ValidateRange(3, 900)] [int]$HaDeadTime, [Parameter (Mandatory = $false)] #Configure HA vNIC on the duplicated Edge. Defaults to source edge setting. [ValidateRange(0, 9)] [int]$HaVnic, [Parameter (Mandatory = $false)] #Configure syslog on the duplicated Edge. Defaults to source edge setting. [switch]$EnableSyslog, [Parameter (Mandatory = $false)] #Configure syslog server(s) on the duplicated Edge. Defaults to source edge setting. If specified, overrides source edge settings (not merged). [ValidateNotNullOrEmpty()] [string[]]$SyslogServer, [Parameter (Mandatory = $false)] [ValidateSet("udp", "tcp", IgnoreCase = $true)] #Configure syslog protocol on the duplicated Edge. Defaults to source edge setting. [string]$SyslogProtocol, [Parameter (Mandatory = $false)] #Interface definitions. Specified as Interface Specs as returned by New-NsxEdgeInterfaceSpec. Must contain the SAME number of interfaces with the same interface indexes, addressgroups per interface, and primary and secondary addresses per addressgroup as the source edge interface. #Netmasks and the CIDR network defined in each addressgroup must match that of the source edge. # #In summary, the only thing that can (must) change from the source edge is the primary and any secondary IP Addresses for every addressgroup on every interface, and potentially, the connected network. #If not specified, the user is interactively prompted for replacement addresses on each primary and secondary address on each addressgroup on each enabled VNIC on the source edge. [ValidateScript( { ValidateEdgeInterfaceSpec $_ })] [System.Xml.XmlElement[]]$Interface, [Parameter (Mandatory = $false)] #Any self signed certificates found on the source edge will be regenerated on the destination edge as new certificates with the fqdn as the cn (all other details duplicated), and services configured to use the regenerated certificate. Set this to $false to disable autogeneration of certificates (services will have to be manually reconfigured to use a different certificate) [switch]$CertFixUps = $true, [Parameter (Mandatory = $false)] #Any self signed certificates generated on the new edge will have the fqdn as the cn. Set -SelfSignedCertificateCN to change the CN used (for all Self Signed certificates) [string]$SelfSignedCertificateCN, [Parameter (Mandatory = $false)] #Any NAT rules found on the source edge that specify any 'local' ip (defined on any interface), will be regenerated on the destination edge with the ip updated to the eqivalent IP on the new edge. Set this to $false to disable automatic fixups of NAT rules. Any rules referencing edge local ip addresses will need to be manually updated. [switch]$NatRuleFixups = $true, [Parameter (Mandatory = $false)] #If routerId is defined and matches any 'local' ip (defined on any interface), it will be updated to match the equivalent IP on the new edge. Set to $false to disable automatic fixup. RouterID will need to be manually updated in this case. [switch]$RouterIdFixup = $true, [Parameter (Mandatory = $false)] #Any user defined local firewall rules with locally scoped objects (ipsets, services, servicegroups) referenced will be updated to match the equivalent object on the new edge. Set to $false to disable automatic fixup. User defined firewall rules will not be duplicated and will need to be manually recreated in this case. [switch]$FirewallFixups = $true, [Parameter (Mandatory = $false)] #Any locally scoped objects (ipsets, services, servicegroups and servicegroup membership) defined within the edges local scope will be recreated on the new edge. This is required for FirewallFixups. [switch]$LocalObjectFixups = $true, [Parameter (Mandatory = $false)] #Number of days any regenerated certificates are valid for. Defaults to 365 [int]$CertValidNumberOfDays = 365, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin {} process { #Clone the Edge Element so we can modify without barfing up the source object. $_Edge = $Edge.CloneNode($true) [System.XML.XMLDocument]$xmlDoc = $_Edge.OwnerDocument #Basic Cleanup and reconfig required to remove internal ids and certain exported config #that is not relevant to the new edge before initial post. #Remove EdgeSummary... $edgeSummary = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge -query 'descendant::edgeSummary') if ( $edgeSummary ) { $_Edge.RemoveChild($edgeSummary) | Out-Null } #Naming $_Edge.name = $Name $_Edge.fqdn = $Hostname if ( $PsBoundParameters.ContainsKey('Tenant')) { $_Edge.tenant = $Tenant } #Appliances element $FirstAppliance = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge -query "descendant::appliances/appliance") | Where-Object { $_.highAvailabilityIndex -eq "0" } switch ($psCmdlet.ParameterSetName) { "Default" { Write-Debug "$($MyInvocation.MyCommand.Name) : Invoked with Default ParameterSet" if ( $FirstAppliance ) { $resPoolId = $FirstAppliance.resourcePoolId } if ( -not $resPoolId ) { throw "Unable to determine existing edges resource pool. Try again and specify appliance resource pool." } } "Cluster" { Write-Debug "$($MyInvocation.MyCommand.Name) : Invoked with Cluster ParameterSet" $ResPoolId = $($cluster | Get-ResourcePool | Where-Object { $_.parent.id -eq $cluster.id }).extensiondata.moref.value } "ResourcePool" { Write-Debug "$($MyInvocation.MyCommand.Name) : Invoked with ResourcePool ParameterSet" $ResPoolId = $ResourcePool.extensiondata.moref.value } } if ( $PsBoundParameters.ContainsKey('Datastore')) { $datastoreId = $datastore.extensiondata.moref.value } else { $datastoreId = $FirstAppliance.datastoreId if ( -not $datastoreId ) { throw "Unable to determine existing edges resource pool. Try again and specify appliance resource pool." } } if ( $PsBoundParameters.ContainsKey('VMFolder')) { $VMFolderId = $VMFolder.extensiondata.moref.value } else { $VMFolderId = $FirstAppliance.vmFolderId if ( -not $VMFolderId ) { throw "Unable to determine existing edges resource pool. Try again and specify appliance resource pool." } } #Ditch the old appliances nodes completely and rebuild. [system.xml.xmlElement]$xmlAppliances = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge -query "descendant::appliances") $oldAppliancesNodes = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $xmlAppliances -query "child::appliance") foreach ( $node in $oldAppliancesNodes) { Write-Debug "$($MyInvocation.MyCommand.Name) : Removing appliance node from Edge XML with moref $($node.vmId)" $null = $xmlAppliances.RemoveChild($node) } #If user has overridden appliance size... if ( $PsBoundParameters.ContainsKey("Formfactor")) { Write-Debug "$($MyInvocation.MyCommand.Name) : Setting formfactor to $formfactor" $xmlAppliances.applianceSize = $FormFactor } Write-Debug "$($MyInvocation.MyCommand.Name) : Creating new primary appliance node with ResourcePool moref: $ResPoolId, Datastore moref: $datastoreid, Folder moref: $VMFolderId." [System.XML.XMLElement]$xmlAppliance = $XMLDoc.CreateElement("appliance") $xmlAppliances.appendChild($xmlAppliance) | Out-Null Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "resourcePoolId" -xmlElementText $ResPoolId Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "datastoreId" -xmlElementText $datastoreId Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "vmFolderId" -xmlElementText $VmFolderId #Kill the version props on edge and all features $VersionNodes = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_Edge -query "descendant::version") foreach ($node in $VersionNodes) { $null = $node.ParentNode.RemoveChild($Node) } #Kill any NAT Rule IDs/Tags (must be regenerated by API) $NATRuleIds = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_Edge -query "child::features/nat/natRules/natRule/ruleId") foreach ($node in $NATRuleIds) { $null = $node.ParentNode.RemoveChild($Node) } $NATRuleTags = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_Edge -query "child::features/nat/natRules/natRule/ruleTag") foreach ($node in $NATRuleTags) { $null = $node.ParentNode.RemoveChild($Node) } #check for bgp neighbour credentials (cant be retrieved using API) $peerPasswords = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_Edge -query "child::features/routing/bgp/bgpNeighbours/bgpNeighbour/password") foreach ($node in $peerPasswords) { Write-Warning "BGP peer password defined for peer $($node.ParentNode.ipAddress). Password will be cleared on duplicated edge and must be manually reconfigured." $null = $node.ParentNode.RemoveChild($node) } #Check if IPSec is enabled - if so, warn about the removal of the global PSK if ( $_Edge.features.ipsec.enabled -eq 'true') { Write-Warning "The IPSec feature is enabled. The global and any site specific Pre Shared Keys will be set to a random value on the duplicated edge and must be manually reconfigured." } $pskNodes = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_Edge.features.ipsec -query "descendant::psk") foreach ($node in $pskNodes) { #just invent a random 8 char (lower/upper/int) string and set the PSK to it. $randomString = -join ((48..57) + (65..90) + (97..122) | Get-Random -Count 8 | ForEach-Object { [char]$_ }) $node."#text" = $randomString Write-Warning "IPSec PSK for site $($node.ParentNode.tostring()) set to $randomString. Please update manually as required." } #Check for self signed certificates. #For the moment, the idea is that SS certs will be regenerated on the destination appliance, and services that use them reconfigured appropriately. #The fqdn is used as the cert CN, unless overridden. Certs cannot be actually created until the edge is deployed, so we have to wait until later to generate them and update services... #Any external certs (or if the user disables the SS cert regeneration) that cause services to have an invalid config will result in warning, #but we will still attempt to provision the edge (dont know yet if invalid certs in config cause edge API to throw, but initial testing indicates it doesnt... Will rethink if this proves inaccurate...) if ( $certfixups ) { $SSCertificates = @() $Certificates = $edge | Get-NsxEdgeCertificate -Connection $Connection foreach ( $cert in $Certificates ) { if ( $cert.certificateType -eq 'certificate_self_signed') { if ( $CertFixUps ) { Write-Warning "Found self signed certificate $($cert.name) on source edge. Certificate will be regenerated on duplicated edge." #Store the certificate for later use once the edge is created with the replacement certificate. $SSCertificates += $cert } else { Write-Warning "Found self signed certificate $($cert.name) on source edge. Any service using this certificate will have an invalid configuration on the duplicated edge and must be manually corrected." } } else { Write-Warning "Found certificate $($cert.name) on source edge which is signed by an external CA. This certificate cannot be exported and must be manually reimported/generated on the destination edge. Any service using this certificate will have an invalid configuration on the duplicated edge and must be manually corrected." } } } #Get the features element. [System.XML.XMLElement]$xmlFeatures = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge -query "child::features") if ( $EnableHA -or ( $_Edge.features.highAvailability.enabled -eq "true" )) { #Generate the HA Appliance node if user enabled HA, or if the source edge had it enabled. #If user specced HADatastore then use that val rather than val of source edge... If ( $PSBoundParameters.ContainsKey("HAdatastore")) { $HADatastoreId = $HAdatastore.extensiondata.moref.value } #Else if the source edge has a HA appliance, use that appliances datastore elseif ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $xmlAppliances -query "appliance[highAvailabilityIndex=1]") ) { $HADatastoreId = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $xmlAppliances -query "appliance[highAvailabilityIndex=1]").datastoreId } #Else, use the first appliances datastore else { $HAdatastoreId = $datastoreId } Write-Debug "$($MyInvocation.MyCommand.Name) : Source edge is HA or user requested HA. Generating secondary appliance node with Datastore moref: $HAdatastoreId " #Define the HA appliance node [System.XML.XMLElement]$xmlAppliance = $XMLDoc.CreateElement("appliance") $null = $xmlAppliances.appendChild($xmlAppliance) Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "resourcePoolId" -xmlElementText $ResPoolId Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "datastoreId" -xmlElementText $HAdatastoreid Add-XmlElement -xmlRoot $xmlAppliance -xmlElementName "vmFolderId" -xmlElementText $VMFolderid #configure HA if not already enabled. HaDeadtime node exists even on non HA edges... $_Edge.features.highAvailability.enabled = "true" if ( $PsBoundParameters.containsKey('HaDeadTime')) { $_Edge.features.highAvailability.declareDeadTime = $HaDeadTime.ToString() } #Node is not guaranteed to exist, have to test first. Love the consistency if ( $PsBoundParameters.containsKey('HaVnic')) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge -query "features/highAvailability/vnic")) { $_Edge.features.highAvailability.vnic = $HAvnic.ToString() } else { Add-XmlElement -xmlRoot $_Edge.features.highAvailability -xmlElementName "vnic" -xmlElementText $HaVnic.ToString() } } } #Configure the syslog element if ( $PSBoundParameters.ContainsKey("EnableSyslog")) { Write-Debug "$($MyInvocation.MyCommand.Name) : Enabling Syslog" $_Edge.features.syslog.enabled = $EnableSyslog.ToString().ToLower() } if ( $PsBoundParameters.containsKey('SyslogProtocol')) { Write-Debug "$($MyInvocation.MyCommand.Name) : Configuring Syslog Protocol" $_Edge.features.syslog.protocol = $SyslogProtocol.ToString() } #If user specified syslog server address, then we have to kill any existing config. if ( $PsBoundParameters.containsKey('SyslogServer')) { $ExistingSyslogServerAddress = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge.features.syslog -query "serverAddresses") if ( $ExistingSyslogServerAddress ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Removing Existing Syslog servers (overidden by user)" $_Edge.features.syslog.RemoveChild($ExistingSyslogServerAddress) } [System.XML.XMLElement]$xmlServerAddresses = $XMLDoc.CreateElement("serverAddresses") $_Edge.features.syslog.appendChild($xmlServerAddresses) | Out-Null foreach ( $server in $SyslogServer ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Adding syslog server element for $server" Add-XmlElement -xmlRoot $xmlServerAddresses -xmlElementName "ipAddress" -xmlElementText $server.ToString() } } #Enable/Disable FW if ( $PSBoundParameters.ContainsKey("FwEnabled")) { Write-Debug "$($MyInvocation.MyCommand.Name) : Setting Firewall to $FwEnabled" $_Edge.features.firewall.enabled = $FwEnabled.ToString().ToLower() } if ( $PsBoundParameters.ContainsKey("FwLoggingEnabled")) { Write-Debug "$($MyInvocation.MyCommand.Name) : Setting Firewall Logging to $FwLoggingEnabled" $_Edge.features.firewall.loggingEnabled = $FwLoggingEnabled.ToString().ToLower() } #Override fw default policy if user specifies... if ( $PsBoundParameters.ContainsKey("FwDefaultPolicyAllow")) { if ( $FwDefaultPolicyAllow ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Setting default firewall policy to accept" $_Edge.features.firewwall.defaultPolicy.action = "accept" } else { Write-Debug "$($MyInvocation.MyCommand.Name) : Setting default firewall policy to deny" $_Edge.features.firewwall.defaultPolicy.action = "deny" } } #Override Rule Autoconfiguration if user specifies if ( $PsBoundParameters.ContainsKey("AutoGenerateRules")) { if ( $AutoGenerateRules ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Configuring rule autoconfiguration as $AutoGenerateRules" $_Edge.autoConfiguration.enabled = $AutoGenerateRules.ToString().ToLower() } } #Credential Settings $_Edge.cliSettings.userName = $UserName Add-XmlElement -xmlRoot $_Edge.cliSettings -xmlElementName "password" -xmlElementText $Password if ( $PsBoundParameters.ContainsKey('EnableSSH') ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Configuring SSH to be $EnableSssh" $_Edge.cliSettings.remoteAccess = $EnableSsh.ToString().ToLower() } #DNS Settings if ( $PsBoundParameters.ContainsKey('PrimaryDnsServer') -or $PSBoundParameters.ContainsKey('SecondaryDNSServer') -or $PSBoundParameters.ContainsKey('DNSDomainName') ) { if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge -query "child::dnsClient")) { Write-Debug "$($MyInvocation.MyCommand.Name) : Generating dnsClient element" [System.XML.XMLElement]$xmlDnsClient = $XMLDoc.CreateElement("dnsClient") $null = $_Edge.appendChild($xmlDnsClient) } if ( $PsBoundParameters.ContainsKey('PrimaryDnsServer') ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Setting Primary DNS to $PrimaryDnsServer" if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge.dnsClient -query "primaryDNS")) { Add-XmlElement -xmlRoot $_Edge.dnsClient -xmlElementName "primaryDns" -xmlElementText $PrimaryDnsServer } else { $_Edge.dnsClient.primaryDNS = $PrimaryDnsServer } } if ( $PsBoundParameters.ContainsKey('SecondaryDNSServer') ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Setting Secondary DNS to $SecondaryDnsServer" if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge.dnsClient -query "secondaryDNS")) { Add-XmlElement -xmlRoot $_Edge.dnsClient -xmlElementName "secondaryDNS" -xmlElementText $SecondaryDNSServer } else { $_Edge.dnsClient.secondaryDNS = $SecondaryDNSServer } } if ( $PsBoundParameters.ContainsKey('DNSDomainName') ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Setting DNS domain name to $DNSDomainName" if ( -not (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge.dnsClient -query "domainName")) { Add-XmlElement -xmlRoot $_Edge.dnsClient -xmlElementName "domainName" -xmlElementText $DNSDomainName } else { $_Edge.dnsClient.domainName = $DNSDomainName } } } #Nics...These are either: # a) Specified as Interface Spec as per normal edge creation (for scripting) # b) Taken from source Edge, and primary/secondary address prompted for. #Setup hashtable to store source/dest replacement IPs. $updatedIps = @{} #Get all existing IPs on the source edge so we check for conflicts. Much easier with strict off... Set-StrictMode -Off $AllExistingAddresses = $_Edge.vnics.vnic.addressGroups.addressGroup.primaryAddress + $_Edge.vnics.vnic.addressGroups.addressGroup.secondaryAddresses.ipAddress Set-StrictMode -Version latest foreach ( $Vnic in $_Edge.vnics.vnic ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Processing VNIC $($Vnic.name)" #First check if user has specified any interface specs: $UserVnic = $false if ( $PsBoundParameters.ContainsKey("Interface")) { #have they specified one for this specific vnic? $UserVnic = $Interface | Where-Object { $_.index -eq $Vnic.Index } If ( $UserVnic ) { #If so, we have to validate to ensure its valid. [System.Array]$UserVnicAddressGroups = $UserVnic.Addressgroups.AddressGroup [System.Array]$VnicAddressGroups = $Vnic.Addressgroups.AddressGroup #Check the right number of addressgroups. If different number, we cant guarantee that we can modify any service configuration for new listener addresses, or that the default route is still valid. if ( $UserVnicAddressGroups.count -ne $VnicAddressGroups.count ) { Throw "Source Vnic '$($vnic.Name)' has different number of addressgroups ($($VnicAddressGroups.count)) to specified Vnic '$($UserVnic.Name)' ($($UserVnicAddressGroups.count)) " } for ( $i = 0; ($i -le ($VnicAddressGroups.count - 1)); $i++ ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Validating AddressGroup $i specified for Vnic $($vnic.name)" $addressGroup = $VnicAddressGroups[$i] $ExistingPrimaryAddress = $addressGroup.primaryAddress $AddressGroupNetMask = $addressGroup.subnetMask $AddressGroupNetwork = Get-NetworkFromHostAddress -Address $ExistingPrimaryAddress -SubnetMask $addressGroupNetmask $NewPrimaryAddress = $UserVnicAddressGroups[$i].PrimaryAddress $NewAddressGroupNetMask = ConvertFrom-Bitmask -bitmask ($UserVnicAddressGroups[$i].subnetPrefixLength) Write-Debug "$($MyInvocation.MyCommand.Name) : Existing Primary Address: $ExistingPrimaryAddress, AddressGroup Mask: $AddressGroupNetMask, AddressGroup Network: $AddressGroupNetwork, New Primary Address: $newPrimaryAddress, New AddressGroup NetMask: $NewAddressGroupNetMask" if ( ( -not (Test-AddressInNetwork -Network $AddressGroupNetwork -SubnetMask $AddressGroupNetMask -Address $NewPrimaryAddress)) -or ($AllExistingAddresses.contains($NewPrimaryAddress)) -or ($updatedIps.containsvalue($NewPrimaryAddress)) -or ( $NewAddressGroupNetMask -ne $AddressGroupNetMask ) -or (( -not ( [ipaddress]::TryParse($NewPrimaryAddress, [ref][ipaddress]$null))))) { Throw "New Vnic '$($UserVnic.Name)', addressgroup $i Primary address ($NewPrimaryAddress/$NewAddressGroupNetMask) is not valid, not in same subnet as the original address, has different netmask, or conflicts with an interface address on the source edge." } #IP is valid, add it to the updated ips hash $updatedIps.Add($ExistingPrimaryAddress, $NewPrimaryAddress) #Check secondary addresses if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $addressGroup -query "secondaryAddresses")) { #If we have them, check they are the right number. [System.Array]$VnicSecondaryAddresses = $addressGroup.secondaryAddresses.ipAddress [System.Array]$UserVnicSecondaryAddresses = $UserVnicAddressGroups[$i].secondaryAddresses.ipAddress #Check the right number of secondary addresses. If different number, we cant guarantee that we can modify any service configuration for new listener addresses, or that the default route is still valid. if ( $UserVnicSecondaryAddresses.count -ne $VnicSecondaryAddresses.count ) { Throw "Source Vnic '$($vnic.Name)', addressgroup $i has different number of secondary addresses ($($VnicSecondaryAddresses.count) to specified Vnic '$($UserVnic.Name)', addressgroup $i ($($UserVnicSecondaryAddresses.count)) " } for ($j = 0; ($j -le ($VnicSecondaryAddresses.Count - 1)); $j++) { $ExistingSecondaryAddress = $VnicSecondaryAddresses[$j] $NewSecondaryAddress = $UserVnicSecondaryAddresses[$j] while ( ( -not (Test-AddressInNetwork -Network $AddressGroupNetwork -SubnetMask $AddressGroupNetMask -Address $NewSecondaryAddress)) -or ($AllExistingAddresses.contains($NewSecondaryAddress)) -or ($updatedIps.containsvalue($NewSecondaryAddress)) -or ( -not ( [ipaddress]::TryParse($NewSecondaryAddress, [ref][ipaddress]$null)))) { Throw "New Vnic '$($UserVnic.Name)', addressgroup $i secondary address ($NewSecondaryAddress) is not valid, not in same subnet as the original address, or conflicts with an interface address on the source edge." } #Keep source/dest ip replacement, so that we can reconfigure services listening on them to use new address... $updatedIps.Add($ExistingsecondaryAddress, $NewSecondaryAddress) #No need to 'update' anything. We just have to do validation in this loop, and track the whole egde old to new ip mapping. Assuming we validate, we simply replace $addressgroup.secondaryAddresses.ipaddress } #Have to do this and first with selectsingle node otherwise PoSH can return a string object if we reference after we remove all child nodes. This ensures we get an XmlElement back [system.xml.xmlelement]$SecondaryAddressesXml = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $addressgroup -query "child::secondaryAddresses") #secondary addresses are valid. Replace the array in the addressgroup xml $addressGroup.secondaryAddresses.RemoveAll() foreach ( $address in $UserVnicAddressGroups[$i].secondaryAddresses.ipAddress ) { Add-XmlElement -xmlRoot $SecondaryAddressesXml -xmlElementName "ipAddress" -xmlElementText $address } } } Write-Debug "$($MyInvocation.MyCommand.Name) : User defined vnic spec for this vnic has been specified by user. Importing spec." $null = $_Edge.vnics.RemoveChild($vnic) $import = $xmlDoc.ImportNode(($UserVnic), $true) $null = $_Edge.vnics.AppendChild($import) } } if ( -not $userVnic ) { #User has not specified interface information on the command line. if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $Vnic -query "addressGroups/addressGroup")) { #Only process if there is already addressing information... Write-Debug "$($MyInvocation.MyCommand.Name) : No user defined vnic spec for this vnic has been specified. Prompting user for details" foreach ( $addressGroup in $Vnic.addressGroups.addressGroup ) { if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $addressGroup -query "primaryAddress")) { $ExistingPrimaryAddress = $addressGroup.primaryAddress $AddressGroupNetMask = $addressGroup.subnetMask $AddressGroupNetwork = Get-NetworkFromHostAddress -Address $ExistingPrimaryAddress -SubnetMask $addressGroupNetMask Write-Debug "$($MyInvocation.MyCommand.Name) : Existing Primary Address: $ExistingPrimaryAddress, AddressGroup Mask: $AddressGroupNetMask, AddressGroup Network: $AddressGroupNetwork" $NewPrimaryAddress = Read-Host -Prompt "Enter new primary address for source edge addressgroup with existing IP $($addressGroup.PrimaryAddress) on vnic $($vnic.index)" while ( ( -not (Test-AddressInNetwork -Network $AddressGroupNetwork -SubnetMask $AddressGroupNetMask -Address $NewPrimaryAddress)) -or ($AllExistingAddresses.contains($NewPrimaryAddress)) -or ($updatedIps.containsvalue($NewPrimaryAddress)) -or ( -not ( [ipaddress]::TryParse($NewPrimaryAddress, [ref][ipaddress]$null)))) { Write-Warning "New Primary address is not valid, not in same subnet as the original address, or conflicts with an interface address on the source edge." $NewPrimaryAddress = Read-Host -Prompt "Enter new primary address for source edge addressgroup with existing IP $($addressGroup.PrimaryAddress) on vnic $($vnic.index)" } #Keep source/dest ip replacement, so that we can reconfigure services listening on them to use new address... $updatedIps.Add($ExistingPrimaryAddress, $NewPrimaryAddress) #Update element... $addressGroup.PrimaryAddress = $newPrimaryAddress.ToString() } if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $addressGroup -query "secondaryAddresses")) { $NewSecondaryAddresses = @() #Have to iterate through a node collection here, so if the user 'blanks' the secondary ip, we have a node (not a string) to remove... foreach ($secondaryAddress in (Invoke-XpathQuery -QueryMethod SelectNodes -Node $addressGroup.secondaryAddresses -query '*')) { $NewSecondaryAddress = Read-Host -Prompt "Enter new secondary address for source edge addressgroup with existing secondary IP $($secondaryAddress."#text") on vnic $($vnic.index)" Write-Debug "$($MyInvocation.MyCommand.Name) : Existing Secondary Address: $secondaryAddress, AddressGroup Mask: $AddressGroupNetMask, AddressGroup Network: $AddressGroupNetwork" while ( ( -not (Test-AddressInNetwork -Network $AddressGroupNetwork -SubnetMask $AddressGroupNetMask -Address $NewSecondaryAddress)) -or ($AllExistingAddresses.contains($NewSecondaryAddress)) -or ($updatedIps.containsvalue($NewSecondaryAddress)) -or ( -not ( [ipaddress]::TryParse($NewSecondaryAddress, [ref][ipaddress]$null)))) { Write-Warning "New Secondary address is not valid, not in same subnet as the original address, or conflicts with an interface address on the source edge." $NewSecondaryAddress = Read-Host -Prompt "Enter new secondary address for source edge addressgroup with existing secondary IP $($secondaryAddress."#text") on vnic $($vnic.index)" } #Keep source/dest ip replacement, so that we can reconfigure services listening on them to use new address... $updatedIps.Add($secondaryAddress."#text" , $NewSecondaryAddress) #Collect the validated ip in an array $NewSecondaryAddresses += $NewSecondaryAddress } #Have to do this and first with selectsingle node otherwise PoSH can return a string object if we reference after we remove all child nodes. This ensures we get an XmlElement back [system.xml.xmlelement]$SecondaryAddressesXml = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $addressgroup -query "child::secondaryAddresses") #secondary addresses are valid. Replace the array in the addressgroup xml $addressGroup.secondaryAddresses.RemoveAll() foreach ( $address in $NewSecondaryAddresses ) { Add-XmlElement -xmlRoot $SecondaryAddressesXml -xmlElementName "ipAddress" -xmlElementText $address } } } } } } #Update any listening services that bind to IPs that have been replaced... #Ipsec... $ipsecSiteNodes = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_Edge -query "descendant::features/ipsec/sites/site") foreach ( $node in $ipsecSiteNodes ) { if ( -not $updatedIps.Contains($node.localIp )) { throw "Unable to determine new Local Ip Address for IPSec site $($node.name). This should not happen." } else { Write-Warning "Updating listener address for IpSec service $($node.name). Previous Address : $($node.localIp), Updated Address $($updatedIps.item($($node.localIp)))" #Update the ipsec listener with the IP that replaced the original listen ip $node.localIp = $updatedIps.($node.localIp).ToString() } } #LB $LBVips = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_Edge -query "descendant::features/loadBalancer/virtualServer") foreach ( $node in $LBVips ) { if ( -not $updatedIps.Contains($node.ipAddress )) { throw "Unable to determine new Local Ip Address for LoadBalancer VIP $($node.name) with ip address $($node.ipAddress). This should not happen." } else { Write-Warning "Updating listener address for LoadBalancer VIP $($node.name). Previous Address : $($node.ipAddress), Updated Address $($updatedIps.item($($node.ipAddress)))" #Update the LB listener with the IP that replaced the original listen ip $node.ipAddress = $updatedIps.item($node.ipAddress).ToString() } } #SSLVPN [System.Xml.XmlElement]$SSLVpnListeners = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge -query "descendant::features/sslvpnConfig/serverSettings/serverAddresses") if ( $SSLVpnListeners ) { #Not sure if the API will allow and empty serverAddresses element, but just in case.. testing for it here. if ( (Invoke-XpathQuery -QueryMethod SelectNodes -Node $SSLVpnListeners -query "child::ipAddress") ) { foreach ( $node in $SSLVpnListeners ) { if ( -not $updatedIps.Contains($node.ipAddress )) { throw "Unable to determine new listener address for SSL VPN Server with existing ip address $($node.ipAddress). This should not happen." } else { Write-Warning "Updating listener address for SSL VPN Server . Previous Address : $($node.ipAddress), Updated Address $($updatedIps.item($($node.ipAddress)))" #Update the LB listener with the IP that replaced the original listen ip $node.ipAddress = $updatedIps.item($node.ipAddress).ToString() } } } } #RouterId Fixup. If ( $RouterIdFixup ) { [System.Xml.XmlElement]$RoutingConfig = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Edge -query "descendant::features/routing/routingGlobalConfig") if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $RoutingConfig -query "child::routerId")) { #RouterId is defined. Update it. if ( -not $updatedIps.Contains($RoutingConfig.routerId )) { Write-Warning "Unable to update Router Id as existing ID does not belong to any interface address of the original edge. RouterId for the new edge will need to be manually updated." } else { Write-Warning "Updating Router ID. Previous ID : $($RoutingConfig.routerId), Updated ID : $($updatedIps.item($($RoutingConfig.routerId)))" #Update the LB listener with the IP that replaced the original listen ip $RoutingConfig.routerId = $($updatedIps.item($($RoutingConfig.routerId))).ToString() } } } #NatFixups If ( $NatRuleFixups ) { $UserRules = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_Edge -query "descendant::features/nat/natRules/natRule[ruleType=`'user`']") if ( $UserRules ) { #There are User defined NAT rules on the Edge. foreach ( $Rule in $UserRules ) { if ( $updatedIps.Contains($Rule.originalAddress )) { Write-Warning "Updating user defined NAT Rule with source edge interface address found as original address. Previous Address : $($Rule.originalAddress), Updated address : $($($updatedIps.item($($Rule.originalAddress))))" #Update the LB listener with the IP that replaced the original listen ip $Rule.originalAddress = $($updatedIps.item($($Rule.originalAddress))).ToString() } if ( $updatedIps.Contains($Rule.translatedAddress )) { Write-Warning "Updating user defined NAT Rule with source edge interface address found as translated address. Previous Address : $($Rule.translatedAddress), Updated address : $($($updatedIps.item($($Rule.translatedAddress))))" #Update the LB listener with the IP that replaced the original listen ip $Rule.translatedAddress = $($updatedIps.item($($Rule.translatedAddress))).ToString() } } } } #FW/Local Object stuff. Dealing with these complicates things, but general approach is as follows: # - Get any Locally scoped Services and save for recreation later. Remove from edge xml # - get fw config(user rules only). Remove from edge xml # - initial create of the edge # - create objects in new scope # - update firewall xml with new objects # - push firewall changes to new edge. #Firewall Fixups #The FW can potentially contain grouping objects or service objects that exist only in the edge scope. API wont let us push invalid FW config, so get user rules here and remove them: $UserFWRules = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_Edge -query "descendant::features/firewall/firewallRules/firewallRule[ruleType=`'user`']") if ( $UserFWRules ) { foreach ($rule in $UserFwRules ) { $null = $_Edge.features.firewall.firewallRules.RemoveChild($rule) } } #################################### # Intial Deployment #################################### Write-Debug "$($MyInvocation.MyCommand.Name) : Performing initial creation post of new Edge XML to NSX API" $body = $_Edge.OuterXml $URI = "/api/4.0/edges" Write-Progress -Activity "Creating Edge Services Gateway $Name" $response = Invoke-NsxWebRequest -method "post" -URI $URI -body $body -connection $connection Write-Progress -Activity "Creating Edge Services Gateway $Name" -Completed $edgeId = $response.Headers.Location.split("/")[$response.Headers.Location.split("/").GetUpperBound(0)] Write-Debug "$($MyInvocation.MyCommand.Name) : Created Edge $edgeid" ################################## # Post Initial Deployment fixup ################################## #Now we have any post deployment fixup. Things like local object (services/groups/ipsets), certificates and local object creation have to be done after the edge is created. #First - object creation. We use hashtables to track old -> new id mappings. if ( -not $LocalObjectFixups ) { Write-Warning "Local object recreation is disabled. Any edge scoped user defined firewall rules will also not be duplicated as a result." } else { #Services: #Local Object fixups #Locally scoped objects like ipsets and services/servicegroups can exist on the edge. If FW rules and other (LB only?) config are using them, they have to be recreated. $LocalServices = Get-NsxService -scopeId $_Edge.id -Connection $Connection | Where-Object { $_.scope.id -eq $_Edge.id } #getting by scope id includes inherited services from globalscope-0, we need to filter for services explicitly defined on this edge too :( $LocalServiceGroups = Get-NsxServiceGroup -scopeId $_Edge.id -Connection $Connection | Where-Object { $_.scope.id -eq $_Edge.id } $LocalIpSets = Get-NsxIpSet -scopeId $_Edge.id -Connection $Connection | Where-Object { $_.scope.id -eq $_Edge.id } $UpdatedServices = @{} foreach ( $Service in $LocalServices ) { Write-Warning "Recreating local service $($Service.name) on new edge." $NewServiceId = Invoke-NsxRestMethod -method Post -URI "/api/2.0/services/application/$edgeId" -body $Service.OuterXml -connection $Connection $UpdatedServices.Add($Service.objectId, $NewServiceId) } #ServiceGroups: $UpdatedServiceGroups = @{} foreach ( $ServiceGroup in $LocalServiceGroups ) { #Need to create without membership as they may contain other servicegroups not yet created, so first we create the servicegroups, then update their membership... #Clone the xmlelement so we can modify it $_ServiceGroup = $ServiceGroup.CloneNode($true) if ( (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_ServiceGroup -query 'child::member') ) { #If it has a membership, then remove it. foreach ( $node in (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_ServiceGroup -query 'child::member')) { $null = $_ServiceGroup.RemoveChild($node) } } Write-Warning "Recreating local ServiceGroup $($ServiceGroup.name) on new edge." $NewServiceGroupId = Invoke-NsxRestMethod -method Post -URI "/api/2.0/services/applicationgroup/$edgeId" -body $_ServiceGroup.OuterXml -connection $Connection $UpdatedServiceGroups.Add($_ServiceGroup.objectId, $NewServiceGroupId) } #ServiceGroup membership foreach ( $ServiceGroup in $LocalServiceGroups ) { $SGMembers = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $ServiceGroup -query 'child::member') foreach ( $member in $SGMembers ) { $UpdatedMemberId = $null switch ($member.objectTypeName) { "ApplicationGroup" { #Member is a servicegroup... lookup updated value $UpdatedMemberId = $UpdatedServiceGroups.Item($member.objectId) } "Application" { #Member is a service... lookup updated value $UpdatedMemberId = $UpdatedServices.Item($member.objectId) } default { throw "Unknown member type for ServiceGroup: $ServiceGroup.objectId, Member : $($member.objectId), objectType : $_" } } #Member may not be local and so update may not be required. if ( $UpdatedMemberId ) { $UpdatedServiceGroupId = $($UpdatedServiceGroups.Item($($ServiceGroup.objectId))) Write-Warning "Updating local ServiceGroup membership for ServiceGroup: $UpdatedServiceGroupId, member: $UpdatedMemberId." $null = Invoke-NsxRestMethod -method put -URI "/api/2.0/services/applicationgroup/$UpdatedServiceGroupId/members/$UpdatedMemberId" -connection $Connection } } } #IPSets $UpdatedIpSets = @{} foreach ( $IpSet in $LocalIpSets ) { Write-Warning "Recreating local ipset $($ipset.name) on new edge." $NewIpSetId = Invoke-NsxRestMethod -method Post -URI "/api/2.0/services/ipset/$edgeId" -body $IpSet.OuterXml -connection $Connection $UpdatedIpSets.Add($ipSet.objectId, $NewIpSetId) } } #Now we have everything we need to readd the firewall rules with any updated local object references. if ( $LocalObjectFixups -and $FirewallFixups) { Write-Warning "Performing firewall fixups for any user based rules that contained local object references on $edgeid." if ( @($UserFwRules).count -ne 0 ) { #If there are userrules to process $UserFWXml = @($UserFWRules)[0].OwnerDocument.CreateElement("firewallRules") foreach ( $rule in $UserFWRules ) { #For each rule - perform any local object updates required, then append it to the new edge fw rules... #IPSets first. $RuleGroupingObjects = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $rule -query "child::source/groupingObjectId | child::destination/groupingObjectId") foreach ($GroupingObject in $RuleGroupingObjects) { if ($updatedIpSets.Item($GroupingObject."#text")) { Write-Warning "Processing FW Rule $($rule.Name), Updating reference to local ipset $($GroupingObject."#text") to $($updatedIpSets.Item($GroupingObject."#text"))." #Ipset was local and was recreated on the new edge...update the rule. $GroupingObject."#text" = $updatedIpSets.Item($GroupingObject."#text") } } #Now Services $RuleServices = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $rule -query "child::application/applicationId") foreach ($Service in $RuleServices) { #Might be a service... if ($updatedServices.Item($Service."#text")) { Write-Warning "Processing FW Rule $($rule.Name), Updating reference to local service $($Service."#text") to $($updatedServices.Item($Service."#text"))." #Service was local service and was recreated on the new edge...update the rule. $Service."#text" = $updatedServices.Item($Service."#text") } #... Or a Service Group if ($updatedServiceGroups.Item($Service."#text")) { Write-Warning "Processing FW Rule $($rule.Name), Updating reference to local service $($Service."#text") to $($updatedServiceGroups.Item($Service."#text"))." #Service was local service group and was recreated on the new edge...update the rule. $Service."#text" = $updatedServiceGroups.Item($Service."#text") } } #In theory - the rule doesnt contain any invalid local objects now, and we can add the modified xmlnode to the element we need to send to the api for a bulk update. NEED TO TEST ORDERING! $null = $UserFWXml.AppendChild($rule) } # Rules can now be pushed at the new ege... Write-Warning "Posting updated user firewall ruleset to Edge $edgeid." $null = Invoke-NsxRestMethod -method post -URI "/api/4.0/edges/$edgeId/firewall/config/rules" -body $UserFWXml.OuterXml -connection $Connection } } ###################### #Re-get the edge so we can perform further fixups. ###################### $NewEdge = Get-NsxEdge -objectId $edgeId -Connection $connection #Clone the NewEdge Element so we can modify without barfing up the original object (we need it for new-csr...). $_NewEdge = $NewEdge.CloneNode($true) #And Remove EdgeSummary from newedge XML... $edgeSummary = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_NewEdge -query 'descendant::edgeSummary') if ( $edgeSummary ) { $null = $_NewEdge.RemoveChild($edgeSummary) } if ( $CertFixUps ) { #Check for any certificates that need to be created on the new edge. if (($SSCertificates.count -ge 1 ) -and ( $CertFixUps ) ) { Write-Debug "$($MyInvocation.MyCommand.Name) : Self signed Certificates found on source Edge. Re-generating them." #Need an appropriate CN - either fqdn or user defined. Defaults to hostname. if ( $SelfSignedCertificateCN ) { $CertCN = $SelfSignedCertificateCN } else { $CertCN = $HostName } #Hashtable to store old to new mapping of cert ids. $UpdatedSSCerts = @{} foreach ( $cert in $SSCertificates ) { #Recreate SS Certs on destination edge. $subject = $cert.x509Certificate.subject -split "," $org = ($subject | Where-Object { $_ -match 'O=' }) -replace '^O=', '' $ou = ($subject | Where-Object { $_ -match 'OU=' }) -replace '^OU=', '' $c = ($subject | Where-Object { $_ -match 'C=' }) -replace '^C=', '' if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $cert -query "child::description") ) { $desc = $cert.description } else { $desc = "PowerNSX Regenerated Self Signed certificate" } Write-Warning "Creating cert on new edge with CN : $CertCN, C : $c, O : $org, OU : $ou, Keysize : $($cert.x509Certificate.publicKeyLength), Algo : $($cert.x509Certificate.publicKeyAlgo), Desc : $desc, Name : $CertCN" $NewCSR = $NewEdge | New-NsxEdgeCsr -CommonName $CertCN -Country $c -Organisation $org -OrganisationalUnit $ou -Keysize $cert.x509Certificate.publicKeyLength -Algorithm $cert.x509Certificate.publicKeyAlgo -Description $desc -Name $CertCN -Connection $Connection $NewCert = $NewCSR | New-NsxEdgeSelfSignedCertificate -NumberOfDays $CertValidNumberOfDays -Connection $Connection $UpdatedSSCerts.add($cert.objectId, $newCert.objectId) Write-Debug "$($MyInvocation.MyCommand.Name) : Add cert mapping - Old Cert : $($cert.objectId), New Cert : $($newCert.objectId)" } #Fixup cert references in IPSec VPN... if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_NewEdge.features.ipsec.global -query "child::serviceCertificate") ) { if ( $UpdatedSSCerts.item($_NewEdge.features.ipsec.global.serviceCertificate) ) { Write-Warning "Fixing up cert for IpSec listener : Old Cert : $($_NewEdge.features.ipsec.global.serviceCertificate), New Cert : $($UpdatedSSCerts.item($_NewEdge.features.ipsec.global.serviceCertificate))" $_NewEdge.features.ipsec.global.serviceCertificate = $UpdatedSSCerts.item($_NewEdge.features.ipsec.global.serviceCertificate) } else { Write-Warning "Unable to configure valid cert for IPSec VPN Server with current invalid cert $($_NewEdge.features.ipsec.global.serviceCertificate). This may be due to the use of an externally signed certificate on the source Edge. The service will have to be manually updated." } } #LB cert Fixup $appProfileCerts = (Invoke-XpathQuery -QueryMethod SelectNodes -Node $_NewEdge.features.loadBalancer.applicationProfile -query "descendant::serviceCertificate") foreach ( $cert in $appProfileCerts ) { $AppProfile = $cert.ParentNode.ParentNode.name if ( $cert.ParentNode.ToString() -eq 'clientSsl' ) { $certType = "Virtual Server Certificate" } else { $certType = "Pool Certificate" } if ( $UpdatedSSCerts.item($cert."#text") ) { Write-Warning "Fixing up cert for Load Balancer application profile $AppProfile $certType : Old Cert : $($cert."#text"), New Cert : $($UpdatedSSCerts.item($cert."#text"))" $cert."#text" = $UpdatedSSCerts.item($cert."#text") } else { Write-Warning "Unable to configure valid cert for Load Balancer Application Profile $AppProfile $certType with current invalid cert $($cert."#text"). This may be due to the use of an externally signed certificate on the source Edge. The application Profile will have to be manually updated." } } #SSLVPN cert Fixup if ( (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_NewEdge.features.sslvpnConfig.serverSettings -query "child::certificateId") ) { if ( $UpdatedSSCerts.item($_NewEdge.features.sslvpnConfig.serverSettings.certificateId) ) { Write-Warning "Fixing up cert for SSLVPN server : Old Cert : $($_NewEdge.features.sslvpnConfig.serverSettings.certificateId), New Cert : $($UpdatedSSCerts.item($_NewEdge.features.sslvpnConfig.serverSettings.certificateId))" $_NewEdge.features.sslvpnConfig.serverSettings.certificateId = $UpdatedSSCerts.item($_NewEdge.features.sslvpnConfig.serverSettings.certificateId) } else { Write-Warning "Unable to configure valid cert for SSL VPN Server with current invalid cert $($_NewEdge.features.sslvpnConfig.serverSettings.certificateId). This may be due to the use of an externally signed certificate on the source Edge. The service will have to be manually updated." } } } } ##################################################### #final update of edge config including cert fixups etc. ##################################################### $body = $_NewEdge.OuterXml $URI = "/api/4.0/edges/$edgeid" Write-Progress -Activity "Updating Edge Services Gateway $Name" $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Updating Edge Services Gateway $Name" -Completed #Get final updated Edge object and return to user. Get-NsxEdge -objectId $edgeId -Connection $connection } end {} } function Get-NsxDns { <# .SYNOPSIS Retrieves the DNS configuration from a specified Edge. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge DNS add DNS server (relay) on the Edge This cmdlet retrieves the DNS configuration from a specified Edge. .EXAMPLE PS C:\> Get-NsxEdge edge01 | Get-NsxDns #> [CmdLetBinding(DefaultParameterSetName = "Name")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdge $_ })] [System.Xml.XmlElement]$Edge ) begin {} process { $_DNS = $Edge.features.dns.CloneNode($True) Add-XmlElement -xmlRoot $_DNS -xmlElementName "edgeId" -xmlElementText $Edge.Id $_DNS } } function Set-NsxDns { <# .SYNOPSIS Configures an NSX DNS. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge DNS add DNS server (relay) on the Edge This cmdlet sets the basic DNS configuration of an NSX Edge. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxDns | Set-NsxDns -Enabled Enabled the DNS server on ESG .EXAMPLE Get-NsxEdge Edge01 | Get-NsxDns | Set-NsxDns -Enabled:$false Disabled the DNS server on ESG .EXAMPLE Get-NsxEdge Edge01 | Get-NsxDns | Set-NsxDns -DNSServer 192.0.2.2 Set the DNS Server to 192.0.2.2 .EXAMPLE Get-NsxEdge Edge01 | Get-NsxDns | Set-NsxDns -CacheSize 32 Change DNS Cache Size to 32 (Mb) .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Set-NsxDNS -EnableLogging Enabled DNS traffic logs. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxLoadBalancer | Set-NsxDNS -LogLevel debug Choose the log level (emergency, alert, critical, error, warning, notice, info, debug) of DNS logs. #> param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] [ValidateScript( { ValidateEdgeDNS $_ })] [System.Xml.XmlElement]$DNS, [Parameter (Mandatory = $False)] [switch]$Enabled, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [ipaddress[]]$DNSServer, [Parameter (Mandatory = $False)] [ValidateRange(1, 8196)] [int]$CacheSize, [Parameter (Mandatory = $False)] [ValidateNotNullOrEmpty()] [switch]$EnableLogging, [Parameter (Mandatory = $False)] [ValidateSet("emergency", "alert", "critical", "error", "warning", "notice", "info", "debug")] [string]$LogLevel, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { } process { #Create private xml element $_Dns = $DNS.CloneNode($true) #Store the edgeId and remove it from the XML as we need to post it... $edgeId = $_Dns.edgeId $_Dns.RemoveChild( $((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Dns -query 'descendant::edgeId')) ) | Out-Null #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('Enabled') ) { if ( Invoke-XpathQuery -Node $_Dns -QueryMethod SelectSingleNode -query "child::enabled" ) { $_Dns.enabled = $Enabled.ToString().ToLower() } else { Add-XmlElement -xmlRoot $_Dns -xmlElementName "enabled" -xmlElementText $Enabled.ToString().ToLower() } } if ( $PsBoundParameters.ContainsKey('CacheSize') ) { if ( Invoke-XpathQuery -Node $_Dns -QueryMethod SelectSingleNode -query "child::cacheSize" ) { $_Dns.CacheSize = $CacheSize.ToString() } else { Add-XmlElement -xmlRoot $_Dns -xmlElementName "cacheSize" -xmlElementText $CacheSize.ToString() } } if ( Invoke-XpathQuery -Node $_Dns -QueryMethod SelectSingleNode -query "child::dnsViews/dnsView") { if ( $PSBoundParameters.ContainsKey("DNSServer")) { if ( Invoke-XpathQuery -Node $_Dns -QueryMethod SelectSingleNode -query "child::dnsViews/dnsView/forwarders" ) { Write-Warning "Existing DNS servers configured are removed" #Remove DNS Server list... $_Dns.dnsViews.dnsview.RemoveChild((Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_Dns -query 'child::dnsViews/dnsView/forwarders')) | Out-Null [System.XML.XMLElement]$xmlDNSlist = $_Dns.OwnerDocument.CreateElement('forwarders') $_Dns.dnsViews.dnsView.Appendchild($xmlDNSlist) | Out-Null #Add list of new DNS Server foreach ($Server in $DNSServer) { Add-XmlElement -xmlRoot $xmlDNSlist -xmlElementName "ipAddress" -xmlElementText $Server.ToString() } } else { [System.XML.XMLElement]$xmlDNSlist = $_Dns.OwnerDocument.CreateElement('forwarders') $_Dns.dnsViews.dnsView.Appendchild($xmlDNSlist) | Out-Null foreach ($Server in $DNSServer) { Add-XmlElement -xmlRoot $xmlDNSlist -xmlElementName "ipAddress" -xmlElementText $Server.ToString() } } } } if ( $PsBoundParameters.ContainsKey('EnableLogging') ) { if ( Invoke-XpathQuery -Node $_Dns -QueryMethod SelectSingleNode -query "child::logging/enable" ) { $_Dns.logging.enable = $EnableLogging.ToString().ToLower() } else { Add-XmlElement -xmlRoot $_Dns -xmlElementName "enable" -xmlElementText $EnableLogging } } if ( $PsBoundParameters.ContainsKey('LogLevel') ) { if ( Invoke-XpathQuery -Node $_Dns -QueryMethod SelectSingleNode -query "child::logging/logLevel" ) { $_Dns.logging.LogLevel = $LogLevel } else { Add-XmlElement -xmlRoot $_Dns -xmlElementName "logLevel" -xmlElementText $LogLevel } } $URI = "/api/4.0/edges/$($edgeId)/dns/config" $body = $_Dns.OuterXml Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" $response = Invoke-NsxWebRequest -method "put" -URI $URI -body $body -connection $connection Write-Progress -Activity "Update Edge Services Gateway $($edgeId)" -Completed Get-NsxEdge -objectId $($edgeId) -Connection $connection | Get-NsxDns } end { } } function Remove-NsxDns { <# .SYNOPSIS Remove the global DNS configuration of an existing NSX Edge Services Gateway. .DESCRIPTION An NSX Edge Service Gateway provides all NSX Edge services such as firewall, NAT, DHCP, VPN, load balancing, and high availability. The NSX Edge DNS add DNS server (relay) on the Edge The Remove-NsxDns cmdlet unconfigures the global DNS configuration of the specified Edge Services Gateway. .EXAMPLE Get-NsxEdge Edge01 | Get-NsxDNS | Remove-NsxDns Remove all NSX DNS configuration with confirmation .EXAMPLE Get-NsxEdge Edge01 | Get-NsxDNS | Remove-NsxDns -NoConfirm:$true Remove all NSX DNS configuration without confirmation #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidDefaultValueSwitchParameter", "")] # Cant remove without breaking backward compatibility [CmdletBinding(DefaultParameterSetName = "Default")] param ( [Parameter (Mandatory = $true, ValueFromPipeline = $true, Position = 1)] #NSX Edge DNS to remove [ValidateScript( { ValidateEdgeDNS $_ })] [System.Xml.XmlElement]$DNS, [Parameter (Mandatory = $False, ParameterSetName = "LegacyConfirm")] #Prompt for confirmation. Specify as -confirm:$false to disable confirmation prompt [switch]$Confirm = $true, [Parameter (Mandatory = $False, ParameterSetName = "Default")] #Disable Prompt for confirmation. [switch]$NoConfirm, [Parameter (Mandatory = $False)] #PowerNSX Connection object [ValidateNotNullOrEmpty()] [PSCustomObject]$Connection = $defaultNSXConnection ) begin { If ( $PSCmdlet.ParameterSetName -eq "LegacyConfirm") { Write-Warning "The -confirm switch is deprecated and will be removed in a future release. Use -NoConfirm instead." $NoConfirm = ( -not $confirm ) } } process { $edgeId = $DNS.edgeId if ( -not ( $Noconfirm )) { $message = "Edge DNS removal is permanent." $question = "Proceed with removal of Edge DNS $($EdgeId) ?" $choices = New-Object Collections.ObjectModel.Collection[Management.Automation.Host.ChoiceDescription] $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes')) $choices.Add((New-Object Management.Automation.Host.ChoiceDescription -ArgumentList '&No')) $decision = $Host.UI.PromptForChoice($message, $question, $choices, 1) } else { $decision = 0 } if ($decision -eq 0) { $URI = "/api/4.0/edges/$($EdgeId)/dns/config" Write-Progress -Activity "Remove DNS for Edge $($EdgeId)" $null = Invoke-NsxWebRequest -method "delete" -URI $URI -connection $connection Write-Progress -Activity "Remove DNS for Edge $($EdgeId)" -Completed } } end {} } #Call Init function _init