Repository: kubernetes-sigs/cloud-provider-azure
Pull request #786 (2021-08-31 12:21:23)

feat: support creating account if not exists when account name is provided
<!--  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: support creating account if not exists when account name is provided

if `accountName` is provided, and `accountOptions.CreateAccount` is set as `true`(by default `false`), this PR would create account if not exists

#### 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:
/hold

#### 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: support creating account if not exists when account name is provided
```

#### 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: support creating account if not exists when account name is provided
```



Patch:
diff --git a/pkg/provider/azure_blobDiskController.go b/pkg/provider/azure_blobDiskController.go
index 410c8c08a..992f4c139 100644
--- a/pkg/provider/azure_blobDiskController.go
+++ b/pkg/provider/azure_blobDiskController.go
@@ -111,13 +111,13 @@ func (c *BlobDiskController) CreateVolume(ctx context.Context, blobName, account
 }
 
 // DeleteVolume deletes a VHD blob
-func (c *BlobDiskController) DeleteVolume(diskURI string) error {
+func (c *BlobDiskController) DeleteVolume(ctx context.Context, diskURI string) error {
 	klog.V(4).Infof("azureDisk - begin to delete volume %s", diskURI)
 	accountName, blob, err := c.common.cloud.getBlobNameAndAccountFromURI(diskURI)
 	if err != nil {
 		return fmt.Errorf("failed to parse vhd URI %w", err)
 	}
-	key, err := c.common.cloud.GetStorageAccesskey(accountName, c.common.resourceGroup)
+	key, err := c.common.cloud.GetStorageAccesskey(ctx, accountName, c.common.resourceGroup)
 	if err != nil {
 		return fmt.Errorf("no key for storage account %s, err %w", accountName, err)
 	}
@@ -249,7 +249,7 @@ func (c *BlobDiskController) CreateBlobDisk(dataDiskName string, storageAccountT
 }
 
 //DeleteBlobDisk : delete a blob disk from a node
-func (c *BlobDiskController) DeleteBlobDisk(diskURI string) error {
+func (c *BlobDiskController) DeleteBlobDisk(ctx context.Context, diskURI string) error {
 	storageAccountName, vhdName, err := diskNameAndSANameFromURI(diskURI)
 	if err != nil {
 		return err
@@ -259,7 +259,7 @@ func (c *BlobDiskController) DeleteBlobDisk(diskURI string) error {
 	if !ok {
 		// the storage account is specified by user
 		klog.V(4).Infof("azureDisk - deleting volume %s", diskURI)
-		return c.DeleteVolume(diskURI)
+		return c.DeleteVolume(ctx, diskURI)
 	}
 
 	blobSvc, err := c.getBlobSvcClient(storageAccountName)
diff --git a/pkg/provider/azure_storageaccount.go b/pkg/provider/azure_storageaccount.go
index 225bd36bc..7c9928513 100644
--- a/pkg/provider/azure_storageaccount.go
+++ b/pkg/provider/azure_storageaccount.go
@@ -42,8 +42,8 @@ const PrivateDNSZoneName = "privatelink.file.core.windows.net"
 // AccountOptions contains the fields which are used to create storage account.
 type AccountOptions struct {
 	Name, Type, Kind, ResourceGroup, Location string
-	// indicate whether create new account when Name is empty
-	EnableHTTPSTrafficOnly                  bool
+	EnableHTTPSTrafficOnly                    bool
+	// indicate whether create new account when Name is empty or when account does not exists
 	CreateAccount                           bool
 	EnableLargeFileShare                    bool
 	CreatePrivateEndpoint                   bool
@@ -60,12 +60,10 @@ type accountWithLocation struct {
 }
 
 // getStorageAccounts get matching storage accounts
-func (az *Cloud) getStorageAccounts(accountOptions *AccountOptions) ([]accountWithLocation, error) {
+func (az *Cloud) getStorageAccounts(ctx context.Context, accountOptions *AccountOptions) ([]accountWithLocation, error) {
 	if az.StorageAccountClient == nil {
 		return nil, fmt.Errorf("StorageAccountClient is nil")
 	}
-	ctx, cancel := getContextWithCancel()
-	defer cancel()
 	result, rerr := az.StorageAccountClient.ListByResourceGroup(ctx, accountOptions.ResourceGroup)
 	if rerr != nil {
 		return nil, rerr.Error()
@@ -92,13 +90,11 @@ func (az *Cloud) getStorageAccounts(accountOptions *AccountOptions) ([]accountWi
 }
 
 // GetStorageAccesskey gets the storage account access key
-func (az *Cloud) GetStorageAccesskey(account, resourceGroup string) (string, error) {
+func (az *Cloud) GetStorageAccesskey(ctx context.Context, account, resourceGroup string) (string, error) {
 	if az.StorageAccountClient == nil {
 		return "", fmt.Errorf("StorageAccountClient is nil")
 	}
 
-	ctx, cancel := getContextWithCancel()
-	defer cancel()
 	result, rerr := az.StorageAccountClient.ListKeys(ctx, resourceGroup, account)
 	if rerr != nil {
 		return "", rerr.Error()
@@ -132,151 +128,165 @@ func (az *Cloud) EnsureStorageAccount(ctx context.Context, accountOptions *Accou
 	location := accountOptions.Location
 	enableHTTPSTrafficOnly := accountOptions.EnableHTTPSTrafficOnly
 
+	var createNewAccount bool
 	if len(accountName) == 0 {
+		createNewAccount = true
 		if !accountOptions.CreateAccount {
 			// find a storage account that matches accountType
-			accounts, err := az.getStorageAccounts(accountOptions)
+			accounts, err := az.getStorageAccounts(ctx, accountOptions)
 			if err != nil {
 				return "", "", fmt.Errorf("could not list storage accounts for account type %s: %w", accountType, err)
 			}
 
 			if len(accounts) > 0 {
 				accountName = accounts[0].Name
+				createNewAccount = false
 				klog.V(4).Infof("found a matching account %s type %s location %s", accounts[0].Name, accounts[0].StorageType, accounts[0].Location)
 			}
 		}
 
 		if len(accountName) == 0 {
-			// set network rules for storage account
-			var networkRuleSet *storage.NetworkRuleSet
-			virtualNetworkRules := []storage.VirtualNetworkRule{}
-			for i, subnetID := range accountOptions.VirtualNetworkResourceIDs {
-				vnetRule := storage.VirtualNetworkRule{
-					VirtualNetworkResourceID: &accountOptions.VirtualNetworkResourceIDs[i],
-					Action:                   storage.ActionAllow,
-				}
-				virtualNetworkRules = append(virtualNetworkRules, vnetRule)
-				klog.V(4).Infof("subnetID(%s) has been set", subnetID)
-			}
-			if len(virtualNetworkRules) > 0 {
-				networkRuleSet = &storage.NetworkRuleSet{
-					VirtualNetworkRules: &virtualNetworkRules,
-					DefaultAction:       storage.DefaultActionDeny,
-				}
+			accountName = generateStorageAccountName(genAccountNamePrefix)
+		}
+	} else {
+		createNewAccount = false
+		if accountOptions.CreateAccount {
+			// check whether account exists
+			if _, err := az.GetStorageAccesskey(ctx, accountName, resourceGroup); err != nil {
+				klog.V(2).Infof("get storage key for storage account %s returned with %v", accountName, err)
+				createNewAccount = true
 			}
+		}
+	}
 
-			if accountOptions.CreatePrivateEndpoint {
-				networkRuleSet = &storage.NetworkRuleSet{
-					DefaultAction: storage.DefaultActionDeny,
-				}
+	if createNewAccount {
+		// set network rules for storage account
+		var networkRuleSet *storage.NetworkRuleSet
+		virtualNetworkRules := []storage.VirtualNetworkRule{}
+		for i, subnetID := range accountOptions.VirtualNetworkResourceIDs {
+			vnetRule := storage.VirtualNetworkRule{
+				VirtualNetworkResourceID: &accountOptions.VirtualNetworkResourceIDs[i],
+				Action:                   storage.ActionAllow,
 			}
-
-			// not found a matching account, now create a new account in current resource group
-			accountName = generateStorageAccountName(genAccountNamePrefix)
-			if location == "" {
-				location = az.Location
+			virtualNetworkRules = append(virtualNetworkRules, vnetRule)
+			klog.V(4).Infof("subnetID(%s) has been set", subnetID)
+		}
+		if len(virtualNetworkRules) > 0 {
+			networkRuleSet = &storage.NetworkRuleSet{
+				VirtualNetworkRules: &virtualNetworkRules,
+				DefaultAction:       storage.DefaultActionDeny,
 			}
-			if accountType == "" {
-				accountType = consts.DefaultStorageAccountType
+		}
+
+		if accountOptions.CreatePrivateEndpoint {
+			networkRuleSet = &storage.NetworkRuleSet{
+				DefaultAction: storage.DefaultActionDeny,
 			}
+		}
+
+		if location == "" {
+			location = az.Location
+		}
+		if accountType == "" {
+			accountType = consts.DefaultStorageAccountType
+		}
+
+		// use StorageV2 by default per https://docs.microsoft.com/en-us/azure/storage/common/storage-account-options
+		kind := consts.DefaultStorageAccountKind
+		if accountKind != "" {
+			kind = storage.Kind(accountKind)
+		}
+		if len(accountOptions.Tags) == 0 {
+			accountOptions.Tags = make(map[string]string)
+		}
+		accountOptions.Tags["created-by"] = "azure"
+		tags := convertMapToMapPointer(accountOptions.Tags)
+
+		klog.V(2).Infof("azure - no matching account found, begin to create a new account %s in resource group %s, location: %s, accountType: %s, accountKind: %s, tags: %+v",
+			accountName, resourceGroup, location, accountType, kind, accountOptions.Tags)
+
+		cp := storage.AccountCreateParameters{
+			Sku:  &storage.Sku{Name: storage.SkuName(accountType)},
+			Kind: kind,
+			AccountPropertiesCreateParameters: &storage.AccountPropertiesCreateParameters{
+				EnableHTTPSTrafficOnly: &enableHTTPSTrafficOnly,
+				NetworkRuleSet:         networkRuleSet,
+				IsHnsEnabled:           accountOptions.IsHnsEnabled,
+				EnableNfsV3:            accountOptions.EnableNfsV3,
+				MinimumTLSVersion:      storage.MinimumTLSVersionTLS12,
+			},
+			Tags:     tags,
+			Location: &location}
+
+		if accountOptions.EnableLargeFileShare {
+			klog.V(2).Infof("Enabling LargeFileShare for storage account(%s)", accountName)
+			cp.AccountPropertiesCreateParameters.LargeFileSharesState = storage.LargeFileSharesStateEnabled
+		}
+		if accountOptions.AllowBlobPublicAccess != nil {
+			klog.V(2).Infof("set AllowBlobPublicAccess(%v) for storage account(%s)", *accountOptions.AllowBlobPublicAccess, accountName)
+			cp.AccountPropertiesCreateParameters.AllowBlobPublicAccess = accountOptions.AllowBlobPublicAccess
+		}
+		if az.StorageAccountClient == nil {
+			return "", "", fmt.Errorf("StorageAccountClient is nil")
+		}
+
+		if rerr := az.StorageAccountClient.Create(ctx, resourceGroup, accountName, cp); rerr != nil {
+			return "", "", fmt.Errorf("failed to create storage account %s, error: %v", accountName, rerr)
+		}
 
-			// use StorageV2 by default per https://docs.microsoft.com/en-us/azure/storage/common/storage-account-options
-			kind := consts.DefaultStorageAccountKind
-			if accountKind != "" {
-				kind = storage.Kind(accountKind)
+		if accountOptions.DisableFileServiceDeleteRetentionPolicy {
+			klog.V(2).Infof("disable DisableFileServiceDeleteRetentionPolicy on account(%s), resource group(%s)", accountName, resourceGroup)
+			prop, err := az.FileClient.GetServiceProperties(resourceGroup, accountName)
+			if err != nil {
+				return "", "", err
 			}
-			if len(accountOptions.Tags) == 0 {
-				accountOptions.Tags = make(map[string]string)
+			if prop.FileServicePropertiesProperties == nil {
+				return "", "", fmt.Errorf("FileServicePropertiesProperties of account(%s), resource group(%s) is nil", accountName, resourceGroup)
 			}
-			accountOptions.Tags["created-by"] = "azure"
-			tags := convertMapToMapPointer(accountOptions.Tags)
-
-			klog.V(2).Infof("azure - no matching account found, begin to create a new account %s in resource group %s, location: %s, accountType: %s, accountKind: %s, tags: %+v",
-				accountName, resourceGroup, location, accountType, kind, accountOptions.Tags)
-
-			cp := storage.AccountCreateParameters{
-				Sku:  &storage.Sku{Name: storage.SkuName(accountType)},
-				Kind: kind,
-				AccountPropertiesCreateParameters: &storage.AccountPropertiesCreateParameters{
-					EnableHTTPSTrafficOnly: &enableHTTPSTrafficOnly,
-					NetworkRuleSet:         networkRuleSet,
-					IsHnsEnabled:           accountOptions.IsHnsEnabled,
-					EnableNfsV3:            accountOptions.EnableNfsV3,
-					MinimumTLSVersion:      storage.MinimumTLSVersionTLS12,
-				},
-				Tags:     tags,
-				Location: &location}
-
-			if accountOptions.EnableLargeFileShare {
-				klog.V(2).Infof("Enabling LargeFileShare for storage account(%s)", accountName)
-				cp.AccountPropertiesCreateParameters.LargeFileSharesState = storage.LargeFileSharesStateEnabled
+			prop.FileServicePropertiesProperties.ShareDeleteRetentionPolicy = &storage.DeleteRetentionPolicy{Enabled: to.BoolPtr(false)}
+			if _, err := az.FileClient.SetServiceProperties(resourceGroup, accountName, prop); err != nil {
+				return "", "", err
 			}
-			if accountOptions.AllowBlobPublicAccess != nil {
-				klog.V(2).Infof("set AllowBlobPublicAccess(%v) for storage account(%s)", *accountOptions.AllowBlobPublicAccess, accountName)
-				cp.AccountPropertiesCreateParameters.AllowBlobPublicAccess = accountOptions.AllowBlobPublicAccess
+		}
+
+		if accountOptions.CreatePrivateEndpoint {
+			vnetResourceGroup := az.ResourceGroup
+			if len(az.VnetResourceGroup) > 0 {
+				vnetResourceGroup = az.VnetResourceGroup
 			}
-			if az.StorageAccountClient == nil {
-				return "", "", fmt.Errorf("StorageAccountClient is nil")
+			// Get properties of the storageAccount
+			storageAccount, err := az.StorageAccountClient.GetProperties(ctx, resourceGroup, accountName)
+			if err != nil {
+				return "", "", fmt.Errorf("Failed to get the properties of storage account(%s), resourceGroup(%s), error: %v", accountName, resourceGroup, err)
 			}
 
-			if rerr := az.StorageAccountClient.Create(ctx, resourceGroup, accountName, cp); rerr != nil {
-				return "", "", fmt.Errorf("failed to create storage account %s, error: %v", accountName, rerr)
+			// Create private endpoint
+			privateEndpointName := accountName + "-pvtendpoint"
+			if err := az.createPrivateEndpoint(ctx, accountName, storageAccount.ID, privateEndpointName, vnetResourceGroup); err != nil {
+				return "", "", fmt.Errorf("Failed to create private endpoint for storage account(%s), resourceGroup(%s), error: %v", accountName, vnetResourceGroup, err)
 			}
 
-			if accountOptions.DisableFileServiceDeleteRetentionPolicy {
-				klog.V(2).Infof("disable DisableFileServiceDeleteRetentionPolicy on account(%s), resource group(%s)", accountName, resourceGroup)
-				prop, err := az.FileClient.GetServiceProperties(resourceGroup, accountName)
-				if err != nil {
-					return "", "", err
-				}
-				if prop.FileServicePropertiesProperties == nil {
-					return "", "", fmt.Errorf("FileServicePropertiesProperties of account(%s), resource group(%s) is nil", accountName, resourceGroup)
-				}
-				prop.FileServicePropertiesProperties.ShareDeleteRetentionPolicy = &storage.DeleteRetentionPolicy{Enabled: to.BoolPtr(false)}
-				if _, err := az.FileClient.SetServiceProperties(resourceGroup, accountName, prop); err != nil {
-					return "", "", err
-				}
+			// Create DNS zone
+			if err := az.createPrivateDNSZone(ctx, vnetResourceGroup); err != nil {
+				return "", "", fmt.Errorf("Failed to create private DNS zone(%s) in resourceGroup(%s), error: %v", PrivateDNSZoneName, vnetResourceGroup, err)
 			}
 
-			if accountOptions.CreatePrivateEndpoint {
-				vnetResourceGroup := az.ResourceGroup
-				if len(az.VnetResourceGroup) > 0 {
-					vnetResourceGroup = az.VnetResourceGroup
-				}
-				// Get properties of the storageAccount
-				storageAccount, err := az.StorageAccountClient.GetProperties(ctx, resourceGroup, accountName)
-				if err != nil {
-					return "", "", fmt.Errorf("Failed to get the properties of storage account(%s), resourceGroup(%s), error: %v", accountName, resourceGroup, err)
-				}
-
-				// Create private endpoint
-				privateEndpointName := accountName + "-pvtendpoint"
-				if err := az.createPrivateEndpoint(ctx, accountName, storageAccount.ID, privateEndpointName, vnetResourceGroup); err != nil {
-					return "", "", fmt.Errorf("Failed to create private endpoint for storage account(%s), resourceGroup(%s), error: %v", accountName, vnetResourceGroup, err)
-				}
-
-				// Create DNS zone
-				if err := az.createPrivateDNSZone(ctx, vnetResourceGroup); err != nil {
-					return "", "", fmt.Errorf("Failed to create private DNS zone(%s) in resourceGroup(%s), error: %v", PrivateDNSZoneName, vnetResourceGroup, err)
-				}
-
-				// Create virtual link to the zone private DNS zone
-				vNetLinkName := accountName + "-vnetlink"
-				if err := az.createVNetLink(ctx, vNetLinkName, vnetResourceGroup); err != nil {
-					return "", "", fmt.Errorf("Failed to create virtual link for vnet(%s) and DNS Zone(%s) in resourceGroup(%s), error: %v", az.VnetName, PrivateDNSZoneName, vnetResourceGroup, err)
-				}
+			// Create virtual link to the zone private DNS zone
+			vNetLinkName := accountName + "-vnetlink"
+			if err := az.createVNetLink(ctx, vNetLinkName, vnetResourceGroup); err != nil {
+				return "", "", fmt.Errorf("Failed to create virtual link for vnet(%s) and DNS Zone(%s) in resourceGroup(%s), error: %v", az.VnetName, PrivateDNSZoneName, vnetResourceGroup, err)
+			}
 
-				// Create dns zone group
-				dnsZoneGroupName := accountName + "-dnszonegroup"
-				if err := az.createPrivateDNSZoneGroup(ctx, dnsZoneGroupName, privateEndpointName, vnetResourceGroup); err != nil {
-					return "", "", fmt.Errorf("Failed to create private DNS zone group - privateEndpoint(%s), vNetName(%s), resourceGroup(%s), error: %v", privateEndpointName, az.VnetName, vnetResourceGroup, err)
-				}
+			// Create dns zone group
+			dnsZoneGroupName := accountName + "-dnszonegroup"
+			if err := az.createPrivateDNSZoneGroup(ctx, dnsZoneGroupName, privateEndpointName, vnetResourceGroup); err != nil {
+				return "", "", fmt.Errorf("Failed to create private DNS zone group - privateEndpoint(%s), vNetName(%s), resourceGroup(%s), error: %v", privateEndpointName, az.VnetName, vnetResourceGroup, err)
 			}
 		}
 	}
 
 	// find the access key with this account
-	accountKey, err := az.GetStorageAccesskey(accountName, resourceGroup)
+	accountKey, err := az.GetStorageAccesskey(ctx, accountName, resourceGroup)
 	if err != nil {
 		return "", "", fmt.Errorf("could not get storage key for storage account %s: %w", accountName, err)
 	}


Test patch:
diff --git a/pkg/provider/azure_blobDiskController_test.go b/pkg/provider/azure_blobDiskController_test.go
index 951c841a9..0111efb05 100644
--- a/pkg/provider/azure_blobDiskController_test.go
+++ b/pkg/provider/azure_blobDiskController_test.go
@@ -117,6 +117,8 @@ func TestCreateVolume(t *testing.T) {
 func TestDeleteVolume(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
+	ctx, cancel := getContextWithCancel()
+	defer cancel()
 	b := GetTestBlobDiskController(t)
 	b.common.cloud.BlobDiskController = &b
 
@@ -126,14 +128,14 @@ func TestDeleteVolume(t *testing.T) {
 
 	fakeDiskURL := "fake"
 	diskURL := "https://foo.blob./vhds/bar.vhd"
-	err := b.DeleteVolume(diskURL)
+	err := b.DeleteVolume(ctx, diskURL)
 	var nilErr error
 	rawErr := fmt.Errorf("%w", nilErr)
 	retryErr := fmt.Errorf("Retriable: false, RetryAfter: 0s, HTTPStatusCode: 500, RawError: %w", rawErr)
 	expectedErr := fmt.Errorf("no key for storage account foo, err %w", retryErr)
 	assert.EqualError(t, expectedErr, err.Error())
 
-	err = b.DeleteVolume(diskURL)
+	err = b.DeleteVolume(ctx, diskURL)
 	assert.EqualError(t, expectedErr, err.Error())
 
 	mockSAClient.EXPECT().ListKeys(gomock.Any(), b.common.resourceGroup, "foo").Return(storage.AccountListKeysResult{
@@ -145,11 +147,11 @@ func TestDeleteVolume(t *testing.T) {
 		},
 	}, nil)
 
-	err = b.DeleteVolume(fakeDiskURL)
+	err = b.DeleteVolume(ctx, fakeDiskURL)
 	expectedErr = fmt.Errorf("failed to parse vhd URI invalid vhd URI for regex https://(.*).blob./vhds/(.*): %w", fmt.Errorf("fake"))
 	assert.EqualError(t, expectedErr, err.Error())
 
-	err = b.DeleteVolume(diskURL)
+	err = b.DeleteVolume(ctx, diskURL)
 	expectedErrStr := "failed to delete vhd https://foo.blob./vhds/bar.vhd, account foo, blob bar.vhd, err: storage: service returned error: " +
 		"StatusCode=403, ErrorCode=AccountIsDisabled, ErrorMessage=The specified account is disabled."
 	assert.Error(t, err)
diff --git a/pkg/provider/azure_storageaccount_test.go b/pkg/provider/azure_storageaccount_test.go
index 7dfb5be9f..8f80e2bf1 100644
--- a/pkg/provider/azure_storageaccount_test.go
+++ b/pkg/provider/azure_storageaccount_test.go
@@ -32,6 +32,7 @@ import (
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/storageaccountclient/mockstorageaccountclient"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/subnetclient/mocksubnetclient"
 	"sigs.k8s.io/cloud-provider-azure/pkg/azureclients/virtualnetworklinksclient/mockvirtualnetworklinksclient"
+	"sigs.k8s.io/cloud-provider-azure/pkg/retry"
 )
 
 const TestLocation = "testLocation"
@@ -40,6 +41,9 @@ func TestGetStorageAccessKeys(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
+	ctx, cancel := getContextWithCancel()
+	defer cancel()
+
 	cloud := &Cloud{}
 	value := "foo bar"
 
@@ -78,7 +82,7 @@ func TestGetStorageAccessKeys(t *testing.T) {
 		mockStorageAccountsClient := mockstorageaccountclient.NewMockInterface(ctrl)
 		cloud.StorageAccountClient = mockStorageAccountsClient
 		mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), "rg", gomock.Any()).Return(test.results, nil).AnyTimes()
-		key, err := cloud.GetStorageAccesskey("acct", "rg")
+		key, err := cloud.GetStorageAccesskey(ctx, "acct", "rg")
 		if test.expectErr && err == nil {
 			t.Errorf("Unexpected non-error")
 			continue
@@ -97,6 +101,9 @@ func TestGetStorageAccount(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
+	ctx, cancel := getContextWithCancel()
+	defer cancel()
+
 	cloud := &Cloud{}
 
 	name := "testAccount"
@@ -136,7 +143,7 @@ func TestGetStorageAccount(t *testing.T) {
 
 	mockStorageAccountsClient.EXPECT().ListByResourceGroup(gomock.Any(), "rg").Return(testResourceGroups, nil).Times(1)
 
-	accountsWithLocations, err := cloud.getStorageAccounts(accountOptions)
+	accountsWithLocations, err := cloud.getStorageAccounts(ctx, accountOptions)
 	if err != nil {
 		t.Errorf("unexpected error: %v", err)
 	}
@@ -173,6 +180,9 @@ func TestGetStorageAccountEdgeCases(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	defer ctrl.Finish()
 
+	ctx, cancel := getContextWithCancel()
+	defer cancel()
+
 	cloud := &Cloud{}
 
 	// default account with name, location, sku, kind
@@ -308,7 +318,7 @@ func TestGetStorageAccountEdgeCases(t *testing.T) {
 
 		mockStorageAccountsClient.EXPECT().ListByResourceGroup(gomock.Any(), "rg").Return(test.testResourceGroups, nil).AnyTimes()
 
-		accountsWithLocations, err := cloud.getStorageAccounts(test.testAccountOptions)
+		accountsWithLocations, err := cloud.getStorageAccounts(ctx, test.testAccountOptions)
 		if !errors.Is(err, test.expectedError) {
 			t.Errorf("unexpected error: %v", err)
 		}
@@ -356,42 +366,63 @@ func TestEnsureStorageAccountWithPrivateEndpoint(t *testing.T) {
 		},
 	}
 
-	mockStorageAccountsClient := mockstorageaccountclient.NewMockInterface(ctrl)
-	mockStorageAccountsClient.EXPECT().ListByResourceGroup(gomock.Any(), gomock.Any()).Return(testStorageAccounts, nil).AnyTimes()
-	mockStorageAccountsClient.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
-	mockStorageAccountsClient.EXPECT().GetProperties(gomock.Any(), gomock.Any(), gomock.Any()).Return(testStorageAccounts[0], nil).AnyTimes()
-	mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(storageAccountListKeys, nil).AnyTimes()
-	cloud.StorageAccountClient = mockStorageAccountsClient
+	tests := []struct {
+		CreateAccount bool
+		AccountName   string
+	}{
+		{
+			CreateAccount: false,
+			AccountName:   "",
+		},
+		{
+			CreateAccount: true,
+			AccountName:   "accountname",
+		},
+	}
 
-	subnet := network.Subnet{SubnetPropertiesFormat: &network.SubnetPropertiesFormat{}}
-	mockSubnetsClient := mocksubnetclient.NewMockInterface(ctrl)
-	mockSubnetsClient.EXPECT().Get(gomock.Any(), vnetResourceGroup, vnetName, subnetName, gomock.Any()).Return(subnet, nil).Times(1)
-	mockSubnetsClient.EXPECT().CreateOrUpdate(gomock.Any(), vnetResourceGroup, vnetName, subnetName, gomock.Any()).Return(nil).Times(1)
-	cloud.SubnetsClient = mockSubnetsClient
+	for _, test := range tests {
+		mockStorageAccountsClient := mockstorageaccountclient.NewMockInterface(ctrl)
+		mockStorageAccountsClient.EXPECT().ListByResourceGroup(gomock.Any(), gomock.Any()).Return(testStorageAccounts, nil).AnyTimes()
+		mockStorageAccountsClient.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
+		mockStorageAccountsClient.EXPECT().GetProperties(gomock.Any(), gomock.Any(), gomock.Any()).Return(testStorageAccounts[0], nil).AnyTimes()
+		if test.AccountName == "" {
+			mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(storageAccountListKeys, nil).AnyTimes()
+		} else {
+			mockStorageAccountsClient.EXPECT().ListKeys(gomock.Any(), gomock.Any(), gomock.Any()).Return(storageAccountListKeys, &retry.Error{}).AnyTimes()
+		}
+		cloud.StorageAccountClient = mockStorageAccountsClient
 
-	mockPrivateDNSClient := mockprivatednsclient.NewMockInterface(ctrl)
-	mockPrivateDNSClient.EXPECT().CreateOrUpdate(gomock.Any(), vnetResourceGroup, gomock.Any(), gomock.Any(), true).Return(nil).Times(1)
-	cloud.privatednsclient = mockPrivateDNSClient
+		subnet := network.Subnet{SubnetPropertiesFormat: &network.SubnetPropertiesFormat{}}
 
-	mockPrivateDNSZoneGroup := mockprivatednszonegroupclient.NewMockInterface(ctrl)
-	mockPrivateDNSZoneGroup.EXPECT().CreateOrUpdate(gomock.Any(), vnetResourceGroup, gomock.Any(), gomock.Any(), gomock.Any(), false).Return(nil).Times(1)
-	cloud.privatednszonegroupclient = mockPrivateDNSZoneGroup
+		mockSubnetsClient := mocksubnetclient.NewMockInterface(ctrl)
+		mockSubnetsClient.EXPECT().Get(gomock.Any(), vnetResourceGroup, vnetName, subnetName, gomock.Any()).Return(subnet, nil).Times(1)
+		mockSubnetsClient.EXPECT().CreateOrUpdate(gomock.Any(), vnetResourceGroup, vnetName, subnetName, gomock.Any()).Return(nil).Times(1)
+		cloud.SubnetsClient = mockSubnetsClient
 
-	mockPrivateEndpointClient := mockprivateendpointclient.NewMockInterface(ctrl)
-	mockPrivateEndpointClient.EXPECT().CreateOrUpdate(gomock.Any(), vnetResourceGroup, gomock.Any(), gomock.Any(), true).Return(nil).Times(1)
-	cloud.privateendpointclient = mockPrivateEndpointClient
+		mockPrivateDNSClient := mockprivatednsclient.NewMockInterface(ctrl)
+		mockPrivateDNSClient.EXPECT().CreateOrUpdate(gomock.Any(), vnetResourceGroup, gomock.Any(), gomock.Any(), true).Return(nil).Times(1)
+		cloud.privatednsclient = mockPrivateDNSClient
 
-	mockVirtualNetworkLinksClient := mockvirtualnetworklinksclient.NewMockInterface(ctrl)
-	mockVirtualNetworkLinksClient.EXPECT().CreateOrUpdate(gomock.Any(), vnetResourceGroup, gomock.Any(), gomock.Any(), gomock.Any(), false).Return(nil).Times(1)
-	cloud.virtualNetworkLinksClient = mockVirtualNetworkLinksClient
+		mockPrivateDNSZoneGroup := mockprivatednszonegroupclient.NewMockInterface(ctrl)
+		mockPrivateDNSZoneGroup.EXPECT().CreateOrUpdate(gomock.Any(), vnetResourceGroup, gomock.Any(), gomock.Any(), gomock.Any(), false).Return(nil).Times(1)
+		cloud.privatednszonegroupclient = mockPrivateDNSZoneGroup
 
-	testAccountOptions := &AccountOptions{
-		ResourceGroup:         "rg",
-		CreatePrivateEndpoint: true,
-	}
+		mockPrivateEndpointClient := mockprivateendpointclient.NewMockInterface(ctrl)
+		mockPrivateEndpointClient.EXPECT().CreateOrUpdate(gomock.Any(), vnetResourceGroup, gomock.Any(), gomock.Any(), true).Return(nil).Times(1)
+		cloud.privateendpointclient = mockPrivateEndpointClient
 
-	if _, _, err := cloud.EnsureStorageAccount(ctx, testAccountOptions, "test"); err != nil {
-		t.Errorf("unexpected error: %v", err)
+		mockVirtualNetworkLinksClient := mockvirtualnetworklinksclient.NewMockInterface(ctrl)
+		mockVirtualNetworkLinksClient.EXPECT().CreateOrUpdate(gomock.Any(), vnetResourceGroup, gomock.Any(), gomock.Any(), gomock.Any(), false).Return(nil).Times(1)
+		cloud.virtualNetworkLinksClient = mockVirtualNetworkLinksClient
+
+		testAccountOptions := &AccountOptions{
+			ResourceGroup:         "rg",
+			CreatePrivateEndpoint: true,
+			Name:                  test.AccountName,
+			CreateAccount:         test.CreateAccount,
+		}
+		_, _, err := cloud.EnsureStorageAccount(ctx, testAccountOptions, "test")
+		assert.Equal(t, err == nil, test.AccountName == "")
 	}
 }