Repository: kubernetes-sigs/cloud-provider-azure
Pull request #2950 (2022-12-15 08:44:52)

feat: update cache support for starndard and vmss flex
<!--  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 feature

#### What this PR does / why we need it:
feat: update cache support for starndard and vmss flex
This PR is a preceding fix of https://github.com/kubernetes-sigs/cloud-provider-azure/pull/2608, also refines original update cache logic in VMSS

#### 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
feat: update cache support for starndard and vmss flex
```

#### 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
feat: update cache support for starndard and vmss flex
```


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

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/cloud-provider-azure/pull/2950#" title="Author self-approved">andyzhangx</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)~~ [andyzhangx]

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":[]} -->
netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *kubernetes-sigs-cloud-provide-azure* processing.


|  Name | Link |
|---------------------------------|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 207d140adafd5e59813c2d6778678bcd6a4b5401 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/kubernetes-sigs-cloud-provide-azure/deploys/639ade8749fdf60008ac5e9f |
coveralls: 
[![Coverage Status](https://coveralls.io/builds/55186265/badge)](https://coveralls.io/builds/55186265)

Coverage decreased (-0.1%) to 79.348% when pulling **207d140adafd5e59813c2d6778678bcd6a4b5401 on andyzhangx:update-cache-standard-flex2** into **0db53790d6c0fde3d0df388d0d6e0ab8d02bb81c on kubernetes-sigs:master**.

k8s-ci-robot: @andyzhangx: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cloud-provider-azure-e2e-capz | 207d140adafd5e59813c2d6778678bcd6a4b5401 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cloud-provider-azure/2950/pull-cloud-provider-azure-e2e-capz/1603310118244454400) | true | `/test pull-cloud-provider-azure-e2e-capz`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cloud-provider-azure&pr=2950). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Aandyzhangx). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cloud-provider-azure/issues?q=is:issue+is:open) when you hit one in your PR.

<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/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
andyzhangx: /retest
k8s-ci-robot: @andyzhangx: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cloud-provider-azure-e2e-capz | 207d140adafd5e59813c2d6778678bcd6a4b5401 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cloud-provider-azure/2950/pull-cloud-provider-azure-e2e-capz/1603325270205403136) | true | `/test pull-cloud-provider-azure-e2e-capz`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cloud-provider-azure&pr=2950). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Aandyzhangx). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cloud-provider-azure/issues?q=is:issue+is:open) when you hit one in your PR.

<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/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @andyzhangx: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cloud-provider-azure-e2e-capz | 207d140adafd5e59813c2d6778678bcd6a4b5401 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cloud-provider-azure/2950/pull-cloud-provider-azure-e2e-capz/1603325270205403136) | true | `/test pull-cloud-provider-azure-e2e-capz`
pull-cloud-provider-azure-e2e-ccm-vmss-capz | 207d140adafd5e59813c2d6778678bcd6a4b5401 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cloud-provider-azure/2950/pull-cloud-provider-azure-e2e-ccm-vmss-capz/1603310118127013888) | true | `/test pull-cloud-provider-azure-e2e-ccm-vmss-capz`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cloud-provider-azure&pr=2950). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Aandyzhangx). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cloud-provider-azure/issues?q=is:issue+is:open) when you hit one in your PR.

<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/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @andyzhangx: PR needs rebase.

<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/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
k8s-ci-robot: @andyzhangx: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-cloud-provider-azure-e2e-ccm-vmss-capz | 207d140adafd5e59813c2d6778678bcd6a4b5401 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cloud-provider-azure/2950/pull-cloud-provider-azure-e2e-ccm-vmss-capz/1603310118127013888) | true | `/test pull-cloud-provider-azure-e2e-ccm-vmss-capz`
pull-cloud-provider-azure-e2e-capz | dd58e4dfb9019167c887ba7d8f94cca977469624 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cloud-provider-azure/2950/pull-cloud-provider-azure-e2e-capz/1603375160558096384) | true | `/test pull-cloud-provider-azure-e2e-capz`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cloud-provider-azure&pr=2950). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Aandyzhangx). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/cloud-provider-azure/issues?q=is:issue+is:open) when you hit one in your PR.

<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/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
andyzhangx: /retest
andyzhangx: /retest
andyzhangx: /retest

Patch:
diff --git a/pkg/azureclients/vmclient/azure_vmclient.go b/pkg/azureclients/vmclient/azure_vmclient.go
index c3357964e..67aaaac61 100644
--- a/pkg/azureclients/vmclient/azure_vmclient.go
+++ b/pkg/azureclients/vmclient/azure_vmclient.go
@@ -324,34 +324,32 @@ func (c *Client) listVmssFlexVMs(ctx context.Context, vmssFlexID string, statusO
 }
 
 // Update updates a VirtualMachine.
