Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 42 additions & 7 deletions src/modules/SdnDiag.NetworkController.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,10 @@ function Get-PublicIpReference {
# with the ipconfiguration and return back to calling function
if ($IpConfiguration.properties.publicIPAddress) {
"Located {0} associated with {1}" -f $IpConfiguration.properties.publicIPAddress.resourceRef, $IpConfiguration.resourceRef | Trace-Output -Level:Verbose
return ($IpConfiguration.properties.publicIPAddress.resourceRef)
return [PSCustomObject]@{
ResourceRef = $IpConfiguration.properties.publicIPAddress.resourceRef
IPAddress = $null
}
}
else {
"Unable to locate an instance-level public IP address associated with {0}" -f $IpConfiguration.resourceRef | Trace-Output -Level:Verbose
Expand Down Expand Up @@ -269,8 +272,26 @@ function Get-PublicIpReference {
$natRule = $loadBalancers.properties.outboundNatRules | Where-Object { $_.resourceRef -eq $obRuleRef }
$frontendConfig = $loadBalancers.properties.frontendIPConfigurations | Where-Object { $_.resourceRef -eq $natRule.properties.frontendIPConfigurations[0].resourceRef }

"Located {0} associated with {0}" -f $frontendConfig.resourceRef, $natRule.resourceRef | Trace-Output -Level:Verbose
return ($frontendConfig.properties.publicIPAddress.resourceRef)
"Located {0} associated with {1}" -f $frontendConfig.resourceRef, $natRule.resourceRef | Trace-Output -Level:Verbose

# the public IP can be referenced via a publicIPAddress resource or by a static privateIPAddress
# from the public VIP logical network; check both and return the appropriate value
if ($frontendConfig.properties.publicIPAddress) {
return [PSCustomObject]@{
ResourceRef = $frontendConfig.properties.publicIPAddress.resourceRef
IPAddress = $null
}
}
elseif (![string]::IsNullOrEmpty($frontendConfig.properties.privateIPAddress)) {
"Located static privateIPAddress {0} on frontend {1}" -f $frontendConfig.properties.privateIPAddress, $frontendConfig.resourceRef | Trace-Output -Level:Verbose
return [PSCustomObject]@{
ResourceRef = $null
IPAddress = $frontendConfig.properties.privateIPAddress
}
}
else {
"Unable to locate publicIPAddress or privateIPAddress on frontend {0}" -f $frontendConfig.resourceRef | Trace-Output -Level:Verbose
}
}
else {
"Unable to locate outboundNatRules associated with {0}" -f $IpConfiguration.properties.loadBalancerBackendAddressPools.resourceRef | Trace-Output -Level:Verbose
Expand Down Expand Up @@ -1832,14 +1853,28 @@ function Get-SdnNetworkInterfaceOutboundPublicIPAddress {
foreach ($ipConfig in $networkInterface.properties.ipConfigurations) {
$publicIpRef = Get-PublicIpReference @ncRestParams -IpConfiguration $ipConfig
if ($publicIpRef) {
$publicIpAddress = Get-SdnResource @ncRestParams -ResourceRef $publicIpRef
if ($publicIpAddress) {
if ($publicIpRef.ResourceRef) {
# public IP is referenced via a publicIPAddress resource; look it up to get the IP address
$publicIpAddress = Get-SdnResource @ncRestParams -ResourceRef $publicIpRef.ResourceRef
if ($publicIpAddress) {
[void]$arrayList.Add(
[PSCustomObject]@{
IPConfigResourceRef = $ipConfig.resourceRef
IPConfigPrivateIPAddress = $ipConfig.properties.privateIPAddress
PublicIPResourceRef = $publicIpAddress.resourceRef
PublicIPAddress = $publicIpAddress.properties.ipAddress
}
)
}
}
elseif ($publicIpRef.IPAddress) {
# public IP is a static privateIPAddress on the LB frontend from the public VIP logical network
[void]$arrayList.Add(
[PSCustomObject]@{
IPConfigResourceRef = $ipConfig.resourceRef
IPConfigPrivateIPAddress = $ipConfig.properties.privateIPAddress
PublicIPResourceRef = $publicIpAddress.resourceRef
PublicIPAddress = $publicIpAddress.properties.ipAddress
PublicIPResourceRef = $null
PublicIPAddress = $publicIpRef.IPAddress
}
)
}
Expand Down
80 changes: 80 additions & 0 deletions tests/offline/NetworkController.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,83 @@ Describe 'NetworkController - Get-SdnResource' {
}
}
}

Describe 'NetworkController - Get-SdnNetworkInterfaceOutboundPublicIPAddress' {
It "Returns public IP for NIC with a direct instance-level publicIPAddress (tenantvm1)" {
InModuleScope SdnDiag.NetworkController {
Mock Invoke-RestMethodWithRetry {
$path = ([Uri]$Uri).AbsolutePath
if ($path -match '/networking/v1/(.+)$') {
$resourceType = ($Matches[1] -split '/')[0]
$refKey = "/$($Matches[1])"
if ($Global:PesterOfflineTests.SdnApiResourcesByRef.ContainsKey($refKey)) {
return $Global:PesterOfflineTests.SdnApiResourcesByRef[$refKey]
}
return [PSCustomObject]@{ value = $Global:PesterOfflineTests.SdnApiResources[$resourceType] }
}
}
$result = Get-SdnNetworkInterfaceOutboundPublicIPAddress -NcUri "https://dvlab-nc.dvlab.contoso.local" -ResourceId "tenantvm1"
$result | Should -Not -BeNullOrEmpty
$result[0].PublicIPAddress | Should -Be "40.40.40.5"
$result[0].PublicIPResourceRef | Should -Be "/publicIPAddresses/pip-tenant-0001"
}
}

It "Returns public IP for NIC using LB outbound NAT with publicIPAddress on frontend (tenantvm2)" {
InModuleScope SdnDiag.NetworkController {
Mock Invoke-RestMethodWithRetry {
$path = ([Uri]$Uri).AbsolutePath
if ($path -match '/networking/v1/(.+)$') {
$resourceType = ($Matches[1] -split '/')[0]
$refKey = "/$($Matches[1])"
if ($Global:PesterOfflineTests.SdnApiResourcesByRef.ContainsKey($refKey)) {
return $Global:PesterOfflineTests.SdnApiResourcesByRef[$refKey]
}
return [PSCustomObject]@{ value = $Global:PesterOfflineTests.SdnApiResources[$resourceType] }
}
}
$result = Get-SdnNetworkInterfaceOutboundPublicIPAddress -NcUri "https://dvlab-nc.dvlab.contoso.local" -ResourceId "tenantvm2"
$result | Should -Not -BeNullOrEmpty
$result[0].PublicIPAddress | Should -Be "40.40.40.4"
$result[0].PublicIPResourceRef | Should -Be "/publicIPAddresses/pip-outbound-0001"
}
}

It "Returns public IP for NIC using LB outbound NAT with static privateIPAddress on frontend (tenantvm3)" {
InModuleScope SdnDiag.NetworkController {
Mock Invoke-RestMethodWithRetry {
$path = ([Uri]$Uri).AbsolutePath
if ($path -match '/networking/v1/(.+)$') {
$resourceType = ($Matches[1] -split '/')[0]
$refKey = "/$($Matches[1])"
if ($Global:PesterOfflineTests.SdnApiResourcesByRef.ContainsKey($refKey)) {
return $Global:PesterOfflineTests.SdnApiResourcesByRef[$refKey]
}
return [PSCustomObject]@{ value = $Global:PesterOfflineTests.SdnApiResources[$resourceType] }
}
}
$result = Get-SdnNetworkInterfaceOutboundPublicIPAddress -NcUri "https://dvlab-nc.dvlab.contoso.local" -ResourceId "tenantvm3"
$result | Should -Not -BeNullOrEmpty
$result[0].PublicIPAddress | Should -Be "40.40.40.6"
$result[0].PublicIPResourceRef | Should -BeNullOrEmpty
}
}

It "Returns empty result for NIC with no public IP association (nic-vm01-0001)" {
InModuleScope SdnDiag.NetworkController {
Mock Invoke-RestMethodWithRetry {
$path = ([Uri]$Uri).AbsolutePath
if ($path -match '/networking/v1/(.+)$') {
$resourceType = ($Matches[1] -split '/')[0]
$refKey = "/$($Matches[1])"
if ($Global:PesterOfflineTests.SdnApiResourcesByRef.ContainsKey($refKey)) {
return $Global:PesterOfflineTests.SdnApiResourcesByRef[$refKey]
}
return [PSCustomObject]@{ value = $Global:PesterOfflineTests.SdnApiResources[$resourceType] }
}
}
$result = Get-SdnNetworkInterfaceOutboundPublicIPAddress -NcUri "https://dvlab-nc.dvlab.contoso.local" -ResourceId "nic-vm01-0001"
$result.Count | Should -Be 0
}
}
}
74 changes: 74 additions & 0 deletions tests/offline/data/SdnApiResources/loadBalancers.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,80 @@
"tenantId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
},
"etag": "W/\"lb-etag-0001\""
},
{
"resourceId": "lb-outbound-0002",
"properties": {
"frontendIPConfigurations": [
{
"etag": "W/\"lb-etag-0002\"",
"resourceRef": "/loadBalancers/lb-outbound-0002/frontendIPConfigurations/feip-0002",
"resourceId": "feip-0002",
"properties": {
"subnet": {
"resourceRef": "/logicalNetworks/PublicVIP/subnets/PublicVIP-subnet-0001"
},
"provisioningState": "Succeeded",
"privateIPAllocationMethod": "Static",
"privateIPAddress": "40.40.40.6"
},
"instanceId": "feip-inst-0002"
}
],
"probes": [],
"backendAddressPools": [
{
"etag": "W/\"lb-etag-0002\"",
"resourceRef": "/loadBalancers/lb-outbound-0002/backendAddressPools/OutboundNatPool",
"resourceId": "OutboundNatPool",
"properties": {
"backendIPConfigurations": [
{
"resourceRef": "/networkInterfaces/tenantvm3/ipConfigurations/ipconfig1"
}
],
"provisioningState": "Succeeded",
"outboundNatRules": [
{
"resourceRef": "/loadBalancers/lb-outbound-0002/outboundNatRules/outbound-nat-0002"
}
]
},
"instanceId": "bepool-inst-0002"
}
],
"outboundNatRules": [
{
"etag": "W/\"lb-etag-0002\"",
"resourceRef": "/loadBalancers/lb-outbound-0002/outboundNatRules/outbound-nat-0002",
"resourceId": "outbound-nat-0002",
"properties": {
"protocol": "All",
"frontendIPConfigurations": [
{
"resourceRef": "/loadBalancers/lb-outbound-0002/frontendIPConfigurations/feip-0002"
}
],
"provisioningState": "Succeeded",
"backendAddressPool": {
"resourceRef": "/loadBalancers/lb-outbound-0002/backendAddressPools/OutboundNatPool"
}
},
"instanceId": "outnat-inst-0002"
}
],
"provisioningState": "Succeeded",
"loadBalancingRules": []
},
"instanceId": "lb-inst-0002-aaaa-bbbb-cccccccccccc",
"resourceRef": "/loadBalancers/lb-outbound-0002",
"resourceMetadata": {
"client": "Network Resource Provider",
"resourceName": "lb-outbound-0002",
"groupId": "TestWorkload-RG",
"tenantId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
},
"etag": "W/\"lb-etag-0002\""
}
],
"nextLink": ""
Expand Down
53 changes: 53 additions & 0 deletions tests/offline/data/SdnApiResources/networkInterfaces.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,59 @@
"resourceId": "tenantvm2",
"etag": "W/\"nic-etag-0002\""
},
{
"properties": {
"configurationState": {
"lastUpdatedTime": "2024-03-15T10:00:00.0000000-05:00",
"id": "nic-inst-0005-aaaa-bbbb-cccccccccccc",
"status": "Success"
},
"privateMacAddress": "001DD8070003",
"isPrimary": true,
"isHostVirtualNetworkInterface": false,
"portSettings": {
"provisioningState": "Succeeded",
"stormLimit": 0,
"vmqWeight": 100,
"iovWeight": 0,
"arpGuardEnabled": "Disabled",
"portFlowLimit": 0,
"macSpoofingEnabled": "Disabled",
"dhcpGuardEnabled": "Disabled"
},
"ipConfigurations": [
{
"properties": {
"privateIPAllocationMethod": "Static",
"subnet": {
"resourceRef": "/virtualNetworks/vnet-0001/subnets/subnet-0001"
},
"loadBalancerInboundNatRules": [],
"loadBalancerBackendAddressPools": [
{
"resourceRef": "/loadBalancers/lb-outbound-0002/backendAddressPools/OutboundNatPool"
}
],
"provisioningState": "Succeeded",
"privateIPAddress": "192.168.33.6"
},
"resourceRef": "/networkInterfaces/tenantvm3/ipConfigurations/ipconfig1",
"resourceId": "ipconfig1",
"etag": "W/\"nic-etag-0005\"",
"instanceId": "ipconf-inst-0005"
}
],
"server": {
"resourceRef": "/servers/DVLAB-S1-N02"
},
"provisioningState": "Succeeded",
"privateMacAllocationMethod": "Dynamic"
},
"instanceId": "nic-inst-0005-aaaa-bbbb-cccccccccccc",
"resourceId": "tenantvm3",
"resourceRef": "/networkInterfaces/tenantvm3",
"etag": "W/\"nic-etag-0005\""
},
{
"properties": {
"configurationState": {
Expand Down