Repository: kubernetes-sigs/cloud-provider-azure
Pull request #3774 (2023-04-20 15:38:48)

[release-1.24][IPv6] Choose correct primary IP config
<!--  Thanks for sending a pull request!  Here are some tips for you:

1. If this is your first time, please read our contributor guidelines: https://git.k8s.io/community/contributors/guide/first-contribution.md#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
2. Please label this pull request according to what type of issue you are addressing, especially if this is a release targeted pull request. For reference on required PR/issue labels, read here:
https://git.k8s.io/community/contributors/devel/sig-release/release.md#issuepr-kind-label
3. Ensure you have added or ran the appropriate tests for your PR: https://git.k8s.io/community/contributors/devel/sig-testing/testing.md
4. If you want *faster* PR reviews, read how: https://git.k8s.io/community/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
5. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

#### What type of PR is this?
/kind bug
<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature
/kind design

Optionally add one or more of the following kinds if applicable:
/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

#### What this PR does / why we need it:
[IPv6] Choose correct primary IP config. Regardless of IPv6 only or dualstack clusters, IPv4 IP config is always primary. So for IPv6 backend address pool, IP config's IP version needs consideration.
#### Which issue(s) this PR fixes:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #

#### Special notes for your reviewer:

#### Does this PR introduce a user-facing change?
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".

For more information on release notes see: https://git.k8s.io/community/contributors/guide/release-notes.md
-->
```release-note
[IPv6] Choose correct primary IP config. Regardless of IPv6 only or dualstack clusters, IPv4 IP config is always primary. So for IPv6 backend address pool, IP config's IP version needs consideration.
```

#### Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:

<!--
This section can be blank if this pull request does not require a release note.

When adding links which point to resources within git repositories, like
KEPs or supporting documentation, please reference a specific commit and avoid
linking directly to the master branch. This ensures that links reference a
specific point in time, rather than a document that may change over time.

See here for guidance on getting permanent links to files: https://help.github.com/en/articles/getting-permanent-links-to-files

Please use the following format for linking documentation:
- [KEP]: <link>
- [Usage]: <link>
- [Other doc]: <link>
-->
```docs

```



