Browse Source

fix: make custom configuration available regardless of environment (#5713)

Gergely Brautigam 4 months ago
parent
commit
b1d7a7fea1

+ 4 - 1
apis/externalsecrets/v1/secretstore_azurekv_types.go

@@ -118,8 +118,11 @@ type AzureKVProvider struct {
 	// +kubebuilder:default=false
 	// +kubebuilder:default=false
 	UseAzureSDK *bool `json:"useAzureSDK,omitempty"`
 	UseAzureSDK *bool `json:"useAzureSDK,omitempty"`
 
 
-	// CustomCloudConfig defines custom Azure Stack Hub or Azure Stack Edge endpoints.
+	// CustomCloudConfig defines custom Azure endpoints for non-standard clouds.
 	// Required when EnvironmentType is AzureStackCloud.
 	// Required when EnvironmentType is AzureStackCloud.
+	// Optional for other environment types - useful for Azure China when using Workload Identity
+	// with AKS, where the OIDC issuer (login.partner.microsoftonline.cn) differs from the
+	// standard China Cloud endpoint (login.chinacloudapi.cn).
 	// IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
 	// IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
 	// configuration is not supported with the legacy go-autorest SDK.
 	// configuration is not supported with the legacy go-autorest SDK.
 	// +optional
 	// +optional

+ 4 - 1
config/crds/bases/external-secrets.io_clustersecretstores.yaml

@@ -811,8 +811,11 @@ spec:
                         type: string
                         type: string
                       customCloudConfig:
                       customCloudConfig:
                         description: |-
                         description: |-
-                          CustomCloudConfig defines custom Azure Stack Hub or Azure Stack Edge endpoints.
+                          CustomCloudConfig defines custom Azure endpoints for non-standard clouds.
                           Required when EnvironmentType is AzureStackCloud.
                           Required when EnvironmentType is AzureStackCloud.
+                          Optional for other environment types - useful for Azure China when using Workload Identity
+                          with AKS, where the OIDC issuer (login.partner.microsoftonline.cn) differs from the
+                          standard China Cloud endpoint (login.chinacloudapi.cn).
                           IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
                           IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
                           configuration is not supported with the legacy go-autorest SDK.
                           configuration is not supported with the legacy go-autorest SDK.
                         properties:
                         properties:

+ 4 - 1
config/crds/bases/external-secrets.io_secretstores.yaml

@@ -811,8 +811,11 @@ spec:
                         type: string
                         type: string
                       customCloudConfig:
                       customCloudConfig:
                         description: |-
                         description: |-
-                          CustomCloudConfig defines custom Azure Stack Hub or Azure Stack Edge endpoints.
+                          CustomCloudConfig defines custom Azure endpoints for non-standard clouds.
                           Required when EnvironmentType is AzureStackCloud.
                           Required when EnvironmentType is AzureStackCloud.
+                          Optional for other environment types - useful for Azure China when using Workload Identity
+                          with AKS, where the OIDC issuer (login.partner.microsoftonline.cn) differs from the
+                          standard China Cloud endpoint (login.chinacloudapi.cn).
                           IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
                           IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
                           configuration is not supported with the legacy go-autorest SDK.
                           configuration is not supported with the legacy go-autorest SDK.
                         properties:
                         properties:

+ 8 - 2
deploy/crds/bundle.yaml

@@ -2862,8 +2862,11 @@ spec:
                           type: string
                           type: string
                         customCloudConfig:
                         customCloudConfig:
                           description: |-
                           description: |-
-                            CustomCloudConfig defines custom Azure Stack Hub or Azure Stack Edge endpoints.
+                            CustomCloudConfig defines custom Azure endpoints for non-standard clouds.
                             Required when EnvironmentType is AzureStackCloud.
                             Required when EnvironmentType is AzureStackCloud.
+                            Optional for other environment types - useful for Azure China when using Workload Identity
+                            with AKS, where the OIDC issuer (login.partner.microsoftonline.cn) differs from the
+                            standard China Cloud endpoint (login.chinacloudapi.cn).
                             IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
                             IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
                             configuration is not supported with the legacy go-autorest SDK.
                             configuration is not supported with the legacy go-autorest SDK.
                           properties:
                           properties:
@@ -14483,8 +14486,11 @@ spec:
                           type: string
                           type: string
                         customCloudConfig:
                         customCloudConfig:
                           description: |-
                           description: |-
-                            CustomCloudConfig defines custom Azure Stack Hub or Azure Stack Edge endpoints.
+                            CustomCloudConfig defines custom Azure endpoints for non-standard clouds.
                             Required when EnvironmentType is AzureStackCloud.
                             Required when EnvironmentType is AzureStackCloud.
+                            Optional for other environment types - useful for Azure China when using Workload Identity
+                            with AKS, where the OIDC issuer (login.partner.microsoftonline.cn) differs from the
+                            standard China Cloud endpoint (login.chinacloudapi.cn).
                             IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
                             IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
                             configuration is not supported with the legacy go-autorest SDK.
                             configuration is not supported with the legacy go-autorest SDK.
                           properties:
                           properties:

+ 4 - 1
docs/api/spec.md

@@ -1275,8 +1275,11 @@ AzureCustomCloudConfig
 </td>
 </td>
 <td>
 <td>
 <em>(Optional)</em>
 <em>(Optional)</em>
-<p>CustomCloudConfig defines custom Azure Stack Hub or Azure Stack Edge endpoints.
+<p>CustomCloudConfig defines custom Azure endpoints for non-standard clouds.
 Required when EnvironmentType is AzureStackCloud.
 Required when EnvironmentType is AzureStackCloud.
+Optional for other environment types - useful for Azure China when using Workload Identity
+with AKS, where the OIDC issuer (login.partner.microsoftonline.cn) differs from the
+standard China Cloud endpoint (login.chinacloudapi.cn).
 IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
 IMPORTANT: This feature REQUIRES UseAzureSDK to be set to true. Custom cloud
 configuration is not supported with the legacy go-autorest SDK.</p>
 configuration is not supported with the legacy go-autorest SDK.</p>
 </td>
 </td>

+ 37 - 5
docs/provider/azure-key-vault.md

@@ -13,7 +13,7 @@ Since the [AAD Pod Identity](https://azure.github.io/aad-pod-identity/docs/) is
 
 
 We support connecting to different cloud flavours azure supports: `PublicCloud`, `USGovernmentCloud`, `ChinaCloud`, `GermanCloud` and `AzureStackCloud` (for Azure Stack Hub/Edge). You have to specify the `environmentType` and point to the correct cloud flavour. This defaults to `PublicCloud`.
 We support connecting to different cloud flavours azure supports: `PublicCloud`, `USGovernmentCloud`, `ChinaCloud`, `GermanCloud` and `AzureStackCloud` (for Azure Stack Hub/Edge). You have to specify the `environmentType` and point to the correct cloud flavour. This defaults to `PublicCloud`.
 
 
-For Azure Stack Hub or Azure Stack Edge environments, you must also provide custom cloud configuration. See the [Azure Stack Configuration](#azure-stack-configuration) section below.
+For environments with non-standard endpoints (Azure Stack, Azure China with AKS Workload Identity, etc.), you can provide custom cloud configuration to override the default endpoints. See the [Custom Cloud Configuration](#custom-cloud-configuration) section below.
 
 
 ```yaml
 ```yaml
 apiVersion: external-secrets.io/v1
 apiVersion: external-secrets.io/v1
@@ -100,9 +100,40 @@ The following example demonstrates using the secretRef field to directly deliver
 {% include 'azkv-workload-identity-secretref.yaml' %}
 {% include 'azkv-workload-identity-secretref.yaml' %}
 ```
 ```
 
 
-### Azure Stack Configuration
+### Custom Cloud Configuration
 
 
-External Secrets Operator supports Azure Stack Hub and Azure Stack Edge through custom cloud configuration. This feature requires using the new Azure SDK.
+External Secrets Operator supports custom cloud endpoints for Azure Stack Hub, Azure Stack Edge, and other scenarios where the default cloud endpoints don't match your environment. This feature requires using the new Azure SDK.
+
+#### Azure China Workload Identity
+
+Azure China's AKS uses a different OIDC issuer (`login.partner.microsoftonline.cn`) than the standard China Cloud endpoint (`login.chinacloudapi.cn`). When using Workload Identity with AKS in Azure China, you need to override the Active Directory endpoint:
+
+```yaml
+apiVersion: external-secrets.io/v1
+kind: ClusterSecretStore
+metadata:
+  name: azure-china-workload-identity
+spec:
+  provider:
+    azurekv:
+      vaultUrl: "https://my-vault.vault.azure.cn"
+      environmentType: ChinaCloud
+      authType: WorkloadIdentity
+      # REQUIRED: Must be true to use custom cloud configuration
+      useAzureSDK: true
+      # Override the Active Directory endpoint to match AKS OIDC issuer
+      customCloudConfig:
+        activeDirectoryEndpoint: "https://login.partner.microsoftonline.cn/"
+        keyVaultEndpoint: "https://vault.azure.cn/"
+        resourceManagerEndpoint: "https://management.chinacloudapi.cn/"
+      serviceAccountRef:
+        name: my-service-account
+        namespace: default
+```
+
+#### Azure Stack Configuration
+
+For Azure Stack Hub or Azure Stack Edge environments:
 
 
 ```yaml
 ```yaml
 apiVersion: external-secrets.io/v1beta1
 apiVersion: external-secrets.io/v1beta1
@@ -138,8 +169,9 @@ spec:
 ```
 ```
 
 
 **Important Notes:**
 **Important Notes:**
-- `useAzureSDK: true` is mandatory for Azure Stack environments
-- The `customCloudConfig` is only valid when `environmentType: AzureStackCloud`
+- `useAzureSDK: true` is mandatory when using `customCloudConfig`
+- `customCloudConfig` can be used with any `environmentType` (PublicCloud, ChinaCloud, etc.)
+- For AzureStackCloud, `customCloudConfig` is required
 - Contact your Azure Stack administrator for the correct endpoint URLs
 - Contact your Azure Stack administrator for the correct endpoint URLs
 
 
 ### Update secret store
 ### Update secret store

+ 1 - 0
generators/v1/acr/go.mod

@@ -38,6 +38,7 @@ require (
 	github.com/Masterminds/goutils v1.1.1 // indirect
 	github.com/Masterminds/goutils v1.1.1 // indirect
 	github.com/Masterminds/semver/v3 v3.4.0 // indirect
 	github.com/Masterminds/semver/v3 v3.4.0 // indirect
 	github.com/Masterminds/sprig/v3 v3.3.0 // indirect
 	github.com/Masterminds/sprig/v3 v3.3.0 // indirect
+	github.com/aws/smithy-go v1.23.1 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect

+ 1 - 0
providers/v1/azure/go.mod

@@ -14,6 +14,7 @@ require (
 	github.com/Azure/go-autorest/autorest/azure/auth v0.5.13
 	github.com/Azure/go-autorest/autorest/azure/auth v0.5.13
 	github.com/Azure/go-autorest/autorest/date v0.3.1
 	github.com/Azure/go-autorest/autorest/date v0.3.1
 	github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0
 	github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0
+	github.com/aws/smithy-go v1.23.1
 	github.com/external-secrets/external-secrets/apis v0.0.0
 	github.com/external-secrets/external-secrets/apis v0.0.0
 	github.com/external-secrets/external-secrets/runtime v0.0.0
 	github.com/external-secrets/external-secrets/runtime v0.0.0
 	github.com/lestrrat-go/jwx/v2 v2.1.6
 	github.com/lestrrat-go/jwx/v2 v2.1.6

+ 8 - 13
providers/v1/azure/keyvault/keyvault.go

@@ -44,6 +44,7 @@ import (
 	kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
 	kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
 	"github.com/Azure/go-autorest/autorest/date"
 	"github.com/Azure/go-autorest/autorest/date"
 	"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
 	"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
+	"github.com/aws/smithy-go/ptr"
 	"github.com/lestrrat-go/jwx/v2/jwk"
 	"github.com/lestrrat-go/jwx/v2/jwk"
 	"github.com/tidwall/gjson"
 	"github.com/tidwall/gjson"
 	"golang.org/x/crypto/sha3"
 	"golang.org/x/crypto/sha3"
@@ -329,23 +330,17 @@ func (a *Azure) ValidateStore(store esv1.GenericStore) (admission.Warnings, erro
 		}
 		}
 	}
 	}
 
 
-	// Validate Azure Stack Cloud configuration
-	if p.EnvironmentType == esv1.AzureEnvironmentAzureStackCloud {
-		// Azure Stack requires custom cloud config
-		if p.CustomCloudConfig == nil {
-			return nil, errors.New("CustomCloudConfig is required when EnvironmentType is AzureStackCloud")
+	if p.CustomCloudConfig != nil {
+		if !ptr.ToBool(p.UseAzureSDK) {
+			return nil, errors.New("CustomCloudConfig requires UseAzureSDK to be set to true - the legacy SDK does not support custom clouds")
 		}
 		}
-		// Azure Stack requires new SDK
-		if p.UseAzureSDK == nil || !*p.UseAzureSDK {
-			return nil, errors.New("AzureStackCloud environment requires UseAzureSDK to be set to true - the legacy SDK does not support custom clouds")
-		}
-		// Validate required fields
 		if p.CustomCloudConfig.ActiveDirectoryEndpoint == "" {
 		if p.CustomCloudConfig.ActiveDirectoryEndpoint == "" {
 			return nil, errors.New("activeDirectoryEndpoint is required in CustomCloudConfig")
 			return nil, errors.New("activeDirectoryEndpoint is required in CustomCloudConfig")
 		}
 		}
-	} else if p.CustomCloudConfig != nil {
-		// CustomCloudConfig should only be used with AzureStackCloud
-		return nil, errors.New("CustomCloudConfig should only be specified when EnvironmentType is AzureStackCloud")
+	}
+
+	if p.EnvironmentType == esv1.AzureEnvironmentAzureStackCloud && p.CustomCloudConfig == nil {
+		return nil, errors.New("CustomCloudConfig is required when EnvironmentType is AzureStackCloud")
 	}
 	}
 
 
 	return nil, nil
 	return nil, nil

+ 22 - 9
providers/v1/azure/keyvault/keyvault_dual_sdk_test.go

@@ -335,7 +335,7 @@ func TestAzureStackCloudConfiguration(t *testing.T) {
 				ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
 				ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
 			},
 			},
 			expectError:   true,
 			expectError:   true,
-			expectedError: "AzureStackCloud environment requires UseAzureSDK to be set to true - the legacy SDK does not support custom clouds",
+			expectedError: "CustomCloudConfig requires UseAzureSDK to be set to true - the legacy SDK does not support custom clouds",
 			description:   "Azure Stack with legacy SDK should fail",
 			description:   "Azure Stack with legacy SDK should fail",
 		},
 		},
 		{
 		{
@@ -346,7 +346,7 @@ func TestAzureStackCloudConfiguration(t *testing.T) {
 				ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
 				ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
 			},
 			},
 			expectError:   true,
 			expectError:   true,
-			expectedError: "AzureStackCloud environment requires UseAzureSDK to be set to true - the legacy SDK does not support custom clouds",
+			expectedError: "CustomCloudConfig requires UseAzureSDK to be set to true - the legacy SDK does not support custom clouds",
 			description:   "Azure Stack without explicit new SDK flag should fail",
 			description:   "Azure Stack without explicit new SDK flag should fail",
 		},
 		},
 		{
 		{
@@ -361,15 +361,14 @@ func TestAzureStackCloudConfiguration(t *testing.T) {
 			description:   "Azure Stack without AAD endpoint should fail",
 			description:   "Azure Stack without AAD endpoint should fail",
 		},
 		},
 		{
 		{
-			name:        "custom_config_without_azure_stack",
+			name:        "custom_config_with_china_cloud",
 			useAzureSDK: ptr.To(true),
 			useAzureSDK: ptr.To(true),
-			envType:     esv1.AzureEnvironmentPublicCloud,
+			envType:     esv1.AzureEnvironmentChinaCloud,
 			customConfig: &esv1.AzureCustomCloudConfig{
 			customConfig: &esv1.AzureCustomCloudConfig{
-				ActiveDirectoryEndpoint: "https://login.microsoftonline.com/",
+				ActiveDirectoryEndpoint: "https://login.partner.microsoftonline.cn/",
 			},
 			},
-			expectError:   true,
-			expectedError: "CustomCloudConfig should only be specified when EnvironmentType is AzureStackCloud",
-			description:   "Custom config with non-AzureStack environment should fail",
+			expectError: false,
+			description: "Custom config with ChinaCloud environment should be allowed for AKS Workload Identity",
 		},
 		},
 		{
 		{
 			name:         "public_cloud_without_custom_config",
 			name:         "public_cloud_without_custom_config",
@@ -464,6 +463,20 @@ func TestGetCloudConfiguration(t *testing.T) {
 			description: "China cloud should return valid configuration",
 			description: "China cloud should return valid configuration",
 		},
 		},
 		{
 		{
+			name: "china_cloud_with_custom_endpoint",
+			provider: &esv1.AzureKVProvider{
+				EnvironmentType: esv1.AzureEnvironmentChinaCloud,
+				UseAzureSDK:     ptr.To(true),
+				CustomCloudConfig: &esv1.AzureCustomCloudConfig{
+					ActiveDirectoryEndpoint: "https://login.partner.microsoftonline.cn/",
+					KeyVaultEndpoint:        ptr.To("https://vault.azure.cn/"),
+					ResourceManagerEndpoint: ptr.To("https://management.chinacloudapi.cn/"),
+				},
+			},
+			expectError: false,
+			description: "China cloud with custom AAD endpoint for AKS Workload Identity should work",
+		},
+		{
 			name: "azure_stack_with_config",
 			name: "azure_stack_with_config",
 			provider: &esv1.AzureKVProvider{
 			provider: &esv1.AzureKVProvider{
 				EnvironmentType: esv1.AzureEnvironmentAzureStackCloud,
 				EnvironmentType: esv1.AzureEnvironmentAzureStackCloud,
@@ -486,7 +499,7 @@ func TestGetCloudConfiguration(t *testing.T) {
 				},
 				},
 			},
 			},
 			expectError:   true,
 			expectError:   true,
-			expectedError: "AzureStackCloud environment requires UseAzureSDK to be set to true",
+			expectedError: "CustomCloudConfig requires UseAzureSDK to be set to true",
 			description:   "Azure Stack without new SDK should fail",
 			description:   "Azure Stack without new SDK should fail",
 		},
 		},
 		{
 		{

+ 38 - 13
providers/v1/azure/keyvault/keyvault_new_sdk.go

@@ -33,6 +33,7 @@ import (
 	"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates"
 	"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azcertificates"
 	"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys"
 	"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys"
 	"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets"
 	"github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets"
+	"github.com/aws/smithy-go/ptr"
 	"github.com/lestrrat-go/jwx/v2/jwk"
 	"github.com/lestrrat-go/jwx/v2/jwk"
 	corev1 "k8s.io/api/core/v1"
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/types"
@@ -330,6 +331,33 @@ func (a *Azure) getSecretTagsWithNewSDK(ctx context.Context, ref esv1.ExternalSe
 
 
 // getCloudConfiguration returns the appropriate cloud configuration for the environment type.
 // getCloudConfiguration returns the appropriate cloud configuration for the environment type.
 func getCloudConfiguration(provider *esv1.AzureKVProvider) (cloud.Configuration, error) {
 func getCloudConfiguration(provider *esv1.AzureKVProvider) (cloud.Configuration, error) {
+	if provider.CustomCloudConfig != nil {
+		if !ptr.ToBool(provider.UseAzureSDK) {
+			return cloud.Configuration{}, errors.New("CustomCloudConfig requires UseAzureSDK to be set to true")
+		}
+
+		var baseConfig cloud.Configuration
+		switch provider.EnvironmentType {
+		case esv1.AzureEnvironmentGermanCloud:
+			return cloud.Configuration{}, errors.New("Azure Germany (Microsoft Cloud Deutschland) was discontinued on October 29, 2021. Please use AzureStackCloud with custom configuration or migrate to public cloud regions")
+		case esv1.AzureEnvironmentPublicCloud:
+			baseConfig = cloud.AzurePublic
+		case esv1.AzureEnvironmentUSGovernmentCloud:
+			baseConfig = cloud.AzureGovernment
+		case esv1.AzureEnvironmentChinaCloud:
+			baseConfig = cloud.AzureChina
+		case esv1.AzureEnvironmentAzureStackCloud:
+			baseConfig = cloud.Configuration{
+				Services: map[cloud.ServiceName]cloud.ServiceConfiguration{},
+			}
+		default:
+			baseConfig = cloud.AzurePublic
+		}
+
+		return buildCustomCloudConfiguration(provider.CustomCloudConfig, baseConfig)
+	}
+
+	// no custom config - use standard cloud configurations
 	switch provider.EnvironmentType {
 	switch provider.EnvironmentType {
 	case esv1.AzureEnvironmentPublicCloud:
 	case esv1.AzureEnvironmentPublicCloud:
 		return cloud.AzurePublic, nil
 		return cloud.AzurePublic, nil
@@ -340,27 +368,24 @@ func getCloudConfiguration(provider *esv1.AzureKVProvider) (cloud.Configuration,
 	case esv1.AzureEnvironmentGermanCloud:
 	case esv1.AzureEnvironmentGermanCloud:
 		return cloud.Configuration{}, errors.New("Azure Germany (Microsoft Cloud Deutschland) was discontinued on October 29, 2021. Please use AzureStackCloud with custom configuration or migrate to public cloud regions")
 		return cloud.Configuration{}, errors.New("Azure Germany (Microsoft Cloud Deutschland) was discontinued on October 29, 2021. Please use AzureStackCloud with custom configuration or migrate to public cloud regions")
 	case esv1.AzureEnvironmentAzureStackCloud:
 	case esv1.AzureEnvironmentAzureStackCloud:
-		// Azure Stack requires custom configuration
-		if provider.CustomCloudConfig == nil {
-			return cloud.Configuration{}, errors.New("CustomCloudConfig is required when EnvironmentType is AzureStackCloud")
-		}
-		// Validate that new SDK is enabled
-		if provider.UseAzureSDK == nil || !*provider.UseAzureSDK {
-			return cloud.Configuration{}, errors.New("AzureStackCloud environment requires UseAzureSDK to be set to true")
-		}
-		return buildCustomCloudConfiguration(provider.CustomCloudConfig)
+		return cloud.Configuration{}, errors.New("CustomCloudConfig is required when EnvironmentType is AzureStackCloud")
 	default:
 	default:
 		return cloud.AzurePublic, nil
 		return cloud.AzurePublic, nil
 	}
 	}
 }
 }
 
 
-// buildCustomCloudConfiguration creates a custom cloud.Configuration for Azure Stack.
-func buildCustomCloudConfiguration(config *esv1.AzureCustomCloudConfig) (cloud.Configuration, error) {
+// buildCustomCloudConfiguration creates a custom cloud.Configuration by merging custom config with base config.
+func buildCustomCloudConfiguration(config *esv1.AzureCustomCloudConfig, baseConfig cloud.Configuration) (cloud.Configuration, error) {
 	cloudConfig := cloud.Configuration{
 	cloudConfig := cloud.Configuration{
-		Services: map[cloud.ServiceName]cloud.ServiceConfiguration{},
+		ActiveDirectoryAuthorityHost: baseConfig.ActiveDirectoryAuthorityHost,
+		Services:                     map[cloud.ServiceName]cloud.ServiceConfiguration{},
+	}
+
+	for k, v := range baseConfig.Services {
+		cloudConfig.Services[k] = v
 	}
 	}
 
 
-	// Set Active Directory endpoint (required)
+	// Set Active Directory endpoint with custom value (required)
 	cloudConfig.ActiveDirectoryAuthorityHost = config.ActiveDirectoryEndpoint
 	cloudConfig.ActiveDirectoryAuthorityHost = config.ActiveDirectoryEndpoint
 
 
 	// Set Resource Manager endpoint if provided
 	// Set Resource Manager endpoint if provided