From bc7d5bbef6754be0ccbb49919d575d8b2afad632 Mon Sep 17 00:00:00 2001 From: Sasi Date: Fri, 22 Dec 2023 01:55:11 -0500 Subject: [PATCH 01/38] EKS access entry and policy association changes --- internal/service/eks/access_entry.go | 205 ++++++++++++++ .../service/eks/access_entry_datasource.go | 104 ++++++++ .../service/eks/access_policy_association.go | 240 +++++++++++++++++ internal/service/eks/cluster.go | 125 ++++++++- internal/service/eks/cluster_data_source.go | 19 ++ internal/service/eks/cluster_test.go | 53 ++++ internal/service/eks/find.go | 252 ++++++++++++++++++ internal/service/eks/id.go | 38 +++ .../docs/d/eks_access_entry.html.markdown | 41 +++ website/docs/d/eks_cluster.html.markdown | 3 + website/docs/r/eks_access_entry.html.markdown | 70 +++++ ...ks_access_policy_association.html.markdown | 72 +++++ website/docs/r/eks_cluster.html.markdown | 34 ++- 13 files changed, 1242 insertions(+), 14 deletions(-) create mode 100644 internal/service/eks/access_entry.go create mode 100644 internal/service/eks/access_entry_datasource.go create mode 100644 internal/service/eks/access_policy_association.go create mode 100644 internal/service/eks/find.go create mode 100644 website/docs/d/eks_access_entry.html.markdown create mode 100644 website/docs/r/eks_access_entry.html.markdown create mode 100644 website/docs/r/eks_access_policy_association.html.markdown diff --git a/internal/service/eks/access_entry.go b/internal/service/eks/access_entry.go new file mode 100644 index 00000000000..25aeab6f1bd --- /dev/null +++ b/internal/service/eks/access_entry.go @@ -0,0 +1,205 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package eks + +import ( + "context" + "log" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/eks" + "github.com/aws/aws-sdk-go-v2/service/eks/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @SDKResource("aws_eks_access_entry", name="Access Entry") +// @Tags(identifierAttribute="arn") +func ResourceAccessEntry() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceAccessEntryCreate, + ReadWithoutTimeout: resourceAccessEntryRead, + UpdateWithoutTimeout: resourceAccessEntryUpdate, + DeleteWithoutTimeout: resourceAccessEntryDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + CustomizeDiff: verify.SetTagsDiff, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "access_entry_arn": { + Type: schema.TypeString, + Computed: true, + }, + "cluster_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validClusterName, + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + }, + "kubernetes_group": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "modified_at": { + Type: schema.TypeString, + Computed: true, + }, + "principal_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: verify.ValidARN, + }, + "user_name": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + names.AttrTags: tftags.TagsSchema(), + names.AttrTagsAll: tftags.TagsSchemaComputed(), + }, + } +} + +func resourceAccessEntryCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).EKSClient(ctx) + + clusterName := d.Get("cluster_name").(string) + principal_arn := d.Get("principal_arn").(string) + accessID := AccessEntryCreateResourceID(clusterName, principal_arn) + input := &eks.CreateAccessEntryInput{ + ClusterName: aws.String(clusterName), + PrincipalArn: aws.String(principal_arn), + KubernetesGroups: flex.ExpandStringValueSet(d.Get("kubernetes_groups").(*schema.Set)), + Tags: getTagsIn(ctx), + } + + _, err := conn.CreateAccessEntry(ctx, input) + if err != nil { + return sdkdiag.AppendErrorf(diags, "creating EKS Access Config: %s", err) + } + + d.SetId(accessID) + + return append(diags, resourceAccessEntryRead(ctx, d, meta)...) +} + +func resourceAccessEntryRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).EKSClient(ctx) + + clusterName, principal_arn, err := AccessEntryParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading EKS Access Entry (%s): %s", d.Id(), err) + } + output, err := FindAccessEntryByID(ctx, conn, clusterName, principal_arn) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EKS Access Entry (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading EKS EKS Access Entry (%s): %s", d.Id(), err) + } + + d.Set("access_entry_arn", output.AccessEntryArn) + d.Set("cluster_name", output.ClusterName) + d.Set("created_at", output.CreatedAt) + // if err := d.Set("kubernetes_groups", aws.StringValueSlice(output.KubernetesGroups)); err != nil { + // return sdkdiag.AppendErrorf(diags, "setting kubernetes_groups: %s", err) + // } + d.Set("kubernetes_groups", output.KubernetesGroups) + d.Set("modified_at", output.ModifiedAt) + d.Set("principal_arn", output.PrincipalArn) + d.Set("user_name", output.Username) + d.Set("type", output.Type) + + setTagsOut(ctx, output.Tags) + + return diags +} + +func resourceAccessEntryUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).EKSClient(ctx) + clusterName, principal_arn, err := AccessEntryParseResourceID(d.Id()) + + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + if d.HasChangesExcept("tags", "tags_all") { + input := &eks.UpdateAccessEntryInput{ + ClusterName: aws.String(clusterName), + PrincipalArn: aws.String(principal_arn), + } + + if d.HasChange("kubernetes_group") { + input.KubernetesGroups = flex.ExpandStringValueSet(d.Get("kubernetes_groups").(*schema.Set)) + } + + _, err := conn.UpdateAccessEntry(ctx, input) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating Access Entry (%s): %s", d.Id(), err) + } + } + + return append(diags, resourceFargateProfileRead(ctx, d, meta)...) +} + +func resourceAccessEntryDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).EKSClient(ctx) + + clusterName, principal_arn, err := AccessEntryParseResourceID(d.Id()) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting EKS Access Entry (%s): %s", d.Id(), err) + } + + log.Printf("[DEBUG] Deleting EKS Access Entry: %s", d.Id()) + _, err = conn.DeleteAccessEntry(ctx, &eks.DeleteAccessEntryInput{ + ClusterName: aws.String(clusterName), + PrincipalArn: aws.String(principal_arn), + }) + + if errs.IsAErrorMessageContains[*types.ResourceNotFoundException](err, "No Access Entry found for Id:") { + return nil + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting EKS Access Entry (%s): %s", d.Id(), err) + } + + return diags +} \ No newline at end of file diff --git a/internal/service/eks/access_entry_datasource.go b/internal/service/eks/access_entry_datasource.go new file mode 100644 index 00000000000..be940915c21 --- /dev/null +++ b/internal/service/eks/access_entry_datasource.go @@ -0,0 +1,104 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package eks + +import ( + "context" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @SDKDataSource("aws_eks_access_entry") +func DataSourceAccessEntry() *schema.Resource { + return &schema.Resource{ + ReadWithoutTimeout: dataSourceAccessEntryRead, + + Schema: map[string]*schema.Schema{ + "access_entry_arn": { + Type: schema.TypeString, + Computed: true, + }, + "cluster_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validClusterName, + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + }, + "kubernetes_group": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "modified_at": { + Type: schema.TypeString, + Computed: true, + }, + "principal_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: verify.ValidARN, + }, + "user_name": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + names.AttrTags: tftags.TagsSchema(), + names.AttrTagsAll: tftags.TagsSchemaComputed(), + }, + } +} + +func dataSourceAccessEntryRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).EKSClient(ctx) + + clusterName, principal_arn, err := AccessEntryParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading EKS Access Entry (%s): %s", d.Id(), err) + } + output, err := FindAccessEntryByID(ctx, conn, clusterName, principal_arn) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EKS Access Entry (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading EKS EKS Access Entry (%s): %s", d.Id(), err) + } + + d.Set("access_entry_arn", output.AccessEntryArn) + d.Set("cluster_name", output.ClusterName) + d.Set("created_at", output.CreatedAt) + // if err := d.Set("kubernetes_groups", aws.StringValueSlice(output.KubernetesGroups)); err != nil { + // return sdkdiag.AppendErrorf(diags, "setting kubernetes_groups: %s", err) + // } + d.Set("kubernetes_groups", output.KubernetesGroups) + d.Set("modified_at", output.ModifiedAt) + d.Set("principal_arn", output.PrincipalArn) + d.Set("user_name", output.Username) + d.Set("type", output.Type) + + setTagsOut(ctx, output.Tags) + + return diags +} \ No newline at end of file diff --git a/internal/service/eks/access_policy_association.go b/internal/service/eks/access_policy_association.go new file mode 100644 index 00000000000..116da627582 --- /dev/null +++ b/internal/service/eks/access_policy_association.go @@ -0,0 +1,240 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package eks + +import ( + "context" + "log" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/eks" + "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +// @SDKResource("aws_eks_access_policy_association", name="Access Policy Association") +func ResourceAccessPolicyAssociation() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceAccessPolicyAssociationCreate, + ReadWithoutTimeout: resourceAccessPolicyAssociationRead, + DeleteWithoutTimeout: resourceAccessPolicyAssociationDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "associated_access_policy": { + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "associated_at": { + Type: schema.TypeString, + Computed: true, + }, + "modified_at": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + + "access_scope": { + Type: schema.TypeList, + MinItems: 1, + MaxItems: 1, + Required: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + }, + "namespaces": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "cluster_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validClusterName, + }, + + "policy_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: verify.ValidARN, + }, + "principal_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: verify.ValidARN, + }, + }, + } +} + +func resourceAccessPolicyAssociationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).EKSConn(ctx) + + clusterName := d.Get("cluster_name").(string) + principal_arn := d.Get("principal_arn").(string) + policy_arn := d.Get("policy_arn").(string) + associateID := AssociatePolicyCreateResourceID(clusterName, principal_arn, policy_arn) + input := &eks.AssociateAccessPolicyInput{ + ClusterName: aws.String(clusterName), + PrincipalArn: aws.String(principal_arn), + PolicyArn: aws.String(policy_arn), + AccessScope: expandAccessScope(d.Get("access_Scope").([]interface{})), + } + + _, err := conn.AssociateAccessPolicyWithContext(ctx, input) + if err != nil { + return sdkdiag.AppendErrorf(diags, "creating EKS Access Policy Association: %s", err) + } + + d.SetId(associateID) + + return append(diags, resourceAccessPolicyAssociationRead(ctx, d, meta)...) +} + +func resourceAccessPolicyAssociationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).EKSConn(ctx) + + clusterName, principal_arn, policy_arn, err := AssociatePolicyParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading EKS Associate Policy (%s): %s", d.Id(), err) + } + output, err := FindAccessPolicyByID(ctx, conn, clusterName, principal_arn, policy_arn) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EKS Access Policy Association (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading EKS Access Policy Association (%s): %s", d.Id(), err) + } + + if output == nil { + if d.IsNewResource() { + return sdkdiag.AppendErrorf(diags, "reading EKS Associated Policy Attachment (%s): not found after creation", d.Id()) + } + + log.Printf("[WARN] EKS Associated Policy Attachment (%s) not found, removing from state", d.Id()) + d.SetId("") + return diags + } + + return diags +} + +func resourceAccessPolicyAssociationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).EKSConn(ctx) + + clusterName, principal_arn, policy_arn, err := AssociatePolicyParseResourceID(d.Id()) + + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting EKS Policy Association (%s): %s", d.Id(), err) + } + + log.Printf("[DEBUG] Deleting EKS Policy Associaion: %s", d.Id()) + + input := &eks.DisassociateAccessPolicyInput{ + ClusterName: aws.String(clusterName), + PrincipalArn: aws.String(principal_arn), + PolicyArn: aws.String(policy_arn), + } + _, err = conn.DisassociateAccessPolicyWithContext(ctx, input) + + if tfawserr.ErrCodeEquals(err, eks.ErrCodeResourceNotFoundException) { + return diags + } + + if err != nil { + return sdkdiag.AppendErrorf(diags, "deleting EKS Policy Associattion (%s): %s", d.Id(), err) + } + + return diags +} + +func expandAccessScope(l []interface{}) *eks.AccessScope { + if len(l) == 0 { + return nil + } + + m := l[0].(map[string]interface{}) + + accessScope := &eks.AccessScope{ + Type: aws.String(m["type"].(string)), + } + + if v, ok := m["namespaces"].(*schema.Set); ok && v.Len() > 0 { + accessScope.Namespaces = flex.ExpandStringSet(v) + } + + return accessScope +} + +func FindAccessPolicyByID(ctx context.Context, conn *eks.EKS, clusterName string, principal_arn string, policy_arn string) (*eks.AssociatedAccessPolicy, error) { + input := &eks.ListAssociatedAccessPoliciesInput{ + ClusterName: aws.String(clusterName), + PrincipalArn: aws.String(principal_arn), + } + + var result *eks.AssociatedAccessPolicy + + err := conn.ListAssociatedAccessPoliciesPagesWithContext(ctx, input, func(page *eks.ListAssociatedAccessPoliciesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, attachedPolicy := range page.AssociatedAccessPolicies { + if attachedPolicy == nil { + continue + } + + if aws.StringValue(attachedPolicy.PolicyArn) == policy_arn { + result = attachedPolicy + return false + } + } + + return !lastPage + }) + + if err != nil { + return nil, err + } + + return result, nil +} \ No newline at end of file diff --git a/internal/service/eks/cluster.go b/internal/service/eks/cluster.go index b8f21e6694d..b611e93354a 100644 --- a/internal/service/eks/cluster.go +++ b/internal/service/eks/cluster.go @@ -58,6 +58,26 @@ func resourceCluster() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "access_config": { + Type: schema.TypeList, + MaxItems: 1, + MinItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "authentication_mode": { + Type: schema.TypeString, + Optional: true, + ValidateDiagFunc: enum.Validate[types.AuthenticationMode](), + }, + "bootstrap_cluster_creator_admin_permissions": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + }, + }, + }, "arn": { Type: schema.TypeString, Computed: true, @@ -308,6 +328,10 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int Tags: getTagsIn(ctx), } + if _, ok := d.GetOk("access_config"); ok { + input.AccessConfig = expandAccessConfigForCreate(d.Get("access_config").([]interface{})) + } + if _, ok := d.GetOk("kubernetes_network_config"); ok { input.KubernetesNetworkConfig = expandKubernetesNetworkConfigRequest(d.Get("kubernetes_network_config").([]interface{})) } @@ -367,8 +391,6 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - var diags diag.Diagnostics - conn := meta.(*conns.AWSClient).EKSClient(ctx) cluster, err := findClusterByName(ctx, conn, d.Id()) @@ -376,16 +398,25 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] EKS Cluster (%s) not found, removing from state", d.Id()) d.SetId("") - return diags + return nil } if err != nil { - return sdkdiag.AppendErrorf(diags, "reading EKS Cluster (%s): %s", d.Id(), err) + return diag.Errorf("reading EKS Cluster (%s): %s", d.Id(), err) + } + + if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig)); err != nil { + return diag.Errorf("setting access_config: %s", err) } + /* + if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig)); err != nil { + return diag.Errorf("setting access_config: %s", err) + } + */ d.Set("arn", cluster.Arn) if err := d.Set("certificate_authority", flattenCertificate(cluster.CertificateAuthority)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting certificate_authority: %s", err) + return diag.Errorf("setting certificate_authority: %s", err) } // cluster_id is only relevant for clusters on Outposts. if cluster.OutpostConfig != nil { @@ -393,33 +424,33 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter } d.Set("created_at", aws.ToTime(cluster.CreatedAt).String()) if err := d.Set("enabled_cluster_log_types", flattenLogging(cluster.Logging)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting enabled_cluster_log_types: %s", err) + return diag.Errorf("setting enabled_cluster_log_types: %s", err) } if err := d.Set("encryption_config", flattenEncryptionConfig(cluster.EncryptionConfig)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting encryption_config: %s", err) + return diag.Errorf("setting encryption_config: %s", err) } d.Set("endpoint", cluster.Endpoint) if err := d.Set("identity", flattenIdentity(cluster.Identity)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting identity: %s", err) + return diag.Errorf("setting identity: %s", err) } if err := d.Set("kubernetes_network_config", flattenKubernetesNetworkConfigResponse(cluster.KubernetesNetworkConfig)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting kubernetes_network_config: %s", err) + return diag.Errorf("setting kubernetes_network_config: %s", err) } d.Set("name", cluster.Name) if err := d.Set("outpost_config", flattenOutpostConfigResponse(cluster.OutpostConfig)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting outpost_config: %s", err) + return diag.Errorf("setting outpost_config: %s", err) } d.Set("platform_version", cluster.PlatformVersion) d.Set("role_arn", cluster.RoleArn) d.Set("status", cluster.Status) d.Set("version", cluster.Version) if err := d.Set("vpc_config", flattenVPCConfigResponse(cluster.ResourcesVpcConfig)); err != nil { - return sdkdiag.AppendErrorf(diags, "setting vpc_config: %s", err) + return diag.Errorf("setting vpc_config: %s", err) } setTagsOut(ctx, cluster.Tags) - return diags + return nil } func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -443,7 +474,28 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int updateID := aws.ToString(output.Update.Id) if _, err := waitClusterUpdateSuccessful(ctx, conn, d.Id(), updateID, d.Timeout(schema.TimeoutUpdate)); err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for EKS Cluster (%s) version update (%s): %s", d.Id(), updateID, err) + return diag.Errorf("waiting for EKS Cluster (%s) version update (%s): %s", d.Id(), updateID, err) + } + } + + if d.HasChange("access_config") { + + input := &eks.UpdateClusterConfigInput{ + AccessConfig: expandAccessConfigForUpdate(d.Get("access_config").([]interface{})), + } + + output, err := conn.UpdateClusterConfig(ctx, input) + + if err != nil { + return diag.Errorf("updating EKS Cluster (%s) Access config: %s", d.Id(), err) + } + + updateID := aws.ToString(output.Update.Id) + + _, err = waitClusterUpdateSuccessful(ctx, conn, d.Id(), updateID, d.Timeout(schema.TimeoutUpdate)) + + if err != nil { + return diag.Errorf("waiting for EKS Cluster (%s) Access config update (%s): %s", d.Id(), updateID, err) } } @@ -744,6 +796,39 @@ func waitClusterUpdateSuccessful(ctx context.Context, conn *eks.Client, name, id return nil, err } +func expandAccessConfigForCreate(l []interface{}) *types.CreateAccessConfigRequest { + tfMap, ok := l[0].(map[string]interface{}) + + if !ok { + return nil + } + + accessConfigRequest := &types.CreateAccessConfigRequest{} + + if v, ok := tfMap["authentication_mode"].(string); ok && v != "" { + accessConfigRequest.AuthenticationMode = types.AuthenticationMode(*aws.String(v)) + } + + if v, ok := tfMap["bootstrap_cluster_creator_admin_permissions"].(bool); ok { + //accessConfigRequest.BootstrapClusterCreatorAdminPermissions = aws.Bool(tfMap["bootstrap_cluster_creator_admin_permissions"].(bool)) + accessConfigRequest.BootstrapClusterCreatorAdminPermissions = aws.Bool(v) + } + return accessConfigRequest +} + +func expandAccessConfigForUpdate(l []interface{}) *types.UpdateAccessConfigRequest { + if len(l) == 0 { + return nil + } + + m := l[0].(map[string]interface{}) + accessConfigRequest := &types.UpdateAccessConfigRequest{ + AuthenticationMode: types.AuthenticationMode(*aws.String(m["authentication_mode"].(string))), + } + + return accessConfigRequest +} + func expandEncryptionConfig(tfList []interface{}) []types.EncryptionConfig { if len(tfList) == 0 { return nil @@ -927,6 +1012,20 @@ func flattenOIDC(oidc *types.OIDC) []map[string]interface{} { return []map[string]interface{}{m} } +func flattenAccessConfigResponse(apiObject *types.AccessConfigResponse) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{ + "authentication_mode": apiObject.AuthenticationMode, + } + + print(aws.ToBool(apiObject.BootstrapClusterCreatorAdminPermissions)) + + return []interface{}{tfMap} +} + func flattenEncryptionConfig(apiObjects []types.EncryptionConfig) []interface{} { if len(apiObjects) == 0 { return nil diff --git a/internal/service/eks/cluster_data_source.go b/internal/service/eks/cluster_data_source.go index 29f947bd7ba..5fd5e44fa70 100644 --- a/internal/service/eks/cluster_data_source.go +++ b/internal/service/eks/cluster_data_source.go @@ -20,6 +20,22 @@ func dataSourceCluster() *schema.Resource { ReadWithoutTimeout: dataSourceClusterRead, Schema: map[string]*schema.Schema{ + "access_config": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "authentication_mode": { + Type: schema.TypeString, + Computed: true, + }, + "bootstrap_cluster_creator_admin_permissions": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, "arn": { Type: schema.TypeString, Computed: true, @@ -203,6 +219,9 @@ func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, meta int } d.SetId(name) + //if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig)); err != nil { + // return diag.Errorf("setting access_config: %s", err) + //} d.Set("arn", cluster.Arn) if err := d.Set("certificate_authority", flattenCertificate(cluster.CertificateAuthority)); err != nil { return sdkdiag.AppendErrorf(diags, "setting certificate_authority: %s", err) diff --git a/internal/service/eks/cluster_test.go b/internal/service/eks/cluster_test.go index 200bbb9ca2f..416e238b655 100644 --- a/internal/service/eks/cluster_test.go +++ b/internal/service/eks/cluster_test.go @@ -79,6 +79,35 @@ func TestAccEKSCluster_basic(t *testing.T) { }) } +func TestAccEKSCluster_AccessConfig_create(t *testing.T) { + ctx := acctest.Context(t) + var cluster types.Cluster + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_cluster.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckClusterDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccClusterConfig_accessConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterExists(ctx, resourceName, &cluster), + //resource.TestMatchResourceAttr(resourceName, "cluster_id", regexp.MustCompile(`^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$`)), + resource.TestCheckResourceAttr(resourceName, "access_config.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccEKSCluster_disappears(t *testing.T) { ctx := acctest.Context(t) var cluster types.Cluster @@ -862,6 +891,26 @@ resource "aws_subnet" "test" { `, rName)) } +func testAccClusterConfig_accessConfig(rName string) string { + return acctest.ConfigCompose(testAccClusterConfig_base(rName), fmt.Sprintf(` +resource "aws_eks_cluster" "test" { + name = %[1]q + role_arn = aws_iam_role.test.arn + + access_config { + bootstrap_cluster_creator_admin_permissions = true + authentication_mode = "CONFIG_MAP" + } + + vpc_config { + subnet_ids = aws_subnet.test[*].id + } + + depends_on = [aws_iam_role_policy_attachment.test-AmazonEKSClusterPolicy] +} +`, rName)) +} + func testAccClusterConfig_required(rName string) string { return acctest.ConfigCompose(testAccClusterConfig_base(rName), fmt.Sprintf(` resource "aws_eks_cluster" "test" { @@ -904,6 +953,10 @@ resource "aws_eks_cluster" "test" { subnet_ids = aws_subnet.test[*].id } + access_config { + authentication_mode = "CONFIG_MAP" + } + depends_on = [aws_iam_role_policy_attachment.test-AmazonEKSClusterPolicy] } `, rName, strings.Join(logTypes, "\", \""))) diff --git a/internal/service/eks/find.go b/internal/service/eks/find.go new file mode 100644 index 00000000000..1b014487873 --- /dev/null +++ b/internal/service/eks/find.go @@ -0,0 +1,252 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package eks + +import ( + "context" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/eks" + "github.com/aws/aws-sdk-go-v2/service/eks/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/errs" +) + +func FindAccessEntryByID(ctx context.Context, conn *eks.Client, clusterName, principal_arn string) (*types.AccessEntry, error) { + input := &eks.DescribeAccessEntryInput{ + ClusterName: aws.String(clusterName), + PrincipalArn: aws.String(principal_arn), + } + + output, err := conn.DescribeAccessEntry(ctx, input) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.AccessEntry == nil { + return nil, &retry.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.AccessEntry, nil +} + +func FindAddonByClusterNameAndAddonName(ctx context.Context, conn *eks.Client, clusterName, addonName string) (*types.Addon, error) { + input := &eks.DescribeAddonInput{ + AddonName: aws.String(addonName), + ClusterName: aws.String(clusterName), + } + + output, err := conn.DescribeAddon(ctx, input) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.Addon == nil { + return nil, &retry.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.Addon, nil +} + +func FindAddonUpdateByClusterNameAddonNameAndID(ctx context.Context, conn *eks.Client, clusterName, addonName, id string) (*types.Update, error) { + input := &eks.DescribeUpdateInput{ + AddonName: aws.String(addonName), + Name: aws.String(clusterName), + UpdateId: aws.String(id), + } + + output, err := conn.DescribeUpdate(ctx, input) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.Update == nil { + return nil, &retry.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.Update, nil +} + +func FindAddonVersionByAddonNameAndKubernetesVersion(ctx context.Context, conn *eks.Client, addonName, kubernetesVersion string, mostRecent bool) (*types.AddonVersionInfo, error) { + input := &eks.DescribeAddonVersionsInput{ + AddonName: aws.String(addonName), + KubernetesVersion: aws.String(kubernetesVersion), + } + var version *types.AddonVersionInfo + + output, err := conn.DescribeAddonVersions(ctx, input) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.Addons == nil { + return nil, &retry.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return version, nil +} + +func FindFargateProfileByClusterNameAndFargateProfileName(ctx context.Context, conn *eks.Client, clusterName, fargateProfileName string) (*types.FargateProfile, error) { + input := &eks.DescribeFargateProfileInput{ + ClusterName: aws.String(clusterName), + FargateProfileName: aws.String(fargateProfileName), + } + + output, err := conn.DescribeFargateProfile(ctx, input) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.FargateProfile == nil { + return nil, &retry.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.FargateProfile, nil +} + +func FindNodegroupByClusterNameAndNodegroupName(ctx context.Context, conn *eks.Client, clusterName, nodeGroupName string) (*types.Nodegroup, error) { + input := &eks.DescribeNodegroupInput{ + ClusterName: aws.String(clusterName), + NodegroupName: aws.String(nodeGroupName), + } + + output, err := conn.DescribeNodegroup(ctx, input) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.Nodegroup == nil { + return nil, &retry.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.Nodegroup, nil +} + +func FindNodegroupUpdateByClusterNameNodegroupNameAndID(ctx context.Context, conn *eks.Client, clusterName, nodeGroupName, id string) (*types.Update, error) { + input := &eks.DescribeUpdateInput{ + Name: aws.String(clusterName), + NodegroupName: aws.String(nodeGroupName), + UpdateId: aws.String(id), + } + + output, err := conn.DescribeUpdate(ctx, input) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.Update == nil { + return nil, &retry.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.Update, nil +} + +func FindOIDCIdentityProviderConfigByClusterNameAndConfigName(ctx context.Context, conn *eks.Client, clusterName, configName string) (*types.OidcIdentityProviderConfig, error) { + input := &eks.DescribeIdentityProviderConfigInput{ + ClusterName: aws.String(clusterName), + IdentityProviderConfig: &types.IdentityProviderConfig{ + Name: aws.String(configName), + Type: aws.String(IdentityProviderConfigTypeOIDC), + }, + } + + output, err := conn.DescribeIdentityProviderConfig(ctx, input) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.IdentityProviderConfig == nil || output.IdentityProviderConfig.Oidc == nil { + return nil, &retry.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.IdentityProviderConfig.Oidc, nil +} \ No newline at end of file diff --git a/internal/service/eks/id.go b/internal/service/eks/id.go index d91fdaf2e4b..2dfbb1a701a 100644 --- a/internal/service/eks/id.go +++ b/internal/service/eks/id.go @@ -27,6 +27,44 @@ func AddonParseResourceID(id string) (string, string, error) { return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]saddon-name", id, addonResourceIDSeparator) } +const accessEntryResourceIDSeparator = ":" + +func AccessEntryCreateResourceID(clusterName, principal_arn string) string { + parts := []string{clusterName, principal_arn} + id := strings.Join(parts, accessEntryResourceIDSeparator) + + return id +} + +func AccessEntryParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, accessEntryResourceIDSeparator) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]sprincipal_arn", id, accessEntryResourceIDSeparator) +} + +const associatePolicyResourceIDSeparator = ":" + +func AssociatePolicyCreateResourceID(clusterName, principal_arn, policy_arn string) string { + parts := []string{clusterName, principal_arn, policy_arn} + id := strings.Join(parts, associatePolicyResourceIDSeparator) + + return id +} + +func AssociatePolicyParseResourceID(id string) (string, string, string, error) { + parts := strings.Split(id, associatePolicyResourceIDSeparator) + + if len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != "" { + return parts[0], parts[1], parts[2], nil + } + + return "", "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]sprincipal_arn", id, associatePolicyResourceIDSeparator) +} + const fargateProfileResourceIDSeparator = ":" func FargateProfileCreateResourceID(clusterName, fargateProfileName string) string { diff --git a/website/docs/d/eks_access_entry.html.markdown b/website/docs/d/eks_access_entry.html.markdown new file mode 100644 index 00000000000..e8cce9792b6 --- /dev/null +++ b/website/docs/d/eks_access_entry.html.markdown @@ -0,0 +1,41 @@ +--- +subcategory: "EKS (Elastic Kubernetes)" +layout: "aws" +page_title: "AWS: aws_eks_access_entry" +description: |- + Access Entry Configurations for the EKS Cluster. +--- + +# Data Source: aws_eks_access_entry + +Access Entry Configurations for the EKS Cluster. + +## Example Usage + +```terraform +data "aws_eks_access_entry" "example" { + cluster_name = aws_eks_cluster.example.name + principal_arn = aws_iam_role.example.arn +} + +output "eks_access_entry_outputs" { + value = aws_eks_access_entry.example +} +``` + +## Argument Reference + +* `cluster_name` – (Required) Name of the EKS Cluster. Must be between 1-100 characters in length. Must begin with an alphanumeric character, and must only contain alphanumeric characters, dashes and underscores (`^[0-9A-Za-z][A-Za-z0-9\-_]+$`). +* `principal_arn` – (Required) The IAM Princial ARN which requires Authentication access to the EKS cluster. + +## Attribute Reference + +This data source exports the following attributes in addition to the arguments above: + +* `access_entry_arn` - Amazon Resource Name (ARN) of the Access Entry. +* `created_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the EKS add-on was created. +* `kubernetes_group` – List of string which can optionally specify the Kubernetes groups the user would belong to when creating an access entry. +* `modified_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the EKS add-on was updated. +* `user_name` - Defaults to principal ARN if user is principal else defaults to assume-role/session-name is role is used. +* `type` - Defaults to STANDARD which provides the standard workflow. EC2_LINUX, EC2_WINDOWS, FARGATE_LINUX types disallow users to input a username or groups, and prevent associations. +* `tags_all` - (Optional) Key-value map of resource tags, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). \ No newline at end of file diff --git a/website/docs/d/eks_cluster.html.markdown b/website/docs/d/eks_cluster.html.markdown index cea2d6c498e..d646b6301de 100644 --- a/website/docs/d/eks_cluster.html.markdown +++ b/website/docs/d/eks_cluster.html.markdown @@ -41,6 +41,9 @@ This data source exports the following attributes in addition to the arguments a * `id` - Name of the cluster * `arn` - ARN of the cluster. +* `access_config` - Configuration block for access config. + * `authentication_mode` - Values reeturned are CONFIGMAP, API or APIANDCONFIGMAP + * `bootstrap_cluster_creator_admin_permissions` - Detault to `true`. * `certificate_authority` - Nested attribute containing `certificate-authority-data` for your cluster. * `data` - The base64 encoded certificate data required to communicate with your cluster. Add this to the `certificate-authority-data` section of the `kubeconfig` file for your cluster. * `cluster_id` - The ID of your local Amazon EKS cluster on the AWS Outpost. This attribute isn't available for an AWS EKS cluster on AWS cloud. diff --git a/website/docs/r/eks_access_entry.html.markdown b/website/docs/r/eks_access_entry.html.markdown new file mode 100644 index 00000000000..19368b06427 --- /dev/null +++ b/website/docs/r/eks_access_entry.html.markdown @@ -0,0 +1,70 @@ +--- +subcategory: "EKS (Elastic Kubernetes)" +layout: "aws" +page_title: "AWS: aws_eks_access_entry" +description: |- + Access Entry Configurations for the EKS Cluster. +--- + +# Resource: aws_eks_access_entry + +Access Entry Configurations for the EKS Cluster. + +## Example Usage + +```terraform +resource "aws_eks_access_enttry" "example" { + cluster_name = aws_eks_cluster.example.name + principal_arn = aws_iam_role.example.arn + kubernetes_group = ["group-1", "group-2"] +} +``` + +## Argument Reference + +The following arguments are required: + +* `cluster_name` – (Required) Name of the EKS Cluster. Must be between 1-100 characters in length. Must begin with an alphanumeric character, and must only contain alphanumeric characters, dashes and underscores (`^[0-9A-Za-z][A-Za-z0-9\-_]+$`). +* `principal_arn` – (Required) The IAM Princial ARN which requires Authentication access to the EKS cluster. + +The following arguments are optional: + +* `kubernetes_group` – (Optional) List of string which can optionally specify the Kubernetes groups the user would belong to when creating an access entry. +* `tags` - (Optional) Key-value map of resource tags. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `access_entry_arn` - Amazon Resource Name (ARN) of the Access Entry. +* `created_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the EKS add-on was created. +* `modified_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the EKS add-on was updated. +* `user_name` - Defaults to principal ARN if user is principal else defaults to assume-role/session-name is role is used. +* `type` - Defaults to STANDARD which provides the standard workflow. EC2_LINUX, EC2_WINDOWS, FARGATE_LINUX types disallow users to input a username or groups, and prevent associations. +* `tags_all` - (Optional) Key-value map of resource tags, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `20m`) +* `update` - (Default `20m`) +* `delete` - (Default `40m`) + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import EKS add-on using the `cluster_name` and `principal_arn` separated by a colon (`:`). For example: + +```terraform +import { + to = aws_eks_access_entry.my_eks_entry + id = "my_cluster_name:my_principal_arn" +} +``` + +Using `terraform import`, import EKS access entry using the `cluster_name` and `principal_arn` separated by a colon (`:`). For example: + +```console +% terraform import aws_eks_access_entry.my_eks_access_entry my_cluster_name:my_principal_arn +``` \ No newline at end of file diff --git a/website/docs/r/eks_access_policy_association.html.markdown b/website/docs/r/eks_access_policy_association.html.markdown new file mode 100644 index 00000000000..eb0a18e1a2e --- /dev/null +++ b/website/docs/r/eks_access_policy_association.html.markdown @@ -0,0 +1,72 @@ +--- +subcategory: "EKS (Elastic Kubernetes)" +layout: "aws" +page_title: "AWS: aws_eks_access_policy_associattion" +description: |- + Access Entry Policy Association for the EKS Cluster. +--- + +# Resource: aws_eks_access_policy_association + +Access Entry Policy Association for the EKS Cluster. + +## Example Usage + +```terraform +resource "aws_eks_access_policy_association" "example" { + cluster_name = aws_eks_cluster.example.name + policy_arn = aws.iam_role.policy.arn + principal_arn = aws_iam_role.example.arn + access_scope = { + type = "namespace" + namespaces = ["example-namespace] + } + +} +``` + +## Argument Reference + +The following arguments are required: + +* `cluster_name` – (Required) Name of the EKS Cluster. Must be between 1-100 characters in length. Must begin with an alphanumeric character, and must only contain alphanumeric characters, dashes and underscores (`^[0-9A-Za-z][A-Za-z0-9\-_]+$`). +* `policy_arn` – (Required) The IAM Policy ARN which is the fine grained access allowed for access. +* `principal_arn` – (Required) The IAM Princial ARN which requires Authentication access to the EKS cluster. +* `access_scope` – (Required) The configuration block to determine the scope of the access. + * `type` - (Required) Valid values are `namespace` or `cluster`. + * `namespaces` - (Optional) The namespaces to which the access scope applies when type is namespace. + + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `associated_access_policy` - Configuraion block which consists of + * `associated_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the policy was associated. + * `modified_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the policy was updated. + + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `20m`) +* `update` - (Default `20m`) +* `delete` - (Default `40m`) + +## Import + +In Terraform v1.5.0 and later, use an [`import` block](https://developer.hashicorp.com/terraform/language/import) to import EKS add-on using the `cluster_name`, `policy_arn` and `principal_arn` separated by a colon (`:`). For example: + +```terraform +import { + to = aws_eks_access_policy_association.my_eks_entry + id = "my_cluster_name:my_policy_arn:my_principal_arn" +} +``` + +Using `terraform import`, import EKS access entry using the `cluster_name` `policy_arn` and `principal_arn` separated by a colon (`:`). For example: + +```console +% terraform import aws_eks_access_policy_association.my_eks_access_entry my_cluster_name:my_policy_arn:my_principal_arn +``` \ No newline at end of file diff --git a/website/docs/r/eks_cluster.html.markdown b/website/docs/r/eks_cluster.html.markdown index ee79fa371f0..b08c0a8adcc 100644 --- a/website/docs/r/eks_cluster.html.markdown +++ b/website/docs/r/eks_cluster.html.markdown @@ -176,6 +176,30 @@ resource "aws_eks_cluster" "example" { } } ``` +### EKS Cluster with Access Config + +```terraform +resource "aws_iam_role" "example" { + assume_role_policy = data.aws_iam_policy_document.example_assume_role_policy.json + name = "example" +} + +resource "aws_eks_cluster" "example" { + name = "example-cluster" + role_arn = aws_iam_role.example.arn + + vpc_config { + endpoint_private_access = true + endpoint_public_access = false + # ... other configuration ... + } + + access_config { + authentication_mode = "CONFIGMAP" + bootstrap_cluster_creator_admin_permissions = true + } +} +``` After adding inline IAM Policies (e.g., [`aws_iam_role_policy` resource](/docs/providers/aws/r/iam_role_policy.html)) or attaching IAM Policies (e.g., [`aws_iam_policy` resource](/docs/providers/aws/r/iam_policy.html) and [`aws_iam_role_policy_attachment` resource](/docs/providers/aws/r/iam_role_policy_attachment.html)) with the desired permissions to the IAM Role, annotate the Kubernetes service account (e.g., [`kubernetes_service_account` resource](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service_account)) and recreate any pods. @@ -189,6 +213,7 @@ The following arguments are required: The following arguments are optional: +* `access_config` - (Optional) Configuration block for the access config associated with your cluster, see [Amazon EKS Control Plane Logging](https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html). * `enabled_cluster_log_types` - (Optional) List of the desired control plane logging to enable. For more information, see [Amazon EKS Control Plane Logging](https://docs.aws.amazon.com/eks/latest/userguide/control-plane-logs.html). * `encryption_config` - (Optional) Configuration block with encryption configuration for the cluster. Only available on Kubernetes 1.13 and above clusters created after March 6, 2020. Detailed below. * `kubernetes_network_config` - (Optional) Configuration block with kubernetes network configuration for the cluster. Detailed below. If removed, Terraform will only perform drift detection if a configuration value is provided. @@ -198,6 +223,13 @@ The following arguments are optional: ### encryption_config +The `access_config` configuration block supports the following arguments: + +* `authentication_mode` - (Optional) The authentication mode for the cluster. Valid values are CONFIGMAP, API or APIANDCONFIGMAP +* `bootstrap_cluster_creator_admin_permissions` - (Optional) Weather to bootstrap the access confif values to the cluster. Default is `true`. + +### encryption_config + The `encryption_config` configuration block supports the following arguments: * `provider` - (Required) Configuration block with provider for encryption. Detailed below. @@ -309,4 +341,4 @@ Using `terraform import`, import EKS Clusters using the `name`. For example: ```console % terraform import aws_eks_cluster.my_cluster my_cluster -``` +``` \ No newline at end of file From 88712817afce37e94d4fe88ee684ed334d7bc001 Mon Sep 17 00:00:00 2001 From: Sasi Date: Wed, 3 Jan 2024 23:07:29 -0500 Subject: [PATCH 02/38] corrected review comments in documentation --- website/docs/d/eks_access_entry.html.markdown | 4 ++-- website/docs/d/eks_cluster.html.markdown | 4 ++-- website/docs/r/eks_access_entry.html.markdown | 6 +++--- website/docs/r/eks_access_policy_association.html.markdown | 4 ++-- website/docs/r/eks_cluster.html.markdown | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/website/docs/d/eks_access_entry.html.markdown b/website/docs/d/eks_access_entry.html.markdown index e8cce9792b6..93823d7698e 100644 --- a/website/docs/d/eks_access_entry.html.markdown +++ b/website/docs/d/eks_access_entry.html.markdown @@ -3,12 +3,12 @@ subcategory: "EKS (Elastic Kubernetes)" layout: "aws" page_title: "AWS: aws_eks_access_entry" description: |- - Access Entry Configurations for the EKS Cluster. + Access Entry Configurations for an EKS Cluster. --- # Data Source: aws_eks_access_entry -Access Entry Configurations for the EKS Cluster. +Access Entry Configurations for an EKS Cluster. ## Example Usage diff --git a/website/docs/d/eks_cluster.html.markdown b/website/docs/d/eks_cluster.html.markdown index d646b6301de..beb09131c51 100644 --- a/website/docs/d/eks_cluster.html.markdown +++ b/website/docs/d/eks_cluster.html.markdown @@ -42,8 +42,8 @@ This data source exports the following attributes in addition to the arguments a * `id` - Name of the cluster * `arn` - ARN of the cluster. * `access_config` - Configuration block for access config. - * `authentication_mode` - Values reeturned are CONFIGMAP, API or APIANDCONFIGMAP - * `bootstrap_cluster_creator_admin_permissions` - Detault to `true`. + * `authentication_mode` - Values returned are CONFIGMAP, API or APIANDCONFIGMAP + * `bootstrap_cluster_creator_admin_permissions` - Default to `true`. * `certificate_authority` - Nested attribute containing `certificate-authority-data` for your cluster. * `data` - The base64 encoded certificate data required to communicate with your cluster. Add this to the `certificate-authority-data` section of the `kubeconfig` file for your cluster. * `cluster_id` - The ID of your local Amazon EKS cluster on the AWS Outpost. This attribute isn't available for an AWS EKS cluster on AWS cloud. diff --git a/website/docs/r/eks_access_entry.html.markdown b/website/docs/r/eks_access_entry.html.markdown index 19368b06427..e8d9c25cfb4 100644 --- a/website/docs/r/eks_access_entry.html.markdown +++ b/website/docs/r/eks_access_entry.html.markdown @@ -3,17 +3,17 @@ subcategory: "EKS (Elastic Kubernetes)" layout: "aws" page_title: "AWS: aws_eks_access_entry" description: |- - Access Entry Configurations for the EKS Cluster. + Access Entry Configurations for an EKS Cluster. --- # Resource: aws_eks_access_entry -Access Entry Configurations for the EKS Cluster. +Access Entry Configurations for an EKS Cluster. ## Example Usage ```terraform -resource "aws_eks_access_enttry" "example" { +resource "aws_eks_access_entry" "example" { cluster_name = aws_eks_cluster.example.name principal_arn = aws_iam_role.example.arn kubernetes_group = ["group-1", "group-2"] diff --git a/website/docs/r/eks_access_policy_association.html.markdown b/website/docs/r/eks_access_policy_association.html.markdown index eb0a18e1a2e..7b15eccfec6 100644 --- a/website/docs/r/eks_access_policy_association.html.markdown +++ b/website/docs/r/eks_access_policy_association.html.markdown @@ -3,12 +3,12 @@ subcategory: "EKS (Elastic Kubernetes)" layout: "aws" page_title: "AWS: aws_eks_access_policy_associattion" description: |- - Access Entry Policy Association for the EKS Cluster. + Access Entry Policy Association for an EKS Cluster. --- # Resource: aws_eks_access_policy_association -Access Entry Policy Association for the EKS Cluster. +Access Entry Policy Association for an EKS Cluster. ## Example Usage diff --git a/website/docs/r/eks_cluster.html.markdown b/website/docs/r/eks_cluster.html.markdown index b08c0a8adcc..8349a4d2959 100644 --- a/website/docs/r/eks_cluster.html.markdown +++ b/website/docs/r/eks_cluster.html.markdown @@ -226,7 +226,7 @@ The following arguments are optional: The `access_config` configuration block supports the following arguments: * `authentication_mode` - (Optional) The authentication mode for the cluster. Valid values are CONFIGMAP, API or APIANDCONFIGMAP -* `bootstrap_cluster_creator_admin_permissions` - (Optional) Weather to bootstrap the access confif values to the cluster. Default is `true`. +* `bootstrap_cluster_creator_admin_permissions` - (Optional) Whether or not to bootstrap the access config values to the cluster. Default is `true`. ### encryption_config From eae825c557861cd4924e026506a61e32b4ea8c9d Mon Sep 17 00:00:00 2001 From: Sasi Date: Mon, 8 Jan 2024 21:51:28 -0500 Subject: [PATCH 03/38] updated access entry resource logic --- internal/service/eks/access_entry.go | 26 +- .../service/eks/access_entry_datasource.go | 30 +-- .../eks/access_entry_datasource_test.go | 66 ++++++ internal/service/eks/access_entry_test.go | 224 ++++++++++++++++++ ...ation.go => access_policy_association.go } | 74 +++--- internal/service/eks/cluster.go | 27 ++- internal/service/eks/cluster_test.go | 2 +- internal/service/eks/find.go | 2 +- internal/service/eks/id.go | 2 +- internal/service/eks/service_package_gen.go | 11 + .../docs/d/eks_access_entry.html.markdown | 2 +- website/docs/r/eks_access_entry.html.markdown | 4 +- 12 files changed, 391 insertions(+), 79 deletions(-) create mode 100644 internal/service/eks/access_entry_datasource_test.go create mode 100644 internal/service/eks/access_entry_test.go rename internal/service/eks/{access_policy_association.go => access_policy_association.go } (79%) diff --git a/internal/service/eks/access_entry.go b/internal/service/eks/access_entry.go index 25aeab6f1bd..9dfb6718216 100644 --- a/internal/service/eks/access_entry.go +++ b/internal/service/eks/access_entry.go @@ -57,7 +57,7 @@ func ResourceAccessEntry() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "kubernetes_group": { + "kubernetes_groups": { Type: schema.TypeSet, Optional: true, Elem: &schema.Schema{ @@ -96,10 +96,13 @@ func resourceAccessEntryCreate(ctx context.Context, d *schema.ResourceData, meta principal_arn := d.Get("principal_arn").(string) accessID := AccessEntryCreateResourceID(clusterName, principal_arn) input := &eks.CreateAccessEntryInput{ - ClusterName: aws.String(clusterName), - PrincipalArn: aws.String(principal_arn), - KubernetesGroups: flex.ExpandStringValueSet(d.Get("kubernetes_groups").(*schema.Set)), - Tags: getTagsIn(ctx), + ClusterName: aws.String(clusterName), + PrincipalArn: aws.String(principal_arn), + Tags: getTagsIn(ctx), + } + + if v, ok := d.GetOk("kubernetes_groups"); ok { + input.KubernetesGroups = flex.ExpandStringValueSet(v.(*schema.Set)) } _, err := conn.CreateAccessEntry(ctx, input) @@ -134,12 +137,9 @@ func resourceAccessEntryRead(ctx context.Context, d *schema.ResourceData, meta i d.Set("access_entry_arn", output.AccessEntryArn) d.Set("cluster_name", output.ClusterName) - d.Set("created_at", output.CreatedAt) - // if err := d.Set("kubernetes_groups", aws.StringValueSlice(output.KubernetesGroups)); err != nil { - // return sdkdiag.AppendErrorf(diags, "setting kubernetes_groups: %s", err) - // } + d.Set("created_at", aws.ToTime(output.CreatedAt).String()) d.Set("kubernetes_groups", output.KubernetesGroups) - d.Set("modified_at", output.ModifiedAt) + d.Set("modified_at", aws.ToTime(output.ModifiedAt).String()) d.Set("principal_arn", output.PrincipalArn) d.Set("user_name", output.Username) d.Set("type", output.Type) @@ -163,7 +163,7 @@ func resourceAccessEntryUpdate(ctx context.Context, d *schema.ResourceData, meta PrincipalArn: aws.String(principal_arn), } - if d.HasChange("kubernetes_group") { + if d.HasChange("kubernetes_groups") { input.KubernetesGroups = flex.ExpandStringValueSet(d.Get("kubernetes_groups").(*schema.Set)) } @@ -193,7 +193,7 @@ func resourceAccessEntryDelete(ctx context.Context, d *schema.ResourceData, meta PrincipalArn: aws.String(principal_arn), }) - if errs.IsAErrorMessageContains[*types.ResourceNotFoundException](err, "No Access Entry found for Id:") { + if errs.IsAErrorMessageContains[*types.ResourceNotFoundException](err, "The specified resource could not be found") { return nil } @@ -202,4 +202,4 @@ func resourceAccessEntryDelete(ctx context.Context, d *schema.ResourceData, meta } return diags -} \ No newline at end of file +} diff --git a/internal/service/eks/access_entry_datasource.go b/internal/service/eks/access_entry_datasource.go index be940915c21..7e9a8aa6945 100644 --- a/internal/service/eks/access_entry_datasource.go +++ b/internal/service/eks/access_entry_datasource.go @@ -7,6 +7,7 @@ import ( "context" "log" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" @@ -18,7 +19,7 @@ import ( ) // @SDKDataSource("aws_eks_access_entry") -func DataSourceAccessEntry() *schema.Resource { +func dataSourceAccessEntry() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceAccessEntryRead, @@ -36,7 +37,7 @@ func DataSourceAccessEntry() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "kubernetes_group": { + "kubernetes_groups": { Type: schema.TypeSet, Computed: true, Elem: &schema.Schema{ @@ -70,30 +71,25 @@ func dataSourceAccessEntryRead(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EKSClient(ctx) - clusterName, principal_arn, err := AccessEntryParseResourceID(d.Id()) - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading EKS Access Entry (%s): %s", d.Id(), err) - } - output, err := FindAccessEntryByID(ctx, conn, clusterName, principal_arn) + principalArn := d.Get("principal_arn").(string) + clusterName := d.Get("cluster_name").(string) + id := AccessEntryCreateResourceID(clusterName, principalArn) + output, err := FindAccessEntryByID(ctx, conn, clusterName, principalArn) if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] EKS Access Entry (%s) not found, removing from state", d.Id()) - d.SetId("") + log.Printf("[WARN] EKS Access Entry (%s) not found, removing from state", id) return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "reading EKS EKS Access Entry (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading EKS Access Entry (%s): %s", id, err) } - + d.SetId(id) d.Set("access_entry_arn", output.AccessEntryArn) d.Set("cluster_name", output.ClusterName) - d.Set("created_at", output.CreatedAt) - // if err := d.Set("kubernetes_groups", aws.StringValueSlice(output.KubernetesGroups)); err != nil { - // return sdkdiag.AppendErrorf(diags, "setting kubernetes_groups: %s", err) - // } + d.Set("created_at", aws.ToTime(output.CreatedAt).String()) d.Set("kubernetes_groups", output.KubernetesGroups) - d.Set("modified_at", output.ModifiedAt) + d.Set("modified_at", aws.ToTime(output.ModifiedAt).String()) d.Set("principal_arn", output.PrincipalArn) d.Set("user_name", output.Username) d.Set("type", output.Type) @@ -101,4 +97,4 @@ func dataSourceAccessEntryRead(ctx context.Context, d *schema.ResourceData, meta setTagsOut(ctx, output.Tags) return diags -} \ No newline at end of file +} diff --git a/internal/service/eks/access_entry_datasource_test.go b/internal/service/eks/access_entry_datasource_test.go new file mode 100644 index 00000000000..95753db52cd --- /dev/null +++ b/internal/service/eks/access_entry_datasource_test.go @@ -0,0 +1,66 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package eks_test + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/eks" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +func TestAccEKSAccessEntryDataSource_basic(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + dataSourceResourceName := "data.aws_eks_access_entry.test" + resourceName := "aws_eks_access_entry.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t); testAccPreCheckAddon(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, eks.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAddonDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAccessEntryDataSourceConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "cluster_name", dataSourceResourceName, "cluster_name"), + resource.TestCheckResourceAttrPair(resourceName, "access_entry_arn", dataSourceResourceName, "access_entry_arn"), + resource.TestCheckResourceAttrPair(resourceName, "kubernetes_groups", dataSourceResourceName, "kubernetes_groups"), + resource.TestCheckResourceAttrPair(resourceName, "created_at", dataSourceResourceName, "created_at"), + resource.TestCheckResourceAttrPair(resourceName, "modified_at", dataSourceResourceName, "modified_at"), + resource.TestCheckResourceAttrPair(resourceName, "principal_arn", dataSourceResourceName, "principal_arn"), + resource.TestCheckResourceAttrPair(resourceName, "user_name", dataSourceResourceName, "user_name"), + resource.TestCheckResourceAttrPair(resourceName, "type", dataSourceResourceName, "type"), + resource.TestCheckResourceAttrPair(resourceName, "tags.%", dataSourceResourceName, "tags.%"), + ), + }, + }, + }) +} + +func testAccAccessEntryDataSourceConfig_basic(rName string) string { + return acctest.ConfigCompose(testAccAccessEntryConfig_base(rName), fmt.Sprintf(` +resource "aws_iam_user" "test" { + name = %[1]q +} + +resource "aws_eks_access_entry" "test" { + cluster_name = aws_eks_cluster.test.name + principal_arn = aws_iam_user.test.arn +} + +data "aws_eks_access_entry" "test" { + cluster_name = aws_eks_cluster.test.name + principal_arn = aws_iam_user.test.arn + depends_on = [ + aws_eks_access_entry.test, + aws_eks_cluster.test, + ] +} +`, rName)) +} diff --git a/internal/service/eks/access_entry_test.go b/internal/service/eks/access_entry_test.go new file mode 100644 index 00000000000..9bd57f85d59 --- /dev/null +++ b/internal/service/eks/access_entry_test.go @@ -0,0 +1,224 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package eks_test + +import ( + "context" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/eks/types" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" + + tfeks "github.com/hashicorp/terraform-provider-aws/internal/service/eks" +) + +func TestAccEKSAccessEntry_basic(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var accessentry types.AccessEntry + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_access_entry.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAccessEntryDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAccessEntryConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccessEntryExists(ctx, resourceName, &accessentry), + resource.TestCheckResourceAttrSet(resourceName, "created_at"), + resource.TestCheckResourceAttrSet(resourceName, "principal_arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccEKSAccessEntry_disappears(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var accessentry types.AccessEntry + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_access_entry.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAccessEntryDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAccessEntryConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccessEntryExists(ctx, resourceName, &accessentry), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfeks.ResourceAccessEntry(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAccessEntryDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_eks_access_entry" { + continue + } + + clusterName, principal_arn, err := tfeks.AccessEntryParseResourceID(rs.Primary.ID) + if err != nil { + return err + } + _, err = tfeks.FindAccessEntryByID(ctx, conn, clusterName, principal_arn) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("EKS Access Entry %s still exists", rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckAccessEntryExists(ctx context.Context, name string, accessentry *types.AccessEntry) resource.TestCheckFunc { + + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + clusterName, principal_arn, err := tfeks.AccessEntryParseResourceID(rs.Primary.ID) + if err != nil { + return err + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) + + output, err := tfeks.FindAccessEntryByID(ctx, conn, clusterName, principal_arn) + + if err != nil { + return err + } + + *accessentry = *output + + return nil + } +} + +func testAccAccessEntryConfig_base(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = < 0 { - accessScope.Namespaces = flex.ExpandStringSet(v) - } + // if v, ok := m["type"].(string); ok && v != "" { + // accessScope.Type = types.AccessScopeType(aws.String(v)) + // } + + // if v, ok := m["namespaces"].(*schema.Set); ok && v.Len() > 0 { + // accessScope.Namespaces = flex.ExpandStringSet(v) + // } return accessScope } -func FindAccessPolicyByID(ctx context.Context, conn *eks.EKS, clusterName string, principal_arn string, policy_arn string) (*eks.AssociatedAccessPolicy, error) { +func FindAccessPolicyByID(ctx context.Context, conn *eks.Client, clusterName string, principal_arn string, policy_arn string) (types.AssociatedAccessPolicy, error) { input := &eks.ListAssociatedAccessPoliciesInput{ ClusterName: aws.String(clusterName), PrincipalArn: aws.String(principal_arn), } - var result *eks.AssociatedAccessPolicy + var result types.AssociatedAccessPolicy - err := conn.ListAssociatedAccessPoliciesPagesWithContext(ctx, input, func(page *eks.ListAssociatedAccessPoliciesOutput, lastPage bool) bool { - if page == nil { - return !lastPage - } + output, err := conn.ListAssociatedAccessPolicies(ctx, input) - for _, attachedPolicy := range page.AssociatedAccessPolicies { - if attachedPolicy == nil { - continue - } + if err != nil { + return nil, err + } - if aws.StringValue(attachedPolicy.PolicyArn) == policy_arn { - result = attachedPolicy - return false - } + if output == nil || output.AssociatedAccessPolicies == nil { + return nil, &retry.NotFoundError{ + Message: "Empty result", + LastRequest: input, } + } - return !lastPage - }) - - if err != nil { - return nil, err + for _, accessPolicy := range output.AssociatedAccessPolicies { + if aws.ToString(accessPolicy.PolicyArn) == policy_arn { + result = accessPolicy + } } return result, nil -} \ No newline at end of file +} diff --git a/internal/service/eks/cluster.go b/internal/service/eks/cluster.go index b611e93354a..b495c8eabb7 100644 --- a/internal/service/eks/cluster.go +++ b/internal/service/eks/cluster.go @@ -7,6 +7,7 @@ import ( "context" "fmt" "log" + "strconv" "time" "github.com/YakDriver/regexache" @@ -74,6 +75,17 @@ func resourceCluster() *schema.Resource { Type: schema.TypeBool, Optional: true, ForceNew: true, + // When value is -1, instance maintenance policy is removed, state file will not contain any value. + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + var bootstrapClusterAdminPermissions bool + if _, ok := d.GetOk("access_config"); ok { + tfMap := d.Get("access_config").([]interface{})[0].(map[string]interface{}) + if v, ok := tfMap["bootstrap_cluster_creator_admin_permissions"].(bool); ok { + bootstrapClusterAdminPermissions = v + } + } + return old == strconv.FormatBool(bootstrapClusterAdminPermissions) + }, }, }, }, @@ -404,8 +416,13 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter if err != nil { return diag.Errorf("reading EKS Cluster (%s): %s", d.Id(), err) } + //Can check if + accessConfig := &types.CreateAccessConfigRequest{} + if v, ok := d.GetOk("access_config"); ok { + accessConfig = expandAccessConfigForCreate(v.([]interface{})) + } - if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig)); err != nil { + if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig, accessConfig.BootstrapClusterCreatorAdminPermissions)); err != nil { return diag.Errorf("setting access_config: %s", err) } /* @@ -810,7 +827,6 @@ func expandAccessConfigForCreate(l []interface{}) *types.CreateAccessConfigReque } if v, ok := tfMap["bootstrap_cluster_creator_admin_permissions"].(bool); ok { - //accessConfigRequest.BootstrapClusterCreatorAdminPermissions = aws.Bool(tfMap["bootstrap_cluster_creator_admin_permissions"].(bool)) accessConfigRequest.BootstrapClusterCreatorAdminPermissions = aws.Bool(v) } return accessConfigRequest @@ -1012,7 +1028,7 @@ func flattenOIDC(oidc *types.OIDC) []map[string]interface{} { return []map[string]interface{}{m} } -func flattenAccessConfigResponse(apiObject *types.AccessConfigResponse) []interface{} { +func flattenAccessConfigResponse(apiObject *types.AccessConfigResponse, bootstrap *bool) []interface{} { if apiObject == nil { return nil } @@ -1020,8 +1036,9 @@ func flattenAccessConfigResponse(apiObject *types.AccessConfigResponse) []interf tfMap := map[string]interface{}{ "authentication_mode": apiObject.AuthenticationMode, } - - print(aws.ToBool(apiObject.BootstrapClusterCreatorAdminPermissions)) + if bootstrap != nil { + tfMap["bootstrap_cluster_creator_admin_permissions"] = aws.ToBool(bootstrap) + } return []interface{}{tfMap} } diff --git a/internal/service/eks/cluster_test.go b/internal/service/eks/cluster_test.go index 416e238b655..d68b45ac9c9 100644 --- a/internal/service/eks/cluster_test.go +++ b/internal/service/eks/cluster_test.go @@ -102,7 +102,7 @@ func TestAccEKSCluster_AccessConfig_create(t *testing.T) { { ResourceName: resourceName, ImportState: true, - ImportStateVerify: true, + ImportStateVerify: false, }, }, }) diff --git a/internal/service/eks/find.go b/internal/service/eks/find.go index 1b014487873..d6b9fce1e2d 100644 --- a/internal/service/eks/find.go +++ b/internal/service/eks/find.go @@ -249,4 +249,4 @@ func FindOIDCIdentityProviderConfigByClusterNameAndConfigName(ctx context.Contex } return output.IdentityProviderConfig.Oidc, nil -} \ No newline at end of file +} diff --git a/internal/service/eks/id.go b/internal/service/eks/id.go index 2dfbb1a701a..2598fc18de9 100644 --- a/internal/service/eks/id.go +++ b/internal/service/eks/id.go @@ -37,7 +37,7 @@ func AccessEntryCreateResourceID(clusterName, principal_arn string) string { } func AccessEntryParseResourceID(id string) (string, string, error) { - parts := strings.Split(id, accessEntryResourceIDSeparator) + parts := strings.SplitN(id, accessEntryResourceIDSeparator, 2) if len(parts) == 2 && parts[0] != "" && parts[1] != "" { return parts[0], parts[1], nil diff --git a/internal/service/eks/service_package_gen.go b/internal/service/eks/service_package_gen.go index f798ef47f68..a63eb5c3915 100644 --- a/internal/service/eks/service_package_gen.go +++ b/internal/service/eks/service_package_gen.go @@ -33,6 +33,9 @@ func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.Servic func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePackageSDKDataSource { return []*types.ServicePackageSDKDataSource{ { + Factory: dataSourceAccessEntry, + TypeName: "aws_eks_access_entry", + }, { Factory: dataSourceAddon, TypeName: "aws_eks_addon", }, @@ -65,6 +68,14 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePackageSDKResource { return []*types.ServicePackageSDKResource{ + { + Factory: ResourceAccessEntry, + TypeName: "aws_eks_access_entry", + Name: "Access Entry", + Tags: &types.ServicePackageResourceTags{ + IdentifierAttribute: "arn", + }, + }, { Factory: resourceAddon, TypeName: "aws_eks_addon", diff --git a/website/docs/d/eks_access_entry.html.markdown b/website/docs/d/eks_access_entry.html.markdown index 93823d7698e..cc61fab9be9 100644 --- a/website/docs/d/eks_access_entry.html.markdown +++ b/website/docs/d/eks_access_entry.html.markdown @@ -34,7 +34,7 @@ This data source exports the following attributes in addition to the arguments a * `access_entry_arn` - Amazon Resource Name (ARN) of the Access Entry. * `created_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the EKS add-on was created. -* `kubernetes_group` – List of string which can optionally specify the Kubernetes groups the user would belong to when creating an access entry. +* `kubernetes_groups` – List of string which can optionally specify the Kubernetes groups the user would belong to when creating an access entry. * `modified_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the EKS add-on was updated. * `user_name` - Defaults to principal ARN if user is principal else defaults to assume-role/session-name is role is used. * `type` - Defaults to STANDARD which provides the standard workflow. EC2_LINUX, EC2_WINDOWS, FARGATE_LINUX types disallow users to input a username or groups, and prevent associations. diff --git a/website/docs/r/eks_access_entry.html.markdown b/website/docs/r/eks_access_entry.html.markdown index e8d9c25cfb4..5a2c569ec3c 100644 --- a/website/docs/r/eks_access_entry.html.markdown +++ b/website/docs/r/eks_access_entry.html.markdown @@ -16,7 +16,7 @@ Access Entry Configurations for an EKS Cluster. resource "aws_eks_access_entry" "example" { cluster_name = aws_eks_cluster.example.name principal_arn = aws_iam_role.example.arn - kubernetes_group = ["group-1", "group-2"] + kubernetes_groups = ["group-1", "group-2"] } ``` @@ -29,7 +29,7 @@ The following arguments are required: The following arguments are optional: -* `kubernetes_group` – (Optional) List of string which can optionally specify the Kubernetes groups the user would belong to when creating an access entry. +* `kubernetes_groups` – (Optional) List of string which can optionally specify the Kubernetes groups the user would belong to when creating an access entry. * `tags` - (Optional) Key-value map of resource tags. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. From 79df630201989b211790604df89e6199548c2897 Mon Sep 17 00:00:00 2001 From: Sasi Date: Tue, 9 Jan 2024 10:47:26 -0500 Subject: [PATCH 04/38] updated access entry and access policy association resource logic --- internal/service/eks/access_entry.go | 2 +- ...ation.go => access_policy_association.go} | 81 +++--- .../eks/access_policy_association_test.go | 236 ++++++++++++++++++ internal/service/eks/exports_test.go | 1 + internal/service/eks/id.go | 4 +- internal/service/eks/service_package_gen.go | 7 +- 6 files changed, 294 insertions(+), 37 deletions(-) rename internal/service/eks/{access_policy_association.go => access_policy_association.go} (76%) create mode 100644 internal/service/eks/access_policy_association_test.go diff --git a/internal/service/eks/access_entry.go b/internal/service/eks/access_entry.go index 9dfb6718216..48832f36230 100644 --- a/internal/service/eks/access_entry.go +++ b/internal/service/eks/access_entry.go @@ -25,7 +25,7 @@ import ( // @SDKResource("aws_eks_access_entry", name="Access Entry") // @Tags(identifierAttribute="arn") -func ResourceAccessEntry() *schema.Resource { +func resourceAccessEntry() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceAccessEntryCreate, ReadWithoutTimeout: resourceAccessEntryRead, diff --git a/internal/service/eks/access_policy_association.go b/internal/service/eks/access_policy_association.go similarity index 76% rename from internal/service/eks/access_policy_association.go rename to internal/service/eks/access_policy_association.go index 0e9fcb8ea71..a8e4d74c1c2 100644 --- a/internal/service/eks/access_policy_association.go +++ b/internal/service/eks/access_policy_association.go @@ -15,13 +15,17 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" + "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_eks_access_policy_association", name="Access Policy Association") -func ResourceAccessPolicyAssociation() *schema.Resource { +func resourceAccessPolicyAssociation() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceAccessPolicyAssociationCreate, ReadWithoutTimeout: resourceAccessPolicyAssociationRead, @@ -36,30 +40,16 @@ func ResourceAccessPolicyAssociation() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "associated_access_policy": { - Type: schema.TypeList, - MinItems: 1, - MaxItems: 1, + "associated_at": { + Type: schema.TypeString, Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "associated_at": { - Type: schema.TypeString, - Computed: true, - }, - "modified_at": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, }, - "access_scope": { Type: schema.TypeList, MinItems: 1, MaxItems: 1, Required: true, + ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "type": { @@ -82,7 +72,10 @@ func ResourceAccessPolicyAssociation() *schema.Resource { ForceNew: true, ValidateFunc: validClusterName, }, - + "modified_at": { + Type: schema.TypeString, + Computed: true, + }, "policy_arn": { Type: schema.TypeString, Required: true, @@ -95,6 +88,8 @@ func ResourceAccessPolicyAssociation() *schema.Resource { ForceNew: true, ValidateFunc: verify.ValidARN, }, + names.AttrTags: tftags.TagsSchemaComputed(), + names.AttrTagsAll: tftags.TagsSchemaComputed(), }, } } @@ -111,7 +106,7 @@ func resourceAccessPolicyAssociationCreate(ctx context.Context, d *schema.Resour ClusterName: aws.String(clusterName), PrincipalArn: aws.String(principal_arn), PolicyArn: aws.String(policy_arn), - AccessScope: expandAccessScope(d.Get("access_Scope").([]interface{})), + AccessScope: expandAccessScope(d.Get("access_scope").([]interface{})), } _, err := conn.AssociateAccessPolicy(ctx, input) @@ -154,6 +149,13 @@ func resourceAccessPolicyAssociationRead(ctx context.Context, d *schema.Resource return diags } + d.Set("access_scope", flattenAccessScope(output.AccessScope)) + d.Set("associated_at", aws.ToTime(output.AssociatedAt).String()) + d.Set("cluster_name", clusterName) + d.Set("modified_at", aws.ToTime(output.ModifiedAt).String()) + d.Set("policy_arn", policy_arn) + d.Set("principal_arn", principal_arn) + return diags } @@ -176,9 +178,9 @@ func resourceAccessPolicyAssociationDelete(ctx context.Context, d *schema.Resour } _, err = conn.DisassociateAccessPolicy(ctx, input) - // if tfawserr.ErrCodeEquals(err, eks.ErrCodeResourceNotFoundException) { - // return diags - // } + if errs.IsAErrorMessageContains[*types.ResourceNotFoundException](err, "The specified resource could not be found") { + return nil + } if err != nil { return sdkdiag.AppendErrorf(diags, "deleting EKS Policy Associattion (%s): %s", d.Id(), err) @@ -192,28 +194,41 @@ func expandAccessScope(l []interface{}) *types.AccessScope { return nil } - // m := l[0].(map[string]interface{}) + m := l[0].(map[string]interface{}) accessScope := &types.AccessScope{} - // if v, ok := m["type"].(string); ok && v != "" { - // accessScope.Type = types.AccessScopeType(aws.String(v)) - // } + if v, ok := m["type"].(string); ok && v != "" { + accessScope.Type = types.AccessScopeType(*aws.String(v)) + } - // if v, ok := m["namespaces"].(*schema.Set); ok && v.Len() > 0 { - // accessScope.Namespaces = flex.ExpandStringSet(v) - // } + if v, ok := m["namespaces"]; ok { + accessScope.Namespaces = flex.ExpandStringValueSet(v.(*schema.Set)) + } return accessScope } -func FindAccessPolicyByID(ctx context.Context, conn *eks.Client, clusterName string, principal_arn string, policy_arn string) (types.AssociatedAccessPolicy, error) { +func flattenAccessScope(apiObject *types.AccessScope) []interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{ + "type": (*string)(&apiObject.Type), + "namespaces": apiObject.Namespaces, + } + + return []interface{}{tfMap} +} + +func FindAccessPolicyByID(ctx context.Context, conn *eks.Client, clusterName string, principal_arn string, policy_arn string) (*types.AssociatedAccessPolicy, error) { input := &eks.ListAssociatedAccessPoliciesInput{ ClusterName: aws.String(clusterName), PrincipalArn: aws.String(principal_arn), } - var result types.AssociatedAccessPolicy + var result *types.AssociatedAccessPolicy output, err := conn.ListAssociatedAccessPolicies(ctx, input) @@ -230,7 +245,7 @@ func FindAccessPolicyByID(ctx context.Context, conn *eks.Client, clusterName str for _, accessPolicy := range output.AssociatedAccessPolicies { if aws.ToString(accessPolicy.PolicyArn) == policy_arn { - result = accessPolicy + result = &accessPolicy } } diff --git a/internal/service/eks/access_policy_association_test.go b/internal/service/eks/access_policy_association_test.go new file mode 100644 index 00000000000..5cecf757f19 --- /dev/null +++ b/internal/service/eks/access_policy_association_test.go @@ -0,0 +1,236 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package eks_test + +import ( + "context" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/eks/types" + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" + + tfeks "github.com/hashicorp/terraform-provider-aws/internal/service/eks" +) + +func TestAccEKSAccessPolicyAssociation_basic(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var associatedaccesspolicy types.AssociatedAccessPolicy + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_access_policy_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAccessPolicyAssociationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAccessPolicyAssociationConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccessPolicyAssociationExists(ctx, resourceName, &associatedaccesspolicy), + resource.TestCheckResourceAttr(resourceName, "associated_access_policy.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "cluster_name"), + resource.TestCheckResourceAttrSet(resourceName, "policy_arn"), + resource.TestCheckResourceAttrSet(resourceName, "principal_arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccEKSAccessPolicyAssociation_disappears(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var associatedaccesspolicy types.AssociatedAccessPolicy + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_access_policy_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAccessPolicyAssociationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAccessPolicyAssociationConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccessPolicyAssociationExists(ctx, resourceName, &associatedaccesspolicy), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfeks.ResourceAccessEntry(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAccessPolicyAssociationDestroy(ctx context.Context) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_eks_access_policy_association" { + continue + } + + clusterName, principal_arn, policy_arn, err := tfeks.AssociatePolicyParseResourceID(rs.Primary.ID) + if err != nil { + return err + } + _, err = tfeks.FindAccessPolicyByID(ctx, conn, clusterName, principal_arn, policy_arn) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("EKS Access Policy Association %s still exists", rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckAccessPolicyAssociationExists(ctx context.Context, name string, associatedaccesspolicy *types.AssociatedAccessPolicy) resource.TestCheckFunc { + + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + clusterName, principal_arn, policy_arn, err := tfeks.AssociatePolicyParseResourceID(rs.Primary.ID) + if err != nil { + return err + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) + + output, err := tfeks.FindAccessPolicyByID(ctx, conn, clusterName, principal_arn, policy_arn) + + if err != nil { + return err + } + + *associatedaccesspolicy = *output + + return nil + } +} + +func testAccAccessPolicyAssociationConfig_base(rName string) string { + return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = < Date: Sat, 13 Jan 2024 19:06:42 -0500 Subject: [PATCH 05/38] fixed access policy association issues --- .../service/eks/access_policy_association.go | 28 ++++++++----------- .../eks/access_policy_association_test.go | 13 +++++---- internal/service/eks/cluster.go | 2 +- internal/service/eks/exports_test.go | 15 +++++----- 4 files changed, 28 insertions(+), 30 deletions(-) diff --git a/internal/service/eks/access_policy_association.go b/internal/service/eks/access_policy_association.go index a8e4d74c1c2..52911a8268e 100644 --- a/internal/service/eks/access_policy_association.go +++ b/internal/service/eks/access_policy_association.go @@ -18,10 +18,8 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" - tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" - "github.com/hashicorp/terraform-provider-aws/names" ) // @SDKResource("aws_eks_access_policy_association", name="Access Policy Association") @@ -54,11 +52,13 @@ func resourceAccessPolicyAssociation() *schema.Resource { Schema: map[string]*schema.Schema{ "type": { Type: schema.TypeString, + ForceNew: true, Required: true, }, "namespaces": { Type: schema.TypeSet, Optional: true, + ForceNew: true, Elem: &schema.Schema{ Type: schema.TypeString, }, @@ -88,8 +88,6 @@ func resourceAccessPolicyAssociation() *schema.Resource { ForceNew: true, ValidateFunc: verify.ValidARN, }, - names.AttrTags: tftags.TagsSchemaComputed(), - names.AttrTagsAll: tftags.TagsSchemaComputed(), }, } } @@ -135,10 +133,6 @@ func resourceAccessPolicyAssociationRead(ctx context.Context, d *schema.Resource return diags } - if err != nil { - return sdkdiag.AppendErrorf(diags, "reading EKS Access Policy Association (%s): %s", d.Id(), err) - } - if output == nil { if d.IsNewResource() { return sdkdiag.AppendErrorf(diags, "reading EKS Associated Policy Attachment (%s): not found after creation", d.Id()) @@ -149,6 +143,10 @@ func resourceAccessPolicyAssociationRead(ctx context.Context, d *schema.Resource return diags } + if err != nil { + return sdkdiag.AppendErrorf(diags, "reading EKS Access Policy Association (%s): %s", d.Id(), err) + } + d.Set("access_scope", flattenAccessScope(output.AccessScope)) d.Set("associated_at", aws.ToTime(output.AssociatedAt).String()) d.Set("cluster_name", clusterName) @@ -162,7 +160,6 @@ func resourceAccessPolicyAssociationRead(ctx context.Context, d *schema.Resource func resourceAccessPolicyAssociationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EKSClient(ctx) - clusterName, principal_arn, policy_arn, err := AssociatePolicyParseResourceID(d.Id()) if err != nil { @@ -178,7 +175,7 @@ func resourceAccessPolicyAssociationDelete(ctx context.Context, d *schema.Resour } _, err = conn.DisassociateAccessPolicy(ctx, input) - if errs.IsAErrorMessageContains[*types.ResourceNotFoundException](err, "The specified resource could not be found") { + if errs.IsA[*types.ResourceNotFoundException](err) { return nil } @@ -231,18 +228,17 @@ func FindAccessPolicyByID(ctx context.Context, conn *eks.Client, clusterName str var result *types.AssociatedAccessPolicy output, err := conn.ListAssociatedAccessPolicies(ctx, input) - - if err != nil { - return nil, err - } - - if output == nil || output.AssociatedAccessPolicies == nil { + if output == nil || output.AssociatedAccessPolicies == nil || errs.IsA[*types.ResourceNotFoundException](err) { return nil, &retry.NotFoundError{ Message: "Empty result", LastRequest: input, } } + if err != nil { + return nil, err + } + for _, accessPolicy := range output.AssociatedAccessPolicies { if aws.ToString(accessPolicy.PolicyArn) == policy_arn { result = &accessPolicy diff --git a/internal/service/eks/access_policy_association_test.go b/internal/service/eks/access_policy_association_test.go index 5cecf757f19..0ebebb7ee22 100644 --- a/internal/service/eks/access_policy_association_test.go +++ b/internal/service/eks/access_policy_association_test.go @@ -43,8 +43,9 @@ func TestAccEKSAccessPolicyAssociation_basic(t *testing.T) { Config: testAccAccessPolicyAssociationConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAccessPolicyAssociationExists(ctx, resourceName, &associatedaccesspolicy), - resource.TestCheckResourceAttr(resourceName, "associated_access_policy.#", "1"), + resource.TestCheckResourceAttrSet(resourceName, "associated_at"), resource.TestCheckResourceAttrSet(resourceName, "cluster_name"), + resource.TestCheckResourceAttrSet(resourceName, "modified_at"), resource.TestCheckResourceAttrSet(resourceName, "policy_arn"), resource.TestCheckResourceAttrSet(resourceName, "principal_arn"), ), @@ -81,7 +82,7 @@ func TestAccEKSAccessPolicyAssociation_disappears(t *testing.T) { Config: testAccAccessPolicyAssociationConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAccessPolicyAssociationExists(ctx, resourceName, &associatedaccesspolicy), - acctest.CheckResourceDisappears(ctx, acctest.Provider, tfeks.ResourceAccessEntry(), resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfeks.ResourceAccessPolicyAssociation(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -131,11 +132,9 @@ func testAccCheckAccessPolicyAssociationExists(ctx context.Context, name string, if err != nil { return err } - conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) output, err := tfeks.FindAccessPolicyByID(ctx, conn, clusterName, principal_arn, policy_arn) - if err != nil { return err } @@ -213,7 +212,7 @@ resource "aws_eks_cluster" "test" { } func testAccAccessPolicyAssociationConfig_basic(rName string) string { - return acctest.ConfigCompose(testAccAccessEntryConfig_base(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccAccessPolicyAssociationConfig_base(rName), fmt.Sprintf(` resource "aws_iam_user" "test" { name = %[1]q } @@ -221,9 +220,10 @@ resource "aws_iam_user" "test" { resource "aws_eks_access_entry" "test" { cluster_name = aws_eks_cluster.test.name principal_arn = aws_iam_user.test.arn + depends_on = [aws_eks_cluster.test] } -resource "aws_eks_access_policy_association" "this" { +resource "aws_eks_access_policy_association" "test" { cluster_name = aws_eks_cluster.test.name principal_arn = aws_iam_user.test.arn policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy" @@ -231,6 +231,7 @@ resource "aws_eks_access_policy_association" "this" { access_scope { type = "cluster" } + depends_on = [aws_eks_cluster.test, aws_eks_access_entry.test] } `, rName)) } diff --git a/internal/service/eks/cluster.go b/internal/service/eks/cluster.go index b495c8eabb7..b70778d8314 100644 --- a/internal/service/eks/cluster.go +++ b/internal/service/eks/cluster.go @@ -75,7 +75,7 @@ func resourceCluster() *schema.Resource { Type: schema.TypeBool, Optional: true, ForceNew: true, - // When value is -1, instance maintenance policy is removed, state file will not contain any value. + // bootstrapClusterAdminPermissions is not returned by API, hence comparing old value with value in schema. DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { var bootstrapClusterAdminPermissions bool if _, ok := d.GetOk("access_config"); ok { diff --git a/internal/service/eks/exports_test.go b/internal/service/eks/exports_test.go index b82dfab905e..877abf163d1 100644 --- a/internal/service/eks/exports_test.go +++ b/internal/service/eks/exports_test.go @@ -5,13 +5,14 @@ package eks // Exports for use in tests only. var ( - ResourceAccessEntry = resourceAccessEntry - ResourceAddon = resourceAddon - ResourceCluster = resourceCluster - ResourceFargateProfile = resourceFargateProfile - ResourceIdentityProviderConfig = resourceIdentityProviderConfig - ResourceNodeGroup = resourceNodeGroup - ResourcePodIdentityAssociation = newPodIdentityAssociationResource + ResourceAccessEntry = resourceAccessEntry + ResourceAccessPolicyAssociation = resourceAccessPolicyAssociation + ResourceAddon = resourceAddon + ResourceCluster = resourceCluster + ResourceFargateProfile = resourceFargateProfile + ResourceIdentityProviderConfig = resourceIdentityProviderConfig + ResourceNodeGroup = resourceNodeGroup + ResourcePodIdentityAssociation = newPodIdentityAssociationResource FindAddonByTwoPartKey = findAddonByTwoPartKey FindClusterByName = findClusterByName From 0df367b986f59ebf9cce1d2a0e37b80028787a1d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 12:20:43 -0500 Subject: [PATCH 06/38] Add CHANGELOG entries. --- .changelog/35037.txt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .changelog/35037.txt diff --git a/.changelog/35037.txt b/.changelog/35037.txt new file mode 100644 index 00000000000..ce366f50607 --- /dev/null +++ b/.changelog/35037.txt @@ -0,0 +1,19 @@ +```release-note:new-resource +aws_eks_access_entry +``` + +```release-note:new-data-source +aws_eks_access_entry +``` + +```release-note:new-resource +aws_eks_access_policy_association +``` + +```release-note:enhancement +resource/aws_eks_cluster: Add `access_config` configuration block +``` + +```release-note:enhancement +resource/aws_eks_cluster: Add `access_config` attribute +``` \ No newline at end of file From 47803d1dc2d9cd57d6ba13a64efe2842c42a1e4b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 12:24:03 -0500 Subject: [PATCH 07/38] Update website/docs/r/eks_access_policy_association.html.markdown --- website/docs/r/eks_access_policy_association.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/eks_access_policy_association.html.markdown b/website/docs/r/eks_access_policy_association.html.markdown index 7b15eccfec6..8f136667faf 100644 --- a/website/docs/r/eks_access_policy_association.html.markdown +++ b/website/docs/r/eks_access_policy_association.html.markdown @@ -15,7 +15,7 @@ Access Entry Policy Association for an EKS Cluster. ```terraform resource "aws_eks_access_policy_association" "example" { cluster_name = aws_eks_cluster.example.name - policy_arn = aws.iam_role.policy.arn + policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy" principal_arn = aws_iam_role.example.arn access_scope = { type = "namespace" From 0c96a6931117e15f48608d8db0e75657dcc512ca Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 12:24:16 -0500 Subject: [PATCH 08/38] Update website/docs/r/eks_access_policy_association.html.markdown --- website/docs/r/eks_access_policy_association.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/eks_access_policy_association.html.markdown b/website/docs/r/eks_access_policy_association.html.markdown index 8f136667faf..82f5b43cfd2 100644 --- a/website/docs/r/eks_access_policy_association.html.markdown +++ b/website/docs/r/eks_access_policy_association.html.markdown @@ -16,7 +16,7 @@ Access Entry Policy Association for an EKS Cluster. resource "aws_eks_access_policy_association" "example" { cluster_name = aws_eks_cluster.example.name policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy" - principal_arn = aws_iam_role.example.arn + principal_arn = aws_iam_user.example.arn access_scope = { type = "namespace" namespaces = ["example-namespace] From fec00bb0095c8ad28669800b51e2c862e301d8ce Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 12:24:39 -0500 Subject: [PATCH 09/38] Update website/docs/r/eks_access_policy_association.html.markdown Co-authored-by: Byungjin Park (Claud) --- website/docs/r/eks_access_policy_association.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/eks_access_policy_association.html.markdown b/website/docs/r/eks_access_policy_association.html.markdown index 82f5b43cfd2..23d95716025 100644 --- a/website/docs/r/eks_access_policy_association.html.markdown +++ b/website/docs/r/eks_access_policy_association.html.markdown @@ -30,7 +30,7 @@ resource "aws_eks_access_policy_association" "example" { The following arguments are required: * `cluster_name` – (Required) Name of the EKS Cluster. Must be between 1-100 characters in length. Must begin with an alphanumeric character, and must only contain alphanumeric characters, dashes and underscores (`^[0-9A-Za-z][A-Za-z0-9\-_]+$`). -* `policy_arn` – (Required) The IAM Policy ARN which is the fine grained access allowed for access. +* `policy_arn` – (Required) The ARN of the access policy that you're associating. * `principal_arn` – (Required) The IAM Princial ARN which requires Authentication access to the EKS cluster. * `access_scope` – (Required) The configuration block to determine the scope of the access. * `type` - (Required) Valid values are `namespace` or `cluster`. From cbb150a358f70e853b3d898107f4e7b3014d41d2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 12:24:53 -0500 Subject: [PATCH 10/38] Update website/docs/d/eks_cluster.html.markdown Co-authored-by: Byungjin Park (Claud) --- website/docs/d/eks_cluster.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/d/eks_cluster.html.markdown b/website/docs/d/eks_cluster.html.markdown index beb09131c51..d0e9ab35250 100644 --- a/website/docs/d/eks_cluster.html.markdown +++ b/website/docs/d/eks_cluster.html.markdown @@ -42,7 +42,7 @@ This data source exports the following attributes in addition to the arguments a * `id` - Name of the cluster * `arn` - ARN of the cluster. * `access_config` - Configuration block for access config. - * `authentication_mode` - Values returned are CONFIGMAP, API or APIANDCONFIGMAP + * `authentication_mode` - Values returned are `CONFIG_MAP`, `API` or `API_AND_CONFIG_MAP` * `bootstrap_cluster_creator_admin_permissions` - Default to `true`. * `certificate_authority` - Nested attribute containing `certificate-authority-data` for your cluster. * `data` - The base64 encoded certificate data required to communicate with your cluster. Add this to the `certificate-authority-data` section of the `kubeconfig` file for your cluster. From 4f375e1c7838bfc20d38f06d90ef885b511fdd60 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 12:25:00 -0500 Subject: [PATCH 11/38] Update website/docs/r/eks_cluster.html.markdown Co-authored-by: Byungjin Park (Claud) --- website/docs/r/eks_cluster.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/eks_cluster.html.markdown b/website/docs/r/eks_cluster.html.markdown index 8349a4d2959..bd821318e35 100644 --- a/website/docs/r/eks_cluster.html.markdown +++ b/website/docs/r/eks_cluster.html.markdown @@ -195,7 +195,7 @@ resource "aws_eks_cluster" "example" { } access_config { - authentication_mode = "CONFIGMAP" + authentication_mode = "CONFIG_MAP" bootstrap_cluster_creator_admin_permissions = true } } From a63c4f041fa1b8221e654771f57cb6067d56cc2a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 12:25:12 -0500 Subject: [PATCH 12/38] Update website/docs/r/eks_cluster.html.markdown Co-authored-by: Byungjin Park (Claud) --- website/docs/r/eks_cluster.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/eks_cluster.html.markdown b/website/docs/r/eks_cluster.html.markdown index bd821318e35..706238d9850 100644 --- a/website/docs/r/eks_cluster.html.markdown +++ b/website/docs/r/eks_cluster.html.markdown @@ -225,7 +225,7 @@ The following arguments are optional: The `access_config` configuration block supports the following arguments: -* `authentication_mode` - (Optional) The authentication mode for the cluster. Valid values are CONFIGMAP, API or APIANDCONFIGMAP +* `authentication_mode` - (Optional) The authentication mode for the cluster. Valid values are `CONFIG_MAP`, `API` or `API_AND_CONFIG_MAP` * `bootstrap_cluster_creator_admin_permissions` - (Optional) Whether or not to bootstrap the access config values to the cluster. Default is `true`. ### encryption_config From b3350b4f8a2c0dd6ef1aaa63a547c5190f3f6abb Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 14:46:37 -0500 Subject: [PATCH 13/38] Fix terrafmt errors in acceptance test configurations. --- internal/service/eks/access_entry_datasource_test.go | 3 ++- internal/service/eks/access_entry_test.go | 3 ++- internal/service/eks/access_policy_association_test.go | 3 ++- internal/service/eks/cluster_test.go | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/internal/service/eks/access_entry_datasource_test.go b/internal/service/eks/access_entry_datasource_test.go index 95753db52cd..bdae5056ef0 100644 --- a/internal/service/eks/access_entry_datasource_test.go +++ b/internal/service/eks/access_entry_datasource_test.go @@ -55,8 +55,9 @@ resource "aws_eks_access_entry" "test" { } data "aws_eks_access_entry" "test" { - cluster_name = aws_eks_cluster.test.name + cluster_name = aws_eks_cluster.test.name principal_arn = aws_iam_user.test.arn + depends_on = [ aws_eks_access_entry.test, aws_eks_cluster.test, diff --git a/internal/service/eks/access_entry_test.go b/internal/service/eks/access_entry_test.go index 9bd57f85d59..e077c5ca3f6 100644 --- a/internal/service/eks/access_entry_test.go +++ b/internal/service/eks/access_entry_test.go @@ -201,8 +201,9 @@ resource "aws_eks_cluster" "test" { vpc_config { subnet_ids = aws_subnet.test[*].id } + access_config { - authentication_mode = "API" + authentication_mode = "API" } depends_on = [aws_iam_role_policy_attachment.test-AmazonEKSClusterPolicy] diff --git a/internal/service/eks/access_policy_association_test.go b/internal/service/eks/access_policy_association_test.go index 0ebebb7ee22..02bcd98b8d7 100644 --- a/internal/service/eks/access_policy_association_test.go +++ b/internal/service/eks/access_policy_association_test.go @@ -202,8 +202,9 @@ resource "aws_eks_cluster" "test" { vpc_config { subnet_ids = aws_subnet.test[*].id } + access_config { - authentication_mode = "API" + authentication_mode = "API" } depends_on = [aws_iam_role_policy_attachment.test-AmazonEKSClusterPolicy] diff --git a/internal/service/eks/cluster_test.go b/internal/service/eks/cluster_test.go index d68b45ac9c9..a7d7b07b36a 100644 --- a/internal/service/eks/cluster_test.go +++ b/internal/service/eks/cluster_test.go @@ -899,7 +899,7 @@ resource "aws_eks_cluster" "test" { access_config { bootstrap_cluster_creator_admin_permissions = true - authentication_mode = "CONFIG_MAP" + authentication_mode = "CONFIG_MAP" } vpc_config { @@ -954,7 +954,7 @@ resource "aws_eks_cluster" "test" { } access_config { - authentication_mode = "CONFIG_MAP" + authentication_mode = "CONFIG_MAP" } depends_on = [aws_iam_role_policy_attachment.test-AmazonEKSClusterPolicy] From 7b6377ac8bb45e871bfe808ea3aa036c3c0ce2ad Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 14:50:16 -0500 Subject: [PATCH 14/38] Fix tfproviderdocs 'file does not end with a valid extension, valid extensions: [.html.markdown .html.md .markdown .md]'. --- ...access_entry.html.markdown => eks_access_entry.html.markdown} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename website/docs/d/{eks_access_entry.html.markdown => eks_access_entry.html.markdown} (100%) diff --git a/website/docs/d/eks_access_entry.html.markdown b/website/docs/d/eks_access_entry.html.markdown similarity index 100% rename from website/docs/d/eks_access_entry.html.markdown rename to website/docs/d/eks_access_entry.html.markdown From 7912fb9c528a248946c970485a11ae9a150d7a77 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 14:51:53 -0500 Subject: [PATCH 15/38] Fix terrafmt errors in documentation. --- website/docs/r/eks_access_entry.html.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/docs/r/eks_access_entry.html.markdown b/website/docs/r/eks_access_entry.html.markdown index 5a2c569ec3c..4849360fa5d 100644 --- a/website/docs/r/eks_access_entry.html.markdown +++ b/website/docs/r/eks_access_entry.html.markdown @@ -14,8 +14,8 @@ Access Entry Configurations for an EKS Cluster. ```terraform resource "aws_eks_access_entry" "example" { - cluster_name = aws_eks_cluster.example.name - principal_arn = aws_iam_role.example.arn + cluster_name = aws_eks_cluster.example.name + principal_arn = aws_iam_role.example.arn kubernetes_groups = ["group-1", "group-2"] } ``` From 0429ed370b48acdf4383dd1b97568249b554ad0e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 14:53:15 -0500 Subject: [PATCH 16/38] Fix importlint errors. --- internal/service/eks/access_entry_test.go | 3 +-- internal/service/eks/access_policy_association_test.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/service/eks/access_entry_test.go b/internal/service/eks/access_entry_test.go index e077c5ca3f6..628eb63e794 100644 --- a/internal/service/eks/access_entry_test.go +++ b/internal/service/eks/access_entry_test.go @@ -14,10 +14,9 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfeks "github.com/hashicorp/terraform-provider-aws/internal/service/eks" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" - - tfeks "github.com/hashicorp/terraform-provider-aws/internal/service/eks" ) func TestAccEKSAccessEntry_basic(t *testing.T) { diff --git a/internal/service/eks/access_policy_association_test.go b/internal/service/eks/access_policy_association_test.go index 02bcd98b8d7..e29879f762e 100644 --- a/internal/service/eks/access_policy_association_test.go +++ b/internal/service/eks/access_policy_association_test.go @@ -14,10 +14,9 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfeks "github.com/hashicorp/terraform-provider-aws/internal/service/eks" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" - - tfeks "github.com/hashicorp/terraform-provider-aws/internal/service/eks" ) func TestAccEKSAccessPolicyAssociation_basic(t *testing.T) { From 372e5734231121a511c6eefe9725f626e89dde7b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 14:54:39 -0500 Subject: [PATCH 17/38] Fix markdownlint 'MD012/no-multiple-blanks Multiple consecutive blank lines [Expected: 1; Actual: 2]'. --- website/docs/r/eks_access_entry.html.markdown | 1 - website/docs/r/eks_access_policy_association.html.markdown | 2 -- 2 files changed, 3 deletions(-) diff --git a/website/docs/r/eks_access_entry.html.markdown b/website/docs/r/eks_access_entry.html.markdown index 4849360fa5d..4a86baf6e8f 100644 --- a/website/docs/r/eks_access_entry.html.markdown +++ b/website/docs/r/eks_access_entry.html.markdown @@ -32,7 +32,6 @@ The following arguments are optional: * `kubernetes_groups` – (Optional) List of string which can optionally specify the Kubernetes groups the user would belong to when creating an access entry. * `tags` - (Optional) Key-value map of resource tags. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. - ## Attribute Reference This resource exports the following attributes in addition to the arguments above: diff --git a/website/docs/r/eks_access_policy_association.html.markdown b/website/docs/r/eks_access_policy_association.html.markdown index 23d95716025..d92b9d76fb9 100644 --- a/website/docs/r/eks_access_policy_association.html.markdown +++ b/website/docs/r/eks_access_policy_association.html.markdown @@ -36,7 +36,6 @@ The following arguments are required: * `type` - (Required) Valid values are `namespace` or `cluster`. * `namespaces` - (Optional) The namespaces to which the access scope applies when type is namespace. - ## Attribute Reference This resource exports the following attributes in addition to the arguments above: @@ -45,7 +44,6 @@ This resource exports the following attributes in addition to the arguments abov * `associated_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the policy was associated. * `modified_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the policy was updated. - ## Timeouts [Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): From 3254b7dffe063180ae19db145ecad708e33053fc Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 14:55:40 -0500 Subject: [PATCH 18/38] Fix markdownlint 'MD047/single-trailing-newline Files should end with a single newline character'. --- website/docs/r/eks_access_entry.html.markdown | 2 +- website/docs/r/eks_access_policy_association.html.markdown | 2 +- website/docs/r/eks_cluster.html.markdown | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docs/r/eks_access_entry.html.markdown b/website/docs/r/eks_access_entry.html.markdown index 4a86baf6e8f..259802f174b 100644 --- a/website/docs/r/eks_access_entry.html.markdown +++ b/website/docs/r/eks_access_entry.html.markdown @@ -66,4 +66,4 @@ Using `terraform import`, import EKS access entry using the `cluster_name` and ` ```console % terraform import aws_eks_access_entry.my_eks_access_entry my_cluster_name:my_principal_arn -``` \ No newline at end of file +``` diff --git a/website/docs/r/eks_access_policy_association.html.markdown b/website/docs/r/eks_access_policy_association.html.markdown index d92b9d76fb9..68a1d8f47c0 100644 --- a/website/docs/r/eks_access_policy_association.html.markdown +++ b/website/docs/r/eks_access_policy_association.html.markdown @@ -67,4 +67,4 @@ Using `terraform import`, import EKS access entry using the `cluster_name` `poli ```console % terraform import aws_eks_access_policy_association.my_eks_access_entry my_cluster_name:my_policy_arn:my_principal_arn -``` \ No newline at end of file +``` diff --git a/website/docs/r/eks_cluster.html.markdown b/website/docs/r/eks_cluster.html.markdown index 706238d9850..cd9ca993bb5 100644 --- a/website/docs/r/eks_cluster.html.markdown +++ b/website/docs/r/eks_cluster.html.markdown @@ -341,4 +341,4 @@ Using `terraform import`, import EKS Clusters using the `name`. For example: ```console % terraform import aws_eks_cluster.my_cluster my_cluster -``` \ No newline at end of file +``` From cc1288d000cb892cbf61ff6eded8c7abc4381d2d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 14:56:46 -0500 Subject: [PATCH 19/38] Fix markdownlint 'MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines', 'MD022/blanks-around-headings/blanks-around-headers Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Above]'. --- website/docs/r/eks_cluster.html.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/website/docs/r/eks_cluster.html.markdown b/website/docs/r/eks_cluster.html.markdown index cd9ca993bb5..a38bff5c2d4 100644 --- a/website/docs/r/eks_cluster.html.markdown +++ b/website/docs/r/eks_cluster.html.markdown @@ -176,6 +176,7 @@ resource "aws_eks_cluster" "example" { } } ``` + ### EKS Cluster with Access Config ```terraform From a2b67933da723349fcfe76db70630ec74f2c11a7 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 15:00:06 -0500 Subject: [PATCH 20/38] Run 'make gen'. --- internal/service/eks/access_entry_datasource.go | 2 +- internal/service/eks/service_package_gen.go | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/service/eks/access_entry_datasource.go b/internal/service/eks/access_entry_datasource.go index 7e9a8aa6945..d986712d4f2 100644 --- a/internal/service/eks/access_entry_datasource.go +++ b/internal/service/eks/access_entry_datasource.go @@ -18,7 +18,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/names" ) -// @SDKDataSource("aws_eks_access_entry") +// @SDKDataSource("aws_eks_access_entry", name="Access Entry") func dataSourceAccessEntry() *schema.Resource { return &schema.Resource{ ReadWithoutTimeout: dataSourceAccessEntryRead, diff --git a/internal/service/eks/service_package_gen.go b/internal/service/eks/service_package_gen.go index a128384fc45..f69c86a6ca9 100644 --- a/internal/service/eks/service_package_gen.go +++ b/internal/service/eks/service_package_gen.go @@ -35,7 +35,9 @@ func (p *servicePackage) SDKDataSources(ctx context.Context) []*types.ServicePac { Factory: dataSourceAccessEntry, TypeName: "aws_eks_access_entry", - }, { + Name: "Access Entry", + }, + { Factory: dataSourceAddon, TypeName: "aws_eks_addon", }, From 9f3dd2eb5fbd4f79b256a633621e682f2b7dd30d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 15:01:59 -0500 Subject: [PATCH 21/38] Fix providerlint 'AWSAT005: avoid hardcoded ARN AWS partitions, use aws_partition data source'. --- internal/service/eks/access_policy_association_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/service/eks/access_policy_association_test.go b/internal/service/eks/access_policy_association_test.go index e29879f762e..9497efd7794 100644 --- a/internal/service/eks/access_policy_association_test.go +++ b/internal/service/eks/access_policy_association_test.go @@ -213,6 +213,8 @@ resource "aws_eks_cluster" "test" { func testAccAccessPolicyAssociationConfig_basic(rName string) string { return acctest.ConfigCompose(testAccAccessPolicyAssociationConfig_base(rName), fmt.Sprintf(` +data "aws_partition" "current" {} + resource "aws_iam_user" "test" { name = %[1]q } @@ -226,7 +228,7 @@ resource "aws_eks_access_entry" "test" { resource "aws_eks_access_policy_association" "test" { cluster_name = aws_eks_cluster.test.name principal_arn = aws_iam_user.test.arn - policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy" + policy_arn = "arn:${data.aws_partition.current.partition}:eks::aws:cluster-access-policy/AmazonEKSViewPolicy" access_scope { type = "cluster" From 671526d19e9e32fb27b9aba21047d7517fefbc0b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 15:10:49 -0500 Subject: [PATCH 22/38] Fix semgrep errors. --- internal/service/eks/access_entry.go | 2 +- .../service/eks/access_policy_association.go | 4 +-- internal/service/eks/cluster.go | 34 ++++++++++--------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/internal/service/eks/access_entry.go b/internal/service/eks/access_entry.go index 48832f36230..bb22dbaaddb 100644 --- a/internal/service/eks/access_entry.go +++ b/internal/service/eks/access_entry.go @@ -194,7 +194,7 @@ func resourceAccessEntryDelete(ctx context.Context, d *schema.ResourceData, meta }) if errs.IsAErrorMessageContains[*types.ResourceNotFoundException](err, "The specified resource could not be found") { - return nil + return diags } if err != nil { diff --git a/internal/service/eks/access_policy_association.go b/internal/service/eks/access_policy_association.go index 52911a8268e..d89b849df87 100644 --- a/internal/service/eks/access_policy_association.go +++ b/internal/service/eks/access_policy_association.go @@ -176,7 +176,7 @@ func resourceAccessPolicyAssociationDelete(ctx context.Context, d *schema.Resour _, err = conn.DisassociateAccessPolicy(ctx, input) if errs.IsA[*types.ResourceNotFoundException](err) { - return nil + return diags } if err != nil { @@ -196,7 +196,7 @@ func expandAccessScope(l []interface{}) *types.AccessScope { accessScope := &types.AccessScope{} if v, ok := m["type"].(string); ok && v != "" { - accessScope.Type = types.AccessScopeType(*aws.String(v)) + accessScope.Type = types.AccessScopeType(v) } if v, ok := m["namespaces"]; ok { diff --git a/internal/service/eks/cluster.go b/internal/service/eks/cluster.go index b70778d8314..3454d86ff4c 100644 --- a/internal/service/eks/cluster.go +++ b/internal/service/eks/cluster.go @@ -403,6 +403,7 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int } func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EKSClient(ctx) cluster, err := findClusterByName(ctx, conn, d.Id()) @@ -410,11 +411,11 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] EKS Cluster (%s) not found, removing from state", d.Id()) d.SetId("") - return nil + return diags } if err != nil { - return diag.Errorf("reading EKS Cluster (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading EKS Cluster (%s): %s", d.Id(), err) } //Can check if accessConfig := &types.CreateAccessConfigRequest{} @@ -423,7 +424,7 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter } if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig, accessConfig.BootstrapClusterCreatorAdminPermissions)); err != nil { - return diag.Errorf("setting access_config: %s", err) + return sdkdiag.AppendErrorf(diags, "setting access_config: %s", err) } /* if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig)); err != nil { @@ -433,7 +434,7 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter d.Set("arn", cluster.Arn) if err := d.Set("certificate_authority", flattenCertificate(cluster.CertificateAuthority)); err != nil { - return diag.Errorf("setting certificate_authority: %s", err) + return sdkdiag.AppendErrorf(diags, "setting certificate_authority: %s", err) } // cluster_id is only relevant for clusters on Outposts. if cluster.OutpostConfig != nil { @@ -441,33 +442,33 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter } d.Set("created_at", aws.ToTime(cluster.CreatedAt).String()) if err := d.Set("enabled_cluster_log_types", flattenLogging(cluster.Logging)); err != nil { - return diag.Errorf("setting enabled_cluster_log_types: %s", err) + return sdkdiag.AppendErrorf(diags, "setting enabled_cluster_log_types: %s", err) } if err := d.Set("encryption_config", flattenEncryptionConfig(cluster.EncryptionConfig)); err != nil { - return diag.Errorf("setting encryption_config: %s", err) + return sdkdiag.AppendErrorf(diags, "setting encryption_config: %s", err) } d.Set("endpoint", cluster.Endpoint) if err := d.Set("identity", flattenIdentity(cluster.Identity)); err != nil { - return diag.Errorf("setting identity: %s", err) + return sdkdiag.AppendErrorf(diags, "setting identity: %s", err) } if err := d.Set("kubernetes_network_config", flattenKubernetesNetworkConfigResponse(cluster.KubernetesNetworkConfig)); err != nil { - return diag.Errorf("setting kubernetes_network_config: %s", err) + return sdkdiag.AppendErrorf(diags, "setting kubernetes_network_config: %s", err) } d.Set("name", cluster.Name) if err := d.Set("outpost_config", flattenOutpostConfigResponse(cluster.OutpostConfig)); err != nil { - return diag.Errorf("setting outpost_config: %s", err) + return sdkdiag.AppendErrorf(diags, "setting outpost_config: %s", err) } d.Set("platform_version", cluster.PlatformVersion) d.Set("role_arn", cluster.RoleArn) d.Set("status", cluster.Status) d.Set("version", cluster.Version) if err := d.Set("vpc_config", flattenVPCConfigResponse(cluster.ResourcesVpcConfig)); err != nil { - return diag.Errorf("setting vpc_config: %s", err) + return sdkdiag.AppendErrorf(diags, "setting vpc_config: %s", err) } setTagsOut(ctx, cluster.Tags) - return nil + return diags } func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { @@ -491,7 +492,7 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int updateID := aws.ToString(output.Update.Id) if _, err := waitClusterUpdateSuccessful(ctx, conn, d.Id(), updateID, d.Timeout(schema.TimeoutUpdate)); err != nil { - return diag.Errorf("waiting for EKS Cluster (%s) version update (%s): %s", d.Id(), updateID, err) + return sdkdiag.AppendErrorf(diags, "waiting for EKS Cluster (%s) version update (%s): %s", d.Id(), updateID, err) } } @@ -504,7 +505,7 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int output, err := conn.UpdateClusterConfig(ctx, input) if err != nil { - return diag.Errorf("updating EKS Cluster (%s) Access config: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "updating EKS Cluster (%s) Access config: %s", d.Id(), err) } updateID := aws.ToString(output.Update.Id) @@ -512,7 +513,7 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int _, err = waitClusterUpdateSuccessful(ctx, conn, d.Id(), updateID, d.Timeout(schema.TimeoutUpdate)) if err != nil { - return diag.Errorf("waiting for EKS Cluster (%s) Access config update (%s): %s", d.Id(), updateID, err) + return sdkdiag.AppendErrorf(diags, "waiting for EKS Cluster (%s) Access config update (%s): %s", d.Id(), updateID, err) } } @@ -823,12 +824,13 @@ func expandAccessConfigForCreate(l []interface{}) *types.CreateAccessConfigReque accessConfigRequest := &types.CreateAccessConfigRequest{} if v, ok := tfMap["authentication_mode"].(string); ok && v != "" { - accessConfigRequest.AuthenticationMode = types.AuthenticationMode(*aws.String(v)) + accessConfigRequest.AuthenticationMode = types.AuthenticationMode(v) } if v, ok := tfMap["bootstrap_cluster_creator_admin_permissions"].(bool); ok { accessConfigRequest.BootstrapClusterCreatorAdminPermissions = aws.Bool(v) } + return accessConfigRequest } @@ -839,7 +841,7 @@ func expandAccessConfigForUpdate(l []interface{}) *types.UpdateAccessConfigReque m := l[0].(map[string]interface{}) accessConfigRequest := &types.UpdateAccessConfigRequest{ - AuthenticationMode: types.AuthenticationMode(*aws.String(m["authentication_mode"].(string))), + AuthenticationMode: types.AuthenticationMode(m["authentication_mode"].(string)), } return accessConfigRequest From 6594cf4d86fe97d03e16836650618eab27b033b8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 15:43:02 -0500 Subject: [PATCH 23/38] r/aws_eks_cluster: Tidy up. --- internal/service/eks/cluster.go | 162 ++++++++++++++++---------------- 1 file changed, 83 insertions(+), 79 deletions(-) diff --git a/internal/service/eks/cluster.go b/internal/service/eks/cluster.go index 3454d86ff4c..ed0125cfbbe 100644 --- a/internal/service/eks/cluster.go +++ b/internal/service/eks/cluster.go @@ -7,7 +7,6 @@ import ( "context" "fmt" "log" - "strconv" "time" "github.com/YakDriver/regexache" @@ -75,17 +74,6 @@ func resourceCluster() *schema.Resource { Type: schema.TypeBool, Optional: true, ForceNew: true, - // bootstrapClusterAdminPermissions is not returned by API, hence comparing old value with value in schema. - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - var bootstrapClusterAdminPermissions bool - if _, ok := d.GetOk("access_config"); ok { - tfMap := d.Get("access_config").([]interface{})[0].(map[string]interface{}) - if v, ok := tfMap["bootstrap_cluster_creator_admin_permissions"].(bool); ok { - bootstrapClusterAdminPermissions = v - } - } - return old == strconv.FormatBool(bootstrapClusterAdminPermissions) - }, }, }, }, @@ -335,21 +323,21 @@ func resourceClusterCreate(ctx context.Context, d *schema.ResourceData, meta int EncryptionConfig: expandEncryptionConfig(d.Get("encryption_config").([]interface{})), Logging: expandLogging(d.Get("enabled_cluster_log_types").(*schema.Set)), Name: aws.String(name), - ResourcesVpcConfig: expandVPCConfigRequestForCreate(d.Get("vpc_config").([]interface{})), + ResourcesVpcConfig: expandVpcConfigRequest(d.Get("vpc_config").([]interface{})), RoleArn: aws.String(d.Get("role_arn").(string)), Tags: getTagsIn(ctx), } - if _, ok := d.GetOk("access_config"); ok { - input.AccessConfig = expandAccessConfigForCreate(d.Get("access_config").([]interface{})) + if v, ok := d.GetOk("access_config"); ok { + input.AccessConfig = expandCreateAccessConfigRequest(v.([]interface{})) } - if _, ok := d.GetOk("kubernetes_network_config"); ok { - input.KubernetesNetworkConfig = expandKubernetesNetworkConfigRequest(d.Get("kubernetes_network_config").([]interface{})) + if v, ok := d.GetOk("kubernetes_network_config"); ok { + input.KubernetesNetworkConfig = expandKubernetesNetworkConfigRequest(v.([]interface{})) } - if _, ok := d.GetOk("outpost_config"); ok { - input.OutpostConfig = expandOutpostConfigRequest(d.Get("outpost_config").([]interface{})) + if v, ok := d.GetOk("outpost_config"); ok { + input.OutpostConfig = expandOutpostConfigRequest(v.([]interface{})) } if v, ok := d.GetOk("version"); ok { @@ -417,21 +405,17 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter if err != nil { return sdkdiag.AppendErrorf(diags, "reading EKS Cluster (%s): %s", d.Id(), err) } - //Can check if - accessConfig := &types.CreateAccessConfigRequest{} + + // bootstrap_cluster_creator_admin_permissions isn't returned from the AWS API. + var bootstrapClusterCreatorAdminPermissions *bool if v, ok := d.GetOk("access_config"); ok { - accessConfig = expandAccessConfigForCreate(v.([]interface{})) + if apiObject := expandCreateAccessConfigRequest(v.([]interface{})); apiObject != nil { + bootstrapClusterCreatorAdminPermissions = apiObject.BootstrapClusterCreatorAdminPermissions + } } - - if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig, accessConfig.BootstrapClusterCreatorAdminPermissions)); err != nil { + if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig, bootstrapClusterCreatorAdminPermissions)); err != nil { return sdkdiag.AppendErrorf(diags, "setting access_config: %s", err) } - /* - if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig)); err != nil { - return diag.Errorf("setting access_config: %s", err) - } - */ - d.Set("arn", cluster.Arn) if err := d.Set("certificate_authority", flattenCertificate(cluster.CertificateAuthority)); err != nil { return sdkdiag.AppendErrorf(diags, "setting certificate_authority: %s", err) @@ -444,7 +428,7 @@ func resourceClusterRead(ctx context.Context, d *schema.ResourceData, meta inter if err := d.Set("enabled_cluster_log_types", flattenLogging(cluster.Logging)); err != nil { return sdkdiag.AppendErrorf(diags, "setting enabled_cluster_log_types: %s", err) } - if err := d.Set("encryption_config", flattenEncryptionConfig(cluster.EncryptionConfig)); err != nil { + if err := d.Set("encryption_config", flattenEncryptionConfigs(cluster.EncryptionConfig)); err != nil { return sdkdiag.AppendErrorf(diags, "setting encryption_config: %s", err) } d.Set("endpoint", cluster.Endpoint) @@ -497,15 +481,14 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int } if d.HasChange("access_config") { - input := &eks.UpdateClusterConfigInput{ - AccessConfig: expandAccessConfigForUpdate(d.Get("access_config").([]interface{})), + AccessConfig: expandUpdateAccessConfigRequest(d.Get("access_config").([]interface{})), } output, err := conn.UpdateClusterConfig(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "updating EKS Cluster (%s) Access config: %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "updating EKS Cluster (%s) access configuration: %s", d.Id(), err) } updateID := aws.ToString(output.Update.Id) @@ -513,7 +496,7 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int _, err = waitClusterUpdateSuccessful(ctx, conn, d.Id(), updateID, d.Timeout(schema.TimeoutUpdate)) if err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for EKS Cluster (%s) Access config update (%s): %s", d.Id(), updateID, err) + return sdkdiag.AppendErrorf(diags, "waiting for EKS Cluster (%s) access configuration update (%s): %s", d.Id(), updateID, err) } } @@ -558,6 +541,7 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int return sdkdiag.AppendErrorf(diags, "waiting for EKS Cluster (%s) logging update (%s): %s", d.Id(), updateID, err) } } + if d.HasChanges("vpc_config.0.endpoint_private_access", "vpc_config.0.endpoint_public_access", "vpc_config.0.public_access_cidrs") { config := &types.VpcConfigRequest{ EndpointPrivateAccess: aws.Bool(d.Get("vpc_config.0.endpoint_private_access").(bool)), @@ -680,22 +664,22 @@ func findClusterByName(ctx context.Context, conn *eks.Client, name string) (*typ return output.Cluster, nil } -func updateVPCConfig(ctx context.Context, conn *eks.Client, name string, config *types.VpcConfigRequest, timeout time.Duration) error { +func updateVPCConfig(ctx context.Context, conn *eks.Client, name string, vpcConfig *types.VpcConfigRequest, timeout time.Duration) error { input := &eks.UpdateClusterConfigInput{ Name: aws.String(name), - ResourcesVpcConfig: config, + ResourcesVpcConfig: vpcConfig, } output, err := conn.UpdateClusterConfig(ctx, input) if err != nil { - return fmt.Errorf("updating EKS Cluster (%s) VPC config: %s", name, err) + return fmt.Errorf("updating EKS Cluster (%s) VPC configuration: %s", name, err) } updateID := aws.ToString(output.Update.Id) if _, err := waitClusterUpdateSuccessful(ctx, conn, name, updateID, timeout); err != nil { - return fmt.Errorf("waiting for EKS Cluster (%s) VPC config update (%s): %s", name, updateID, err) + return fmt.Errorf("waiting for EKS Cluster (%s) VPC configuration update (%s): %s", name, updateID, err) } return nil @@ -814,37 +798,46 @@ func waitClusterUpdateSuccessful(ctx context.Context, conn *eks.Client, name, id return nil, err } -func expandAccessConfigForCreate(l []interface{}) *types.CreateAccessConfigRequest { - tfMap, ok := l[0].(map[string]interface{}) +func expandCreateAccessConfigRequest(tfList []interface{}) *types.CreateAccessConfigRequest { + if len(tfList) == 0 { + return nil + } + tfMap, ok := tfList[0].(map[string]interface{}) if !ok { return nil } - accessConfigRequest := &types.CreateAccessConfigRequest{} + apiObject := &types.CreateAccessConfigRequest{} if v, ok := tfMap["authentication_mode"].(string); ok && v != "" { - accessConfigRequest.AuthenticationMode = types.AuthenticationMode(v) + apiObject.AuthenticationMode = types.AuthenticationMode(v) } if v, ok := tfMap["bootstrap_cluster_creator_admin_permissions"].(bool); ok { - accessConfigRequest.BootstrapClusterCreatorAdminPermissions = aws.Bool(v) + apiObject.BootstrapClusterCreatorAdminPermissions = aws.Bool(v) } - return accessConfigRequest + return apiObject } -func expandAccessConfigForUpdate(l []interface{}) *types.UpdateAccessConfigRequest { - if len(l) == 0 { +func expandUpdateAccessConfigRequest(tfList []interface{}) *types.UpdateAccessConfigRequest { + if len(tfList) == 0 { return nil } - m := l[0].(map[string]interface{}) - accessConfigRequest := &types.UpdateAccessConfigRequest{ - AuthenticationMode: types.AuthenticationMode(m["authentication_mode"].(string)), + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil } - return accessConfigRequest + apiObject := &types.UpdateAccessConfigRequest{} + + if v, ok := tfMap["authentication_mode"].(string); ok && v != "" { + apiObject.AuthenticationMode = types.AuthenticationMode(v) + } + + return apiObject } func expandEncryptionConfig(tfList []interface{}) []types.EncryptionConfig { @@ -856,7 +849,6 @@ func expandEncryptionConfig(tfList []interface{}) []types.EncryptionConfig { for _, tfMapRaw := range tfList { tfMap, ok := tfMapRaw.(map[string]interface{}) - if !ok { continue } @@ -876,8 +868,11 @@ func expandEncryptionConfig(tfList []interface{}) []types.EncryptionConfig { } func expandProvider(tfList []interface{}) *types.Provider { - tfMap, ok := tfList[0].(map[string]interface{}) + if len(tfList) == 0 { + return nil + } + tfMap, ok := tfList[0].(map[string]interface{}) if !ok { return nil } @@ -891,9 +886,12 @@ func expandProvider(tfList []interface{}) *types.Provider { return apiObject } -func expandOutpostConfigRequest(l []interface{}) *types.OutpostConfigRequest { - tfMap, ok := l[0].(map[string]interface{}) +func expandOutpostConfigRequest(tfList []interface{}) *types.OutpostConfigRequest { + if len(tfList) == 0 { + return nil + } + tfMap, ok := tfList[0].(map[string]interface{}) if !ok { return nil } @@ -905,7 +903,7 @@ func expandOutpostConfigRequest(l []interface{}) *types.OutpostConfigRequest { } if v, ok := tfMap["control_plane_placement"].([]interface{}); ok { - outpostConfigRequest.ControlPlanePlacement = expandControlPlanePlacement(v) + outpostConfigRequest.ControlPlanePlacement = expandControlPlanePlacementRequest(v) } if v, ok := tfMap["outpost_arns"].(*schema.Set); ok && v.Len() > 0 { @@ -915,13 +913,12 @@ func expandOutpostConfigRequest(l []interface{}) *types.OutpostConfigRequest { return outpostConfigRequest } -func expandControlPlanePlacement(tfList []interface{}) *types.ControlPlanePlacementRequest { +func expandControlPlanePlacementRequest(tfList []interface{}) *types.ControlPlanePlacementRequest { if len(tfList) == 0 { return nil } tfMap, ok := tfList[0].(map[string]interface{}) - if !ok { return nil } @@ -935,44 +932,50 @@ func expandControlPlanePlacement(tfList []interface{}) *types.ControlPlanePlacem return apiObject } -func expandVPCConfigRequestForCreate(l []interface{}) *types.VpcConfigRequest { - if len(l) == 0 { +func expandVpcConfigRequest(tfList []interface{}) *types.VpcConfigRequest { // nosemgrep:ci.caps5-in-func-name + if len(tfList) == 0 { return nil } - m := l[0].(map[string]interface{}) + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } - vpcConfigRequest := &types.VpcConfigRequest{ - EndpointPrivateAccess: aws.Bool(m["endpoint_private_access"].(bool)), - EndpointPublicAccess: aws.Bool(m["endpoint_public_access"].(bool)), - SecurityGroupIds: flex.ExpandStringValueSet(m["security_group_ids"].(*schema.Set)), - SubnetIds: flex.ExpandStringValueSet(m["subnet_ids"].(*schema.Set)), + apiObject := &types.VpcConfigRequest{ + EndpointPrivateAccess: aws.Bool(tfMap["endpoint_private_access"].(bool)), + EndpointPublicAccess: aws.Bool(tfMap["endpoint_public_access"].(bool)), + SecurityGroupIds: flex.ExpandStringValueSet(tfMap["security_group_ids"].(*schema.Set)), + SubnetIds: flex.ExpandStringValueSet(tfMap["subnet_ids"].(*schema.Set)), } - if v, ok := m["public_access_cidrs"].(*schema.Set); ok && v.Len() > 0 { - vpcConfigRequest.PublicAccessCidrs = flex.ExpandStringValueSet(v) + if v, ok := tfMap["public_access_cidrs"].(*schema.Set); ok && v.Len() > 0 { + apiObject.PublicAccessCidrs = flex.ExpandStringValueSet(v) } - return vpcConfigRequest + return apiObject } func expandKubernetesNetworkConfigRequest(tfList []interface{}) *types.KubernetesNetworkConfigRequest { - tfMap, ok := tfList[0].(map[string]interface{}) + if len(tfList) == 0 { + return nil + } + tfMap, ok := tfList[0].(map[string]interface{}) if !ok { return nil } apiObject := &types.KubernetesNetworkConfigRequest{} - if v, ok := tfMap["service_ipv4_cidr"].(string); ok && v != "" { - apiObject.ServiceIpv4Cidr = aws.String(v) - } - if v, ok := tfMap["ip_family"].(string); ok && v != "" { apiObject.IpFamily = types.IpFamily(v) } + if v, ok := tfMap["service_ipv4_cidr"].(string); ok && v != "" { + apiObject.ServiceIpv4Cidr = aws.String(v) + } + return apiObject } @@ -1030,7 +1033,7 @@ func flattenOIDC(oidc *types.OIDC) []map[string]interface{} { return []map[string]interface{}{m} } -func flattenAccessConfigResponse(apiObject *types.AccessConfigResponse, bootstrap *bool) []interface{} { +func flattenAccessConfigResponse(apiObject *types.AccessConfigResponse, bootstrapClusterCreatorAdminPermissions *bool) []interface{} { if apiObject == nil { return nil } @@ -1038,14 +1041,15 @@ func flattenAccessConfigResponse(apiObject *types.AccessConfigResponse, bootstra tfMap := map[string]interface{}{ "authentication_mode": apiObject.AuthenticationMode, } - if bootstrap != nil { - tfMap["bootstrap_cluster_creator_admin_permissions"] = aws.ToBool(bootstrap) + + if bootstrapClusterCreatorAdminPermissions != nil { + tfMap["bootstrap_cluster_creator_admin_permissions"] = aws.ToBool(bootstrapClusterCreatorAdminPermissions) } return []interface{}{tfMap} } -func flattenEncryptionConfig(apiObjects []types.EncryptionConfig) []interface{} { +func flattenEncryptionConfigs(apiObjects []types.EncryptionConfig) []interface{} { if len(apiObjects) == 0 { return nil } @@ -1076,7 +1080,7 @@ func flattenProvider(apiObject *types.Provider) []interface{} { return []interface{}{tfMap} } -func flattenVPCConfigResponse(vpcConfig *types.VpcConfigResponse) []map[string]interface{} { +func flattenVPCConfigResponse(vpcConfig *types.VpcConfigResponse) []map[string]interface{} { // nosemgrep:ci.caps5-in-func-name if vpcConfig == nil { return []map[string]interface{}{} } From 9a1f0364c39b4ae2f573ed679d42ad9051e7d535 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 15:46:58 -0500 Subject: [PATCH 24/38] d/aws_eks_cluster: Tidy up. --- internal/service/eks/cluster_data_source.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/internal/service/eks/cluster_data_source.go b/internal/service/eks/cluster_data_source.go index 5fd5e44fa70..a3f6d6fc77b 100644 --- a/internal/service/eks/cluster_data_source.go +++ b/internal/service/eks/cluster_data_source.go @@ -29,10 +29,6 @@ func dataSourceCluster() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "bootstrap_cluster_creator_admin_permissions": { - Type: schema.TypeBool, - Computed: true, - }, }, }, }, @@ -219,9 +215,9 @@ func dataSourceClusterRead(ctx context.Context, d *schema.ResourceData, meta int } d.SetId(name) - //if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig)); err != nil { - // return diag.Errorf("setting access_config: %s", err) - //} + if err := d.Set("access_config", flattenAccessConfigResponse(cluster.AccessConfig, nil)); err != nil { + return sdkdiag.AppendErrorf(diags, "setting access_config: %s", err) + } d.Set("arn", cluster.Arn) if err := d.Set("certificate_authority", flattenCertificate(cluster.CertificateAuthority)); err != nil { return sdkdiag.AppendErrorf(diags, "setting certificate_authority: %s", err) From aebc49a9a20f7f2bb9f9592fa7a3a36e89db4f97 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 15:48:25 -0500 Subject: [PATCH 25/38] Fix terrafmt errors in documentation. --- .../r/eks_access_policy_association.html.markdown | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/website/docs/r/eks_access_policy_association.html.markdown b/website/docs/r/eks_access_policy_association.html.markdown index 68a1d8f47c0..c269d5baa17 100644 --- a/website/docs/r/eks_access_policy_association.html.markdown +++ b/website/docs/r/eks_access_policy_association.html.markdown @@ -14,14 +14,14 @@ Access Entry Policy Association for an EKS Cluster. ```terraform resource "aws_eks_access_policy_association" "example" { - cluster_name = aws_eks_cluster.example.name - policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy" - principal_arn = aws_iam_user.example.arn + cluster_name = aws_eks_cluster.example.name + policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy" + principal_arn = aws_iam_user.example.arn + access_scope = { type = "namespace" - namespaces = ["example-namespace] - } - + namespaces = ["example-namespace"] + } } ``` From 7446fed66f58c2d4a92653be6cb26cfc7e1afcf4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 15:49:35 -0500 Subject: [PATCH 26/38] Fix markdownlint 'MD047/single-trailing-newline Files should end with a single newline character'. --- website/docs/d/eks_access_entry.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/d/eks_access_entry.html.markdown b/website/docs/d/eks_access_entry.html.markdown index cc61fab9be9..b19c8149375 100644 --- a/website/docs/d/eks_access_entry.html.markdown +++ b/website/docs/d/eks_access_entry.html.markdown @@ -38,4 +38,4 @@ This data source exports the following attributes in addition to the arguments a * `modified_at` - Date and time in [RFC3339 format](https://tools.ietf.org/html/rfc3339#section-5.8) that the EKS add-on was updated. * `user_name` - Defaults to principal ARN if user is principal else defaults to assume-role/session-name is role is used. * `type` - Defaults to STANDARD which provides the standard workflow. EC2_LINUX, EC2_WINDOWS, FARGATE_LINUX types disallow users to input a username or groups, and prevent associations. -* `tags_all` - (Optional) Key-value map of resource tags, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). \ No newline at end of file +* `tags_all` - (Optional) Key-value map of resource tags, including those inherited from the provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block). From 63ee0612bb7c8fa060a1c478342e98b1d07bc88a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 17:45:55 -0500 Subject: [PATCH 27/38] Fix golangci-lint 'whitespace'. --- internal/service/eks/access_entry_test.go | 1 - internal/service/eks/access_policy_association_test.go | 1 - 2 files changed, 2 deletions(-) diff --git a/internal/service/eks/access_entry_test.go b/internal/service/eks/access_entry_test.go index 628eb63e794..3f80adb2d20 100644 --- a/internal/service/eks/access_entry_test.go +++ b/internal/service/eks/access_entry_test.go @@ -117,7 +117,6 @@ func testAccCheckAccessEntryDestroy(ctx context.Context) resource.TestCheckFunc } func testAccCheckAccessEntryExists(ctx context.Context, name string, accessentry *types.AccessEntry) resource.TestCheckFunc { - return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { diff --git a/internal/service/eks/access_policy_association_test.go b/internal/service/eks/access_policy_association_test.go index 9497efd7794..dd7bda372e7 100644 --- a/internal/service/eks/access_policy_association_test.go +++ b/internal/service/eks/access_policy_association_test.go @@ -120,7 +120,6 @@ func testAccCheckAccessPolicyAssociationDestroy(ctx context.Context) resource.Te } func testAccCheckAccessPolicyAssociationExists(ctx context.Context, name string, associatedaccesspolicy *types.AssociatedAccessPolicy) resource.TestCheckFunc { - return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { From 0e40def87998882f199dfc98cd5b2c28d254b741 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 17:48:56 -0500 Subject: [PATCH 28/38] Fix golangci-lint 'exportloopref'. --- internal/service/eks/access_policy_association.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/service/eks/access_policy_association.go b/internal/service/eks/access_policy_association.go index d89b849df87..32cc9e56784 100644 --- a/internal/service/eks/access_policy_association.go +++ b/internal/service/eks/access_policy_association.go @@ -219,10 +219,10 @@ func flattenAccessScope(apiObject *types.AccessScope) []interface{} { return []interface{}{tfMap} } -func FindAccessPolicyByID(ctx context.Context, conn *eks.Client, clusterName string, principal_arn string, policy_arn string) (*types.AssociatedAccessPolicy, error) { +func FindAccessPolicyByID(ctx context.Context, conn *eks.Client, clusterName, principalARN, policyARN string) (*types.AssociatedAccessPolicy, error) { input := &eks.ListAssociatedAccessPoliciesInput{ ClusterName: aws.String(clusterName), - PrincipalArn: aws.String(principal_arn), + PrincipalArn: aws.String(principalARN), } var result *types.AssociatedAccessPolicy @@ -239,9 +239,10 @@ func FindAccessPolicyByID(ctx context.Context, conn *eks.Client, clusterName str return nil, err } - for _, accessPolicy := range output.AssociatedAccessPolicies { - if aws.ToString(accessPolicy.PolicyArn) == policy_arn { - result = &accessPolicy + for _, v := range output.AssociatedAccessPolicies { + v := v + if aws.ToString(v.PolicyArn) == policyARN { + result = &v } } From e1486331f6e7a8f454995856cf0c74e8b6b93b13 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 17:49:24 -0500 Subject: [PATCH 29/38] r/aws_eks_cluster: Remove 'MinItems' from 'access_config'. --- internal/service/eks/cluster.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/service/eks/cluster.go b/internal/service/eks/cluster.go index ed0125cfbbe..b696aa39146 100644 --- a/internal/service/eks/cluster.go +++ b/internal/service/eks/cluster.go @@ -61,7 +61,6 @@ func resourceCluster() *schema.Resource { "access_config": { Type: schema.TypeList, MaxItems: 1, - MinItems: 1, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ From b4501e0ca040a36313465549258ada6ae78a89d0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 17:50:10 -0500 Subject: [PATCH 30/38] Check 'access_config' in 'TestAccEKSClusterDataSource_basic'. --- internal/service/eks/cluster_data_source_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/service/eks/cluster_data_source_test.go b/internal/service/eks/cluster_data_source_test.go index 3f4a82aba5c..842f29e3bc2 100644 --- a/internal/service/eks/cluster_data_source_test.go +++ b/internal/service/eks/cluster_data_source_test.go @@ -27,10 +27,12 @@ func TestAccEKSClusterDataSource_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccClusterDataSourceConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "access_config.#", dataSourceResourceName, "access_config.#"), resource.TestCheckResourceAttrPair(resourceName, "arn", dataSourceResourceName, "arn"), resource.TestCheckResourceAttr(dataSourceResourceName, "certificate_authority.#", "1"), resource.TestCheckResourceAttrPair(resourceName, "certificate_authority.0.data", dataSourceResourceName, "certificate_authority.0.data"), + resource.TestCheckResourceAttrPair(resourceName, "id", dataSourceResourceName, "cluster_id"), resource.TestCheckResourceAttrPair(resourceName, "created_at", dataSourceResourceName, "created_at"), resource.TestCheckResourceAttr(dataSourceResourceName, "enabled_cluster_log_types.#", "2"), resource.TestCheckTypeSetElemAttr(dataSourceResourceName, "enabled_cluster_log_types.*", "api"), @@ -43,6 +45,7 @@ func TestAccEKSClusterDataSource_basic(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "kubernetes_network_config.0.ip_family", dataSourceResourceName, "kubernetes_network_config.0.ip_family"), resource.TestCheckResourceAttrPair(resourceName, "kubernetes_network_config.0.service_ipv4_cidr", dataSourceResourceName, "kubernetes_network_config.0.service_ipv4_cidr"), resource.TestCheckResourceAttrPair(resourceName, "kubernetes_network_config.0.service_ipv6_cidr", dataSourceResourceName, "kubernetes_network_config.0.service_ipv6_cidr"), + resource.TestCheckResourceAttrPair(resourceName, "outpost_config.#", dataSourceResourceName, "outpost_config.#"), resource.TestMatchResourceAttr(dataSourceResourceName, "platform_version", regexache.MustCompile(`^eks\.\d+$`)), resource.TestCheckResourceAttrPair(resourceName, "role_arn", dataSourceResourceName, "role_arn"), resource.TestCheckResourceAttrPair(resourceName, "status", dataSourceResourceName, "status"), From e9431c567ca0c4e3d1653b37bec6685e23aebf53 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 17:50:43 -0500 Subject: [PATCH 31/38] r/aws_eks_cluster: Additional 'access_config' checks. --- internal/service/eks/cluster_test.go | 92 +++++++++++++++---- .../service/eks/clusters_data_source_test.go | 2 +- 2 files changed, 74 insertions(+), 20 deletions(-) diff --git a/internal/service/eks/cluster_test.go b/internal/service/eks/cluster_test.go index a7d7b07b36a..a28f7cd4841 100644 --- a/internal/service/eks/cluster_test.go +++ b/internal/service/eks/cluster_test.go @@ -42,21 +42,26 @@ func TestAccEKSCluster_basic(t *testing.T) { CheckDestroy: testAccCheckClusterDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccClusterConfig_required(rName), - Check: resource.ComposeTestCheckFunc( + Config: testAccClusterConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckClusterExists(ctx, resourceName, &cluster), + resource.TestCheckResourceAttr(resourceName, "access_config.#", "0"), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "eks", regexache.MustCompile(fmt.Sprintf("cluster/%s$", rName))), resource.TestCheckResourceAttr(resourceName, "certificate_authority.#", "1"), resource.TestCheckResourceAttrSet(resourceName, "certificate_authority.0.data"), resource.TestCheckNoResourceAttr(resourceName, "cluster_id"), + resource.TestCheckResourceAttrSet(resourceName, "created_at"), + resource.TestCheckResourceAttr(resourceName, "enabled_cluster_log_types.#", "0"), + resource.TestCheckResourceAttr(resourceName, "encryption_config.#", "0"), resource.TestMatchResourceAttr(resourceName, "endpoint", regexache.MustCompile(`^https://`)), resource.TestCheckResourceAttr(resourceName, "identity.#", "1"), resource.TestCheckResourceAttr(resourceName, "identity.0.oidc.#", "1"), resource.TestMatchResourceAttr(resourceName, "identity.0.oidc.0.issuer", regexache.MustCompile(`^https://`)), - resource.TestCheckResourceAttr(resourceName, "name", rName), resource.TestCheckResourceAttr(resourceName, "kubernetes_network_config.#", "1"), resource.TestCheckResourceAttrSet(resourceName, "kubernetes_network_config.0.service_ipv4_cidr"), resource.TestCheckResourceAttr(resourceName, "kubernetes_network_config.0.ip_family", "ipv4"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "outpost_config.#", "0"), resource.TestMatchResourceAttr(resourceName, "platform_version", regexache.MustCompile(`^eks\.\d+$`)), resource.TestCheckResourceAttrPair(resourceName, "role_arn", "aws_iam_role.test", "arn"), resource.TestCheckResourceAttr(resourceName, "status", string(types.ClusterStatusActive)), @@ -79,6 +84,30 @@ func TestAccEKSCluster_basic(t *testing.T) { }) } +func TestAccEKSCluster_disappears(t *testing.T) { + ctx := acctest.Context(t) + var cluster types.Cluster + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_cluster.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckClusterDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccClusterConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterExists(ctx, resourceName, &cluster), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfeks.ResourceCluster(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccEKSCluster_AccessConfig_create(t *testing.T) { ctx := acctest.Context(t) var cluster types.Cluster @@ -92,11 +121,12 @@ func TestAccEKSCluster_AccessConfig_create(t *testing.T) { CheckDestroy: testAccCheckClusterDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccClusterConfig_accessConfig(rName), + Config: testAccClusterConfig_accessConfig(rName, types.AuthenticationModeConfigMap), Check: resource.ComposeTestCheckFunc( testAccCheckClusterExists(ctx, resourceName, &cluster), - //resource.TestMatchResourceAttr(resourceName, "cluster_id", regexp.MustCompile(`^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$`)), resource.TestCheckResourceAttr(resourceName, "access_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "access_config.0.authentication_mode", string(types.AuthenticationModeConfigMap)), + resource.TestCheckResourceAttr(resourceName, "access_config.0.bootstrap_cluster_creator_admin_permissions", "true"), ), }, { @@ -108,7 +138,7 @@ func TestAccEKSCluster_AccessConfig_create(t *testing.T) { }) } -func TestAccEKSCluster_disappears(t *testing.T) { +func TestAccEKSCluster_AccessConfig_update(t *testing.T) { ctx := acctest.Context(t) var cluster types.Cluster rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -121,12 +151,36 @@ func TestAccEKSCluster_disappears(t *testing.T) { CheckDestroy: testAccCheckClusterDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccClusterConfig_required(rName), + Config: testAccClusterConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterExists(ctx, resourceName, &cluster), - acctest.CheckResourceDisappears(ctx, acctest.Provider, tfeks.ResourceCluster(), resourceName), + resource.TestCheckResourceAttr(resourceName, "access_config.#", "0"), + ), + }, + { + Config: testAccClusterConfig_accessConfig(rName, types.AuthenticationModeConfigMap), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterExists(ctx, resourceName, &cluster), + resource.TestCheckResourceAttr(resourceName, "access_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "access_config.0.authentication_mode", string(types.AuthenticationModeConfigMap)), + resource.TestCheckResourceAttr(resourceName, "access_config.0.bootstrap_cluster_creator_admin_permissions", "true"), + ), + }, + { + Config: testAccClusterConfig_accessConfig(rName, types.AuthenticationModeApiAndConfigMap), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterExists(ctx, resourceName, &cluster), + resource.TestCheckResourceAttr(resourceName, "access_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "access_config.0.authentication_mode", string(types.AuthenticationModeApiAndConfigMap)), + resource.TestCheckResourceAttr(resourceName, "access_config.0.bootstrap_cluster_creator_admin_permissions", "true"), + ), + }, + { + Config: testAccClusterConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckClusterExists(ctx, resourceName, &cluster), + resource.TestCheckResourceAttr(resourceName, "access_config.#", "0"), ), - ExpectNonEmptyPlan: true, }, }, }) @@ -178,7 +232,7 @@ func TestAccEKSCluster_Encryption_update(t *testing.T) { CheckDestroy: testAccCheckClusterDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccClusterConfig_required(rName), + Config: testAccClusterConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterExists(ctx, resourceName, &cluster1), resource.TestCheckResourceAttr(resourceName, "encryption_config.#", "0"), @@ -323,7 +377,7 @@ func TestAccEKSCluster_logging(t *testing.T) { }, // Disable all log types. { - Config: testAccClusterConfig_required(rName), + Config: testAccClusterConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterExists(ctx, resourceName, &cluster2), testAccCheckClusterNotRecreated(&cluster1, &cluster2), @@ -891,17 +945,12 @@ resource "aws_subnet" "test" { `, rName)) } -func testAccClusterConfig_accessConfig(rName string) string { +func testAccClusterConfig_basic(rName string) string { return acctest.ConfigCompose(testAccClusterConfig_base(rName), fmt.Sprintf(` resource "aws_eks_cluster" "test" { name = %[1]q role_arn = aws_iam_role.test.arn - access_config { - bootstrap_cluster_creator_admin_permissions = true - authentication_mode = "CONFIG_MAP" - } - vpc_config { subnet_ids = aws_subnet.test[*].id } @@ -911,19 +960,24 @@ resource "aws_eks_cluster" "test" { `, rName)) } -func testAccClusterConfig_required(rName string) string { +func testAccClusterConfig_accessConfig(rName string, authenticationMode types.AuthenticationMode) string { return acctest.ConfigCompose(testAccClusterConfig_base(rName), fmt.Sprintf(` resource "aws_eks_cluster" "test" { name = %[1]q role_arn = aws_iam_role.test.arn + access_config { + bootstrap_cluster_creator_admin_permissions = true + authentication_mode = %[2]q + } + vpc_config { subnet_ids = aws_subnet.test[*].id } depends_on = [aws_iam_role_policy_attachment.test-AmazonEKSClusterPolicy] } -`, rName)) +`, rName, authenticationMode)) } func testAccClusterConfig_version(rName, version string) string { diff --git a/internal/service/eks/clusters_data_source_test.go b/internal/service/eks/clusters_data_source_test.go index d4d02357276..043abb4f88c 100644 --- a/internal/service/eks/clusters_data_source_test.go +++ b/internal/service/eks/clusters_data_source_test.go @@ -34,7 +34,7 @@ func TestAccEKSClustersDataSource_basic(t *testing.T) { } func testAccClustersDataSourceConfig_basic(rName string) string { - return acctest.ConfigCompose(testAccClusterConfig_required(rName), ` + return acctest.ConfigCompose(testAccClusterConfig_basic(rName), ` data "aws_eks_clusters" "test" { depends_on = [aws_eks_cluster.test] } From d3ac1ad9fc4688c513f450cd3fe6d2a24b443bbf Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 17 Jan 2024 18:23:57 -0500 Subject: [PATCH 32/38] Remove 'access_config' from acceptance test configuration. --- internal/service/eks/cluster_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/service/eks/cluster_test.go b/internal/service/eks/cluster_test.go index a28f7cd4841..34c9aabedf5 100644 --- a/internal/service/eks/cluster_test.go +++ b/internal/service/eks/cluster_test.go @@ -1007,10 +1007,6 @@ resource "aws_eks_cluster" "test" { subnet_ids = aws_subnet.test[*].id } - access_config { - authentication_mode = "CONFIG_MAP" - } - depends_on = [aws_iam_role_policy_attachment.test-AmazonEKSClusterPolicy] } `, rName, strings.Join(logTypes, "\", \""))) From 21ff1a8676071668ab74b3d31027e530ad7c4e67 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Jan 2024 05:46:58 -0500 Subject: [PATCH 33/38] Fix terrafmt error. --- website/docs/r/eks_access_policy_association.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/eks_access_policy_association.html.markdown b/website/docs/r/eks_access_policy_association.html.markdown index c269d5baa17..1095c965da3 100644 --- a/website/docs/r/eks_access_policy_association.html.markdown +++ b/website/docs/r/eks_access_policy_association.html.markdown @@ -21,7 +21,7 @@ resource "aws_eks_access_policy_association" "example" { access_scope = { type = "namespace" namespaces = ["example-namespace"] - } + } } ``` From bced00395148a9cc4d3e8550307fa8f7cf8e285f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Jan 2024 07:38:57 -0500 Subject: [PATCH 34/38] r/aws_eks_cluster: 'access_config' is Computed. --- internal/service/eks/cluster.go | 27 +++++++++++-------- .../service/eks/cluster_data_source_test.go | 2 +- internal/service/eks/cluster_test.go | 6 ++--- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/internal/service/eks/cluster.go b/internal/service/eks/cluster.go index b696aa39146..78dc48f8177 100644 --- a/internal/service/eks/cluster.go +++ b/internal/service/eks/cluster.go @@ -62,11 +62,13 @@ func resourceCluster() *schema.Resource { Type: schema.TypeList, MaxItems: 1, Optional: true, + Computed: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "authentication_mode": { Type: schema.TypeString, Optional: true, + Computed: true, ValidateDiagFunc: enum.Validate[types.AuthenticationMode](), }, "bootstrap_cluster_creator_admin_permissions": { @@ -480,22 +482,25 @@ func resourceClusterUpdate(ctx context.Context, d *schema.ResourceData, meta int } if d.HasChange("access_config") { - input := &eks.UpdateClusterConfigInput{ - AccessConfig: expandUpdateAccessConfigRequest(d.Get("access_config").([]interface{})), - } + if v, ok := d.GetOk("access_config"); ok { + input := &eks.UpdateClusterConfigInput{ + AccessConfig: expandUpdateAccessConfigRequest(v.([]interface{})), + Name: aws.String(d.Id()), + } - output, err := conn.UpdateClusterConfig(ctx, input) + output, err := conn.UpdateClusterConfig(ctx, input) - if err != nil { - return sdkdiag.AppendErrorf(diags, "updating EKS Cluster (%s) access configuration: %s", d.Id(), err) - } + if err != nil { + return sdkdiag.AppendErrorf(diags, "updating EKS Cluster (%s) access configuration: %s", d.Id(), err) + } - updateID := aws.ToString(output.Update.Id) + updateID := aws.ToString(output.Update.Id) - _, err = waitClusterUpdateSuccessful(ctx, conn, d.Id(), updateID, d.Timeout(schema.TimeoutUpdate)) + _, err = waitClusterUpdateSuccessful(ctx, conn, d.Id(), updateID, d.Timeout(schema.TimeoutUpdate)) - if err != nil { - return sdkdiag.AppendErrorf(diags, "waiting for EKS Cluster (%s) access configuration update (%s): %s", d.Id(), updateID, err) + if err != nil { + return sdkdiag.AppendErrorf(diags, "waiting for EKS Cluster (%s) access configuration update (%s): %s", d.Id(), updateID, err) + } } } diff --git a/internal/service/eks/cluster_data_source_test.go b/internal/service/eks/cluster_data_source_test.go index 842f29e3bc2..d06879bad63 100644 --- a/internal/service/eks/cluster_data_source_test.go +++ b/internal/service/eks/cluster_data_source_test.go @@ -32,7 +32,7 @@ func TestAccEKSClusterDataSource_basic(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "arn", dataSourceResourceName, "arn"), resource.TestCheckResourceAttr(dataSourceResourceName, "certificate_authority.#", "1"), resource.TestCheckResourceAttrPair(resourceName, "certificate_authority.0.data", dataSourceResourceName, "certificate_authority.0.data"), - resource.TestCheckResourceAttrPair(resourceName, "id", dataSourceResourceName, "cluster_id"), + resource.TestCheckNoResourceAttr(dataSourceResourceName, "cluster_id"), resource.TestCheckResourceAttrPair(resourceName, "created_at", dataSourceResourceName, "created_at"), resource.TestCheckResourceAttr(dataSourceResourceName, "enabled_cluster_log_types.#", "2"), resource.TestCheckTypeSetElemAttr(dataSourceResourceName, "enabled_cluster_log_types.*", "api"), diff --git a/internal/service/eks/cluster_test.go b/internal/service/eks/cluster_test.go index 34c9aabedf5..0d35ee7010a 100644 --- a/internal/service/eks/cluster_test.go +++ b/internal/service/eks/cluster_test.go @@ -45,7 +45,7 @@ func TestAccEKSCluster_basic(t *testing.T) { Config: testAccClusterConfig_basic(rName), Check: resource.ComposeAggregateTestCheckFunc( testAccCheckClusterExists(ctx, resourceName, &cluster), - resource.TestCheckResourceAttr(resourceName, "access_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "access_config.#", "1"), acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "eks", regexache.MustCompile(fmt.Sprintf("cluster/%s$", rName))), resource.TestCheckResourceAttr(resourceName, "certificate_authority.#", "1"), resource.TestCheckResourceAttrSet(resourceName, "certificate_authority.0.data"), @@ -154,7 +154,7 @@ func TestAccEKSCluster_AccessConfig_update(t *testing.T) { Config: testAccClusterConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterExists(ctx, resourceName, &cluster), - resource.TestCheckResourceAttr(resourceName, "access_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "access_config.#", "1"), ), }, { @@ -179,7 +179,7 @@ func TestAccEKSCluster_AccessConfig_update(t *testing.T) { Config: testAccClusterConfig_basic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClusterExists(ctx, resourceName, &cluster), - resource.TestCheckResourceAttr(resourceName, "access_config.#", "0"), + resource.TestCheckResourceAttr(resourceName, "access_config.#", "1"), ), }, }, From 7c192002582cdce9d44443b11454670598102c9a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Jan 2024 07:39:09 -0500 Subject: [PATCH 35/38] Acceptance test output: % make testacc TESTARGS='-run=TestAccEKSCluster_AccessConfig\|TestAccEKSCluster_basic\|TestAccEKSClusterDataSource_basic' PKG=eks ACCTEST_PARALLELISM=2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/eks/... -v -count 1 -parallel 2 -run=TestAccEKSCluster_AccessConfig\|TestAccEKSCluster_basic\|TestAccEKSClusterDataSource_basic -timeout 360m === RUN TestAccEKSClusterDataSource_basic === PAUSE TestAccEKSClusterDataSource_basic === RUN TestAccEKSCluster_basic === PAUSE TestAccEKSCluster_basic === RUN TestAccEKSCluster_AccessConfig_create === PAUSE TestAccEKSCluster_AccessConfig_create === RUN TestAccEKSCluster_AccessConfig_update === PAUSE TestAccEKSCluster_AccessConfig_update === CONT TestAccEKSClusterDataSource_basic === CONT TestAccEKSCluster_AccessConfig_create --- PASS: TestAccEKSClusterDataSource_basic (647.09s) === CONT TestAccEKSCluster_AccessConfig_update --- PASS: TestAccEKSCluster_AccessConfig_create (670.26s) === CONT TestAccEKSCluster_basic --- PASS: TestAccEKSCluster_basic (619.94s) --- PASS: TestAccEKSCluster_AccessConfig_update (1495.46s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/eks 1916.223s From 955f816c1dfb0d854ae06a66b97e72c7773bdd57 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Jan 2024 08:06:43 -0500 Subject: [PATCH 36/38] aws_eks_access_entry: Tidy up and additional tests. --- internal/service/eks/access_entry.go | 109 +++++++++++----- .../service/eks/access_entry_datasource.go | 21 ++- .../eks/access_entry_datasource_test.go | 12 +- internal/service/eks/access_entry_test.go | 120 +++++++++++++++--- internal/service/eks/exports_test.go | 1 + internal/service/eks/find.go | 29 ----- internal/service/eks/id.go | 19 --- internal/service/eks/service_package_gen.go | 2 +- 8 files changed, 197 insertions(+), 116 deletions(-) diff --git a/internal/service/eks/access_entry.go b/internal/service/eks/access_entry.go index bb22dbaaddb..8d5a9838029 100644 --- a/internal/service/eks/access_entry.go +++ b/internal/service/eks/access_entry.go @@ -5,13 +5,16 @@ package eks import ( "context" + "fmt" "log" + "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/eks" "github.com/aws/aws-sdk-go-v2/service/eks/types" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs" @@ -24,13 +27,14 @@ import ( ) // @SDKResource("aws_eks_access_entry", name="Access Entry") -// @Tags(identifierAttribute="arn") +// @Tags(identifierAttribute="access_entry_arn") func resourceAccessEntry() *schema.Resource { return &schema.Resource{ CreateWithoutTimeout: resourceAccessEntryCreate, ReadWithoutTimeout: resourceAccessEntryRead, UpdateWithoutTimeout: resourceAccessEntryUpdate, DeleteWithoutTimeout: resourceAccessEntryDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -74,16 +78,16 @@ func resourceAccessEntry() *schema.Resource { ForceNew: true, ValidateFunc: verify.ValidARN, }, - "user_name": { + names.AttrTags: tftags.TagsSchema(), + names.AttrTagsAll: tftags.TagsSchemaComputed(), + "type": { Type: schema.TypeString, Computed: true, }, - "type": { + "user_name": { Type: schema.TypeString, Computed: true, }, - names.AttrTags: tftags.TagsSchema(), - names.AttrTagsAll: tftags.TagsSchemaComputed(), }, } } @@ -93,11 +97,11 @@ func resourceAccessEntryCreate(ctx context.Context, d *schema.ResourceData, meta conn := meta.(*conns.AWSClient).EKSClient(ctx) clusterName := d.Get("cluster_name").(string) - principal_arn := d.Get("principal_arn").(string) - accessID := AccessEntryCreateResourceID(clusterName, principal_arn) + principalARN := d.Get("principal_arn").(string) + id := accessEntryCreateResourceID(clusterName, principalARN) input := &eks.CreateAccessEntryInput{ ClusterName: aws.String(clusterName), - PrincipalArn: aws.String(principal_arn), + PrincipalArn: aws.String(principalARN), Tags: getTagsIn(ctx), } @@ -106,11 +110,12 @@ func resourceAccessEntryCreate(ctx context.Context, d *schema.ResourceData, meta } _, err := conn.CreateAccessEntry(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating EKS Access Config: %s", err) + return sdkdiag.AppendErrorf(diags, "creating EKS Access Entry (%s): %s", id, err) } - d.SetId(accessID) + d.SetId(id) return append(diags, resourceAccessEntryRead(ctx, d, meta)...) } @@ -119,11 +124,12 @@ func resourceAccessEntryRead(ctx context.Context, d *schema.ResourceData, meta i var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EKSClient(ctx) - clusterName, principal_arn, err := AccessEntryParseResourceID(d.Id()) + clusterName, principalARN, err := accessEntryParseResourceID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading EKS Access Entry (%s): %s", d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } - output, err := FindAccessEntryByID(ctx, conn, clusterName, principal_arn) + + output, err := findAccessEntryByTwoPartKey(ctx, conn, clusterName, principalARN) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] EKS Access Entry (%s) not found, removing from state", d.Id()) @@ -132,17 +138,17 @@ func resourceAccessEntryRead(ctx context.Context, d *schema.ResourceData, meta i } if err != nil { - return sdkdiag.AppendErrorf(diags, "reading EKS EKS Access Entry (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "reading EKS Access Entry (%s): %s", d.Id(), err) } d.Set("access_entry_arn", output.AccessEntryArn) d.Set("cluster_name", output.ClusterName) - d.Set("created_at", aws.ToTime(output.CreatedAt).String()) + d.Set("created_at", aws.ToTime(output.CreatedAt).Format(time.RFC3339)) d.Set("kubernetes_groups", output.KubernetesGroups) - d.Set("modified_at", aws.ToTime(output.ModifiedAt).String()) + d.Set("modified_at", aws.ToTime(output.ModifiedAt).Format(time.RFC3339)) d.Set("principal_arn", output.PrincipalArn) - d.Set("user_name", output.Username) d.Set("type", output.Type) + d.Set("user_name", output.Username) setTagsOut(ctx, output.Tags) @@ -152,25 +158,26 @@ func resourceAccessEntryRead(ctx context.Context, d *schema.ResourceData, meta i func resourceAccessEntryUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EKSClient(ctx) - clusterName, principal_arn, err := AccessEntryParseResourceID(d.Id()) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } if d.HasChangesExcept("tags", "tags_all") { + clusterName, principalARN, err := accessEntryParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + input := &eks.UpdateAccessEntryInput{ ClusterName: aws.String(clusterName), - PrincipalArn: aws.String(principal_arn), + PrincipalArn: aws.String(principalARN), } if d.HasChange("kubernetes_groups") { input.KubernetesGroups = flex.ExpandStringValueSet(d.Get("kubernetes_groups").(*schema.Set)) } - _, err := conn.UpdateAccessEntry(ctx, input) + _, err = conn.UpdateAccessEntry(ctx, input) if err != nil { - return sdkdiag.AppendErrorf(diags, "updating Access Entry (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "updating EKS Access Entry (%s): %s", d.Id(), err) } } @@ -181,19 +188,18 @@ func resourceAccessEntryDelete(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EKSClient(ctx) - clusterName, principal_arn, err := AccessEntryParseResourceID(d.Id()) - + clusterName, principalARN, err := accessEntryParseResourceID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting EKS Access Entry (%s): %s", d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } log.Printf("[DEBUG] Deleting EKS Access Entry: %s", d.Id()) _, err = conn.DeleteAccessEntry(ctx, &eks.DeleteAccessEntryInput{ ClusterName: aws.String(clusterName), - PrincipalArn: aws.String(principal_arn), + PrincipalArn: aws.String(principalARN), }) - if errs.IsAErrorMessageContains[*types.ResourceNotFoundException](err, "The specified resource could not be found") { + if errs.IsA[*types.ResourceNotFoundException](err) { return diags } @@ -203,3 +209,48 @@ func resourceAccessEntryDelete(ctx context.Context, d *schema.ResourceData, meta return diags } + +const accessEntryResourceIDSeparator = ":" + +func accessEntryCreateResourceID(clusterName, principal_arn string) string { + parts := []string{clusterName, principal_arn} + id := strings.Join(parts, accessEntryResourceIDSeparator) + + return id +} + +func accessEntryParseResourceID(id string) (string, string, error) { + parts := strings.SplitN(id, accessEntryResourceIDSeparator, 2) + + if len(parts) == 2 && parts[0] != "" && parts[1] != "" { + return parts[0], parts[1], nil + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]sprincipal-arn", id, accessEntryResourceIDSeparator) +} + +func findAccessEntryByTwoPartKey(ctx context.Context, conn *eks.Client, clusterName, principalARN string) (*types.AccessEntry, error) { + input := &eks.DescribeAccessEntryInput{ + ClusterName: aws.String(clusterName), + PrincipalArn: aws.String(principalARN), + } + + output, err := conn.DescribeAccessEntry(ctx, input) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.AccessEntry == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.AccessEntry, nil +} diff --git a/internal/service/eks/access_entry_datasource.go b/internal/service/eks/access_entry_datasource.go index d986712d4f2..907222be6cb 100644 --- a/internal/service/eks/access_entry_datasource.go +++ b/internal/service/eks/access_entry_datasource.go @@ -5,7 +5,7 @@ package eks import ( "context" - "log" + "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" @@ -13,7 +13,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -71,28 +70,24 @@ func dataSourceAccessEntryRead(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EKSClient(ctx) - principalArn := d.Get("principal_arn").(string) clusterName := d.Get("cluster_name").(string) - id := AccessEntryCreateResourceID(clusterName, principalArn) - output, err := FindAccessEntryByID(ctx, conn, clusterName, principalArn) - - if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] EKS Access Entry (%s) not found, removing from state", id) - return diags - } + principalARN := d.Get("principal_arn").(string) + id := accessEntryCreateResourceID(clusterName, principalARN) + output, err := findAccessEntryByTwoPartKey(ctx, conn, clusterName, principalARN) if err != nil { return sdkdiag.AppendErrorf(diags, "reading EKS Access Entry (%s): %s", id, err) } + d.SetId(id) d.Set("access_entry_arn", output.AccessEntryArn) d.Set("cluster_name", output.ClusterName) - d.Set("created_at", aws.ToTime(output.CreatedAt).String()) + d.Set("created_at", aws.ToTime(output.CreatedAt).Format(time.RFC3339)) d.Set("kubernetes_groups", output.KubernetesGroups) - d.Set("modified_at", aws.ToTime(output.ModifiedAt).String()) + d.Set("modified_at", aws.ToTime(output.ModifiedAt).Format(time.RFC3339)) d.Set("principal_arn", output.PrincipalArn) - d.Set("user_name", output.Username) d.Set("type", output.Type) + d.Set("user_name", output.Username) setTagsOut(ctx, output.Tags) diff --git a/internal/service/eks/access_entry_datasource_test.go b/internal/service/eks/access_entry_datasource_test.go index bdae5056ef0..e60e1be9a8c 100644 --- a/internal/service/eks/access_entry_datasource_test.go +++ b/internal/service/eks/access_entry_datasource_test.go @@ -23,20 +23,18 @@ func TestAccEKSAccessEntryDataSource_basic(t *testing.T) { PreCheck: func() { acctest.PreCheck(ctx, t); testAccPreCheck(ctx, t); testAccPreCheckAddon(ctx, t) }, ErrorCheck: acctest.ErrorCheck(t, eks.EndpointsID), ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, - CheckDestroy: testAccCheckAddonDestroy(ctx), Steps: []resource.TestStep{ { Config: testAccAccessEntryDataSourceConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(resourceName, "cluster_name", dataSourceResourceName, "cluster_name"), + Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPair(resourceName, "access_entry_arn", dataSourceResourceName, "access_entry_arn"), - resource.TestCheckResourceAttrPair(resourceName, "kubernetes_groups", dataSourceResourceName, "kubernetes_groups"), + resource.TestCheckResourceAttrPair(resourceName, "cluster_name", dataSourceResourceName, "cluster_name"), resource.TestCheckResourceAttrPair(resourceName, "created_at", dataSourceResourceName, "created_at"), - resource.TestCheckResourceAttrPair(resourceName, "modified_at", dataSourceResourceName, "modified_at"), + resource.TestCheckResourceAttrPair(resourceName, "kubernetes_groups.#", dataSourceResourceName, "kubernetes_groups.#"), resource.TestCheckResourceAttrPair(resourceName, "principal_arn", dataSourceResourceName, "principal_arn"), - resource.TestCheckResourceAttrPair(resourceName, "user_name", dataSourceResourceName, "user_name"), - resource.TestCheckResourceAttrPair(resourceName, "type", dataSourceResourceName, "type"), resource.TestCheckResourceAttrPair(resourceName, "tags.%", dataSourceResourceName, "tags.%"), + resource.TestCheckResourceAttrPair(resourceName, "type", dataSourceResourceName, "type"), + resource.TestCheckResourceAttrPair(resourceName, "user_name", dataSourceResourceName, "user_name"), ), }, }, diff --git a/internal/service/eks/access_entry_test.go b/internal/service/eks/access_entry_test.go index 3f80adb2d20..b27b333b140 100644 --- a/internal/service/eks/access_entry_test.go +++ b/internal/service/eks/access_entry_test.go @@ -40,10 +40,15 @@ func TestAccEKSAccessEntry_basic(t *testing.T) { Steps: []resource.TestStep{ { Config: testAccAccessEntryConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckAccessEntryExists(ctx, resourceName, &accessentry), - resource.TestCheckResourceAttrSet(resourceName, "created_at"), - resource.TestCheckResourceAttrSet(resourceName, "principal_arn"), + resource.TestCheckResourceAttrSet(resourceName, "access_entry_arn"), + acctest.CheckResourceAttrRFC3339(resourceName, "created_at"), + resource.TestCheckResourceAttr(resourceName, "kubernetes_groups.#", "0"), + acctest.CheckResourceAttrRFC3339(resourceName, "modified_at"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttrSet(resourceName, "type"), + resource.TestCheckResourceAttrSet(resourceName, "user_name"), ), }, { @@ -86,6 +91,59 @@ func TestAccEKSAccessEntry_disappears(t *testing.T) { }) } +func TestAccEKSAccessEntry_tags(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var accessentry types.AccessEntry + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_access_entry.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAccessEntryDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAccessEntryConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccessEntryExists(ctx, resourceName, &accessentry), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAccessEntryConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccessEntryExists(ctx, resourceName, &accessentry), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAccessEntryConfig_tags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccessEntryExists(ctx, resourceName, &accessentry), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + func testAccCheckAccessEntryDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) @@ -95,11 +153,7 @@ func testAccCheckAccessEntryDestroy(ctx context.Context) resource.TestCheckFunc continue } - clusterName, principal_arn, err := tfeks.AccessEntryParseResourceID(rs.Primary.ID) - if err != nil { - return err - } - _, err = tfeks.FindAccessEntryByID(ctx, conn, clusterName, principal_arn) + _, err := tfeks.FindAccessEntryByTwoPartKey(ctx, conn, rs.Primary.Attributes["cluster_name"], rs.Primary.Attributes["principal_arn"]) if tfresource.NotFound(err) { continue @@ -116,27 +170,22 @@ func testAccCheckAccessEntryDestroy(ctx context.Context) resource.TestCheckFunc } } -func testAccCheckAccessEntryExists(ctx context.Context, name string, accessentry *types.AccessEntry) resource.TestCheckFunc { +func testAccCheckAccessEntryExists(ctx context.Context, n string, v *types.AccessEntry) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) - } - - clusterName, principal_arn, err := tfeks.AccessEntryParseResourceID(rs.Primary.ID) - if err != nil { - return err + return fmt.Errorf("Not found: %s", n) } conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) - output, err := tfeks.FindAccessEntryByID(ctx, conn, clusterName, principal_arn) + output, err := tfeks.FindAccessEntryByTwoPartKey(ctx, conn, rs.Primary.Attributes["cluster_name"], rs.Primary.Attributes["principal_arn"]) if err != nil { return err } - *accessentry = *output + *v = *output return nil } @@ -221,3 +270,38 @@ resource "aws_eks_access_entry" "test" { } `, rName)) } + +func testAccAccessEntryConfig_tags1(rName, tagKey1, tagValue1 string) string { + return acctest.ConfigCompose(testAccAccessEntryConfig_base(rName), fmt.Sprintf(` +resource "aws_iam_user" "test" { + name = %[1]q +} + +resource "aws_eks_access_entry" "test" { + cluster_name = aws_eks_cluster.test.name + principal_arn = aws_iam_user.test.arn + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1)) +} + +func testAccAccessEntryConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return acctest.ConfigCompose(testAccAccessEntryConfig_base(rName), fmt.Sprintf(` +resource "aws_iam_user" "test" { + name = %[1]q +} + +resource "aws_eks_access_entry" "test" { + cluster_name = aws_eks_cluster.test.name + principal_arn = aws_iam_user.test.arn + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) +} diff --git a/internal/service/eks/exports_test.go b/internal/service/eks/exports_test.go index 877abf163d1..2d7acf9fa95 100644 --- a/internal/service/eks/exports_test.go +++ b/internal/service/eks/exports_test.go @@ -14,6 +14,7 @@ var ( ResourceNodeGroup = resourceNodeGroup ResourcePodIdentityAssociation = newPodIdentityAssociationResource + FindAccessEntryByTwoPartKey = findAccessEntryByTwoPartKey FindAddonByTwoPartKey = findAddonByTwoPartKey FindClusterByName = findClusterByName FindFargateProfileByTwoPartKey = findFargateProfileByTwoPartKey diff --git a/internal/service/eks/find.go b/internal/service/eks/find.go index d6b9fce1e2d..719f410ddb5 100644 --- a/internal/service/eks/find.go +++ b/internal/service/eks/find.go @@ -13,35 +13,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs" ) -func FindAccessEntryByID(ctx context.Context, conn *eks.Client, clusterName, principal_arn string) (*types.AccessEntry, error) { - input := &eks.DescribeAccessEntryInput{ - ClusterName: aws.String(clusterName), - PrincipalArn: aws.String(principal_arn), - } - - output, err := conn.DescribeAccessEntry(ctx, input) - - if errs.IsA[*types.ResourceNotFoundException](err) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: input, - } - } - - if err != nil { - return nil, err - } - - if output == nil || output.AccessEntry == nil { - return nil, &retry.NotFoundError{ - Message: "Empty result", - LastRequest: input, - } - } - - return output.AccessEntry, nil -} - func FindAddonByClusterNameAndAddonName(ctx context.Context, conn *eks.Client, clusterName, addonName string) (*types.Addon, error) { input := &eks.DescribeAddonInput{ AddonName: aws.String(addonName), diff --git a/internal/service/eks/id.go b/internal/service/eks/id.go index 3a61bef8990..8ca0e3b4b48 100644 --- a/internal/service/eks/id.go +++ b/internal/service/eks/id.go @@ -27,25 +27,6 @@ func AddonParseResourceID(id string) (string, string, error) { return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]saddon-name", id, addonResourceIDSeparator) } -const accessEntryResourceIDSeparator = ":" - -func AccessEntryCreateResourceID(clusterName, principal_arn string) string { - parts := []string{clusterName, principal_arn} - id := strings.Join(parts, accessEntryResourceIDSeparator) - - return id -} - -func AccessEntryParseResourceID(id string) (string, string, error) { - parts := strings.SplitN(id, accessEntryResourceIDSeparator, 2) - - if len(parts) == 2 && parts[0] != "" && parts[1] != "" { - return parts[0], parts[1], nil - } - - return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]sprincipal_arn", id, accessEntryResourceIDSeparator) -} - const associatePolicyResourceIDSeparator = "#" func AssociatePolicyCreateResourceID(clusterName, principal_arn, policy_arn string) string { diff --git a/internal/service/eks/service_package_gen.go b/internal/service/eks/service_package_gen.go index f69c86a6ca9..b250698fdad 100644 --- a/internal/service/eks/service_package_gen.go +++ b/internal/service/eks/service_package_gen.go @@ -75,7 +75,7 @@ func (p *servicePackage) SDKResources(ctx context.Context) []*types.ServicePacka TypeName: "aws_eks_access_entry", Name: "Access Entry", Tags: &types.ServicePackageResourceTags{ - IdentifierAttribute: "arn", + IdentifierAttribute: "access_entry_arn", }, }, { From fb7bef3ef72ac26a257cbd794988ddb866982811 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Jan 2024 10:40:49 -0500 Subject: [PATCH 37/38] eks: Correct 'resourceAccessEntryUpdate'. --- internal/service/eks/access_entry.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/eks/access_entry.go b/internal/service/eks/access_entry.go index 8d5a9838029..5cc817b8848 100644 --- a/internal/service/eks/access_entry.go +++ b/internal/service/eks/access_entry.go @@ -181,7 +181,7 @@ func resourceAccessEntryUpdate(ctx context.Context, d *schema.ResourceData, meta } } - return append(diags, resourceFargateProfileRead(ctx, d, meta)...) + return append(diags, resourceAccessEntryRead(ctx, d, meta)...) } func resourceAccessEntryDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { From 152868a839aa78c9bb655bbc5d359b6f2709059f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 18 Jan 2024 11:11:46 -0500 Subject: [PATCH 38/38] r/aws_eks_access_policy_association: Tidy up and additional tests. --- internal/service/eks/access_entry_test.go | 32 ++++ .../service/eks/access_policy_association.go | 176 +++++++++++------- .../eks/access_policy_association_test.go | 55 ++++-- internal/service/eks/exports_test.go | 1 + internal/service/eks/id.go | 19 -- 5 files changed, 176 insertions(+), 107 deletions(-) diff --git a/internal/service/eks/access_entry_test.go b/internal/service/eks/access_entry_test.go index b27b333b140..3e9508733ae 100644 --- a/internal/service/eks/access_entry_test.go +++ b/internal/service/eks/access_entry_test.go @@ -91,6 +91,38 @@ func TestAccEKSAccessEntry_disappears(t *testing.T) { }) } +func TestAccEKSAccessEntry_Disappears_cluster(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var accessentry types.AccessEntry + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_access_entry.test" + clusterResourceName := "aws_eks_cluster.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAccessEntryDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAccessEntryConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccessEntryExists(ctx, resourceName, &accessentry), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfeks.ResourceCluster(), clusterResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccEKSAccessEntry_tags(t *testing.T) { ctx := acctest.Context(t) if testing.Short() { diff --git a/internal/service/eks/access_policy_association.go b/internal/service/eks/access_policy_association.go index 32cc9e56784..e7c532178b3 100644 --- a/internal/service/eks/access_policy_association.go +++ b/internal/service/eks/access_policy_association.go @@ -5,7 +5,9 @@ package eks import ( "context" + "fmt" "log" + "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" @@ -18,6 +20,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag" "github.com/hashicorp/terraform-provider-aws/internal/flex" + tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -28,6 +31,7 @@ func resourceAccessPolicyAssociation() *schema.Resource { CreateWithoutTimeout: resourceAccessPolicyAssociationCreate, ReadWithoutTimeout: resourceAccessPolicyAssociationRead, DeleteWithoutTimeout: resourceAccessPolicyAssociationDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -38,10 +42,6 @@ func resourceAccessPolicyAssociation() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "associated_at": { - Type: schema.TypeString, - Computed: true, - }, "access_scope": { Type: schema.TypeList, MinItems: 1, @@ -50,11 +50,6 @@ func resourceAccessPolicyAssociation() *schema.Resource { ForceNew: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "type": { - Type: schema.TypeString, - ForceNew: true, - Required: true, - }, "namespaces": { Type: schema.TypeSet, Optional: true, @@ -63,9 +58,18 @@ func resourceAccessPolicyAssociation() *schema.Resource { Type: schema.TypeString, }, }, + "type": { + Type: schema.TypeString, + ForceNew: true, + Required: true, + }, }, }, }, + "associated_at": { + Type: schema.TypeString, + Computed: true, + }, "cluster_name": { Type: schema.TypeString, Required: true, @@ -97,22 +101,23 @@ func resourceAccessPolicyAssociationCreate(ctx context.Context, d *schema.Resour conn := meta.(*conns.AWSClient).EKSClient(ctx) clusterName := d.Get("cluster_name").(string) - principal_arn := d.Get("principal_arn").(string) - policy_arn := d.Get("policy_arn").(string) - associateID := AssociatePolicyCreateResourceID(clusterName, principal_arn, policy_arn) + principalARN := d.Get("principal_arn").(string) + policyARN := d.Get("policy_arn").(string) + id := accessPolicyAssociationCreateResourceID(clusterName, principalARN, policyARN) input := &eks.AssociateAccessPolicyInput{ - ClusterName: aws.String(clusterName), - PrincipalArn: aws.String(principal_arn), - PolicyArn: aws.String(policy_arn), AccessScope: expandAccessScope(d.Get("access_scope").([]interface{})), + ClusterName: aws.String(clusterName), + PolicyArn: aws.String(policyARN), + PrincipalArn: aws.String(principalARN), } _, err := conn.AssociateAccessPolicy(ctx, input) + if err != nil { - return sdkdiag.AppendErrorf(diags, "creating EKS Access Policy Association: %s", err) + return sdkdiag.AppendErrorf(diags, "creating EKS Access Policy Association (%s): %s", id, err) } - d.SetId(associateID) + d.SetId(id) return append(diags, resourceAccessPolicyAssociationRead(ctx, d, meta)...) } @@ -121,11 +126,12 @@ func resourceAccessPolicyAssociationRead(ctx context.Context, d *schema.Resource var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EKSClient(ctx) - clusterName, principal_arn, policy_arn, err := AssociatePolicyParseResourceID(d.Id()) + clusterName, principalARN, policyARN, err := accessPolicyAssociationParseResourceID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading EKS Associate Policy (%s): %s", d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } - output, err := FindAccessPolicyByID(ctx, conn, clusterName, principal_arn, policy_arn) + + output, err := findAccessPolicyAssociationByThreePartKey(ctx, conn, clusterName, principalARN, policyARN) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] EKS Access Policy Association (%s) not found, removing from state", d.Id()) @@ -133,16 +139,6 @@ func resourceAccessPolicyAssociationRead(ctx context.Context, d *schema.Resource return diags } - if output == nil { - if d.IsNewResource() { - return sdkdiag.AppendErrorf(diags, "reading EKS Associated Policy Attachment (%s): not found after creation", d.Id()) - } - - log.Printf("[WARN] EKS Associated Policy Attachment (%s) not found, removing from state", d.Id()) - d.SetId("") - return diags - } - if err != nil { return sdkdiag.AppendErrorf(diags, "reading EKS Access Policy Association (%s): %s", d.Id(), err) } @@ -151,8 +147,8 @@ func resourceAccessPolicyAssociationRead(ctx context.Context, d *schema.Resource d.Set("associated_at", aws.ToTime(output.AssociatedAt).String()) d.Set("cluster_name", clusterName) d.Set("modified_at", aws.ToTime(output.ModifiedAt).String()) - d.Set("policy_arn", policy_arn) - d.Set("principal_arn", principal_arn) + d.Set("policy_arn", policyARN) + d.Set("principal_arn", principalARN) return diags } @@ -160,32 +156,98 @@ func resourceAccessPolicyAssociationRead(ctx context.Context, d *schema.Resource func resourceAccessPolicyAssociationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var diags diag.Diagnostics conn := meta.(*conns.AWSClient).EKSClient(ctx) - clusterName, principal_arn, policy_arn, err := AssociatePolicyParseResourceID(d.Id()) + clusterName, principalARN, policyARN, err := accessPolicyAssociationParseResourceID(d.Id()) if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting EKS Policy Association (%s): %s", d.Id(), err) + return sdkdiag.AppendFromErr(diags, err) } - log.Printf("[DEBUG] Deleting EKS Policy Associaion: %s", d.Id()) - - input := &eks.DisassociateAccessPolicyInput{ + log.Printf("[DEBUG] Deleting EKS Access Policy Association: %s", d.Id()) + _, err = conn.DisassociateAccessPolicy(ctx, &eks.DisassociateAccessPolicyInput{ ClusterName: aws.String(clusterName), - PrincipalArn: aws.String(principal_arn), - PolicyArn: aws.String(policy_arn), - } - _, err = conn.DisassociateAccessPolicy(ctx, input) + PolicyArn: aws.String(policyARN), + PrincipalArn: aws.String(principalARN), + }) if errs.IsA[*types.ResourceNotFoundException](err) { return diags } if err != nil { - return sdkdiag.AppendErrorf(diags, "deleting EKS Policy Associattion (%s): %s", d.Id(), err) + return sdkdiag.AppendErrorf(diags, "deleting EKS Access Policy Association (%s): %s", d.Id(), err) } return diags } +const accessPolicyAssociationResourceIDSeparator = "#" + +func accessPolicyAssociationCreateResourceID(clusterName, principalARN, policyARN string) string { + parts := []string{clusterName, principalARN, policyARN} + id := strings.Join(parts, accessPolicyAssociationResourceIDSeparator) + + return id +} + +func accessPolicyAssociationParseResourceID(id string) (string, string, string, error) { + parts := strings.SplitN(id, accessPolicyAssociationResourceIDSeparator, 3) + + if len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != "" { + return parts[0], parts[1], parts[2], nil + } + + return "", "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]sprincipal-arn%[2]spolicy-arn", id, accessPolicyAssociationResourceIDSeparator) +} + +func findAccessPolicyAssociationByThreePartKey(ctx context.Context, conn *eks.Client, clusterName, principalARN, policyARN string) (*types.AssociatedAccessPolicy, error) { + input := &eks.ListAssociatedAccessPoliciesInput{ + ClusterName: aws.String(clusterName), + PrincipalArn: aws.String(principalARN), + } + + return findAssociatedAccessPolicy(ctx, conn, input, func(v *types.AssociatedAccessPolicy) bool { + return aws.ToString(v.PolicyArn) == policyARN + }) +} + +func findAssociatedAccessPolicy(ctx context.Context, conn *eks.Client, input *eks.ListAssociatedAccessPoliciesInput, filter tfslices.Predicate[*types.AssociatedAccessPolicy]) (*types.AssociatedAccessPolicy, error) { + output, err := findAssociatedAccessPolicies(ctx, conn, input, filter) + + if err != nil { + return nil, err + } + + return tfresource.AssertSingleValueResult(output) +} + +func findAssociatedAccessPolicies(ctx context.Context, conn *eks.Client, input *eks.ListAssociatedAccessPoliciesInput, filter tfslices.Predicate[*types.AssociatedAccessPolicy]) ([]types.AssociatedAccessPolicy, error) { + var output []types.AssociatedAccessPolicy + + pages := eks.NewListAssociatedAccessPoliciesPaginator(conn, input) + for pages.HasMorePages() { + page, err := pages.NextPage(ctx) + + if errs.IsA[*types.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + for _, v := range page.AssociatedAccessPolicies { + if filter(&v) { + output = append(output, v) + } + } + } + + return output, nil +} + func expandAccessScope(l []interface{}) *types.AccessScope { if len(l) == 0 { return nil @@ -218,33 +280,3 @@ func flattenAccessScope(apiObject *types.AccessScope) []interface{} { return []interface{}{tfMap} } - -func FindAccessPolicyByID(ctx context.Context, conn *eks.Client, clusterName, principalARN, policyARN string) (*types.AssociatedAccessPolicy, error) { - input := &eks.ListAssociatedAccessPoliciesInput{ - ClusterName: aws.String(clusterName), - PrincipalArn: aws.String(principalARN), - } - - var result *types.AssociatedAccessPolicy - - output, err := conn.ListAssociatedAccessPolicies(ctx, input) - if output == nil || output.AssociatedAccessPolicies == nil || errs.IsA[*types.ResourceNotFoundException](err) { - return nil, &retry.NotFoundError{ - Message: "Empty result", - LastRequest: input, - } - } - - if err != nil { - return nil, err - } - - for _, v := range output.AssociatedAccessPolicies { - v := v - if aws.ToString(v.PolicyArn) == policyARN { - result = &v - } - } - - return result, nil -} diff --git a/internal/service/eks/access_policy_association_test.go b/internal/service/eks/access_policy_association_test.go index dd7bda372e7..cff1a0cca0e 100644 --- a/internal/service/eks/access_policy_association_test.go +++ b/internal/service/eks/access_policy_association_test.go @@ -89,6 +89,38 @@ func TestAccEKSAccessPolicyAssociation_disappears(t *testing.T) { }) } +func TestAccEKSAccessPolicyAssociation_Disappears_cluster(t *testing.T) { + ctx := acctest.Context(t) + if testing.Short() { + t.Skip("skipping long-running test in short mode") + } + + var associatedaccesspolicy types.AssociatedAccessPolicy + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_eks_access_policy_association.test" + clusterResourceName := "aws_eks_cluster.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + testAccPreCheck(ctx, t) + }, + ErrorCheck: acctest.ErrorCheck(t, names.EKSEndpointID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckAccessPolicyAssociationDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccAccessPolicyAssociationConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAccessPolicyAssociationExists(ctx, resourceName, &associatedaccesspolicy), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tfeks.ResourceCluster(), clusterResourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func testAccCheckAccessPolicyAssociationDestroy(ctx context.Context) resource.TestCheckFunc { return func(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) @@ -98,11 +130,7 @@ func testAccCheckAccessPolicyAssociationDestroy(ctx context.Context) resource.Te continue } - clusterName, principal_arn, policy_arn, err := tfeks.AssociatePolicyParseResourceID(rs.Primary.ID) - if err != nil { - return err - } - _, err = tfeks.FindAccessPolicyByID(ctx, conn, clusterName, principal_arn, policy_arn) + _, err := tfeks.FindAccessPolicyAssociationByThreePartKey(ctx, conn, rs.Primary.Attributes["cluster_name"], rs.Primary.Attributes["principal_arn"], rs.Primary.Attributes["policy_arn"]) if tfresource.NotFound(err) { continue @@ -119,25 +147,22 @@ func testAccCheckAccessPolicyAssociationDestroy(ctx context.Context) resource.Te } } -func testAccCheckAccessPolicyAssociationExists(ctx context.Context, name string, associatedaccesspolicy *types.AssociatedAccessPolicy) resource.TestCheckFunc { +func testAccCheckAccessPolicyAssociationExists(ctx context.Context, n string, v *types.AssociatedAccessPolicy) resource.TestCheckFunc { return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] + rs, ok := s.RootModule().Resources[n] if !ok { - return fmt.Errorf("Not found: %s", name) + return fmt.Errorf("Not found: %s", n) } - clusterName, principal_arn, policy_arn, err := tfeks.AssociatePolicyParseResourceID(rs.Primary.ID) - if err != nil { - return err - } conn := acctest.Provider.Meta().(*conns.AWSClient).EKSClient(ctx) - output, err := tfeks.FindAccessPolicyByID(ctx, conn, clusterName, principal_arn, policy_arn) + output, err := tfeks.FindAccessPolicyAssociationByThreePartKey(ctx, conn, rs.Primary.Attributes["cluster_name"], rs.Primary.Attributes["principal_arn"], rs.Primary.Attributes["policy_arn"]) + if err != nil { return err } - *associatedaccesspolicy = *output + *v = *output return nil } @@ -212,8 +237,6 @@ resource "aws_eks_cluster" "test" { func testAccAccessPolicyAssociationConfig_basic(rName string) string { return acctest.ConfigCompose(testAccAccessPolicyAssociationConfig_base(rName), fmt.Sprintf(` -data "aws_partition" "current" {} - resource "aws_iam_user" "test" { name = %[1]q } diff --git a/internal/service/eks/exports_test.go b/internal/service/eks/exports_test.go index 2d7acf9fa95..b5cdf7e2aea 100644 --- a/internal/service/eks/exports_test.go +++ b/internal/service/eks/exports_test.go @@ -15,6 +15,7 @@ var ( ResourcePodIdentityAssociation = newPodIdentityAssociationResource FindAccessEntryByTwoPartKey = findAccessEntryByTwoPartKey + FindAccessPolicyAssociationByThreePartKey = findAccessPolicyAssociationByThreePartKey FindAddonByTwoPartKey = findAddonByTwoPartKey FindClusterByName = findClusterByName FindFargateProfileByTwoPartKey = findFargateProfileByTwoPartKey diff --git a/internal/service/eks/id.go b/internal/service/eks/id.go index 8ca0e3b4b48..d91fdaf2e4b 100644 --- a/internal/service/eks/id.go +++ b/internal/service/eks/id.go @@ -27,25 +27,6 @@ func AddonParseResourceID(id string) (string, string, error) { return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]saddon-name", id, addonResourceIDSeparator) } -const associatePolicyResourceIDSeparator = "#" - -func AssociatePolicyCreateResourceID(clusterName, principal_arn, policy_arn string) string { - parts := []string{clusterName, principal_arn, policy_arn} - id := strings.Join(parts, associatePolicyResourceIDSeparator) - - return id -} - -func AssociatePolicyParseResourceID(id string) (string, string, string, error) { - parts := strings.SplitN(id, associatePolicyResourceIDSeparator, 3) - - if len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != "" { - return parts[0], parts[1], parts[2], nil - } - - return "", "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected cluster-name%[2]sprincipal_arn", id, associatePolicyResourceIDSeparator) -} - const fargateProfileResourceIDSeparator = ":" func FargateProfileCreateResourceID(clusterName, fargateProfileName string) string {