Patch:
diff --git a/go.mod b/go.mod
index add7dd1b9..077a48d40 100644
--- a/go.mod
+++ b/go.mod
@@ -24,10 +24,10 @@ require (
 	k8s.io/api v0.24.13
 	k8s.io/apimachinery v0.24.13
 	k8s.io/apiserver v0.24.12
-	k8s.io/client-go v0.24.13
+	k8s.io/client-go v0.24.12
 	k8s.io/cloud-provider v0.24.12
 	k8s.io/component-base v0.24.12
-	k8s.io/component-helpers v0.24.13
+	k8s.io/component-helpers v0.24.12
 	k8s.io/controller-manager v0.24.12
 	k8s.io/klog/v2 v2.60.1
 	k8s.io/kubelet v0.24.12
diff --git a/go.sum b/go.sum
index 8f8676829..66d9a3e06 100644
--- a/go.sum
+++ b/go.sum
@@ -818,14 +818,14 @@ k8s.io/apimachinery v0.24.13 h1:ju8KJuoUDKlWQfFUDLFbJRiCiDVnj5yoqKSITyx7z+4=
 k8s.io/apimachinery v0.24.13/go.mod h1:Yg8GIoNnVG9af59MrlKMm4Unsw3EBj+MfEBvfSid2/4=
 k8s.io/apiserver v0.24.12 h1:rraKkjlx8HQ1PPM2jLtY0MQJMn4eMFKnFlt148ReEhg=
 k8s.io/apiserver v0.24.12/go.mod h1:YcGUZmuwQ0GqtEO0ovM0BReQ4jQ7hj6U6G+9dkR+LT8=
-k8s.io/client-go v0.24.13 h1:wlyue6MHZ4qcja8NGEoCWLaou6qlCbv+kmj3lBfROIo=
-k8s.io/client-go v0.24.13/go.mod h1:HjvA0mAO9iijaL8KuZZlLNdxILsvaYpYM1KtyOllUQk=
+k8s.io/client-go v0.24.12 h1:i38g1SL83zs4CBCY9vQqtBfx+evsghBoBGz5u6lwtKs=
+k8s.io/client-go v0.24.12/go.mod h1:yY5hsSExZV2ZWwBANP/eBhB3SorSf0nkADD7/KYgPmU=
 k8s.io/cloud-provider v0.24.12 h1:nvvMBBHEv+pMgx+mdXryfcrXxJZaOMxRYjqF6bAKOBc=
 k8s.io/cloud-provider v0.24.12/go.mod h1:5Wdc7lCGn6ofNhyfFhi5DfeXSCDaFXvTPQgAvjzcGd0=
 k8s.io/component-base v0.24.12 h1:+q1myd+LJv5xEMyG8KrwromIcLYkk8DGMwBJbCcCXSA=
 k8s.io/component-base v0.24.12/go.mod h1:vfKpKKTm3iXonv1zwUeLj8/fNZXfK2jfgzDMU8zfars=
-k8s.io/component-helpers v0.24.13 h1:e2bMMF2EowwcoHXVvA/TJa3/MC6Cr+ASqhdcWe1Nu+Q=
-k8s.io/component-helpers v0.24.13/go.mod h1:rWuWGs92KA4lZfEnQ658HHPoYFzZTTywNLE/jLn06is=
+k8s.io/component-helpers v0.24.12 h1:16o0RUM34j6QkOFLU87kbyaMP+Fit+PzG2iRsJ9Azt8=
+k8s.io/component-helpers v0.24.12/go.mod h1:oBT9p6Dd0lIW7CGQNHiDfUveWEECXTVFzwmYHg4PVyg=
 k8s.io/controller-manager v0.24.12 h1:AUre5+hsMry5R8xuVRp3xMe6czIm1PZlh2TcF5TzCCc=
 k8s.io/controller-manager v0.24.12/go.mod h1:XdyfRFfhXGP08AZU/ydu8sjW2PDbsxS7+gZHGAA4NWM=
 k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
diff --git a/pkg/provider/azure_standard.go b/pkg/provider/azure_standard.go
index d611138d5..2e40e1272 100644
--- a/pkg/provider/azure_standard.go
+++ b/pkg/provider/azure_standard.go
@@ -56,6 +56,10 @@ var (
 	vmasIDRE           = regexp.MustCompile(`/subscriptions/(?:.*)/resourceGroups/(?:.*)/providers/Microsoft.Compute/availabilitySets/(.+)`)
 )
 
+const (
+	v6Suffix = "IPv6"
+)
+
 // getStandardMachineID returns the full identifier of a virtual machine.
 func (az *Cloud) getStandardMachineID(subscriptionID, resourceGroup, machineName string) string {
 	return fmt.Sprintf(
@@ -281,6 +285,11 @@ func getBackendPoolName(clusterName string, service *v1.Service) string {
 	return clusterName
 }
 
+// ifBackendPoolIPv6 checks if a backend pool is of IPv6 according to name/ID.
+func isBackendPoolIPv6(name string) bool {
+	return strings.HasSuffix(name, fmt.Sprintf("-%s", v6Suffix))
+}
+
 func (az *Cloud) getLoadBalancerRuleName(service *v1.Service, protocol v1.Protocol, port int32) string {
 	prefix := az.getRulePrefix(service)
 	ruleName := fmt.Sprintf("%s-%s-%d", prefix, protocol, port)
diff --git a/pkg/provider/azure_vmss.go b/pkg/provider/azure_vmss.go
index 87ea13c00..de2346b9f 100644
--- a/pkg/provider/azure_vmss.go
+++ b/pkg/provider/azure_vmss.go
@@ -33,7 +33,6 @@ import (
 	utilerrors "k8s.io/apimachinery/pkg/util/errors"
 	cloudprovider "k8s.io/cloud-provider"
 	"k8s.io/klog/v2"
-	utilnet "k8s.io/utils/net"
 	"k8s.io/utils/pointer"
 
 	azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
@@ -919,8 +918,8 @@ func (ss *ScaleSet) GetPrimaryInterface(nodeName string) (network.Interface, err
 	return nic, nil
 }
 
-// getPrimaryNetworkInterfaceConfiguration gets primary network interface configuration for scale set virtual machine.
-func (ss *ScaleSet) getPrimaryNetworkInterfaceConfiguration(networkConfigurations []compute.VirtualMachineScaleSetNetworkConfiguration, nodeName string) (*compute.VirtualMachineScaleSetNetworkConfiguration, error) {
+// getPrimaryNetworkInterfaceConfiguration gets primary network interface configuration for VMSS VM or VMSS.
+func getPrimaryNetworkInterfaceConfiguration(networkConfigurations []compute.VirtualMachineScaleSetNetworkConfiguration, resource string) (*compute.VirtualMachineScaleSetNetworkConfiguration, error) {
 	if len(networkConfigurations) == 1 {
 		return &networkConfigurations[0], nil
 	}
@@ -932,58 +931,38 @@ func (ss *ScaleSet) getPrimaryNetworkInterfaceConfiguration(networkConfiguration
 		}
 	}
 
-	return nil, fmt.Errorf("failed to find a primary network configuration for the scale set VM %q", nodeName)
+	return nil, fmt.Errorf("failed to find a primary network configuration for the VMSS VM or VMSS %q", resource)
 }
 
-// getPrimaryNetworkInterfaceConfigurationForScaleSet gets primary network interface configuration for scale set.
-func (ss *ScaleSet) getPrimaryNetworkInterfaceConfigurationForScaleSet(networkConfigurations []compute.VirtualMachineScaleSetNetworkConfiguration, vmssName string) (*compute.VirtualMachineScaleSetNetworkConfiguration, error) {
-	if len(networkConfigurations) == 1 {
-		return &networkConfigurations[0], nil
-	}
-
-	for idx := range networkConfigurations {
-		networkConfig := &networkConfigurations[idx]
-		if networkConfig.Primary != nil && *networkConfig.Primary {
-			return networkConfig, nil
-		}
-	}
-
-	return nil, fmt.Errorf("failed to find a primary network configuration for the scale set %q", vmssName)
-}
-
-func getPrimaryIPConfigFromVMSSNetworkConfig(config *compute.VirtualMachineScaleSetNetworkConfiguration) (*compute.VirtualMachineScaleSetIPConfiguration, error) {
+func getPrimaryIPConfigFromVMSSNetworkConfig(config *compute.VirtualMachineScaleSetNetworkConfiguration, backendPoolID, resource string) (*compute.VirtualMachineScaleSetIPConfiguration, error) {
 	ipConfigurations := *config.IPConfigurations
-	if len(ipConfigurations) == 1 {
-		return &ipConfigurations[0], nil
-	}
-
-	for idx := range ipConfigurations {
-		ipConfig := &ipConfigurations[idx]
-		if ipConfig.Primary != nil && *ipConfig.Primary {
-			return ipConfig, nil
+	isIPv6 := isBackendPoolIPv6(backendPoolID)
+
+	if !isIPv6 {
+		// There should be exactly one primary IP config.
+		// https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/virtual-network-network-interface-addresses?tabs=nic-address-portal#ip-configurations
+		if len(ipConfigurations) == 1 {
+			return &ipConfigurations[0], nil
+		}
+		for idx := range ipConfigurations {
+			ipConfig := &ipConfigurations[idx]
+			if ipConfig.Primary != nil && *ipConfig.Primary {
+				return ipConfig, nil
+			}
 		}
-	}
-
-	return nil, fmt.Errorf("failed to find a primary IP configuration")
-}
-
-func (ss *ScaleSet) getConfigForScaleSetByIPFamily(config *compute.VirtualMachineScaleSetNetworkConfiguration, nodeName string, IPv6 bool) (*compute.VirtualMachineScaleSetIPConfiguration, error) {
-	ipConfigurations := *config.IPConfigurations
-
-	var ipVersion compute.IPVersion
-	if IPv6 {
-		ipVersion = compute.IPv6
 	} else {
-		ipVersion = compute.IPv4
-	}
-	for idx := range ipConfigurations {
-		ipConfig := &ipConfigurations[idx]
-		if ipConfig.PrivateIPAddressVersion == ipVersion {
-			return ipConfig, nil
+		// For IPv6 or dualstack service, we need to pick the right IP configuration based on the cluster ip family
+		// IPv6 configuration is only supported as non-primary, so we need to fetch the ip configuration where the
+		// privateIPAddressVersion matches the clusterIP family
+		for idx := range ipConfigurations {
+			ipConfig := &ipConfigurations[idx]
+			if ipConfig.PrivateIPAddressVersion == compute.IPv6 {
+				return ipConfig, nil
+			}
 		}
 	}
 
-	return nil, fmt.Errorf("failed to find a IPconfiguration(IPv6=%v) for the scale set VM %q", IPv6, nodeName)
+	return nil, fmt.Errorf("failed to find a primary IP configuration (IPv6=%t) for the VMSS VM or VMSS %q", isIPv6, resource)
 }
 
 // EnsureHostInPool ensures the given VM's Primary NIC's Primary IP Configuration is
@@ -1040,28 +1019,15 @@ func (ss *ScaleSet) EnsureHostInPool(service *v1.Service, nodeName types.NodeNam
 	}
 
 	networkInterfaceConfigurations := *vm.VirtualMachineScaleSetVMProperties.NetworkProfileConfiguration.NetworkInterfaceConfigurations
-	primaryNetworkInterfaceConfiguration, err := ss.getPrimaryNetworkInterfaceConfiguration(networkInterfaceConfigurations, vmName)
+	primaryNetworkInterfaceConfiguration, err := getPrimaryNetworkInterfaceConfiguration(networkInterfaceConfigurations, vmName)
 	if err != nil {
 		return "", "", "", nil, err
 	}
 
-	var primaryIPConfiguration *compute.VirtualMachineScaleSetIPConfiguration
-	ipv6 := utilnet.IsIPv6String(service.Spec.ClusterIP)
 	// Find primary network interface configuration.
-	if !ss.Cloud.ipv6DualStackEnabled && !ipv6 {
-		// Find primary IP configuration.
-		primaryIPConfiguration, err = getPrimaryIPConfigFromVMSSNetworkConfig(primaryNetworkInterfaceConfiguration)
-		if err != nil {
-			return "", "", "", nil, err
-		}
-	} else {
-		// For IPv6 or dualstack service, we need to pick the right IP configuration based on the cluster ip family
-		// IPv6 configuration is only supported as non-primary, so we need to fetch the ip configuration where the
-		// privateIPAddressVersion matches the clusterIP family
-		primaryIPConfiguration, err = ss.getConfigForScaleSetByIPFamily(primaryNetworkInterfaceConfiguration, vmName, ipv6)
-		if err != nil {
-			return "", "", "", nil, err
-		}
+	primaryIPConfiguration, err := getPrimaryIPConfigFromVMSSNetworkConfig(primaryNetworkInterfaceConfiguration, backendPoolID, vmName)
+	if err != nil {
+		return "", "", "", nil, err
 	}
 
 	// Update primary IP configuration's LoadBalancerBackendAddressPools.
@@ -1215,24 +1181,14 @@ func (ss *ScaleSet) ensureVMSSInPool(service *v1.Service, nodes []*v1.Node, back
 			continue
 		}
 		vmssNIC := *vmss.VirtualMachineProfile.NetworkProfile.NetworkInterfaceConfigurations
-		primaryNIC, err := ss.getPrimaryNetworkInterfaceConfigurationForScaleSet(vmssNIC, vmssName)
+		primaryNIC, err := getPrimaryNetworkInterfaceConfiguration(vmssNIC, vmssName)
 		if err != nil {
 			return err
 		}
-		var primaryIPConfig *compute.VirtualMachineScaleSetIPConfiguration
-		ipv6 := utilnet.IsIPv6String(service.Spec.ClusterIP)
 		// Find primary network interface configuration.
-		if !ss.Cloud.ipv6DualStackEnabled && !ipv6 {
-			// Find primary IP configuration.
-			primaryIPConfig, err = getPrimaryIPConfigFromVMSSNetworkConfig(primaryNIC)
-			if err != nil {
-				return err
-			}
-		} else {
-			primaryIPConfig, err = ss.getConfigForScaleSetByIPFamily(primaryNIC, "", ipv6)
-			if err != nil {
-				return err
-			}
+		primaryIPConfig, err := getPrimaryIPConfigFromVMSSNetworkConfig(primaryNIC, backendPoolID, vmssName)
+		if err != nil {
+			return err
 		}
 
 		loadBalancerBackendAddressPools := []compute.SubResource{}
@@ -1451,40 +1407,20 @@ func (ss *ScaleSet) ensureBackendPoolDeletedFromNode(nodeName, backendPoolID str
 		return "", "", "", nil, nil
 	}
 	networkInterfaceConfigurations := *vm.VirtualMachineScaleSetVMProperties.NetworkProfileConfiguration.NetworkInterfaceConfigurations
-	primaryNetworkInterfaceConfiguration, err := ss.getPrimaryNetworkInterfaceConfiguration(networkInterfaceConfigurations, nodeName)
+	primaryNetworkInterfaceConfiguration, err := getPrimaryNetworkInterfaceConfiguration(networkInterfaceConfigurations, nodeName)
 	if err != nil {
 		return "", "", "", nil, err
 	}
 
-	// Find primary IP configuration.
-	primaryIPConfiguration, err := getPrimaryIPConfigFromVMSSNetworkConfig(primaryNetworkInterfaceConfiguration)
+	found, err := deleteBackendPoolFromIPConfig("ensureBackendPoolDeletedFromNode", backendPoolID, nodeName, primaryNetworkInterfaceConfiguration)
 	if err != nil {
 		return "", "", "", nil, err
 	}
-	if primaryIPConfiguration.LoadBalancerBackendAddressPools == nil || len(*primaryIPConfiguration.LoadBalancerBackendAddressPools) == 0 {
-		return "", "", "", nil, nil
-	}
-
-	// Construct new loadBalancerBackendAddressPools and remove backendAddressPools from primary IP configuration.
-	existingBackendPools := *primaryIPConfiguration.LoadBalancerBackendAddressPools
-	newBackendPools := []compute.SubResource{}
-	foundPool := false
-	for i := len(existingBackendPools) - 1; i >= 0; i-- {
-		curPool := existingBackendPools[i]
-		if strings.EqualFold(backendPoolID, *curPool.ID) {
-			klog.V(10).Infof("ensureBackendPoolDeletedFromNode gets unwanted backend pool %q for node %s", backendPoolID, nodeName)
-			foundPool = true
-			newBackendPools = append(existingBackendPools[:i], existingBackendPools[i+1:]...)
-		}
-	}
-
-	// Pool not found, assume it has been already removed.
-	if !foundPool {
+	if !found {
 		return "", "", "", nil, nil
 	}
 
 	// Compose a new vmssVM with added backendPoolID.
-	primaryIPConfiguration.LoadBalancerBackendAddressPools = &newBackendPools
 	newVM := &compute.VirtualMachineScaleSetVM{
 		Location: &vm.Location,
 		VirtualMachineScaleSetVMProperties: &compute.VirtualMachineScaleSetVMProperties{
@@ -1583,13 +1519,13 @@ func (ss *ScaleSet) ensureBackendPoolDeletedFromVMSS(backendPoolID, vmSetName st
 				return true
 			}
 			vmssNIC := *vmss.VirtualMachineProfile.NetworkProfile.NetworkInterfaceConfigurations
-			primaryNIC, err := ss.getPrimaryNetworkInterfaceConfigurationForScaleSet(vmssNIC, pointer.StringDeref(vmss.Name, ""))
+			primaryNIC, err := getPrimaryNetworkInterfaceConfiguration(vmssNIC, pointer.StringDeref(vmss.Name, ""))
 			if err != nil {
 				klog.Errorf("ensureBackendPoolDeletedFromVMSS: failed to get the primary network interface config of the VMSS %s: %v", pointer.StringDeref(vmss.Name, ""), err)
 				errorList = append(errorList, err)
 				return true
 			}
-			primaryIPConfig, err := getPrimaryIPConfigFromVMSSNetworkConfig(primaryNIC)
+			primaryIPConfig, err := getPrimaryIPConfigFromVMSSNetworkConfig(primaryNIC, backendPoolID, pointer.StringDeref(vmss.Name, ""))
 			if err != nil {
 				klog.Errorf("ensureBackendPoolDeletedFromVMSS: failed to the primary IP config from the VMSS %s's network config : %v", pointer.StringDeref(vmss.Name, ""), err)
 				errorList = append(errorList, err)
@@ -1795,6 +1731,35 @@ func (ss *ScaleSet) GetNodeCIDRMasksByProviderID(providerID string) (int, int, e
 	return ipv4Mask, ipv6Mask, nil
 }
 
+// deleteBackendPoolFromIPConfig deletes the backend pool from the IP config.
+func deleteBackendPoolFromIPConfig(msg, backendPoolID, resource string, primaryNIC *compute.VirtualMachineScaleSetNetworkConfiguration) (bool, error) {
+	primaryIPConfig, err := getPrimaryIPConfigFromVMSSNetworkConfig(primaryNIC, backendPoolID, resource)
+	if err != nil {
+		klog.Errorf("%s: failed to get the primary IP config from the VMSS %q's network config: %v", msg, resource, err)
+		return false, err
+	}
+	loadBalancerBackendAddressPools := []compute.SubResource{}
+	if primaryIPConfig.LoadBalancerBackendAddressPools != nil {
+		loadBalancerBackendAddressPools = *primaryIPConfig.LoadBalancerBackendAddressPools
+	}
+
+	var found bool
+	var newBackendPools []compute.SubResource
+	for i := len(loadBalancerBackendAddressPools) - 1; i >= 0; i-- {
+		curPool := loadBalancerBackendAddressPools[i]
+		if strings.EqualFold(backendPoolID, *curPool.ID) {
+			klog.V(10).Infof("%s gets unwanted backend pool %q for VMSS OR VMSS VM %q", msg, backendPoolID, resource)
+			found = true
+			newBackendPools = append(loadBalancerBackendAddressPools[:i], loadBalancerBackendAddressPools[i+1:]...)
+		}
+	}
+	if !found {
+		return false, nil
+	}
+	primaryIPConfig.LoadBalancerBackendAddressPools = &newBackendPools
+	return true, nil
+}
+
 // EnsureBackendPoolDeletedFromVMSets ensures the loadBalancer backendAddressPools deleted from the specified VMSS
 func (ss *ScaleSet) EnsureBackendPoolDeletedFromVMSets(vmssNamesMap map[string]bool, backendPoolID string) error {
 	vmssUpdaters := make([]func() error, 0, len(vmssNamesMap))
@@ -1819,40 +1784,23 @@ func (ss *ScaleSet) EnsureBackendPoolDeletedFromVMSets(vmssNamesMap map[string]b
 			continue
 		}
 		vmssNIC := *vmss.VirtualMachineProfile.NetworkProfile.NetworkInterfaceConfigurations
-		primaryNIC, err := ss.getPrimaryNetworkInterfaceConfigurationForScaleSet(vmssNIC, vmssName)
+		primaryNIC, err := getPrimaryNetworkInterfaceConfiguration(vmssNIC, vmssName)
 		if err != nil {
 			klog.Errorf("EnsureBackendPoolDeletedFromVMSets: failed to get the primary network interface config of the VMSS %s: %v", vmssName, err)
 			errors = append(errors, err)
 			continue
 		}
-		primaryIPConfig, err := getPrimaryIPConfigFromVMSSNetworkConfig(primaryNIC)
+		found, err := deleteBackendPoolFromIPConfig("EnsureBackendPoolDeletedFromVMSets", backendPoolID, vmssName, primaryNIC)
 		if err != nil {
-			klog.Errorf("EnsureBackendPoolDeletedFromVMSets: failed to the primary IP config from the VMSS %s's network config : %v", vmssName, err)
 			errors = append(errors, err)
 			continue
 		}
-		loadBalancerBackendAddressPools := []compute.SubResource{}
-		if primaryIPConfig.LoadBalancerBackendAddressPools != nil {
-			loadBalancerBackendAddressPools = *primaryIPConfig.LoadBalancerBackendAddressPools
-		}
-
-		var found bool
-		var newBackendPools []compute.SubResource
-		for i := len(loadBalancerBackendAddressPools) - 1; i >= 0; i-- {
-			curPool := loadBalancerBackendAddressPools[i]
-			if strings.EqualFold(backendPoolID, *curPool.ID) {
-				klog.V(10).Infof("EnsureBackendPoolDeletedFromVMSets gets unwanted backend pool %q for VMSS %s", backendPoolID, vmssName)
-				found = true
-				newBackendPools = append(loadBalancerBackendAddressPools[:i], loadBalancerBackendAddressPools[i+1:]...)
-			}
-		}
 		if !found {
 			continue
 		}
 
 		vmssUpdaters = append(vmssUpdaters, func() error {
 			// Compose a new vmss with added backendPoolID.
-			primaryIPConfig.LoadBalancerBackendAddressPools = &newBackendPools
 			newVMSS := compute.VirtualMachineScaleSet{
 				Location: vmss.Location,
 				VirtualMachineScaleSetProperties: &compute.VirtualMachineScaleSetProperties{
diff --git a/vendor/k8s.io/client-go/tools/cache/controller.go b/vendor/k8s.io/client-go/tools/cache/controller.go
index 96005ff58..0762da3be 100644
--- a/vendor/k8s.io/client-go/tools/cache/controller.go
+++ b/vendor/k8s.io/client-go/tools/cache/controller.go
@@ -353,6 +353,17 @@ func NewIndexerInformer(
 	return clientState, newInformer(lw, objType, resyncPeriod, h, clientState, nil)
 }
 
+// TransformFunc allows for transforming an object before it will be processed
+// and put into the controller cache and before the corresponding handlers will
+// be called on it.
+// TransformFunc (similarly to ResourceEventHandler functions) should be able
+// to correctly handle the tombstone of type cache.DeletedFinalStateUnknown
+//
+// The most common usage pattern is to clean-up some parts of the object to
+// reduce component memory usage if a given component doesn't care about them.
+// given controller doesn't care for them
+type TransformFunc func(interface{}) (interface{}, error)
+
 // NewTransformingInformer returns a Store and a controller for populating
 // the store while also providing event notifications. You should only used
 // the returned Store for Get/List operations; Add/Modify/Deletes will cause
@@ -400,11 +411,19 @@ func processDeltas(
 	// Object which receives event notifications from the given deltas
 	handler ResourceEventHandler,
 	clientState Store,
+	transformer TransformFunc,
 	deltas Deltas,
 ) error {
 	// from oldest to newest
 	for _, d := range deltas {
 		obj := d.Object
+		if transformer != nil {
+			var err error
+			obj, err = transformer(obj)
+			if err != nil {
+				return err
+			}
+		}
 
 		switch d.Type {
 		case Sync, Replaced, Added, Updated:
@@ -456,7 +475,6 @@ func newInformer(
 	fifo := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
 		KnownObjects:          clientState,
 		EmitDeltaTypeReplaced: true,
-		Transformer:           transformer,
 	})
 
 	cfg := &Config{
@@ -468,7 +486,7 @@ func newInformer(
 
 		Process: func(obj interface{}) error {
 			if deltas, ok := obj.(Deltas); ok {
-				return processDeltas(h, clientState, deltas)
+				return processDeltas(h, clientState, transformer, deltas)
 			}
 			return errors.New("object given as Process argument is not Deltas")
 		},
diff --git a/vendor/k8s.io/client-go/tools/cache/delta_fifo.go b/vendor/k8s.io/client-go/tools/cache/delta_fifo.go
index 84f3ab9ca..0c13a41f0 100644
--- a/vendor/k8s.io/client-go/tools/cache/delta_fifo.go
+++ b/vendor/k8s.io/client-go/tools/cache/delta_fifo.go
@@ -51,10 +51,6 @@ type DeltaFIFOOptions struct {
 	// When true, `Replaced` events will be sent for items passed to a Replace() call.
 	// When false, `Sync` events will be sent instead.
 	EmitDeltaTypeReplaced bool
-
-	// If set, will be called for objects before enqueueing them. Please
-	// see the comment on TransformFunc for details.
-	Transformer TransformFunc
 }
 
 // DeltaFIFO is like FIFO, but differs in two ways.  One is that the
@@ -133,32 +129,8 @@ type DeltaFIFO struct {
 	// emitDeltaTypeReplaced is whether to emit the Replaced or Sync
 	// DeltaType when Replace() is called (to preserve backwards compat).
 	emitDeltaTypeReplaced bool
-
-	// Called with every object if non-nil.
-	transformer TransformFunc
 }
 
-// TransformFunc allows for transforming an object before it will be processed.
-// TransformFunc (similarly to ResourceEventHandler functions) should be able
-// to correctly handle the tombstone of type cache.DeletedFinalStateUnknown.
-//
-// New in v1.27: In such cases, the contained object will already have gone
-// through the transform object separately (when it was added / updated prior
-// to the delete), so the TransformFunc can likely safely ignore such objects
-// (i.e., just return the input object).
-//
-// The most common usage pattern is to clean-up some parts of the object to
-// reduce component memory usage if a given component doesn't care about them.
-//
-// New in v1.27: unless the object is a DeletedFinalStateUnknown, TransformFunc
-// sees the object before any other actor, and it is now safe to mutate the
-// object in place instead of making a copy.
-//
-// Note that TransformFunc is called while inserting objects into the
-// notification queue and is therefore extremely performance sensitive; please
-// do not do anything that will take a long time.
-type TransformFunc func(interface{}) (interface{}, error)
-
 // DeltaType is the type of a change (addition, deletion, etc)
 type DeltaType string
 
@@ -255,7 +227,6 @@ func NewDeltaFIFOWithOptions(opts DeltaFIFOOptions) *DeltaFIFO {
 		knownObjects: opts.KnownObjects,
 
 		emitDeltaTypeReplaced: opts.EmitDeltaTypeReplaced,
-		transformer:           opts.Transformer,
 	}
 	f.cond.L = &f.lock
 	return f
@@ -440,21 +411,6 @@ func (f *DeltaFIFO) queueActionLocked(actionType DeltaType, obj interface{}) err
 	if err != nil {
 		return KeyError{obj, err}
 	}
-
-	// Every object comes through this code path once, so this is a good
-	// place to call the transform func.  If obj is a
-	// DeletedFinalStateUnknown tombstone, then the containted inner object
-	// will already have gone through the transformer, but we document that
-	// this can happen. In cases involving Replace(), such an object can
-	// come through multiple times.
-	if f.transformer != nil {
-		var err error
-		obj, err = f.transformer(obj)
-		if err != nil {
-			return err
-		}
-	}
-
 	oldDeltas := f.items[id]
 	newDeltas := append(oldDeltas, Delta{actionType, obj})
 	newDeltas = dedupDeltas(newDeltas)
@@ -610,11 +566,12 @@ func (f *DeltaFIFO) Pop(process PopProcessFunc) (interface{}, error) {
 // using the Sync or Replace DeltaType and then (2) it does some deletions.
 // In particular: for every pre-existing key K that is not the key of
 // an object in `list` there is the effect of
-// `Delete(DeletedFinalStateUnknown{K, O})` where O is the latest known
-// object of K. The pre-existing keys are those in the union set of the keys in
-// `f.items` and `f.knownObjects` (if not nil). The last known object for key K is
-// the one present in the last delta in `f.items`. If there is no delta for K
-// in `f.items`, it is the object in `f.knownObjects`
+// `Delete(DeletedFinalStateUnknown{K, O})` where O is current object
+// of K.  If `f.knownObjects == nil` then the pre-existing keys are
+// those in `f.items` and the current object of K is the `.Newest()`
+// of the Deltas associated with K.  Otherwise the pre-existing keys
+// are those listed by `f.knownObjects` and the current object of K is
+// what `f.knownObjects.GetByKey(K)` returns.
 func (f *DeltaFIFO) Replace(list []interface{}, _ string) error {
 	f.lock.Lock()
 	defer f.lock.Unlock()
@@ -638,54 +595,56 @@ func (f *DeltaFIFO) Replace(list []interface{}, _ string) error {
 		}
 	}
 
-	// Do deletion detection against objects in the queue
-	queuedDeletions := 0
-	for k, oldItem := range f.items {
-		if keys.Has(k) {
-			continue
-		}
-		// Delete pre-existing items not in the new list.
-		// This could happen if watch deletion event was missed while
-		// disconnected from apiserver.
-		var deletedObj interface{}
-		if n := oldItem.Newest(); n != nil {
-			deletedObj = n.Object
-
-			// if the previous object is a DeletedFinalStateUnknown, we have to extract the actual Object
-			if d, ok := deletedObj.(DeletedFinalStateUnknown); ok {
-				deletedObj = d.Obj
-			}
-		}
-		queuedDeletions++
-		if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil {
-			return err
-		}
-	}
-
-	if f.knownObjects != nil {
-		// Detect deletions for objects not present in the queue, but present in KnownObjects
-		knownKeys := f.knownObjects.ListKeys()
-		for _, k := range knownKeys {
+	if f.knownObjects == nil {
+		// Do deletion detection against our own list.
+		queuedDeletions := 0
+		for k, oldItem := range f.items {
 			if keys.Has(k) {
 				continue
 			}
-			if len(f.items[k]) > 0 {
-				continue
-			}
-
-			deletedObj, exists, err := f.knownObjects.GetByKey(k)
-			if err != nil {
-				deletedObj = nil
-				klog.Errorf("Unexpected error %v during lookup of key %v, placing DeleteFinalStateUnknown marker without object", err, k)
-			} else if !exists {
-				deletedObj = nil
-				klog.Infof("Key %v does not exist in known objects store, placing DeleteFinalStateUnknown marker without object", k)
+			// Delete pre-existing items not in the new list.
+			// This could happen if watch deletion event was missed while
+			// disconnected from apiserver.
+			var deletedObj interface{}
+			if n := oldItem.Newest(); n != nil {
+				deletedObj = n.Object
 			}
 			queuedDeletions++
 			if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil {
 				return err
 			}
 		}
+
+		if !f.populated {
+			f.populated = true
+			// While there shouldn't be any queued deletions in the initial
+			// population of the queue, it's better to be on the safe side.
+			f.initialPopulationCount = keys.Len() + queuedDeletions
+		}
+
+		return nil
+	}
+
+	// Detect deletions not already in the queue.
+	knownKeys := f.knownObjects.ListKeys()
+	queuedDeletions := 0
+	for _, k := range knownKeys {
+		if keys.Has(k) {
+			continue
+		}
+
+		deletedObj, exists, err := f.knownObjects.GetByKey(k)
+		if err != nil {
+			deletedObj = nil
+			klog.Errorf("Unexpected error %v during lookup of key %v, placing DeleteFinalStateUnknown marker without object", err, k)
+		} else if !exists {
+			deletedObj = nil
+			klog.Infof("Key %v does not exist in known objects store, placing DeleteFinalStateUnknown marker without object", k)
+		}
+		queuedDeletions++
+		if err := f.queueActionLocked(Deleted, DeletedFinalStateUnknown{k, deletedObj}); err != nil {
+			return err
+		}
 	}
 
 	if !f.populated {
diff --git a/vendor/k8s.io/client-go/tools/cache/shared_informer.go b/vendor/k8s.io/client-go/tools/cache/shared_informer.go
index 35ebd396c..9f42782d1 100644
--- a/vendor/k8s.io/client-go/tools/cache/shared_informer.go
+++ b/vendor/k8s.io/client-go/tools/cache/shared_informer.go
@@ -190,7 +190,10 @@ type SharedInformer interface {
 	//
 	// Must be set before starting the informer.
 	//
-	// Please see the comment on TransformFunc for more details.
+	// Note: Since the object given to the handler may be already shared with
+	//	other goroutines, it is advisable to copy the object being
+	//  transform before mutating it at all and returning the copy to prevent
+	//	data races.
 	SetTransform(handler TransformFunc) error
 }
 
@@ -401,7 +404,6 @@ func (s *sharedIndexInformer) Run(stopCh <-chan struct{}) {
 	fifo := NewDeltaFIFOWithOptions(DeltaFIFOOptions{
 		KnownObjects:          s.indexer,
 		EmitDeltaTypeReplaced: true,
-		Transformer:           s.transform,
 	})
 
 	cfg := &Config{
@@ -566,7 +568,7 @@ func (s *sharedIndexInformer) HandleDeltas(obj interface{}) error {
 	defer s.blockDeltas.Unlock()
 
 	if deltas, ok := obj.(Deltas); ok {
-		return processDeltas(s, s.indexer, deltas)
+		return processDeltas(s, s.indexer, s.transform, deltas)
 	}
 	return errors.New("object given as Process argument is not Deltas")
 }
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 1f3521c10..3ba937416 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -870,7 +870,7 @@ k8s.io/apiserver/plugin/pkg/audit/truncate
 k8s.io/apiserver/plugin/pkg/audit/webhook
 k8s.io/apiserver/plugin/pkg/authenticator/token/webhook
 k8s.io/apiserver/plugin/pkg/authorizer/webhook
-# k8s.io/client-go v0.24.13
+# k8s.io/client-go v0.24.12
 ## explicit; go 1.19
 k8s.io/client-go/applyconfigurations/admissionregistration/v1
 k8s.io/client-go/applyconfigurations/admissionregistration/v1beta1
@@ -1191,7 +1191,7 @@ k8s.io/component-base/metrics/testutil
 k8s.io/component-base/term
 k8s.io/component-base/traces
 k8s.io/component-base/version
-# k8s.io/component-helpers v0.24.13
+# k8s.io/component-helpers v0.24.12
 ## explicit; go 1.19
 k8s.io/component-helpers/node/util
 # k8s.io/controller-manager v0.24.12


Test patch:
diff --git a/pkg/provider/azure_standard_test.go b/pkg/provider/azure_standard_test.go
index 60ab033ba..65c1e1030 100644
--- a/pkg/provider/azure_standard_test.go
+++ b/pkg/provider/azure_standard_test.go
@@ -923,6 +923,22 @@ func TestGetBackendPoolName(t *testing.T) {
 	}
 }
 
+func TestIsBackendPoolIPv6(t *testing.T) {
+	testcases := []struct {
+		name           string
+		expectedIsIPv6 bool
+	}{
+		{"bp-IPv6", true},
+		{"bp-IPv4", false},
+		{"bp", false},
+		{"bp-ipv6", false},
+	}
+	for _, test := range testcases {
+		isIPv6 := isBackendPoolIPv6(test.name)
+		assert.Equal(t, test.expectedIsIPv6, isIPv6)
+	}
+}
+
 func TestGetStandardInstanceIDByNodeName(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
diff --git a/pkg/provider/azure_vmss_test.go b/pkg/provider/azure_vmss_test.go
index d56ecbece..6409b83ce 100644
--- a/pkg/provider/azure_vmss_test.go
+++ b/pkg/provider/azure_vmss_test.go
@@ -48,13 +48,15 @@ import (
 )
 
 const (
-	fakePrivateIP        = "10.240.0.10"
-	fakePublicIP         = "10.10.10.10"
-	testVMSSName         = "vmss"
-	testVMPowerState     = "PowerState/Running"
-	testLBBackendpoolID0 = "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Network/loadBalancers/lb/backendAddressPools/backendpool-0"
-	testLBBackendpoolID1 = "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Network/loadBalancers/lb/backendAddressPools/backendpool-1"
-	testLBBackendpoolID2 = "/subscriptions/sub/resourceGroups/rg1/providers/Microsoft.Network/loadBalancers/lb/backendAddressPools/backendpool-2"
+	fakePrivateIP          = "10.240.0.10"
+	fakePublicIP           = "10.10.10.10"
+	testVMSSName           = "vmss"
+	testVMPowerState       = "PowerState/Running"
+	testLBBackendpoolID0   = "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Network/loadBalancers/lb/backendAddressPools/backendpool-0"
+	testLBBackendpoolID0v6 = "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Network/loadBalancers/lb/backendAddressPools/backendpool-0" + "-" + v6Suffix
+	testLBBackendpoolID1   = "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Network/loadBalancers/lb/backendAddressPools/backendpool-1"
+	testLBBackendpoolID1v6 = "/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Network/loadBalancers/lb/backendAddressPools/backendpool-1" + "-" + v6Suffix
+	testLBBackendpoolID2   = "/subscriptions/sub/resourceGroups/rg1/providers/Microsoft.Network/loadBalancers/lb/backendAddressPools/backendpool-2"
 )
 
 func buildTestVMSSWithLB(name, namePrefix string, lbBackendpoolIDs []string, ipv6 bool) compute.VirtualMachineScaleSet {
@@ -144,36 +146,30 @@ func buildTestVirtualMachineEnv(ss *Cloud, scaleSetName, zone string, faultDomai
 				VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
 					Primary:                         pointer.Bool(true),
 					LoadBalancerBackendAddressPools: &[]compute.SubResource{{ID: pointer.String(testLBBackendpoolID0)}},
+					PrivateIPAddressVersion:         compute.IPv4,
 				},
 			},
 		}
+		if isIPv6 {
+			ipConfigurations = append(ipConfigurations, compute.VirtualMachineScaleSetIPConfiguration{
+				Name: pointer.String("ipconfigv6"),
+				VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+					Primary:                         pointer.Bool(false),
+					LoadBalancerBackendAddressPools: &[]compute.SubResource{{ID: pointer.String(testLBBackendpoolID0v6)}},
+					PrivateIPAddressVersion:         compute.IPv6,
+				},
+			})
+		}
 		networkConfigurations := []compute.VirtualMachineScaleSetNetworkConfiguration{
 			{
-				Name: pointer.String("ipconfig1"),
+				Name: pointer.String("vmss-nic"),
 				ID:   pointer.String("fakeNetworkConfiguration"),
 				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
 					IPConfigurations: &ipConfigurations,
+					Primary:          pointer.Bool(true),
 				},
 			},
 		}
-		if isIPv6 {
-			networkConfigurations = append(networkConfigurations, compute.VirtualMachineScaleSetNetworkConfiguration{
-				Name: pointer.String("ipconfig1v6"),
-				ID:   pointer.String("fakeNetworkConfigurationIPv6"),
-				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
-					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
-						{
-							Name: pointer.String("ipconfig1"),
-							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
-								Primary:                         pointer.Bool(false),
-								LoadBalancerBackendAddressPools: &[]compute.SubResource{{ID: pointer.String(testLBBackendpoolID0)}},
-								PrivateIPAddressVersion:         compute.IPv6,
-							},
-						},
-					},
-				},
-			})
-		}
 
 		vmssVM := compute.VirtualMachineScaleSetVM{
 			VirtualMachineScaleSetVMProperties: &compute.VirtualMachineScaleSetVMProperties{
@@ -1720,19 +1716,16 @@ func TestGetVMSetNames(t *testing.T) {
 	}
 }
 
-func TestGetPrimaryNetworkInterfaceConfigurationForScaleSet(t *testing.T) {
+func TestGetPrimaryNetworkInterfaceConfiguration(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
-	ss, err := NewTestScaleSet(ctrl)
-	assert.NoError(t, err, "unexpected error when creating test VMSS")
-
 	networkConfigs := []compute.VirtualMachineScaleSetNetworkConfiguration{
 		{Name: pointer.String("config-0")},
 	}
-	config, err := ss.getPrimaryNetworkInterfaceConfigurationForScaleSet(networkConfigs, testVMSSName)
-	assert.Nil(t, err, "getPrimaryNetworkInterfaceConfigurationForScaleSet should return the correct network config")
-	assert.Equal(t, &networkConfigs[0], config, "getPrimaryNetworkInterfaceConfigurationForScaleSet should return the correct network config")
+	config, err := getPrimaryNetworkInterfaceConfiguration(networkConfigs, testVMSSName)
+	assert.Nil(t, err, "getPrimaryNetworkInterfaceConfiguration should return the correct network config")
+	assert.Equal(t, &networkConfigs[0], config, "getPrimaryNetworkInterfaceConfiguration should return the correct network config")
 
 	networkConfigs = []compute.VirtualMachineScaleSetNetworkConfiguration{
 		{
@@ -1748,9 +1741,9 @@ func TestGetPrimaryNetworkInterfaceConfigurationForScaleSet(t *testing.T) {
 			},
 		},
 	}
-	config, err = ss.getPrimaryNetworkInterfaceConfigurationForScaleSet(networkConfigs, testVMSSName)
-	assert.Nil(t, err, "getPrimaryNetworkInterfaceConfigurationForScaleSet should return the correct network config")
-	assert.Equal(t, &networkConfigs[1], config, "getPrimaryNetworkInterfaceConfigurationForScaleSet should return the correct network config")
+	config, err = getPrimaryNetworkInterfaceConfiguration(networkConfigs, testVMSSName)
+	assert.Nil(t, err, "getPrimaryNetworkInterfaceConfiguration should return the correct network config")
+	assert.Equal(t, &networkConfigs[1], config, "getPrimaryNetworkInterfaceConfiguration should return the correct network config")
 
 	networkConfigs = []compute.VirtualMachineScaleSetNetworkConfiguration{
 		{
@@ -1766,106 +1759,403 @@ func TestGetPrimaryNetworkInterfaceConfigurationForScaleSet(t *testing.T) {
 			},
 		},
 	}
-	config, err = ss.getPrimaryNetworkInterfaceConfigurationForScaleSet(networkConfigs, testVMSSName)
-	assert.Equal(t, fmt.Errorf("failed to find a primary network configuration for the scale set \"vmss\""), err, "getPrimaryNetworkInterfaceConfigurationForScaleSet should report an error if there is no primary nic")
-	assert.Nil(t, config, "getPrimaryNetworkInterfaceConfigurationForScaleSet should report an error if there is no primary nic")
+	config, err = getPrimaryNetworkInterfaceConfiguration(networkConfigs, testVMSSName)
+	assert.Equal(t, fmt.Errorf("failed to find a primary network configuration for the VMSS VM or VMSS \"vmss\""), err, "getPrimaryNetworkInterfaceConfiguration should report an error if there is no primary nic")
+	assert.Nil(t, config, "getPrimaryNetworkInterfaceConfiguration should report an error if there is no primary nic")
 }
 
 func TestGetPrimaryIPConfigFromVMSSNetworkConfig(t *testing.T) {
-	config := &compute.VirtualMachineScaleSetNetworkConfiguration{
-		VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
-			IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
-				{
-					Name: pointer.String("config-0"),
+	testcases := []struct {
+		desc             string
+		netConfig        *compute.VirtualMachineScaleSetNetworkConfiguration
+		backendPoolID    string
+		expectedIPConfig *compute.VirtualMachineScaleSetIPConfiguration
+		expectedErr      error
+	}{
+		{
+			desc: "only one IPv4 without primary (should not exist)",
+			netConfig: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+						},
+					},
 				},
 			},
+			backendPoolID: testLBBackendpoolID0,
+			expectedIPConfig: &compute.VirtualMachineScaleSetIPConfiguration{
+				Name: pointer.String("config-0"),
+			},
 		},
-	}
-
-	ipConfig, err := getPrimaryIPConfigFromVMSSNetworkConfig(config)
-	assert.Nil(t, err, "getPrimaryIPConfigFromVMSSNetworkConfig should return the correct IP config")
-	assert.Equal(t, (*config.IPConfigurations)[0], *ipConfig, "getPrimaryIPConfigFromVMSSNetworkConfig should return the correct IP config")
-
-	config = &compute.VirtualMachineScaleSetNetworkConfiguration{
-		VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
-			IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
-				{
-					Name: pointer.String("config-0"),
-					VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
-						Primary: pointer.Bool(false),
+		{
+			desc: "two IPv4 but one with primary",
+			netConfig: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(false),
+							},
+						},
+						{
+							Name: pointer.String("config-1"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(true),
+							},
+						},
 					},
 				},
-				{
-					Name: pointer.String("config-1"),
-					VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
-						Primary: pointer.Bool(true),
+			},
+			backendPoolID: testLBBackendpoolID0,
+			expectedIPConfig: &compute.VirtualMachineScaleSetIPConfiguration{
+				Name: pointer.String("config-1"),
+				VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+					Primary: pointer.Bool(true),
+				},
+			},
+		},
+		{
+			desc: "multiple IPv4 without primary",
+			netConfig: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(false),
+							},
+						},
+						{
+							Name: pointer.String("config-1"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(false),
+							},
+						},
 					},
 				},
 			},
+			backendPoolID: testLBBackendpoolID0,
+			expectedErr:   fmt.Errorf("failed to find a primary IP configuration (IPv6=false) for the VMSS VM or VMSS \"vmss-config-0\""),
 		},
-	}
-
-	ipConfig, err = getPrimaryIPConfigFromVMSSNetworkConfig(config)
-	assert.Nil(t, err, "getPrimaryIPConfigFromVMSSNetworkConfig should return the correct IP config")
-	assert.Equal(t, (*config.IPConfigurations)[1], *ipConfig, "getPrimaryIPConfigFromVMSSNetworkConfig should return the correct IP config")
-
-	config = &compute.VirtualMachineScaleSetNetworkConfiguration{
-		VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
-			IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
-				{
-					Name: pointer.String("config-0"),
-					VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
-						Primary: pointer.Bool(false),
+		{
+			desc: "dualstack for IPv4",
+			netConfig: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								PrivateIPAddressVersion: compute.IPv4,
+								Primary:                 pointer.Bool(true),
+							},
+						},
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								PrivateIPAddressVersion: compute.IPv6,
+							},
+						},
 					},
 				},
-				{
-					Name: pointer.String("config-1"),
-					VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
-						Primary: pointer.Bool(false),
+			},
+			backendPoolID: testLBBackendpoolID0,
+			expectedIPConfig: &compute.VirtualMachineScaleSetIPConfiguration{
+				Name: pointer.String("config-0"),
+				VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+					PrivateIPAddressVersion: compute.IPv4,
+					Primary:                 pointer.Bool(true),
+				},
+			},
+		},
+		{
+			desc: "dualstack for IPv6",
+			netConfig: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								PrivateIPAddressVersion: compute.IPv4,
+								Primary:                 pointer.Bool(true),
+							},
+						},
+						{
+							Name: pointer.String("config-0-IPv6"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								PrivateIPAddressVersion: compute.IPv6,
+							},
+						},
 					},
 				},
 			},
+			backendPoolID: testLBBackendpoolID0v6,
+			expectedIPConfig: &compute.VirtualMachineScaleSetIPConfiguration{
+				Name: pointer.String("config-0-IPv6"),
+				VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+					PrivateIPAddressVersion: compute.IPv6,
+				},
+			},
 		},
 	}
 
-	ipConfig, err = getPrimaryIPConfigFromVMSSNetworkConfig(config)
-	assert.Equal(t, err, fmt.Errorf("failed to find a primary IP configuration"), "getPrimaryIPConfigFromVMSSNetworkConfig should report an error if there is no primary IP config")
-	assert.Nil(t, ipConfig, "getPrimaryIPConfigFromVMSSNetworkConfig should report an error if there is no primary IP config")
+	for _, tc := range testcases {
+		t.Run(tc.desc, func(t *testing.T) {
+			ipConfig, err := getPrimaryIPConfigFromVMSSNetworkConfig(tc.netConfig, tc.backendPoolID, "vmss-config-0")
+			assert.Equal(t, tc.expectedErr, err)
+			assert.Equal(t, tc.expectedIPConfig, ipConfig)
+		})
+	}
 }
 
-func TestGetConfigForScaleSetByIPFamily(t *testing.T) {
-	ctrl := gomock.NewController(t)
-	defer ctrl.Finish()
-
-	ss, err := NewTestScaleSet(ctrl)
-	assert.NoError(t, err, "unexpected error when creating test VMSS")
-
-	config := &compute.VirtualMachineScaleSetNetworkConfiguration{
-		VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
-			IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
-				{
-					Name: pointer.String("config-0"),
-					VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
-						PrivateIPAddressVersion: compute.IPv4,
+func TestDeleteBackendPoolFromIPConfig(t *testing.T) {
+	testcases := []struct {
+		desc               string
+		backendPoolID      string
+		primaryNIC         *compute.VirtualMachineScaleSetNetworkConfiguration
+		expectedPrimaryNIC *compute.VirtualMachineScaleSetNetworkConfiguration
+		expectedFound      bool
+		expectedErr        error
+	}{
+		{
+			desc:          "delete backend pool from ip config",
+			backendPoolID: "backendpool-0",
+			primaryNIC: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(true),
+								LoadBalancerBackendAddressPools: &[]compute.SubResource{
+									{
+										ID: pointer.String("backendpool-0"),
+									},
+									{
+										ID: pointer.String("backendpool-1"),
+									},
+								},
+							},
+						},
 					},
 				},
-				{
-					Name: pointer.String("config-0"),
-					VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
-						PrivateIPAddressVersion: compute.IPv6,
+			},
+			expectedPrimaryNIC: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(true),
+								LoadBalancerBackendAddressPools: &[]compute.SubResource{
+									{
+										ID: pointer.String("backendpool-1"),
+									},
+								},
+							},
+						},
 					},
 				},
 			},
+			expectedFound: true,
+		},
+		{
+			desc:          "backend pool not found",
+			backendPoolID: "backendpool-0",
+			primaryNIC: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(true),
+								LoadBalancerBackendAddressPools: &[]compute.SubResource{
+									{
+										ID: pointer.String("backendpool-1"),
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			expectedPrimaryNIC: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(true),
+								LoadBalancerBackendAddressPools: &[]compute.SubResource{
+									{
+										ID: pointer.String("backendpool-1"),
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			expectedFound: false,
+		},
+		{
+			desc:          "delete backend pool from ip config IPv6",
+			backendPoolID: "backendpool-0-IPv6",
+			primaryNIC: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(true),
+								LoadBalancerBackendAddressPools: &[]compute.SubResource{
+									{
+										ID: pointer.String("backendpool-1"),
+									},
+								},
+							},
+						},
+						{
+							Name: pointer.String("config-1"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(false),
+								LoadBalancerBackendAddressPools: &[]compute.SubResource{
+									{
+										ID: pointer.String("backendpool-0-IPv6"),
+									},
+								},
+								PrivateIPAddressVersion: compute.IPv6,
+							},
+						},
+					},
+				},
+			},
+			expectedPrimaryNIC: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(true),
+								LoadBalancerBackendAddressPools: &[]compute.SubResource{
+									{
+										ID: pointer.String("backendpool-1"),
+									},
+								},
+							},
+						},
+						{
+							Name: pointer.String("config-1"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary:                         pointer.Bool(false),
+								LoadBalancerBackendAddressPools: &[]compute.SubResource{},
+								PrivateIPAddressVersion:         compute.IPv6,
+							},
+						},
+					},
+				},
+			},
+			expectedFound: true,
+		},
+		{
+			desc:          "primary IP config not found IPv4",
+			backendPoolID: "backendpool-0",
+			primaryNIC: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(false),
+							},
+						},
+						{
+							Name: pointer.String("config-1"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(false),
+							},
+						},
+					},
+				},
+			},
+			expectedPrimaryNIC: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(false),
+							},
+						},
+						{
+							Name: pointer.String("config-1"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary: pointer.Bool(false),
+							},
+						},
+					},
+				},
+			},
+			expectedFound: false,
+			expectedErr:   fmt.Errorf("failed to find a primary IP configuration (IPv6=false) for the VMSS VM or VMSS \"test-resource\""),
+		},
+		{
+			desc:          "primary IP config not found IPv6",
+			backendPoolID: "backendpool-0-IPv6",
+			primaryNIC: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary:                 pointer.Bool(true),
+								PrivateIPAddressVersion: compute.IPv4,
+							},
+						},
+						{
+							Name: pointer.String("config-1"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary:                 pointer.Bool(false),
+								PrivateIPAddressVersion: compute.IPv4,
+							},
+						},
+					},
+				},
+			},
+			expectedPrimaryNIC: &compute.VirtualMachineScaleSetNetworkConfiguration{
+				VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
+					IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
+						{
+							Name: pointer.String("config-0"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary:                 pointer.Bool(true),
+								PrivateIPAddressVersion: compute.IPv4,
+							},
+						},
+						{
+							Name: pointer.String("config-1"),
+							VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
+								Primary:                 pointer.Bool(false),
+								PrivateIPAddressVersion: compute.IPv4,
+							},
+						},
+					},
+				},
+			},
+			expectedFound: false,
+			expectedErr:   fmt.Errorf("failed to find a primary IP configuration (IPv6=true) for the VMSS VM or VMSS \"test-resource\""),
 		},
 	}
 
