(That’s what I like to call “Title Gore”! Hope the SEO was worth it!)

We have a Windows Server 2019 environment that hosts a number of isolated Virtual Networks. One of those Virtual Networks is home to 2x IIS web servers, serving up some APIs and webapps on HTTPS (443). Both of those IIS virtual machines were configured behind a simple SLB Rule through our SDNv2 Network Controller.

Recently, we had a request to also allow connections on HTTP (80), mostly to accommodate IIS rewrite rules to shim users to the HTTPS version of the site. I’ve never had to make a change to a deployed SLB object before – as a matter of fact, I actually never had to configure multiple ports on the same rule!

I kind of blame SCVMM for this – we use it to manage our Network Controller stamp and they do not expose the ability to configure multiple load-balanced ports behind the same VIP. But you can certainly do it with raw Network Controller PowerShell!

Nerdy Talk

If you want to skip to the tl;dr version, blow past this section. I do not suggest this unless you are very familiar and comfortable with NC manipulation.

First, let’s get the raw JSON output of the current Load Balancer object. We can copy/paste this into something like JSON Editor Online to help visualize things better.

Assuming you don’t know the Load Balancer’s ResourceId, let’s quickly search for it:

$uri = "https://nc-cluster.domain.com"
$lbVIP = "172.20.200.150"
$lb = Get-NetworkControllerLoadBalancer -ConnectionUri | ? ResourceId -like "*$lbVIP*"

Cool, so you found the Load Balancer object. Let’s convert it to JSON and copy it to the clipboard:

$lb | ConvertTo-Json -Depth 20 | clip.exe

Now paste it over in the JSON Editor Online site and click the circled buttons – those will clean up the hideous indentation and pretty up the payload to a nice nested tree:

Let’s zoom in on the Properties node on the right. You’ll see that there are currently only 1x LoadBalancingRules and Probes:

Expanding into them and you can see the defined rule (A) and health probe (B) you’ve previously configured. Some important things to pay attention to for later:

  • The LoadBalancingRule contains a reference to the Probe definition
  • Each ResourceRef contains the node’s ResourceId

So we now know our tasks:

  1. Create a new LoadBalancerRule
  2. Create a new LoadBalancerProbe
  3. Reference the LoadBalancerProbe in the new LoadBalancerRule
  4. “Commit” the changes to Network Controller

Step 1 – Create the new LoadBalancerRule

# Create a new LB Rule and LB Rule Properties object
# Yes, InstanceId and ResourceId can and probably should be different GUIDs
$lbRuleNew = New-Object Microsoft.Windows.NetworkController.LoadBalancingRule
$lbRuleNew.InstanceId = (New-Guid).Guid
$lbRuleNew.ResourceId = (New-Guid).Guid
$lbRuleNew.ResourceRef = "/loadBalancers/$lbID/loadBalancingRules/$($lbRuleNew.ResourceId)"

# Some helpful metadata to identify stuff later thru NC
# You can use whatever values you want here
$lbRuleNew.ResourceMetadata = New-Object Microsoft.Windows.NetworkController.ResourceMetadata
$lbRuleNew.ResourceMetadata.Client = "Manual PowerShell"
$lbRuleNew.ResourceMetadata.ResourceName = "HTTP-Public-Web-TCP"


# Create the new LB Rule's properties
# We are referencing the existing stuff in index [0], which for me was the only Rule in the collection. 
# You may reuse this script one day and have more ports to add, so this covers that scenario.
$lbRuleNew.Properties = New-Object Microsoft.Windows.NetworkController.LoadBalancingRuleProperties
$lbRuleNew.Properties.LoadDistribution = $lb.Properties.LoadBalancingRules[0].Properties.LoadDistribution
$lbRuleNew.Properties.FrontendIPConfigurations = $lb.Properties.LoadBalancingRules[0].Properties.FrontendIPConfigurations
$lbRuleNew.Properties.BackendAddressPool = $lb.Properties.LoadBalancingRules[0].Properties.BackendAddressPool
$lbRuleNew.Properties.Protocol = "TCP"
$lbRuleNew.Properties.FrontendPort = 80
$lbRuleNew.Properties.BackendPort = 80
$lbRuleNew.Properties.IdleTimeoutInMinutes = 4

