Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tag Support to API Gateway Domain Name Resource #10567

Merged
20 changes: 20 additions & 0 deletions aws/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
31 changes: 30 additions & 1 deletion aws/resource_aws_api_gateway_domain_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -139,6 +141,11 @@ func resourceAwsApiGatewayDomainName() *schema.Resource {
Type: schema.TypeString,
Computed: true,
},
"arn": {
Type: schema.TypeString,
Computed: true,
},
"tags": tagsSchema(),
},
}
}
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -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),
Expand Down
113 changes: 106 additions & 7 deletions aws/resource_aws_api_gateway_domain_name_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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) },
Expand All @@ -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"},
Expand All @@ -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`)),
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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),
),
},
Expand All @@ -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]
Expand Down Expand Up @@ -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)
}
2 changes: 2 additions & 0 deletions website/docs/r/api_gateway_domain_name.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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

Expand Down