From a078082eda7d1e5569fddc949fa58c0edafb86c1 Mon Sep 17 00:00:00 2001 From: Matt Burgess <549318+mattburgess@users.noreply.github.com> Date: Tue, 12 Jul 2022 08:28:44 +0100 Subject: [PATCH 1/5] Add rolesanywhere/trust_anchor resource --- internal/conns/config.go | 7 + internal/provider/provider.go | 3 + internal/service/rolesanywhere/find.go | 38 ++ internal/service/rolesanywhere/generate.go | 4 + internal/service/rolesanywhere/tags_gen.go | 94 +++++ .../service/rolesanywhere/trust_anchor.go | 326 ++++++++++++++++++ .../rolesanywhere/trust_anchor_test.go | 289 ++++++++++++++++ names/names.go | 1 + .../rolesanywhere_trust_anchor.html.markdown | 97 ++++++ 9 files changed, 859 insertions(+) create mode 100644 internal/service/rolesanywhere/find.go create mode 100644 internal/service/rolesanywhere/generate.go create mode 100644 internal/service/rolesanywhere/tags_gen.go create mode 100644 internal/service/rolesanywhere/trust_anchor.go create mode 100644 internal/service/rolesanywhere/trust_anchor_test.go create mode 100644 website/docs/r/rolesanywhere_trust_anchor.html.markdown diff --git a/internal/conns/config.go b/internal/conns/config.go index 676bcadea2d..74df7ea8011 100644 --- a/internal/conns/config.go +++ b/internal/conns/config.go @@ -7,6 +7,7 @@ import ( "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" "github.com/aws/aws-sdk-go-v2/service/kendra" + "github.com/aws/aws-sdk-go-v2/service/rolesanywhere" "github.com/aws/aws-sdk-go-v2/service/route53domains" "github.com/aws/aws-sdk-go-v2/service/transcribe" "github.com/aws/aws-sdk-go/aws" @@ -198,6 +199,12 @@ func (c *Config) Client(ctx context.Context) (interface{}, diag.Diagnostics) { } }) + client.RolesAnywhereConn = rolesanywhere.NewFromConfig(cfg, func(o *rolesanywhere.Options) { + if endpoint := c.Endpoints[names.RolesAnywhere]; endpoint != "" { + o.EndpointResolver = rolesanywhere.EndpointResolverFromURL(endpoint) + } + }) + client.Route53DomainsConn = route53domains.NewFromConfig(cfg, func(o *route53domains.Options) { if endpoint := c.Endpoints[names.Route53Domains]; endpoint != "" { o.EndpointResolver = route53domains.EndpointResolverFromURL(endpoint) diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 8b02b45eb84..2ba4d1c6e24 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -144,6 +144,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/service/redshiftdata" "github.com/hashicorp/terraform-provider-aws/internal/service/resourcegroups" "github.com/hashicorp/terraform-provider-aws/internal/service/resourcegroupstaggingapi" + "github.com/hashicorp/terraform-provider-aws/internal/service/rolesanywhere" "github.com/hashicorp/terraform-provider-aws/internal/service/route53" "github.com/hashicorp/terraform-provider-aws/internal/service/route53domains" "github.com/hashicorp/terraform-provider-aws/internal/service/route53recoverycontrolconfig" @@ -1834,6 +1835,8 @@ func Provider() *schema.Provider { "aws_resourcegroups_group": resourcegroups.ResourceGroup(), + "aws_rolesanywhere_trust_anchor": rolesanywhere.ResourceTrustAnchor(), + "aws_route53_delegation_set": route53.ResourceDelegationSet(), "aws_route53_health_check": route53.ResourceHealthCheck(), "aws_route53_hosted_zone_dnssec": route53.ResourceHostedZoneDNSSEC(), diff --git a/internal/service/rolesanywhere/find.go b/internal/service/rolesanywhere/find.go new file mode 100644 index 00000000000..c4e86b0f44d --- /dev/null +++ b/internal/service/rolesanywhere/find.go @@ -0,0 +1,38 @@ +package rolesanywhere + +import ( + "context" + "errors" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rolesanywhere" + "github.com/aws/aws-sdk-go-v2/service/rolesanywhere/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" +) + +func FindTrustAnchorByID(ctx context.Context, conn *rolesanywhere.Client, id string) (*rolesanywhere.GetTrustAnchorOutput, error) { + in := &rolesanywhere.GetTrustAnchorInput{ + TrustAnchorId: aws.String(id), + } + + out, err := conn.GetTrustAnchor(ctx, in) + + var resourceNotFoundException *types.ResourceNotFoundException + if errors.As(err, &resourceNotFoundException) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + if err != nil { + return nil, err + } + + if out == nil { + return nil, tfresource.NewEmptyResultError(in) + } + + return out, nil +} diff --git a/internal/service/rolesanywhere/generate.go b/internal/service/rolesanywhere/generate.go new file mode 100644 index 00000000000..2a62a622172 --- /dev/null +++ b/internal/service/rolesanywhere/generate.go @@ -0,0 +1,4 @@ +//go:generate go run ../../generate/tags/main.go -AwsSdkVersion=2 -ListTags -ServiceTagsSlice -UpdateTags +// ONLY generate directives and package declaration! Do not add anything else to this file. + +package rolesanywhere diff --git a/internal/service/rolesanywhere/tags_gen.go b/internal/service/rolesanywhere/tags_gen.go new file mode 100644 index 00000000000..576bb99c6a1 --- /dev/null +++ b/internal/service/rolesanywhere/tags_gen.go @@ -0,0 +1,94 @@ +// Code generated by internal/generate/tags/main.go; DO NOT EDIT. +package rolesanywhere + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rolesanywhere" + "github.com/aws/aws-sdk-go-v2/service/rolesanywhere/types" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" +) + +// ListTags lists rolesanywhere service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func ListTags(ctx context.Context, conn *rolesanywhere.Client, identifier string) (tftags.KeyValueTags, error) { + input := &rolesanywhere.ListTagsForResourceInput{ + ResourceArn: aws.String(identifier), + } + + output, err := conn.ListTagsForResource(ctx, input) + + if err != nil { + return tftags.New(nil), err + } + + return KeyValueTags(output.Tags), nil +} + +// []*SERVICE.Tag handling + +// Tags returns rolesanywhere service tags. +func Tags(tags tftags.KeyValueTags) []types.Tag { + result := make([]types.Tag, 0, len(tags)) + + for k, v := range tags.Map() { + tag := types.Tag{ + Key: aws.String(k), + Value: aws.String(v), + } + + result = append(result, tag) + } + + return result +} + +// KeyValueTags creates tftags.KeyValueTags from rolesanywhere service tags. +func KeyValueTags(tags []types.Tag) tftags.KeyValueTags { + m := make(map[string]*string, len(tags)) + + for _, tag := range tags { + m[aws.ToString(tag.Key)] = tag.Value + } + + return tftags.New(m) +} + +// UpdateTags updates rolesanywhere service tags. +// The identifier is typically the Amazon Resource Name (ARN), although +// it may also be a different identifier depending on the service. +func UpdateTags(ctx context.Context, conn *rolesanywhere.Client, identifier string, oldTagsMap interface{}, newTagsMap interface{}) error { + oldTags := tftags.New(oldTagsMap) + newTags := tftags.New(newTagsMap) + + if removedTags := oldTags.Removed(newTags); len(removedTags) > 0 { + input := &rolesanywhere.UntagResourceInput{ + ResourceArn: aws.String(identifier), + TagKeys: removedTags.IgnoreAWS().Keys(), + } + + _, err := conn.UntagResource(ctx, input) + + if err != nil { + return fmt.Errorf("error untagging resource (%s): %w", identifier, err) + } + } + + if updatedTags := oldTags.Updated(newTags); len(updatedTags) > 0 { + input := &rolesanywhere.TagResourceInput{ + ResourceArn: aws.String(identifier), + Tags: Tags(updatedTags.IgnoreAWS()), + } + + _, err := conn.TagResource(ctx, input) + + if err != nil { + return fmt.Errorf("error tagging resource (%s): %w", identifier, err) + } + } + + return nil +} diff --git a/internal/service/rolesanywhere/trust_anchor.go b/internal/service/rolesanywhere/trust_anchor.go new file mode 100644 index 00000000000..9aa850157c9 --- /dev/null +++ b/internal/service/rolesanywhere/trust_anchor.go @@ -0,0 +1,326 @@ +package rolesanywhere + +import ( + "context" + "errors" + "log" + + "github.com/aws/aws-sdk-go-v2/service/rolesanywhere" + "github.com/aws/aws-sdk-go-v2/service/rolesanywhere/types" + "github.com/aws/aws-sdk-go/aws" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +func ResourceTrustAnchor() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceTrustAnchorCreate, + ReadContext: resourceTrustAnchorRead, + UpdateContext: resourceTrustAnchorUpdate, + DeleteContext: resourceTrustAnchorDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + }, + "source": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "source_data": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "acm_pca_arn": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: verify.ValidARN, + }, + "x509_certificate_data": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + }, + "source_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(trustAnchorTypeValues(types.TrustAnchorType("").Values()...), false), + }, + }, + }, + }, + "tags": tftags.TagsSchema(), + "tags_all": tftags.TagsSchemaComputed(), + }, + CustomizeDiff: verify.SetTagsDiff, + } +} + +func resourceTrustAnchorCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).RolesAnywhereConn + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) + name := d.Get("name").(string) + + input := &rolesanywhere.CreateTrustAnchorInput{ + Enabled: aws.Bool(d.Get("enabled").(bool)), + Name: aws.String(name), + Source: expandSource(d.Get("source").([]interface{})), + Tags: Tags(tags.IgnoreAWS()), + } + + var output *rolesanywhere.CreateTrustAnchorOutput + output, err := conn.CreateTrustAnchor(ctx, input) + if err != nil { + return diag.Errorf("creating RolesAnywhere Trust Anchor (%s): %s", name, err) + } + d.SetId(aws.StringValue(output.TrustAnchor.TrustAnchorId)) + + return resourceTrustAnchorRead(ctx, d, meta) +} + +func resourceTrustAnchorRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).RolesAnywhereConn + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + + getTrustAnchorOutput, err := FindTrustAnchorByID(ctx, conn, d.Id()) + + var resourceNotFoundException *types.ResourceNotFoundException + if !d.IsNewResource() && errors.As(err, &resourceNotFoundException) { + log.Printf("[WARN] RolesAnywhere Trust Anchor (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.Errorf("reading RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) + } + + trustAnchor := getTrustAnchorOutput.TrustAnchor + + d.Set("arn", trustAnchor.TrustAnchorArn) + d.Set("enabled", trustAnchor.Enabled) + d.Set("name", trustAnchor.Name) + + if err := d.Set("source", flattenSource(trustAnchor.Source)); err != nil { + return diag.Errorf("setting RolesAnywhere Trust Anchor source: %s", err) + } + + tags, err := ListTags(ctx, conn, d.Get("arn").(string)) + if err != nil { + return diag.Errorf("listing tags for RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) + } + + tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return diag.Errorf("setting tags: %s", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return diag.Errorf("setting tags_all: %s", err) + } + + return nil +} + +func resourceTrustAnchorUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).RolesAnywhereConn + + input := &rolesanywhere.UpdateTrustAnchorInput{ + TrustAnchorId: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Source: expandSource(d.Get("source").([]interface{})), + } + + log.Printf("[DEBUG] Updating RolesAnywhere Trust Anchor (%s): %#v", d.Id(), input) + _, err := conn.UpdateTrustAnchor(ctx, input) + if err != nil { + return diag.Errorf("updating RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) + } + + if d.HasChange("enabled") { + _, n := d.GetChange("enabled") + if n == "true" { + err := enableTrustAnchor(ctx, d.Id(), meta) + if err != nil { + diag.Errorf("enabling RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) + } + } else { + err := disableTrustAnchor(ctx, d.Id(), meta) + if err != nil { + diag.Errorf("disabling RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) + } + } + } + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + if err := UpdateTags(ctx, conn, d.Get("arn").(string), o, n); err != nil { + return diag.Errorf("error updating tags: %s", err) + } + } + + return resourceTrustAnchorRead(ctx, d, meta) +} + +func resourceTrustAnchorDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).RolesAnywhereConn + + input := &rolesanywhere.DeleteTrustAnchorInput{ + TrustAnchorId: aws.String(d.Id()), + } + + _, err := conn.DeleteTrustAnchor(ctx, input) + + var resourceNotFoundException *types.ResourceNotFoundException + if errors.As(err, &resourceNotFoundException) { + return nil + } + + if err != nil { + return diag.Errorf("deleting RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) + } + + return nil +} + +func flattenSource(apiObject *types.Source) []interface{} { + if apiObject == nil { + return nil + } + + m := map[string]interface{}{} + + m["source_type"] = apiObject.SourceType + m["source_data"] = flattenSourceData(apiObject.SourceData) + + return []interface{}{m} +} + +func flattenSourceData(apiObject types.SourceData) []interface{} { + if apiObject == nil { + return []interface{}{} + } + + m := map[string]interface{}{} + + switch v := apiObject.(type) { + case *types.SourceDataMemberAcmPcaArn: + m["acm_pca_arn"] = v.Value + case *types.SourceDataMemberX509CertificateData: + m["x509_certificate_data"] = v.Value + case *types.UnknownUnionMember: + log.Println("unknown tag:", v.Tag) + default: + log.Println("union is nil or unknown type") + } + + return []interface{}{m} +} + +func expandSource(tfList []interface{}) *types.Source { + if len(tfList) == 0 || tfList[0] == nil { + return nil + } + + tfMap, ok := tfList[0].(map[string]interface{}) + if !ok { + return nil + } + + result := &types.Source{} + + if v, ok := tfMap["source_type"].(string); ok && v != "" { + result.SourceType = types.TrustAnchorType(v) + } + + if v, ok := tfMap["source_data"].([]interface{}); ok && len(v) > 0 && v[0] != nil { + if result.SourceType == types.TrustAnchorTypeAwsAcmPca { + result.SourceData = expandSourceDataACMPCA(v[0].(map[string]interface{})) + } else if result.SourceType == types.TrustAnchorTypeCertificateBundle { + result.SourceData = expandSourceDataCertificateBundle(v[0].(map[string]interface{})) + } + } + + return result +} + +func expandSourceDataACMPCA(tfMap map[string]interface{}) *types.SourceDataMemberAcmPcaArn { + result := &types.SourceDataMemberAcmPcaArn{} + + if v, ok := tfMap["acm_pca_arn"].(string); ok && v != "" { + result.Value = v + } + + return result +} + +func expandSourceDataCertificateBundle(tfMap map[string]interface{}) *types.SourceDataMemberX509CertificateData { + result := &types.SourceDataMemberX509CertificateData{} + + if v, ok := tfMap["x509_certificate_data"].(string); ok && v != "" { + result.Value = v + } + + return result +} + +func disableTrustAnchor(ctx context.Context, trustAnchorId string, meta interface{}) error { + conn := meta.(*conns.AWSClient).RolesAnywhereConn + + input := &rolesanywhere.DisableTrustAnchorInput{ + TrustAnchorId: aws.String(trustAnchorId), + } + + _, err := conn.DisableTrustAnchor(ctx, input) + return err +} + +func enableTrustAnchor(ctx context.Context, trustAnchorId string, meta interface{}) error { + conn := meta.(*conns.AWSClient).RolesAnywhereConn + + input := &rolesanywhere.EnableTrustAnchorInput{ + TrustAnchorId: aws.String(trustAnchorId), + } + + _, err := conn.EnableTrustAnchor(ctx, input) + return err +} + +func trustAnchorTypeValues(input ...types.TrustAnchorType) []string { + var output []string + + for _, v := range input { + output = append(output, string(v)) + } + + return output +} diff --git a/internal/service/rolesanywhere/trust_anchor_test.go b/internal/service/rolesanywhere/trust_anchor_test.go new file mode 100644 index 00000000000..8a6c1902997 --- /dev/null +++ b/internal/service/rolesanywhere/trust_anchor_test.go @@ -0,0 +1,289 @@ +package rolesanywhere_test + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/rolesanywhere" + "github.com/aws/aws-sdk-go-v2/service/rolesanywhere/types" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfrolesanywhere "github.com/hashicorp/terraform-provider-aws/internal/service/rolesanywhere" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func TestAccRolesAnywhereTrustAnchor_basic(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + caCommonName := acctest.RandomDomainName() + resourceName := "aws_rolesanywhere_trust_anchor.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, names.RolesAnywhereEndpointID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckTrustAnchorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTrustAnchorConfig_basic(rName, caCommonName), + Check: resource.ComposeTestCheckFunc( + testAccCheckTrustAnchorExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "enabled"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "source.#", "1"), + resource.TestCheckResourceAttr(resourceName, "source.0.source_data.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "source.0.source_data.0.acm_pca_arn", "aws_acmpca_certificate_authority.test", "arn"), + resource.TestCheckResourceAttr(resourceName, "source.0.source_type", "AWS_ACM_PCA"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccRolesAnywhereTrustAnchor_tags(t *testing.T) { + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + caCommonName := acctest.RandomDomainName() + resourceName := "aws_rolesanywhere_trust_anchor.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, names.RolesAnywhereEndpointID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckTrustAnchorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTrustAnchorConfig_tags1(rName, caCommonName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckTrustAnchorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccTrustAnchorConfig_tags2(rName, caCommonName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckTrustAnchorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccTrustAnchorConfig_tags1(rName, caCommonName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckTrustAnchorExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func TestAccRolesAnywhereTrustAnchor_disappears(t *testing.T) { + + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + caCommonName := acctest.RandomDomainName() + resourceName := "aws_rolesanywhere_trust_anchor.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, names.RolesAnywhereEndpointID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckTrustAnchorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTrustAnchorConfig_basic(rName, caCommonName), + Check: resource.ComposeTestCheckFunc( + testAccCheckTrustAnchorExists(resourceName), + acctest.CheckResourceDisappears(acctest.Provider, tfrolesanywhere.ResourceTrustAnchor(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckTrustAnchorDestroy(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).RolesAnywhereConn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_rolesanywhere_trust_anchor" { + continue + } + + resp, err := conn.GetTrustAnchor(context.TODO(), &rolesanywhere.GetTrustAnchorInput{ + TrustAnchorId: aws.String(rs.Primary.ID), + }) + + if err == nil { + if *resp.TrustAnchor.TrustAnchorId == rs.Primary.ID { + return fmt.Errorf("Trust Anchor %s still exists", rs.Primary.ID) + } + } + + var resourceNotFoundException *types.ResourceNotFoundException + if errors.As(err, &resourceNotFoundException) { + continue + } + + if err != nil { + return err + } + } + + return nil +} + +func testAccCheckTrustAnchorExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No Trust Anchor is set") + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).RolesAnywhereConn + + _, err := tfrolesanywhere.FindTrustAnchorByID(context.TODO(), conn, rs.Primary.ID) + + if err != nil { + return fmt.Errorf("Error describing Trust Anchor: %s", err.Error()) + } + + return nil + } +} + +func testAccTrustAnchorConfigBase(caCommonName string) string { + return fmt.Sprintf(` +resource "aws_acmpca_certificate_authority" "test" { + permanent_deletion_time_in_days = 7 + type = "ROOT" + certificate_authority_configuration { + key_algorithm = "RSA_4096" + signing_algorithm = "SHA512WITHRSA" + subject { + common_name = %[1]q + } + } +} + +data "aws_partition" "current" {} + +resource "aws_acmpca_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + certificate_signing_request = aws_acmpca_certificate_authority.test.certificate_signing_request + signing_algorithm = "SHA512WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" + + validity { + type = "YEARS" + value = 1 + } +} + +resource "aws_acmpca_certificate_authority_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.test.arn + certificate = aws_acmpca_certificate.test.certificate + certificate_chain = aws_acmpca_certificate.test.certificate_chain +} +`, caCommonName) +} + +func testAccTrustAnchorConfig_basic(rName, caCommonName string) string { + return acctest.ConfigCompose( + testAccTrustAnchorConfigBase(caCommonName), + fmt.Sprintf(` +resource "aws_rolesanywhere_trust_anchor" "test" { + name = %[1]q + source { + source_data { + acm_pca_arn = aws_acmpca_certificate_authority.test.arn + } + source_type = "AWS_ACM_PCA" + } + depends_on = [aws_acmpca_certificate_authority_certificate.test] +} +`, rName)) +} + +func testAccTrustAnchorConfig_tags1(rName, caCommonName, tag, value string) string { + return acctest.ConfigCompose( + testAccTrustAnchorConfigBase(caCommonName), + fmt.Sprintf(` +resource "aws_rolesanywhere_trust_anchor" "test" { + name = %[1]q + source { + source_data { + acm_pca_arn = aws_acmpca_certificate_authority.test.arn + } + source_type = "AWS_ACM_PCA" + } + tags = { + %[2]q = %[3]q + } + depends_on = [aws_acmpca_certificate_authority_certificate.test] +} +`, rName, tag, value)) +} + +func testAccTrustAnchorConfig_tags2(rName, caCommonName, tag1, value1, tag2, value2 string) string { + return acctest.ConfigCompose( + testAccTrustAnchorConfigBase(caCommonName), + fmt.Sprintf(` +resource "aws_rolesanywhere_trust_anchor" "test" { + name = %[1]q + source { + source_data { + acm_pca_arn = aws_acmpca_certificate_authority.test.arn + } + source_type = "AWS_ACM_PCA" + } + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } + depends_on = [aws_acmpca_certificate_authority_certificate.test] +} +`, rName, tag1, value1, tag2, value2)) +} + +func testAccPreCheck(t *testing.T) { + acctest.PreCheckPartitionHasService(names.RolesAnywhereEndpointID, t) + + conn := acctest.Provider.Meta().(*conns.AWSClient).RolesAnywhereConn + + input := &rolesanywhere.ListTrustAnchorsInput{} + + _, err := conn.ListTrustAnchors(context.TODO(), input) + + if acctest.PreCheckSkipError(err) { + t.Skipf("skipping acceptance testing: %s", err) + } + + if err != nil { + t.Fatalf("unexpected PreCheck error: %s", err) + } +} diff --git a/names/names.go b/names/names.go index d94e5e320e8..061a5d9862c 100644 --- a/names/names.go +++ b/names/names.go @@ -24,6 +24,7 @@ import ( // This "should" be defined by the AWS Go SDK v2, but currently isn't. const ( KendraEndpointID = "kendra" + RolesAnywhereEndpointID = "rolesanywhere" Route53DomainsEndpointID = "route53domains" TranscribeEndpointID = "transcribe" ) diff --git a/website/docs/r/rolesanywhere_trust_anchor.html.markdown b/website/docs/r/rolesanywhere_trust_anchor.html.markdown new file mode 100644 index 00000000000..f07743bb4e8 --- /dev/null +++ b/website/docs/r/rolesanywhere_trust_anchor.html.markdown @@ -0,0 +1,97 @@ +--- +subcategory: "Roles Anywhere" +layout: "aws" +page_title: "AWS: aws_rolesanywhere_trust_anchor" +description: |- + Provides a Roles Anywhere Trust Anchor resource +--- + +# Resource: aws_rolesanywhere_trust_anchor + +Terraform resource for managing a Roles Anywhere Trust Anchor. + +## Example Usage + +```terraform +resource "aws_acmpca_certificate_authority" "example" { + permanent_deletion_time_in_days = 7 + type = "ROOT" + certificate_authority_configuration { + key_algorithm = "RSA_4096" + signing_algorithm = "SHA512WITHRSA" + subject { + common_name = "example.com" + } + } +} + +data "aws_partition" "current" {} + +resource "aws_acmpca_certificate" "test" { + certificate_authority_arn = aws_acmpca_certificate_authority.example.arn + certificate_signing_request = aws_acmpca_certificate_authority.example.certificate_signing_request + signing_algorithm = "SHA512WITHRSA" + + template_arn = "arn:${data.aws_partition.current.partition}:acm-pca:::template/RootCACertificate/V1" + + validity { + type = "YEARS" + value = 1 + } +} + +resource "aws_acmpca_certificate_authority_certificate" "example" { + certificate_authority_arn = aws_acmpca_certificate_authority.example.arn + certificate = aws_acmpca_certificate.example.certificate + certificate_chain = aws_acmpca_certificate.example.certificate_chain +} + +resource "aws_rolesanywhere_trust_anchor" "test" { + name = "example" + source { + source_data { + acm_pca_arn = aws_acmpca_certificate_authority.example.arn + } + source_type = "AWS_ACM_PCA" + } + # Wait for the ACMPCA to be ready to receive requests before setting up the trust anchor + depends_on = [aws_acmpca_certificate_authority_certificate.example] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `enabled` - (Optional) Whether or not the Trust Anchor should be enabled. +* `name` - (Required) The name of the Trust Anchor. +* `source` - (Required) The source of trust, documented below +* `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +### Nested Blocks + +#### `source` + +* `source_data` - (Required) The data denoting the source of trust, documented below +* `source_type` - (Required) The type of the source of trust. Must be either `AWS_ACM_PCA` or `CERTIFICATE_BUNDLE`. + +#### `source_data` + +* `acm_pca_arn` - (Optional, required when `source_type` is `AWS_ACM_PCA`) The ARN of an ACM Private Certificate Authority. +* `x509_certificate_data` - (Optional, required when `source_type` is `CERTIFICATE_BUNDLE`) + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - Amazon Resource Name (ARN) of the Trust Anchor +* `id` - The Trust Anchor ID. +* `tags_all` - A map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block). + +## Import + +`aws_rolesanywhere_trust_anchor` can be imported using its `id`, e.g. + +``` +$ terraform import aws_rolesanywhere_trust_anchor.example 92b2fbbb-984d-41a3-a765-e3cbdb69ebb1 +``` From 2bc8c1d0aebc6da46799156e3cbf0868837ccac6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 18 Jul 2022 12:00:54 -0400 Subject: [PATCH 2/5] Add CHANGELOG entry. --- .changelog/25779.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/25779.txt diff --git a/.changelog/25779.txt b/.changelog/25779.txt new file mode 100644 index 00000000000..7cdae15d0ea --- /dev/null +++ b/.changelog/25779.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_rolesanywhere_trust_anchor +``` \ No newline at end of file From a42b86847b108bda40f536ab60dc3248adc101a2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 18 Jul 2022 12:11:31 -0400 Subject: [PATCH 3/5] aws_rolesanywhere_trust_anchor: 'FindTrustAnchorByID' returns '*types.TrustAnchorDetail'. --- internal/service/rolesanywhere/find.go | 6 +- .../service/rolesanywhere/trust_anchor.go | 69 ++++++++++--------- .../rolesanywhere/trust_anchor_test.go | 29 +++----- 3 files changed, 49 insertions(+), 55 deletions(-) diff --git a/internal/service/rolesanywhere/find.go b/internal/service/rolesanywhere/find.go index c4e86b0f44d..4144e21394b 100644 --- a/internal/service/rolesanywhere/find.go +++ b/internal/service/rolesanywhere/find.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) -func FindTrustAnchorByID(ctx context.Context, conn *rolesanywhere.Client, id string) (*rolesanywhere.GetTrustAnchorOutput, error) { +func FindTrustAnchorByID(ctx context.Context, conn *rolesanywhere.Client, id string) (*types.TrustAnchorDetail, error) { in := &rolesanywhere.GetTrustAnchorInput{ TrustAnchorId: aws.String(id), } @@ -30,9 +30,9 @@ func FindTrustAnchorByID(ctx context.Context, conn *rolesanywhere.Client, id str return nil, err } - if out == nil { + if out == nil || out.TrustAnchor == nil { return nil, tfresource.NewEmptyResultError(in) } - return out, nil + return out.TrustAnchor, nil } diff --git a/internal/service/rolesanywhere/trust_anchor.go b/internal/service/rolesanywhere/trust_anchor.go index 9aa850157c9..d4b9757db4c 100644 --- a/internal/service/rolesanywhere/trust_anchor.go +++ b/internal/service/rolesanywhere/trust_anchor.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" 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" ) @@ -22,6 +23,7 @@ func ResourceTrustAnchor() *schema.Resource { ReadContext: resourceTrustAnchorRead, UpdateContext: resourceTrustAnchorUpdate, DeleteContext: resourceTrustAnchorDelete, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -77,6 +79,7 @@ func ResourceTrustAnchor() *schema.Resource { "tags": tftags.TagsSchema(), "tags_all": tftags.TagsSchemaComputed(), }, + CustomizeDiff: verify.SetTagsDiff, } } @@ -94,11 +97,13 @@ func resourceTrustAnchorCreate(ctx context.Context, d *schema.ResourceData, meta Tags: Tags(tags.IgnoreAWS()), } - var output *rolesanywhere.CreateTrustAnchorOutput + log.Printf("[DEBUG] Creating RolesAnywhere Trust Anchor (%s): %#v", d.Id(), input) output, err := conn.CreateTrustAnchor(ctx, input) + if err != nil { return diag.Errorf("creating RolesAnywhere Trust Anchor (%s): %s", name, err) } + d.SetId(aws.StringValue(output.TrustAnchor.TrustAnchorId)) return resourceTrustAnchorRead(ctx, d, meta) @@ -109,10 +114,9 @@ func resourceTrustAnchorRead(ctx context.Context, d *schema.ResourceData, meta i defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - getTrustAnchorOutput, err := FindTrustAnchorByID(ctx, conn, d.Id()) + trustAnchor, err := FindTrustAnchorByID(ctx, conn, d.Id()) - var resourceNotFoundException *types.ResourceNotFoundException - if !d.IsNewResource() && errors.As(err, &resourceNotFoundException) { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] RolesAnywhere Trust Anchor (%s) not found, removing from state", d.Id()) d.SetId("") return nil @@ -122,17 +126,16 @@ func resourceTrustAnchorRead(ctx context.Context, d *schema.ResourceData, meta i return diag.Errorf("reading RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) } - trustAnchor := getTrustAnchorOutput.TrustAnchor - d.Set("arn", trustAnchor.TrustAnchorArn) d.Set("enabled", trustAnchor.Enabled) d.Set("name", trustAnchor.Name) if err := d.Set("source", flattenSource(trustAnchor.Source)); err != nil { - return diag.Errorf("setting RolesAnywhere Trust Anchor source: %s", err) + return diag.Errorf("setting source: %s", err) } tags, err := ListTags(ctx, conn, d.Get("arn").(string)) + if err != nil { return diag.Errorf("listing tags for RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) } @@ -154,29 +157,30 @@ func resourceTrustAnchorRead(ctx context.Context, d *schema.ResourceData, meta i func resourceTrustAnchorUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).RolesAnywhereConn - input := &rolesanywhere.UpdateTrustAnchorInput{ - TrustAnchorId: aws.String(d.Id()), - Name: aws.String(d.Get("name").(string)), - Source: expandSource(d.Get("source").([]interface{})), - } + if d.HasChangesExcept("tags", "tags_all") { + input := &rolesanywhere.UpdateTrustAnchorInput{ + TrustAnchorId: aws.String(d.Id()), + Name: aws.String(d.Get("name").(string)), + Source: expandSource(d.Get("source").([]interface{})), + } - log.Printf("[DEBUG] Updating RolesAnywhere Trust Anchor (%s): %#v", d.Id(), input) - _, err := conn.UpdateTrustAnchor(ctx, input) - if err != nil { - return diag.Errorf("updating RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) - } + log.Printf("[DEBUG] Updating RolesAnywhere Trust Anchor (%s): %#v", d.Id(), input) + _, err := conn.UpdateTrustAnchor(ctx, input) - if d.HasChange("enabled") { - _, n := d.GetChange("enabled") - if n == "true" { - err := enableTrustAnchor(ctx, d.Id(), meta) - if err != nil { - diag.Errorf("enabling RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) - } - } else { - err := disableTrustAnchor(ctx, d.Id(), meta) - if err != nil { - diag.Errorf("disabling RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) + if err != nil { + return diag.Errorf("updating RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) + } + + if d.HasChange("enabled") { + _, n := d.GetChange("enabled") + if n == "true" { + if err := enableTrustAnchor(ctx, d.Id(), meta); err != nil { + diag.Errorf("enabling RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) + } + } else { + if err := disableTrustAnchor(ctx, d.Id(), meta); err != nil { + diag.Errorf("disabling RolesAnywhere Trust Anchor (%s): %s", d.Id(), err) + } } } } @@ -184,7 +188,7 @@ func resourceTrustAnchorUpdate(ctx context.Context, d *schema.ResourceData, meta if d.HasChange("tags_all") { o, n := d.GetChange("tags_all") if err := UpdateTags(ctx, conn, d.Get("arn").(string), o, n); err != nil { - return diag.Errorf("error updating tags: %s", err) + return diag.Errorf("updating tags: %s", err) } } @@ -194,11 +198,10 @@ func resourceTrustAnchorUpdate(ctx context.Context, d *schema.ResourceData, meta func resourceTrustAnchorDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { conn := meta.(*conns.AWSClient).RolesAnywhereConn - input := &rolesanywhere.DeleteTrustAnchorInput{ + log.Printf("[DEBUG] Deleting RolesAnywhere Trust Anchor (%s)", d.Id()) + _, err := conn.DeleteTrustAnchor(ctx, &rolesanywhere.DeleteTrustAnchorInput{ TrustAnchorId: aws.String(d.Id()), - } - - _, err := conn.DeleteTrustAnchor(ctx, input) + }) var resourceNotFoundException *types.ResourceNotFoundException if errors.As(err, &resourceNotFoundException) { diff --git a/internal/service/rolesanywhere/trust_anchor_test.go b/internal/service/rolesanywhere/trust_anchor_test.go index 8a6c1902997..70eeedcf938 100644 --- a/internal/service/rolesanywhere/trust_anchor_test.go +++ b/internal/service/rolesanywhere/trust_anchor_test.go @@ -2,19 +2,17 @@ package rolesanywhere_test import ( "context" - "errors" "fmt" "testing" - "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/rolesanywhere" - "github.com/aws/aws-sdk-go-v2/service/rolesanywhere/types" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfrolesanywhere "github.com/hashicorp/terraform-provider-aws/internal/service/rolesanywhere" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/names" ) @@ -128,39 +126,32 @@ func testAccCheckTrustAnchorDestroy(s *terraform.State) error { continue } - resp, err := conn.GetTrustAnchor(context.TODO(), &rolesanywhere.GetTrustAnchorInput{ - TrustAnchorId: aws.String(rs.Primary.ID), - }) - - if err == nil { - if *resp.TrustAnchor.TrustAnchorId == rs.Primary.ID { - return fmt.Errorf("Trust Anchor %s still exists", rs.Primary.ID) - } - } + _, err := tfrolesanywhere.FindTrustAnchorByID(context.TODO(), conn, rs.Primary.ID) - var resourceNotFoundException *types.ResourceNotFoundException - if errors.As(err, &resourceNotFoundException) { + if tfresource.NotFound(err) { continue } if err != nil { return err } + + return fmt.Errorf("RolesAnywhere Trust Anchor %s still exists", rs.Primary.ID) } return nil } -func testAccCheckTrustAnchorExists(name string) resource.TestCheckFunc { +func testAccCheckTrustAnchorExists(n string) 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) } if rs.Primary.ID == "" { - return fmt.Errorf("No Trust Anchor is set") + return fmt.Errorf("No RolesAnywhere Trust Anchor ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).RolesAnywhereConn @@ -168,7 +159,7 @@ func testAccCheckTrustAnchorExists(name string) resource.TestCheckFunc { _, err := tfrolesanywhere.FindTrustAnchorByID(context.TODO(), conn, rs.Primary.ID) if err != nil { - return fmt.Errorf("Error describing Trust Anchor: %s", err.Error()) + return err } return nil From 29b6a77fddb489a4ef4c39b023ad8583579de81a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 18 Jul 2022 13:36:59 -0400 Subject: [PATCH 4/5] aws_rolesanywhere_trust_anchor: Add 'TestAccRolesAnywhereTrustAnchor_certificateBundle'. --- internal/acctest/crypto.go | 62 +++++++++++++++++++ .../rolesanywhere/trust_anchor_test.go | 57 +++++++++++++++-- 2 files changed, 113 insertions(+), 6 deletions(-) diff --git a/internal/acctest/crypto.go b/internal/acctest/crypto.go index 448385158b4..02cd810cb3d 100644 --- a/internal/acctest/crypto.go +++ b/internal/acctest/crypto.go @@ -193,6 +193,68 @@ func TLSRSAX509SelfSignedCACertificatePEM(keyPem string) string { return string(pem.EncodeToMemory(certificateBlock)) } +// TLSRSAX509SelfSignedCACertificateForRolesAnywhereTrustAnchorPEM generates a x509 CA certificate PEM string. +// The CA certificate is suitable for use as an IAM RolesAnywhere Trust Anchor. +// See https://docs.aws.amazon.com/rolesanywhere/latest/userguide/trust-model.html#signature-verification. +// Wrap with TLSPEMEscapeNewlines() to allow simple fmt.Sprintf() +// configurations such as: root_certificate_pem = "%[1]s" +func TLSRSAX509SelfSignedCACertificateForRolesAnywhereTrustAnchorPEM(keyPem string) string { + keyBlock, _ := pem.Decode([]byte(keyPem)) + + key, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes) + + if err != nil { + //lintignore:R009 + panic(err) + } + + publicKeyBytes, err := x509.MarshalPKIXPublicKey(&key.PublicKey) + + if err != nil { + //lintignore:R009 + panic(err) + } + + publicKeyBytesSha1 := sha1.Sum(publicKeyBytes) + + serialNumber, err := rand.Int(rand.Reader, tlsX509CertificateSerialNumberLimit) + + if err != nil { + //lintignore:R009 + panic(err) + } + + certificate := &x509.Certificate{ + BasicConstraintsValid: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + IsCA: true, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign | x509.KeyUsageCRLSign, + NotAfter: time.Now().Add(24 * time.Hour), //nolint:gomnd + NotBefore: time.Now(), + SerialNumber: serialNumber, + SignatureAlgorithm: x509.SHA256WithRSA, + Subject: pkix.Name{ + CommonName: "ACME Root CA", + Organization: []string{"ACME Examples, Inc"}, + }, + SubjectKeyId: publicKeyBytesSha1[:], + } + + certificateBytes, err := x509.CreateCertificate(rand.Reader, certificate, certificate, &key.PublicKey, key) + + if err != nil { + //lintignore:R009 + panic(err) + } + + certificateBlock := &pem.Block{ + Bytes: certificateBytes, + Type: pemBlockTypeCertificate, + } + + return string(pem.EncodeToMemory(certificateBlock)) +} + // TLSRSAX509SelfSignedCertificatePEM generates a x509 certificate PEM string. // Wrap with TLSPEMEscapeNewlines() to allow simple fmt.Sprintf() // configurations such as: private_key_pem = "%[1]s" diff --git a/internal/service/rolesanywhere/trust_anchor_test.go b/internal/service/rolesanywhere/trust_anchor_test.go index 70eeedcf938..4476b2376b8 100644 --- a/internal/service/rolesanywhere/trust_anchor_test.go +++ b/internal/service/rolesanywhere/trust_anchor_test.go @@ -49,7 +49,6 @@ func TestAccRolesAnywhereTrustAnchor_basic(t *testing.T) { } func TestAccRolesAnywhereTrustAnchor_tags(t *testing.T) { - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) caCommonName := acctest.RandomDomainName() resourceName := "aws_rolesanywhere_trust_anchor.test" @@ -95,7 +94,6 @@ func TestAccRolesAnywhereTrustAnchor_tags(t *testing.T) { } func TestAccRolesAnywhereTrustAnchor_disappears(t *testing.T) { - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) caCommonName := acctest.RandomDomainName() resourceName := "aws_rolesanywhere_trust_anchor.test" @@ -118,6 +116,36 @@ func TestAccRolesAnywhereTrustAnchor_disappears(t *testing.T) { }) } +func TestAccRolesAnywhereTrustAnchor_certificateBundle(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_rolesanywhere_trust_anchor.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, names.RolesAnywhereEndpointID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckTrustAnchorDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTrustAnchorConfig_certificateBundle(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckTrustAnchorExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "enabled"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "source.#", "1"), + resource.TestCheckResourceAttr(resourceName, "source.0.source_data.#", "1"), + resource.TestCheckResourceAttr(resourceName, "source.0.source_type", "CERTIFICATE_BUNDLE"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckTrustAnchorDestroy(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).RolesAnywhereConn @@ -166,7 +194,7 @@ func testAccCheckTrustAnchorExists(n string) resource.TestCheckFunc { } } -func testAccTrustAnchorConfigBase(caCommonName string) string { +func testAccTrustAnchorConfig_acmBase(caCommonName string) string { return fmt.Sprintf(` resource "aws_acmpca_certificate_authority" "test" { permanent_deletion_time_in_days = 7 @@ -205,7 +233,7 @@ resource "aws_acmpca_certificate_authority_certificate" "test" { func testAccTrustAnchorConfig_basic(rName, caCommonName string) string { return acctest.ConfigCompose( - testAccTrustAnchorConfigBase(caCommonName), + testAccTrustAnchorConfig_acmBase(caCommonName), fmt.Sprintf(` resource "aws_rolesanywhere_trust_anchor" "test" { name = %[1]q @@ -222,7 +250,7 @@ resource "aws_rolesanywhere_trust_anchor" "test" { func testAccTrustAnchorConfig_tags1(rName, caCommonName, tag, value string) string { return acctest.ConfigCompose( - testAccTrustAnchorConfigBase(caCommonName), + testAccTrustAnchorConfig_acmBase(caCommonName), fmt.Sprintf(` resource "aws_rolesanywhere_trust_anchor" "test" { name = %[1]q @@ -242,7 +270,7 @@ resource "aws_rolesanywhere_trust_anchor" "test" { func testAccTrustAnchorConfig_tags2(rName, caCommonName, tag1, value1, tag2, value2 string) string { return acctest.ConfigCompose( - testAccTrustAnchorConfigBase(caCommonName), + testAccTrustAnchorConfig_acmBase(caCommonName), fmt.Sprintf(` resource "aws_rolesanywhere_trust_anchor" "test" { name = %[1]q @@ -261,6 +289,23 @@ resource "aws_rolesanywhere_trust_anchor" "test" { `, rName, tag1, value1, tag2, value2)) } +func testAccTrustAnchorConfig_certificateBundle(rName string) string { + caKey := acctest.TLSRSAPrivateKeyPEM(2048) + caCertificate := acctest.TLSRSAX509SelfSignedCACertificateForRolesAnywhereTrustAnchorPEM(caKey) + + return fmt.Sprintf(` +resource "aws_rolesanywhere_trust_anchor" "test" { + name = %[1]q + source { + source_data { + x509_certificate_data = "%[2]s" + } + source_type = "CERTIFICATE_BUNDLE" + } +} +`, rName, acctest.TLSPEMEscapeNewlines(caCertificate)) +} + func testAccPreCheck(t *testing.T) { acctest.PreCheckPartitionHasService(names.RolesAnywhereEndpointID, t) From d2c8a0f05b596a729b3ec5672ff9986b58576170 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 18 Jul 2022 13:40:21 -0400 Subject: [PATCH 5/5] Use 'ParallelTest'. --- internal/service/rolesanywhere/trust_anchor_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/service/rolesanywhere/trust_anchor_test.go b/internal/service/rolesanywhere/trust_anchor_test.go index 4476b2376b8..e4e85a211c2 100644 --- a/internal/service/rolesanywhere/trust_anchor_test.go +++ b/internal/service/rolesanywhere/trust_anchor_test.go @@ -21,7 +21,7 @@ func TestAccRolesAnywhereTrustAnchor_basic(t *testing.T) { caCommonName := acctest.RandomDomainName() resourceName := "aws_rolesanywhere_trust_anchor.test" - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, names.RolesAnywhereEndpointID), ProviderFactories: acctest.ProviderFactories, @@ -53,7 +53,7 @@ func TestAccRolesAnywhereTrustAnchor_tags(t *testing.T) { caCommonName := acctest.RandomDomainName() resourceName := "aws_rolesanywhere_trust_anchor.test" - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, names.RolesAnywhereEndpointID), ProviderFactories: acctest.ProviderFactories, @@ -98,7 +98,7 @@ func TestAccRolesAnywhereTrustAnchor_disappears(t *testing.T) { caCommonName := acctest.RandomDomainName() resourceName := "aws_rolesanywhere_trust_anchor.test" - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, names.RolesAnywhereEndpointID), ProviderFactories: acctest.ProviderFactories, @@ -120,7 +120,7 @@ func TestAccRolesAnywhereTrustAnchor_certificateBundle(t *testing.T) { rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_rolesanywhere_trust_anchor.test" - resource.Test(t, resource.TestCase{ + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, names.RolesAnywhereEndpointID), ProviderFactories: acctest.ProviderFactories,