Skip to content

Commit 7dc9e70

Browse files
authored
azurerm_kubernetes_cluster - add support for bootstrap_profile (hashicorp#30532)
[ENHANCEMENT] * `azurerm_kubernetes_cluster` - add support for `bootstrap_profile`
1 parent 34342fc commit 7dc9e70

3 files changed

Lines changed: 394 additions & 3 deletions

File tree

internal/services/containers/kubernetes_cluster_resource.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/hashicorp/go-azure-helpers/resourcemanager/identity"
2121
"github.com/hashicorp/go-azure-helpers/resourcemanager/location"
2222
"github.com/hashicorp/go-azure-helpers/resourcemanager/tags"
23+
"github.com/hashicorp/go-azure-sdk/resource-manager/containerregistry/2023-11-01-preview/registries"
2324
"github.com/hashicorp/go-azure-sdk/resource-manager/containerservice/2025-05-01/agentpools"
2425
"github.com/hashicorp/go-azure-sdk/resource-manager/containerservice/2025-05-01/maintenanceconfigurations"
2526
"github.com/hashicorp/go-azure-sdk/resource-manager/containerservice/2025-05-01/managedclusters"
@@ -113,6 +114,16 @@ func resourceKubernetesCluster() *pluginsdk.Resource {
113114
// Once it is GA, an additional logic is needed to handle the uninstallation of network policy.
114115
return old.(string) != ""
115116
}),
117+
func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error {
118+
outboundType := d.Get("network_profile.0.outbound_type").(string)
119+
artifactSource := d.Get("bootstrap_profile.0.artifact_source").(string)
120+
121+
if outboundType == string(managedclusters.OutboundTypeNone) && artifactSource != string(managedclusters.ArtifactSourceCache) {
122+
return fmt.Errorf("when `network_profile.0.outbound_type` is set to `none`, `bootstrap_profile.0.artifact_source` must be set to `Cache`")
123+
}
124+
125+
return nil
126+
},
116127
),
117128

118129
Timeouts: &pluginsdk.ResourceTimeout{
@@ -676,6 +687,30 @@ func resourceKubernetesCluster() *pluginsdk.Resource {
676687
},
677688
},
678689

690+
"bootstrap_profile": {
691+
Type: pluginsdk.TypeList,
692+
Optional: true,
693+
// Note: O+C because the API returns a default value for `bootstrapProfile` when it is omitted in the API request
694+
Computed: true,
695+
MaxItems: 1,
696+
Elem: &pluginsdk.Resource{
697+
Schema: map[string]*pluginsdk.Schema{
698+
"artifact_source": {
699+
Type: pluginsdk.TypeString,
700+
Optional: true,
701+
ValidateFunc: validation.StringInSlice(managedclusters.PossibleValuesForArtifactSource(), false),
702+
Default: managedclusters.ArtifactSourceDirect,
703+
},
704+
705+
"container_registry_id": {
706+
Type: pluginsdk.TypeString,
707+
Optional: true,
708+
ValidateFunc: registries.ValidateRegistryID,
709+
},
710+
},
711+
},
712+
},
713+
679714
"local_account_disabled": {
680715
Type: pluginsdk.TypeBool,
681716
Optional: true,
@@ -1115,6 +1150,7 @@ func resourceKubernetesCluster() *pluginsdk.Resource {
11151150
string(managedclusters.OutboundTypeUserDefinedRouting),
11161151
string(managedclusters.OutboundTypeManagedNATGateway),
11171152
string(managedclusters.OutboundTypeUserAssignedNATGateway),
1153+
string(managedclusters.OutboundTypeNone),
11181154
}, false),
11191155
},
11201156