-func (c *Client) Update(ctx context.Context, resourceGroupName string, VMName string, parameters compute.VirtualMachineUpdate, source string) *retry.Error {
+func (c *Client) Update(ctx context.Context, resourceGroupName string, VMName string, parameters compute.VirtualMachineUpdate, source string) (*compute.VirtualMachine, *retry.Error) {
 	mc := metrics.NewMetricContext("vm", "update", resourceGroupName, c.subscriptionID, source)
 
 	// Report errors if the client is rate limited.
 	if !c.rateLimiterWriter.TryAccept() {
 		mc.RateLimitedCount()
-		return retry.GetRateLimitError(true, "VMUpdate")
+		return nil, retry.GetRateLimitError(true, "VMUpdate")
 	}
 
 	// Report errors if the client is throttled.
 	if c.RetryAfterWriter.After(time.Now()) {
 		mc.ThrottledCount()
 		rerr := retry.GetThrottlingError("VMUpdate", "client throttled", c.RetryAfterWriter)
-		return rerr
+		return nil, rerr
 	}
 
-	rerr := c.updateVM(ctx, resourceGroupName, VMName, parameters, source)
+	result, rerr := c.updateVM(ctx, resourceGroupName, VMName, parameters, source)
 	mc.Observe(rerr)
 	if rerr != nil {
 		if rerr.IsThrottled() {
 			// Update RetryAfterReader so that no more requests would be sent until RetryAfter expires.
 			c.RetryAfterWriter = rerr.RetryAfter
 		}
-
-		return rerr
+		return result, rerr
 	}
-
-	return nil
+	return result, rerr
 }
 
 // UpdateAsync updates a VirtualMachine asynchronously
@@ -393,7 +391,7 @@ func (c *Client) UpdateAsync(ctx context.Context, resourceGroupName string, VMNa
 }
 
 // WaitForUpdateResult waits for the response of the update request
-func (c *Client) WaitForUpdateResult(ctx context.Context, future *azure.Future, resourceGroupName, source string) *retry.Error {
+func (c *Client) WaitForUpdateResult(ctx context.Context, future *azure.Future, resourceGroupName, source string) (*compute.VirtualMachine, *retry.Error) {
 	mc := metrics.NewMetricContext("vm", "wait_for_update_result", resourceGroupName, c.subscriptionID, source)
 	response, err := c.armClient.WaitForAsyncOperationResult(ctx, future, "VMWaitForUpdateResult")
 	mc.Observe(retry.NewErrorOrNil(false, err))
@@ -405,13 +403,24 @@ func (c *Client) WaitForUpdateResult(ctx context.Context, future *azure.Future,
 		} else {
 			klog.V(5).Infof("Received error in WaitForAsyncOperationResult: '%s', no response", err.Error())
 		}
-		return retry.GetError(response, err)
+		return nil, retry.GetError(response, err)
 	}
-	return nil
+	if response != nil && response.StatusCode != http.StatusNoContent {
+		result, rerr := c.updateResponder(response)
+		if rerr != nil {
+			klog.V(5).Infof("Received error in WaitForAsyncOperationResult updateResponder: '%s'", rerr.Error())
+		}
+
+		return result, rerr
+	}
+
+	result := &compute.VirtualMachine{}
+	result.Response = autorest.Response{Response: response}
+	return result, nil
 }
 
 // updateVM updates a VirtualMachine.
-func (c *Client) updateVM(ctx context.Context, resourceGroupName string, VMName string, parameters compute.VirtualMachineUpdate, source string) *retry.Error {
+func (c *Client) updateVM(ctx context.Context, resourceGroupName string, VMName string, parameters compute.VirtualMachineUpdate, source string) (*compute.VirtualMachine, *retry.Error) {
 	resourceID := armclient.GetResourceID(
 		c.subscriptionID,
 		resourceGroupName,
@@ -423,18 +432,20 @@ func (c *Client) updateVM(ctx context.Context, resourceGroupName string, VMName
 	defer c.armClient.CloseResponse(ctx, response)
 	if rerr != nil {
 		klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "vm.put.request", resourceID, rerr.Error())
-		return rerr
+		return nil, rerr
 	}
 
 	if response != nil && response.StatusCode != http.StatusNoContent {
-		_, rerr = c.updateResponder(response)
+		result, rerr := c.updateResponder(response)
 		if rerr != nil {
 			klog.V(5).Infof("Received error in %s: resourceID: %s, error: %s", "vm.put.respond", resourceID, rerr.Error())
-			return rerr
 		}
+		return result, rerr
 	}
 
-	return nil
+	result := &compute.VirtualMachine{}
+	result.Response = autorest.Response{Response: response}
+	return result, nil
 }
 
 func (c *Client) updateResponder(resp *http.Response) (*compute.VirtualMachine, *retry.Error) {
diff --git a/pkg/azureclients/vmclient/interface.go b/pkg/azureclients/vmclient/interface.go
index 5c41811df..20058fa98 100644
--- a/pkg/azureclients/vmclient/interface.go
+++ b/pkg/azureclients/vmclient/interface.go
@@ -53,13 +53,13 @@ type Interface interface {
 	CreateOrUpdate(ctx context.Context, resourceGroupName string, VMName string, parameters compute.VirtualMachine, source string) *retry.Error
 
 	// Update updates a VirtualMachine.
-	Update(ctx context.Context, resourceGroupName string, VMName string, parameters compute.VirtualMachineUpdate, source string) *retry.Error
+	Update(ctx context.Context, resourceGroupName string, VMName string, parameters compute.VirtualMachineUpdate, source string) (*compute.VirtualMachine, *retry.Error)
 
 	// UpdateAsync updates a VirtualMachine asynchronously
 	UpdateAsync(ctx context.Context, resourceGroupName string, VMName string, parameters compute.VirtualMachineUpdate, source string) (*azure.Future, *retry.Error)
 
 	// WaitForUpdateResult waits for the response of the update request
-	WaitForUpdateResult(ctx context.Context, future *azure.Future, resourceGroupName, source string) *retry.Error
+	WaitForUpdateResult(ctx context.Context, future *azure.Future, resourceGroupName, source string) (*compute.VirtualMachine, *retry.Error)
 
 	// Delete deletes a VirtualMachine.
 	Delete(ctx context.Context, resourceGroupName string, VMName string) *retry.Error
diff --git a/pkg/azureclients/vmclient/mockvmclient/interface.go b/pkg/azureclients/vmclient/mockvmclient/interface.go
index 4ecd399f5..29845eec0 100644
--- a/pkg/azureclients/vmclient/mockvmclient/interface.go
+++ b/pkg/azureclients/vmclient/mockvmclient/interface.go
@@ -143,11 +143,12 @@ func (mr *MockInterfaceMockRecorder) ListVmssFlexVMsWithoutInstanceView(ctx, vms
 }
 
 // Update mocks base method.
-func (m *MockInterface) Update(ctx context.Context, resourceGroupName, VMName string, parameters compute.VirtualMachineUpdate, source string) *retry.Error {
+func (m *MockInterface) Update(ctx context.Context, resourceGroupName, VMName string, parameters compute.VirtualMachineUpdate, source string) (*compute.VirtualMachine, *retry.Error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "Update", ctx, resourceGroupName, VMName, parameters, source)
-	ret0, _ := ret[0].(*retry.Error)
-	return ret0
+	ret0, _ := ret[0].(*compute.VirtualMachine)
+	ret1, _ := ret[1].(*retry.Error)
+	return ret0, ret1
 }
 
 // Update indicates an expected call of Update.
@@ -172,11 +173,12 @@ func (mr *MockInterfaceMockRecorder) UpdateAsync(ctx, resourceGroupName, VMName,
 }
 
 // WaitForUpdateResult mocks base method.
-func (m *MockInterface) WaitForUpdateResult(ctx context.Context, future *azure.Future, resourceGroupName, source string) *retry.Error {
+func (m *MockInterface) WaitForUpdateResult(ctx context.Context, future *azure.Future, resourceGroupName, source string) (*compute.VirtualMachine, *retry.Error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "WaitForUpdateResult", ctx, future, resourceGroupName, source)
-	ret0, _ := ret[0].(*retry.Error)
-	return ret0
+	ret0, _ := ret[0].(*compute.VirtualMachine)
+	ret1, _ := ret[1].(*retry.Error)
+	return ret0, ret1
 }
 
 // WaitForUpdateResult indicates an expected call of WaitForUpdateResult.
diff --git a/pkg/provider/azure_controller_common.go b/pkg/provider/azure_controller_common.go
index 74c019b6b..a8feb0893 100644
--- a/pkg/provider/azure_controller_common.go
+++ b/pkg/provider/azure_controller_common.go
@@ -261,9 +261,18 @@ func (c *controllerCommon) AttachDisk(ctx context.Context, async bool, diskName,
 	}
 	c.diskStateMap.Store(disk, "attaching")
 	defer c.diskStateMap.Delete(disk)
-	future, err := vmset.AttachDisk(ctx, nodeName, diskMap)
-	if future == nil {
-		return -1, fmt.Errorf("nil future was returned: %w", err)
+
+	defer func() {
+		// invalidate the cache if there is error in disk attach
+		if err != nil {
+			_ = vmset.DeleteCacheForNode(string(nodeName))
+		}
+	}()
+
+	var future *azure.Future
+	future, err = vmset.AttachDisk(ctx, nodeName, diskMap)
+	if err != nil {
+		return -1, err
 	}
 	// err will be handled by waitForUpdateResult below
 
@@ -286,7 +295,6 @@ func (c *controllerCommon) AttachDisk(ctx context.Context, async bool, diskName,
 // waitForUpdateResult handles asynchronous VM update operations and retries with backoff if OperationPreempted error is observed
 func (c *controllerCommon) waitForUpdateResult(ctx context.Context, vmset VMSet, nodeName types.NodeName, future *azure.Future, updateErr error) (err error) {
 	err = updateErr
-
 	if err == nil {
 		err = vmset.WaitForUpdateResult(ctx, future, nodeName, "attach_disk")
 	}
@@ -308,7 +316,6 @@ func (c *controllerCommon) waitForUpdateResult(ctx context.Context, vmset VMSet,
 	if err != nil && configAccepted(future) {
 		err = retry.NewPartialUpdateError(err.Error())
 	}
-
 	return
 }
 
@@ -423,7 +430,18 @@ func (c *controllerCommon) UpdateVM(ctx context.Context, nodeName types.NodeName
 	node := strings.ToLower(string(nodeName))
 	c.lockMap.LockEntry(node)
 	defer c.lockMap.UnlockEntry(node)
-	return vmset.UpdateVM(ctx, nodeName)
+
+	defer func() {
+		// invalidate the cache if there is an error with UpdateVM operation
+		if err != nil {
+			_ = vmset.DeleteCacheForNode(string(nodeName))
+		}
+	}()
+
+	klog.V(2).Infof("azureDisk - update: vm(%s)", nodeName)
+	err = vmset.UpdateVM(ctx, nodeName)
+	klog.V(2).Infof("azureDisk - update: vm(%s) returned with %v", err)
+	return err
 }
 
 func (c *controllerCommon) insertDetachDiskRequest(diskName, diskURI, nodeName string) error {
diff --git a/pkg/provider/azure_controller_standard.go b/pkg/provider/azure_controller_standard.go
index c67172eb4..e6e2b054e 100644
--- a/pkg/provider/azure_controller_standard.go
+++ b/pkg/provider/azure_controller_standard.go
@@ -31,6 +31,7 @@ import (
 
 	azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
 	"sigs.k8s.io/cloud-provider-azure/pkg/consts"
+	"sigs.k8s.io/cloud-provider-azure/pkg/retry"
 )
 
 // AttachDisk attaches a disk to vm
@@ -100,10 +101,6 @@ func (as *availabilitySet) AttachDisk(ctx context.Context, nodeName types.NodeNa
 		},
 	}
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - attach disk list(%s)", nodeResourceGroup, vmName, diskMap)
-	// Invalidate the cache right after updating
-	defer func() {
-		_ = as.DeleteCacheForNode(vmName)
-	}()
 
 	future, rerr := as.VirtualMachinesClient.UpdateAsync(ctx, nodeResourceGroup, vmName, newVM, "attach_disk")
 	if rerr != nil {
@@ -124,8 +121,13 @@ func (as *availabilitySet) AttachDisk(ctx context.Context, nodeName types.NodeNa
 }
 
 func (as *availabilitySet) DeleteCacheForNode(nodeName string) error {
-	_ = as.cloud.vmCache.Delete(nodeName)
-	return nil
+	err := as.cloud.vmCache.Delete(nodeName)
+	if err == nil {
+		klog.V(2).Infof("DeleteCacheForNode(%s) successfully", nodeName)
+	} else {
+		klog.Errorf("DeleteCacheForNode(%s) failed with %v", nodeName, err)
+	}
+	return err
 }
 
 // WaitForUpdateResult waits for the response of the update request
@@ -136,9 +138,17 @@ func (as *availabilitySet) WaitForUpdateResult(ctx context.Context, future *azur
 		return err
 	}
 
-	if rerr := as.VirtualMachinesClient.WaitForUpdateResult(ctx, future, nodeResourceGroup, source); rerr != nil {
+	result, rerr := as.VirtualMachinesClient.WaitForUpdateResult(ctx, future, nodeResourceGroup, source)
+	if rerr != nil {
 		return rerr.Error()
 	}
+
+	// clean node cache first and then update cache
+	_ = as.DeleteCacheForNode(vmName)
+	if result != nil && result.VirtualMachineProperties != nil {
+		// if we have an updated result, we update the vmss vm cache
+		as.updateCache(vmName, result)
+	}
 	return nil
 }
 
@@ -198,19 +208,28 @@ func (as *availabilitySet) DetachDisk(ctx context.Context, nodeName types.NodeNa
 		},
 	}
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - detach disk list(%s)", nodeResourceGroup, vmName, nodeName, diskMap)
-	// Invalidate the cache right after updating
+
+	var result *compute.VirtualMachine
+	var rerr *retry.Error
 	defer func() {
+		// invalidate the cache right after updating
 		_ = as.DeleteCacheForNode(vmName)
+
+		// update the cache with the updated result only if its not nil
+		// and contains the VirtualMachineProperties
+		if rerr == nil && result != nil && result.VirtualMachineProperties != nil {
+			as.updateCache(vmName, result)
+		}
 	}()
 
-	rerr := as.VirtualMachinesClient.Update(ctx, nodeResourceGroup, vmName, newVM, "detach_disk")
+	result, rerr = as.VirtualMachinesClient.Update(ctx, nodeResourceGroup, vmName, newVM, "detach_disk")
 	if rerr != nil {
 		klog.Errorf("azureDisk - detach disk list(%s) on rg(%s) vm(%s) failed, err: %v", diskMap, nodeResourceGroup, vmName, rerr)
 		if rerr.HTTPStatusCode == http.StatusNotFound {
 			klog.Errorf("azureDisk - begin to filterNonExistingDisks(%v) on rg(%s) vm(%s)", diskMap, nodeResourceGroup, vmName)
 			disks := as.filterNonExistingDisks(ctx, *vm.StorageProfile.DataDisks)
 			newVM.VirtualMachineProperties.StorageProfile.DataDisks = &disks
-			rerr = as.VirtualMachinesClient.Update(ctx, nodeResourceGroup, vmName, newVM, "detach_disk")
+			result, rerr = as.VirtualMachinesClient.Update(ctx, nodeResourceGroup, vmName, newVM, "detach_disk")
 		}
 	}
 
@@ -227,7 +246,6 @@ func (as *availabilitySet) UpdateVM(ctx context.Context, nodeName types.NodeName
 	if err != nil {
 		return err
 	}
-
 	return as.WaitForUpdateResult(ctx, future, nodeName, "update_vm")
 }
 
@@ -238,20 +256,19 @@ func (as *availabilitySet) UpdateVMAsync(ctx context.Context, nodeName types.Nod
 	if err != nil {
 		return nil, err
 	}
-	klog.V(2).Infof("azureDisk - update(%s): vm(%s)", nodeResourceGroup, vmName)
-	// Invalidate the cache right after updating
-	defer func() {
-		_ = as.DeleteCacheForNode(vmName)
-	}()
 
 	future, rerr := as.VirtualMachinesClient.UpdateAsync(ctx, nodeResourceGroup, vmName, compute.VirtualMachineUpdate{}, "update_vm")
-	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - returned with %v", nodeResourceGroup, vmName, rerr)
 	if rerr != nil {
 		return future, rerr.Error()
 	}
 	return future, nil
 }
 
+func (as *availabilitySet) updateCache(nodeName string, vm *compute.VirtualMachine) {
+	as.cloud.vmCache.Update(nodeName, vm)
+	klog.V(2).Infof("updateCache(%s) successfully", nodeName)
+}
+
 // GetDataDisks gets a list of data disks attached to the node.
 func (as *availabilitySet) GetDataDisks(nodeName types.NodeName, crt azcache.AzureCacheReadType) ([]compute.DataDisk, *string, error) {
 	vm, err := as.getVirtualMachine(nodeName, crt)
diff --git a/pkg/provider/azure_controller_vmss.go b/pkg/provider/azure_controller_vmss.go
index 4178913f7..56df9d99b 100644
--- a/pkg/provider/azure_controller_vmss.go
+++ b/pkg/provider/azure_controller_vmss.go
@@ -107,11 +107,6 @@ func (ss *ScaleSet) AttachDisk(ctx context.Context, nodeName types.NodeName, dis
 		},
 	}
 
-	// Invalidate the cache right after updating
-	defer func() {
-		_ = ss.DeleteCacheForNode(vmName)
-	}()
-
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - attach disk list(%s)", nodeResourceGroup, nodeName, diskMap)
 	future, rerr := ss.VirtualMachineScaleSetVMsClient.UpdateAsync(ctx, nodeResourceGroup, vm.VMSSName, vm.InstanceID, newVM, "attach_disk")
 	if rerr != nil {
@@ -139,23 +134,30 @@ func (ss *ScaleSet) WaitForUpdateResult(ctx context.Context, future *azure.Futur
 		return err
 	}
 
-	var result *compute.VirtualMachineScaleSetVM
-	var rerr *retry.Error
-	defer func() {
-		if rerr == nil && result != nil && result.VirtualMachineScaleSetVMProperties != nil {
-			// If we have an updated result, we update the vmss vm cache
-			vm, err := ss.getVmssVM(vmName, azcache.CacheReadTypeDefault)
-			if err != nil {
-				return
-			}
-			_ = ss.updateCache(vmName, nodeResourceGroup, vm.VMSSName, vm.InstanceID, result)
-		}
-	}()
-
-	result, rerr = ss.VirtualMachineScaleSetVMsClient.WaitForUpdateResult(ctx, future, nodeResourceGroup, source)
+	result, rerr := ss.VirtualMachineScaleSetVMsClient.WaitForUpdateResult(ctx, future, nodeResourceGroup, source)
 	if rerr != nil {
 		return rerr.Error()
 	}
+
+	var vmssName, instanceID string
+	if result != nil && result.VirtualMachineScaleSetVMProperties != nil {
+		// get vmssName, instanceID from cache first
+		vm, err := ss.getVmssVM(vmName, azcache.CacheReadTypeDefault)
+		if err == nil && vm != nil {
+			vmssName = vm.VMSSName
+			instanceID = vm.InstanceID
+		} else {
+			klog.Errorf("getVmssVM failed with error(%v) or nil pointer", err)
+		}
+	}
+
+	// clean node cache first and then update cache
+	_ = ss.DeleteCacheForNode(vmName)
+	if vmssName != "" && instanceID != "" {
+		if err := ss.updateCache(vmName, nodeResourceGroup, vmssName, instanceID, result); err != nil {
+			klog.Errorf("updateCache(%s, %s, %s, %s) failed with error: %v", vmName, nodeResourceGroup, vmssName, instanceID, err)
+		}
+	}
 	return nil
 }
 
@@ -219,30 +221,23 @@ func (ss *ScaleSet) DetachDisk(ctx context.Context, nodeName types.NodeName, dis
 		},
 	}
 
-	var updateResult *compute.VirtualMachineScaleSetVM
+	var result *compute.VirtualMachineScaleSetVM
 	var rerr *retry.Error
 
 	defer func() {
-		// If there is an error with Update operation,
-		// invalidate the cache
-		if rerr != nil {
-			_ = ss.DeleteCacheForNode(vmName)
-			return
-		}
+		_ = ss.DeleteCacheForNode(vmName)
 
 		// Update the cache with the updated result only if its not nil
 		// and contains the VirtualMachineScaleSetVMProperties
-		if updateResult != nil && updateResult.VirtualMachineScaleSetVMProperties != nil {
-			if err := ss.updateCache(vmName, nodeResourceGroup, vm.VMSSName, vm.InstanceID, updateResult); err != nil {
-				klog.Errorf("updateCache(%s, %s, %s) failed with error: %v", vmName, nodeResourceGroup, vm.VMSSName, vm.InstanceID, err)
-				// if err faced during updating cache, invalidate the cache
-				_ = ss.DeleteCacheForNode(vmName)
+		if rerr == nil && result != nil && result.VirtualMachineScaleSetVMProperties != nil {
+			if err := ss.updateCache(vmName, nodeResourceGroup, vm.VMSSName, vm.InstanceID, result); err != nil {
+				klog.Errorf("updateCache(%s, %s, %s, %s) failed with error: %v", vmName, nodeResourceGroup, vm.VMSSName, vm.InstanceID, err)
 			}
 		}
 	}()
 
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - detach disk list(%s)", nodeResourceGroup, nodeName, diskMap)
-	updateResult, rerr = ss.VirtualMachineScaleSetVMsClient.Update(ctx, nodeResourceGroup, vm.VMSSName, vm.InstanceID, newVM,
+	result, rerr = ss.VirtualMachineScaleSetVMsClient.Update(ctx, nodeResourceGroup, vm.VMSSName, vm.InstanceID, newVM,
 		"detach_disk")
 	if rerr != nil {
 		klog.Errorf("azureDisk - detach disk list(%s) on rg(%s) vm(%s) failed, err: %v", diskMap, nodeResourceGroup, nodeName, rerr)
@@ -250,7 +245,7 @@ func (ss *ScaleSet) DetachDisk(ctx context.Context, nodeName types.NodeName, dis
 			klog.Errorf("azureDisk - begin to filterNonExistingDisks(%v) on rg(%s) vm(%s)", diskMap, nodeResourceGroup, nodeName)
 			disks := ss.filterNonExistingDisks(ctx, *newVM.VirtualMachineScaleSetVMProperties.StorageProfile.DataDisks)
 			newVM.VirtualMachineScaleSetVMProperties.StorageProfile.DataDisks = &disks
-			updateResult, rerr = ss.VirtualMachineScaleSetVMsClient.Update(ctx, nodeResourceGroup, vm.VMSSName, vm.InstanceID, newVM, "detach_disk")
+			result, rerr = ss.VirtualMachineScaleSetVMsClient.Update(ctx, nodeResourceGroup, vm.VMSSName, vm.InstanceID, newVM, "detach_disk")
 		}
 	}
 
@@ -267,7 +262,6 @@ func (ss *ScaleSet) UpdateVM(ctx context.Context, nodeName types.NodeName) error
 	if err != nil {
 		return err
 	}
-
 	return ss.WaitForUpdateResult(ctx, future, nodeName, "update_vm")
 }
 
@@ -284,23 +278,7 @@ func (ss *ScaleSet) UpdateVMAsync(ctx context.Context, nodeName types.NodeName)
 		return nil, err
 	}
 
-	var future *azure.Future
-	var rerr *retry.Error
-
-	// Invalidate the cache right after updating
-	defer func() {
-		// If there is an error with Update operation,
-		// invalidate the cache
-		if rerr != nil {
-			_ = ss.DeleteCacheForNode(vmName)
-			return
-		}
-	}()
-
-	klog.V(2).Infof("azureDisk - update(%s): vm(%s)", nodeResourceGroup, nodeName)
-	future, rerr = ss.VirtualMachineScaleSetVMsClient.UpdateAsync(ctx, nodeResourceGroup, vm.VMSSName, vm.InstanceID, compute.VirtualMachineScaleSetVM{}, "update_vmss_instance")
-
-	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - returned with %v", nodeResourceGroup, nodeName, rerr)
+	future, rerr := ss.VirtualMachineScaleSetVMsClient.UpdateAsync(ctx, nodeResourceGroup, vm.VMSSName, vm.InstanceID, compute.VirtualMachineScaleSetVM{}, "update_vmss_instance")
 	if rerr != nil {
 		return future, rerr.Error()
 	}
diff --git a/pkg/provider/azure_controller_vmssflex.go b/pkg/provider/azure_controller_vmssflex.go
index d526f4719..1c0eb1e51 100644
--- a/pkg/provider/azure_controller_vmssflex.go
+++ b/pkg/provider/azure_controller_vmssflex.go
@@ -21,6 +21,7 @@ import (
 	"fmt"
 	"net/http"
 	"strings"
+	"sync"
 
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-03-01/compute"
 	"github.com/Azure/go-autorest/autorest/azure"
@@ -30,6 +31,7 @@ import (
 
 	azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
 	"sigs.k8s.io/cloud-provider-azure/pkg/consts"
+	"sigs.k8s.io/cloud-provider-azure/pkg/retry"
 )
 
 // AttachDisk attaches a disk to vm
@@ -99,10 +101,6 @@ func (fs *FlexScaleSet) AttachDisk(ctx context.Context, nodeName types.NodeName,
 		},
 	}
 
-	defer func() {
-		_ = fs.DeleteCacheForNode(vmName)
-	}()
-
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - attach disk list(%s)", nodeResourceGroup, vmName, diskMap)
 
 	future, rerr := fs.VirtualMachinesClient.UpdateAsync(ctx, nodeResourceGroup, *vm.Name, newVM, "attach_disk")
@@ -179,20 +177,30 @@ func (fs *FlexScaleSet) DetachDisk(ctx context.Context, nodeName types.NodeName,
 		},
 	}
 
+	var result *compute.VirtualMachine
+	var rerr *retry.Error
 	defer func() {
 		_ = fs.DeleteCacheForNode(vmName)
+
+		// update the cache with the updated result only if its not nil
+		// and contains the VirtualMachineProperties
+		if rerr == nil && result != nil && result.VirtualMachineProperties != nil {
+			if err := fs.updateCache(vmName, result); err != nil {
+				klog.Errorf("updateCache(%s) failed with error: %v", vmName, err)
+			}
+		}
 	}()
 
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - detach disk list(%s)", nodeResourceGroup, vmName, nodeName, diskMap)
 
-	rerr := fs.VirtualMachinesClient.Update(ctx, nodeResourceGroup, *vm.Name, newVM, "detach_disk")
+	result, rerr = fs.VirtualMachinesClient.Update(ctx, nodeResourceGroup, *vm.Name, newVM, "detach_disk")
 	if rerr != nil {
 		klog.Errorf("azureDisk - detach disk list(%s) on rg(%s) vm(%s) failed, err: %v", diskMap, nodeResourceGroup, vmName, rerr)
 		if rerr.HTTPStatusCode == http.StatusNotFound {
 			klog.Errorf("azureDisk - begin to filterNonExistingDisks(%v) on rg(%s) vm(%s)", diskMap, nodeResourceGroup, vmName)
 			disks := fs.filterNonExistingDisks(ctx, *vm.StorageProfile.DataDisks)
 			newVM.VirtualMachineProperties.StorageProfile.DataDisks = &disks
-			rerr = fs.VirtualMachinesClient.Update(ctx, nodeResourceGroup, *vm.Name, newVM, "detach_disk")
+			result, rerr = fs.VirtualMachinesClient.Update(ctx, nodeResourceGroup, *vm.Name, newVM, "detach_disk")
 		}
 	}
 
@@ -210,9 +218,18 @@ func (fs *FlexScaleSet) WaitForUpdateResult(ctx context.Context, future *azure.F
 	if err != nil {
 		return err
 	}
-	if rerr := fs.VirtualMachinesClient.WaitForUpdateResult(ctx, future, nodeResourceGroup, source); rerr != nil {
+	result, rerr := fs.VirtualMachinesClient.WaitForUpdateResult(ctx, future, nodeResourceGroup, source)
+	if rerr != nil {
 		return rerr.Error()
 	}
+
+	// clean node cache first and then update cache
+	_ = fs.DeleteCacheForNode(vmName)
+	if result != nil && result.VirtualMachineProperties != nil {
+		if err := fs.updateCache(vmName, result); err != nil {
+			klog.Errorf("updateCache(%s) failed with error: %v", vmName, err)
+		}
+	}
 	return nil
 }
 
@@ -222,18 +239,16 @@ func (fs *FlexScaleSet) UpdateVM(ctx context.Context, nodeName types.NodeName) e
 	if err != nil {
 		return err
 	}
-
 	return fs.WaitForUpdateResult(ctx, future, nodeName, "update_vm")
 }
 
 // UpdateVMAsync updates a vm asynchronously
 func (fs *FlexScaleSet) UpdateVMAsync(ctx context.Context, nodeName types.NodeName) (*azure.Future, error) {
 	vmName := mapNodeNameToVMName(nodeName)
-
 	vm, err := fs.getVmssFlexVM(vmName, azcache.CacheReadTypeDefault)
 	if err != nil {
 		// if host doesn't exist, no need to update
-		klog.Warningf("azureDisk - cannot find node %s, skip updating vm)", nodeName)
+		klog.Warningf("azureDisk - cannot find node %s, skip updating vm", nodeName)
 		return nil, nil
 	}
 	nodeResourceGroup, err := fs.GetNodeResourceGroup(vmName)
@@ -241,20 +256,48 @@ func (fs *FlexScaleSet) UpdateVMAsync(ctx context.Context, nodeName types.NodeNa
 		return nil, err
 	}
 
-	defer func() {
-		_ = fs.DeleteCacheForNode(vmName)
-	}()
-
-	klog.V(2).Infof("azureDisk - update(%s): vm(%s)", nodeResourceGroup, vmName)
-
 	future, rerr := fs.VirtualMachinesClient.UpdateAsync(ctx, nodeResourceGroup, *vm.Name, compute.VirtualMachineUpdate{}, "update_vm")
-	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - returned with %v", nodeResourceGroup, vmName, rerr)
 	if rerr != nil {
 		return future, rerr.Error()
 	}
 	return future, nil
 }
 
+func (fs *FlexScaleSet) updateCache(nodeName string, vm *compute.VirtualMachine) error {
+	if vm == nil {
+		return fmt.Errorf("vm is nil")
+	}
+	if vm.Name == nil {
+		return fmt.Errorf("vm.Name is nil")
+	}
+	if vm.VirtualMachineProperties == nil {
+		return fmt.Errorf("vm.VirtualMachineProperties is nil")
+	}
+	if vm.OsProfile == nil || vm.OsProfile.ComputerName == nil {
+		return fmt.Errorf("vm.OsProfile.ComputerName is nil")
+	}
+
+	vmssFlexID, err := fs.getNodeVmssFlexID(nodeName)
+	if err != nil {
+		return err
+	}
+
+	fs.lockMap.LockEntry(vmssFlexID)
+	defer fs.lockMap.UnlockEntry(vmssFlexID)
+	cached, err := fs.vmssFlexVMCache.Get(vmssFlexID, azcache.CacheReadTypeDefault)
+	if err != nil {
+		return err
+	}
+	vmMap := cached.(*sync.Map)
+	vmMap.Store(nodeName, vm)
+	fs.vmssFlexVMCache.Update(vmssFlexID, vmMap)
+
+	fs.vmssFlexVMNameToVmssID.Store(strings.ToLower(*vm.OsProfile.ComputerName), vmssFlexID)
+	fs.vmssFlexVMNameToNodeName.Store(*vm.Name, strings.ToLower(*vm.OsProfile.ComputerName))
+	klog.V(2).Infof("updateCache(%s) for vmssFlexID(%s) successfully", nodeName, vmssFlexID)
+	return nil
+}
+
 // GetDataDisks gets a list of data disks attached to the node.
 func (fs *FlexScaleSet) GetDataDisks(nodeName types.NodeName, crt azcache.AzureCacheReadType) ([]compute.DataDisk, *string, error) {
 	vm, err := fs.getVmssFlexVM(string(nodeName), crt)
diff --git a/pkg/provider/azure_managedDiskController.go b/pkg/provider/azure_managedDiskController.go
index b43e549c6..5685b10cf 100644
--- a/pkg/provider/azure_managedDiskController.go
+++ b/pkg/provider/azure_managedDiskController.go
@@ -254,7 +254,7 @@ func (c *ManagedDiskController) CreateManagedDisk(ctx context.Context, options *
 	if options.SkipGetDiskOperation {
 		klog.Warningf("azureDisk - GetDisk(%s, StorageAccountType:%s) is throttled, unable to confirm provisioningState in poll process", options.DiskName, options.StorageAccountType)
 	} else {
-		err = kwait.ExponentialBackoff(defaultBackOff, func() (bool, error) {
+		err = kwait.ExponentialBackoffWithContext(ctx, defaultBackOff, func() (bool, error) {
 			provisionState, id, err := c.GetDisk(ctx, subsID, rg, options.DiskName)
 			if err == nil {
 				if id != "" {
diff --git a/pkg/provider/azure_vmss_cache.go b/pkg/provider/azure_vmss_cache.go
index 47ff879cb..231cd94a6 100644
--- a/pkg/provider/azure_vmss_cache.go
+++ b/pkg/provider/azure_vmss_cache.go
@@ -152,11 +152,6 @@ func (ss *ScaleSet) getVMSSVMsFromCache(resourceGroup, vmssName string, crt azca
 	return virtualMachines, nil
 }
 
-// gcVMSSVMCache delete stale VMSS VMs caches from deleted VMSSes.
-func (ss *ScaleSet) gcVMSSVMCache() error {
-	return ss.vmssCache.Delete(consts.VMSSKey)
-}
-
 // newVMSSVirtualMachinesCache instantiates a new VMs cache for VMs belonging to the provided VMSS.
 func (ss *ScaleSet) newVMSSVirtualMachinesCache() (*azcache.TimedCache, error) {
 	vmssVirtualMachinesCacheTTL := time.Duration(ss.Config.VmssVirtualMachinesCacheTTLInSeconds) * time.Second
@@ -261,7 +256,7 @@ func (ss *ScaleSet) newVMSSVirtualMachinesCache() (*azcache.TimedCache, error) {
 func (ss *ScaleSet) DeleteCacheForNode(nodeName string) error {
 	vmManagementType, err := ss.getVMManagementTypeByNodeName(nodeName, azcache.CacheReadTypeUnsafe)
 	if err != nil {
-		klog.Errorf("Failed to check VM management type: %v", err)
+		klog.Errorf("getVMManagementTypeByNodeName(%s) failed with %v", nodeName, err)
 		return err
 	}
 
@@ -276,20 +271,23 @@ func (ss *ScaleSet) DeleteCacheForNode(nodeName string) error {
 
 	node, err := ss.getNodeIdentityByNodeName(nodeName, azcache.CacheReadTypeUnsafe)
 	if err != nil {
-		klog.Errorf("DeleteCacheForNode(%s) failed with error: %v", nodeName, err)
+		klog.Errorf("getNodeIdentityByNodeName(%s) failed with %v", nodeName, err)
 		return err
 	}
+	// get sync.Map cache and remove the node from the cache
+	cacheKey := getVMSSVMCacheKey(node.resourceGroup, node.vmssName)
+	ss.lockMap.LockEntry(cacheKey)
+	defer ss.lockMap.UnlockEntry(cacheKey)
 
-	err = ss.vmssVMCache.Delete(getVMSSVMCacheKey(node.resourceGroup, node.vmssName))
+	virtualMachines, err := ss.getVMSSVMsFromCache(node.resourceGroup, node.vmssName, azcache.CacheReadTypeUnsafe)
 	if err != nil {
-		klog.Errorf("DeleteCacheForNode(%s) failed to remove from vmssVMCache with error: %v", nodeName, err)
+		klog.Errorf("getVMSSVMsFromCache(%s, %s) failed with %v", node.resourceGroup, node.vmssName, err)
 		return err
 	}
 
-	if err := ss.gcVMSSVMCache(); err != nil {
-		klog.Errorf("DeleteCacheForNode(%s) failed to gc stale vmss caches: %v", nodeName, err)
-	}
-
+	virtualMachines.Delete(nodeName)
+	ss.vmssVMCache.Update(cacheKey, virtualMachines)
+	klog.V(2).Infof("DeleteCacheForNode(%s, %s, %s) successfully", node.resourceGroup, node.vmssName, nodeName)
 	return nil
 }
 
@@ -301,12 +299,9 @@ func (ss *ScaleSet) updateCache(nodeName, resourceGroupName, vmssName, instanceI
 
 	virtualMachines, err := ss.getVMSSVMsFromCache(resourceGroupName, vmssName, azcache.CacheReadTypeUnsafe)
 	if err != nil {
-		err = fmt.Errorf("updateCache(%s, %s, %s) failed getting vmCache with error: %w", vmssName, resourceGroupName, nodeName, err)
-		return err
+		return fmt.Errorf("updateCache(%s, %s, %s) failed getting vmCache with error: %w", vmssName, resourceGroupName, nodeName, err)
 	}
 
-	localCache := &sync.Map{}
-
 	vmssVMCacheEntry := &VMSSVirtualMachineEntry{
 		ResourceGroup:  resourceGroupName,
 		VMSSName:       vmssName,
@@ -315,8 +310,10 @@ func (ss *ScaleSet) updateCache(nodeName, resourceGroupName, vmssName, instanceI
 		LastUpdate:     time.Now().UTC(),
 	}
 
+	localCache := &sync.Map{}
 	localCache.Store(nodeName, vmssVMCacheEntry)
 
+	// copy all elements except current VM to localCache
 	virtualMachines.Range(func(key, value interface{}) bool {
 		if key.(string) != nodeName {
 			localCache.Store(key.(string), value.(*VMSSVirtualMachineEntry))
@@ -325,7 +322,7 @@ func (ss *ScaleSet) updateCache(nodeName, resourceGroupName, vmssName, instanceI
 	})
 
 	ss.vmssVMCache.Update(cacheKey, localCache)
-	klog.V(4).Infof("updateCache(%s, %s, %s) for cacheKey(%s) updated successfully", vmssName, resourceGroupName, nodeName, cacheKey)
+	klog.V(2).Infof("updateCache(%s, %s, %s) for cacheKey(%s) updated successfully", vmssName, resourceGroupName, nodeName, cacheKey)
 	return nil
 }
 
diff --git a/pkg/provider/azure_vmssflex_cache.go b/pkg/provider/azure_vmssflex_cache.go
index efca4cb12..9371f9f83 100644
--- a/pkg/provider/azure_vmssflex_cache.go
+++ b/pkg/provider/azure_vmssflex_cache.go
@@ -317,17 +317,28 @@ func (fs *FlexScaleSet) getVmssFlexByName(vmssFlexName string) (*compute.Virtual
 func (fs *FlexScaleSet) DeleteCacheForNode(nodeName string) error {
 	vmssFlexID, err := fs.getNodeVmssFlexID(nodeName)
 	if err != nil {
+		klog.Errorf("getNodeVmssFlexID(%s) failed with %v", nodeName, err)
 		return err
 	}
 
+	fs.lockMap.LockEntry(vmssFlexID)
+	defer fs.lockMap.UnlockEntry(vmssFlexID)
 	cached, err := fs.vmssFlexVMCache.Get(vmssFlexID, azcache.CacheReadTypeDefault)
 	if err != nil {
+		klog.Errorf("vmssFlexVMCache.Get(%s, %s) failed with %v", vmssFlexID, nodeName, err)
+		return err
+	}
+	if cached == nil {
+		err := fmt.Errorf("nil cache returned from %s", vmssFlexID)
+		klog.Errorf("DeleteCacheForNode(%s, %s) failed with %v", vmssFlexID, nodeName, err)
 		return err
 	}
 	vmMap := cached.(*sync.Map)
 	vmMap.Delete(nodeName)
 
+	fs.vmssFlexVMCache.Update(vmssFlexID, vmMap)
 	fs.vmssFlexVMNameToVmssID.Delete(nodeName)
 
+	klog.V(2).Infof("DeleteCacheForNode(%s, %s) successfully", vmssFlexID, nodeName)
 	return nil
 }


Test patch:
diff --git a/pkg/azureclients/vmclient/azure_vmclient_test.go b/pkg/azureclients/vmclient/azure_vmclient_test.go
index 346d210c9..a0646b2a9 100644
--- a/pkg/azureclients/vmclient/azure_vmclient_test.go
+++ b/pkg/azureclients/vmclient/azure_vmclient_test.go
@@ -845,7 +845,7 @@ func TestUpdate(t *testing.T) {
 	armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1)
 
 	vmClient := getTestVMClient(armClient)
-	rerr := vmClient.Update(context.TODO(), "rg", "vm1", testVM, "test")
+	_, rerr := vmClient.Update(context.TODO(), "rg", "vm1", testVM, "test")
 	assert.Nil(t, rerr)
 }
 
@@ -911,7 +911,7 @@ func TestWaitForUpdateResult(t *testing.T) {
 		armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1)
 
 		vmClient := getTestVMClient(armClient)
-		err := vmClient.WaitForUpdateResult(context.TODO(), &azure.Future{}, "rg", "test")
+		_, err := vmClient.WaitForUpdateResult(context.TODO(), &azure.Future{}, "rg", "test")
 		assert.Equal(t, err, test.expectedResult)
 	}
 }
@@ -930,7 +930,7 @@ func TestUpdateWithUpdateResponderError(t *testing.T) {
 	armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1)
 
 	vmClient := getTestVMClient(armClient)
-	rerr := vmClient.Update(context.TODO(), "rg", "vm1", testVM, "test")
+	_, rerr := vmClient.Update(context.TODO(), "rg", "vm1", testVM, "test")
 	assert.NotNil(t, rerr)
 }
 
@@ -946,7 +946,7 @@ func TestUpdateNeverRateLimiter(t *testing.T) {
 	armClient := mockarmclient.NewMockInterface(ctrl)
 	vmClient := getTestVMClientWithNeverRateLimiter(armClient)
 	testVM := compute.VirtualMachineUpdate{}
-	rerr := vmClient.Update(context.TODO(), "rg", "vm1", testVM, "test")
+	_, rerr := vmClient.Update(context.TODO(), "rg", "vm1", testVM, "test")
 	assert.NotNil(t, rerr)
 	assert.Equal(t, vmUpdateErr, rerr)
 }
@@ -964,7 +964,7 @@ func TestUpdateRetryAfterReader(t *testing.T) {
 	testVM := compute.VirtualMachineUpdate{}
 	armClient := mockarmclient.NewMockInterface(ctrl)
 	vmClient := getTestVMClientWithRetryAfterReader(armClient)
-	rerr := vmClient.Update(context.TODO(), "rg", "vm1", testVM, "test")
+	_, rerr := vmClient.Update(context.TODO(), "rg", "vm1", testVM, "test")
 	assert.NotNil(t, rerr)
 	assert.Equal(t, vmUpdateErr, rerr)
 }
@@ -990,7 +990,7 @@ func TestUpdateThrottle(t *testing.T) {
 	armClient.EXPECT().CloseResponse(gomock.Any(), gomock.Any()).Times(1)
 
 	vmClient := getTestVMClient(armClient)
-	rerr := vmClient.Update(context.TODO(), "rg", "vm1", testVM, "test")
+	_, rerr := vmClient.Update(context.TODO(), "rg", "vm1", testVM, "test")
 	assert.NotNil(t, rerr)
 	assert.Equal(t, throttleErr, rerr)
 }
diff --git a/pkg/provider/azure_controller_common_test.go b/pkg/provider/azure_controller_common_test.go
index f923ecd79..9c770a65d 100644
--- a/pkg/provider/azure_controller_common_test.go
+++ b/pkg/provider/azure_controller_common_test.go
@@ -105,8 +105,8 @@ func TestCommonAttachDisk(t *testing.T) {
 		initVM(testCloud, expectedVMs)
 		mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
 		mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(statusCode)).MaxTimes(1)
-		mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).MaxTimes(1)
-		mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(result).MaxTimes(1)
+		mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).MaxTimes(1)
+		mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil, result).MaxTimes(1)
 	}
 
 	maxShare := int32(1)
@@ -238,8 +238,8 @@ func TestCommonAttachDisk(t *testing.T) {
 				mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
 				mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(200)).AnyTimes()
 				gomock.InOrder(
-					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(result),
-					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil),
+					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil, result),
+					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil, nil),
 				)
 			},
 		},
@@ -257,7 +257,7 @@ func TestCommonAttachDisk(t *testing.T) {
 				defaultSetup(testCloud, expectedVMs, statusCode, result)
 				mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
 				mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(200)).AnyTimes()
-				mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(result).AnyTimes()
+				mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil, result).AnyTimes()
 			},
 			contextDuration:      time.Second,
 			isContextDeadlineErr: true,
@@ -322,8 +322,8 @@ func TestWaitForUpdateResult(t *testing.T) {
 	defaultSetup := func(testCloud *Cloud, statusCode int, result *retry.Error) {
 		mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
 		mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(statusCode)).MaxTimes(1)
-		mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).MaxTimes(1)
-		mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(result).MaxTimes(1)
+		mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).MaxTimes(1)
+		mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil, result).MaxTimes(1)
 	}
 
 	testCases := []struct {
@@ -371,8 +371,8 @@ func TestWaitForUpdateResult(t *testing.T) {
 				mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
 				mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(200))
 				gomock.InOrder(
-					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(result),
-					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil),
+					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil, result),
+					mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil, nil),
 				)
 			},
 		},
@@ -387,7 +387,7 @@ func TestWaitForUpdateResult(t *testing.T) {
 				defaultSetup(testCloud, statusCode, result)
 				mockVMsClient := testCloud.VirtualMachinesClient.(*mockvmclient.MockInterface)
 				mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).DoAndReturn(fakeUpdateAsync(200)).AnyTimes()
-				mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(result).AnyTimes()
+				mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil, result).AnyTimes()
 			},
 			contextDuration:   time.Second,
 			expectedErrorType: context.DeadlineExceeded,