Step 2 – Create the new LoadBalancerProbe

# Create the new LB Rule Probe's properties
$lbRuleNewProbe = New-Object Microsoft.Windows.NetworkController.LoadBalancerProbe
$lbRuleNewProbe.InstanceId = (New-Guid).Guid
$lbRuleNewProbe.ResourceId = (New-Guid).Guid
$lbRuleNewProbe.ResourceRef = "/loadBalancers/$lbID/Probes/$($lbRuleNewProbe.ResourceId)"

$lbRuleNewProbe.ResourceMetadata = New-Object Microsoft.Windows.NetworkController.ResourceMetadata
$lbRuleNew.ResourceMetadata.Client = "Manual PowerShell"

$lbRuleNewProbe.Properties = New-Object Microsoft.Windows.NetworkController.LoadBalancerProbeProperties
$lbRuleNewProbe.Properties.Protocol = "TCP"
$lbRuleNewProbe.Properties.Port = 80
$lbRuleNewProbe.Properties.IntervalInSeconds = 5
$lbRuleNewProbe.Properties.NumberOfProbes = 3

Step 3 – Reference the LoadBalancerProbe in the new LoadBalancerRule

$lbRuleNew.Properties.Probe.ResourceRef = $lbRuleNewProbe.ResourceRef

Step 4 – “Commit” the changes to Network Controller

I have “commit” in quotes because we’re not really making a “change”. We’re literally going to overwrite the existing config with a new config. I’m unsure if I would use the term “immutable” to describe the existing configs, but Network Controller doesn’t let you make tiny changes, it literally expects you to rebuild the entire rule and re-POST it with the same IDs

# Add the new objects we just made to the existing collections
$lb.Properties.LoadBalancingRules += $lbRuleNew
$lb.Properties.Probes += $lbRuleNewProbe

# POST it back to Network Controller
New-NetworkControllerLoadBalancer -ConnectionUri $uri -ResourceId $lbID -Properties $lb.Properties -PassInnerException -Force

If it fails, you’ll know – NC will puke out some error. If it works, you should get no output. Huzzah! Let’s check the raw JSON again to confirm:

$lbFinalResult = Get-NetworkControllerLoadBalancer -ConnectionUri | ? ResourceId -like "*$lbVIP*"

$lbFinalResult | ConvertTo-Json -Depth 20 | clip.exe

We now see an additional LoadBalancingRule and Probe added to each collection! Expanding them open, we’ll see the configs we made:

  • A – The LoadBalancingRule with port 80 configured
  • B – The Probe health checks
  • C – The binding ResourceRef between the LoadBalancingRule and the Probe

tl;dr – That was a lot of work!

Yup! It sure was! Unfortunately, as of 2019 UR3, SCVMM doesn’t have the ability to configure multiple ports and protocols on the same VIP. I’ve filed an improvement request, but who knows how long (if ever) to get the fix in.

I personally have not used Windows Admin Center to manage my SDN stamp – this is due to the Kerberos/Certificate deployment “mismatch”; where WAC can’t manage Network Controller if SCVMM is already doing that.

So we’re left with raw PowerShell. You don’t need to dump the output to ConvertTo-Json; you can very easily just check the PowerShell object values right there in your PoSh session. However, I like viewing the entire LB config in a single JSON payload instead of burrowing down thru each PowerShell object.

If you’re feeling risky, you can just manipulate the huge JSON payload and POST it directly to the Network Controller API; that’s really all the Network Controller cmdlets are – a wrapper for some Invoke-RestMethod calls.