@@ -1700,6 +1736,9 @@ func resourceKubernetesClusterCreate(d *pluginsdk.ResourceData, meta interface{}
17001736
azureMonitorKubernetesMetricsRaw := d.Get("monitor_metrics").([]interface{})
17011737
azureMonitorProfile := expandKubernetesClusterAzureMonitorProfile(azureMonitorKubernetesMetricsRaw)
17021738

1739+
bootstrapProfileRaw := d.Get("bootstrap_profile").([]interface{})
1740+
bootstrapProfile := expandBootstrapProfile(bootstrapProfileRaw)
1741+
17031742
httpProxyConfigRaw := d.Get("http_proxy_config").([]interface{})
17041743
httpProxyConfig := expandKubernetesClusterHttpProxyConfig(httpProxyConfigRaw)
17051744

@@ -1792,6 +1831,7 @@ func resourceKubernetesClusterCreate(d *pluginsdk.ResourceData, meta interface{}
17921831
DnsPrefix: pointer.To(dnsPrefix),
17931832
EnableRBAC: pointer.To(d.Get("role_based_access_control_enabled").(bool)),
17941833
KubernetesVersion: pointer.To(kubernetesVersion),
1834+
BootstrapProfile: bootstrapProfile,
17951835
LinuxProfile: linuxProfile,
17961836
WindowsProfile: windowsProfile,
17971837
MetricsProfile: metricsProfile,
@@ -2370,6 +2410,21 @@ func resourceKubernetesClusterUpdate(d *pluginsdk.ResourceData, meta interface{}
23702410
}
23712411
}
23722412

2413+
if d.HasChange("bootstrap_profile") {
2414+
bootstrapProfileRaw := d.Get("bootstrap_profile").([]interface{})
2415+
profile := expandBootstrapProfile(bootstrapProfileRaw)
2416+
2417+
// If profile is removed in the config, we should set ArtifactSource to Direct as it's the default value in the service side.
2418+
if profile == nil {
2419+
profile = &managedclusters.ManagedClusterBootstrapProfile{
2420+
ArtifactSource: pointer.To(managedclusters.ArtifactSourceDirect),
2421+
}
2422+
}
2423+
2424+
updateCluster = true
2425+
existing.Model.Properties.BootstrapProfile = profile
2426+
}
2427+
23732428
if d.HasChange("upgrade_override") {
23742429
upgradeOverrideSettingRaw := d.Get("upgrade_override").([]interface{})
23752430

@@ -2920,6 +2975,14 @@ func resourceKubernetesClusterRead(d *pluginsdk.ResourceData, meta interface{})
29202975
}
29212976

29222977
d.Set("support_plan", pointer.From(props.SupportPlan))
2978+
2979+
bootstrapProfile, err := flattenBootstrapProfile(props.BootstrapProfile)
2980+
if err != nil {
2981+
return fmt.Errorf("flattening `bootstrap_profile`: %+v", err)
2982+
}
2983+
if err := d.Set("bootstrap_profile", bootstrapProfile); err != nil {
2984+
return fmt.Errorf("setting `bootstrap_profile`: %+v", err)
2985+
}
29232986
}
29242987

29252988
identity, err := identity.FlattenSystemOrUserAssignedMap(model.Identity)
@@ -3171,6 +3234,44 @@ func flattenKubernetesClusterAPIAccessProfile(profile *managedclusters.ManagedCl
31713234
}
31723235
}
31733236

3237+
func expandBootstrapProfile(rawBootstrapProfile []interface{}) *managedclusters.ManagedClusterBootstrapProfile {
3238+
if len(rawBootstrapProfile) == 0 || rawBootstrapProfile[0] == nil {
3239+
return nil
3240+
}
3241+
3242+
config := rawBootstrapProfile[0].(map[string]interface{})
3243+
var containerRegistryID *string
3244+
if v, exists := config["container_registry_id"]; exists && v != "" {
3245+
containerRegistryID = pointer.To(v.(string))
3246+
}
3247+
3248+
return &managedclusters.ManagedClusterBootstrapProfile{
3249+
ArtifactSource: pointer.ToEnum[managedclusters.ArtifactSource](config["artifact_source"].(string)),
3250+
ContainerRegistryId: containerRegistryID,
3251+
}
3252+
}
3253+
3254+
func flattenBootstrapProfile(profile *managedclusters.ManagedClusterBootstrapProfile) ([]interface{}, error) {
3255+
if profile == nil || profile.ArtifactSource == nil {
3256+
return []interface{}{}, nil
3257+
}
3258+
3259+
var containerRegistryID string
3260+
if profile.ContainerRegistryId != nil {
3261+
id, err := registries.ParseRegistryID(*profile.ContainerRegistryId)
3262+
if err != nil {
3263+
return nil, err
3264+
}
3265+
containerRegistryID = id.ID()
3266+
}
3267+
return []interface{}{
3268+
map[string]interface{}{
3269+
"artifact_source": profile.ArtifactSource,
3270+
"container_registry_id": containerRegistryID,
3271+
},
3272+
}, nil
3273+
}
3274+
31743275
func expandKubernetesClusterWorkloadAutoscalerProfile(input []interface{}, d *pluginsdk.ResourceData) *managedclusters.ManagedClusterWorkloadAutoScalerProfile {
31753276
if len(input) == 0 {
31763277
return nil

0 commit comments

Comments
 (0)