modules/SdnDiag.Server/SdnDiag.Server.psm1
# Copyright (c) Microsoft Corporation. # Licensed under the MIT License. Using module .\SdnDiag.Server.Helper.psm1 Import-Module $PSScriptRoot\..\SdnDiag.Common\SdnDiag.Common.psm1 Import-Module $PSScriptRoot\..\SdnDiag.Utilities\SdnDiag.Utilities.psm1 # create local variable to store configuration data $configurationData = Import-PowerShellDataFile -Path "$PSScriptRoot\SdnDiag.Server.Config.psd1" New-Variable -Name 'SdnDiagnostics_Server' -Scope 'Local' -Force -Value @{ Config = $configurationData } ##### FUNCTIONS AUTO-POPULATED BELOW THIS LINE DURING BUILD ##### function Get-OvsdbAddressMapping { <# .SYNOPSIS Returns a list of address mappings from within the OVSDB database. .EXAMPLE PS> Get-OvsdbAddressMapping #> try { $arrayList = [System.Collections.ArrayList]::new() $ovsdbResults = Get-OvsdbDatabase -Table ms_vtep $paMappingTable = $ovsdbResults | Where-Object { $_.caption -eq 'Physical_Locator table' } $caMappingTable = $ovsdbResults | Where-Object { $_.caption -eq 'Ucast_Macs_Remote table' } $logicalSwitchTable = $ovsdbResults | Where-Object { $_.caption -eq 'Logical_Switch table' } if ($null -eq $caMappingTable) { return $null } # enumerate the json rules for each of the tables and create psobject for the mappings # unfortunately these values do not return in key/value pair and need to manually map each property foreach ($caMapping in $caMappingTable.Data) { $mac = $caMapping[0] $uuid = $caMapping[1][1] $ca = $caMapping[2] $locator = $caMapping[3][1] $logicalSwitch = $caMapping[4][1] $mappingType = $caMapping[5] $pa = [string]::Empty $encapType = [string]::Empty $rdid = [string]::Empty $vsid = 0 # Get PA from locator table foreach ($paMapping in $paMappingTable.Data) { $curLocator = $paMapping[0][1] if ($curLocator -eq $locator) { $pa = $paMapping[3] $encapType = $paMapping[4] break } } # Get Rdid and VSID from logical switch table foreach ($switch in $logicalSwitchTable.Data) { $curSwitch = $switch[0][1] if ($curSwitch -eq $logicalSwitch) { $rdid = $switch[1] $vsid = $switch[3] break } } # create the psobject now that we have all the mappings identified $result = New-Object PSObject -Property @{ UUID = $uuid CustomerAddress = $ca ProviderAddress = $pa MAC = $mac RoutingDomainID = $rdid VirtualSwitchID = $vsid MappingType = $mappingType EncapType = $encapType } # add the psobject to the array [void]$arrayList.Add($result) } return $arrayList } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-OvsdbDatabase { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [OvsdbTable]$Table ) try { $localPort = Get-NetTCPConnection -LocalPort:6641 -ErrorAction:SilentlyContinue if ($null -eq $localPort){ throw New-Object System.NullReferenceException("No endpoint listening on port 6641. Ensure NCHostAgent service is running.") } $cmdline = "ovsdb-client.exe dump tcp:127.0.0.1:6641 -f json {0}" -f $Table $databaseResults = Invoke-Expression $cmdline | ConvertFrom-Json if($null -eq $databaseResults){ $msg = "Unable to retrieve OVSDB results`n`t{0}" -f $_ throw New-Object System.NullReferenceException($msg) } else { return $databaseResults } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-OvsdbFirewallRuleTable { <# .SYNOPSIS Returns a list of firewall rules defined within the firewall table of the OVSDB database. .EXAMPLE PS> Get-OvsdbFirewallRuleTable #> try { $arrayList = [System.Collections.ArrayList]::new() $ovsdbResults = Get-OvsdbDatabase -Table ms_firewall $firewallTable = $ovsdbResults | Where-Object { $_.caption -eq 'FW_Rules table' } if ($null -eq $firewallTable) { return $null } # enumerate the json rules and create object for each firewall rule returned # there is no nice way to generate this and requires manually mapping as only the values are return foreach ($obj in $firewallTable.data) { $result = New-Object PSObject -Property @{ uuid = $obj[0][1] action = $obj[1] direction = $obj[2] dst_ip_addresses = $obj[3] dst_ports = $obj[4] logging_state = $obj[5] priority = $obj[6] protocols = $obj[7] rule_id = $obj[8] rule_state = $obj[9] rule_type = $obj[10] src_ip_addresses = $obj[11] src_ports = $obj[12] vnic_id = $obj[13].Trim('{', '}') } # add the psobject to array list [void]$arrayList.Add($result) } return $arrayList } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-OvsdbGlobalTable { <# .SYNOPSIS Returns the global table configuration from OVSDB database. .EXAMPLE PS> Get-OvsdbGlobalTable #> try { $arrayList = [System.Collections.ArrayList]::new() $ovsdbResults = Get-OvsdbDatabase -Table ms_vtep $globalTable = $ovsdbResults | Where-Object { $_.caption -eq 'Global table' } if ($null -eq $globalTable) { return $null } # enumerate the json results and add to psobject foreach ($obj in $globalTable.data) { $result = New-Object PSObject -Property @{ uuid = $obj[0][1] cur_cfg = $obj[1] next_cfg = $obj[4] switches = $obj[6][1] } # add the psobject to array [void]$arrayList.Add($result) } return $arrayList } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-OvsdbPhysicalPortTable { <# .SYNOPSIS Returns a list of ports defined within the Physical_Port table of the OVSDB database. .EXAMPLE PS> Get-OvsdbPhysicalPortTable #> try { $arrayList = [System.Collections.ArrayList]::new() $ovsdbResults = Get-OvsdbDatabase -Table ms_vtep $portTable = $ovsdbResults | Where-Object { $_.caption -eq 'Physical_Port table' } if ($null -eq $portTable) { return $null } # enumerate the json objects and create psobject for each port foreach ($obj in $portTable.data) { $result = New-Object PSObject -Property @{ uuid = $obj[0][1] description = $obj[1] name = $obj[2].Trim('{', '}') } # there are numerous key/value pairs within this object with some having different properties # enumerate through the properties and add property and value for each on that exist foreach ($property in $obj[4][1]) { $result | Add-Member -MemberType NoteProperty -Name $property[0] -Value $property[1] } # add the psobject to array [void]$arrayList.Add($result) } return $arrayList } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-OvsdbRouterTable { <# .SYNOPSIS Returns the logical router table configuration from OVSDB database. .EXAMPLE PS> Get-OvsdbRouterTable #> try { $arrayList = [System.Collections.ArrayList]::new() $ovsdbResults = Get-OvsdbDatabase -Table ms_vtep $routerTable = $ovsdbResults | Where-Object { $_.caption -eq 'Logical_Router table' } if ($null -eq $routerTable) { return $null } # enumerate the json results and add to psobject foreach ($obj in $routerTable.data) { $staticroute = "" if($obj[5][1].count -gt 0){ foreach($route in $obj[5][1]){ if(![string]::IsNullOrEmpty(($staticroute))){ $staticroute += ', ' } $staticRoute += "$($route[0])=$($route[1])" } } $switchbinding = "" if($obj[6][1].count -gt 0){ foreach($switch in $obj[6][1]){ if(![string]::IsNullOrEmpty(($switchbinding))){ $switchbinding += ', ' } $switchbinding += "$($switch[0])=$($switch[1][1])" } } $result = New-Object PSObject -Property @{ #uuid = $obj[0][1] description = $obj[1] enable = $obj[2] vnetid = $obj[3] staticroutes = $staticroute switchbinding = $switchbinding } # add the psobject to array [void]$arrayList.Add($result) } return $arrayList } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-OvsdbUcastMacRemoteTable { <# .SYNOPSIS Returns a list of mac addresses defined within the Ucast_Macs_Remote table of the OVSDB database. .EXAMPLE PS> Get-OvsdbUcastMacRemoteTable #> try { $arrayList = [System.Collections.ArrayList]::new() $ovsdbResults = Get-OvsdbDatabase -Table ms_vtep $ucastMacsRemoteTable = $ovsdbResults | Where-Object { $_.caption -eq 'Ucast_Macs_Remote table' } if ($null -eq $ucastMacsRemoteTable) { return $null } # enumerate the json objects and create psobject for each port foreach ($obj in $ucastMacsRemoteTable.data) { $result = New-Object PSObject -Property @{ uuid = $obj[1][1] mac = $obj[0] ipaddr = $obj[2] locator = $obj[3][1] logical_switch = $obj[4][1] mapping_type = $obj[5] } [void]$arrayList.Add($result) } return $arrayList } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-VfpVMSwitchPort { <# .SYNOPSIS Returns a list of ports from within VFP. #> try { $arrayList = [System.Collections.ArrayList]::new() $vfpResults = vfpctrl /list-vmswitch-port if ($null -eq $vfpResults) { $msg = "Unable to retrieve vmswitch ports from vfpctrl`n{0}" -f $_ throw New-Object System.NullReferenceException($msg) } foreach ($line in $vfpResults) { $line = $line.Trim() if ($line -like 'ITEM LIST' -or $line -ilike '==========='){ continue } if ([string]::IsNullOrEmpty($line)) { continue } # lines in the VFP output that contain : contain properties and values # need to split these based on count of ":" to build key and values if ($line.Contains(":")) { [System.String[]]$results = $line.Split(':').Replace(" ", "").Trim() if ($results.Count -eq 3) { $key = "$($results[0])-$($results[1])" $value = $results[2] } elseif ($results.Count -eq 2) { $key = $results[0] $value = $results[1] } # all groups begin with this property and value so need to create a new psobject when we see these keys if ($key -ieq 'Portname') { if ($object) { [void]$arrayList.Add($object) } $object = New-Object -TypeName PSObject $object | Add-Member -MemberType NoteProperty -Name 'PortName' -Value $value continue } # add the line values to the object $object | Add-Member -MemberType NoteProperty -Name $key -Value $value } elseif ($line.Contains('Command list-vmswitch-port succeeded!')) { if ($object) { [void]$arrayList.Add($object) } } else { if ($line.Contains('Port is')) { $object | Add-Member -MemberType NoteProperty -Name 'PortState' -Value $line.Split(' ')[2].Replace('.','').Trim() } elseif ($line.Contains('MAC Learning is')) { $object | Add-Member -MemberType NoteProperty -Name 'MACLearning' -Value $line.Split(' ')[3].Replace('.','').Trim() } elseif ($line.Contains('NIC is')) { $object | Add-Member -MemberType NoteProperty -Name 'NICState' -Value $line.Split(' ')[2].Replace('.','').Trim() } } } return $arrayList } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnNetAdapterEncapOverheadConfig { <# .SYNOPSIS Retrieves the EncapOverhead and JumboPacket properties of each network interface attached to a vfp enabled vmswitch .EXAMPLE PS> Get-SdnNetAdapterEncapOverheadConfig #> try { $switchArrayList = @() # filter to only look at vSwitches where the Microsoft Azure VFP Switch Extension is installed # once we have the vSwitches, then need to then filter and only look at switches where VFP is enabled $vfpSwitch = Get-VMSwitch | Where-Object {$_.Extensions.Id -ieq 'F74F241B-440F-4433-BB28-00F89EAD20D8'} foreach ($switch in $vfpSwitch) { $vfpExtension = $switch.Extensions | Where-Object {$_.Id -ieq 'F74F241B-440F-4433-BB28-00F89EAD20D8'} if ($vfpExtension.Enabled -ieq $false) { continue } $interfaceArrayList = @() $supportsEncapOverhead = $false $encapOverheadValue = $null $supportsJumboPacket = $false $jumboPacketValue = $null # enumerate each of the physical network adapters that are bound to the vmswitch foreach ($physicalNicIfDesc in $switch.NetAdapterInterfaceDescriptions) { # get the encap overhead settings for each of the network interfaces within the vm switch team $encapOverhead = Get-NetAdapterAdvancedProperty -InterfaceDescription $physicalNicIfDesc -RegistryKeyword "*Encapoverhead" -ErrorAction SilentlyContinue if ($null -eq $encapoverhead) { "Network interface {0} does not support EncapOverhead." -f $physicalNicIfDesc | Trace-Output -Level:Warning } else { $supportsEncapOverhead = $true [int]$encapOverheadValue = $encapoverhead.DisplayValue } # get the jumbo packet settings for each of the network interfaces within the vm switch team $jumboPacket = Get-NetAdapterAdvancedProperty -InterfaceDescription $physicalNicIfDesc -RegistryKeyword "*JumboPacket" -ErrorAction SilentlyContinue if ($null -eq $jumboPacket) { "Network interface {0} does not support JumboPacket." -f $physicalNicIfDesc | Trace-Output -Level:Warning } else { $supportsJumboPacket = $true [int]$jumboPacketValue = $jumboPacket.RegistryValue[0] } $object = [PSCustomObject]@{ Switch = $switch.Name NetworkInterface = $physicalNicIfDesc EncapOverheadEnabled = $supportsEncapOverhead EncapOverheadValue = $encapOverheadValue JumboPacketEnabled = $supportsJumboPacket JumboPacketValue = $jumboPacketValue } # add each network interface to the interface array $interfaceArrayList += $object } # add each of the switches to the array $switchArrayList += $interfaceArrayList } return $switchArrayList } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnNetAdapterRdmaConfig { <# .SYNOPSIS Checks numerous settings within a network adapter to validate RDMA status. .PARAMETER InterfaceIndex Interface index of the adapter for which RDMA config is to be verified. .EXAMPLE PS> Get-SdnNetAdapterRdmaConfig -InterfaceIndex 25 #> [CmdletBinding()] Param( [Parameter(Mandatory = $true)] [uint32]$InterfaceIndex ) try { [System.String]$adapterType = $null [bool]$rdmaEnabled = $false [bool]$maxQueueConfigIsValid = $false [bool]$smbInterfaceRdmaCapable = $false [bool]$qosEnabled = $false [bool]$qosOperationalFlowControlEnabled = $false $rdmaAdapter = Get-NetAdapter -InterfaceIndex $InterfaceIndex if ($null -eq $rdmaAdapter) { throw New-Object System.NullReferenceException("Adapter with interface index $InterfaceIndex was not found") } "Determining adapter type based on interface description '{0}'" -f $rdmaAdapter.InterfaceDescription | Trace-Output -Level:Verbose switch -Wildcard ($rdmaAdapter.InterfaceDescription) { 'Hyper-V Virtual Ethernet Adapter*' { $adapterType = "vNIC" } 'Microsoft Hyper-V Network Adapter*' { $adapterType = "vmNIC" } default { $adapterType = "pNIC" } } "Network adapter {0} (Name: {1}) is a {2}" -f $rdmaAdapter.InterfaceIndex, $rdmaAdapter.Name, $adapterType | Trace-Output -Level:Verbose $rdmaCapabilities = Get-NetAdapterRdma -InterfaceDescription $rdmaAdapter.InterfaceDescription if($null -eq $rdmaCapabilities -or $rdmaCapabilities.Enabled -ieq $false) { $rdmaEnabled = $false "Network adapter {0} is not enabled for RDMA" -f $rdmaAdapter.InterfaceIndex | Trace-Output -Level:Warning } else { $rdmaEnabled = $rdmaCapabilities.Enabled } if ($rdmaCapabilities.MaxQueuePairCount -eq 0 -or $rdmaCapabilities.MaxCompletionQueueCount -eq 0) { $maxQueueConfigIsValid = $false "RDMA capabilities for adapter {0} are not valid. MaxQueuePairCount and MaxCompletionQueueCount cannot be set to 0" -f $rdmaAdapter.InterfaceIndex | Trace-Output -Level:Warning } else { $maxQueueConfigIsValid = $true } $rdmaAdapterSmbClientNetworkInterface = Get-SmbClientNetworkInterface | Where-Object {$_.InterfaceIndex -ieq $InterfaceIndex} if ($null -eq $rdmaAdapterSmbClientNetworkInterface) { "No interfaces found within SMB Client Network Interfaces that match interface index {0}" -f $InterfaceIndex | Trace-Output -Level:Warning } else { if ($rdmaAdapterSmbClientNetworkInterface.RdmaCapable -eq $false) { $smbInterfaceRdmaCapable = $false "SMB did not detect network adapter {0} as RDMA capable. Make sure the adapter is bound to TCP/IP and not to other protocol like vmSwitch." -f $rdmaAdapter.InterfaceIndex | Trace-Output -Level:Warning } else { $smbInterfaceRdmaCapable = $true } } if ($adapterType -eq "vNIC") { "Retrieving vSwitch bound to the virtual adapter" | Trace-Output -Level:Verbose $virtualAdapter = Get-VMNetworkAdapter -ManagementOS | Where-Object {$_.DeviceId -eq $rdmaAdapter.DeviceID} $vSwitch = Get-VMSwitch -Name $virtualAdapter.SwitchName if ($vSwitch) { "Found vSwitch: {0}" -f $vSwitch.Name | Trace-Output -Level:Verbose $rdmaAdapters = Get-NetAdapter -InterfaceDescription $vSwitch.NetAdapterInterfaceDescriptions if ($rdmaAdapters) { "Found the following physical adapter(s) bound to vSwitch:`r`n`n {0}" -f ` ($rdmaAdapters.InterfaceDescription ` | Select-Object @{n="Description";e={"`t$($_)"}} ` | Select-Object -ExpandProperty Description ` | Out-String ) | Trace-Output -Level:Verbose } } } if ($null -ne $rdmaAdapters -and $adapterType -ne "vmNIC") { "Checking if QoS/DCB/PFC are configured on each physical adapter(s)" | Trace-Output -Level:Verbose # set these values to $true as we are looping multiple interfaces # we want to ensure if one interface is false for either value, that the object is reset back to $false # this ensures we don't get a false positive if some interfaces are enabled vs others are disabled $qosEnabled = $true $qosOperationalFlowControlEnabled = $true foreach ($qosAdapter in $rdmaAdapters) { "Checking {0}" -f $qosAdapter.InterfaceDescription | Trace-Output -Level:Verbose $qos = Get-NetAdapterQos -Name $qosAdapter.Name "NetAdapterQos is currently set to {0}" -f $qos.Enabled | Trace-Output -Level:Verbose if ($qos.Enabled -eq $false) { $qosEnabled = $false "QoS is not enabled for adapter {0}. This is required for RDMA over Converged Ethernet (RoCE)." -f $qosAdapter.InterfaceDescription | Trace-Output -Level:Warning } "OperationalFlowControl is currently set to {0}" -f $qos.OperationalFlowControl | Trace-Output -Level:Verbose if ($qos.OperationalFlowControl -eq "All Priorities Disabled") { $qosOperationalFlowControlEnabled = $false "Flow control priorities are disabled for adapter {0}. This is required for RDMA over Converged Ethernet (RoCE)." -f $qosAdapter.InterfaceDescription | Trace-Output -Level:Warning } } } $object = [PSCustomObject]@{ Name = $rdmaAdapter.Name InterfaceDescription = $rdmaAdapter.InterfaceDescription InterfaceIndex = $InterfaceIndex AdapterType = $adapterType MaxQueueConfigIsValid = $maxQueueConfigIsValid QoSEnabled = $qosEnabled QoSOperationalFlowControlEnabled = $qosOperationalFlowControlEnabled RdmaEnabled = $rdmaEnabled SMBInterfaceRdmaCapable = $smbInterfaceRdmaCapable } return $object } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnOvsdbAddressMapping { <# .SYNOPSIS Gets the address mappings from OVSDB. .PARAMETER ComputerName Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more remote computers. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .PARAMETER AsJob Switch indicating to trigger a background job to perform the operation. .PARAMETER PassThru Switch indicating to wait for background job completes and display results to current session. .PARAMETER Timeout Specify the timeout duration to wait before job is automatically terminated. If omitted, defaults to 300 seconds. .EXAMPLE PS> Get-SdnOvsdbAddressMapping .EXAMPLE PS> Get-SdnOvsdbAddressMapping -ComputerName 'Server01','Server02' .EXAMPLE PS> Get-SdnOvsdbAddressMapping -ComputerName 'Server01','Server02' -Credential (Get-Credential) .EXAMPLE PS> Get-SdnOvsdbAddressMapping -ComputerName 'Server01','Server02' -AsJob .EXAMPLE PS> Get-SdnOvsdbAddressMapping -ComputerName 'Server01','Server02' -AsJob -PassThru .EXAMPLE PS> Get-SdnOvsdbAddressMapping -ComputerName 'Server01','Server02' -AsJob -PassThru -Timeout 600 #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string[]]$ComputerName, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$AsJob, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$PassThru, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [int]$Timeout = 300 ) try { if ($PSBoundParameters.ContainsKey('ComputerName')) { Invoke-PSRemoteCommand -ComputerName $ComputerName -ScriptBlock { Get-SdnOvsdbAddressMapping } -Credential $Credential ` -AsJob:($AsJob.IsPresent) -PassThru:($PassThru.IsPresent) -ExecutionTimeout $Timeout } else { Get-OvsdbAddressMapping } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnOvsdbFirewallRuleTable { <# .SYNOPSIS Gets the firewall rules from OVSDB .PARAMETER ComputerName Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more remote computers. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .PARAMETER AsJob Switch indicating to trigger a background job to perform the operation. .PARAMETER PassThru Switch indicating to wait for background job completes and display results to current session. .PARAMETER Timeout Specify the timeout duration to wait before job is automatically terminated. If omitted, defaults to 300 seconds. .EXAMPLE PS> Get-SdnOvsdbFirewallRuleTable -ComputerName 'Server01','Server02' .EXAMPLE PS> Get-SdnOvsdbFirewallRuleTable -ComputerName 'Server01','Server02' -Credential (Get-Credential) .EXAMPLE PS> Get-SdnOvsdbFirewallRuleTable -ComputerName 'Server01','Server02' -AsJob .EXAMPLE PS> Get-SdnOvsdbFirewallRuleTable -ComputerName 'Server01','Server02' -AsJob -PassThru .EXAMPLE PS> Get-SdnOvsdbFirewallRuleTable -ComputerName 'Server01','Server02' -AsJob -PassThru -Timeout 600 #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string[]]$ComputerName, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$AsJob, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$PassThru, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [int]$Timeout = 300 ) try { if ($PSBoundParameters.ContainsKey('ComputerName')) { Invoke-PSRemoteCommand -ComputerName $ComputerName -ScriptBlock { Get-SdnOvsdbFirewallRuleTable } -Credential $Credential ` -AsJob:($AsJob.IsPresent) -PassThru:($PassThru.IsPresent) -ExecutionTimeout $Timeout } else { Get-OvsdbFirewallRuleTable } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnOvsdbGlobalTable { <# .SYNOPSIS Gets the global table results from OVSDB. .PARAMETER ComputerName Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more remote computers. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .PARAMETER AsJob Switch indicating to trigger a background job to perform the operation. .PARAMETER PassThru Switch indicating to wait for background job completes and display results to current session. .PARAMETER Timeout Specify the timeout duration to wait before job is automatically terminated. If omitted, defaults to 300 seconds. .EXAMPLE PS> Get-SdnOvsdbGlobalTable -ComputerName 'Server01','Server02' .EXAMPLE PS> Get-SdnOvsdbGlobalTable -ComputerName 'Server01','Server02' -Credential (Get-Credential) .EXAMPLE PS> Get-SdnOvsdbGlobalTable -ComputerName 'Server01','Server02' -AsJob .EXAMPLE PS> Get-SdnOvsdbGlobalTable -ComputerName 'Server01','Server02' -AsJob -PassThru .EXAMPLE PS> Get-SdnOvsdbGlobalTable -ComputerName 'Server01','Server02' -AsJob -PassThru -Timeout 600 #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string[]]$ComputerName, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$AsJob, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$PassThru, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [int]$Timeout = 300 ) try { if ($PSBoundParameters.ContainsKey('ComputerName')) { Invoke-PSRemoteCommand -ComputerName $ComputerName -ScriptBlock { Get-SdnOvsdbGlobalTable } -Credential $Credential ` -AsJob:($AsJob.IsPresent) -PassThru:($PassThru.IsPresent) -ExecutionTimeout $Timeout } else { Get-OvsdbGlobalTable } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnOvsdbPhysicalPortTable { <# .SYNOPSIS Gets the physical port table results from OVSDB. .PARAMETER ComputerName Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more remote computers. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .PARAMETER AsJob Switch indicating to trigger a background job to perform the operation. .PARAMETER PassThru Switch indicating to wait for background job completes and display results to current session. .PARAMETER Timeout Specify the timeout duration to wait before job is automatically terminated. If omitted, defaults to 300 seconds. .EXAMPLE PS> Get-SdnOvsdbPhysicalPortTable -ComputerName 'Server01','Server02' .EXAMPLE PS> Get-SdnOvsdbPhysicalPortTable -ComputerName 'Server01','Server02' -Credential (Get-Credential) .EXAMPLE PS> Get-SdnOvsdbPhysicalPortTable -ComputerName 'Server01','Server02' -AsJob .EXAMPLE PS> Get-SdnOvsdbPhysicalPortTable -ComputerName 'Server01','Server02' -AsJob -PassThru .EXAMPLE PS> Get-SdnOvsdbPhysicalPortTable -ComputerName 'Server01','Server02' -AsJob -PassThru -Timeout 600 #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string[]]$ComputerName, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$AsJob, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$PassThru, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [int]$Timeout = 300 ) try { if ($PSBoundParameters.ContainsKey('ComputerName')) { Invoke-PSRemoteCommand -ComputerName $ComputerName -ScriptBlock { Get-SdnOvsdbPhysicalPortTable } -Credential $Credential ` -AsJob:($AsJob.IsPresent) -PassThru:($PassThru.IsPresent) -ExecutionTimeout $Timeout } else { Get-OvsdbPhysicalPortTable } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnOvsdbRouterTable { <# .SYNOPSIS Gets the logical router table results from OVSDB. .PARAMETER ComputerName Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more remote computers. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .PARAMETER AsJob Switch indicating to trigger a background job to perform the operation. .PARAMETER PassThru Switch indicating to wait for background job completes and display results to current session. .PARAMETER Timeout Specify the timeout duration to wait before job is automatically terminated. If omitted, defaults to 300 seconds. .EXAMPLE PS> Get-SdnOvsdbRouterTable -ComputerName 'Server01','Server02' .EXAMPLE PS> Get-SdnOvsdbRouterTable -ComputerName 'Server01','Server02' -Credential (Get-Credential) .EXAMPLE PS> Get-SdnOvsdbRouterTable -ComputerName 'Server01','Server02' -AsJob .EXAMPLE PS> Get-SdnOvsdbRouterTable -ComputerName 'Server01','Server02' -AsJob -PassThru .EXAMPLE PS> Get-SdnOvsdbRouterTable -ComputerName 'Server01','Server02' -AsJob -PassThru -Timeout 600 #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string[]]$ComputerName, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$AsJob, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$PassThru, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [int]$Timeout = 300 ) try { if ($PSBoundParameters.ContainsKey('ComputerName')) { Invoke-PSRemoteCommand -ComputerName $ComputerName -ScriptBlock { Get-SdnOvsdbRouterTable } -Credential $Credential ` -AsJob:($AsJob.IsPresent) -PassThru:($PassThru.IsPresent) -ExecutionTimeout $Timeout } else { Get-OvsdbRouterTable } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnOvsdbUcastMacRemoteTable { <# .SYNOPSIS Gets the ucast mac remote table results from OVSDB. .PARAMETER ComputerName Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more remote computers. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .PARAMETER AsJob Switch indicating to trigger a background job to perform the operation. .PARAMETER PassThru Switch indicating to wait for background job completes and display results to current session. .PARAMETER Timeout Specify the timeout duration to wait before job is automatically terminated. If omitted, defaults to 300 seconds. .EXAMPLE PS> Get-SdnOvsdbUcastMacRemoteTable -ComputerName 'Server01','Server02' .EXAMPLE PS> Get-SdnOvsdbUcastMacRemoteTable -ComputerName 'Server01','Server02' -Credential (Get-Credential) .EXAMPLE PS> Get-SdnOvsdbUcastMacRemoteTable -ComputerName 'Server01','Server02' -AsJob .EXAMPLE PS> Get-SdnOvsdbUcastMacRemoteTable -ComputerName 'Server01','Server02' -AsJob -PassThru .EXAMPLE PS> Get-SdnOvsdbUcastMacRemoteTable -ComputerName 'Server01','Server02' -AsJob -PassThru -Timeout 600 #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string[]]$ComputerName, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$AsJob, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$PassThru, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [int]$Timeout = 300 ) try { if ($PSBoundParameters.ContainsKey('ComputerName')) { Invoke-PSRemoteCommand -ComputerName $ComputerName -ScriptBlock { Get-SdnOvsdbUcastMacRemoteTable } -Credential $Credential ` -AsJob:($AsJob.IsPresent) -PassThru:($PassThru.IsPresent) -ExecutionTimeout $Timeout } else { Get-OvsdbUcastMacRemoteTable } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnProviderAddress { <# .SYNOPSIS Retrieves the Provider Address that is assigned to the computer. .PARAMETER ComputerName Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more remote computers. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .PARAMETER AsJob Switch indicating to trigger a background job to perform the operation. .PARAMETER PassThru Switch indicating to wait for background job completes and display results to current session. .PARAMETER Timeout Specify the timeout duration to wait before job is automatically terminated. If omitted, defaults to 300 seconds. .EXAMPLE PS> Get-SdnProviderAddress -ComputerName 'Server01','Server02' .EXAMPLE PS> Get-SdnProviderAddress -ComputerName 'Server01','Server02' -Credential (Get-Credential) .EXAMPLE PS> Get-SdnProviderAddress -ComputerName 'Server01','Server02' -AsJob .EXAMPLE PS> Get-SdnProviderAddress -ComputerName 'Server01','Server02' -AsJob -PassThru .EXAMPLE PS> Get-SdnProviderAddress -ComputerName 'Server01','Server02' -AsJob -PassThru -Timeout 600 #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [string[]]$ComputerName, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$AsJob, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$PassThru, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [int]$Timeout = 300 ) try { if ($PSBoundParameters.ContainsKey('ComputerName')) { Invoke-PSRemoteCommand -ComputerName $ComputerName -ScriptBlock { Get-SdnProviderAddress } -Credential $Credential ` -AsJob:($AsJob.IsPresent) -PassThru:($PassThru.IsPresent) -ExecutionTimeout $Timeout } else { Get-ProviderAddress } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnServerConfigurationState { <# .SYNOPSIS Outputs a set of configuration state files for the server role. .PARAMETER OutputDirectory Specifies a specific path and folder in which to save the files. .EXAMPLE PS> Get-SdnServerConfigurationState -OutputDirectory "C:\Temp\CSS_SDN" #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [System.IO.FileInfo]$OutputDirectory ) $ProgressPreference = 'SilentlyContinue' try { $config = Get-SdnModuleConfiguration -Role:Server [System.IO.FileInfo]$OutputDirectory = Join-Path -Path $OutputDirectory.FullName -ChildPath "ConfigState" [System.IO.FileInfo]$regDir = Join-Path -Path $OutputDirectory.FullName -ChildPath "Registry" "Collect configuration state details for role {0}" -f $config.Name | Trace-Output if (-NOT (Initialize-DataCollection -Role:Server -FilePath $OutputDirectory.FullName -MinimumMB 100)) { "Unable to initialize environment for data collection" | Trace-Output -Level:Exception return } Export-RegistryKeyConfigDetails -Path $config.properties.regKeyPaths -OutputDirectory $regDir.FullName Get-GeneralConfigurationState -OutputDirectory $OutputDirectory.FullName # Gather VFP port configuration details "Gathering VFP port details" | Trace-Output -Level:Verbose foreach ($vm in (Get-WmiObject -na root\virtualization\v2 msvm_computersystem)) { foreach ($vma in $vm.GetRelated("Msvm_SyntheticEthernetPort")) { foreach ($port in $vma.GetRelated("Msvm_SyntheticEthernetPortSettingData").GetRelated("Msvm_EthernetPortAllocationSettingData").GetRelated("Msvm_EthernetSwitchPort")) { $outputDir = New-Item -Path (Join-Path -Path $OutputDirectory.FullName -ChildPath "VFP\$($vm.ElementName)") -ItemType Directory -Force vfpctrl /list-nat-range /port $($port.Name) | Export-ObjectToFile -FilePath $outputDir.FullName -Prefix 'NatInfo' -Name $port.Name -FileType txt vfpctrl /list-rule /port $($port.Name) | Export-ObjectToFile -FilePath $outputDir.FullName -Prefix 'RuleInfo' -Name $port.Name -FileType txt vfpctrl /list-mapping /port $($port.Name) | Export-ObjectToFile -FilePath $outputDir.FullName -Prefix 'ListMapping' -Name $port.Name -FileType txt vfpctrl /get-port-flow-settings /port $($port.Name) | Export-ObjectToFile -FilePath $outputDir.FullName -Prefix 'PortFlowSettings' -Name $port.Name -FileType txt vfpctrl /get-port-flow-stats /port $($port.Name) | Export-ObjectToFile -FilePath $outputDir.FullName -Prefix 'PortFlowStats' -Name $port.Name -FileType txt vfpctrl /get-flow-stats /port $($port.Name) | Export-ObjectToFile -FilePath $outputDir.FullName -Prefix 'FlowStats' -Name $port.Name -FileType txt vfpctrl /get-port-state /port $($port.Name) | Export-ObjectToFile -FilePath $outputDir.FullName -Prefix 'PortState' -Name $port.Name -FileType txt Get-SdnVfpPortState -PortId $($port.Name) | Export-ObjectToFile -FilePath $outputDir.FullName -Prefix 'PortState' -Name $port.Name -FileType json } } } vfpctrl /list-vmswitch-port | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'vfpctrl_list-vmswitch-port' -FileType txt Get-SdnVfpVmSwitchPort | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-SdnVfpVmSwitchPort' -FileType csv Get-SdnVfpVmSwitchPort | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-SdnVfpVmSwitchPort' -FileType json # Gather OVSDB databases "Gathering ovsdb database output" | Trace-Output -Level:Verbose ovsdb-client.exe dump tcp:127.0.0.1:6641 ms_vtep | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'ovsdb_vtep' -FileType txt ovsdb-client.exe dump tcp:127.0.0.1:6641 ms_firewall | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'ovsdb_firewall' -FileType txt ovsdb-client.exe dump tcp:127.0.0.1:6641 ms_service_insertion | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'ovsdb_serviceinsertion' -FileType txt Get-SdnOvsdbAddressMapping | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-SdnOvsdbAddressMapping' -FileType csv Get-SdnOvsdbFirewallRuleTable | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-SdnOvsdbFirewallRuleTable' -FileType csv Get-SdnOvsdbGlobalTable | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-SdnOvsdbGlobalTable' -FileType csv Get-SdnOvsdbPhysicalPortTable | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-SdnOvsdbPhysicalPortTable' -FileType csv Get-SdnOvsdbUcastMacRemoteTable | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-SdnOvsdbUcastMacRemoteTable' -FileType csv # Gather Hyper-V network details "Gathering hyper-v configuration details" | Trace-Output -Level:Verbose Get-PACAMapping | Sort-Object PSComputerName | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-PACAMapping' -FileType txt -Format Table Get-ProviderAddress | Sort-Object PSComputerName | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-ProviderAddress' -FileType txt -Format Table Get-CustomerRoute | Sort-Object PSComputerName | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-CustomerRoute' -FileType txt -Format Table Get-NetAdapterVPort | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-NetAdapterVPort' -FileType txt -Format Table Get-NetAdapterVmqQueue | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-NetAdapterVmqQueue' -FileType txt -Format Table Get-SdnNetAdapterEncapOverheadConfig | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-SdnNetAdapterEncapOverheadConfig' -FileType txt -Format Table Get-VMSwitch | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-VMSwitch' -FileType txt -Format List Get-VMSwitchTeam | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-VMSwitchTeam' -FileType txt -Format List Get-SdnVMNetworkAdapterPortProfile -AllVMs | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-SdnVMNetworkAdapterPortProfile' -FileType txt -Format Table Get-VMNetworkAdapterIsolation | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-VMNetworkAdapterIsolation' -FileType txt -Format Table Get-VMNetworkAdapterRoutingDomainMapping | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-VMNetworkAdapterRoutingDomainMapping' -FileType txt -Format Table Get-VMSystemSwitchExtensionPortFeature -FeatureId "9940cd46-8b06-43bb-b9d5-93d50381fd56" | Export-ObjectToFile -FilePath $OutputDirectory.FullName -Name 'Get-VMSystemSwitchExtensionPortFeature' -FileType json } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } $ProgressPreference = 'Continue' } function Get-SdnVfpPortGroup { <# .SYNOPSIS Enumerates the groups contained within the specific Virtual Filtering Platform (VFP) layer specified for the port. .PARAMETER PortId The Port ID GUID for the network interface. .PARAMETER Layer Specify the target layer. .PARAMETER Direction Specify the direction .PARAMETER Type Specifies an array of IP address families. The cmdlet gets the configuration that matches the address families .PARAMETER Name Returns the specific group name. If omitted, will return all groups within the VFP layer. .EXAMPLE PS> Get-SdnVfpPortGroup -PortId '2152523D-333F-4082-ADE4-107D8CA75F5B' -Layer 'SLB_NAT_LAYER' .EXAMPLE PS> Get-SdnVfpPortGroup -PortId '2152523D-333F-4082-ADE4-107D8CA75F5B' -Layer 'SLB_NAT_LAYER' -Name 'SLB_GROUP_NAT_IPv4_IN' #> [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [Parameter(Mandatory = $true, ParameterSetName = 'Name')] [Parameter(Mandatory = $true, ParameterSetName = 'Default')] [GUID]$PortId, [Parameter(Mandatory = $true, ParameterSetName = 'Name')] [Parameter(Mandatory = $true, ParameterSetName = 'Default')] [System.String]$Layer, [Parameter(Mandatory = $false, ParameterSetName = 'Default')] [ValidateSet('IN','OUT')] [System.String]$Direction, [Parameter(Mandatory = $false, ParameterSetName = 'Default')] [ValidateSet('IPv4','IPv6')] [System.String]$Type, [Parameter(Mandatory = $false, ParameterSetName = 'Name')] [System.String]$Name ) try { $arrayList = [System.Collections.ArrayList]::new() $vfpGroups = vfpctrl /list-group /port $PortId /layer $Layer if ($null -eq $vfpGroups){ return $null } # due to how vfp handles not throwing a terminating error if port ID does not exist, # need to manually examine the response to see if it contains a failure if ($vfpGroups[0] -ilike "ERROR*") { "{0}" -f $vfpGroups[0] | Trace-Output -Level:Exception return $null } foreach ($line in $vfpGroups) { $line = $line.Trim() if ($line -like 'ITEM LIST' -or $line -ilike '==========='){ continue } if ([string]::IsNullOrEmpty($line)) { continue } # in situations where the value might be nested in another line we need to do some additional data processing # subkey is declared below if the value is null after the split if ($subKey) { if($null -eq $subObject){ $subObject = New-Object -TypeName PSObject } if ($null -eq $subArrayList) { $subArrayList = [System.Collections.ArrayList]::new() } switch ($subKey) { 'Conditions' { # this will have a pattern of multiple lines nested under Conditions: in which we see a pattern of property:value format # we also see common pattern that Match type is the next property after Conditions, so we can use that to determine when # no further processing is needed for this sub value if ($line.Contains('Match type')) { $object | Add-Member -NotePropertyMembers @{Conditions = $subObject} $subObject = $null $subKey = $null } # if <none> is defined for conditions, we can also assume there is nothing to define and will just add elseif ($line.Contains('<none>')) { $object | Add-Member -MemberType NoteProperty -Name $subKey -Value 'None' $subObject = $null $subKey = $null } elseif ($line.Contains(':')) { [System.String[]]$subResults = $line.Split(':').Trim() $subObject | Add-Member -MemberType NoteProperty -Name $subResults[0] -Value $subResults[1] } } } } # lines in the VFP output that contain : contain properties and values # need to split these based on count of ":" to build key and values if ($line.Contains(':')) { [System.String[]]$results = $line.Split(':').Trim() if ($results.Count -eq 2) { [System.String]$key = $results[0].Trim() [System.String]$value = $results[1].Trim() # all groups begin with this property and value so need to create a new psobject when we see these keys if ($key -ieq 'Group') { if ($object) { [void]$arrayList.Add($object) } $object = New-Object -TypeName PSObject $object | Add-Member -MemberType NoteProperty -Name 'Group' -Value $value continue } if ($key -ieq 'Conditions') { $subKey = $key continue } # if the key is priority, we want to declare the value as an int value so we can properly sort the results if ($key -ieq 'Priority') { [int]$value = $results[1] } # add the line values to the object $object | Add-Member -MemberType NoteProperty -Name $key -Value $value } } elseif ($line.Contains('Command list-group succeeded!')) { if ($object) { [void]$arrayList.Add($object) } } } if ($Name) { return ($arrayList | Where-Object { $_.Group -ieq $Name }) } if ($Direction) { $arrayList = $arrayList | Where-Object {$_.Direction -ieq $Direction} } if ($Type) { $arrayList = $arrayList | Where-Object {$_.Type -ieq $Type} } return ($arrayList | Sort-Object -Property Priority) } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnVfpPortLayer { <# .SYNOPSIS Enumerates the layers contained within Virtual Filtering Platform (VFP) for specified for the port. .PARAMETER PortId The Port ID GUID for the network interface. .PARAMETER Name Returns the specific layer name. If omitted, will return all layers within VFP. .EXAMPLE PS> Get-SdnVfpPortLayer .EXAMPLE PS> Get-SdnVfpPortLayer -PortId '2152523D-333F-4082-ADE4-107D8CA75F5B' #> param ( [Parameter(Mandatory = $true)] [GUID]$PortId, [Parameter(Mandatory = $false)] [System.String]$Name ) try { $arrayList = [System.Collections.ArrayList]::new() $vfpLayers = vfpctrl /list-layer /port $PortId if ($null -eq $vfpLayers){ return $null } # due to how vfp handles not throwing a terminating error if port ID does not exist, # need to manually examine the response to see if it contains a failure if ($vfpLayers[0] -ilike "ERROR*") { "{0}" -f $vfpLayers[0] | Trace-Output -Level:Exception return $null } foreach ($line in $vfpLayers) { $line = $line.Trim() if ($line -like 'ITEM LIST' -or $line -ilike '==========='){ continue } if ([string]::IsNullOrEmpty($line)) { continue } # lines in the VFP output that contain : contain properties and values # need to split these based on count of ":" to build key and values if ($line.Contains(':')) { [System.String[]]$results = $line.Split(':').Trim() if ($results.Count -eq 2) { [System.String]$key = $results[0].Trim() [System.String]$value = $results[1].Trim() # all layers begin with this property and value so need to create a new psobject when we see these keys if ($key -ieq 'Layer') { if ($object) { [void]$arrayList.Add($object) } $object = New-Object -TypeName PSObject $object | Add-Member -MemberType NoteProperty -Name 'Layer' -Value $value continue } # if the key is priority, we want to declare the value as an int value so we can properly sort the results if ($key -ieq 'Priority') { [int]$value = $value } else { [System.String]$value = $value } } # add the line values to the object $object | Add-Member -MemberType NoteProperty -Name $key -Value $value } elseif ($line.Contains('Command list-layer succeeded!')) { if ($object) { [void]$arrayList.Add($object) } } } if ($Name) { return ($arrayList | Where-Object { $_.Layer -eq $Name }) } else { return ($arrayList | Sort-Object -Property Priority) } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnVfpPortRule { <# .SYNOPSIS Enumerates the rules contained within the specific group within Virtual Filtering Platform (VFP) layer specified for the port. .PARAMETER PortId The Port ID GUID for the network interface. .PARAMETER Layer Specify the target layer. .PARAMETER Group Specify the group layer. .PARAMETER Name Returns the specific rule name. If omitted, will return all rules within the VFP group. .EXAMPLE PS> Get-SdnVfpPortRule -PortId '2152523D-333F-4082-ADE4-107D8CA75F5B' -Layer 'SLB_NAT_LAYER' -Group 'SLB_GROUP_NAT_IPv4_IN' .EXAMPLE PS> Get-SdnVfpPortRule -PortId '2152523D-333F-4082-ADE4-107D8CA75F5B' -Layer 'SLB_NAT_LAYER' -Group 'SLB_GROUP_NAT_IPv4_IN' -Name 'SLB_DEFAULT_RULE' #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [GUID]$PortId, [Parameter(Mandatory = $true)] [System.String]$Layer, [Parameter(Mandatory = $true)] [System.String]$Group, [Parameter(Mandatory = $false)] [System.String]$Name ) try { $arrayList = [System.Collections.ArrayList]::new() $vfpRules = vfpctrl /list-rule /port $PortId /layer $Layer /group $Group if ($null -eq $vfpRules){ return $null } # due to how vfp handles not throwing a terminating error if port ID does not exist, # need to manually examine the response to see if it contains a failure if ($vfpRules[0] -ilike "ERROR*") { "{0}" -f $vfpRules[0] | Trace-Output -Level:Exception return $null } foreach ($line in $vfpRules) { $line = $line.Trim() if ($line -like 'ITEM LIST' -or $line -ilike '==========='){ continue } if ([string]::IsNullOrEmpty($line)) { continue } # in situations where the value might be nested in another line we need to do some additional data processing # subkey is declared below if the value is null after the split if ($subKey) { if($null -eq $subObject){ $subObject = New-Object -TypeName PSObject } if ($null -eq $subArrayList) { $subArrayList = [System.Collections.ArrayList]::new() } switch ($subKey) { 'Conditions' { # this will have a pattern of multiple lines nested under Conditions: in which we see a pattern of property:value format # we also see common pattern that Flow TTL is the next property after Conditions, so we can use that to determine when # no further processing is needed for this sub value if ($line.Contains('Flow TTL')) { $object | Add-Member -NotePropertyMembers @{Conditions = $subObject} $subObject = $null $subKey = $null } # if <none> is defined for conditions, we can also assume there is nothing to define and will just add elseif ($line.Contains('<none>')) { $object | Add-Member -MemberType NoteProperty -Name $subKey -Value 'None' $subObject = $null $subKey = $null } else { [System.String[]]$subResults = $line.Split(':').Trim() $subObject | Add-Member -MemberType NoteProperty -Name $subResults[0] -Value $subResults[1] } } 'Encap Destination(s)' { # we typically see a format pattern of {property=value,property=value} for encap destination # and should be contained all within a single line. we also see a matching pattern that FlagsEx is the next property result # so we can use that to determine when no further processing is needed for this sub value if ($line.Contains('FlagsEx')) { $object | Add-Member -MemberType NoteProperty -Name 'Encap Destination' -Value $subObject $subObject = $null $subKey = $null } else { [System.String[]]$subResults = $line.Replace('{','').Replace('}','').Split(',').Trim() foreach ($subResult in $subResults) { [System.String]$subKeyName = $subResult.Split('=')[0].Trim() [System.String]$subKeyValue = $subResult.Split('=')[1].Trim() $subObject | Add-Member -MemberType NoteProperty -Name $subKeyName -Value $subKeyValue } } } } # since we are processing sub values, we want to move to the next line and not do any further processing continue } # lines in the VFP output that contain : contain properties and values # need to split these based on count of ":" to build key and values if ($line.Contains(':')) { [System.String[]]$results = $line.Split(':') if ($results.Count -eq 2) { [System.String]$key = $results[0].Trim() [System.String]$value = $results[1].Trim() # all groups begin with this property and value so need to create a new psobject when we see these keys if ($key -ieq 'RULE') { if ($object) { [void]$arrayList.Add($object) } $object = New-Object -TypeName PSObject $object | Add-Member -MemberType NoteProperty -Name 'Rule' -Value $value continue } # because some rules defined within groups do not have a rule name defined such as NAT layers, # grab the friendly name and update the ps object if ($key -ieq 'Friendly name') { if([String]::IsNullOrEmpty($object.Rule)) { $object.Rule = $value } } if ($key -ieq 'Conditions' -or $key -ieq 'Encap Destination(s)') { $subKey = $key continue } # if the key is priority, we want to declare the value as an int value so we can properly sort the results if ($key -ieq 'Priority') { [int]$value = $value } # add the line values to the object $object | Add-Member -MemberType NoteProperty -Name $key -Value $value } } elseif ($line.Contains('Command list-rule succeeded!')) { if ($object) { [void]$arrayList.Add($object) } } } if ($Name) { return ($arrayList | Where-Object {$_.Rule -ieq $Name -or $_.'Friendly name' -ieq $Name}) } return ($arrayList | Sort-Object -Property Priority) } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnVfpPortState { <# .SYNOPSIS Returns the current VFP port state for a particular port Id. .DESCRIPTION Executes 'vfpctrl.exe /get-port-state /port $PortId' to return back the current state of the port specified. .PARAMETER PortId The Port ID GUID for the network interface. .EXAMPLE PS> Get-SdnVfpPortState -PortId 3DC59D2B-9BFE-4996-AEB6-2589BD20B559 #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [GUID]$PortId ) try { $object = New-Object -TypeName PSObject $vfpPortState = vfpctrl.exe /get-port-state /port $PortId if ($null -eq $vfpPortState) { $msg = "Unable to locate port ID {0} from vfpctrl`n{1}" -f $PortId, $_ throw New-Object System.NullReferenceException($msg) } foreach ($line in $vfpPortState) { $trimmedLine = $line.Replace(':','').Trim() # look for true/false and then seperate out the key/value pairs # we will convert the true/false values to boolean when adding to the object if ($trimmedLine -match '(.*)\s+(True|False)') { $object | Add-Member -MemberType NoteProperty -Name $Matches.1 -Value ([System.Convert]::ToBoolean($Matches.2)) continue } # look for enabled/disabled and then seperate out the key/value pairs if ($trimmedLine -match '(.*)\s+(Enabled|Disabled)') { $object | Add-Member -MemberType NoteProperty -Name $Matches.1 -Value $Matches.2 continue } } return $object } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnVfpVmSwitchPort { <# .SYNOPSIS Returns a list of ports from within virtual filtering platform. .PARAMETER PortName The port name of the VFP interface .PARAMETER VMName The Name of the Virtual Machine .PARAMETER VMID The ID of the Virtual Machine .PARAMETER ComputerName Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more remote computers. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .EXAMPLE PS> Get-SdnVfpVmSwitchPort -ComputerName 'Server01','Server02' .EXAMPLE PS> Get-SdnVfpVmSwitchPort -ComputerName 'Server01','Server02' -Credential (Get-Credential) .EXAMPLE PS> Get-SdnVfpVmSwitchPort -VMName 'SDN-MUX01' .EXAMPLE PS> Get-SdnVfpVmSwitchPort -VMID 699FBDA2-15A0-4D73-A6EF-9D55623A27CE #> [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [Parameter(Mandatory = $false, Position = 1, ParameterSetName = 'Port')] [System.String]$PortName, [Parameter(Mandatory = $false, Position = 2, ParameterSetName = 'VMID')] [System.String]$VMID, [Parameter(Mandatory = $false, Position = 3, ParameterSetName = 'VMName')] [System.String]$VMName, [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'Port')] [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'VMID')] [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'VMName')] [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'Default')] [string[]]$ComputerName, [Parameter(Mandatory = $false, Position = 5, ParameterSetName = 'Port')] [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'VMID')] [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'VMName')] [Parameter(Mandatory = $false, Position = 4, ParameterSetName = 'Default')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) try { if ($PSBoundParameters.ContainsKey('ComputerName')) { $results = Invoke-PSRemoteCommand -ComputerName $ComputerName -Credential $Credential -ScriptBlock { Get-SdnVfpVmSwitchPort } } else { $results = Get-VfpVMSwitchPort } switch ($PSCmdlet.ParameterSetName) { 'Port' { return ($results | Where-Object {$_.PortName -ieq $PortName}) } 'VMID' { return ($results | Where-Object {$_.VMID -ieq $VMID}) } 'VMName' { return ($results | Where-Object {$_.VMName -ieq $VMName}) } default { return $results } } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnVMNetworkAdapter { <# .SYNOPSIS Retrieves the virtual machine network adapters that are allocated on a hyper-v host .PARAMETER ComputerName Type the NetBIOS name, an IP address, or a fully qualified domain name of one or more remote computers. To specify the local computer, type the computer name, localhost, or a dot (.). When the computer is in a different domain than the user, the fully qualified domain name is required .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .PARAMETER AsJob Switch indicating to trigger a background job to perform the operation. .PARAMETER PassThru Switch indicating to wait for background job completes and display results to current session. .PARAMETER Timeout Specify the timeout duration to wait before job is automatically terminated. If omitted, defaults to 600 seconds. .EXAMPLE PS> Get-SdnVMNetworkAdapter -ComputerName 'Server01','Server02' .EXAMPLE PS> Get-SdnVMNetworkAdapter -ComputerName 'Server01','Server02' -Credential (Get-Credential) .EXAMPLE PS> Get-SdnVMNetworkAdapter -ComputerName 'Server01','Server02' -AsJob .EXAMPLE PS> Get-SdnVMNetworkAdapter -ComputerName 'Server01','Server02' -AsJob -PassThru .EXAMPLE PS> Get-SdnVMNetworkAdapter -ComputerName 'Server01','Server02' -AsJob -PassThru -Timeout 600 #> param ( [Parameter(Mandatory = $true)] [System.String[]]$ComputerName, [Parameter(Mandatory = $false)] [VMState]$VmState = 'Running', [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$AsJob, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [Switch]$PassThru, [Parameter(Mandatory = $false, ParameterSetName = 'AsJob')] [int]$Timeout = 600 ) try { $scriptBlock = { param([Parameter(Position = 0)][String]$VmState) $virtualMachines = Get-VM | Where-Object { $_.State -eq $VmState } $virtualMachines | Get-VMNetworkAdapter } Invoke-PSRemoteCommand -ComputerName $ComputerName -Credential $Credential -ScriptBlock $scriptBlock -ArgumentList $VmState ` -AsJob:($AsJob.IsPresent) -PassThru:($PassThru.IsPresent) -ExecutionTimeout $Timeout } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Get-SdnVMNetworkAdapterPortProfile { <# .SYNOPSIS Retrieves the port profile applied to the virtual machine network interfaces. .PARAMETER VMName Specifies the name of the virtual machine to be retrieved. .PARAMETER AllVMs Switch to indicate to get all the virtual machines network interfaces on the hypervisor host. .PARAMETER HostVmNic When true, displays Port Profiles of Host VNics. Otherwise displays Port Profiles of Vm VNics. .EXAMPLE Get-SdnVMNetworkAdapterPortProfile -VMName 'VM01' .EXAMPLE Get-SdnVMNetworkAdapterPortProfile -AllVMs #> [CmdletBinding(DefaultParameterSetName = 'SingleVM')] param ( [Parameter(Mandatory = $true, ParameterSetName = 'SingleVM')] [System.String]$VMName, [Parameter(Mandatory = $true, ParameterSetName = 'AllVMs')] [Switch]$AllVMs, [Parameter(ParameterSetName = 'SingleVM', Mandatory = $false)] [Parameter(ParameterSetName = 'AllVMs', Mandatory = $false)] [switch]$HostVmNic ) [System.Guid]$portProfileFeatureId = "9940cd46-8b06-43bb-b9d5-93d50381fd56" try { if ($null -eq (Get-Module -Name Hyper-V)) { Import-Module -Name Hyper-V -Force } $arrayList = [System.Collections.ArrayList]::new() if ($AllVMs) { $netAdapters = Get-VMNetworkAdapter -All | Where-Object { $_.IsManagementOs -eq $HostVmNic } } else { $netAdapters = Get-VMNetworkAdapter -VMName $VMName | Where-Object { $_.IsManagementOs -eq $HostVmNic } } foreach ($adapter in $netAdapters | Where-Object { $_.IsManagementOs -eq $false }) { "Enumerating port features and data for adapter {0}" -f $adapter.MacAddress | Trace-Output -Level:Verbose $currentProfile = Get-VMSwitchExtensionPortFeature -FeatureId $portProfileFeatureId -VMNetworkAdapter $adapter if ($null -eq $currentProfile) { "{0} attached to {1} does not have a port profile" -f $adapter.MacAddress, $adapter.VMName | Trace-Output -Level:Warning continue } $object = [PSCustomObject]@{ VMName = $adapter.VMName Name = $adapter.Name MacAddress = $adapter.MacAddress ProfileId = $currentProfile.SettingData.ProfileId ProfileData = $currentProfile.SettingData.ProfileData } $portData = (Get-VMSwitchExtensionPortData -VMNetworkAdapter $adapter) # we will typically see multiple port data values for each adapter, however the deviceid should be the same across all of the objects # defensive coding in place for situation where vm is not in proper state and this portdata is null if ($portData) { $object | Add-Member -MemberType NoteProperty -Name 'PortId' -Value $portData[0].data.deviceid } else { $object | Add-Member -MemberType NoteProperty -Name 'PortId' -Value $null } [void]$arrayList.Add($object) } return ($arrayList | Sort-Object -Property Name) } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function New-SdnServerCertificate { <# .SYNOPSIS Generate new self-signed certificate to be used by the Hyper-V host and distributes to the Network Controller(s) within the environment. .PARAMETER NotAfter Specifies the date and time, as a DateTime object, that the certificate expires. To obtain a DateTime object, use the Get-Date cmdlet. The default value for this parameter is one year after the certificate was created. .PARAMETER Path Specifies the file path location where a .cer file is exported automatically. .PARAMETER FabricDetails The SDN Fabric details derived from Get-SdnInfrastructureInfo. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user .EXAMPLE New-SdnServerCertificate -NotAfter (Get-Date).AddYears(1) -FabricDetails $Global:SdnDiagnostics.EnvironmentInfo #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [datetime]$NotAfter = (Get-Date).AddYears(3), [Parameter(Mandatory = $false)] [System.String]$Path = "$(Get-WorkingDirectory)\MuxCert_{0}" -f (Get-FormattedDateTimeUTC), [Parameter(Mandatory = $false)] [System.Object]$FabricDetails, [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) $config = Get-SdnModuleConfiguration -Role 'Server' $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature if (-NOT ($confirmFeatures)) { throw New-Object System.NotSupportedException("The current machine is not a Server, run this on Server.") } # ensure that the module is running as local administrator $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-NOT $elevated) { throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") } try { if (-NOT (Test-Path -Path $Path -PathType Container)) { "Creating directory {0}" -f $Path | Trace-Output $CertPath = New-Item -Path $Path -ItemType Directory -Force } else { $CertPath = Get-Item -Path $Path } $serverCert = Get-ItemPropertyValue -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NcHostAgent\Parameters' -Name 'HostAgentCertificateCName' $subjectName = "CN={0}" -f $serverCert $certificate = New-SdnCertificate -Subject $subjectName -NotAfter $NotAfter # after the certificate has been generated, we want to export the certificate and save the file to directory # This allows the rest of the function to pick up these files and perform the steps as normal [System.String]$cerFilePath = "$(Join-Path -Path $CertPath.FullName -ChildPath $subjectName.ToString().ToLower().Replace('.','_').Replace("=",'_').Trim()).cer" "Exporting certificate to {0}" -f $cerFilePath | Trace-Output $exportedCertificate = Export-Certificate -Cert $certificate -FilePath $cerFilePath -Type CERT Copy-CertificateToFabric -CertFile $exportedCertificate.FullName -FabricDetails $FabricDetails -ServerNodeCert -Credential $Credential $certObject = [PSCustomObject]@{ Certificate = $certificate FileInfo = $exportedCertificate } return $certObject } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Set-SdnVMNetworkAdapterPortProfile { <# .SYNOPSIS Configures the port profile applied to the virtual machine network interfaces. .PARAMETER VMName Specifies the name of the virtual machine. .PARAMETER MacAddress Specifies the MAC address of the VM network adapter. .PARAMETER ProfileId The InstanceID of the Network Interface taken from Network Controller. .PARAMETER ProfileData 1 = VfpEnabled, 2 = VfpDisabled (usually in the case of Mux). If ommited, defaults to 1. .PARAMETER HyperVHost Type the NetBIOS name, an IP address, or a fully qualified domain name of the computer that is hosting the virtual machine. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .EXAMPLE Set-SdnVMNetworkAdapterPortProfile -VMName 'TestVM01' -MacAddress 001DD826100E #> [CmdletBinding(DefaultParameterSetName = 'Local')] param ( [Parameter(Mandatory = $true, ParameterSetName = 'Local')] [Parameter(Mandatory = $true, ParameterSetName = 'Remote')] [System.String]$VMName, [Parameter(Mandatory = $true, ParameterSetName = 'Local')] [Parameter(Mandatory = $true, ParameterSetName = 'Remote')] [System.String]$MacAddress, [Parameter(Mandatory = $true, ParameterSetName = 'Local')] [Parameter(Mandatory = $true, ParameterSetName = 'Remote')] [System.Guid]$ProfileId, [Parameter(Mandatory = $false, ParameterSetName = 'Local')] [Parameter(Mandatory = $false, ParameterSetName = 'Remote')] [Int]$ProfileData = 1, [Parameter(Mandatory = $false, ParameterSetName = 'Remote')] [System.String]$HyperVHost, [Parameter(Mandatory = $false, ParameterSetName = 'Remote')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) function Set-VMNetworkAdapterPortProfile { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0)] [System.String]$VMName, [Parameter(Mandatory = $true, Position = 1)] [System.String]$MacAddress, [Parameter(Mandatory = $true, Position = 2)] [System.Guid]$ProfileId, [Parameter(Mandatory = $false, Position = 3)] [System.Int16]$ProfileData = 1 ) [System.Guid]$portProfileFeatureId = "9940cd46-8b06-43bb-b9d5-93d50381fd56" [System.Guid]$vendorId = "1FA41B39-B444-4E43-B35A-E1F7985FD548" if ($null -eq (Get-Module -Name Hyper-V)) { Import-Module -Name Hyper-V -Force } $vmNic = Get-VMNetworkAdapter -VMName $VmName | Where-Object {$_.MacAddress -ieq $MacAddress} if ($null -eq $vmNic) { "Unable to locate VMNetworkAdapter" | Trace-Output -Level:Exception return } $portProfileDefaultSetting = Get-VMSystemSwitchExtensionPortFeature -FeatureId $portProfileFeatureId -ErrorAction Stop $portProfileDefaultSetting.SettingData.ProfileId = $ProfileId.ToString("B") $portProfileDefaultSetting.SettingData.NetCfgInstanceId = "{56785678-a0e5-4a26-bc9b-c0cba27311a3}" $portProfileDefaultSetting.SettingData.CdnLabelString = "TestCdn" $portProfileDefaultSetting.SettingData.CdnLabelId = 1111 $portProfileDefaultSetting.SettingData.ProfileName = "Testprofile" $portProfileDefaultSetting.SettingData.VendorId = $vendorId.ToString("B") $portProfileDefaultSetting.SettingData.VendorName = "NetworkController" $portProfileDefaultSetting.SettingData.ProfileData = $ProfileData $currentProfile = Get-VMSwitchExtensionPortFeature -FeatureId $portProfileFeatureId -VMNetworkAdapter $vmNic if ($null -eq $currentProfile) { "Port profile not previously configured" | Trace-Output Add-VMSwitchExtensionPortFeature -VMSwitchExtensionFeature $portProfileDefaultSetting -VMNetworkAdapter $vmNic } else { "Current Settings: ProfileId [{0}] ProfileData [{1}]" -f $currentProfile.SettingData.ProfileId, $currentProfile.SettingData.ProfileData | Trace-Output $currentProfile.SettingData.ProfileId = $ProfileId.ToString("B") $currentProfile.SettingData.ProfileData = $ProfileData $currentProfile.SettingData.VendorId = $vendorId.ToString("B") Set-VMSwitchExtensionPortFeature -VMSwitchExtensionFeature $currentProfile -VMNetworkAdapter $vmNic } "Successfully created/added Port Profile for VM [{0})], Adapter [{1}], PortProfileId [{2}], ProfileData [{3}]" -f $vmNic.VMName, $vmNic.Name, $ProfileId.ToString(), $ProfileData | Trace-Output } $splat = @{ VMName = $VMName MacAddress = $MacAddress ProfileId = $ProfileId ProfileData = $ProfileData } try { switch ($PSCmdlet.ParameterSetName) { 'Remote' { Invoke-PSRemoteCommand -ComputerName $HyperVHost -Credential $Credential -ScriptBlock { param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][String]$param2, [Parameter(Position = 2)][Guid]$param3, [Parameter(Position = 3)][Int]$param4) Set-VMNetworkAdapterPortProfile -VMName $param1 -MacAddress $param2 -ProfileId $param3 -ProfileData $param4 } -ArgumentList @($splat.VMName, $splat.MacAddress, $splat.ProfileId, $splat.ProfileData) } 'Local' { Set-VMNetworkAdapterPortProfile @splat } } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Show-SdnVfpPortConfig { <# .SYNOPSIS Enumerates the VFP layers, groups and rules contained within Virtual Filtering Platform (VFP) for the specified port. .PARAMETER PortId The Port ID GUID for the network interface. .PARAMETER Direction Specify the direction .PARAMETER Type Specifies an array of IP address families. The cmdlet gets the configuration that matches the address families .EXAMPLE PS Show-SdnVfpPortConfig -PortId 8440FB77-196C-402E-8564-B0EF9E5B1931 .EXAMPLE PS> Show-SdnVfpPortConfig -PortId 8440FB77-196C-402E-8564-B0EF9E5B1931 -Direction IN .EXAMPLE PS> Show-SdnVfpPortConfig -PortId 8440FB77-196C-402E-8564-B0EF9E5B1931 -Direction IN -Type IPv4 #> [CmdletBinding(DefaultParameterSetName = 'Default')] param ( [Parameter(Mandatory = $true, ParameterSetName = 'Default')] [GUID]$PortId, [Parameter(Mandatory = $false, ParameterSetName = 'Default')] [ValidateSet('IPv4','IPv6')] [System.String]$Type, [Parameter(Mandatory = $false, ParameterSetName = 'Default')] [ValidateSet('IN','OUT')] [System.String]$Direction ) try { $vfpLayers = Get-SdnVfpPortLayer -PortId $PortId if ($null -eq $vfpLayers) { "Unable to locate PortId {0}" -f $PortId | Trace-Output -Level:Exception return $null } foreach ($layer in $vfpLayers) { "== Layer: {0} ==" -f $layer.LAYER | Write-Host -ForegroundColor:Magenta if ($Direction) { $vfpGroups = Get-SdnVfpPortGroup -PortId $PortId -Layer $layer.LAYER -Direction $Direction } else { $vfpGroups = Get-SdnVfpPortGroup -PortId $PortId -Layer $layer.LAYER } if ($Type) { $vfpGroups = $vfpGroups | Where-Object {$_.Type -ieq $Type} } foreach ($group in $vfpGroups) { "== Group: {0} ==" -f $group.GROUP | Write-Host -ForegroundColor:Yellow Get-SdnVfpPortRule -PortId $PortId -Layer $layer.LAYER -Group $group.GROUP | Format-Table -AutoSize } } } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Start-SdnServerCertificateRotation { <# .SYNOPSIS Performs a certificate rotation operation for the Servers. .PARAMETER Credential Specifies a user account that has permission to perform this action on the Server and Network Controller nodes. The default is the current user. .PARAMETER NcRestCredential Specifies a user account that has permission to access the northbound NC API interface. The default is the current user. .PARAMETER GenerateCertificate Switch to determine if certificate rotate function should generate self-signed certificates. .PARAMETER CertPath Path directory where certificate(s) .pfx files are located for use with certificate rotation. .PARAMETER CertPassword SecureString password for accessing the .pfx files, or if using -GenerateCertificate, what the .pfx files will be encrypted with. .PARAMETER NotAfter Expiration date when using -GenerateCertificate. If ommited, defaults to 3 years. .PARAMETER CertRotateConfig The Config generated by New-SdnCertificateRotationConfig to include appropriate certificate thumbprints for server nodes. .PARAMETER Force Switch to force the rotation without being prompted, when Service Fabric is unhealthy. #> [CmdletBinding(DefaultParameterSetName = 'GenerateCertificate')] param ( [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] [System.String]$NetworkController, [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $NcRestCredential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] [System.String]$CertPath, [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [Switch]$GenerateCertificate, [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [System.Security.SecureString]$CertPassword, [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] [datetime]$NotAfter = (Get-Date).AddYears(3), [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] [hashtable]$CertRotateConfig, [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] [switch]$Force ) # these are not yet supported and will take a bit more time to implement as it touches on core framework for rotate functionality # however majority of the environments impacted are using sdnexpress which leverage self-signed certificates. if ($CertRotateConfig -or $CertPath) { "This feature is not yet supported and is under development. Please use -GenerateCertificate or reference {0} for manual steps." ` -f 'https://learn.microsoft.com/en-us/azure-stack/hci/manage/update-network-controller-certificates?tabs=manual-renewal' | Trace-Output -Level:Warning return } # ensure that the module is running as local administrator $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-NOT $elevated) { throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") } # add disclaimer that this feature is currently under preview if (!$Force) { "This feature is currently under preview. Please report any issues to https://github.com/microsoft/SdnDiagnostics/issues so we can accurately track any issues and help unblock your cert rotation." | Trace-Output -Level:Warning $confirm = Confirm-UserInput -Message "Do you want to proceed with certificate rotation? [Y/N]:" if (-NOT $confirm) { "User has opted to abort the operation. Terminating operation" | Trace-Output -Level:Warning return } } $array = @() $headers = @{"Accept"="application/json"} $content = "application/json; charset=UTF-8" try { "Starting certificate rotation" | Trace-Output "Retrieving current SDN environment details" | Trace-Output if ([String]::IsNullOrEmpty($CertPath)) { [System.String]$CertPath = "$(Get-WorkingDirectory)\MuxCert_{0}" -f (Get-FormattedDateTimeUTC) if (-NOT (Test-Path -Path $CertPath -PathType Container)) { $null = New-Item -Path $CertPath -ItemType Directory -Force } } [System.IO.FileSystemInfo]$CertPath = Get-Item -Path $CertPath -ErrorAction Stop $sdnFabricDetails = Get-SdnInfrastructureInfo -NetworkController $NetworkController -Credential $Credential -NcRestCredential $NcRestCredential -ErrorAction Stop $servers = Get-SdnServer -NcUri $sdnFabricDetails.NcUrl -Credential $NcRestCredential -ErrorAction Stop # before we proceed with anything else, we want to make sure that all the Network Controllers and MUXes within the SDN fabric are running the current version Install-SdnDiagnostics -ComputerName $sdnFabricDetails.NetworkController -ErrorAction Stop Install-SdnDiagnostics -ComputerName $sdnFabricDetails.Server -ErrorAction Stop ##################################### # # Create Certificate (Optional) # ##################################### if ($PSCmdlet.ParameterSetName -ieq 'GenerateCertificate') { "== STAGE: CREATE SELF SIGNED CERTIFICATES ==" | Trace-Output # retrieve the corresponding managementAddress from each of the server resources # and invoke remote operation to the server to generate the self-signed certificate foreach ($server in $servers) { $serverConnection = $server.properties.connections | Where-Object {$_.credentialType -ieq "X509Certificate"} $managementAddress = $serverConnection.managementAddresses[0] $serverCert = Invoke-PSRemoteCommand -ComputerName $managementAddress -Credential $Credential -ScriptBlock { param( [Parameter(Position = 0)][DateTime]$param1, [Parameter(Position = 1)][PSCredential]$param2, [Parameter(Position = 2)][String]$param3, [Parameter(Position = 3)][System.Object]$param4 ) New-SdnServerCertificate -NotAfter $param1 -Credential $param2 -Path $param3 -FabricDetails $param4 } -ArgumentList @($NotAfter, $Credential, $CertPath.FullName, $sdnFabricDetails) $array += [PSCustomObject]@{ ManagementAddress = $managementAddress ResourceRef = $server.resourceRef Certificate = $serverCert.Certificate } } } # loop through all the objects to perform PUT operation against the server resource # to update the base64 encoding for the certificate that NC should use when communicating with the server resource foreach ($obj in $array) { "Updating certificate information for {0}" -f $obj.ResourceRef | Trace-Output $server = Get-SdnResource -NcUri $sdnFabricDetails.NcUrl -Credential $NcRestCredential -ResourceRef $obj.ResourceRef $encoding = [System.Convert]::ToBase64String($obj.Certificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)) $endpoint = Get-SdnApiEndpoint -NcUri $sdnFabricDetails.NcUrl -ResourceRef $server.resourceRef $server.properties.certificate = $encoding $jsonBody = $server | ConvertTo-Json -Depth 100 $null = Invoke-RestMethodWithRetry -Method 'Put' -UseBasicParsing -Uri $endpoint -Headers $headers -ContentType $content -Body $jsonBody -Credential $NcRestCredential if (-NOT (Confirm-ProvisioningStateSucceeded -Uri $endpoint -Credential $NcRestCredential -UseBasicParsing)) { throw New-Object System.Exception("ProvisioningState is not succeeded") } else { "Successfully updated the certificate information for {0}" -f $obj.ResourceRef | Trace-Output } # after we have generated the certificates and updated the servers to use the new certificate # we will want to go and remove any old certificates as this will cause issues in older builds "Removing the old certificates on {0} that match {1}" -f $obj.managementAddress, $obj.Certificate.Subject | Trace-Output $certsRemoved = Invoke-PSRemoteCommand -ComputerName $obj.managementAddress -Credential $Credential -ScriptBlock { param([Parameter(Mandatory = $true)]$param1) $certs = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Subject $param1.Subject if ($certs.Count -ge 2) { $certToRemove = $certs | Where-Object {$_.Thumbprint -ine $param1.Thumbprint} $certToRemove | Remove-Item return $certToRemove } } -ArgumentList $obj.Certificate if ($certsRemoved) { foreach ($cert in $certsRemoved) { "Removed certificate subject {0} and thumbprint {1}" -f $cert.Subject, $cert.Thumbprint | Trace-Output } } # restart nchostagent on server $null = Invoke-PSRemoteCommand -ComputerName $obj.managementAddress -Credential $Credential -ScriptBlock { Restart-Service -Name NcHostAgent -Force } } "Certificate rotation for Servers has completed" | Trace-Output -Level:Success } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } function Test-SdnProviderAddressConnectivity { <# .SYNOPSIS Tests whether jumbo packets can be sent between the provider addresses on the current host to the remote provider addresses defined. .PARAMETER ProviderAddress The IP address assigned to a hidden network adapter in a non-default network compartment. .EXAMPLE PS> Test-SdnProviderAddressConnectivity -ProviderAddress (Get-SdnProviderAddress -ComputerName 'Server01','Server02').ProviderAddress #> [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string[]]$ProviderAddress ) $maxEncapOverhead = 160 $defaultMTU = 1500 $icmpHeader = 28 $jumboPacket = ($maxEncapOverhead + $defaultMTU) - $icmpHeader $standardPacket = $defaultMTU - $icmpHeader try { $arrayList = [System.Collections.ArrayList]::new() $sourceProviderAddress = (Get-ProviderAddress).ProviderAddress if ($null -eq $sourceProviderAddress) { "No provider addresses returned on {0}" -f $env:COMPUTERNAME | Trace-Output -Level:Warning return } $compartmentId = (Get-NetCompartment | Where-Object { $_.CompartmentDescription -ieq 'PAhostVNic' }).CompartmentId if ($null -eq $compartmentId) { "No compartment returned on {0} that matches description PAhostVNic" -f $env:COMPUTERNAME | Trace-Output -Level:Warning return } foreach ($srcAddress in $sourceProviderAddress) { if ($srcAddress -ilike "169.*") { # if the PA address is an APIPA, it's an indication that host has been added to SDN data plane, however no tenant workloads have yet been provisioned onto the host "Skipping validation of {0} as it's an APIPA address" -f $srcAddress | Trace-Output continue } foreach ($dstAddress in $ProviderAddress) { if ($dstAddress -ilike "169.*") { # if the PA address is an APIPA, it's an indication that host has been added to SDN data plane, however no tenant workloads have yet been provisioned onto the host "Skipping validation of {0} as it's an APIPA address" -f $dstAddress | Trace-Output continue } $results = Test-Ping -DestinationAddress $dstAddress -SourceAddress $srcAddress -CompartmentId $compartmentId -BufferSize $jumboPacket, $standardPacket -DontFragment [void]$arrayList.Add($results) } } return $arrayList } catch { "{0}`n{1}" -f $_.Exception, $_.ScriptStackTrace | Trace-Output -Level:Error } } |