So here’s the entire script, soup-to-nuts. Make sure you edit your ports, protocols, load distribution (aka – affinity stickiness), and whatever else you need for your config – don’t just blindly run this unless you know what you’re doing đŸ˜€

# Configure your specifics here
$uri = "https://nc-cluster.domain.com"
$lbVIP = "172.20.200.150"

# Get the Load Balancer
$lb = Get-NetworkControllerLoadBalancer -ConnectionUri | ? ResourceId -like "*$lbVIP*"

# Create a new LB Rule and LB Rule Properties object
# Yes, InstanceId and ResourceId can and probably should be different unique identifiers
$lbRuleNew = New-Object Microsoft.Windows.NetworkController.LoadBalancingRule
$lbRuleNew.InstanceId = (New-Guid).Guid
$lbRuleNew.ResourceId = (New-Guid).Guid
$lbRuleNew.ResourceRef = "/loadBalancers/$lbID/loadBalancingRules/$($lbRuleNew.ResourceId)"

# Some helpful metadata to identify stuff later thru NC
# You can use whatever values you want here
$lbRuleNew.ResourceMetadata = New-Object Microsoft.Windows.NetworkController.ResourceMetadata
$lbRuleNew.ResourceMetadata.Client = "Manual PowerShell"
$lbRuleNew.ResourceMetadata.ResourceName = "HTTP-Public-Web-TCP"

# Create the new LB Rule's properties
# We are referencing the existing stuff in index [0], which for me was the only Rule in the collection. 
# You may reuse this script one day and have more ports to add, so this covers that scenario.
$lbRuleNew.Properties = New-Object Microsoft.Windows.NetworkController.LoadBalancingRuleProperties
$lbRuleNew.Properties.LoadDistribution = $lb.Properties.LoadBalancingRules[0].Properties.LoadDistribution
$lbRuleNew.Properties.FrontendIPConfigurations = $lb.Properties.LoadBalancingRules[0].Properties.FrontendIPConfigurations
$lbRuleNew.Properties.BackendAddressPool = $lb.Properties.LoadBalancingRules[0].Properties.BackendAddressPool
$lbRuleNew.Properties.Protocol = "TCP"
$lbRuleNew.Properties.FrontendPort = 80
$lbRuleNew.Properties.BackendPort = 80
$lbRuleNew.Properties.IdleTimeoutInMinutes = 4

# Create the new LB Rule Probe's properties
$lbRuleNewProbe = New-Object Microsoft.Windows.NetworkController.LoadBalancerProbe
$lbRuleNewProbe.InstanceId = (New-Guid).Guid
$lbRuleNewProbe.ResourceId = (New-Guid).Guid
$lbRuleNewProbe.ResourceRef = "/loadBalancers/$lbID/Probes/$($lbRuleNewProbe.ResourceId)"

$lbRuleNewProbe.ResourceMetadata = New-Object Microsoft.Windows.NetworkController.ResourceMetadata
$lbRuleNew.ResourceMetadata.Client = "Manual PowerShell"

$lbRuleNewProbe.Properties = New-Object Microsoft.Windows.NetworkController.LoadBalancerProbeProperties
$lbRuleNewProbe.Properties.Protocol = "TCP"
$lbRuleNewProbe.Properties.Port = 80
$lbRuleNewProbe.Properties.IntervalInSeconds = 5
$lbRuleNewProbe.Properties.NumberOfProbes = 3

# Reference the Probe back to the LB Rule
$lbRuleNew.Properties.Probe.ResourceRef = $lbRuleNewProbe.ResourceRef

# Commit changes
# Add the new objects we just made to the existing collections
$lb.Properties.LoadBalancingRules += $lbRuleNew
$lb.Properties.Probes += $lbRuleNewProbe

# POST it back to Network Controller
New-NetworkControllerLoadBalancer -ConnectionUri $uri -ResourceId $lbID -Properties $lb.Properties -PassInnerException -Force

Leave a Reply

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