-	ipConfig, err := ss.getConfigForScaleSetByIPFamily(config, "vmss-vm-000000", true)
-	assert.Nil(t, err, "getConfigForScaleSetByIPFamily should find the IPV6 config")
-	assert.Equal(t, (*config.IPConfigurations)[1], *ipConfig, "getConfigForScaleSetByIPFamily should find the IPV6 config")
-
-	ipConfig, err = ss.getConfigForScaleSetByIPFamily(config, "vmss-vm-000000", false)
-	assert.Nil(t, err, "getConfigForScaleSetByIPFamily should find the IPV4 config")
-	assert.Equal(t, (*config.IPConfigurations)[0], *ipConfig, "getConfigForScaleSetByIPFamily should find the IPV4 config")
+	for _, tc := range testcases {
+		t.Run(tc.desc, func(t *testing.T) {
+			found, err := deleteBackendPoolFromIPConfig("test", tc.backendPoolID, "test-resource", tc.primaryNIC)
+			assert.Equal(t, tc.expectedFound, found)
+			assert.Equal(t, tc.expectedErr, err)
+			assert.Equal(t, tc.expectedPrimaryNIC, tc.primaryNIC)
+		})
+	}
 }
 
 func TestEnsureHostInPool(t *testing.T) {
@@ -1943,7 +2233,7 @@ func TestEnsureHostInPool(t *testing.T) {
 					NetworkProfileConfiguration: &compute.VirtualMachineScaleSetVMNetworkProfileConfiguration{
 						NetworkInterfaceConfigurations: &[]compute.VirtualMachineScaleSetNetworkConfiguration{
 							{
-								Name: pointer.String("ipconfig1"),
+								Name: pointer.String("vmss-nic"),
 								ID:   pointer.String("fakeNetworkConfiguration"),
 								VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
 									IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
@@ -1959,9 +2249,11 @@ func TestEnsureHostInPool(t *testing.T) {
 														ID: pointer.String("/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Network/loadBalancers/lb-internal/backendAddressPools/backendpool-1"),
 													},
 												},
+												PrivateIPAddressVersion: compute.IPv4,
 											},
 										},
 									},
+									Primary: pointer.Bool(true),
 								},
 							},
 						},
