Repository: kubernetes-sigs/cloud-provider-azure
Pull request #7593 (2024-11-14 17:48:41)

build(deps): bump oss/go/microsoft/golang from `ea0b733` to `bd0f197`
Bumps oss/go/microsoft/golang from `ea0b733` to `bd0f197`.


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=oss/go/microsoft/golang&package-manager=docker&previous-version=1.23-bullseye&new-version=1.23-bullseye)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>

k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

Approval requirements bypassed by manually added approval.

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/cloud-provider-azure/pull/7593#" title="Author self-approved">dependabot[bot]</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fcloud-provider-azure).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/cloud-provider-azure/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["nilo19"]} -->
k8s-ci-robot: Hi @dependabot[bot]. Thanks for your PR.

I'm waiting for a [kubernetes-sigs](https://github.com/orgs/kubernetes-sigs/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://git.k8s.io/community/community-membership.md#member) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fcloud-provider-azure).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>


Patch:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 8d3d496e3..c86f9b235 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -70,7 +70,7 @@ jobs:
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@396bb3e45325a47dd9ef434068033c6d5bb0d11a # v3.27.3
+      uses: github/codeql-action/init@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4
       with:
         languages: ${{ matrix.language }}
         # If you wish to specify custom queries, you can do so here or in a config file.
@@ -92,4 +92,4 @@ jobs:
         format: golang
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@396bb3e45325a47dd9ef434068033c6d5bb0d11a # v3.27.3
+      uses: github/codeql-action/analyze@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 2e3a1066f..57eb6a5ff 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -72,6 +72,6 @@ jobs:
 
       # Upload the results to GitHub's code scanning dashboard.
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@396bb3e45325a47dd9ef434068033c6d5bb0d11a # v3.27.3
+        uses: github/codeql-action/upload-sarif@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4
         with:
           sarif_file: results.sarif
diff --git a/.github/workflows/trivy.yaml b/.github/workflows/trivy.yaml
index f49913b7d..708ca74b8 100644
--- a/.github/workflows/trivy.yaml
+++ b/.github/workflows/trivy.yaml
@@ -67,7 +67,7 @@ jobs:
         env:
           TRIVY_SKIP_DB_UPDATE: true
       - name: Upload Trivy scan results to GitHub Security tab
-        uses: github/codeql-action/upload-sarif@396bb3e45325a47dd9ef434068033c6d5bb0d11a # v3.27.3
+        uses: github/codeql-action/upload-sarif@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4
         with:
           sarif_file: 'trivy-ccm-results.sarif'
           category: azure-cloud-controller-manager-image
@@ -83,7 +83,7 @@ jobs:
         env:
           TRIVY_SKIP_DB_UPDATE: true
       - name: Upload Trivy scan results to GitHub Security tab
-        uses: github/codeql-action/upload-sarif@396bb3e45325a47dd9ef434068033c6d5bb0d11a # v3.27.3
+        uses: github/codeql-action/upload-sarif@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4
         with:
           sarif_file: 'trivy-cnm-linux-results.sarif'
           category: azure-cloud-node-manager-linux-image
@@ -99,7 +99,7 @@ jobs:
         env:
           TRIVY_SKIP_DB_UPDATE: true
       - name: Upload Trivy scan results to GitHub Security tab
-        uses: github/codeql-action/upload-sarif@396bb3e45325a47dd9ef434068033c6d5bb0d11a # v3.27.3
+        uses: github/codeql-action/upload-sarif@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f # v3.27.4
         with:
           sarif_file: 'trivy-health-probe-proxy-linux-results.sarif'
           category: health-probe-proxy-linux-image
diff --git a/Dockerfile b/Dockerfile
index 67149eb70..447a9401b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -14,7 +14,7 @@
 
 # syntax=docker/dockerfile:1
 
-FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.23-bullseye@sha256:ea0b7339cbba81c9973bed366b7e7f5e304936acb931fdf51dcfd921dfe02984 AS builder
+FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.23-bullseye@sha256:bd0f1973513fe7f8e7f025c6461e7cfdbbe579867802136999e5e78b54655f81 AS builder
 
 ARG ENABLE_GIT_COMMAND=true
 ARG ARCH=amd64
diff --git a/cloud-node-manager.Dockerfile b/cloud-node-manager.Dockerfile
index 170ed5bb5..263ed09e1 100644
--- a/cloud-node-manager.Dockerfile
+++ b/cloud-node-manager.Dockerfile
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.23@sha256:57529e2747164a583f3d1b86239a0e75bf38c79446504ad6a42c6ea671499bfa AS builder
+FROM --platform=linux/amd64 mcr.microsoft.com/oss/go/microsoft/golang:1.23@sha256:28f5a50c0e8b9b8bf7e94f366169120736bc3887e69184d58364fe189e7eb658 AS builder
 
 ARG ENABLE_GIT_COMMAND=true
 ARG ARCH=amd64
diff --git a/go.mod b/go.mod
index 111f212f2..27b29c53c 100644
--- a/go.mod
+++ b/go.mod
@@ -50,7 +50,7 @@ require (
 	k8s.io/kubelet v0.31.2
 	k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
 	sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.1.15
-	sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.1.4
+	sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.1.5
 	sigs.k8s.io/yaml v1.4.0
 )
 
diff --git a/go.sum b/go.sum
index 203226594..a740de6ab 100644
--- a/go.sum
+++ b/go.sum
@@ -457,8 +457,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsA
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
 sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.1.15 h1:8M5S8zVUoK8HU0rKqbPw5/sWL/NAmnO8+GqPIz4w+iA=
 sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.1.15/go.mod h1:OJBnDbWJgOndWItjTj3LMf8n2Px1kgj9zjhA9ZMGYI0=
-sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.1.4 h1:78nKI0bjXCXLKGccr2aITi0mJ1YScqMeoCiYSrzi3Ag=
-sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.1.4/go.mod h1:qpPLf/GsTSQV8pGzInA07hYT7OgwN5AqLoYf0i5qI2I=
+sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.1.5 h1:FrkM6pIC6b3dmrd3RgPqrOK44z9gFsR4NI5ZFlUtiQo=
+sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.1.5/go.mod h1:qpPLf/GsTSQV8pGzInA07hYT7OgwN5AqLoYf0i5qI2I=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
 sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
diff --git a/pkg/provider/azure_loadbalancer.go b/pkg/provider/azure_loadbalancer.go
index 7a369a4e3..3b8fe1d8e 100644
--- a/pkg/provider/azure_loadbalancer.go
+++ b/pkg/provider/azure_loadbalancer.go
@@ -181,7 +181,7 @@ func (az *Cloud) reconcileService(ctx context.Context, clusterName string, servi
 		az.localServiceNameToServiceInfoMap.Store(key, newServiceInfo(getServiceIPFamily(service), lbName))
 		// There are chances that the endpointslice changes after EnsureHostsInPool, so
 		// need to check endpointslice for a second time.
-		if err := az.checkAndApplyLocalServiceBackendPoolUpdates(ctx, *lb, service); err != nil {
+		if err := az.checkAndApplyLocalServiceBackendPoolUpdates(*lb, service); err != nil {
 			logger.Error(err, "Failed to checkAndApplyLocalServiceBackendPoolUpdates")
 			return nil, err
 		}
diff --git a/pkg/provider/azure_loadbalancer_backendpool.go b/pkg/provider/azure_loadbalancer_backendpool.go
index d0f6f09f9..44ea68380 100644
--- a/pkg/provider/azure_loadbalancer_backendpool.go
+++ b/pkg/provider/azure_loadbalancer_backendpool.go
@@ -202,7 +202,7 @@ func (bc *backendPoolTypeNodeIPConfig) ReconcileBackendPools(
 			if bp.BackendAddressPoolPropertiesFormat != nil &&
 				bp.LoadBalancerBackendAddresses != nil &&
 				len(*bp.LoadBalancerBackendAddresses) > 0 {
-				if removeNodeIPAddressesFromBackendPool(bp, []string{}, true, false) {
+				if removeNodeIPAddressesFromBackendPool(bp, []string{}, true, false, false) {
 					isMigration = true
 					bp.VirtualNetwork = nil
 					if err := bc.CreateOrUpdateLBBackendPool(ctx, lbName, bp); err != nil {
@@ -410,7 +410,6 @@ func (bi *backendPoolTypeNodeIP) EnsureHostsInPool(ctx context.Context, service
 		changed               bool
 		numOfAdd, numOfDelete int
 		activeNodes           *utilsets.IgnoreCaseSet
-		err                   error
 	)
 	if bi.useMultipleStandardLoadBalancers() {
 		if !isLocalService(service) {
@@ -425,10 +424,12 @@ func (bi *backendPoolTypeNodeIP) EnsureHostsInPool(ctx context.Context, service
 					"current load balancer", si.lbName)
 				return nil
 			}
-			activeNodes, err = bi.getLocalServiceEndpointsNodeNames(ctx, service)
-			if err != nil {
-				return err
-			}
+			activeNodes = bi.getLocalServiceEndpointsNodeNames(service)
+		}
+
+		if isNICPool(backendPool) {
+			klog.V(4).InfoS("EnsureHostsInPool: skipping NIC-based backend pool", "backendPoolName", ptr.Deref(backendPool.Name, ""))
+			return nil
 		}
 	}
 
@@ -463,7 +464,7 @@ func (bi *backendPoolTypeNodeIP) EnsureHostsInPool(ctx context.Context, service
 			}
 
 			if bi.useMultipleStandardLoadBalancers() {
-				if !activeNodes.Has(node.Name) {
+				if activeNodes != nil && !activeNodes.Has(node.Name) {
 					klog.V(4).Infof("bi.EnsureHostsInPool: node %s should not be in load balancer %q", node.Name, lbName)
 					continue
 				}
@@ -500,7 +501,7 @@ func (bi *backendPoolTypeNodeIP) EnsureHostsInPool(ctx context.Context, service
 				}
 			}
 		}
-		removeNodeIPAddressesFromBackendPool(backendPool, nodeIPsToBeDeleted, false, bi.useMultipleStandardLoadBalancers())
+		removeNodeIPAddressesFromBackendPool(backendPool, nodeIPsToBeDeleted, false, bi.useMultipleStandardLoadBalancers(), true)
 	}
 	if changed {
 		klog.V(2).Infof("bi.EnsureHostsInPool: updating backend pool %s of load balancer %s to add %d nodes and remove %d nodes", lbBackendPoolName, lbName, numOfAdd, numOfDelete)
@@ -633,7 +634,7 @@ func (bi *backendPoolTypeNodeIP) ReconcileBackendPools(ctx context.Context, clus
 		if isMigration && bi.EnableMigrateToIPBasedBackendPoolAPI {
 			var backendPoolNames []string
 			for _, id := range lbBackendPoolIDsSlice {
-				name, err := getLBNameFromBackendPoolID(id)
+				name, err := getBackendPoolNameFromBackendPoolID(id)
 				if err != nil {
 					klog.Errorf("bi.ReconcileBackendPools for service (%s): failed to get LB name from backend pool ID: %s", serviceName, err.Error())
 					return false, false, nil, err
@@ -674,7 +675,7 @@ func (bi *backendPoolTypeNodeIP) ReconcileBackendPools(ctx context.Context, clus
 				}
 			}
 			if len(nodeIPAddressesToBeDeleted) > 0 {
-				if removeNodeIPAddressesFromBackendPool(bp, nodeIPAddressesToBeDeleted, false, false) {
+				if removeNodeIPAddressesFromBackendPool(bp, nodeIPAddressesToBeDeleted, false, false, true) {
 					updated = true
 				}
 			}
@@ -871,11 +872,13 @@ func hasIPAddressInBackendPool(backendPool *network.BackendAddressPool, ipAddres
 func removeNodeIPAddressesFromBackendPool(
 	backendPool network.BackendAddressPool,
 	nodeIPAddresses []string,
-	removeAll, useMultipleStandardLoadBalancers bool,
+	removeAll, useMultipleStandardLoadBalancers, isNodeIP bool,
 ) bool {
 	changed := false
 	nodeIPsSet := utilsets.NewString(nodeIPAddresses...)
 
+	logger := klog.Background().WithName("removeNodeIPAddressFromBackendPool")
+
 	if backendPool.BackendAddressPoolPropertiesFormat == nil ||
 		backendPool.LoadBalancerBackendAddresses == nil {
 		return false
@@ -886,7 +889,13 @@ func removeNodeIPAddressesFromBackendPool(
 		if addresses[i].LoadBalancerBackendAddressPropertiesFormat != nil {
 			ipAddress := ptr.Deref((*backendPool.LoadBalancerBackendAddresses)[i].IPAddress, "")
 			if ipAddress == "" {
-				klog.V(4).Infof("removeNodeIPAddressFromBackendPool: LoadBalancerBackendAddress %s is not IP-based, skipping", ptr.Deref(addresses[i].Name, ""))
+				if isNodeIP {
+					logger.V(4).Info("LoadBalancerBackendAddress is not IP-based, removing", "LoadBalancerBackendAddress", ptr.Deref(addresses[i].Name, ""))
+					addresses = append(addresses[:i], addresses[i+1:]...)
+					changed = true
+				} else {
+					logger.V(4).Info("LoadBalancerBackendAddress is not IP-based, skipping", "LoadBalancerBackendAddress", ptr.Deref(addresses[i].Name, ""))
+				}
 				continue
 			}
 			if removeAll || nodeIPsSet.Has(ipAddress) {
diff --git a/pkg/provider/azure_loadbalancer_repo.go b/pkg/provider/azure_loadbalancer_repo.go
index 15775beba..189b0c955 100644
--- a/pkg/provider/azure_loadbalancer_repo.go
+++ b/pkg/provider/azure_loadbalancer_repo.go
@@ -297,7 +297,6 @@ func (az *Cloud) MigrateToIPBasedBackendPoolAndWaitForCompletion(
 		}
 		return true, nil
 	})
-
 	if err != nil {
 		if errors.Is(err, wait.ErrWaitTimeout) {
 			klog.Warningf("MigrateToIPBasedBackendPoolAndWaitForCompletion: Timeout waiting for migration to IP based backend pool for lb %s, backend pool %s", lbName, strings.Join(backendPoolNames, ","))
@@ -351,7 +350,7 @@ func (az *Cloud) getAzureLoadBalancer(ctx context.Context, name string, crt azca
 // If not same, the lbName for existingBackendPools would also be returned.
 func isBackendPoolOnSameLB(newBackendPoolID string, existingBackendPools []string) (bool, string, error) {
 	matches := backendPoolIDRE.FindStringSubmatch(newBackendPoolID)
-	if len(matches) != 2 {
+	if len(matches) != 3 {
 		return false, "", fmt.Errorf("new backendPoolID %q is in wrong format", newBackendPoolID)
 	}
 
@@ -359,7 +358,7 @@ func isBackendPoolOnSameLB(newBackendPoolID string, existingBackendPools []strin
 	newLBNameTrimmed := trimSuffixIgnoreCase(newLBName, consts.InternalLoadBalancerNameSuffix)
 	for _, backendPool := range existingBackendPools {
 		matches := backendPoolIDRE.FindStringSubmatch(backendPool)
-		if len(matches) != 2 {
+		if len(matches) != 3 {
 			return false, "", fmt.Errorf("existing backendPoolID %q is in wrong format", backendPool)
 		}
 
@@ -380,3 +379,18 @@ func (az *Cloud) serviceOwnsRule(service *v1.Service, rule string) bool {
 	prefix := az.getRulePrefix(service)
 	return strings.HasPrefix(strings.ToUpper(rule), strings.ToUpper(prefix))
 }
+
+func isNICPool(bp network.BackendAddressPool) bool {
+	logger := klog.Background().WithName("isNICPool").WithValues("backendPoolName", ptr.Deref(bp.Name, ""))
+	if bp.BackendAddressPoolPropertiesFormat != nil &&
+		bp.LoadBalancerBackendAddresses != nil {
+		for _, addr := range *bp.LoadBalancerBackendAddresses {
+			if ptr.Deref(addr.IPAddress, "") == "" {
+				logger.V(4).Info("The load balancer backend address has empty ip address, assuming it is a NIC pool",
+					"loadBalancerBackendAddress", ptr.Deref(addr.Name, ""))
+				return true
+			}
+		}
+	}
+	return false
+}
diff --git a/pkg/provider/azure_local_services.go b/pkg/provider/azure_local_services.go
index ce9224865..f2b3aad82 100644
--- a/pkg/provider/azure_local_services.go
+++ b/pkg/provider/azure_local_services.go
@@ -27,7 +27,6 @@ import (
 
 	v1 "k8s.io/api/core/v1"
 	discovery_v1 "k8s.io/api/discovery/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/apimachinery/pkg/util/wait"
 	"k8s.io/client-go/informers"
@@ -210,7 +209,7 @@ func (updater *loadBalancerBackendPoolUpdater) process(ctx context.Context) {
 			lbOp := op.(*loadBalancerBackendPoolUpdateOperation)
 			switch lbOp.kind {
 			case consts.LoadBalancerBackendPoolUpdateOperationRemove:
-				removed := removeNodeIPAddressesFromBackendPool(bp, lbOp.nodeIPs, false, true)
+				removed := removeNodeIPAddressesFromBackendPool(bp, lbOp.nodeIPs, false, true, true)
 				changed = changed || removed
 			case consts.LoadBalancerBackendPoolUpdateOperationAdd:
 				added := updater.az.addNodeIPAddressesToBackendPool(&bp, lbOp.nodeIPs)
@@ -446,8 +445,10 @@ func (az *Cloud) getLocalServiceBackendPoolID(serviceName string, lbName string,
 
 // localServiceOwnsBackendPool checks if a backend pool is owned by a local service.
 func localServiceOwnsBackendPool(serviceName, bpName string) bool {
-	prefix := strings.Replace(serviceName, "/", "-", -1)
-	return strings.HasPrefix(strings.ToLower(bpName), strings.ToLower(prefix))
+	if strings.HasSuffix(strings.ToLower(bpName), consts.IPVersionIPv6StringLower) {
+		return strings.EqualFold(getLocalServiceBackendPoolName(serviceName, true), bpName)
+	}
+	return strings.EqualFold(getLocalServiceBackendPoolName(serviceName, false), bpName)
 }
 
 type serviceInfo struct {
@@ -463,50 +464,30 @@ func newServiceInfo(ipFamily, lbName string) *serviceInfo {
 }
 
 // getLocalServiceEndpointsNodeNames gets the node names that host all endpoints of the local service.
-func (az *Cloud) getLocalServiceEndpointsNodeNames(ctx context.Context, service *v1.Service) (*utilsets.IgnoreCaseSet, error) {
-	var (
-		ep           *discovery_v1.EndpointSlice
-		foundInCache bool
-	)
+func (az *Cloud) getLocalServiceEndpointsNodeNames(service *v1.Service) *utilsets.IgnoreCaseSet {
+	var eps []*discovery_v1.EndpointSlice
 	az.endpointSlicesCache.Range(func(_, value interface{}) bool {
 		endpointSlice := value.(*discovery_v1.EndpointSlice)
 		if strings.EqualFold(getServiceNameOfEndpointSlice(endpointSlice), service.Name) &&
 			strings.EqualFold(endpointSlice.Namespace, service.Namespace) {
-			ep = endpointSlice
-			foundInCache = true
-			return false
+			eps = append(eps, endpointSlice)
 		}
 		return true
 	})
-	if ep == nil {
-		klog.Infof("EndpointSlice for service %s/%s not found, try to list EndpointSlices", service.Namespace, service.Name)
-		eps, err := az.KubeClient.DiscoveryV1().EndpointSlices(service.Namespace).List(ctx, metav1.ListOptions{})
-		if err != nil {
-			klog.Errorf("Failed to list EndpointSlices for service %s/%s: %s", service.Namespace, service.Name, err.Error())
-			return nil, err
-		}
-		for _, endpointSlice := range eps.Items {
-			endpointSlice := endpointSlice
-			if strings.EqualFold(getServiceNameOfEndpointSlice(&endpointSlice), service.Name) {
-				ep = &endpointSlice
-				break
-			}
-		}
-	}
-	if ep == nil {
-		return nil, fmt.Errorf("failed to find EndpointSlice for service %s/%s", service.Namespace, service.Name)
-	}
-	if !foundInCache {
-		az.endpointSlicesCache.Store(strings.ToLower(fmt.Sprintf("%s/%s", ep.Namespace, ep.Name)), ep)
+	if len(eps) == 0 {
+		klog.Warningf("getLocalServiceEndpointsNodeNames: failed to find EndpointSlice for service %s/%s", service.Namespace, service.Name)
+		return nil
 	}
 
 	var nodeNames []string
-	for _, endpoint := range ep.Endpoints {
-		klog.V(4).Infof("EndpointSlice %s/%s has endpoint %s on node %s", ep.Namespace, ep.Name, endpoint.Addresses, ptr.Deref(endpoint.NodeName, ""))
-		nodeNames = append(nodeNames, ptr.Deref(endpoint.NodeName, ""))
+	for _, ep := range eps {
+		for _, endpoint := range ep.Endpoints {
+			klog.V(4).Infof("EndpointSlice %s/%s has endpoint %s on node %s", ep.Namespace, ep.Name, endpoint.Addresses, ptr.Deref(endpoint.NodeName, ""))
+			nodeNames = append(nodeNames, ptr.Deref(endpoint.NodeName, ""))
+		}
 	}
 
-	return utilsets.NewString(nodeNames...), nil
+	return utilsets.NewString(nodeNames...)
 }
 
 // cleanupLocalServiceBackendPool cleans up the backend pool of
@@ -548,12 +529,13 @@ func (az *Cloud) cleanupLocalServiceBackendPool(
 
 // checkAndApplyLocalServiceBackendPoolUpdates if the IPs in the backend pool are aligned
 // with the corresponding endpointslice, and update the backend pool if necessary.
-func (az *Cloud) checkAndApplyLocalServiceBackendPoolUpdates(ctx context.Context, lb network.LoadBalancer, service *v1.Service) error {
+func (az *Cloud) checkAndApplyLocalServiceBackendPoolUpdates(lb network.LoadBalancer, service *v1.Service) error {
 	serviceName := getServiceName(service)
-	endpointsNodeNames, err := az.getLocalServiceEndpointsNodeNames(ctx, service)
-	if err != nil {
-		return err
+	endpointsNodeNames := az.getLocalServiceEndpointsNodeNames(service)
+	if endpointsNodeNames == nil {
+		return nil
 	}
+
 	var expectedIPs []string
 	for _, nodeName := range endpointsNodeNames.UnsortedList() {
 		ips := az.nodePrivateIPs[strings.ToLower(nodeName)]
diff --git a/pkg/provider/azure_standard.go b/pkg/provider/azure_standard.go
index fdfbd9933..6ec29f657 100644
--- a/pkg/provider/azure_standard.go
+++ b/pkg/provider/azure_standard.go
@@ -48,7 +48,7 @@ import (
 var (
 	errNotInVMSet      = errors.New("vm is not in the vmset")
 	providerIDRE       = regexp.MustCompile(`.*/subscriptions/(?:.*)/Microsoft.Compute/virtualMachines/(.+)$`)
-	backendPoolIDRE    = regexp.MustCompile(`^/subscriptions/(?:.*)/resourceGroups/(?:.*)/providers/Microsoft.Network/loadBalancers/(.+)/backendAddressPools/(?:.*)`)
+	backendPoolIDRE    = regexp.MustCompile(`^/subscriptions/(?:.*)/resourceGroups/(?:.*)/providers/Microsoft.Network/loadBalancers/(.+)/backendAddressPools/(.+)`)
 	nicResourceGroupRE = regexp.MustCompile(`.*/subscriptions/(?:.*)/resourceGroups/(.+)/providers/Microsoft.Network/networkInterfaces/(?:.*)`)
 	nicIDRE            = regexp.MustCompile(`(?i)/subscriptions/(?:.*)/resourceGroups/(.+)/providers/Microsoft.Network/networkInterfaces/(.+)/ipConfigurations/(?:.*)`)
 	vmIDRE             = regexp.MustCompile(`(?i)/subscriptions/(?:.*)/resourceGroups/(?:.*)/providers/Microsoft.Compute/virtualMachines/(.+)`)
diff --git a/pkg/provider/azure_utils.go b/pkg/provider/azure_utils.go
index 6cb2def64..c81e31467 100644
--- a/pkg/provider/azure_utils.go
+++ b/pkg/provider/azure_utils.go
@@ -458,13 +458,13 @@ func getResourceIDPrefix(id string) string {
 	return id[:idx]
 }
 
-func getLBNameFromBackendPoolID(backendPoolID string) (string, error) {
+func getBackendPoolNameFromBackendPoolID(backendPoolID string) (string, error) {
 	matches := backendPoolIDRE.FindStringSubmatch(backendPoolID)
-	if len(matches) != 2 {
+	if len(matches) != 3 {
 		return "", fmt.Errorf("backendPoolID %q is in wrong format", backendPoolID)
 	}
 
-	return matches[1], nil
+	return matches[2], nil
 }
 
 func countNICsOnBackendPool(backendPool network.BackendAddressPool) int {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 1c1bce370..76467da1f 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1587,7 +1587,7 @@ sigs.k8s.io/cloud-provider-azure/pkg/azclient/virtualmachinescalesetvmclient
 sigs.k8s.io/cloud-provider-azure/pkg/azclient/virtualnetworkclient
 sigs.k8s.io/cloud-provider-azure/pkg/azclient/virtualnetworklinkclient
 sigs.k8s.io/cloud-provider-azure/pkg/azclient/virtualnetworklinkclient/mock_virtualnetworklinkclient
-# sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.1.4
+# sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.1.5
 ## explicit; go 1.23.1
 sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader
 # sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd
diff --git a/vendor/sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader/load.go b/vendor/sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader/load.go
index efa79c52b..63d6b1889 100644
--- a/vendor/sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader/load.go
+++ b/vendor/sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader/load.go
@@ -56,22 +56,22 @@ func Load[Type any](ctx context.Context, secretLoaderConfig *K8sSecretLoaderConf
 	var err error
 	if fileLoaderConfig != nil {
 		loadConfigloader := newEmptyLoader[ConfigMergeConfig](&ConfigMergeConfig{CloudConfigType: CloudConfigTypeMerge})
-		loadConfigloader = newFileLoader(fileLoaderConfig.FilePath, loadConfigloader, newYamlByteLoader[ConfigMergeConfig])
+		loadConfigloader = newFileLoader(fileLoaderConfig.FilePath, loadConfigloader, NewYamlByteLoader[ConfigMergeConfig])
 		//by default the config load type  is merge
 		loadConfig, err = loadConfigloader.Load(ctx)
 		if err != nil {
 			return nil, err
 		}
-		configloader = newFileLoader(fileLoaderConfig.FilePath, nil, newYamlByteLoader[Type])
+		configloader = newFileLoader(fileLoaderConfig.FilePath, nil, NewYamlByteLoader[Type])
 		if strings.EqualFold(string(loadConfig.CloudConfigType), string(CloudConfigTypeFile)) {
 			return configloader.Load(ctx)
 		}
 	}
 	if secretLoaderConfig != nil {
 		if loadConfig != nil && strings.EqualFold(string(loadConfig.CloudConfigType), string(CloudConfigTypeSecret)) {
-			configloader = newK8sSecretLoader(&secretLoaderConfig.K8sSecretConfig, secretLoaderConfig.KubeClient, nil, newYamlByteLoader[Type])
+			configloader = newK8sSecretLoader(&secretLoaderConfig.K8sSecretConfig, secretLoaderConfig.KubeClient, nil, NewYamlByteLoader[Type])
 		} else {
-			configloader = newK8sSecretLoader(&secretLoaderConfig.K8sSecretConfig, secretLoaderConfig.KubeClient, configloader, newYamlByteLoader[Type])
+			configloader = newK8sSecretLoader(&secretLoaderConfig.K8sSecretConfig, secretLoaderConfig.KubeClient, configloader, NewYamlByteLoader[Type])
 		}
 	}
 	return configloader.Load(ctx)
diff --git a/vendor/sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader/loader_yaml.go b/vendor/sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader/loader_yaml.go
index c5a8a1f30..346025834 100644
--- a/vendor/sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader/loader_yaml.go
+++ b/vendor/sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader/loader_yaml.go
@@ -23,14 +23,14 @@ import (
 	"sigs.k8s.io/yaml"
 )
 
-// yamlByteLoader is a FactoryConfigLoader that loads a YAML file from a byte array.
-type yamlByteLoader[Type any] struct {
+// YamlByteLoader is a FactoryConfigLoader that loads a YAML file from a byte array.
+type YamlByteLoader[Type any] struct {
 	content []byte
 	configLoader[Type]
 }
 
 // Load loads the YAML file from the byte array and returns the client factory config.
-func (s *yamlByteLoader[Type]) Load(ctx context.Context) (*Type, error) {
+func (s *YamlByteLoader[Type]) Load(ctx context.Context) (*Type, error) {
 	if s.configLoader == nil {
 		s.configLoader = newEmptyLoader[Type](nil)
 	}
@@ -45,9 +45,9 @@ func (s *yamlByteLoader[Type]) Load(ctx context.Context) (*Type, error) {
 	return config, nil
 }
 
-// newYamlByteLoader creates a YamlByteLoader with the specified content and loader.
-func newYamlByteLoader[Type any](content []byte, loader configLoader[Type]) configLoader[Type] {
-	return &yamlByteLoader[Type]{
+// NewYamlByteLoader creates a YamlByteLoader with the specified content and loader.
+func NewYamlByteLoader[Type any](content []byte, loader configLoader[Type]) configLoader[Type] {
+	return &YamlByteLoader[Type]{
 		content:      content,
 		configLoader: loader,
 	}


Test patch:
diff --git a/e2e.Dockerfile b/e2e.Dockerfile
index a1b380e42..9b1e5c55a 100644
--- a/e2e.Dockerfile
+++ b/e2e.Dockerfile
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-FROM mcr.microsoft.com/oss/go/microsoft/golang:1.23-bullseye@sha256:ea0b7339cbba81c9973bed366b7e7f5e304936acb931fdf51dcfd921dfe02984
+FROM mcr.microsoft.com/oss/go/microsoft/golang:1.23-bullseye@sha256:bd0f1973513fe7f8e7f025c6461e7cfdbbe579867802136999e5e78b54655f81
 
 WORKDIR /go/src/sigs.k8s.io/cloud-provider-azure
 
diff --git a/pkg/provider/azure_loadbalancer_backendpool_test.go b/pkg/provider/azure_loadbalancer_backendpool_test.go
index 0b02f3a26..72155935a 100644
--- a/pkg/provider/azure_loadbalancer_backendpool_test.go
+++ b/pkg/provider/azure_loadbalancer_backendpool_test.go
@@ -201,19 +201,14 @@ func TestEnsureHostsInPoolNodeIP(t *testing.T) {
 			},
 		},
 		{
-			desc: "should add correct nodes to the pool and remove unwanted ones when using multi-slb",
+			desc: "should skip NIC-based backend pool when using multi-slb",
 			backendPool: network.BackendAddressPool{
 				Name: ptr.To("kubernetes"),
 				BackendAddressPoolPropertiesFormat: &network.BackendAddressPoolPropertiesFormat{
 					LoadBalancerBackendAddresses: &[]network.LoadBalancerBackendAddress{
 						{
 							LoadBalancerBackendAddressPropertiesFormat: &network.LoadBalancerBackendAddressPropertiesFormat{
-								IPAddress: ptr.To("10.0.0.1"),
-							},
-						},
-						{
-							LoadBalancerBackendAddressPropertiesFormat: &network.LoadBalancerBackendAddressPropertiesFormat{
-								IPAddress: ptr.To("10.0.0.3"),
+								IPAddress: ptr.To(""),
 							},
 						},
 					},
@@ -230,47 +225,53 @@ func TestEnsureHostsInPoolNodeIP(t *testing.T) {
 			expectedBackendPool: network.BackendAddressPool{
 				Name: ptr.To("kubernetes"),
 				BackendAddressPoolPropertiesFormat: &network.BackendAddressPoolPropertiesFormat{
-					VirtualNetwork: &network.SubResource{ID: ptr.To("/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/virtualNetworks/vnet")},
 					LoadBalancerBackendAddresses: &[]network.LoadBalancerBackendAddress{
 						{
-							Name: ptr.To("vmss-2"),
 							LoadBalancerBackendAddressPropertiesFormat: &network.LoadBalancerBackendAddressPropertiesFormat{
-								IPAddress: ptr.To("10.0.0.4"),
+								IPAddress: ptr.To(""),
 							},
 						},
 					},
 				},
 			},
+			skip: true,
 		},
 		{
-			desc:  "should add ips to the local service dedicated backend pool",
-			local: true,
+			desc: "should add correct nodes to the pool and remove unwanted ones when using multi-slb",
 			backendPool: network.BackendAddressPool{
-				Name: ptr.To("default-svc-1"),
+				Name: ptr.To("kubernetes"),
 				BackendAddressPoolPropertiesFormat: &network.BackendAddressPoolPropertiesFormat{
-					LoadBalancerBackendAddresses: &[]network.LoadBalancerBackendAddress{},
+					LoadBalancerBackendAddresses: &[]network.LoadBalancerBackendAddress{
+						{
+							LoadBalancerBackendAddressPropertiesFormat: &network.LoadBalancerBackendAddressPropertiesFormat{
+								IPAddress: ptr.To("10.0.0.1"),
+							},
+						},
+						{
+							LoadBalancerBackendAddressPropertiesFormat: &network.LoadBalancerBackendAddressPropertiesFormat{
+								IPAddress: ptr.To("10.0.0.3"),
+							},
+						},
+					},
 				},
 			},
 			multiSLBConfigs: []MultipleStandardLoadBalancerConfiguration{
 				{
 					Name: "kubernetes",
+					MultipleStandardLoadBalancerConfigurationStatus: MultipleStandardLoadBalancerConfigurationStatus{
+						ActiveNodes: utilsets.NewString("vmss-2"),
+					},
 				},
 			},
 			expectedBackendPool: network.BackendAddressPool{
-				Name: ptr.To("default-svc-1"),
+				Name: ptr.To("kubernetes"),
 				BackendAddressPoolPropertiesFormat: &network.BackendAddressPoolPropertiesFormat{
 					VirtualNetwork: &network.SubResource{ID: ptr.To("/subscriptions/subscription/resourceGroups/rg/providers/Microsoft.Network/virtualNetworks/vnet")},
 					LoadBalancerBackendAddresses: &[]network.LoadBalancerBackendAddress{
 						{
-							Name: ptr.To("vmss-0"),
-							LoadBalancerBackendAddressPropertiesFormat: &network.LoadBalancerBackendAddressPropertiesFormat{
-								IPAddress: ptr.To("10.0.0.2"),
-							},
-						},
-						{
-							Name: ptr.To("vmss-1"),
+							Name: ptr.To("vmss-2"),
 							LoadBalancerBackendAddressPropertiesFormat: &network.LoadBalancerBackendAddressPropertiesFormat{
-								IPAddress: ptr.To("10.0.0.1"),
+								IPAddress: ptr.To("10.0.0.4"),
 							},
 						},
 					},
@@ -934,12 +935,14 @@ func TestReconcileBackendPoolsNodeIPConfigToIPWithMigrationAPI(t *testing.T) {
 	mockVMSet.EXPECT().GetPrimaryVMSetName().Return("k8s-agentpool1-00000000").AnyTimes()
 
 	mockLBClient := mockloadbalancerclient.NewMockInterface(ctrl)
-	mockLBClient.EXPECT().MigrateToIPBasedBackendPool(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(retry.NewError(false, errors.New("error")))
+	mockLBClient.EXPECT().MigrateToIPBasedBackendPool(gomock.Any(), gomock.Any(), "testCluster", []string{"testCluster"}).Return(retry.NewError(false, errors.New("error")))
 
 	az := GetTestCloud(ctrl)
 	az.VMSet = mockVMSet
 	az.LoadBalancerClient = mockLBClient
 	az.EnableMigrateToIPBasedBackendPoolAPI = true
+	az.LoadBalancerSku = "standard"
+	az.MultipleStandardLoadBalancerConfigurations = []MultipleStandardLoadBalancerConfiguration{{Name: "kubernetes"}}
 
 	bi := newBackendPoolTypeNodeIP(az)
 	svc := getTestService("test", v1.ProtocolTCP, nil, false, 80)
@@ -947,7 +950,7 @@ func TestReconcileBackendPoolsNodeIPConfigToIPWithMigrationAPI(t *testing.T) {
 	assert.Error(t, err)
 	assert.Contains(t, err.Error(), "error")
 
-	mockLBClient.EXPECT().MigrateToIPBasedBackendPool(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+	mockLBClient.EXPECT().MigrateToIPBasedBackendPool(gomock.Any(), gomock.Any(), "testCluster", []string{"testCluster"}).Return(nil)
 	bps := buildLBWithVMIPs(testClusterName, []string{"1.2.3.4", "2.3.4.5"}).BackendAddressPools
 	mockLBClient.EXPECT().GetLBBackendPool(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return((*bps)[0], nil)
 	mockLBClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(network.LoadBalancer{}, nil)
@@ -980,6 +983,7 @@ func TestRemoveNodeIPAddressFromBackendPool(t *testing.T) {
 		description                           string
 		removeAll, useMultiSLB                bool
 		unwantedIPs, existingIPs, expectedIPs []string
+		isNodeIP                              bool
 	}{
 		{
 			description: "removeNodeIPAddressFromBackendPool should remove the unwanted IP addresses from the backend pool",
@@ -1007,12 +1011,26 @@ func TestRemoveNodeIPAddressFromBackendPool(t *testing.T) {
 			existingIPs: []string{"1.2.3.4", "4.3.2.1", ""},
 			expectedIPs: []string{""},
 		},
+		{
+			description: "removeNodeIPAddressFromBackendPool should skip non-IP based backend addresses when isNodeIP is false",
+			unwantedIPs: []string{"1.2.3.4"},
+			existingIPs: []string{"1.2.3.4", ""},
+			expectedIPs: []string{""},
+		},
+		{
+			description: "removeNodeIPAddressFromBackendPool should remove non-IP based backend addresses when isNodeIP is true",
+			unwantedIPs: []string{"1.2.3.4"},
+			existingIPs: []string{"1.2.3.4", ""},
+			expectedIPs: []string{},
+			useMultiSLB: true,
+			isNodeIP:    true,
+		},
 	} {
 		t.Run(tc.description, func(t *testing.T) {
 			backendPool := buildTestLoadBalancerBackendPoolWithIPs("kubernetes", tc.existingIPs)
 			expectedBackendPool := buildTestLoadBalancerBackendPoolWithIPs("kubernetes", tc.expectedIPs)
 
-			removeNodeIPAddressesFromBackendPool(backendPool, tc.unwantedIPs, tc.removeAll, tc.useMultiSLB)
+			removeNodeIPAddressesFromBackendPool(backendPool, tc.unwantedIPs, tc.removeAll, tc.useMultiSLB, tc.isNodeIP)
 			assert.Equal(t, expectedBackendPool, backendPool)
 		})
 	}
diff --git a/pkg/provider/azure_loadbalancer_repo_test.go b/pkg/provider/azure_loadbalancer_repo_test.go
index 38204109d..85da86606 100644
--- a/pkg/provider/azure_loadbalancer_repo_test.go
+++ b/pkg/provider/azure_loadbalancer_repo_test.go
@@ -429,3 +429,101 @@ func TestServiceOwnsRuleSharedProbe(t *testing.T) {
 		})
 	}
 }
+
+func TestIsNICPool(t *testing.T) {
+	tests := []struct {
+		desc     string
+		bp       network.BackendAddressPool
+		expected bool
+	}{
+		{
+			desc: "nil BackendAddressPoolPropertiesFormat",
+			bp: network.BackendAddressPool{
+				Name: ptr.To("pool1"),
+			},
+			expected: false,
+		},
+		{
+			desc: "nil LoadBalancerBackendAddresses",
+			bp: network.BackendAddressPool{
+				Name:                               ptr.To("pool1"),
+				BackendAddressPoolPropertiesFormat: &network.BackendAddressPoolPropertiesFormat{},
+			},
+			expected: false,
+		},
+		{
+			desc: "empty LoadBalancerBackendAddresses",
+			bp: network.BackendAddressPool{
+				Name: ptr.To("pool1"),
+				BackendAddressPoolPropertiesFormat: &network.BackendAddressPoolPropertiesFormat{
+					LoadBalancerBackendAddresses: &[]network.LoadBalancerBackendAddress{},
+				},
+			},
+			expected: false,
+		},
+		{
+			desc: "LoadBalancerBackendAddress with empty IPAddress",
+			bp: network.BackendAddressPool{
+				Name: ptr.To("pool1"),
+				BackendAddressPoolPropertiesFormat: &network.BackendAddressPoolPropertiesFormat{
+					LoadBalancerBackendAddresses: &[]network.LoadBalancerBackendAddress{
+						{
+							Name: ptr.To("addr1"),
+							LoadBalancerBackendAddressPropertiesFormat: &network.LoadBalancerBackendAddressPropertiesFormat{
+								IPAddress: ptr.To(""),
+							},
+						},
+					},
+				},
+			},
+			expected: true,
+		},
+		{
+			desc: "LoadBalancerBackendAddress with non-empty IPAddress",
+			bp: network.BackendAddressPool{
+				Name: ptr.To("pool1"),
+				BackendAddressPoolPropertiesFormat: &network.BackendAddressPoolPropertiesFormat{
+					LoadBalancerBackendAddresses: &[]network.LoadBalancerBackendAddress{
+						{
+							Name: ptr.To("addr1"),
+							LoadBalancerBackendAddressPropertiesFormat: &network.LoadBalancerBackendAddressPropertiesFormat{
+								IPAddress: ptr.To("10.0.0.1"),
+							},
+						},
+					},
+				},
+			},
+			expected: false,
+		},
+		{
+			desc: "LoadBalancerBackendAddress with both empty and non-empty IPAddress",
+			bp: network.BackendAddressPool{
+				Name: ptr.To("pool1"),
+				BackendAddressPoolPropertiesFormat: &network.BackendAddressPoolPropertiesFormat{
+					LoadBalancerBackendAddresses: &[]network.LoadBalancerBackendAddress{
+						{
+							Name: ptr.To("addr1"),
+							LoadBalancerBackendAddressPropertiesFormat: &network.LoadBalancerBackendAddressPropertiesFormat{
+								IPAddress: ptr.To(""),
+							},
+						},
+						{
+							Name: ptr.To("addr2"),
+							LoadBalancerBackendAddressPropertiesFormat: &network.LoadBalancerBackendAddressPropertiesFormat{
+								IPAddress: ptr.To("10.0.0.2"),
+							},
+						},
+					},
+				},
+			},
+			expected: true,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.desc, func(t *testing.T) {
+			result := isNICPool(test.bp)
+			assert.Equal(t, test.expected, result)
+		})
+	}
+}
diff --git a/pkg/provider/azure_local_services_test.go b/pkg/provider/azure_local_services_test.go
index 0e05e66c8..bbfa6d739 100644
--- a/pkg/provider/azure_local_services_test.go
+++ b/pkg/provider/azure_local_services_test.go
@@ -34,7 +34,6 @@ import (
 	discovery_v1 "k8s.io/api/discovery/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/informers"
-	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/kubernetes/fake"
 	"k8s.io/utils/ptr"
 
@@ -546,27 +545,20 @@ func TestCheckAndApplyLocalServiceBackendPoolUpdates(t *testing.T) {
 	for _, tc := range []struct {
 		description string
 		existingEPS *discovery_v1.EndpointSlice
-		expectedErr error
 	}{
 		{
 			description: "should update backend pool as expected",
 			existingEPS: getTestEndpointSlice("eps1", "default", "svc1", "node2"),
 		},
 		{
-			description: "should report an error if failed to get the endpointslice",
-			expectedErr: errors.New("failed to find EndpointSlice for service default/svc1"),
+			description: "should not report an error if failed to get the endpointslice",
 		},
 	} {
 		t.Run(tc.description, func(t *testing.T) {
 			cloud := GetTestCloud(ctrl)
 			cloud.localServiceNameToServiceInfoMap.Store("default/svc1", &serviceInfo{lbName: "lb1"})
 			svc := getTestService("svc1", v1.ProtocolTCP, nil, false)
-			var client kubernetes.Interface
-			if tc.existingEPS != nil {
-				client = fake.NewSimpleClientset(&svc, tc.existingEPS)
-			} else {
-				client = fake.NewSimpleClientset(&svc)
-			}
+			client := fake.NewSimpleClientset(&svc)
 			cloud.KubeClient = client
 			informerFactory := informers.NewSharedInformerFactory(client, 0)
 			cloud.serviceLister = informerFactory.Core().V1().Services().Lister()
@@ -582,6 +574,9 @@ func TestCheckAndApplyLocalServiceBackendPoolUpdates(t *testing.T) {
 				"node1": utilsets.NewString("10.0.0.1", "fd00::1"),
 				"node2": utilsets.NewString("10.0.0.2", "fd00::2"),
 			}
+			if tc.existingEPS != nil {
+				cloud.endpointSlicesCache.Store(fmt.Sprintf("%s/%s", tc.existingEPS.Name, tc.existingEPS.Namespace), tc.existingEPS)
+			}
 
 			existingBackendPool := getTestBackendAddressPoolWithIPs("lb1", "default-svc1", []string{"10.0.0.1"})
 			existingBackendPoolIPv6 := getTestBackendAddressPoolWithIPs("lb1", "default-svc1-ipv6", []string{"fd00::1"})
@@ -611,12 +606,12 @@ func TestCheckAndApplyLocalServiceBackendPoolUpdates(t *testing.T) {
 			cloud.backendPoolUpdater = u
 			go cloud.backendPoolUpdater.run(ctx)
 
-			err := cloud.checkAndApplyLocalServiceBackendPoolUpdates(ctx, existingLB, &svc)
-			if tc.expectedErr != nil {
-				assert.Equal(t, tc.expectedErr, err)
-			} else {
-				assert.NoError(t, err)
+			if tc.existingEPS != nil {
+				_, _ = client.DiscoveryV1().EndpointSlices("default").Create(context.Background(), tc.existingEPS, metav1.CreateOptions{})
 			}
+
+			err := cloud.checkAndApplyLocalServiceBackendPoolUpdates(existingLB, &svc)
+			assert.NoError(t, err)
 			time.Sleep(2 * time.Second)
 		})
 	}