Skip to content

Commit aea201c

Browse files
committed
fix: Allow EKS Auto Mode settings to be enabled, disabled,and removed
1 parent 6649522 commit aea201c

File tree

3 files changed

+136
-70
lines changed

3 files changed

+136
-70
lines changed

.changelog/40582.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
resource/aws_eks_cluster: Allow EKS Auto Mode settings (`compute_config` / `kubernetes_network_config.elastic_load_balancing` / `storage_config.block_storage` ) to be enabled, disabled, and removed from the configuration
3+
```

internal/service/eks/cluster.go

Lines changed: 85 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -54,46 +54,11 @@ func resourceCluster() *schema.Resource {
5454

5555
CustomizeDiff: customdiff.Sequence(
5656
validateAutoModeCustomizeDiff,
57+
validateAutoModeComputeConfigCustomizeDiff,
5758
customdiff.ForceNewIfChange("encryption_config", func(_ context.Context, old, new, meta any) bool {
5859
// You cannot disable envelope encryption after enabling it. This action is irreversible.
5960
return len(old.([]any)) == 1 && len(new.([]any)) == 0
6061
}),
61-
func(ctx context.Context, rd *schema.ResourceDiff, meta any) error {
62-
if rd.Id() == "" {
63-
return nil
64-
}
65-
oldValue, newValue := rd.GetChange("compute_config")
66-
67-
oldComputeConfig := expandComputeConfigRequest(oldValue.([]any))
68-
newComputeConfig := expandComputeConfigRequest(newValue.([]any))
69-
70-
if newComputeConfig == nil || oldComputeConfig == nil {
71-
return nil
72-
}
73-
74-
oldRoleARN := aws.ToString(oldComputeConfig.NodeRoleArn)
75-
newRoleARN := aws.ToString(newComputeConfig.NodeRoleArn)
76-
77-
newComputeConfigEnabled := aws.ToBool(newComputeConfig.Enabled)
78-
79-
// Do not force new if auto mode is disabled in new config and role ARN is unset
80-
if !newComputeConfigEnabled && newRoleARN == "" {
81-
return nil
82-
}
83-
84-
// Do not force new if built-in node pools are zeroed in new config and role ARN is unset
85-
if len(newComputeConfig.NodePools) == 0 && newRoleARN == "" {
86-
return nil
87-
}
88-
89-
// only force new if an existing role has changed, not if a new role is added
90-
if oldRoleARN != "" && oldRoleARN != newRoleARN {
91-
if err := rd.ForceNew("compute_config.0.node_role_arn"); err != nil {
92-
return err
93-
}
94-
}
95-
return nil
96-
},
9762
),
9863

9964
Timeouts: &schema.ResourceTimeout{
@@ -153,12 +118,14 @@ func resourceCluster() *schema.Resource {
153118
"compute_config": {
154119
Type: schema.TypeList,
155120
Optional: true,
121+
Computed: true,
156122
MaxItems: 1,
157123
Elem: &schema.Resource{
158124
Schema: map[string]*schema.Schema{
159125
names.AttrEnabled: {
160126
Type: schema.TypeBool,
161127
Optional: true,
128+
Default: false,
162129
},
163130
"node_pools": {
164131
Type: schema.TypeSet,
@@ -411,6 +378,7 @@ func resourceCluster() *schema.Resource {
411378
"storage_config": {
412379
Type: schema.TypeList,
413380
Optional: true,
381+
Computed: true,
414382
MaxItems: 1,
415383
Elem: &schema.Resource{
416384
Schema: map[string]*schema.Schema{
@@ -423,6 +391,7 @@ func resourceCluster() *schema.Resource {
423391
names.AttrEnabled: {
424392
Type: schema.TypeBool,
425393
Optional: true,
394+
Default: false,
426395
},
427396
},
428397
},
@@ -526,30 +495,25 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta any
526495
name := d.Get(names.AttrName).(string)
527496
input := eks.CreateClusterInput{
528497
BootstrapSelfManagedAddons: aws.Bool(d.Get("bootstrap_self_managed_addons").(bool)),
498+
ComputeConfig: expandComputeConfigRequest(d.Get("compute_config").([]any)),
529499
EncryptionConfig: expandEncryptionConfig(d.Get("encryption_config").([]any)),
500+
KubernetesNetworkConfig: expandKubernetesNetworkConfigRequest(d.Get("kubernetes_network_config").([]any)),
530501
Logging: expandLogging(d.Get("enabled_cluster_log_types").(*schema.Set)),
531502
Name: aws.String(name),
532503
ResourcesVpcConfig: expandVpcConfigRequest(d.Get(names.AttrVPCConfig).([]any)),
533504
RoleArn: aws.String(d.Get(names.AttrRoleARN).(string)),
505+
StorageConfig: expandStorageConfigRequest(d.Get("storage_config").([]any)),
534506
Tags: getTagsIn(ctx),
535507
}
536508

537509
if v, ok := d.GetOk("access_config"); ok {
538510
input.AccessConfig = expandCreateAccessConfigRequest(v.([]any))
539511
}
540512

541-
if v, ok := d.GetOk("compute_config"); ok {
542-
input.ComputeConfig = expandComputeConfigRequest(v.([]any))
543-
}
544-
545513
if v, ok := d.GetOk(names.AttrDeletionProtection); ok {
546514
input.DeletionProtection = aws.Bool(v.(bool))
547515
}
548516

549-
if v, ok := d.GetOk("kubernetes_network_config"); ok {
550-
input.KubernetesNetworkConfig = expandKubernetesNetworkConfigRequest(v.([]any))
551-
}
552-
553517
if v, ok := d.GetOk("outpost_config"); ok {
554518
input.OutpostConfig = expandOutpostConfigRequest(v.([]any))
555519
}
@@ -558,10 +522,6 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta any
558522
input.RemoteNetworkConfig = expandCreateRemoteNetworkConfigRequest(v.([]any))
559523
}
560524

561-
if v, ok := d.GetOk("storage_config"); ok {
562-
input.StorageConfig = expandStorageConfigRequest(v.([]any))
563-
}
564-
565525
if v, ok := d.GetOk("upgrade_policy"); ok {
566526
input.UpgradePolicy = expandUpgradePolicy(v.([]any))
567527
}
@@ -755,10 +715,15 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta any
755715
}
756716
}
757717

718+
// All three fields are required to enable/disable Auto Mode or else you receive the error:
719+
// InvalidParameterException: For EKS Auto Mode, please ensure that all required configs,
720+
// including computeConfig, kubernetesNetworkConfig, and blockStorage are all either fully enabled or fully disabled.
721+
// In addition, when updating other Auto Mode arguments (i.e. - computeConfig.nodePools/nodeRoleARN), all 3 fields are required
758722
if d.HasChanges("compute_config", "kubernetes_network_config", "storage_config") {
759723
computeConfig := expandComputeConfigRequest(d.Get("compute_config").([]any))
760724
kubernetesNetworkConfig := expandKubernetesNetworkConfigRequest(d.Get("kubernetes_network_config").([]any))
761725
storageConfig := expandStorageConfigRequest(d.Get("storage_config").([]any))
726+
762727
input := eks.UpdateClusterConfigInput{
763728
ComputeConfig: computeConfig,
764729
KubernetesNetworkConfig: kubernetesNetworkConfig,
@@ -769,13 +734,13 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta any
769734
output, err := conn.UpdateClusterConfig(ctx, &input)
770735

771736
if err != nil {
772-
return sdkdiag.AppendErrorf(diags, "updating EKS Cluster (%s) compute config: %s", d.Id(), err)
737+
return sdkdiag.AppendErrorf(diags, "updating EKS Cluster (%s) Auto Mode settings: %s", d.Id(), err)
773738
}
774739

775740
updateID := aws.ToString(output.Update.Id)
776741

777742
if _, err = waitClusterUpdateSuccessful(ctx, conn, d.Id(), updateID, d.Timeout(schema.TimeoutUpdate)); err != nil {
778-
return sdkdiag.AppendErrorf(diags, "waiting for EKS Cluster (%s) compute config update (%s): %s", d.Id(), updateID, err)
743+
return sdkdiag.AppendErrorf(diags, "waiting for EKS Cluster (%s) Auto Mode settings update (%s): %s", d.Id(), updateID, err)
779744
}
780745
}
781746

@@ -1145,7 +1110,7 @@ func waitClusterDeleted(ctx context.Context, conn *eks.Client, name string, time
11451110
return nil, err
11461111
}
11471112

1148-
func waitClusterUpdateSuccessful(ctx context.Context, conn *eks.Client, name, id string, timeout time.Duration) (*types.Update, error) { //nolint:unparam
1113+
func waitClusterUpdateSuccessful(ctx context.Context, conn *eks.Client, name, id string, timeout time.Duration) (*types.Update, error) {
11491114
stateConf := &retry.StateChangeConf{
11501115
Pending: enum.Slice(types.UpdateStatusInProgress),
11511116
Target: enum.Slice(types.UpdateStatusSuccessful),
@@ -1209,17 +1174,21 @@ func expandUpdateAccessConfigRequest(tfList []any) *types.UpdateAccessConfigRequ
12091174
}
12101175

12111176
func expandComputeConfigRequest(tfList []any) *types.ComputeConfigRequest {
1177+
apiObject := &types.ComputeConfigRequest{}
1178+
12121179
if len(tfList) == 0 {
1213-
return nil
1180+
// Ensure this is always present to avoid the error:
1181+
// InvalidParameterException: The type for cluster update was not provided.
1182+
// when the field is removed (nil)
1183+
apiObject.Enabled = aws.Bool(false)
1184+
return apiObject
12141185
}
12151186

12161187
tfMap, ok := tfList[0].(map[string]any)
12171188
if !ok {
12181189
return nil
12191190
}
12201191

1221-
apiObject := &types.ComputeConfigRequest{}
1222-
12231192
if v, ok := tfMap[names.AttrEnabled].(bool); ok {
12241193
apiObject.Enabled = aws.Bool(v)
12251194
}
@@ -1282,17 +1251,23 @@ func expandProvider(tfList []any) *types.Provider {
12821251
}
12831252

12841253
func expandStorageConfigRequest(tfList []any) *types.StorageConfigRequest {
1254+
apiObject := &types.StorageConfigRequest{}
1255+
12851256
if len(tfList) == 0 {
1286-
return nil
1257+
// Ensure this is always present to avoid the error:
1258+
// InvalidParameterException: The type for cluster update was not provided.
1259+
// when the field is removed (nil)
1260+
apiObject.BlockStorage = &types.BlockStorage{
1261+
Enabled: aws.Bool(false),
1262+
}
1263+
return apiObject
12871264
}
12881265

12891266
tfMap, ok := tfList[0].(map[string]any)
12901267
if !ok {
12911268
return nil
12921269
}
12931270

1294-
apiObject := &types.StorageConfigRequest{}
1295-
12961271
if v, ok := tfMap["block_storage"].([]any); ok {
12971272
apiObject.BlockStorage = expandBlockStorage(v)
12981273
}
@@ -1365,7 +1340,7 @@ func expandControlPlanePlacementRequest(tfList []any) *types.ControlPlanePlaceme
13651340
return apiObject
13661341
}
13671342

1368-
func expandVpcConfigRequest(tfList []any) *types.VpcConfigRequest { // nosemgrep:ci.caps5-in-func-name
1343+
func expandVpcConfigRequest(tfList []any) *types.VpcConfigRequest {
13691344
if len(tfList) == 0 {
13701345
return nil
13711346
}
@@ -1390,17 +1365,24 @@ func expandVpcConfigRequest(tfList []any) *types.VpcConfigRequest { // nosemgrep
13901365
}
13911366

13921367
func expandKubernetesNetworkConfigRequest(tfList []any) *types.KubernetesNetworkConfigRequest {
1368+
apiObject := &types.KubernetesNetworkConfigRequest{}
1369+
13931370
if len(tfList) == 0 {
1394-
return nil
1371+
// Required to avoid the error:
1372+
// InvalidParameterException: For EKS Auto Mode, please ensure that all required configs,
1373+
// including computeConfig, kubernetesNetworkConfig, and blockStorage are all either fully enabled or fully disabled.
1374+
// since the other two fields have been injected with `enabled: false` when the field is not present
1375+
apiObject.ElasticLoadBalancing = &types.ElasticLoadBalancing{
1376+
Enabled: aws.Bool(false),
1377+
}
1378+
return apiObject
13951379
}
13961380

13971381
tfMap, ok := tfList[0].(map[string]any)
13981382
if !ok {
13991383
return nil
14001384
}
14011385

1402-
apiObject := &types.KubernetesNetworkConfigRequest{}
1403-
14041386
if v, ok := tfMap["elastic_load_balancing"].([]any); ok {
14051387
apiObject.ElasticLoadBalancing = expandKubernetesNetworkConfigElasticLoadBalancing(v)
14061388
}
@@ -1479,6 +1461,7 @@ func expandUpdateRemoteNetworkConfigRequest(tfList []any) *types.RemoteNetworkCo
14791461

14801462
return apiObject
14811463
}
1464+
14821465
func expandRemoteNodeNetworks(tfList []any) []types.RemoteNodeNetwork {
14831466
var apiObjects = []types.RemoteNodeNetwork{}
14841467

@@ -1681,7 +1664,7 @@ func flattenProvider(apiObject *types.Provider) []any {
16811664
return []any{tfMap}
16821665
}
16831666

1684-
func flattenVPCConfigResponse(vpcConfig *types.VpcConfigResponse) []map[string]any { // nosemgrep:ci.caps5-in-func-name
1667+
func flattenVPCConfigResponse(vpcConfig *types.VpcConfigResponse) []map[string]any {
16851668
if vpcConfig == nil {
16861669
return []map[string]any{}
16871670
}
@@ -1885,3 +1868,43 @@ func validateAutoModeCustomizeDiff(_ context.Context, d *schema.ResourceDiff, _
18851868

18861869
return nil
18871870
}
1871+
1872+
// Allow setting `compute_config.node_role_arn` to `null` when disabling auto mode or
1873+
// built-in node pools without forcing re-creation of the cluster
1874+
func validateAutoModeComputeConfigCustomizeDiff(_ context.Context, diff *schema.ResourceDiff, _ any) error {
1875+
if diff.Id() == "" {
1876+
return nil
1877+
}
1878+
1879+
oldValue, newValue := diff.GetChange("compute_config")
1880+
1881+
oldComputeConfig := expandComputeConfigRequest(oldValue.([]any))
1882+
newComputeConfig := expandComputeConfigRequest(newValue.([]any))
1883+
1884+
if newComputeConfig == nil || oldComputeConfig == nil {
1885+
return nil
1886+
}
1887+
1888+
oldRoleARN := aws.ToString(oldComputeConfig.NodeRoleArn)
1889+
newRoleARN := aws.ToString(newComputeConfig.NodeRoleArn)
1890+
1891+
newComputeConfigEnabled := aws.ToBool(newComputeConfig.Enabled)
1892+
1893+
// Do not force new if auto mode is disabled in new config and role ARN is unset
1894+
if !newComputeConfigEnabled && newRoleARN == "" {
1895+
return nil
1896+
}
1897+
1898+
// Do not force new if built-in node pools are zeroed in new config and role ARN is unset
1899+
if len(newComputeConfig.NodePools) == 0 && newRoleARN == "" {
1900+
return nil
1901+
}
1902+
1903+
// only force new if an existing role has changed, not if a new role is added
1904+
if oldRoleARN != "" && oldRoleARN != newRoleARN {
1905+
if err := diff.ForceNew("compute_config.0.node_role_arn"); err != nil {
1906+
return err
1907+
}
1908+
}
1909+
return nil
1910+
}

0 commit comments

Comments
 (0)