Repository: kubernetes-sigs/cloud-provider-azure
Pull request #8993 (2025-04-30 03:40:03)

[release-1.32] feat: add VNetLinkName and PublicNetworkAccess in account creation
This is an automated cherry-pick of #8990

/assign andyzhangx

```release-note
feat: add VNetLinkName and PublicNetworkAccess in account creation
```

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

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/cloud-provider-azure/pull/8993#" title="Author self-approved">k8s-infra-cherrypick-robot</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign [martinforreal](https://github.com/martinforreal) for approval. For more information see [the Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

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).

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

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

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["martinforreal"]} -->
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/8993#pullrequestreview-2805764042" title="LGTM">andyzhangx</a>*, *<a href="https://github.com/kubernetes-sigs/cloud-provider-azure/pull/8993#" title="Author self-approved">k8s-infra-cherrypick-robot</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/release-1.32/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":[]} -->
andyzhangx: /kind feature

Patch:
diff --git a/pkg/consts/consts.go b/pkg/consts/consts.go
index 6f75dc435..658b7fd1f 100644
--- a/pkg/consts/consts.go
+++ b/pkg/consts/consts.go
@@ -399,7 +399,7 @@ const (
 	// ParentResourceNotFoundMessageCode is the error code that the parent VMSS of the VM is not found.
 	ParentResourceNotFoundMessageCode = "ParentResourceNotFound"
 	// ResourceNotFoundMessageCode is the error code that the resource is not found.
-	ResourceNotFoundMessageCode = "ResourceNotFound"
+	ResourceNotFoundMessageCode = "NotFound"
 	// ConcurrentRequestConflictMessage is the error message that the request failed due to the conflict with another concurrent operation.
 	ConcurrentRequestConflictMessage = "The request failed due to conflict with a concurrent request."
 	// CannotUpdateVMBeingDeletedMessagePrefix is the prefix of the error message that the request failed due to delete a VM that is being deleted
diff --git a/pkg/provider/azure_loadbalancer.go b/pkg/provider/azure_loadbalancer.go
index 24fa1504f..81679821d 100644
--- a/pkg/provider/azure_loadbalancer.go
+++ b/pkg/provider/azure_loadbalancer.go
@@ -522,7 +522,7 @@ func (az *Cloud) shouldChangeLoadBalancer(service *v1.Service, currLBName, clust
 	// 1. Using multiple standard load balancers.
 	// 2. Migrate from multiple standard load balancers to single standard load balancer.
 	if az.UseStandardLoadBalancer() {
-		if currLBName != expectedLBName {
+		if !strings.EqualFold(currLBName, expectedLBName) {
 			klog.V(2).Infof("shouldChangeLoadBalancer(%s, %s, %s): change the LB to another one %s", service.Name, currLBName, clusterName, expectedLBName)
 			return true
 		}
diff --git a/pkg/provider/storage/azure_storageaccount.go b/pkg/provider/storage/azure_storageaccount.go
index 26f68e4bf..986d1da0b 100644
--- a/pkg/provider/storage/azure_storageaccount.go
+++ b/pkg/provider/storage/azure_storageaccount.go
@@ -97,6 +97,9 @@ type AccountOptions struct {
 	SourceAccountName string
 	// default is "privatelink"
 	PrivateDNSZoneName string
+	// default is vnetName + "-vnetlink"
+	VNetLinkName        string
+	PublicNetworkAccess string
 }
 
 type accountWithLocation struct {
@@ -485,6 +488,9 @@ func (az *AccountRepo) EnsureStorageAccount(ctx context.Context, accountOptions
 
 		// Create virtual link to the private DNS zone
 		vNetLinkName := vnetName + "-vnetlink"
+		if accountOptions.VNetLinkName != "" {
+			vNetLinkName = accountOptions.VNetLinkName
+		}
 		if _, err := clientFactory.GetVirtualNetworkLinkClient().Get(ctx, vnetResourceGroup, privateDNSZoneName, vNetLinkName); err != nil {
 			if strings.Contains(err.Error(), consts.ResourceNotFoundMessageCode) {
 				if err := az.createVNetLink(ctx, vNetLinkName, vnetResourceGroup, vnetName, privateDNSZoneName); err != nil {
@@ -532,6 +538,13 @@ func (az *AccountRepo) EnsureStorageAccount(ctx context.Context, accountOptions
 		}
 		tags := convertMapToMapPointer(accountOptions.Tags)
 
+		var publicNetworkAccess *armstorage.PublicNetworkAccess
+		if accountOptions.PublicNetworkAccess != "" {
+			klog.V(2).Infof("set PublicNetworkAccess(%s) on account(%s), subscription(%s), resource group(%s)", accountOptions.PublicNetworkAccess, accountName, subsID, resourceGroup)
+			access := armstorage.PublicNetworkAccess(accountOptions.PublicNetworkAccess)
+			publicNetworkAccess = &access
+		}
+
 		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)
 
@@ -544,6 +557,7 @@ func (az *AccountRepo) EnsureStorageAccount(ctx context.Context, accountOptions
 				IsHnsEnabled:           accountOptions.IsHnsEnabled,
 				EnableNfsV3:            accountOptions.EnableNfsV3,
 				MinimumTLSVersion:      to.Ptr(armstorage.MinimumTLSVersionTLS12),
+				PublicNetworkAccess:    publicNetworkAccess,
 			},
 			Tags:     tags,
 			Location: &location}


Test patch:
diff --git a/pkg/provider/azure_loadbalancer_test.go b/pkg/provider/azure_loadbalancer_test.go
index c8650c0e2..d5cd0f5a4 100644
--- a/pkg/provider/azure_loadbalancer_test.go
+++ b/pkg/provider/azure_loadbalancer_test.go
@@ -2019,6 +2019,45 @@ func TestGetServiceLoadBalancerCommon(t *testing.T) {
 			expectedExists: false,
 			expectedError:  false,
 		},
+		{
+			desc: "getServiceLoadBalancer should not change LB when names are the same but with different case with standard LB",
+			SKU:  consts.LoadBalancerSKUStandard,
+			existingLBs: []*armnetwork.LoadBalancer{
+				{
+					Name: ptr.To("TestCluster"),
+					Properties: &armnetwork.LoadBalancerPropertiesFormat{
+						FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{
+							{
+								Name: ptr.To("aservice1"),
+								Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{
+									PublicIPAddress: &armnetwork.PublicIPAddress{
+										ID: ptr.To("TestCluster-aservice1"),
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+			service: getTestService("service1", v1.ProtocolTCP, nil, false, 80),
+			wantLB:  true,
+			expectedLB: &armnetwork.LoadBalancer{
+				Name: ptr.To("TestCluster"),
+				Properties: &armnetwork.LoadBalancerPropertiesFormat{
+					FrontendIPConfigurations: []*armnetwork.FrontendIPConfiguration{
+						{
+							Name: ptr.To("aservice1"),
+							Properties: &armnetwork.FrontendIPConfigurationPropertiesFormat{
+								PublicIPAddress: &armnetwork.PublicIPAddress{ID: ptr.To("TestCluster-aservice1")},
+							},
+						},
+					},
+				},
+			},
+			expectedStatus: &v1.LoadBalancerStatus{Ingress: []v1.LoadBalancerIngress{{IP: "1.2.3.4", Hostname: ""}}},
+			expectedExists: true,
+			expectedError:  false,
+		},
 	}
 
 	for _, test := range testCases {
diff --git a/pkg/provider/storage/azure_storageaccount_test.go b/pkg/provider/storage/azure_storageaccount_test.go
index aa8d7ff98..643c881c8 100644
--- a/pkg/provider/storage/azure_storageaccount_test.go
+++ b/pkg/provider/storage/azure_storageaccount_test.go
@@ -373,7 +373,6 @@ func TestGetStorageAccountEdgeCases(t *testing.T) {
 }
 
 func TestEnsureStorageAccount(t *testing.T) {
-
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
@@ -415,6 +414,8 @@ func TestEnsureStorageAccount(t *testing.T) {
 		name                            string
 		createAccount                   bool
 		createPrivateEndpoint           *bool
+		vNetLinkName                    string
+		publicNetworkAccess             string
 		SubnetPropertiesFormatNil       bool
 		mockStorageAccountsClient       bool
 		setAccountOptions               bool
@@ -449,6 +450,8 @@ func TestEnsureStorageAccount(t *testing.T) {
 			name:                            "[Success] EnsureStorageAccount with createPrivateEndpoint",
 			createAccount:                   true,
 			createPrivateEndpoint:           ptr.To(true),
+			vNetLinkName:                    "vnetLinkName",
+			publicNetworkAccess:             string(armstorage.PublicNetworkAccessDisabled),
 			mockStorageAccountsClient:       true,
 			setAccountOptions:               true,
 			requireInfrastructureEncryption: ptr.To(true),
@@ -564,6 +567,8 @@ func TestEnsureStorageAccount(t *testing.T) {
 				testAccountOptions = &AccountOptions{
 					ResourceGroup:             test.resourceGroup,
 					CreatePrivateEndpoint:     test.createPrivateEndpoint,
+					VNetLinkName:              test.vNetLinkName,
+					PublicNetworkAccess:       test.publicNetworkAccess,
 					Name:                      test.accountName,
 					CreateAccount:             test.createAccount,
 					SubscriptionID:            test.subscriptionID,