diff --git a/aws/provider_test.go b/aws/provider_test.go index c98da9c9d91d..7ab6632fa66f 100644 --- a/aws/provider_test.go +++ b/aws/provider_test.go @@ -128,6 +128,26 @@ func testAccMatchResourceAttrRegionalARN(resourceName, attributeName, arnService } } +// testAccMatchResourceAttrRegionalARN ensures the Terraform state regexp matches a formatted ARN with region and no account id +func testAccMatchResourceAttrRegionalARNNoAccount(resourceName, attributeName, arnService string, arnResourceRegexp *regexp.Regexp) resource.TestCheckFunc { + return func(s *terraform.State) error { + arnRegexp := arn.ARN{ + Partition: testAccGetPartition(), + Region: testAccGetRegion(), + Resource: arnResourceRegexp.String(), + Service: arnService, + }.String() + + attributeMatch, err := regexp.Compile(arnRegexp) + + if err != nil { + return fmt.Errorf("Unable to compile ARN regexp (%s): %s", arnRegexp, err) + } + + return resource.TestMatchResourceAttr(resourceName, attributeName, attributeMatch)(s) + } +} + // testAccCheckResourceAttrGlobalARN ensures the Terraform state exactly matches a formatted ARN without region func testAccCheckResourceAttrGlobalARN(resourceName, attributeName, arnService, arnResource string) resource.TestCheckFunc { return func(s *terraform.State) error { diff --git a/aws/resource_aws_api_gateway_domain_name.go b/aws/resource_aws_api_gateway_domain_name.go index 0cdd4ca7f178..7d81914b6515 100644 --- a/aws/resource_aws_api_gateway_domain_name.go +++ b/aws/resource_aws_api_gateway_domain_name.go @@ -6,10 +6,12 @@ import ( "time" "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/aws-sdk-go/service/apigateway" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) func resourceAwsApiGatewayDomainName() *schema.Resource { @@ -139,6 +141,11 @@ func resourceAwsApiGatewayDomainName() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tagsSchema(), }, } } @@ -187,6 +194,10 @@ func resourceAwsApiGatewayDomainNameCreate(d *schema.ResourceData, meta interfac params.SecurityPolicy = aws.String(v.(string)) } + if v, ok := d.GetOk("tags"); ok { + params.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().ApigatewayTags() + } + domainName, err := conn.CreateDomainName(params) if err != nil { return fmt.Errorf("Error creating API Gateway Domain Name: %s", err) @@ -205,7 +216,7 @@ func resourceAwsApiGatewayDomainNameRead(d *schema.ResourceData, meta interface{ DomainName: aws.String(d.Id()), }) if err != nil { - if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NotFoundException" { + if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == apigateway.ErrCodeNotFoundException { log.Printf("[WARN] API Gateway Domain Name (%s) not found, removing from state", d.Id()) d.SetId("") return nil @@ -214,6 +225,17 @@ func resourceAwsApiGatewayDomainNameRead(d *schema.ResourceData, meta interface{ return err } + if err := d.Set("tags", keyvaluetags.ApigatewayKeyValueTags(domainName.Tags).IgnoreAws().Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Service: "apigateway", + Region: meta.(*AWSClient).region, + Resource: fmt.Sprintf("/domainnames/%s", d.Id()), + }.String() + d.Set("arn", arn) d.Set("certificate_arn", domainName.CertificateArn) d.Set("certificate_name", domainName.CertificateName) if err := d.Set("certificate_upload_date", domainName.CertificateUploadDate.Format(time.RFC3339)); err != nil { @@ -300,6 +322,13 @@ func resourceAwsApiGatewayDomainNameUpdate(d *schema.ResourceData, meta interfac conn := meta.(*AWSClient).apigateway log.Printf("[DEBUG] Updating API Gateway Domain Name %s", d.Id()) + if d.HasChange("tags") { + o, n := d.GetChange("tags") + if err := keyvaluetags.ApigatewayUpdateTags(conn, d.Get("arn").(string), o, n); err != nil { + return fmt.Errorf("error updating tags: %s", err) + } + } + _, err := conn.UpdateDomainName(&apigateway.UpdateDomainNameInput{ DomainName: aws.String(d.Id()), PatchOperations: resourceAwsApiGatewayDomainNameUpdateOperations(d), diff --git a/aws/resource_aws_api_gateway_domain_name_test.go b/aws/resource_aws_api_gateway_domain_name_test.go index 3d5cedafdee9..2d20456f7ad3 100644 --- a/aws/resource_aws_api_gateway_domain_name_test.go +++ b/aws/resource_aws_api_gateway_domain_name_test.go @@ -41,6 +41,7 @@ func TestAccAWSAPIGatewayDomainName_CertificateArn(t *testing.T) { Config: testAccAWSAPIGatewayDomainNameConfig_CertificateArn(rName, certificateArn), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/domainnames/+.`)), resource.TestCheckResourceAttrSet(resourceName, "cloudfront_domain_name"), resource.TestCheckResourceAttr(resourceName, "cloudfront_zone_id", "Z2FDTNDATAQYW2"), resource.TestCheckResourceAttr(resourceName, "domain_name", rName), @@ -84,6 +85,7 @@ func TestAccAWSAPIGatewayDomainName_CertificateName(t *testing.T) { } var conf apigateway.DomainName + resourceName := "aws_api_gateway_domain_name.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -93,16 +95,17 @@ func TestAccAWSAPIGatewayDomainName_CertificateName(t *testing.T) { { Config: testAccAWSAPIGatewayDomainNameConfig_CertificateName(domainName, certificatePrivateKey, certificateBody, certificateChain), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSAPIGatewayDomainNameExists("aws_api_gateway_domain_name.test", &conf), - resource.TestCheckResourceAttr("aws_api_gateway_domain_name.test", "certificate_name", "tf-acc-apigateway-domain-name"), - resource.TestCheckResourceAttrSet("aws_api_gateway_domain_name.test", "cloudfront_domain_name"), - resource.TestCheckResourceAttr("aws_api_gateway_domain_name.test", "cloudfront_zone_id", "Z2FDTNDATAQYW2"), - resource.TestCheckResourceAttr("aws_api_gateway_domain_name.test", "domain_name", domainName), - resource.TestCheckResourceAttrSet("aws_api_gateway_domain_name.test", "certificate_upload_date"), + testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &conf), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.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, "domain_name", domainName), + resource.TestCheckResourceAttrSet(resourceName, "certificate_upload_date"), ), }, { - ResourceName: "aws_api_gateway_domain_name.test", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, ImportStateVerifyIgnore: []string{"certificate_body", "certificate_chain", "certificate_private_key"}, @@ -128,6 +131,7 @@ func TestAccAWSAPIGatewayDomainName_RegionalCertificateArn(t *testing.T) { Config: testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateArn(rName, key, certificate), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/domainnames/+.`)), resource.TestCheckResourceAttr(resourceName, "domain_name", rName), resource.TestMatchResourceAttr(resourceName, "regional_domain_name", regexp.MustCompile(`.*\.execute-api\..*`)), resource.TestMatchResourceAttr(resourceName, "regional_zone_id", regexp.MustCompile(`^Z`)), @@ -170,6 +174,7 @@ func TestAccAWSAPIGatewayDomainName_RegionalCertificateName(t *testing.T) { Config: testAccAWSAPIGatewayDomainNameConfig_RegionalCertificateName(rName, key, certificate, caCertificate), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/domainnames/+.`)), resource.TestCheckResourceAttr(resourceName, "certificate_body", certificate), resource.TestCheckResourceAttr(resourceName, "certificate_chain", caCertificate), resource.TestCheckResourceAttr(resourceName, "certificate_name", "tf-acc-apigateway-domain-name"), @@ -201,6 +206,7 @@ func TestAccAWSAPIGatewayDomainName_SecurityPolicy(t *testing.T) { Config: testAccAWSAPIGatewayDomainNameConfig_SecurityPolicy(rName, key, certificate, apigateway.SecurityPolicyTls12), Check: resource.ComposeTestCheckFunc( testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/domainnames/+.`)), resource.TestCheckResourceAttr(resourceName, "security_policy", apigateway.SecurityPolicyTls12), ), }, @@ -213,6 +219,54 @@ func TestAccAWSAPIGatewayDomainName_SecurityPolicy(t *testing.T) { }) } +func TestAccAWSAPIGatewayDomainName_Tags(t *testing.T) { + var domainName apigateway.DomainName + resourceName := "aws_api_gateway_domain_name.test" + rName := fmt.Sprintf("tf-acc-%s.terraformtest.com", acctest.RandString(8)) + + key := tlsRsaPrivateKeyPem(2048) + certificate := tlsRsaX509SelfSignedCertificatePem(key, rName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSAPIGatewayDomainNameDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSAPIGatewayDomainNameConfigTags1(rName, key, certificate, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName), + testAccMatchResourceAttrRegionalARNNoAccount(resourceName, "arn", "apigateway", regexp.MustCompile(`/domainnames/+.`)), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + Config: testAccAWSAPIGatewayDomainNameConfigTags2(rName, key, certificate, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSAPIGatewayDomainNameConfigTags1(rName, key, certificate, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSAPIGatewayDomainNameExists(resourceName, &domainName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckAWSAPIGatewayDomainNameExists(n string, res *apigateway.DomainName) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] @@ -346,3 +400,48 @@ resource "aws_api_gateway_domain_name" "test" { } `, domainName, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(key), securityPolicy) } + +func testAccAWSAPIGatewayDomainNameConfigTags1(domainName, key, certificate, tagKey1, tagValue1 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 + regional_certificate_arn = "${aws_acm_certificate.test.arn}" + + endpoint_configuration { + types = ["REGIONAL"] + } + + tags = { + %[4]q = %[5]q + } +} +`, domainName, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(key), tagKey1, tagValue1) +} + +func testAccAWSAPIGatewayDomainNameConfigTags2(domainName, key, certificate, tagKey1, tagValue1, tagKey2, tagValue2 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 + regional_certificate_arn = "${aws_acm_certificate.test.arn}" + + endpoint_configuration { + types = ["REGIONAL"] + } + + tags = { + %[4]q = %[5]q + %[6]q = %[7]q + } +} +`, domainName, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(key), tagKey1, tagValue1, tagKey2, tagValue2) +} diff --git a/website/docs/r/api_gateway_domain_name.html.markdown b/website/docs/r/api_gateway_domain_name.html.markdown index a9508d0c0abe..ffd9a7e3f9f2 100644 --- a/website/docs/r/api_gateway_domain_name.html.markdown +++ b/website/docs/r/api_gateway_domain_name.html.markdown @@ -148,6 +148,7 @@ The following arguments are supported: * `domain_name` - (Required) The fully-qualified domain name to register * `endpoint_configuration` - (Optional) Configuration block defining API endpoint information including type. Defined below. * `security_policy` - (Optional) The Transport Layer Security (TLS) version + cipher suite for this DomainName. The valid values are `TLS_1_0` and `TLS_1_2`. Must be configured to perform drift detection. +* `tags` - (Optional) Key-value mapping of resource tags When referencing an AWS-managed certificate, the following arguments are supported: @@ -187,6 +188,7 @@ In addition to the arguments, the following attributes are exported: that can be used to create a Route53 alias record for the distribution. * `regional_domain_name` - The hostname for the custom domain's regional endpoint. * `regional_zone_id` - The hosted zone ID that can be used to create a Route53 alias record for the regional endpoint. +* `arn` - Amazon Resource Name (ARN) ## Import