@@ -512,7 +512,7 @@ func TestCommonAttachDiskWithVMSS(t *testing.T) {
 			if len(expectedVMs) == 0 {
 				mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any()).Return(compute.VirtualMachine{}, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
 			}
-			mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
+			mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
 		}
 
 		lun, err := common.AttachDisk(ctx, true, "test", diskURI, test.nodeName, compute.CachingTypesReadOnly, test.existedDisk)
@@ -573,7 +573,7 @@ func TestCommonDetachDisk(t *testing.T) {
 		if len(expectedVMs) == 0 {
 			mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any()).Return(compute.VirtualMachine{}, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
 		}
-		mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
+		mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
 
 		err := common.DetachDisk(ctx, test.diskName, diskURI, test.nodeName)
 		assert.Equal(t, test.expectedErr, err != nil, "TestCase[%d]: %s, err: %v", i, test.desc, err)
@@ -648,9 +648,9 @@ func TestCommonUpdateVM(t *testing.T) {
 		if test.isErrorRetriable {
 			testCloud.CloudProviderBackoff = true
 			testCloud.ResourceRequestBackoff = wait.Backoff{Steps: 1}
-			mockVMsClient.EXPECT().WaitForUpdateResult(ctx, &future, testCloud.ResourceGroup, gomock.Any()).Return(&retry.Error{HTTPStatusCode: http.StatusBadRequest, Retriable: true, RawError: fmt.Errorf("Retriable: true")}).AnyTimes()
+			mockVMsClient.EXPECT().WaitForUpdateResult(ctx, &future, testCloud.ResourceGroup, gomock.Any()).Return(nil, &retry.Error{HTTPStatusCode: http.StatusBadRequest, Retriable: true, RawError: fmt.Errorf("Retriable: true")}).AnyTimes()
 		} else {
-			mockVMsClient.EXPECT().WaitForUpdateResult(ctx, &future, testCloud.ResourceGroup, gomock.Any()).Return(nil).AnyTimes()
+			mockVMsClient.EXPECT().WaitForUpdateResult(ctx, &future, testCloud.ResourceGroup, gomock.Any()).Return(nil, nil).AnyTimes()
 		}
 
 		err = common.UpdateVM(ctx, test.nodeName)
diff --git a/pkg/provider/azure_controller_standard_test.go b/pkg/provider/azure_controller_standard_test.go
index 3be4edba5..16ce41612 100644
--- a/pkg/provider/azure_controller_standard_test.go
+++ b/pkg/provider/azure_controller_standard_test.go
@@ -190,9 +190,9 @@ func TestStandardDetachDisk(t *testing.T) {
 		}
 		mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, "vm2", gomock.Any()).Return(compute.VirtualMachine{}, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
 		if test.isDetachFail {
-			mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(&retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
+			mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
 		} else {
-			mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, "vm1", gomock.Any(), "detach_disk").Return(nil).AnyTimes()
+			mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, "vm1", gomock.Any(), "detach_disk").Return(nil, nil).AnyTimes()
 		}
 
 		diskMap := map[string]string{}
@@ -267,9 +267,10 @@ func TestStandardUpdateVM(t *testing.T) {
 		mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(&future, err).AnyTimes()
 
 		if test.isDetachFail {
-			mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), &future, testCloud.ResourceGroup, gomock.Any()).Return(&retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
+			mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), &future, testCloud.ResourceGroup, gomock.Any()).Return(nil, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
 		} else {
-			mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), &future, testCloud.ResourceGroup, gomock.Any()).Return(nil).AnyTimes()
+			mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), &future, testCloud.ResourceGroup, gomock.Any()).Return(nil, nil).AnyTimes()
+			mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
 		}
 
 		err = vmSet.UpdateVM(ctx, test.nodeName)
