Repository: kubernetes-sigs/cloud-provider-azure
Pull request #5360 (2024-02-02 09:36:01)

Refactor: remove async ops in vmset
<!--  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 cleanup

<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature
/kind design

Optionally add one or more of the following kinds if applicable:
/kind api-change
/kind deprecation
/kind failing-test
/kind flake
/kind regression
-->

#### What this PR does / why we need it:

#### 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
NONE
```

#### 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

```


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/5360#" title="Author self-approved">MartinForReal</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)~~ [MartinForReal]

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":[]} -->
k8s-ci-robot: @MartinForReal: 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-unit | 39b2cdfdcbc240570f0fa8daa00c6c7ebcc9deea | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_cloud-provider-azure/5360/pull-cloud-provider-azure-unit/1753963060701171712) | true | `/test pull-cloud-provider-azure-unit`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=cloud-provider-azure&pr=5360). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AMartinForReal). 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 -->

Patch:
diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go
index 75bd1708b..3b2448658 100644
--- a/pkg/consts/consts.go
+++ b/pkg/consts/consts.go
@@ -113,8 +113,8 @@ const (
 	// StrRawVersion is the raw version string
 	StrRawVersion string = "raw"
 
-	// ProvisionStateDeleting indicates VMSS instances are in Deleting state.
-	ProvisionStateDeleting = "Deleting"
+	// VirtualMachineScaleSetsDeallocating indicates VMSS instances are in Deallocating state.
+	VirtualMachineScaleSetsDeallocating = "Deallocating"
 	// VmssMachineIDTemplate is the vmss manchine ID template
 	VmssMachineIDTemplate = "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Compute/virtualMachineScaleSets/%s/virtualMachines/%s"
 	// VMSetCIDRIPV4TagKey specifies the node ipv4 CIDR mask of the instances on the VMSS or VMAS
@@ -132,8 +132,6 @@ const (
 	ProvisioningStateDeleting = "Deleting"
 	// ProvisioningStateSucceeded ...
 	ProvisioningStateSucceeded = "Succeeded"
-	// ProvisioningStateUnknown is the unknown provisioning state
-	ProvisioningStateUnknown = "Unknown"
 )
 
 // cache
@@ -574,13 +572,3 @@ const (
 	ClusterServiceLoadBalancerHealthProbeDefaultPath         = "/healthz"
 	SharedProbeName                                          = "cluster-service-shared-health-probe"
 )
-
-// VM power state
-const (
-	VMPowerStatePrefix       = "PowerState/"
-	VMPowerStateStopped      = "stopped"
-	VMPowerStateStopping     = "stopping"
-	VMPowerStateDeallocated  = "deallocated"
-	VMPowerStateDeallocating = "deallocating"
-	VMPowerStateUnknown      = "unknown"
-)
diff --git a/pkg/provider/azure_controller_standard.go b/pkg/provider/azure_controller_standard.go
index 48b0989d9..e47b8296d 100644
--- a/pkg/provider/azure_controller_standard.go
+++ b/pkg/provider/azure_controller_standard.go
@@ -36,16 +36,16 @@ import (
 )
 
 // AttachDisk attaches a disk to vm
-func (as *availabilitySet) AttachDisk(ctx context.Context, nodeName types.NodeName, diskMap map[string]*AttachDiskOptions) (*azure.Future, error) {
+func (as *availabilitySet) AttachDisk(ctx context.Context, nodeName types.NodeName, diskMap map[string]*AttachDiskOptions) error {
 	vm, err := as.getVirtualMachine(nodeName, azcache.CacheReadTypeDefault)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
 	vmName := mapNodeNameToVMName(nodeName)
 	nodeResourceGroup, err := as.GetNodeResourceGroup(vmName)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
 	disks := make([]compute.DataDisk, len(*vm.StorageProfile.DataDisks))
@@ -61,7 +61,7 @@ func (as *availabilitySet) AttachDisk(ctx context.Context, nodeName types.NodeNa
 					attached = true
 					break
 				}
-				return nil, fmt.Errorf("disk(%s) already attached to node(%s) on LUN(%d), but target LUN is %d", diskURI, nodeName, *disk.Lun, opt.Lun)
+				return fmt.Errorf("disk(%s) already attached to node(%s) on LUN(%d), but target LUN is %d", diskURI, nodeName, *disk.Lun, opt.Lun)
 			}
 		}
 		if attached {
@@ -115,9 +115,9 @@ func (as *availabilitySet) AttachDisk(ctx context.Context, nodeName types.NodeNa
 
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - attach disk list(%v) returned with %v", nodeResourceGroup, vmName, diskMap, rerr)
 	if rerr != nil {
-		return future, rerr.Error()
+		return rerr.Error()
 	}
-	return future, nil
+	return as.WaitForUpdateResult(ctx, future, nodeName, "attach_disk")
 }
 
 func (as *availabilitySet) DeleteCacheForNode(nodeName string) error {
diff --git a/pkg/provider/azure_controller_vmss.go b/pkg/provider/azure_controller_vmss.go
index 064961a47..fada0393f 100644
--- a/pkg/provider/azure_controller_vmss.go
+++ b/pkg/provider/azure_controller_vmss.go
@@ -36,16 +36,16 @@ import (
 )
 
 // AttachDisk attaches a disk to vm
-func (ss *ScaleSet) AttachDisk(ctx context.Context, nodeName types.NodeName, diskMap map[string]*AttachDiskOptions) (*azure.Future, error) {
+func (ss *ScaleSet) AttachDisk(ctx context.Context, nodeName types.NodeName, diskMap map[string]*AttachDiskOptions) error {
 	vmName := mapNodeNameToVMName(nodeName)
 	vm, err := ss.getVmssVM(vmName, azcache.CacheReadTypeDefault)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
 	nodeResourceGroup, err := ss.GetNodeResourceGroup(vmName)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
 	var disks []compute.DataDisk
@@ -67,7 +67,7 @@ func (ss *ScaleSet) AttachDisk(ctx context.Context, nodeName types.NodeName, dis
 					attached = true
 					break
 				}
-				return nil, fmt.Errorf("disk(%s) already attached to node(%s) on LUN(%d), but target LUN is %d", diskURI, nodeName, *disk.Lun, opt.Lun)
+				return fmt.Errorf("disk(%s) already attached to node(%s) on LUN(%d), but target LUN is %d", diskURI, nodeName, *disk.Lun, opt.Lun)
 
 			}
 		}
@@ -122,9 +122,9 @@ func (ss *ScaleSet) AttachDisk(ctx context.Context, nodeName types.NodeName, dis
 
 	klog.V(2).Infof("azureDisk - update: rg(%s) vm(%s) - attach disk list(%+v) returned with %v", nodeResourceGroup, nodeName, diskMap, rerr)
 	if rerr != nil {
-		return future, rerr.Error()
+		return rerr.Error()
 	}
-	return future, nil
+	return ss.WaitForUpdateResult(ctx, future, nodeName, "attach_disk")
 }
 
 // WaitForUpdateResult waits for the response of the update request
diff --git a/pkg/provider/azure_controller_vmssflex.go b/pkg/provider/azure_controller_vmssflex.go
index 635292887..d34ea871e 100644
--- a/pkg/provider/azure_controller_vmssflex.go
+++ b/pkg/provider/azure_controller_vmssflex.go
@@ -36,16 +36,16 @@ import (
 )
 
 // AttachDisk attaches a disk to vm
-func (fs *FlexScaleSet) AttachDisk(ctx context.Context, nodeName types.NodeName, diskMap map[string]*AttachDiskOptions) (*azure.Future, error) {
+func (fs *FlexScaleSet) AttachDisk(ctx context.Context, nodeName types.NodeName, diskMap map[string]*AttachDiskOptions) error {
 	vmName := mapNodeNameToVMName(nodeName)
 	vm, err := fs.getVmssFlexVM(vmName, azcache.CacheReadTypeDefault)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
 	nodeResourceGroup, err := fs.GetNodeResourceGroup(vmName)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
 	disks := make([]compute.DataDisk, len(*vm.StorageProfile.DataDisks))
@@ -61,7 +61,7 @@ func (fs *FlexScaleSet) AttachDisk(ctx context.Context, nodeName types.NodeName,
 					attached = true
 					break
 				}
-				return nil, fmt.Errorf("disk(%s) already attached to node(%s) on LUN(%d), but target LUN is %d", diskURI, nodeName, *disk.Lun, opt.Lun)
+				return fmt.Errorf("disk(%s) already attached to node(%s) on LUN(%d), but target LUN is %d", diskURI, nodeName, *disk.Lun, opt.Lun)
 			}
 		}
 		if attached {
@@ -116,9 +116,9 @@ func (fs *FlexScaleSet) AttachDisk(ctx context.Context, nodeName types.NodeName,
 
 	klog.V(2).Infof("azureDisk - update(%s): vm(%s) - attach disk list(%+v) returned with %v", nodeResourceGroup, vmName, diskMap, rerr)
 	if rerr != nil {
-		return future, rerr.Error()
+		return rerr.Error()
 	}
-	return future, nil
+	return fs.WaitForUpdateResult(ctx, future, nodeName, "attach_disk")
 }
 
 // DetachDisk detaches a disk from VM
diff --git a/pkg/provider/azure_instances_v1.go b/pkg/provider/azure_instances_v1.go
index 2e5c9ebbf..e8a4a76f4 100644
--- a/pkg/provider/azure_instances_v1.go
+++ b/pkg/provider/azure_instances_v1.go
@@ -35,6 +35,12 @@ import (
 var _ cloudprovider.Instances = (*Cloud)(nil)
 
 const (
+	vmPowerStatePrefix       = "PowerState/"
+	vmPowerStateStopped      = "stopped"
+	vmPowerStateDeallocated  = "deallocated"
+	vmPowerStateDeallocating = "deallocating"
+	vmPowerStateUnknown      = "unknown"
+
 	// nodeNameEnvironmentName is the environment variable name for getting node name.
 	// It is only used for out-of-tree cloud provider.
 	nodeNameEnvironmentName = "NODE_NAME"
@@ -260,7 +266,7 @@ func (az *Cloud) InstanceShutdownByProviderID(_ context.Context, providerID stri
 
 	status := strings.ToLower(powerStatus)
 	provisioningSucceeded := strings.EqualFold(strings.ToLower(provisioningState), strings.ToLower(string(consts.ProvisioningStateSucceeded)))
-	return provisioningSucceeded && (status == consts.VMPowerStateStopped || status == consts.VMPowerStateDeallocated || status == consts.VMPowerStateDeallocating), nil
+	return provisioningSucceeded && (status == vmPowerStateStopped || status == vmPowerStateDeallocated || status == vmPowerStateDeallocating), nil
 }
 
 func (az *Cloud) isCurrentInstance(name types.NodeName, metadataVMName string) (bool, error) {
diff --git a/pkg/provider/azure_mock_vmsets.go b/pkg/provider/azure_mock_vmsets.go
index 40daae0d7..78de8bf84 100644
--- a/pkg/provider/azure_mock_vmsets.go
+++ b/pkg/provider/azure_mock_vmsets.go
@@ -33,12 +33,10 @@ import (
 	v5 "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5"
 	compute "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-08-01/compute"
 	network "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2022-07-01/network"
-	azure "github.com/Azure/go-autorest/autorest/azure"
 	gomock "go.uber.org/mock/gomock"
 	v1 "k8s.io/api/core/v1"
 	types "k8s.io/apimachinery/pkg/types"
 	cloud_provider "k8s.io/cloud-provider"
-
 	cache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
 )
 
@@ -66,12 +64,11 @@ func (m *MockVMSet) EXPECT() *MockVMSetMockRecorder {
 }
 
 // AttachDisk mocks base method.
-func (m *MockVMSet) AttachDisk(ctx context.Context, nodeName types.NodeName, diskMap map[string]*AttachDiskOptions) (*azure.Future, error) {
+func (m *MockVMSet) AttachDisk(ctx context.Context, nodeName types.NodeName, diskMap map[string]*AttachDiskOptions) error {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "AttachDisk", ctx, nodeName, diskMap)
-	ret0, _ := ret[0].(*azure.Future)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
+	ret0, _ := ret[0].(error)
+	return ret0
 }
 
 // AttachDisk indicates an expected call of AttachDisk.
@@ -425,32 +422,3 @@ func (mr *MockVMSetMockRecorder) UpdateVM(ctx, nodeName any) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateVM", reflect.TypeOf((*MockVMSet)(nil).UpdateVM), ctx, nodeName)
 }
-
-// UpdateVMAsync mocks base method.
-func (m *MockVMSet) UpdateVMAsync(ctx context.Context, nodeName types.NodeName) (*azure.Future, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "UpdateVMAsync", ctx, nodeName)
-	ret0, _ := ret[0].(*azure.Future)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// UpdateVMAsync indicates an expected call of UpdateVMAsync.
-func (mr *MockVMSetMockRecorder) UpdateVMAsync(ctx, nodeName any) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateVMAsync", reflect.TypeOf((*MockVMSet)(nil).UpdateVMAsync), ctx, nodeName)
-}
-
-// WaitForUpdateResult mocks base method.
-func (m *MockVMSet) WaitForUpdateResult(ctx context.Context, future *azure.Future, nodeName types.NodeName, source string) error {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "WaitForUpdateResult", ctx, future, nodeName, source)
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// WaitForUpdateResult indicates an expected call of WaitForUpdateResult.
-func (mr *MockVMSetMockRecorder) WaitForUpdateResult(ctx, future, nodeName, source any) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitForUpdateResult", reflect.TypeOf((*MockVMSet)(nil).WaitForUpdateResult), ctx, future, nodeName, source)
-}
diff --git a/pkg/provider/azure_standard.go b/pkg/provider/azure_standard.go
index 1d1863b2c..702435652 100644
--- a/pkg/provider/azure_standard.go
+++ b/pkg/provider/azure_standard.go
@@ -38,12 +38,10 @@ import (
 	cloudprovider "k8s.io/cloud-provider"
 	"k8s.io/klog/v2"
 	"k8s.io/utils/pointer"
-	"k8s.io/utils/ptr"
 
 	azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
 	"sigs.k8s.io/cloud-provider-azure/pkg/consts"
 	"sigs.k8s.io/cloud-provider-azure/pkg/metrics"
-	vmutil "sigs.k8s.io/cloud-provider-azure/pkg/util/vm"
 )
 
 var (
@@ -503,13 +501,19 @@ func (as *availabilitySet) GetPowerStatusByNodeName(name string) (powerState str
 		return powerState, err
 	}
 
-	if vm.InstanceView != nil {
-		return vmutil.GetVMPowerState(ptr.Deref(vm.Name, ""), vm.InstanceView.Statuses), nil
+	if vm.InstanceView != nil && vm.InstanceView.Statuses != nil {
+		statuses := *vm.InstanceView.Statuses
+		for _, status := range statuses {
+			state := pointer.StringDeref(status.Code, "")
+			if strings.HasPrefix(state, vmPowerStatePrefix) {
+				return strings.TrimPrefix(state, vmPowerStatePrefix), nil
+			}
+		}
 	}
 
 	// vm.InstanceView or vm.InstanceView.Statuses are nil when the VM is under deleting.
 	klog.V(3).Infof("InstanceView for node %q is nil, assuming it's deleting", name)
-	return consts.VMPowerStateUnknown, nil
+	return vmPowerStateUnknown, nil
 }
 
 // GetProvisioningStateByNodeName returns the provisioningState for the specified node.
diff --git a/pkg/provider/azure_vmsets.go b/pkg/provider/azure_vmsets.go
index 0ac8ae191..d680f5ced 100644
--- a/pkg/provider/azure_vmsets.go
+++ b/pkg/provider/azure_vmsets.go
@@ -22,7 +22,6 @@ import (
 	"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5"
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-08-01/compute"
 	"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2022-07-01/network"
-	"github.com/Azure/go-autorest/autorest/azure"
 
 	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
@@ -77,11 +76,10 @@ type VMSet interface {
 	EnsureBackendPoolDeletedFromVMSets(vmSetNamesMap map[string]bool, backendPoolIDs []string) error
 
 	// AttachDisk attaches a disk to vm
-	AttachDisk(ctx context.Context, nodeName types.NodeName, diskMap map[string]*AttachDiskOptions) (*azure.Future, error)
+	AttachDisk(ctx context.Context, nodeName types.NodeName, diskMap map[string]*AttachDiskOptions) error
 	// DetachDisk detaches a disk from vm
 	DetachDisk(ctx context.Context, nodeName types.NodeName, diskMap map[string]string) error
 	// WaitForUpdateResult waits for the response of the update request
-	WaitForUpdateResult(ctx context.Context, future *azure.Future, nodeName types.NodeName, source string) error
 
 	// GetDataDisks gets a list of data disks attached to the node.
 	GetDataDisks(nodeName types.NodeName, crt azcache.AzureCacheReadType) ([]*armcompute.DataDisk, *string, error)
@@ -89,9 +87,6 @@ type VMSet interface {
 	// UpdateVM updates a vm
 	UpdateVM(ctx context.Context, nodeName types.NodeName) error
 
-	// UpdateVMAsync updates a vm asynchronously
-	UpdateVMAsync(ctx context.Context, nodeName types.NodeName) (*azure.Future, error)
-
 	// GetPowerStatusByNodeName returns the powerState for the specified node.
 	GetPowerStatusByNodeName(name string) (string, error)
 
diff --git a/pkg/provider/azure_vmss.go b/pkg/provider/azure_vmss.go
index 3c84f767b..69389b263 100644
--- a/pkg/provider/azure_vmss.go
+++ b/pkg/provider/azure_vmss.go
@@ -35,13 +35,11 @@ import (
 	cloudprovider "k8s.io/cloud-provider"
 	"k8s.io/klog/v2"
 	"k8s.io/utils/pointer"
-	"k8s.io/utils/ptr"
 
 	azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
 	"sigs.k8s.io/cloud-provider-azure/pkg/consts"
 	"sigs.k8s.io/cloud-provider-azure/pkg/metrics"
 	"sigs.k8s.io/cloud-provider-azure/pkg/provider/virtualmachine"
-	vmutil "sigs.k8s.io/cloud-provider-azure/pkg/util/vm"
 )
 
 var (
@@ -284,14 +282,20 @@ func (ss *ScaleSet) GetPowerStatusByNodeName(name string) (powerState string, er
 
 	if vm.IsVirtualMachineScaleSetVM() {
 		v := vm.AsVirtualMachineScaleSetVM()
-		if v.InstanceView != nil {
-			return vmutil.GetVMPowerState(ptr.Deref(v.Name, ""), v.InstanceView.Statuses), nil
+		if v.InstanceView != nil && v.InstanceView.Statuses != nil {
+			statuses := *v.InstanceView.Statuses
+			for _, status := range statuses {
+				state := pointer.StringDeref(status.Code, "")
+				if strings.HasPrefix(state, vmPowerStatePrefix) {
+					return strings.TrimPrefix(state, vmPowerStatePrefix), nil
+				}
+			}
 		}
 	}
 
 	// vm.InstanceView or vm.InstanceView.Statuses are nil when the VM is under deleting.
 	klog.V(3).Infof("InstanceView for node %q is nil, assuming it's deleting", name)
-	return consts.VMPowerStateUnknown, nil
+	return vmPowerStateUnknown, nil
 }
 
 // GetProvisioningStateByNodeName returns the provisioningState for the specified node.
@@ -1025,8 +1029,6 @@ func getPrimaryIPConfigFromVMSSNetworkConfig(config *compute.VirtualMachineScale
 // EnsureHostInPool ensures the given VM's Primary NIC's Primary IP Configuration is
 // participating in the specified LoadBalancer Backend Pool, which returns (resourceGroup, vmasName, instanceID, vmssVM, error).
 func (ss *ScaleSet) EnsureHostInPool(_ *v1.Service, nodeName types.NodeName, backendPoolID string, vmSetNameOfLB string) (string, string, string, *compute.VirtualMachineScaleSetVM, error) {
-	logger := klog.Background().WithName("EnsureHostInPool").
-		WithValues("nodeName", nodeName, "backendPoolID", backendPoolID, "vmSetNameOfLB", vmSetNameOfLB)
 	vmName := mapNodeNameToVMName(nodeName)
 	vm, err := ss.getVmssVM(vmName, azcache.CacheReadTypeDefault)
 	if err != nil {
@@ -1035,20 +1037,13 @@ func (ss *ScaleSet) EnsureHostInPool(_ *v1.Service, nodeName types.NodeName, bac
 			return "", "", "", nil, nil
 		}
 
-		logger.Error(err, "failed to get vmss vm", "vmName", vmName)
+		klog.Errorf("EnsureHostInPool: failed to get VMSS VM %s: %v", vmName, err)
 		if !errors.Is(err, ErrorNotVmssInstance) {
 			return "", "", "", nil, err
 		}
 	}
-	statuses := vm.GetInstanceViewStatus()
-	vmPowerState := vmutil.GetVMPowerState(vm.Name, statuses)
-	provisioningState := vm.GetProvisioningState()
-	if vmutil.IsNotActiveVMState(provisioningState, vmPowerState) {
-		logger.V(2).Info("skip updating the node because it is not in an active state", "vmName", vmName, "provisioningState", provisioningState, "vmPowerState", vmPowerState)
-		return "", "", "", nil, nil
-	}
 
-	logger.V(2).Info("ensuring the vmss node in LB backendpool", "vmss name", vm.VMSSName)
+	klog.V(2).Infof("ensuring node %q of scaleset %q in LB backendpool %q", nodeName, vm.VMSSName, backendPoolID)
 
 	// Check scale set name:
 	// - For basic SKU load balancer, return nil if the node's scale set is mismatched with vmSetNameOfLB.
@@ -1062,13 +1057,14 @@ func (ss *ScaleSet) EnsureHostInPool(_ *v1.Service, nodeName types.NodeName, bac
 	}
 
 	if vmSetNameOfLB != "" && needCheck && !strings.EqualFold(vmSetNameOfLB, vm.VMSSName) {
-		logger.V(3).Info("skips the node %s because it is not in the ScaleSet %s", vmName, vmSetNameOfLB)
+		klog.V(3).Infof("EnsureHostInPool skips node %s because it is not in the ScaleSet %s", vmName, vmSetNameOfLB)
 		return "", "", "", nil, nil
 	}
 
 	// Find primary network interface configuration.
 	if vm.VirtualMachineScaleSetVMProperties.NetworkProfileConfiguration.NetworkInterfaceConfigurations == nil {
-		logger.V(4).Info("cannot obtain the primary network interface configuration, of the vm, probably because the vm's being deleted", "vmName", vmName)
+		klog.V(4).Infof("EnsureHostInPool: cannot obtain the primary network interface configuration, of vm %s, "+
+			"probably because the vm's being deleted", vmName)
 		return "", "", "", nil, nil
 	}
 
@@ -1118,7 +1114,7 @@ func (ss *ScaleSet) EnsureHostInPool(_ *v1.Service, nodeName types.NodeName, bac
 			return "", "", "", nil, err
 		}
 		if !isSameLB {
-			logger.V(4).Info("The node has already been added to an LB, omit adding it to a new one", "lbName", oldLBName)
+			klog.V(4).Infof("Node %q has already been added to LB %q, omit adding it to a new one", nodeName, oldLBName)
 			return "", "", "", nil, nil
 		}
 	}
@@ -1225,7 +1221,7 @@ func (ss *ScaleSet) ensureVMSSInPool(_ *v1.Service, nodes []*v1.Node, backendPoo
 
 		// When vmss is being deleted, CreateOrUpdate API would report "the vmss is being deleted" error.
 		// Since it is being deleted, we shouldn't send more CreateOrUpdate requests for it.
-		if vmss.ProvisioningState != nil && strings.EqualFold(*vmss.ProvisioningState, consts.ProvisionStateDeleting) {
+		if vmss.ProvisioningState != nil && strings.EqualFold(*vmss.ProvisioningState, consts.VirtualMachineScaleSetsDeallocating) {
 			klog.V(3).Infof("ensureVMSSInPool: found vmss %s being deleted, skipping", vmssName)
 			continue
 		}
@@ -1497,7 +1493,6 @@ func (ss *ScaleSet) EnsureHostsInPool(service *v1.Service, nodes []*v1.Node, bac
 // ensureBackendPoolDeletedFromNode ensures the loadBalancer backendAddressPools deleted
 // from the specified node, which returns (resourceGroup, vmasName, instanceID, vmssVM, error).
 func (ss *ScaleSet) ensureBackendPoolDeletedFromNode(nodeName string, backendPoolIDs []string) (string, string, string, *compute.VirtualMachineScaleSetVM, error) {
-	logger := klog.Background().WithName("ensureBackendPoolDeletedFromNode").WithValues("nodeName", nodeName, "backendPoolIDs", backendPoolIDs)
 	vm, err := ss.getVmssVM(nodeName, azcache.CacheReadTypeDefault)
 	if err != nil {
 		if errors.Is(err, cloudprovider.InstanceNotFound) {
@@ -1508,14 +1503,6 @@ func (ss *ScaleSet) ensureBackendPoolDeletedFromNode(nodeName string, backendPoo
 		return "", "", "", nil, err
 	}
 
-	statuses := vm.GetInstanceViewStatus()
-	vmPowerState := vmutil.GetVMPowerState(vm.Name, statuses)
-	provisioningState := vm.GetProvisioningState()
-	if vmutil.IsNotActiveVMState(provisioningState, vmPowerState) {
-		logger.V(2).Info("skip updating the node because it is not in an active state", "provisioningState", provisioningState, "vmPowerState", vmPowerState)
-		return "", "", "", nil, nil
-	}
-
 	// Find primary network interface configuration.
 	if vm.VirtualMachineScaleSetVMProperties.NetworkProfileConfiguration.NetworkInterfaceConfigurations == nil {
 		klog.V(4).Infof("ensureBackendPoolDeletedFromNode: cannot obtain the primary network interface configuration, of vm %s, "+
@@ -1692,7 +1679,7 @@ func (ss *ScaleSet) ensureBackendPoolDeletedFromVmssUniform(backendPoolIDs []str
 
 			// When vmss is being deleted, CreateOrUpdate API would report "the vmss is being deleted" error.
 			// Since it is being deleted, we shouldn't send more CreateOrUpdate requests for it.
-			if vmss.ProvisioningState != nil && strings.EqualFold(*vmss.ProvisioningState, consts.ProvisionStateDeleting) {
+			if vmss.ProvisioningState != nil && strings.EqualFold(*vmss.ProvisioningState, consts.VirtualMachineScaleSetsDeallocating) {
 				klog.V(3).Infof("ensureBackendPoolDeletedFromVMSS: found vmss %s being deleted, skipping", pointer.StringDeref(vmss.Name, ""))
 				return true
 			}
@@ -2099,7 +2086,7 @@ func (ss *ScaleSet) EnsureBackendPoolDeletedFromVMSets(vmssNamesMap map[string]b
 
 		// When vmss is being deleted, CreateOrUpdate API would report "the vmss is being deleted" error.
 		// Since it is being deleted, we shouldn't send more CreateOrUpdate requests for it.
-		if vmss.ProvisioningState != nil && strings.EqualFold(*vmss.ProvisioningState, consts.ProvisionStateDeleting) {
+		if vmss.ProvisioningState != nil && strings.EqualFold(*vmss.ProvisioningState, consts.VirtualMachineScaleSetsDeallocating) {
 			klog.V(3).Infof("EnsureBackendPoolDeletedFromVMSets: found vmss %s being deleted, skipping", vmssName)
 			continue
 		}
diff --git a/pkg/provider/azure_vmss_repo.go b/pkg/provider/azure_vmss_repo.go
index 89b5a900f..6721dceab 100644
--- a/pkg/provider/azure_vmss_repo.go
+++ b/pkg/provider/azure_vmss_repo.go
@@ -39,7 +39,7 @@ func (az *Cloud) CreateOrUpdateVMSS(resourceGroupName string, VMScaleSetName str
 		klog.Errorf("CreateOrUpdateVMSS: error getting vmss(%s): %v", VMScaleSetName, rerr)
 		return rerr
 	}
-	if vmss.ProvisioningState != nil && strings.EqualFold(*vmss.ProvisioningState, consts.ProvisionStateDeleting) {
+	if vmss.ProvisioningState != nil && strings.EqualFold(*vmss.ProvisioningState, consts.VirtualMachineScaleSetsDeallocating) {
 		klog.V(3).Infof("CreateOrUpdateVMSS: found vmss %s being deleted, skipping", VMScaleSetName)
 		return nil
 	}
diff --git a/pkg/provider/azure_vmssflex.go b/pkg/provider/azure_vmssflex.go
index 646785c75..ef98ae0eb 100644
--- a/pkg/provider/azure_vmssflex.go
+++ b/pkg/provider/azure_vmssflex.go
@@ -27,19 +27,16 @@ import (
 
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-08-01/compute"
 	"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2022-07-01/network"
-
 	v1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
 	utilerrors "k8s.io/apimachinery/pkg/util/errors"
 	cloudprovider "k8s.io/cloud-provider"
 	"k8s.io/klog/v2"
 	"k8s.io/utils/pointer"
-	"k8s.io/utils/ptr"
 
 	azcache "sigs.k8s.io/cloud-provider-azure/pkg/cache"
 	"sigs.k8s.io/cloud-provider-azure/pkg/consts"
 	"sigs.k8s.io/cloud-provider-azure/pkg/metrics"
-	vmutil "sigs.k8s.io/cloud-provider-azure/pkg/util/vm"
 )
 
 var (
@@ -270,13 +267,19 @@ func (fs *FlexScaleSet) GetPowerStatusByNodeName(name string) (powerState string
 		return powerState, err
 	}
 
-	if vm.InstanceView != nil {
-		return vmutil.GetVMPowerState(ptr.Deref(vm.Name, ""), vm.InstanceView.Statuses), nil
+	if vm.InstanceView != nil && vm.InstanceView.Statuses != nil {
+		statuses := *vm.InstanceView.Statuses
+		for _, status := range statuses {
+			state := pointer.StringDeref(status.Code, "")
+			if strings.HasPrefix(state, vmPowerStatePrefix) {
+				return strings.TrimPrefix(state, vmPowerStatePrefix), nil
+			}
+		}
 	}
 
 	// vm.InstanceView or vm.InstanceView.Statuses are nil when the VM is under deleting.
 	klog.V(3).Infof("InstanceView for node %q is nil, assuming it's deleting", name)
-	return consts.VMPowerStateUnknown, nil
+	return vmPowerStateUnknown, nil
 }
 
 // GetPrimaryInterface gets machine primary network interface by node name.
@@ -613,7 +616,7 @@ func (fs *FlexScaleSet) ensureVMSSFlexInPool(_ *v1.Service, nodes []*v1.Node, ba
 
 		// When vmss is being deleted, CreateOrUpdate API would report "the vmss is being deleted" error.
 		// Since it is being deleted, we shouldn't send more CreateOrUpdate requests for it.
-		if vmssFlex.ProvisioningState != nil && strings.EqualFold(*vmssFlex.ProvisioningState, consts.ProvisionStateDeleting) {
+		if vmssFlex.ProvisioningState != nil && strings.EqualFold(*vmssFlex.ProvisioningState, consts.VirtualMachineScaleSetsDeallocating) {
 			klog.V(3).Infof("ensureVMSSFlexInPool: found vmss %s being deleted, skipping", vmssFlexID)
 			continue
 		}
@@ -787,7 +790,7 @@ func (fs *FlexScaleSet) EnsureBackendPoolDeletedFromVMSets(vmssNamesMap map[stri
 
 		// When vmss is being deleted, CreateOrUpdate API would report "the vmss is being deleted" error.
 		// Since it is being deleted, we shouldn't send more CreateOrUpdate requests for it.
-		if vmss.ProvisioningState != nil && strings.EqualFold(*vmss.ProvisioningState, consts.ProvisionStateDeleting) {
+		if vmss.ProvisioningState != nil && strings.EqualFold(*vmss.ProvisioningState, consts.VirtualMachineScaleSetsDeallocating) {
 			klog.V(3).Infof("fs.EnsureBackendPoolDeletedFromVMSets: found vmss %s being deleted, skipping", vmssName)
 			continue
 		}
diff --git a/pkg/provider/virtualmachine/virtualmachine.go b/pkg/provider/virtualmachine/virtualmachine.go
index 924f08c8e..bd6b77535 100644
--- a/pkg/provider/virtualmachine/virtualmachine.go
+++ b/pkg/provider/virtualmachine/virtualmachine.go
@@ -18,10 +18,7 @@ package virtualmachine
 
 import (
 	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-08-01/compute"
-
 	"k8s.io/utils/pointer"
-
-	"sigs.k8s.io/cloud-provider-azure/pkg/consts"
 )
 
 type Variant string
@@ -148,36 +145,6 @@ func (vm *VirtualMachine) AsVirtualMachineScaleSetVM() *compute.VirtualMachineSc
 	return vm.vmssVM
 }
 
-func (vm *VirtualMachine) GetInstanceViewStatus() *[]compute.InstanceViewStatus {
-	if vm.IsVirtualMachine() && vm.vm != nil &&
-		vm.vm.VirtualMachineProperties != nil &&
-		vm.vm.VirtualMachineProperties.InstanceView != nil {
-		return vm.vm.VirtualMachineProperties.InstanceView.Statuses
-	}
-	if vm.IsVirtualMachineScaleSetVM() &&
-		vm.vmssVM != nil &&
-		vm.vmssVM.VirtualMachineScaleSetVMProperties != nil &&
-		vm.vmssVM.VirtualMachineScaleSetVMProperties.InstanceView != nil {
-		return vm.vmssVM.VirtualMachineScaleSetVMProperties.InstanceView.Statuses
-	}
-	return nil
-}
-
-func (vm *VirtualMachine) GetProvisioningState() string {
-	if vm.IsVirtualMachine() && vm.vm != nil &&
-		vm.vm.VirtualMachineProperties != nil &&
-		vm.vm.VirtualMachineProperties.ProvisioningState != nil {
-		return *vm.vm.VirtualMachineProperties.ProvisioningState
-	}
-	if vm.IsVirtualMachineScaleSetVM() &&
-		vm.vmssVM != nil &&
-		vm.vmssVM.VirtualMachineScaleSetVMProperties != nil &&
-		vm.vmssVM.VirtualMachineScaleSetVMProperties.ProvisioningState != nil {
-		return *vm.vmssVM.VirtualMachineScaleSetVMProperties.ProvisioningState
-	}
-	return consts.ProvisioningStateUnknown
-}
-
 // StringMap returns a map of strings built from the map of string pointers. The empty string is
 // used for nil pointers.
 func stringMap(msp map[string]*string) map[string]string {
diff --git a/pkg/util/string/string.go b/pkg/util/string/string.go
deleted file mode 100644
index 10510ecc7..000000000
--- a/pkg/util/string/string.go
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
-Copyright 2024 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package stringutils
-
-import "strings"
-
-// HasPrefixCaseInsensitive returns true if the string has the prefix, case-insensitive.
-func HasPrefixCaseInsensitive(s string, prefix string) bool {
-	return strings.HasPrefix(strings.ToLower(s), strings.ToLower(prefix))
-}
diff --git a/pkg/util/vm/vm.go b/pkg/util/vm/vm.go
deleted file mode 100644
index a5084f30b..000000000
--- a/pkg/util/vm/vm.go
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-Copyright 2024 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package vm
-
-import (
-	"strings"
-
-	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-08-01/compute"
-
-	"k8s.io/klog/v2"
-	"k8s.io/utils/ptr"
-
-	"sigs.k8s.io/cloud-provider-azure/pkg/consts"
-	stringutils "sigs.k8s.io/cloud-provider-azure/pkg/util/string"
-)
-
-// GetVMPowerState returns the power state of the VM
-func GetVMPowerState(vmName string, vmStatuses *[]compute.InstanceViewStatus) string {
-	logger := klog.Background().WithName("getVMSSVMPowerState").WithValues("vmName", vmName)
-	if vmStatuses != nil {
-		for _, status := range *vmStatuses {
-			state := ptr.Deref(status.Code, "")
-			if stringutils.HasPrefixCaseInsensitive(state, consts.VMPowerStatePrefix) {
-				return strings.TrimPrefix(state, consts.VMPowerStatePrefix)
-			}
-		}
-	}
-	logger.V(3).Info("vm status is nil in the instance view or there is no power state in the status")
-	return consts.VMPowerStateUnknown
-}
-
-// IsNotActiveVMState checks if the VM is in the active states
-func IsNotActiveVMState(provisioningState, powerState string) bool {
-	return strings.EqualFold(provisioningState, consts.ProvisioningStateDeleting) ||
-		strings.EqualFold(provisioningState, consts.ProvisioningStateUnknown) ||
-		strings.EqualFold(powerState, consts.VMPowerStateUnknown) ||
-		strings.EqualFold(powerState, consts.VMPowerStateStopped) ||
-		strings.EqualFold(powerState, consts.VMPowerStateStopping) ||
-		strings.EqualFold(powerState, consts.VMPowerStateDeallocated) ||
-		strings.EqualFold(powerState, consts.VMPowerStateDeallocating)
-}


Test patch:
diff --git a/pkg/provider/azure_controller_standard_test.go b/pkg/provider/azure_controller_standard_test.go
index c664578ca..c42647c8c 100644
--- a/pkg/provider/azure_controller_standard_test.go
+++ b/pkg/provider/azure_controller_standard_test.go
@@ -43,11 +43,6 @@ var (
 )
 
 func TestStandardAttachDisk(t *testing.T) {
-	ctrl := gomock.NewController(t)
-	defer ctrl.Finish()
-
-	ctx, cancel := getContextWithCancel()
-	defer cancel()
 
 	testCases := []struct {
 		desc            string
@@ -85,6 +80,11 @@ func TestStandardAttachDisk(t *testing.T) {
 	}
 
 	for i, test := range testCases {
+		ctrl := gomock.NewController(t)
+		defer ctrl.Finish()
+
+		ctx, cancel := getContextWithCancel()
+		defer cancel()
 		testCloud := GetTestCloud(ctrl)
 		vmSet := testCloud.VMSet
 		expectedVMs := setTestVirtualMachines(testCloud, map[string]string{"vm1": "PowerState/Running"}, false)
@@ -116,6 +116,7 @@ func TestStandardAttachDisk(t *testing.T) {
 			mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
 		} else {
 			mockVMsClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
+			mockVMsClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), testCloud.ResourceGroup, gomock.Any()).Return(nil, nil).AnyTimes()
 		}
 
 		options := AttachDiskOptions{
@@ -131,7 +132,7 @@ func TestStandardAttachDisk(t *testing.T) {
 		diskMap := map[string]*AttachDiskOptions{
 			"uri": &options,
 		}
-		_, err := vmSet.AttachDisk(ctx, test.nodeName, diskMap)
+		err := vmSet.AttachDisk(ctx, test.nodeName, diskMap)
 		assert.Equal(t, test.expectedErr, err != nil, "TestCase[%d]: %s, err: %v", i, test.desc, err)
 	}
 }
diff --git a/pkg/provider/azure_controller_vmss_test.go b/pkg/provider/azure_controller_vmss_test.go
index 65ca40f70..84186e2ef 100644
--- a/pkg/provider/azure_controller_vmss_test.go
+++ b/pkg/provider/azure_controller_vmss_test.go
@@ -134,6 +134,7 @@ func TestAttachDiskWithVMSS(t *testing.T) {
 			mockVMSSVMClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, scaleSetName, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, &retry.Error{HTTPStatusCode: http.StatusNotFound, RawError: cloudprovider.InstanceNotFound}).AnyTimes()
 		} else {
 			mockVMSSVMClient.EXPECT().UpdateAsync(gomock.Any(), testCloud.ResourceGroup, scaleSetName, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
+			mockVMSSVMClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil).AnyTimes()
 		}
 
 		diskMap := map[string]*AttachDiskOptions{}
@@ -153,7 +154,7 @@ func TestAttachDiskWithVMSS(t *testing.T) {
 				testCloud.SubscriptionID, testCloud.ResourceGroup, diskName)
 			diskMap[diskURI] = &options
 		}
-		_, err = ss.AttachDisk(ctx, test.vmssvmName, diskMap)
+		err = ss.AttachDisk(ctx, test.vmssvmName, diskMap)
 		assert.Equal(t, test.expectedErr, err, "TestCase[%d]: %s, expected error: %v, return error: %v", i, test.desc, test.expectedErr, err)
 	}
 }
diff --git a/pkg/provider/azure_controller_vmssflex_test.go b/pkg/provider/azure_controller_vmssflex_test.go
index 7cc1ea12f..20dc9b98a 100644
--- a/pkg/provider/azure_controller_vmssflex_test.go
+++ b/pkg/provider/azure_controller_vmssflex_test.go
@@ -108,7 +108,7 @@ func TestAttachDiskWithVmssFlex(t *testing.T) {
 		mockVMClient.EXPECT().ListVmssFlexVMsWithOnlyInstanceView(gomock.Any(), gomock.Any()).Return(tc.testVMListWithOnlyInstanceView, tc.vmListErr).AnyTimes()
 
 		mockVMClient.EXPECT().UpdateAsync(gomock.Any(), gomock.Any(), tc.vmName, gomock.Any(), gomock.Any()).Return(nil, tc.vmssFlexVMUpdateError).AnyTimes()
-
+		mockVMClient.EXPECT().WaitForUpdateResult(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, tc.vmssFlexVMUpdateError).AnyTimes()
 		options := AttachDiskOptions{
 			Lun:                     1,
 			DiskName:                "diskname",
@@ -123,7 +123,7 @@ func TestAttachDiskWithVmssFlex(t *testing.T) {
 			"uri": &options,
 		}
 
-		_, err = fs.AttachDisk(ctx, tc.nodeName, diskMap)
+		err = fs.AttachDisk(ctx, tc.nodeName, diskMap)
 		if tc.expectedErr == nil {
 			assert.NoError(t, err)
 		} else {
diff --git a/pkg/provider/azure_standard_test.go b/pkg/provider/azure_standard_test.go
index 9369480ad..d63697928 100644
--- a/pkg/provider/azure_standard_test.go
+++ b/pkg/provider/azure_standard_test.go
@@ -1050,7 +1050,7 @@ func TestGetStandardVMPowerStatusByNodeName(t *testing.T) {
 					ProvisioningState: pointer.String("Succeeded"),
 				},
 			},
-			expectedStatus: consts.VMPowerStateUnknown,
+			expectedStatus: vmPowerStateUnknown,
 		},
 		{
 			name:     "GetPowerStatusByNodeName should get vmPowerStateUnknown if vm.InstanceView.statuses is nil",
@@ -1062,7 +1062,7 @@ func TestGetStandardVMPowerStatusByNodeName(t *testing.T) {
 					InstanceView:      &compute.VirtualMachineInstanceView{},
 				},
 			},
-			expectedStatus: consts.VMPowerStateUnknown,
+			expectedStatus: vmPowerStateUnknown,
 		},
 	}
 	for _, test := range testcases {
diff --git a/pkg/provider/azure_vmss_repo_test.go b/pkg/provider/azure_vmss_repo_test.go
index a8f3fef7a..f7802d49b 100644
--- a/pkg/provider/azure_vmss_repo_test.go
+++ b/pkg/provider/azure_vmss_repo_test.go
@@ -62,7 +62,7 @@ func TestCreateOrUpdateVMSS(t *testing.T) {
 		{
 			vmss: compute.VirtualMachineScaleSet{
 				VirtualMachineScaleSetProperties: &compute.VirtualMachineScaleSetProperties{
-					ProvisioningState: pointer.String(consts.ProvisionStateDeleting),
+					ProvisioningState: pointer.String(consts.VirtualMachineScaleSetsDeallocating),
 				},
 			},
 		},
diff --git a/pkg/provider/azure_vmss_test.go b/pkg/provider/azure_vmss_test.go
index 439308383..4617023e9 100644
--- a/pkg/provider/azure_vmss_test.go
+++ b/pkg/provider/azure_vmss_test.go
@@ -34,7 +34,6 @@ import (
 	"k8s.io/apimachinery/pkg/util/sets"
 	cloudprovider "k8s.io/cloud-provider"
 	"k8s.io/utils/pointer"
-	"k8s.io/utils/ptr"
 
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/interfaceclient/mockinterfaceclient"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/publicipclient/mockpublicipclient"
@@ -792,7 +791,7 @@ func TestGetPowerStatusByNodeName(t *testing.T) {
 			description:        "GetPowerStatusByNodeName should return vmPowerStateUnknown when the vm.InstanceView.Statuses is nil",
 			vmList:             []string{"vmss-vm-000001"},
 			nilStatus:          true,
-			expectedPowerState: consts.VMPowerStateUnknown,
+			expectedPowerState: vmPowerStateUnknown,
 		},
 	}
 
@@ -2097,7 +2096,6 @@ func TestEnsureHostInPool(t *testing.T) {
 		isBasicLB                 bool
 		isNilVMNetworkConfigs     bool
 		isVMBeingDeleted          bool
-		isVMNotActive             bool
 		expectedNodeResourceGroup string
 		expectedVMSSName          string
 		expectedInstanceID        string
@@ -2181,11 +2179,6 @@ func TestEnsureHostInPool(t *testing.T) {
 				},
 			},
 		},
-		{
-			description:   "EnsureHostInPool should skip if the current node is not active",
-			nodeName:      "vmss-vm-000000",
-			isVMNotActive: true,
-		},
 	}
 
 	for _, test := range testCases {
@@ -2216,11 +2209,6 @@ func TestEnsureHostInPool(t *testing.T) {
 		if test.isNilVMNetworkConfigs {
 			expectedVMSSVMs[0].NetworkProfileConfiguration.NetworkInterfaceConfigurations = nil
 		}
-		if test.isVMNotActive {
-			(*expectedVMSSVMs[0].InstanceView.Statuses)[0] = compute.InstanceViewStatus{
-				Code: ptr.To("PowerState/deallocated"),
-			}
-		}
 		mockVMSSVMClient := ss.VirtualMachineScaleSetVMsClient.(*mockvmssvmclient.MockInterface)
 		mockVMSSVMClient.EXPECT().List(
 			gomock.Any(),
@@ -2450,7 +2438,7 @@ func TestEnsureVMSSInPool(t *testing.T) {
 
 			expectedVMSS := buildTestVMSSWithLB(testVMSSName, "vmss-vm-", []string{testLBBackendpoolID0}, test.setIPv6Config)
 			if test.isVMSSDeallocating {
-				expectedVMSS.ProvisioningState = pointer.String(consts.ProvisionStateDeleting)
+				expectedVMSS.ProvisioningState = pointer.String(consts.VirtualMachineScaleSetsDeallocating)
 			}
 			if test.isVMSSNilNICConfig {
 				expectedVMSS.VirtualMachineProfile.NetworkProfile.NetworkInterfaceConfigurations = nil
@@ -2580,7 +2568,6 @@ func TestEnsureBackendPoolDeletedFromNodeCommon(t *testing.T) {
 		nodeName                  string
 		backendpoolIDs            []string
 		isNilVMNetworkConfigs     bool
-		isVMNotActive             bool
 		expectedNodeResourceGroup string
 		expectedVMSSName          string
 		expectedInstanceID        string
@@ -2643,11 +2630,6 @@ func TestEnsureBackendPoolDeletedFromNodeCommon(t *testing.T) {
 				},
 			},
 		},
-		{
-			description:   "ensureBackendPoolDeletedFromNode should skip if the node is not active",
-			nodeName:      "vmss-vm-000000",
-			isVMNotActive: true,
-		},
 	}
 
 	for _, test := range testCases {
@@ -2664,11 +2646,6 @@ func TestEnsureBackendPoolDeletedFromNodeCommon(t *testing.T) {
 			if test.isNilVMNetworkConfigs {
 				expectedVMSSVMs[0].NetworkProfileConfiguration.NetworkInterfaceConfigurations = nil
 			}
-			if test.isVMNotActive {
-				(*expectedVMSSVMs[0].InstanceView.Statuses)[0] = compute.InstanceViewStatus{
-					Code: ptr.To("PowerState/deallocated"),
-				}
-			}
 			mockVMSSVMClient := ss.VirtualMachineScaleSetVMsClient.(*mockvmssvmclient.MockInterface)
 			mockVMSSVMClient.EXPECT().List(gomock.Any(), ss.ResourceGroup, testVMSSName, gomock.Any()).Return(expectedVMSSVMs, nil).AnyTimes()
 
@@ -2749,7 +2726,7 @@ func TestEnsureBackendPoolDeletedFromVMSS(t *testing.T) {
 
 		expectedVMSS := buildTestVMSSWithLB(testVMSSName, "vmss-vm-", []string{testLBBackendpoolID0}, false)
 		if test.isVMSSDeallocating {
-			expectedVMSS.ProvisioningState = pointer.String(consts.ProvisionStateDeleting)
+			expectedVMSS.ProvisioningState = pointer.String(consts.VirtualMachineScaleSetsDeallocating)
 		}
 		if test.isVMSSNilNICConfig {
 			expectedVMSS.VirtualMachineProfile.NetworkProfile.NetworkInterfaceConfigurations = nil
diff --git a/pkg/provider/azure_vmssflex_test.go b/pkg/provider/azure_vmssflex_test.go
index bb69070d0..b508ba037 100644
--- a/pkg/provider/azure_vmssflex_test.go
+++ b/pkg/provider/azure_vmssflex_test.go
@@ -1250,7 +1250,7 @@ func TestEnsureVMSSFlexInPool(t *testing.T) {
 		testVmssFlex := genreteTestVmssFlex("vmssflex1", testVmssFlex1ID)
 
 		if tc.isVMSSDeallocating {
-			testVmssFlex.ProvisioningState = pointer.String(consts.ProvisionStateDeleting)
+			testVmssFlex.ProvisioningState = pointer.String(consts.VirtualMachineScaleSetsDeallocating)
 		}
 		if !tc.hasDefaultVMProfile {
 			testVmssFlex.VirtualMachineProfile = nil
@@ -1500,7 +1500,7 @@ func TestEnsureBackendPoolDeletedFromVMSetsVmssFlex(t *testing.T) {
 			testVmssFlex := genreteTestVmssFlex("vmssflex1", testVmssFlex1ID)
 
 			if tc.isVMSSDeallocating {
-				testVmssFlex.ProvisioningState = pointer.String(consts.ProvisionStateDeleting)
+				testVmssFlex.ProvisioningState = pointer.String(consts.VirtualMachineScaleSetsDeallocating)
 			}
 			if !tc.hasDefaultVMProfile {
 				testVmssFlex.VirtualMachineProfile = nil
diff --git a/pkg/util/string/string_test.go b/pkg/util/string/string_test.go
deleted file mode 100644
index 8449cfc6f..000000000
--- a/pkg/util/string/string_test.go
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
-Copyright 2024 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package stringutils
-
-import (
-	"testing"
-)
-
-func TestHasPrefixCaseInsensitive(t *testing.T) {
-	cases := []struct {
-		s      string
-		prefix string
-		want   bool
-	}{
-		{"HelloWorld", "hello", true},
-		{"HelloWorld", "WORLD", false},
-		{"", "prefix", false},
-		{"CaseSensitive", "casesensitive", true},
-		{"CaseSensitive", "CASE", true},
-	}
-
-	for _, c := range cases {
-		got := HasPrefixCaseInsensitive(c.s, c.prefix)
-		if got != c.want {
-			t.Errorf("HasPrefixCaseInsensitive(%q, %q) == %v, want %v", c.s, c.prefix, got, c.want)
-		}
-	}
-}
diff --git a/pkg/util/vm/vm_test.go b/pkg/util/vm/vm_test.go
deleted file mode 100644
index ec1cd9708..000000000
--- a/pkg/util/vm/vm_test.go
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
-Copyright 2024 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package vm
-
-import (
-	"testing"
-
-	"k8s.io/utils/ptr"
-
-	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2022-08-01/compute"
-	"github.com/stretchr/testify/assert"
-)
-
-func TestGetVMPowerState(t *testing.T) {
-	type testCase struct {
-		name       string
-		vmStatuses *[]compute.InstanceViewStatus
-		expected   string
-	}
-
-	tests := []testCase{
-		{
-			name: "should return power state when there is power state status",
-			vmStatuses: &[]compute.InstanceViewStatus{
-				{Code: ptr.To("foo")},
-				{Code: ptr.To("PowerState/Running")},
-			},
-			expected: "Running",
-		},
-		{
-			name: "should return unknown when there is no power state status",
-			vmStatuses: &[]compute.InstanceViewStatus{
-				{Code: ptr.To("foo")},
-			},
-			expected: "unknown",
-		},
-		{
-			name:       "should return unknown when vmStatuses is nil",
-			vmStatuses: nil,
-			expected:   "unknown",
-		},
-		{
-			name:       "should return unknown when vmStatuses is empty",
-			vmStatuses: &[]compute.InstanceViewStatus{},
-			expected:   "unknown",
-		},
-	}
-
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			assert.Equal(t, test.expected, GetVMPowerState("vm", test.vmStatuses))
-		})
-	}
-}
-
-func TestIsNotActiveVMState(t *testing.T) {
-	type testCase struct {
-		name              string
-		provisioningState string
-		powerState        string
-		expected          bool
-	}
-
-	tests := []testCase{
-		{
-			name:              "should return true when provisioning state is deleting",
-			provisioningState: "deleting",
-			powerState:        "running",
-			expected:          true,
-		},
-		{
-			name:              "should return true when provisioning state is unknown",
-			provisioningState: "unknown",
-			powerState:        "running",
-			expected:          true,
-		},
-		{
-			name:              "should return true when power state is unknown",
-			provisioningState: "running",
-			powerState:        "unknown",
-			expected:          true,
-		},
-		{
-			name:              "should return true when power state is stopped",
-			provisioningState: "running",
-			powerState:        "stopped",
-			expected:          true,
-		},
-		{
-			name:              "should return true when power state is stopping",
-			provisioningState: "running",
-			powerState:        "stopping",
-			expected:          true,
-		},
-		{
-			name:              "should return true when power state is deallocated",
-			provisioningState: "running",
-			powerState:        "deallocated",
-			expected:          true,
-		},
-		{
-			name:              "should return true when power state is deallocating",
-			provisioningState: "running",
-			powerState:        "deallocating",
-			expected:          true,
-		},
-		{
-			name:              "should return false when provisioning state is running and power state is running",
-			provisioningState: "running",
-			powerState:        "running",
-			expected:          false,
-		},
-	}
-
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			assert.Equal(t, test.expected, IsNotActiveVMState(test.provisioningState, test.powerState))
-		})
-	}
-}