PowerNSX.psm1
#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:AllValidIcmpTypes = @("echo-reply", "destination-unreachable", "source-quench", "redirect", "echo-request", "time-exceeded", "parameter-problem", "timestamp-request", "timestamp-reply", "address-mask-request", "address-mask-reply" ) 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" } if ( $script:PNsxPSTarget -eq "Core" ) { if ( $PSVersionTable.GitCommitId -ne 'v6.0.0-alpha.18') { write-warning "This build of PowerShell core has known issues that affect PowerNSX. The only recommended build of PowerShell Core at this stage is alpha-18." if ( $PSVersionTable.PSVersion -ne '6.0.0-alpha') { throw "The PowerShell Core Beta has known issues that cause PowerNSX to fail. Refusing to load module." } } } ## 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 = delegate { return true; }; } } } "@ 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<string, string> Headers; public string Content; public internalWebResponse() { this.Headers = new Dictionary<string,string>(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 Test-WebServerSSL { # Function original location: http://en-us.sysadmins.lv/Lists/Posts/Post.aspx?List=332991f0-bfed-4143-9eea-f521167d287c&ID=60 # Ref : https://communities.vmware.com/thread/501913?start=0&tstart=0 - Thanks Alan ;) [CmdletBinding()] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] [string]$URL, [Parameter(Position = 1)] [ValidateRange(1,65535)] [int]$Port = 443, [Parameter(Position = 2)] [Net.WebProxy]$Proxy, [Parameter(Position = 3)] [int]$Timeout = 15000, [switch]$UseUserContext ) Add-Type @" using System; using System.Net; using System.Security.Cryptography.X509Certificates; namespace PKI { namespace Web { public class WebSSL { public Uri OriginalURi; public Uri ReturnedURi; public X509Certificate2 Certificate; //public X500DistinguishedName Issuer; //public X500DistinguishedName Subject; public string Issuer; public string Subject; public string[] SubjectAlternativeNames; public bool CertificateIsValid; //public X509ChainStatus[] ErrorInformation; public string[] ErrorInformation; public HttpWebResponse Response; } } } "@ $ConnectString = "https://$url`:$port" $WebRequest = [Net.WebRequest]::Create($ConnectString) $WebRequest.Proxy = $Proxy $WebRequest.Credentials = $null $WebRequest.Timeout = $Timeout $WebRequest.AllowAutoRedirect = $true [Net.ServicePointManager]::ServerCertificateValidationCallback = {$true} try {$Response = $WebRequest.GetResponse()} catch {} if ($WebRequest.ServicePoint.Certificate -ne $null) { $Cert = [Security.Cryptography.X509Certificates.X509Certificate2]$WebRequest.ServicePoint.Certificate.Handle try {$SAN = ($Cert.Extensions | Where-Object {$_.Oid.Value -eq "2.5.29.17"}).Format(0) -split ", "} catch {$SAN = $null} $chain = New-Object Security.Cryptography.X509Certificates.X509Chain -ArgumentList (!$UseUserContext) [void]$chain.ChainPolicy.ApplicationPolicy.Add("1.3.6.1.5.5.7.3.1") $Status = $chain.Build($Cert) New-Object PKI.Web.WebSSL -Property @{ OriginalUri = $ConnectString; ReturnedUri = $Response.ResponseUri; Certificate = $WebRequest.ServicePoint.Certificate; Issuer = $WebRequest.ServicePoint.Certificate.Issuer; Subject = $WebRequest.ServicePoint.Certificate.Subject; SubjectAlternativeNames = $SAN; CertificateIsValid = $Status; Response = $Response; ErrorInformation = $chain.ChainStatus | ForEach-Object {$_.Status} } $chain.Reset() [Net.ServicePointManager]::ServerCertificateValidationCallback = $null $ServicePoint = [System.Net.ServicePointManager]::FindServicePoint($ConnectString) $ServicePoint.CloseConnectionGroup("") | out-null write-debug "$($MyInvocation.MyCommand.Name) : Closing connections to $ConnectString." } else { Write-Error $Error[0] } } 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") { #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 = "^(?<Internal>#\sinternal\s#\s)?(?<RuleSetMember>rule\s)?(?<RuleId>\d+)\sat\s(?<Position>\d+)\s(?<Direction>in|out|inout)\s" + "(?<Type>protocol|ethertype)\s(?<Service>.*?)\sfrom\s(?<Source>.*?)\sto\s(?<Destination>.*?)(?:\sport\s(?<Port>.*))?\s" + "(?<Action>accept|reject|drop)(?:\swith\s(?<Log>log))?(?:\stag\s(?<Tag>'.*'))?;" 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:<digits> "^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. "^(?<Type>ip|mac)\s(?<Address>.*),$" { #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 } } ######## ######## # 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+$" )) { #argument is moref string and refers to vm, resource pool or dvportgroup. $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 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 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." } } ########## ########## # 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" $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 ) { write-warning "Response header $header.key has more than one value. Only the first value is retained. Please raise an issue on the PowerNSX Github site with steps to reproduce if you see this warning!" } $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")] #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 } $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)$($URI)" write-debug "$($MyInvocation.MyCommand.Name) : Method: $method, URI: $FullURI, 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")] #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 } $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)$($URI)" write-debug "$($MyInvocation.MyCommand.Name) : Method: $method, URI: $FullURI, 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.) #> [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)] #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-<user>@<server>-<datetime>.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 } catch { Throw "Connection to NSX server $NsxServer failed : $_" } #Right - we got here, so now we try to build a connection object and retreive 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 # 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 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 retreived. 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 retreived. 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 retreived. 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 retreived. 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 retreived. 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 } 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 "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 retreive 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 retreive 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 #<nsxcli><command>show cluster all</command></nsxcli> $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 retreive 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 retreive 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 retreive 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 (One only.) [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. $OvfConfiguration.NetworkMapping.VSMgmt.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 $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 applaince 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. #> [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" $method = "put" } "Sso" { If ( (-not $PsBoundParameters.ContainsKey('SslThumbprint')) -and (-not $AcceptAnyThumbprint )) { Throw "Must specify an SSL Thumbprint or AcceptAnyThumbprint" } #Need to get the SSL thumbprint for vCenter to send it in the request. if ( $AcceptAnyThumbprint ) { try { $Cert = Test-WebServerSSL $SsoServer -erroraction Stop #The cert thumprint comes back without colons separating the bytes, so we add them $Thumbprint = $Cert.Certificate.Thumbprint -replace '(..(?!$))','$1:' } catch { Throw "An error occured retrieving the SSO SSL certificate. $_" } } else { $Thumbprint = $SslThumbprint } #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 $Thumbprint $method = "post" $uri = "/api/2.0/services/ssoconfig" } "vCenter" { If ( (-not $PsBoundParameters.ContainsKey('SslThumbprint')) -and (-not $AcceptAnyThumbprint )) { Throw "Must specify an SSL Thumbprint or AcceptAnyThumbprint" } #Need to get the SSL thumbprint for vCenter to send it in the request. if ( $AcceptAnyThumbprint ) { try { $VcCert = Test-WebServerSSL $vCenterServer -erroraction Stop #The cert thumprint comes back without colons separating the bytes, so we add them $Thumbprint = $VcCert.Certificate.Thumbprint -replace '(..(?!$))','$1:' } catch { Throw "An error occured retrieving the vCenter SSL certificate. $_" } } else { $Thumbprint = $SslThumbprint } #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 $Thumbprint Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "assignRoleToUser" -xmlElementText "true" Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "pluginDownloadServer" -xmlElementText "" $method = "put" $uri = "/api/2.0/services/vcconfig" } } Invoke-NsxRestMethod -Method $method -body $xmlRoot.outerXml -uri $uri -Connection $Connection } 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 Retreives 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 Retreives 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 Retreives 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 Retreives 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 Retreives 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 Retreives 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 Retreives 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 Retreives 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 Retreives 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 Retreives 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 retreived 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 $ControllerCluster = Get-Cluster vSphereCluster $ControllerDatastore = Get-Datastore $ControllerDatastoreName -server $Connection.VIConnection $ControllerPortGroup = Get-VDPortGroup $ControllerPortGroupName -server $Connection.VIConnection New-NsxController -ipPool $ippool -cluster $ControllerCluster -datastore $ControllerDatastore -PortGroup $ControllerPortGroup -password $DefaultNsxControllerPassword -connection $Connection -confirm:$false .EXAMPLE $ControllerName = "MyNSXCtrl1" $ippool = New-NsxIpPool -Name ControllerPool -Gateway 192.168.10.1 -SubnetPrefixLength 24 -StartAddress 192.168.10.100 -endaddress 192.168.10.200 $ControllerCluster = Get-Cluster vSphereCluster $ControllerDatastore = Get-Datastore $ControllerDatastoreName -server $Connection.VIConnection $ControllerPortGroup = Get-VDPortGroup $ControllerPortGroupName -server $Connection.VIConnection New-NsxController -ControllerName $ControllerName -ipPool $ippool -cluster $ControllerCluster -datastore $ControllerDatastore -PortGroup $ControllerPortGroup -password $DefaultNsxControllerPassword -connection $Connection -confirm:$false .EXAMPLE A secondary or tertiary controller does not require a Password to be defined. New-NsxController -ipPool $ippool -cluster $ControllerCluster -datastore $ControllerDatastore -PortGroup $ControllerPortGroup -connection $Connection -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=$False)] #Controller 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." } $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')) } 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("ControllerName")) {Add-XmlElement -xmlRoot $ControllerSpec -xmlElementName "name" -xmlElementText $ControllerName.ToString()} 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() $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 Retreives 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 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 retreives 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-NsxClusterVibs 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 { $null = 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" #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() 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 retreives Segment Ranges from the connected NSX manager. #> [CmdletBinding(DefaultParameterSetName="Default")] param ( [Parameter (Mandatory=$false,Position=1,ParameterSetName = "Name")] #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 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")] #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")) { if ( $PsBoundParameters.containsKey('name') ) { $response.vdnscopes.vdnscope | where-object { $_.name -eq $name } } elseif ( $UniversalOnly ) { $response.vdnscopes.vdnscope | where-object { $_.isUniversal -eq 'True' } } elseif ( $LocalOnly ) { $response.vdnscopes.vdnscope | where-object { $_.isUniversal -eq 'False' } } else { $response.vdnscopes.vdnscope } } } } 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 Connect-NsxServer Add-NsxLicense "aaaa-bbbb-cccc-dddd-eeee" #> 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 ( $Connection.Version -gt 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 Retreives 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 retreives existing licenses from the vCenter associated with the specified (or default) NSX connection. .EXAMPLE Connect-NsxServer Add-NsxLicense "aaaa-bbbb-cccc-dddd-eeee" #> 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 ( $Connection.Version -gt 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 retreive NSX license. $_" } } } end {} } ######### ######### # User related functions function Get-NsxUserRole { <# .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 Example1: Get a named Logical Switch PS C:\> Get-NsxTransportZone | Get-NsxLogicalswitch -name LS1 Example2: Get all logical switches in a given transport zone. PS C:\> Get-NsxTransportZone | Get-NsxLogicalswitch #> 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 retreive 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 Example1: Get a named Logical Switch PS C:\> Get-NsxTransportZone | Get-NsxLogicalswitch -name LS1 Example2: Get all logical switches in a given transport zone. PS C:\> Get-NsxTransportZone | Get-NsxLogicalswitch #> [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 Example1: Create a Logical Switch with default control plane mode. PS C:\> Get-NsxTransportZone | New-NsxLogicalSwitch -name LS6 Example2: Create a Logical Switch with a specific control plane mode. PS C:\> Get-NsxTransportZone | New-NsxLogicalSwitch -name LS6 -ControlPlaneMode MULTICAST_MODE #> [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 Example1: Remove a Logical Switch PS C:\> Get-NsxTransportZone | Get-NsxLogicalSwitch LS6 | Remove-NsxLogicalSwitch Example2: Remove a Logical Switch without confirmation. PS C:\> Get-NsxTransportZone | Get-NsxLogicalSwitch LS6 | Remove-NsxLogicalSwitch -confirm:$false #> [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 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 Retreives 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 retreive 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 <name> 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 retreive 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 Retreives 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 retreive 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 {} } ######## ######## # 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 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(6,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 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 or Redploy operation to be performed on the specified Edges 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. #> [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.) [ValidateSet("ForceSync", "Redeploy")] [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 Disable the Edge Firewall on ESG Edge01 PS C:\> $edge = Get-NsxEdge Edge01 PS C:\> $edge.features.firewall.enabled = "false" PS C:\> $edge | Set-NsxEdge #> [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, [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 } $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. This cmdlet removes the specified ESG. .EXAMPLE PS C:\> Get-NsxEdge Edge01 | Remove-NsxEdge -confirm:$false #> [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 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 Enable SSH on edge Edge01 C:\PS> Get-NsxEdge Edge01 | Enable-NsxEdgeSsh #> 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 Disable SSH on edge Edge01 C:\PS> Get-NsxEdge Edge01 | Disable-NsxEdgeSsh #> [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 {} } ######### ######### # 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 retreives the global NAT 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 $_EdgeNat = $Edge.features.nat.CloneNode($True) Add-XmlElement -xmlRoot $_EdgeNat -xmlElementName "edgeId" -xmlElementText $Edge.Id $_EdgeNat } end {} } function Get-NsxEdgeNatRule { <# .SYNOPSIS Retreives 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 retreives the nat rules from the nat configuration specified. #> 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. #> [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)] [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 ( -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. #> [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. #> [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 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 retreives 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 retreives 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 retreives 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 retreives 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 {} } ######### ######### # 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 Retreives 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 retreives the routing configuration of the specified Edge Services Gateway. .EXAMPLE Get routing configuration for the ESG Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting #> 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 Retreives 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 retreives the static routes from the routing configuration specified. .EXAMPLE Get static routes defining on ESG Edge01 PS C:\> Get-NsxEdge | Get-NsxEdgeRouting | Get-NsxEdgeStaticRoute #> 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 Remove a route to 1.1.1.0/24 via 10.0.0.100 from ESG Edge01 PS C:\> 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 .EXAMPLE Remove all routes to 1.1.1.0/24 from ESG Edge01 PS C:\> Get-NsxEdge Edge01 | Get-NsxEdgeRouting | Get-NsxEdgeStaticRoute | where-object { $_.network -eq '1.1.1.0/24' } | Remove-NsxEdgeStaticRoute #> [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 Retreives 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 retreives 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 Retreives 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 retreives 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 retreives 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() } $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 Retreives 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 retreives 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 retreives 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 retreives 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 retreives 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 <this edge> | 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 Retreives 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 retreives 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 Retreives 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 retreives 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 Add a new static route to LogicalRouter LogicalRouter01 for 1.1.1.0/24 via 10.0.0.200 PS C:\> Get-NsxLogicalRouter LogicalRouter01 | Get-NsxLogicalRouterRouting | New-NsxLogicalRouterStaticRoute -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({ 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 Retreives 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 retreives 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 Retreives 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 retreives 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 retreives 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() } $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 Retreives 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 retreives 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 retreives 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 retreives 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 retreives 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 <this logicalrouter> | 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 Retreives 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 retreived 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 Retreives 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 retreived 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)] #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+$" )) { $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=$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 { #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+$" )) { $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 ( $FailIfAbsent) { #Need to check before removing the member, because we are now using bulk update, the API doesnt do this for us. #To support the prior functionality of failIfAbsent, we have to check ourselves... $existingMember = (Invoke-XpathQuery -QueryMethod SelectSingleNode -Node $_SecurityGroup -query "child::member[objectId=`"$MemberMoref`"]" ) if ( $existingMember -eq $null ) { throw "Member $($_Member.Name) ($MemberMoref) is not a member of the specified SecurityGroup." } else { $null = $_SecurityGroup.Removechild($existingMember) } } } $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 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 if ( (Invoke-XPathQuery -QueryMethod SelectSingleNode -Node $response -Query 'descendant::securityTags/securityTag')) { if ( $PsBoundParameters.ContainsKey('Name')) { $tags = $response.securitytags.securitytag | where-object { $_.name -eq $name } } else { $tags = $response.securitytags.securitytag } 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")] [ValidateNotNullorEmpty()] [VMware.VimAutomation.ViCore.Interop.V1.Inventory.VirtualMachineInterop]$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... $vm = Get-Vm -Server $Connection.VIConnection -id "VirtualMachine-$($node.objectId)" [pscustomobject]@{ "SecurityTag" = $SecurityTag; "VirtualMachine" = $vm } } } } 'VirtualMachine' { #I know this is inneficient, but attempt at refactoring has led down a rabbit hole I dont have time for at the moment. # 'Ill be back...'' $vmMoid = $VirtualMachine.ExtensionData.MoRef.Value Write-Progress -activity "Fetching Security Tags assigned to Virtual Machine $($vmMoid)" Get-NsxSecurityTag -connection $connection | Get-NsxSecurityTagAssignment -connection $connection | Where-Object {($_.VirtualMachine.id -replace "VirtualMachine-","") -eq $($vmMoid)} } } } 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-NsxSecurityTagAssigment | Remove-NsxSecurityTagAssignment Gets all assigment of Security Tag ST-WEB-DMZ and removes its assignment from all VMs with confirmation. .EXAMPLE Get-VM Web01 | Get-NsxSecurityTagAssigment | 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)] #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. IPAddresses 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" -IPAddresses "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" -IPAddresses "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" -IPAddresses "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. [string]$IPAddresses, [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 ( $IPAddresses ) { Add-XmlElement -xmlRoot $xmlRoot -xmlElementName "value" -xmlElementText $IPaddresses } 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)] #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)] #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.applicationProtocol) : $($application.element.value)" } } } 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)] #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." } } } 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 ( ( $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")] [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 PS C:\> Get-NsxServiceGroup Heartbeat | Add-NsxServiceGroupMember -Member $Service1 PS C:\> get-nsxservicegroup Service-Group-4 | Add-NsxServiceGroupMember $Service1,$Service2 #> 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 retreived using a simple API call which is typically much less expensive than the alternative of retreiving the complete configuration from the API for a specific type of object. This cmdlet also exposes 'shortcut' functionality that lets you retreive 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 retreiving 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 apliedto 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 apliedto 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. Currently this cmdlet only supports creating a section at the top of the ruleset. .EXAMPLE PS C:\> New-NsxFirewallSection -Name TestSection #> [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)] #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("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 $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 {} process { if ( $PSCmdlet.ParameterSetName -eq "Section" ) { $URI = "/api/4.0/firewall/$scopeID/config/$ruletype/$($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 C:\> Get-NsxFirewallSection TestSection | New-NsxFirewallRule -Name TestRule -Source $LS1 -Destination $LS1 -Action allow -service (Get-NsxService HTTP) -AppliedTo $LS1 -EnableLogging -Comment "Testing Rule Creation" .EXAMPLE PS C:\> 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 disabled rule to 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")] [string]$Position="Top", [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 ) # Todo: Review need to specify rule type in param - should be able to determine from section type that is mandatory...? begin {} process { $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 } } #Do the post $body = $Section.OuterXml $URI = "/api/4.0/firewall/$scopeId/config/$ruletype/$($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 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 Retreives the entire contents of the exclusion list .EXAMPLE Get-NsxFirewallExclusionListMember | where-object { $_.name -match 'myvm'} Retreives 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 -ObjectId 403 Retrieves a Distributed Firewall configuration by ObjectId #> [CmdLetBinding(DefaultParameterSetName="Name")] param ( [Parameter (Mandatory=$false,ParameterSetName="ObjectId")] [string]$ObjectId, [Parameter (Mandatory=$false,Position=1,ParameterSetName="Name")] [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")){ $Return.firewallDrafts.firewallDraft | where-object {$_.name -eq $Name} } 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 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 retreives 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() } $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 $port 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-NsxSecurityPolicy { <# .SYNOPSIS Retrieves 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 PS C:\> Get-NsxSecurityPolicy SecPolicy_WebServers #> [CmdLetBinding(DefaultParameterSetName="Name")] param ( [Parameter (Mandatory=$false,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=$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 { if ( -not $objectId ) { #All Security Policies $URI = "/api/2.0/services/policy/securitypolicy/all" $response = invoke-nsxrestmethod -method "get" -uri $URI -connection $connection if ( $Name ) { $FinalSecPol = $response.securityPolicies.securityPolicy | where-object { $_.name -eq $Name } } else { $FinalSecPol = $response.securityPolicies.securityPolicy } } else { #Just getting a single Security group $URI = "/api/2.0/services/policy/securitypolicy/$objectId" $response = invoke-nsxrestmethod -method "get" -uri $URI -connection $connection $FinalSecPol = $response.securityPolicy } 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 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)] [ValidateNotNullOrEmpty()] [System.Xml.XmlElement]$SecurityPolicy, [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 $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 {} } ######## ######## # 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 {} } 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(6,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 {} } #Call Init function _init |