@@ -361,7 +362,7 @@ func TestGetDataDisks(t *testing.T) {
 			mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, *vm.Name, gomock.Any()).Return(vm, nil).AnyTimes()
 		}
 		mockVMsClient.EXPECT().Get(gomock.Any(), testCloud.ResourceGroup, gomock.Not("vm1"), gomock.Any()).Return(compute.VirtualMachine{}, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
-		mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
+		mockVMsClient.EXPECT().Update(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
 
 		dataDisks, _, err := vmSet.GetDataDisks(test.nodeName, test.crt)
 		assert.Equal(t, test.expectedDataDisks, dataDisks, "TestCase[%d]: %s", i, test.desc)
diff --git a/pkg/provider/azure_controller_vmssflex_test.go b/pkg/provider/azure_controller_vmssflex_test.go
index 5eb06864a..864429c39 100644
--- a/pkg/provider/azure_controller_vmssflex_test.go
+++ b/pkg/provider/azure_controller_vmssflex_test.go
@@ -204,7 +204,7 @@ func TestDettachDiskWithVmssFlex(t *testing.T) {
 		mockVMClient.EXPECT().ListVmssFlexVMsWithoutInstanceView(gomock.Any(), gomock.Any()).Return(tc.testVMListWithoutInstanceView, tc.vmListErr).AnyTimes()
 		mockVMClient.EXPECT().ListVmssFlexVMsWithOnlyInstanceView(gomock.Any(), gomock.Any()).Return(tc.testVMListWithOnlyInstanceView, tc.vmListErr).AnyTimes()
 
-		mockVMClient.EXPECT().Update(gomock.Any(), gomock.Any(), tc.vmName, gomock.Any(), "detach_disk").Return(tc.vmssFlexVMUpdateError).AnyTimes()
+		mockVMClient.EXPECT().Update(gomock.Any(), gomock.Any(), tc.vmName, gomock.Any(), "detach_disk").Return(nil, tc.vmssFlexVMUpdateError).AnyTimes()
 
 		err = fs.DetachDisk(ctx, tc.nodeName, tc.diskMap)
 		if tc.expectedErr == nil {
@@ -272,7 +272,8 @@ func TestUpdateVMWithVmssFlex(t *testing.T) {
 		future, err := azure.NewFutureFromResponse(r)
 
 		mockVMClient.EXPECT().UpdateAsync(gomock.Any(), gomock.Any(), tc.vmName, gomock.Any(), "update_vm").Return(&future, err).AnyTimes()
-		mockVMClient.EXPECT().WaitForUpdateResult(gomock.Any(), &future, gomock.Any(), gomock.Any()).Return(tc.vmssFlexVMUpdateError).AnyTimes()
+		mockVMClient.EXPECT().WaitForUpdateResult(gomock.Any(), &future, gomock.Any(), gomock.Any()).Return(nil, tc.vmssFlexVMUpdateError).AnyTimes()
+		mockVMClient.EXPECT().Update(gomock.Any(), gomock.Any(), tc.vmName, gomock.Any(), "update_vm").Return(nil, tc.vmssFlexVMUpdateError).AnyTimes()
 
 		err = fs.UpdateVM(ctx, tc.nodeName)
 
@@ -341,5 +342,57 @@ func TestGetDataDisksWithVmssFlex(t *testing.T) {
 			assert.EqualError(t, err, tc.expectedErr.Error(), tc.description)
 		}
 	}
+}
+
+func TestUpdateCache(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
+
+	fs, err := NewTestFlexScaleSet(ctrl)
+	assert.NoError(t, err, "unexpected error when creating test FlexScaleSet")
+
+	testCases := []struct {
+		description string
+		nodeName    string
+		vm          *compute.VirtualMachine
+		expectedErr error
+	}{
+		{
+			description: "vm is nil",
+			nodeName:    "vmssflex1000001",
+			expectedErr: fmt.Errorf("vm is nil"),
+		},
+		{
+			description: "vm.VirtualMachineProperties is nil",
+			nodeName:    "vmssflex1000001",
+			vm:          &compute.VirtualMachine{Name: to.StringPtr("vmssflex1000001")},
+			expectedErr: fmt.Errorf("vm.VirtualMachineProperties is nil"),
+		},
+		{
+			description: "vm.OsProfile.ComputerName is nil",
+			nodeName:    "vmssflex1000001",
+			vm: &compute.VirtualMachine{
+				Name:                     to.StringPtr("vmssflex1000001"),
+				VirtualMachineProperties: &compute.VirtualMachineProperties{},
+			},
+			expectedErr: fmt.Errorf("vm.OsProfile.ComputerName is nil"),
+		},
+		{
+			description: "vm.OsProfile.ComputerName is nil",
+			nodeName:    "vmssflex1000001",
+			vm: &compute.VirtualMachine{
+				Name: to.StringPtr("vmssflex1000001"),
+				VirtualMachineProperties: &compute.VirtualMachineProperties{
+					OsProfile: &compute.OSProfile{},
+				},
+			},
+			expectedErr: fmt.Errorf("vm.OsProfile.ComputerName is nil"),
+		},
+	}
+
+	for _, test := range testCases {
+		err = fs.updateCache(test.nodeName, test.vm)
+		assert.Equal(t, test.expectedErr, err, test.description)
+	}
 
 }
diff --git a/pkg/provider/azure_instances_test.go b/pkg/provider/azure_instances_test.go
index 7c3a6cf4d..b6692b4e1 100644
--- a/pkg/provider/azure_instances_test.go
+++ b/pkg/provider/azure_instances_test.go
@@ -259,7 +259,7 @@ func TestInstanceID(t *testing.T) {
 			mockVMsClient.EXPECT().Get(gomock.Any(), cloud.ResourceGroup, *vm.Name, gomock.Any()).Return(vm, nil).AnyTimes()
 		}
 		mockVMsClient.EXPECT().Get(gomock.Any(), cloud.ResourceGroup, "vm3", gomock.Any()).Return(compute.VirtualMachine{}, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
-		mockVMsClient.EXPECT().Update(gomock.Any(), cloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
+		mockVMsClient.EXPECT().Update(gomock.Any(), cloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
 
 		instanceID, err := cloud.InstanceID(context.Background(), types.NodeName(test.nodeName))
 		assert.Equal(t, test.expectedErrMsg, err, test.name)
@@ -715,7 +715,7 @@ func TestInstanceExistsByProviderID(t *testing.T) {
 			mockVMsClient.EXPECT().Get(gomock.Any(), cloud.ResourceGroup, *vm.Name, gomock.Any()).Return(vm, nil).AnyTimes()
 		}
 		mockVMsClient.EXPECT().Get(gomock.Any(), cloud.ResourceGroup, "vm3", gomock.Any()).Return(compute.VirtualMachine{}, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
-		mockVMsClient.EXPECT().Update(gomock.Any(), cloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
+		mockVMsClient.EXPECT().Update(gomock.Any(), cloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
 
 		exist, err := cloud.InstanceExistsByProviderID(context.Background(), test.providerID)
 		assert.Equal(t, test.expectedErrMsg, err, test.name)