@@ -2141,10 +2433,10 @@ func TestEnsureVMSSInPool(t *testing.T) {
 				},
 			},
 			isBasicLB:       false,
-			backendPoolID:   testLBBackendpoolID1,
+			backendPoolID:   testLBBackendpoolID1v6,
 			clusterIP:       "fd00::e68b",
 			expectedPutVMSS: false,
-			expectedErr:     fmt.Errorf("failed to find a IPconfiguration(IPv6=true) for the scale set VM \"\""),
+			expectedErr:     fmt.Errorf("failed to find a primary IP configuration (IPv6=true) for the VMSS VM or VMSS \"vmss\""),
 		},
 		{
 			description: "ensureVMSSInPool should update the VMSS correctly for IPv6",
@@ -2156,7 +2448,7 @@ func TestEnsureVMSSInPool(t *testing.T) {
 				},
 			},
 			isBasicLB:       false,
-			backendPoolID:   testLBBackendpoolID1,
+			backendPoolID:   testLBBackendpoolID1v6,
 			setIPv6Config:   true,
 			clusterIP:       "fd00::e68b",
 			expectedPutVMSS: true,
@@ -2209,7 +2501,7 @@ func TestEnsureVMSSInPool(t *testing.T) {
 			expectedGetInstanceID: "invalid",
 		},
 		{
-			description: "ensureVMSSInPool should report an error if failed to get instsance ID",
+			description: "ensureVMSSInPool should report an error if failed to get instance ID",
 			nodes: []*v1.Node{
 				{},
 			},
@@ -2222,41 +2514,43 @@ func TestEnsureVMSSInPool(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		ss, err := NewTestScaleSet(ctrl)
-		assert.NoError(t, err, test.description)
+		t.Run(test.description, func(t *testing.T) {
+			ss, err := NewTestScaleSet(ctrl)
+			assert.NoError(t, err, test.description)
 
-		if !test.isBasicLB {
-			ss.LoadBalancerSku = consts.LoadBalancerSkuStandard
-		}
+			if !test.isBasicLB {
+				ss.LoadBalancerSku = consts.LoadBalancerSkuStandard
+			}
 
-		expectedVMSS := buildTestVMSSWithLB(testVMSSName, "vmss-vm-", []string{testLBBackendpoolID0}, test.setIPv6Config)
-		if test.isVMSSDeallocating {
-			expectedVMSS.ProvisioningState = pointer.String(consts.VirtualMachineScaleSetsDeallocating)
-		}
-		if test.isVMSSNilNICConfig {
-			expectedVMSS.VirtualMachineProfile.NetworkProfile.NetworkInterfaceConfigurations = nil
-		}
-		mockVMSSClient := ss.cloud.VirtualMachineScaleSetsClient.(*mockvmssclient.MockInterface)
-		mockVMSSClient.EXPECT().List(gomock.Any(), ss.ResourceGroup).Return([]compute.VirtualMachineScaleSet{expectedVMSS}, nil).AnyTimes()
-		vmssPutTimes := 0
-		if test.expectedPutVMSS {
-			vmssPutTimes = 1
-			mockVMSSClient.EXPECT().Get(gomock.Any(), ss.ResourceGroup, testVMSSName).Return(expectedVMSS, nil)
-		}
-		mockVMSSClient.EXPECT().CreateOrUpdate(gomock.Any(), ss.ResourceGroup, testVMSSName, gomock.Any()).Return(nil).Times(vmssPutTimes)
+			expectedVMSS := buildTestVMSSWithLB(testVMSSName, "vmss-vm-", []string{testLBBackendpoolID0}, test.setIPv6Config)
+			if test.isVMSSDeallocating {
+				expectedVMSS.ProvisioningState = pointer.String(consts.VirtualMachineScaleSetsDeallocating)
+			}
+			if test.isVMSSNilNICConfig {
+				expectedVMSS.VirtualMachineProfile.NetworkProfile.NetworkInterfaceConfigurations = nil
+			}
+			mockVMSSClient := ss.cloud.VirtualMachineScaleSetsClient.(*mockvmssclient.MockInterface)
+			mockVMSSClient.EXPECT().List(gomock.Any(), ss.ResourceGroup).Return([]compute.VirtualMachineScaleSet{expectedVMSS}, nil).AnyTimes()
+			vmssPutTimes := 0
+			if test.expectedPutVMSS {
+				vmssPutTimes = 1
+				mockVMSSClient.EXPECT().Get(gomock.Any(), ss.ResourceGroup, testVMSSName).Return(expectedVMSS, nil)
+			}
+			mockVMSSClient.EXPECT().CreateOrUpdate(gomock.Any(), ss.ResourceGroup, testVMSSName, gomock.Any()).Return(nil).Times(vmssPutTimes)
 
-		expectedVMSSVMs, _, _ := buildTestVirtualMachineEnv(ss.cloud, testVMSSName, "", 0, []string{"vmss-vm-000000"}, "", test.setIPv6Config)
-		mockVMSSVMClient := ss.cloud.VirtualMachineScaleSetVMsClient.(*mockvmssvmclient.MockInterface)
-		mockVMSSVMClient.EXPECT().List(gomock.Any(), ss.ResourceGroup, testVMSSName, gomock.Any()).Return(expectedVMSSVMs, nil).AnyTimes()
+			expectedVMSSVMs, _, _ := buildTestVirtualMachineEnv(ss.cloud, testVMSSName, "", 0, []string{"vmss-vm-000000"}, "", test.setIPv6Config)
+			mockVMSSVMClient := ss.cloud.VirtualMachineScaleSetVMsClient.(*mockvmssvmclient.MockInterface)
+			mockVMSSVMClient.EXPECT().List(gomock.Any(), ss.ResourceGroup, testVMSSName, gomock.Any()).Return(expectedVMSSVMs, nil).AnyTimes()
 
-		if test.expectedGetInstanceID != "" {
-			mockVMSet := NewMockVMSet(ctrl)
-			mockVMSet.EXPECT().GetInstanceIDByNodeName(gomock.Any()).Return(test.expectedGetInstanceID, test.getInstanceIDErr)
-			ss.VMSet = mockVMSet
-		}
+			if test.expectedGetInstanceID != "" {
+				mockVMSet := NewMockVMSet(ctrl)
+				mockVMSet.EXPECT().GetInstanceIDByNodeName(gomock.Any()).Return(test.expectedGetInstanceID, test.getInstanceIDErr)
+				ss.VMSet = mockVMSet
+			}
 
-		err = ss.ensureVMSSInPool(&v1.Service{Spec: v1.ServiceSpec{ClusterIP: test.clusterIP}}, test.nodes, test.backendPoolID, test.vmSetName)
-		assert.Equal(t, test.expectedErr, err, test.description+", but an error occurs")
+			err = ss.ensureVMSSInPool(&v1.Service{Spec: v1.ServiceSpec{ClusterIP: test.clusterIP}}, test.nodes, test.backendPoolID, test.vmSetName)
+			assert.Equal(t, test.expectedErr, err, test.description+", but an error occurs")
+		})
 	}
 }
 
