From fa821b9662ba19fac36d7326724b8d16f31d4175 Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 29 Nov 2024 11:55:41 -0500 Subject: [PATCH 1/9] support API Gateway custom domain names for private endpoints Signed-off-by: drfaust92 --- internal/service/apigateway/domain_name.go | 116 +++++++++++++--- .../apigateway/domain_name_data_source.go | 15 ++- .../service/apigateway/domain_name_test.go | 125 +++++++++++++++++- .../d/api_gateway_domain_name.html.markdown | 2 + .../r/api_gateway_domain_name.html.markdown | 4 +- 5 files changed, 240 insertions(+), 22 deletions(-) diff --git a/internal/service/apigateway/domain_name.go b/internal/service/apigateway/domain_name.go index de5abb04b9f..a8410273ced 100644 --- a/internal/service/apigateway/domain_name.go +++ b/internal/service/apigateway/domain_name.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "log" + "strings" "time" "github.com/aws/aws-sdk-go-v2/aws" @@ -16,6 +17,7 @@ import ( "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-plugin-sdk/v2/helper/structure" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/enum" @@ -97,6 +99,10 @@ func resourceDomainName() *schema.Resource { Required: true, ForceNew: true, }, + "domain_name_id": { + Type: schema.TypeString, + Computed: true, + }, "endpoint_configuration": { Type: schema.TypeList, Optional: true, @@ -112,8 +118,8 @@ func resourceDomainName() *schema.Resource { // BadRequestException: Cannot create an api with multiple Endpoint Types MaxItems: 1, Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.StringInSlice(enum.Slice(types.EndpointTypeEdge, types.EndpointTypeRegional), false), + Type: schema.TypeString, + ValidateDiagFunc: enum.Validate[types.EndpointType](), }, }, }, @@ -142,6 +148,17 @@ func resourceDomainName() *schema.Resource { Computed: true, ValidateFunc: verify.ValidARN, }, + names.AttrPolicy: { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringIsJSON, + DiffSuppressFunc: verify.SuppressEquivalentPolicyDiffs, + DiffSuppressOnRefresh: true, + StateFunc: func(v interface{}) string { + json, _ := structure.NormalizeJsonString(v) + return json + }, + }, "regional_certificate_arn": { Type: schema.TypeString, Optional: true, @@ -213,6 +230,10 @@ func resourceDomainNameCreate(ctx context.Context, d *schema.ResourceData, meta input.OwnershipVerificationCertificateArn = aws.String(v.(string)) } + if v, ok := d.GetOk(names.AttrPolicy); ok { + input.Policy = aws.String(v.(string)) + } + if v, ok := d.GetOk("regional_certificate_arn"); ok { input.RegionalCertificateArn = aws.String(v.(string)) } @@ -231,7 +252,13 @@ func resourceDomainNameCreate(ctx context.Context, d *schema.ResourceData, meta return sdkdiag.AppendErrorf(diags, "creating API Gateway Domain Name (%s): %s", domainName, err) } - d.SetId(aws.ToString(output.DomainName)) + if output.DomainNameId != nil { + d.SetId(strings.Join([]string{aws.ToString(output.DomainName), domainNameResourceIDSeparator, aws.ToString(output.DomainNameId)}, "")) + } else { + d.SetId(aws.ToString(output.DomainName)) + } + + log.Printf("[DEBUG] API Gateway Domain Name Id is: (%s)", d.Id()) return append(diags, resourceDomainNameRead(ctx, d, meta)...) } @@ -240,7 +267,12 @@ func resourceDomainNameRead(ctx context.Context, d *schema.ResourceData, meta in var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayClient(ctx) - domainName, err := findDomainByName(ctx, conn, d.Id()) + domainNameStr, domainNameId, err := DomainNameID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + domainName, err := findDomainByName(ctx, conn, domainNameStr, domainNameId) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] API Gateway Domain Name (%s) not found, removing from state", d.Id()) @@ -270,11 +302,13 @@ func resourceDomainNameRead(ctx context.Context, d *schema.ResourceData, meta in return sdkdiag.AppendErrorf(diags, "setting mutual_tls_authentication: %s", err) } d.Set("ownership_verification_certificate_arn", domainName.OwnershipVerificationCertificateArn) + d.Set(names.AttrPolicy, domainName.Policy) d.Set("regional_certificate_arn", domainName.RegionalCertificateArn) d.Set("regional_certificate_name", domainName.RegionalCertificateName) d.Set("regional_domain_name", domainName.RegionalDomainName) d.Set("regional_zone_id", domainName.RegionalHostedZoneId) d.Set("security_policy", domainName.SecurityPolicy) + d.Set("domain_name_id", domainName.DomainNameId) setTagsOut(ctx, domainName.Tags) @@ -379,16 +413,35 @@ func resourceDomainNameUpdate(ctx context.Context, d *schema.ResourceData, meta }) } - _, err := conn.UpdateDomainName(ctx, &apigateway.UpdateDomainNameInput{ - DomainName: aws.String(d.Id()), + if d.HasChange(names.AttrPolicy) { + operations = append(operations, types.PatchOperation{ + Op: types.OpReplace, + Path: aws.String("/policy"), + Value: aws.String(d.Get(names.AttrPolicy).(string)), + }) + } + + domainName, domainNameId, err := DomainNameID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + input := &apigateway.UpdateDomainNameInput{ + DomainName: aws.String(domainName), PatchOperations: operations, - }) + } + + if domainNameId != "" { + input.DomainNameId = aws.String(domainNameId) + } + + output, err := conn.UpdateDomainName(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating API Gateway Domain Name (%s): %s", d.Id(), err) } - if _, err := waitDomainNameUpdated(ctx, conn, d.Id()); err != nil { + if _, err := waitDomainNameUpdated(ctx, conn, d.Id(), *output.DomainNameId); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for API Gateway Domain Name (%s) update: %s", d.Id(), err) } } @@ -401,9 +454,21 @@ func resourceDomainNameDelete(ctx context.Context, d *schema.ResourceData, meta conn := meta.(*conns.AWSClient).APIGatewayClient(ctx) log.Printf("[DEBUG] Deleting API Gateway Domain Name: %s", d.Id()) - _, err := conn.DeleteDomainName(ctx, &apigateway.DeleteDomainNameInput{ - DomainName: aws.String(d.Id()), - }) + + domainName, domainNameId, err := DomainNameID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + + input := &apigateway.DeleteDomainNameInput{ + DomainName: aws.String(domainName), + } + + if domainNameId != "" { + input.DomainNameId = aws.String(domainNameId) + } + + _, err = conn.DeleteDomainName(ctx, input) if errs.IsA[*types.NotFoundException](err) { return diags @@ -416,11 +481,15 @@ func resourceDomainNameDelete(ctx context.Context, d *schema.ResourceData, meta return diags } -func findDomainByName(ctx context.Context, conn *apigateway.Client, domainName string) (*apigateway.GetDomainNameOutput, error) { +func findDomainByName(ctx context.Context, conn *apigateway.Client, domainName, domainNameId string) (*apigateway.GetDomainNameOutput, error) { input := &apigateway.GetDomainNameInput{ DomainName: aws.String(domainName), } + if domainNameId != "" { + input.DomainNameId = aws.String(domainNameId) + } + output, err := conn.GetDomainName(ctx, input) if errs.IsA[*types.NotFoundException](err) { @@ -441,9 +510,9 @@ func findDomainByName(ctx context.Context, conn *apigateway.Client, domainName s return output, nil } -func statusDomainName(ctx context.Context, conn *apigateway.Client, domainName string) retry.StateRefreshFunc { +func statusDomainName(ctx context.Context, conn *apigateway.Client, domainName, domainNameId string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := findDomainByName(ctx, conn, domainName) + output, err := findDomainByName(ctx, conn, domainName, domainNameId) if tfresource.NotFound(err) { return nil, "", nil @@ -456,14 +525,14 @@ func statusDomainName(ctx context.Context, conn *apigateway.Client, domainName s } } -func waitDomainNameUpdated(ctx context.Context, conn *apigateway.Client, domainName string) (*types.DomainName, error) { +func waitDomainNameUpdated(ctx context.Context, conn *apigateway.Client, domainName, domainNameId string) (*types.DomainName, error) { const ( timeout = 15 * time.Minute ) stateConf := &retry.StateChangeConf{ Pending: enum.Slice(types.DomainNameStatusUpdating), Target: enum.Slice(types.DomainNameStatusAvailable), - Refresh: statusDomainName(ctx, conn, domainName), + Refresh: statusDomainName(ctx, conn, domainName, domainNameId), Timeout: timeout, Delay: 1 * time.Minute, MinTimeout: 10 * time.Second, @@ -521,3 +590,18 @@ func flattenMutualTLSAuthentication(apiObject *types.MutualTlsAuthentication) [] func domainNameARN(ctx context.Context, c *conns.AWSClient, domainName string) string { return c.RegionalARNNoAccount(ctx, "apigateway", fmt.Sprintf("/domainnames/%s", domainName)) } + +const domainNameResourceIDSeparator = "/" + +func DomainNameID(id string) (string, string, error) { + if !strings.Contains(id, domainNameResourceIDSeparator) { + return id, "", nil + } else { + parts := strings.Split(id, domainNameResourceIDSeparator) + if len(parts) != 2 { + return "", "", fmt.Errorf("Unexpected format of ID (%[1]s), expected DOMAIN-NAME%[2]sDOMAIN-NAME-ID", id, domainNameResourceIDSeparator) + } + + return parts[0], parts[1], nil + } +} diff --git a/internal/service/apigateway/domain_name_data_source.go b/internal/service/apigateway/domain_name_data_source.go index 2bcc319df01..b43d88fbd15 100644 --- a/internal/service/apigateway/domain_name_data_source.go +++ b/internal/service/apigateway/domain_name_data_source.go @@ -54,6 +54,10 @@ func dataSourceDomainName() *schema.Resource { Type: schema.TypeString, Required: true, }, + "domain_name_id": { + Type: schema.TypeString, + Computed: true, + }, "endpoint_configuration": { Type: schema.TypeList, Computed: true, @@ -67,6 +71,10 @@ func dataSourceDomainName() *schema.Resource { }, }, }, + names.AttrPolicy: { + Type: schema.TypeString, + Computed: true, + }, "regional_certificate_arn": { Type: schema.TypeString, Computed: true, @@ -96,8 +104,12 @@ func dataSourceDomainNameRead(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayClient(ctx) + var domainNameId string domainName := d.Get(names.AttrDomainName).(string) - output, err := findDomainByName(ctx, conn, domainName) + if v, ok := d.GetOk("domain_name_id"); ok { + domainNameId = v.(string) + } + output, err := findDomainByName(ctx, conn, domainName, domainNameId) if err != nil { return sdkdiag.AppendErrorf(diags, "reading API Gateway Domain Name (%s): %s", domainName, err) @@ -116,6 +128,7 @@ func dataSourceDomainNameRead(ctx context.Context, d *schema.ResourceData, meta if err := d.Set("endpoint_configuration", flattenEndpointConfiguration(output.EndpointConfiguration)); err != nil { return sdkdiag.AppendErrorf(diags, "setting endpoint_configuration: %s", err) } + d.Set("policy", output.Policy) d.Set("regional_certificate_arn", output.RegionalCertificateArn) d.Set("regional_certificate_name", output.RegionalCertificateName) d.Set("regional_domain_name", output.RegionalDomainName) diff --git a/internal/service/apigateway/domain_name_test.go b/internal/service/apigateway/domain_name_test.go index eefc9073fd3..70c9693932e 100644 --- a/internal/service/apigateway/domain_name_test.go +++ b/internal/service/apigateway/domain_name_test.go @@ -45,7 +45,7 @@ func TestAccAPIGatewayDomainName_certificateARN(t *testing.T) { testAccCheckResourceAttrRegionalARNEdgeDomainName(resourceName, names.AttrARN, "apigateway", domain), resource.TestCheckResourceAttrPair(resourceName, names.AttrCertificateARN, acmCertificateResourceName, names.AttrARN), resource.TestMatchResourceAttr(resourceName, "cloudfront_domain_name", regexache.MustCompile(`[0-9a-z]+.cloudfront.net`)), - resource.TestCheckResourceAttr(resourceName, "cloudfront_zone_id", "Z2FDTNDATAQYW2"), + resource.TestCheckResourceAttr(resourceName, "cloudfront_zone_id", acctest.Provider.Meta().(*conns.AWSClient).CloudFrontDistributionHostedZoneID(ctx)), resource.TestCheckResourceAttrPair(resourceName, names.AttrDomainName, acmCertificateResourceName, names.AttrDomainName), ), }, @@ -107,7 +107,7 @@ func TestAccAPIGatewayDomainName_certificateName(t *testing.T) { acctest.MatchResourceAttrRegionalARNNoAccount(resourceName, names.AttrARN, "apigateway", regexache.MustCompile(`/domainnames/.+$`)), resource.TestCheckResourceAttr(resourceName, "certificate_name", "tf-acc-apigateway-domain-name"), resource.TestCheckResourceAttrSet(resourceName, "cloudfront_domain_name"), - resource.TestCheckResourceAttr(resourceName, "cloudfront_zone_id", "Z2FDTNDATAQYW2"), + resource.TestCheckResourceAttr(resourceName, "cloudfront_zone_id", acctest.Provider.Meta().(*conns.AWSClient).CloudFrontDistributionHostedZoneID(ctx)), resource.TestCheckResourceAttr(resourceName, names.AttrDomainName, domainName), resource.TestCheckResourceAttrSet(resourceName, "certificate_upload_date"), ), @@ -229,6 +229,67 @@ func TestAccAPIGatewayDomainName_securityPolicy(t *testing.T) { }) } +func TestAccAPIGatewayDomainName_private(t *testing.T) { + ctx := acctest.Context(t) + var domainName apigateway.GetDomainNameOutput + resourceName := "aws_api_gateway_domain_name.test" + rName := acctest.RandomSubdomain() + key := acctest.TLSRSAPrivateKeyPEM(t, 2048) + certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, key, rName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.APIGatewayServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDomainNameDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDomainNameConfig_private(rName, key, certificate), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainNameExists(ctx, resourceName, &domainName), + resource.TestCheckResourceAttrSet(resourceName, "domain_name_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAPIGatewayDomainName_privatePolicy(t *testing.T) { + ctx := acctest.Context(t) + var domainName apigateway.GetDomainNameOutput + resourceName := "aws_api_gateway_domain_name.test" + rName := acctest.RandomSubdomain() + key := acctest.TLSRSAPrivateKeyPEM(t, 2048) + certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, key, rName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.APIGatewayServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDomainNameDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccDomainNameConfig_privatePolicy(rName, key, certificate), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainNameExists(ctx, resourceName, &domainName), + resource.TestCheckResourceAttrSet(resourceName, "domain_name_id"), + resource.TestCheckResourceAttrSet(resourceName, "policy"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAPIGatewayDomainName_disappears(t *testing.T) { ctx := acctest.Context(t) var domainName apigateway.GetDomainNameOutput @@ -359,7 +420,11 @@ func testAccCheckDomainNameExists(ctx context.Context, n string, v *apigateway.G conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayClient(ctx) - output, err := tfapigateway.FindDomainByName(ctx, conn, rs.Primary.ID) + domainName, domainNameId, err := tfapigateway.DomainNameID(rs.Primary.ID) + if err != nil { + return err + } + output, err := tfapigateway.FindDomainByName(ctx, conn, domainName, domainNameId) if err != nil { return err @@ -380,7 +445,11 @@ func testAccCheckDomainNameDestroy(ctx context.Context) resource.TestCheckFunc { continue } - _, err := tfapigateway.FindDomainByName(ctx, conn, rs.Primary.ID) + domainName, domainNameId, err := tfapigateway.DomainNameID(rs.Primary.ID) + if err != nil { + return err + } + _, err = tfapigateway.FindDomainByName(ctx, conn, domainName, domainNameId) if tfresource.NotFound(err) { continue @@ -531,6 +600,54 @@ resource "aws_api_gateway_domain_name" "test" { `, domainName, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key), securityPolicy) } +func testAccDomainNameConfig_private(domainName, key, certificate string) string { + return fmt.Sprintf(` +resource "aws_acm_certificate" "test" { + certificate_body = "%[2]s" + private_key = "%[3]s" +} + +resource "aws_api_gateway_domain_name" "test" { + domain_name = %[1]q + certificate_arn = aws_acm_certificate.test.arn + + endpoint_configuration { + types = ["PRIVATE"] + } +} +`, domainName, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key)) +} + +func testAccDomainNameConfig_privatePolicy(domainName, key, certificate string) string { + return fmt.Sprintf(` +resource "aws_acm_certificate" "test" { + certificate_body = "%[2]s" + private_key = "%[3]s" +} + +resource "aws_api_gateway_domain_name" "test" { + domain_name = %[1]q + certificate_arn = aws_acm_certificate.test.arn + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Principal = { + AWS = "*" + } + Action = "execute-api:Invoke" + Resource = "*" + }] + }) + + endpoint_configuration { + types = ["PRIVATE"] + } +} +`, domainName, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key)) +} + func testAccDomainNameConfig_mutualTLSAuthentication(rName, rootDomain, domain string) string { return acctest.ConfigCompose( testAccDomainNameConfig_basePublicCert(rootDomain, domain), diff --git a/website/docs/d/api_gateway_domain_name.html.markdown b/website/docs/d/api_gateway_domain_name.html.markdown index a02d4393182..a151733180b 100644 --- a/website/docs/d/api_gateway_domain_name.html.markdown +++ b/website/docs/d/api_gateway_domain_name.html.markdown @@ -21,6 +21,7 @@ data "aws_api_gateway_domain_name" "example" { ## Argument Reference * `domain_name` - (Required) Fully-qualified domain name to look up. If no domain name is found, an error will be returned. +* `domain_name_id` - (Optional) The identifier for the domain name resource. Supported only for private custom domain names. ## Attribute Reference @@ -34,6 +35,7 @@ This data source exports the following attributes in addition to the arguments a * `cloudfront_zone_id` - For convenience, the hosted zone ID (`Z2FDTNDATAQYW2`) that can be used to create a Route53 alias record for the distribution. * `endpoint_configuration` - List of objects with the endpoint configuration of this domain name. * `types` - List of endpoint types. +* `policy` - A stringified JSON policy document that applies to the execute-api service for this DomainName regardless of the caller and Method configuration. Supported only for private custom domain names. * `regional_certificate_arn` - ARN for an AWS-managed certificate that is used for validating the regional domain name. * `regional_certificate_name` - User-friendly name of the certificate that is used by regional endpoint for this domain name. * `regional_domain_name` - Hostname for the custom domain's regional endpoint. diff --git a/website/docs/r/api_gateway_domain_name.html.markdown b/website/docs/r/api_gateway_domain_name.html.markdown index b74e6d4721a..2b6ec8a9fb0 100644 --- a/website/docs/r/api_gateway_domain_name.html.markdown +++ b/website/docs/r/api_gateway_domain_name.html.markdown @@ -157,6 +157,7 @@ This resource supports the following arguments: * `domain_name` - (Required) Fully-qualified domain name to register. * `endpoint_configuration` - (Optional) Configuration block defining API endpoint information including type. See below. * `mutual_tls_authentication` - (Optional) Mutual TLS authentication configuration for the domain name. See below. +* `policy` - (Optional) A stringified JSON policy document that applies to the execute-api service for this DomainName regardless of the caller and Method configuration. Supported only for private custom domain names. * `ownership_verification_certificate_arn` - (Optional) ARN of the AWS-issued certificate used to validate custom domain ownership (when `certificate_arn` is issued via an ACM Private CA or `mutual_tls_authentication` is configured with an ACM-imported certificate.) * `security_policy` - (Optional) Transport Layer Security (TLS) version + cipher suite for this DomainName. Valid values are `TLS_1_0` and `TLS_1_2`. Must be configured to perform drift detection. * `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. @@ -176,7 +177,7 @@ When uploading a certificate, the following arguments are supported: ### endpoint_configuration -* `types` - (Required) List of endpoint types. This resource currently only supports managing a single value. Valid values: `EDGE` or `REGIONAL`. If unspecified, defaults to `EDGE`. Must be declared as `REGIONAL` in non-Commercial partitions. Refer to the [documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/create-regional-api.html) for more information on the difference between edge-optimized and regional APIs. +* `types` - (Required) A list of endpoint types of an API or its custom domain name. For an edge-optimized API and its custom domain name, the endpoint type is `EDGE`. For a regional API and its custom domain name, the endpoint type is `REGIONAL`. For a private API, the endpoint type is `PRIVATE`. ### mutual_tls_authentication @@ -191,6 +192,7 @@ This resource exports the following attributes in addition to the arguments abov * `certificate_upload_date` - Upload date associated with the domain certificate. * `cloudfront_domain_name` - Hostname created by Cloudfront to represent the distribution that implements this domain name mapping. * `cloudfront_zone_id` - For convenience, the hosted zone ID (`Z2FDTNDATAQYW2`) that can be used to create a Route53 alias record for the distribution. +* `domain_name_id` - The identifier for the domain name resource. Supported only for private custom domain names. * `id` - Internal identifier assigned to this domain name by API Gateway. * `regional_domain_name` - Hostname for the custom domain's regional endpoint. * `regional_zone_id` - Hosted zone ID that can be used to create a Route53 alias record for the regional endpoint. From 011bd1f6e03090c4cd20e553e24ff1199a88bd4f Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 29 Nov 2024 12:04:47 -0500 Subject: [PATCH 2/9] changelog --- .changelog/40364.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .changelog/40364.txt diff --git a/.changelog/40364.txt b/.changelog/40364.txt new file mode 100644 index 00000000000..0b04b9d94c4 --- /dev/null +++ b/.changelog/40364.txt @@ -0,0 +1,15 @@ +```release-note:enhancement +resource/aws_apigateway_domain_name: Add support for `policy` argument +``` + +```release-note:enhancement +resource/aws_apigateway_domain_name: Add support for value `PRIVATE` for the `endpoint_configuration.types` argument +``` + +```release-note:enhancement +resource/aws_apigateway_domain_name: Add support for `domain_name_id` attribute +``` + +```release-note:enhancement +data-source/aws_apigateway_domain_name: Add support for `policy` and `domain_name_id` attributes +``` \ No newline at end of file From 21ab2686ccfa83f4e0ba52fd3b825eeee6fa133a Mon Sep 17 00:00:00 2001 From: drfaust92 Date: Fri, 29 Nov 2024 15:14:55 -0500 Subject: [PATCH 3/9] support API Gateway custom domain names for private endpoints Signed-off-by: drfaust92 --- internal/service/apigateway/domain_name_data_source.go | 2 +- internal/service/apigateway/domain_name_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/apigateway/domain_name_data_source.go b/internal/service/apigateway/domain_name_data_source.go index b43d88fbd15..fc419412195 100644 --- a/internal/service/apigateway/domain_name_data_source.go +++ b/internal/service/apigateway/domain_name_data_source.go @@ -128,7 +128,7 @@ func dataSourceDomainNameRead(ctx context.Context, d *schema.ResourceData, meta if err := d.Set("endpoint_configuration", flattenEndpointConfiguration(output.EndpointConfiguration)); err != nil { return sdkdiag.AppendErrorf(diags, "setting endpoint_configuration: %s", err) } - d.Set("policy", output.Policy) + d.Set(names.AttrPolicy, output.Policy) d.Set("regional_certificate_arn", output.RegionalCertificateArn) d.Set("regional_certificate_name", output.RegionalCertificateName) d.Set("regional_domain_name", output.RegionalDomainName) diff --git a/internal/service/apigateway/domain_name_test.go b/internal/service/apigateway/domain_name_test.go index 70c9693932e..5b64ede7cda 100644 --- a/internal/service/apigateway/domain_name_test.go +++ b/internal/service/apigateway/domain_name_test.go @@ -278,7 +278,7 @@ func TestAccAPIGatewayDomainName_privatePolicy(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckDomainNameExists(ctx, resourceName, &domainName), resource.TestCheckResourceAttrSet(resourceName, "domain_name_id"), - resource.TestCheckResourceAttrSet(resourceName, "policy"), + resource.TestCheckResourceAttrSet(resourceName, names.AttrPolicy), ), }, { From b30e7070ab53ec43579e24d0ec794f7f89aa4752 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 4 Dec 2024 15:07:16 -0500 Subject: [PATCH 4/9] Tweak CHANGELOG entries. --- .changelog/40364.txt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.changelog/40364.txt b/.changelog/40364.txt index 0b04b9d94c4..98d547e5c8d 100644 --- a/.changelog/40364.txt +++ b/.changelog/40364.txt @@ -1,15 +1,11 @@ ```release-note:enhancement -resource/aws_apigateway_domain_name: Add support for `policy` argument +resource/aws_api_gateway_domain_name: Add `policy` argument and `domain_name_id` attribute ``` ```release-note:enhancement -resource/aws_apigateway_domain_name: Add support for value `PRIVATE` for the `endpoint_configuration.types` argument +resource/aws_api_gateway_domain_name: Support `PRIVATE` as a valid value for `endpoint_configuration.types` argument, enabling custom domain name support for private REST API endpoints ``` ```release-note:enhancement -resource/aws_apigateway_domain_name: Add support for `domain_name_id` attribute -``` - -```release-note:enhancement -data-source/aws_apigateway_domain_name: Add support for `policy` and `domain_name_id` attributes +data-source/aws_api_gateway_domain_name: Add `policy` and `domain_name_id` attributes ``` \ No newline at end of file From d1d0cfbdf1516d8e713091e5378fb025e7f41989 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 4 Dec 2024 15:29:22 -0500 Subject: [PATCH 5/9] Cosmetics. --- internal/service/apigateway/domain_name.go | 156 +++++++++--------- .../apigateway/domain_name_data_source.go | 10 +- .../domain_name_data_source_test.go | 1 + .../service/apigateway/domain_name_test.go | 12 +- internal/service/apigateway/exports_test.go | 2 +- 5 files changed, 91 insertions(+), 90 deletions(-) diff --git a/internal/service/apigateway/domain_name.go b/internal/service/apigateway/domain_name.go index a8410273ced..3822f46f1a1 100644 --- a/internal/service/apigateway/domain_name.go +++ b/internal/service/apigateway/domain_name.go @@ -252,13 +252,7 @@ func resourceDomainNameCreate(ctx context.Context, d *schema.ResourceData, meta return sdkdiag.AppendErrorf(diags, "creating API Gateway Domain Name (%s): %s", domainName, err) } - if output.DomainNameId != nil { - d.SetId(strings.Join([]string{aws.ToString(output.DomainName), domainNameResourceIDSeparator, aws.ToString(output.DomainNameId)}, "")) - } else { - d.SetId(aws.ToString(output.DomainName)) - } - - log.Printf("[DEBUG] API Gateway Domain Name Id is: (%s)", d.Id()) + d.SetId(domainNameCreateResourceID(aws.ToString(output.DomainName), aws.ToString(output.DomainNameId))) return append(diags, resourceDomainNameRead(ctx, d, meta)...) } @@ -267,12 +261,12 @@ func resourceDomainNameRead(ctx context.Context, d *schema.ResourceData, meta in var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayClient(ctx) - domainNameStr, domainNameId, err := DomainNameID(d.Id()) + domainName, domainNameID, err := domainNameParseResourceID(d.Id()) if err != nil { return sdkdiag.AppendFromErr(diags, err) } - domainName, err := findDomainByName(ctx, conn, domainNameStr, domainNameId) + output, err := findDomainNameByTwoPartKey(ctx, conn, domainName, domainNameID) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] API Gateway Domain Name (%s) not found, removing from state", d.Id()) @@ -285,32 +279,32 @@ func resourceDomainNameRead(ctx context.Context, d *schema.ResourceData, meta in } d.Set(names.AttrARN, domainNameARN(ctx, meta.(*conns.AWSClient), d.Id())) - d.Set(names.AttrCertificateARN, domainName.CertificateArn) - d.Set("certificate_name", domainName.CertificateName) - if domainName.CertificateUploadDate != nil { - d.Set("certificate_upload_date", domainName.CertificateUploadDate.Format(time.RFC3339)) + d.Set(names.AttrCertificateARN, output.CertificateArn) + d.Set("certificate_name", output.CertificateName) + if output.CertificateUploadDate != nil { + d.Set("certificate_upload_date", output.CertificateUploadDate.Format(time.RFC3339)) } else { d.Set("certificate_upload_date", nil) } - d.Set("cloudfront_domain_name", domainName.DistributionDomainName) + d.Set("cloudfront_domain_name", output.DistributionDomainName) d.Set("cloudfront_zone_id", meta.(*conns.AWSClient).CloudFrontDistributionHostedZoneID(ctx)) - d.Set(names.AttrDomainName, domainName.DomainName) - if err := d.Set("endpoint_configuration", flattenEndpointConfiguration(domainName.EndpointConfiguration)); err != nil { + d.Set(names.AttrDomainName, output.DomainName) + d.Set("domain_name_id", output.DomainNameId) + if err := d.Set("endpoint_configuration", flattenEndpointConfiguration(output.EndpointConfiguration)); err != nil { return sdkdiag.AppendErrorf(diags, "setting endpoint_configuration: %s", err) } - if err = d.Set("mutual_tls_authentication", flattenMutualTLSAuthentication(domainName.MutualTlsAuthentication)); err != nil { + if err = d.Set("mutual_tls_authentication", flattenMutualTLSAuthentication(output.MutualTlsAuthentication)); err != nil { return sdkdiag.AppendErrorf(diags, "setting mutual_tls_authentication: %s", err) } - d.Set("ownership_verification_certificate_arn", domainName.OwnershipVerificationCertificateArn) - d.Set(names.AttrPolicy, domainName.Policy) - d.Set("regional_certificate_arn", domainName.RegionalCertificateArn) - d.Set("regional_certificate_name", domainName.RegionalCertificateName) - d.Set("regional_domain_name", domainName.RegionalDomainName) - d.Set("regional_zone_id", domainName.RegionalHostedZoneId) - d.Set("security_policy", domainName.SecurityPolicy) - d.Set("domain_name_id", domainName.DomainNameId) + d.Set("ownership_verification_certificate_arn", output.OwnershipVerificationCertificateArn) + d.Set(names.AttrPolicy, output.Policy) + d.Set("regional_certificate_arn", output.RegionalCertificateArn) + d.Set("regional_certificate_name", output.RegionalCertificateName) + d.Set("regional_domain_name", output.RegionalDomainName) + d.Set("regional_zone_id", output.RegionalHostedZoneId) + d.Set("security_policy", output.SecurityPolicy) - setTagsOut(ctx, domainName.Tags) + setTagsOut(ctx, output.Tags) return diags } @@ -319,6 +313,11 @@ func resourceDomainNameUpdate(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayClient(ctx) + domainName, domainNameID, err := domainNameParseResourceID(d.Id()) + if err != nil { + return sdkdiag.AppendFromErr(diags, err) + } + if d.HasChangesExcept(names.AttrTags, names.AttrTagsAll) { var operations []types.PatchOperation @@ -342,12 +341,12 @@ func resourceDomainNameUpdate(ctx context.Context, d *schema.ResourceData, meta // The domain name must have an endpoint type. // If attempting to remove the configuration, do nothing. if v, ok := d.GetOk("endpoint_configuration"); ok && len(v.([]interface{})) > 0 { - m := v.([]interface{})[0].(map[string]interface{}) + tfMap := v.([]interface{})[0].(map[string]interface{}) operations = append(operations, types.PatchOperation{ Op: types.OpReplace, Path: aws.String("/endpointConfiguration/types/0"), - Value: aws.String(m["types"].([]interface{})[0].(string)), + Value: aws.String(tfMap["types"].([]interface{})[0].(string)), }) } } @@ -389,6 +388,14 @@ func resourceDomainNameUpdate(ctx context.Context, d *schema.ResourceData, meta }) } + if d.HasChange(names.AttrPolicy) { + operations = append(operations, types.PatchOperation{ + Op: types.OpReplace, + Path: aws.String("/policy"), + Value: aws.String(d.Get(names.AttrPolicy).(string)), + }) + } + if d.HasChange("regional_certificate_arn") { operations = append(operations, types.PatchOperation{ Op: types.OpReplace, @@ -413,35 +420,21 @@ func resourceDomainNameUpdate(ctx context.Context, d *schema.ResourceData, meta }) } - if d.HasChange(names.AttrPolicy) { - operations = append(operations, types.PatchOperation{ - Op: types.OpReplace, - Path: aws.String("/policy"), - Value: aws.String(d.Get(names.AttrPolicy).(string)), - }) - } - - domainName, domainNameId, err := DomainNameID(d.Id()) - if err != nil { - return sdkdiag.AppendFromErr(diags, err) - } - input := &apigateway.UpdateDomainNameInput{ DomainName: aws.String(domainName), PatchOperations: operations, } - - if domainNameId != "" { - input.DomainNameId = aws.String(domainNameId) + if domainNameID != "" { + input.DomainNameId = aws.String(domainNameID) } - output, err := conn.UpdateDomainName(ctx, input) + _, err := conn.UpdateDomainName(ctx, input) if err != nil { return sdkdiag.AppendErrorf(diags, "updating API Gateway Domain Name (%s): %s", d.Id(), err) } - if _, err := waitDomainNameUpdated(ctx, conn, d.Id(), *output.DomainNameId); err != nil { + if _, err := waitDomainNameUpdated(ctx, conn, d.Id(), domainNameID); err != nil { return sdkdiag.AppendErrorf(diags, "waiting for API Gateway Domain Name (%s) update: %s", d.Id(), err) } } @@ -453,9 +446,7 @@ func resourceDomainNameDelete(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayClient(ctx) - log.Printf("[DEBUG] Deleting API Gateway Domain Name: %s", d.Id()) - - domainName, domainNameId, err := DomainNameID(d.Id()) + domainName, domainNameID, err := domainNameParseResourceID(d.Id()) if err != nil { return sdkdiag.AppendFromErr(diags, err) } @@ -463,11 +454,11 @@ func resourceDomainNameDelete(ctx context.Context, d *schema.ResourceData, meta input := &apigateway.DeleteDomainNameInput{ DomainName: aws.String(domainName), } - - if domainNameId != "" { - input.DomainNameId = aws.String(domainNameId) + if domainNameID != "" { + input.DomainNameId = aws.String(domainNameID) } + log.Printf("[DEBUG] Deleting API Gateway Domain Name: %s", d.Id()) _, err = conn.DeleteDomainName(ctx, input) if errs.IsA[*types.NotFoundException](err) { @@ -481,13 +472,42 @@ func resourceDomainNameDelete(ctx context.Context, d *schema.ResourceData, meta return diags } -func findDomainByName(ctx context.Context, conn *apigateway.Client, domainName, domainNameId string) (*apigateway.GetDomainNameOutput, error) { +const domainNameResourceIDSeparator = "/" + +func domainNameCreateResourceID(domainName, domainNameID string) string { + var id string + + if domainNameID == "" { + id = domainName + } else { + parts := []string{domainName, domainNameID} + id = strings.Join(parts, domainNameResourceIDSeparator) + } + + return id +} + +func domainNameParseResourceID(id string) (string, string, error) { + switch parts := strings.SplitN(id, domainNameResourceIDSeparator, 2); len(parts) { + case 1: + if domainName := parts[0]; domainName != "" { + return domainName, "", nil + } + case 2: + if domainName, domainNameID := parts[0], parts[1]; domainName != "" && domainNameID != "" { + return domainName, domainNameID, nil + } + } + + return "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected DOMAIN-NAME or DOMAIN-NAME%[2]sDOMAIN-NAME-ID", id, domainNameResourceIDSeparator) +} + +func findDomainNameByTwoPartKey(ctx context.Context, conn *apigateway.Client, domainName, domainNameID string) (*apigateway.GetDomainNameOutput, error) { input := &apigateway.GetDomainNameInput{ DomainName: aws.String(domainName), } - - if domainNameId != "" { - input.DomainNameId = aws.String(domainNameId) + if domainNameID != "" { + input.DomainNameId = aws.String(domainNameID) } output, err := conn.GetDomainName(ctx, input) @@ -510,13 +530,14 @@ func findDomainByName(ctx context.Context, conn *apigateway.Client, domainName, return output, nil } -func statusDomainName(ctx context.Context, conn *apigateway.Client, domainName, domainNameId string) retry.StateRefreshFunc { +func statusDomainName(ctx context.Context, conn *apigateway.Client, domainName, domainNameID string) retry.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := findDomainByName(ctx, conn, domainName, domainNameId) + output, err := findDomainNameByTwoPartKey(ctx, conn, domainName, domainNameID) if tfresource.NotFound(err) { return nil, "", nil } + if err != nil { return nil, "", err } @@ -525,14 +546,14 @@ func statusDomainName(ctx context.Context, conn *apigateway.Client, domainName, } } -func waitDomainNameUpdated(ctx context.Context, conn *apigateway.Client, domainName, domainNameId string) (*types.DomainName, error) { +func waitDomainNameUpdated(ctx context.Context, conn *apigateway.Client, domainName, domainNameID string) (*types.DomainName, error) { const ( timeout = 15 * time.Minute ) stateConf := &retry.StateChangeConf{ Pending: enum.Slice(types.DomainNameStatusUpdating), Target: enum.Slice(types.DomainNameStatusAvailable), - Refresh: statusDomainName(ctx, conn, domainName, domainNameId), + Refresh: statusDomainName(ctx, conn, domainName, domainNameID), Timeout: timeout, Delay: 1 * time.Minute, MinTimeout: 10 * time.Second, @@ -590,18 +611,3 @@ func flattenMutualTLSAuthentication(apiObject *types.MutualTlsAuthentication) [] func domainNameARN(ctx context.Context, c *conns.AWSClient, domainName string) string { return c.RegionalARNNoAccount(ctx, "apigateway", fmt.Sprintf("/domainnames/%s", domainName)) } - -const domainNameResourceIDSeparator = "/" - -func DomainNameID(id string) (string, string, error) { - if !strings.Contains(id, domainNameResourceIDSeparator) { - return id, "", nil - } else { - parts := strings.Split(id, domainNameResourceIDSeparator) - if len(parts) != 2 { - return "", "", fmt.Errorf("Unexpected format of ID (%[1]s), expected DOMAIN-NAME%[2]sDOMAIN-NAME-ID", id, domainNameResourceIDSeparator) - } - - return parts[0], parts[1], nil - } -} diff --git a/internal/service/apigateway/domain_name_data_source.go b/internal/service/apigateway/domain_name_data_source.go index fc419412195..e78e68a5450 100644 --- a/internal/service/apigateway/domain_name_data_source.go +++ b/internal/service/apigateway/domain_name_data_source.go @@ -56,6 +56,7 @@ func dataSourceDomainName() *schema.Resource { }, "domain_name_id": { Type: schema.TypeString, + Optional: true, Computed: true, }, "endpoint_configuration": { @@ -104,15 +105,15 @@ func dataSourceDomainNameRead(ctx context.Context, d *schema.ResourceData, meta var diags diag.Diagnostics conn := meta.(*conns.AWSClient).APIGatewayClient(ctx) - var domainNameId string + var domainNameID string domainName := d.Get(names.AttrDomainName).(string) if v, ok := d.GetOk("domain_name_id"); ok { - domainNameId = v.(string) + domainNameID = v.(string) } - output, err := findDomainByName(ctx, conn, domainName, domainNameId) + output, err := findDomainNameByTwoPartKey(ctx, conn, domainName, domainNameID) if err != nil { - return sdkdiag.AppendErrorf(diags, "reading API Gateway Domain Name (%s): %s", domainName, err) + return sdkdiag.AppendErrorf(diags, "reading API Gateway Domain Name (%s): %s", domainNameCreateResourceID(domainName, domainNameID), err) } d.SetId(aws.ToString(output.DomainName)) @@ -125,6 +126,7 @@ func dataSourceDomainNameRead(ctx context.Context, d *schema.ResourceData, meta d.Set("cloudfront_domain_name", output.DistributionDomainName) d.Set("cloudfront_zone_id", meta.(*conns.AWSClient).CloudFrontDistributionHostedZoneID(ctx)) d.Set(names.AttrDomainName, output.DomainName) + d.Set("domain_name_id", output.DomainNameId) if err := d.Set("endpoint_configuration", flattenEndpointConfiguration(output.EndpointConfiguration)); err != nil { return sdkdiag.AppendErrorf(diags, "setting endpoint_configuration: %s", err) } diff --git a/internal/service/apigateway/domain_name_data_source_test.go b/internal/service/apigateway/domain_name_data_source_test.go index 000c3e08349..f594d37c2c4 100644 --- a/internal/service/apigateway/domain_name_data_source_test.go +++ b/internal/service/apigateway/domain_name_data_source_test.go @@ -36,6 +36,7 @@ func TestAccAPIGatewayDomainNameDataSource_basic(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "cloudfront_domain_name", dataSourceName, "cloudfront_domain_name"), resource.TestCheckResourceAttrPair(resourceName, "cloudfront_zone_id", dataSourceName, "cloudfront_zone_id"), resource.TestCheckResourceAttrPair(resourceName, names.AttrDomainName, dataSourceName, names.AttrDomainName), + resource.TestCheckResourceAttrPair(resourceName, "domain_name_id", dataSourceName, "domain_name_id"), resource.TestCheckResourceAttrPair(resourceName, "endpoint_configuration.#", dataSourceName, "endpoint_configuration.#"), resource.TestCheckResourceAttrPair(resourceName, "regional_certificate_arn", dataSourceName, "regional_certificate_arn"), resource.TestCheckResourceAttrPair(resourceName, "regional_certificate_name", dataSourceName, "regional_certificate_name"), diff --git a/internal/service/apigateway/domain_name_test.go b/internal/service/apigateway/domain_name_test.go index 5b64ede7cda..73951b6bc67 100644 --- a/internal/service/apigateway/domain_name_test.go +++ b/internal/service/apigateway/domain_name_test.go @@ -420,11 +420,7 @@ func testAccCheckDomainNameExists(ctx context.Context, n string, v *apigateway.G conn := acctest.Provider.Meta().(*conns.AWSClient).APIGatewayClient(ctx) - domainName, domainNameId, err := tfapigateway.DomainNameID(rs.Primary.ID) - if err != nil { - return err - } - output, err := tfapigateway.FindDomainByName(ctx, conn, domainName, domainNameId) + output, err := tfapigateway.FindDomainNameByTwoPartKey(ctx, conn, rs.Primary.Attributes[names.AttrDomainName], rs.Primary.Attributes["domain_name_id"]) if err != nil { return err @@ -445,11 +441,7 @@ func testAccCheckDomainNameDestroy(ctx context.Context) resource.TestCheckFunc { continue } - domainName, domainNameId, err := tfapigateway.DomainNameID(rs.Primary.ID) - if err != nil { - return err - } - _, err = tfapigateway.FindDomainByName(ctx, conn, domainName, domainNameId) + _, err := tfapigateway.FindDomainNameByTwoPartKey(ctx, conn, rs.Primary.Attributes[names.AttrDomainName], rs.Primary.Attributes["domain_name_id"]) if tfresource.NotFound(err) { continue diff --git a/internal/service/apigateway/exports_test.go b/internal/service/apigateway/exports_test.go index 8933b86d518..db693178cfd 100644 --- a/internal/service/apigateway/exports_test.go +++ b/internal/service/apigateway/exports_test.go @@ -38,7 +38,7 @@ var ( FindDeploymentByTwoPartKey = findDeploymentByTwoPartKey FindDocumentationPartByTwoPartKey = findDocumentationPartByTwoPartKey FindDocumentationVersionByTwoPartKey = findDocumentationVersionByTwoPartKey - FindDomainByName = findDomainByName + FindDomainNameByTwoPartKey = findDomainNameByTwoPartKey FindGatewayResponseByTwoPartKey = findGatewayResponseByTwoPartKey FindIntegrationByThreePartKey = findIntegrationByThreePartKey FindIntegrationResponseByFourPartKey = findIntegrationResponseByFourPartKey From 6a6d2e380c6a936d87dababae01aeb50b18496f0 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 4 Dec 2024 15:36:00 -0500 Subject: [PATCH 6/9] Add 'TestAccAPIGatewayDomainName_updateIDFormat'. --- .../service/apigateway/domain_name_test.go | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/internal/service/apigateway/domain_name_test.go b/internal/service/apigateway/domain_name_test.go index 73951b6bc67..eee680a33d1 100644 --- a/internal/service/apigateway/domain_name_test.go +++ b/internal/service/apigateway/domain_name_test.go @@ -411,6 +411,44 @@ func TestAccAPIGatewayDomainName_MutualTLSAuthentication_ownership(t *testing.T) }) } +func TestAccAPIGatewayDomainName_updateIDFormat(t *testing.T) { + ctx := acctest.Context(t) + var domainName apigateway.GetDomainNameOutput + resourceName := "aws_api_gateway_domain_name.test" + rName := acctest.RandomSubdomain() + key := acctest.TLSRSAPrivateKeyPEM(t, 2048) + certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(t, key, rName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t) }, + ErrorCheck: acctest.ErrorCheck(t, names.APIGatewayServiceID), + CheckDestroy: testAccCheckDomainNameDestroy(ctx), + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "aws": { + Source: "hashicorp/aws", + VersionConstraint: "5.80.0", + }, + }, + Config: testAccDomainNameConfig_regionalCertificateARN(rName, key, certificate), + Check: resource.ComposeTestCheckFunc( + testAccCheckDomainNameExists(ctx, resourceName, &domainName), + testAccCheckResourceAttrRegionalARNRegionalDomainName(resourceName, names.AttrARN, "apigateway", rName), + resource.TestCheckResourceAttr(resourceName, names.AttrDomainName, rName), + acctest.MatchResourceAttrRegionalHostname(resourceName, "regional_domain_name", "execute-api", regexache.MustCompile(`d-[0-9a-z]+`)), + resource.TestMatchResourceAttr(resourceName, "regional_zone_id", regexache.MustCompile(`^Z`)), + ), + }, + { + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Config: testAccDomainNameConfig_regionalCertificateARN(rName, key, certificate), + PlanOnly: true, + }, + }, + }) +} + func testAccCheckDomainNameExists(ctx context.Context, n string, v *apigateway.GetDomainNameOutput) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] From 6e3cbf947e6c40b596c112878cf2ffe75ace90f5 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 4 Dec 2024 16:34:29 -0500 Subject: [PATCH 7/9] Use ',' as resource ID separator. --- internal/service/apigateway/domain_name.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/service/apigateway/domain_name.go b/internal/service/apigateway/domain_name.go index 3822f46f1a1..2ba789aa05d 100644 --- a/internal/service/apigateway/domain_name.go +++ b/internal/service/apigateway/domain_name.go @@ -23,6 +23,7 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/enum" "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" @@ -472,7 +473,7 @@ func resourceDomainNameDelete(ctx context.Context, d *schema.ResourceData, meta return diags } -const domainNameResourceIDSeparator = "/" +const domainNameResourceIDSeparator = flex.ResourceIdSeparator func domainNameCreateResourceID(domainName, domainNameID string) string { var id string From 336b3876e60d7044aa5fa16e3eadb1d7d087185c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 4 Dec 2024 16:36:50 -0500 Subject: [PATCH 8/9] Tweak acceptance tests. --- internal/service/apigateway/domain_name_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/service/apigateway/domain_name_test.go b/internal/service/apigateway/domain_name_test.go index eee680a33d1..7194ad71187 100644 --- a/internal/service/apigateway/domain_name_test.go +++ b/internal/service/apigateway/domain_name_test.go @@ -47,6 +47,7 @@ func TestAccAPIGatewayDomainName_certificateARN(t *testing.T) { resource.TestMatchResourceAttr(resourceName, "cloudfront_domain_name", regexache.MustCompile(`[0-9a-z]+.cloudfront.net`)), resource.TestCheckResourceAttr(resourceName, "cloudfront_zone_id", acctest.Provider.Meta().(*conns.AWSClient).CloudFrontDistributionHostedZoneID(ctx)), resource.TestCheckResourceAttrPair(resourceName, names.AttrDomainName, acmCertificateResourceName, names.AttrDomainName), + resource.TestCheckResourceAttr(resourceName, "domain_name_id", ""), ), }, { @@ -434,10 +435,7 @@ func TestAccAPIGatewayDomainName_updateIDFormat(t *testing.T) { Config: testAccDomainNameConfig_regionalCertificateARN(rName, key, certificate), Check: resource.ComposeTestCheckFunc( testAccCheckDomainNameExists(ctx, resourceName, &domainName), - testAccCheckResourceAttrRegionalARNRegionalDomainName(resourceName, names.AttrARN, "apigateway", rName), - resource.TestCheckResourceAttr(resourceName, names.AttrDomainName, rName), - acctest.MatchResourceAttrRegionalHostname(resourceName, "regional_domain_name", "execute-api", regexache.MustCompile(`d-[0-9a-z]+`)), - resource.TestMatchResourceAttr(resourceName, "regional_zone_id", regexache.MustCompile(`^Z`)), + resource.TestCheckNoResourceAttr(resourceName, "domain_name_id"), ), }, { From 179d02a6ee3f8096987c5fb8c321234d73743f4b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 4 Dec 2024 16:42:14 -0500 Subject: [PATCH 9/9] Tweak acceptance tests. --- internal/service/apigateway/domain_name_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/apigateway/domain_name_test.go b/internal/service/apigateway/domain_name_test.go index 7194ad71187..0cb3f47b4c6 100644 --- a/internal/service/apigateway/domain_name_test.go +++ b/internal/service/apigateway/domain_name_test.go @@ -47,7 +47,6 @@ func TestAccAPIGatewayDomainName_certificateARN(t *testing.T) { resource.TestMatchResourceAttr(resourceName, "cloudfront_domain_name", regexache.MustCompile(`[0-9a-z]+.cloudfront.net`)), resource.TestCheckResourceAttr(resourceName, "cloudfront_zone_id", acctest.Provider.Meta().(*conns.AWSClient).CloudFrontDistributionHostedZoneID(ctx)), resource.TestCheckResourceAttrPair(resourceName, names.AttrDomainName, acmCertificateResourceName, names.AttrDomainName), - resource.TestCheckResourceAttr(resourceName, "domain_name_id", ""), ), }, { @@ -143,6 +142,7 @@ func TestAccAPIGatewayDomainName_regionalCertificateARN(t *testing.T) { testAccCheckDomainNameExists(ctx, resourceName, &domainName), testAccCheckResourceAttrRegionalARNRegionalDomainName(resourceName, names.AttrARN, "apigateway", rName), resource.TestCheckResourceAttr(resourceName, names.AttrDomainName, rName), + resource.TestCheckResourceAttr(resourceName, "domain_name_id", ""), acctest.MatchResourceAttrRegionalHostname(resourceName, "regional_domain_name", "execute-api", regexache.MustCompile(`d-[0-9a-z]+`)), resource.TestMatchResourceAttr(resourceName, "regional_zone_id", regexache.MustCompile(`^Z`)), ),