@@ -2351,7 +2645,7 @@ func TestEnsureHostsInPool(t *testing.T) {
 	}
 }
 
-func TestEnsureBackendPoolDeletedFromNode(t *testing.T) {
+func TestEnsureBackendPoolDeletedFromNodeCommon(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
@@ -2393,7 +2687,7 @@ func TestEnsureBackendPoolDeletedFromNode(t *testing.T) {
 					NetworkProfileConfiguration: &compute.VirtualMachineScaleSetVMNetworkProfileConfiguration{
 						NetworkInterfaceConfigurations: &[]compute.VirtualMachineScaleSetNetworkConfiguration{
 							{
-								Name: pointer.String("ipconfig1"),
+								Name: pointer.String("vmss-nic"),
 								ID:   pointer.String("fakeNetworkConfiguration"),
 								VirtualMachineScaleSetNetworkConfigurationProperties: &compute.VirtualMachineScaleSetNetworkConfigurationProperties{
 									IPConfigurations: &[]compute.VirtualMachineScaleSetIPConfiguration{
@@ -2402,9 +2696,11 @@ func TestEnsureBackendPoolDeletedFromNode(t *testing.T) {
 											VirtualMachineScaleSetIPConfigurationProperties: &compute.VirtualMachineScaleSetIPConfigurationProperties{
 												Primary:                         pointer.Bool(true),
 												LoadBalancerBackendAddressPools: &[]compute.SubResource{},
+												PrivateIPAddressVersion:         compute.IPv4,
 											},
 										},
 									},
+									Primary: pointer.Bool(true),
 								},
 							},
 						},
@@ -2415,26 +2711,28 @@ func TestEnsureBackendPoolDeletedFromNode(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		ss, err := NewTestScaleSet(ctrl)
-		assert.NoError(t, err, test.description)
+		t.Run(test.description, func(t *testing.T) {
+			ss, err := NewTestScaleSet(ctrl)
+			assert.NoError(t, err, test.description)
 
-		expectedVMSS := buildTestVMSS(testVMSSName, "vmss-vm-")
-		mockVMSSClient := ss.cloud.VirtualMachineScaleSetsClient.(*mockvmssclient.MockInterface)
-		mockVMSSClient.EXPECT().List(gomock.Any(), ss.ResourceGroup).Return([]compute.VirtualMachineScaleSet{expectedVMSS}, nil).AnyTimes()
+			expectedVMSS := buildTestVMSS(testVMSSName, "vmss-vm-")
+			mockVMSSClient := ss.cloud.VirtualMachineScaleSetsClient.(*mockvmssclient.MockInterface)
+			mockVMSSClient.EXPECT().List(gomock.Any(), ss.ResourceGroup).Return([]compute.VirtualMachineScaleSet{expectedVMSS}, nil).AnyTimes()
 
-		expectedVMSSVMs, _, _ := buildTestVirtualMachineEnv(ss.cloud, testVMSSName, "", 0, []string{"vmss-vm-000000"}, "", false)
-		if test.isNilVMNetworkConfigs {
-			expectedVMSSVMs[0].NetworkProfileConfiguration.NetworkInterfaceConfigurations = nil
-		}
-		mockVMSSVMClient := ss.cloud.VirtualMachineScaleSetVMsClient.(*mockvmssvmclient.MockInterface)
-		mockVMSSVMClient.EXPECT().List(gomock.Any(), ss.ResourceGroup, testVMSSName, gomock.Any()).Return(expectedVMSSVMs, nil).AnyTimes()
+			expectedVMSSVMs, _, _ := buildTestVirtualMachineEnv(ss.cloud, testVMSSName, "", 0, []string{"vmss-vm-000000"}, "", false)
+			if test.isNilVMNetworkConfigs {
+				expectedVMSSVMs[0].NetworkProfileConfiguration.NetworkInterfaceConfigurations = nil
+			}
+			mockVMSSVMClient := ss.cloud.VirtualMachineScaleSetVMsClient.(*mockvmssvmclient.MockInterface)
+			mockVMSSVMClient.EXPECT().List(gomock.Any(), ss.ResourceGroup, testVMSSName, gomock.Any()).Return(expectedVMSSVMs, nil).AnyTimes()
 
-		nodeResourceGroup, ssName, instanceID, vm, err := ss.ensureBackendPoolDeletedFromNode(test.nodeName, test.backendpoolID)
-		assert.Equal(t, test.expectedErr, err, test.description+", but an error occurs")
-		assert.Equal(t, test.expectedNodeResourceGroup, nodeResourceGroup, test.description)
-		assert.Equal(t, test.expectedVMSSName, ssName, test.description)
-		assert.Equal(t, test.expectedInstanceID, instanceID, test.description)
-		assert.Equal(t, test.expectedVMSSVM, vm, test.description)
+			nodeResourceGroup, ssName, instanceID, vm, err := ss.ensureBackendPoolDeletedFromNode(test.nodeName, test.backendpoolID)
+			assert.Equal(t, test.expectedErr, err)
+			assert.Equal(t, test.expectedNodeResourceGroup, nodeResourceGroup)
+			assert.Equal(t, test.expectedVMSSName, ssName)
+			assert.Equal(t, test.expectedInstanceID, instanceID)
+			assert.Equal(t, test.expectedVMSSVM, vm)
+		})
 	}
 }