From f6880bb3b4d61dbb89eed3b3386437a413030ba2 Mon Sep 17 00:00:00 2001 From: Stijn De Haes Date: Tue, 5 Jan 2021 17:29:37 +0100 Subject: [PATCH] resource/aws_vpc_endpoint_service: Make `private_dns_name` configurable and add `private_dns_name_configuration` attribute (#16495) Output from acceptance testing: ``` --- PASS: TestAccAWSVpcEndpointService_GatewayLoadBalancerArns (211.72s) --- PASS: TestAccAWSVpcEndpointService_disappears (259.98s) --- PASS: TestAccAWSVpcEndpointService_private_dns_name (260.23s) --- PASS: TestAccAWSVpcEndpointService_tags (260.39s) --- PASS: TestAccAWSVpcEndpointService_basic (315.92s) --- PASS: TestAccAWSVpcEndpointService_AllowedPrincipals (326.95s) ``` --- aws/resource_aws_vpc_endpoint_service.go | 69 ++++++++++++++++++- aws/resource_aws_vpc_endpoint_service_test.go | 54 +++++++++++++++ .../docs/r/vpc_endpoint_service.html.markdown | 7 +- 3 files changed, 128 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_vpc_endpoint_service.go b/aws/resource_aws_vpc_endpoint_service.go index a1322b374e4..eead52efb51 100644 --- a/aws/resource_aws_vpc_endpoint_service.go +++ b/aws/resource_aws_vpc_endpoint_service.go @@ -79,6 +79,31 @@ func resourceAwsVpcEndpointService() *schema.Resource { "private_dns_name": { Type: schema.TypeString, Computed: true, + Optional: true, + }, + "private_dns_name_configuration": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, }, "service_name": { Type: schema.TypeString, @@ -104,6 +129,9 @@ func resourceAwsVpcEndpointServiceCreate(d *schema.ResourceData, meta interface{ AcceptanceRequired: aws.Bool(d.Get("acceptance_required").(bool)), TagSpecifications: ec2TagSpecificationsFromMap(d.Get("tags").(map[string]interface{}), "vpc-endpoint-service"), } + if v, ok := d.GetOk("private_dns_name"); ok { + req.PrivateDnsName = aws.String(v.(string)) + } if v, ok := d.GetOk("gateway_load_balancer_arns"); ok { if v, ok := v.(*schema.Set); ok && v.Len() > 0 { @@ -214,17 +242,56 @@ func resourceAwsVpcEndpointServiceRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("error setting allowed_principals: %s", err) } + err = d.Set("private_dns_name_configuration", flattenPrivateDnsNameConfiguration(svcCfg.PrivateDnsNameConfiguration)) + if err != nil { + return fmt.Errorf("error setting private_dns_name_configuration: %w", err) + } + return nil } +func flattenPrivateDnsNameConfiguration(privateDnsNameConfiguration *ec2.PrivateDnsNameConfiguration) []interface{} { + if privateDnsNameConfiguration == nil { + return nil + } + tfMap := map[string]interface{}{} + + if v := privateDnsNameConfiguration.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + if v := privateDnsNameConfiguration.State; v != nil { + tfMap["state"] = aws.StringValue(v) + } + + if v := privateDnsNameConfiguration.Type; v != nil { + tfMap["type"] = aws.StringValue(v) + } + + if v := privateDnsNameConfiguration.Value; v != nil { + tfMap["value"] = aws.StringValue(v) + } + + // The EC2 API can return a XML structure with no elements + if len(tfMap) == 0 { + return nil + } + + return []interface{}{tfMap} +} + func resourceAwsVpcEndpointServiceUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn - if d.HasChanges("acceptance_required", "gateway_load_balancer_arns", "network_load_balancer_arns") { + if d.HasChanges("acceptance_required", "gateway_load_balancer_arns", "network_load_balancer_arns", "private_dns_name") { modifyCfgReq := &ec2.ModifyVpcEndpointServiceConfigurationInput{ ServiceId: aws.String(d.Id()), } + if d.HasChange("private_dns_name") { + modifyCfgReq.PrivateDnsName = aws.String(d.Get("private_dns_name").(string)) + } + if d.HasChange("acceptance_required") { modifyCfgReq.AcceptanceRequired = aws.Bool(d.Get("acceptance_required").(bool)) } diff --git a/aws/resource_aws_vpc_endpoint_service_test.go b/aws/resource_aws_vpc_endpoint_service_test.go index e06ab30404b..9ef445892d9 100644 --- a/aws/resource_aws_vpc_endpoint_service_test.go +++ b/aws/resource_aws_vpc_endpoint_service_test.go @@ -99,6 +99,7 @@ func TestAccAWSVpcEndpointService_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "allowed_principals.#", "0"), resource.TestCheckResourceAttr(resourceName, "manages_vpc_endpoints", "false"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.#", "0"), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`vpc-endpoint-service/vpce-svc-.+`)), ), }, @@ -256,6 +257,44 @@ func TestAccAWSVpcEndpointService_tags(t *testing.T) { }) } +func TestAccAWSVpcEndpointService_private_dns_name(t *testing.T) { + var svcCfg ec2.ServiceConfiguration + resourceName := "aws_vpc_endpoint_service.test" + rName1 := acctest.RandomWithPrefix("tf-acc-test") + rName2 := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVpcEndpointServiceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVpcEndpointServiceConfigPrivateDnsName(rName1, rName2, "example.com"), + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcEndpointServiceExists(resourceName, &svcCfg), + resource.TestCheckResourceAttr(resourceName, "private_dns_name", "example.com"), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.0.type", "TXT"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccVpcEndpointServiceConfigPrivateDnsName(rName1, rName2, "changed.com"), + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcEndpointServiceExists(resourceName, &svcCfg), + resource.TestCheckResourceAttr(resourceName, "private_dns_name", "changed.com"), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.0.type", "TXT"), + ), + }, + }, + }) +} + func testAccCheckVpcEndpointServiceDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ec2conn @@ -525,3 +564,18 @@ resource "aws_vpc_endpoint_service" "test" { } `, tagKey1, tagValue1, tagKey2, tagValue2)) } + +func testAccVpcEndpointServiceConfigPrivateDnsName(rName1, rName2, dnsName string) string { + return composeConfig( + testAccVpcEndpointServiceConfig_base(rName1, rName2), + fmt.Sprintf(` +resource "aws_vpc_endpoint_service" "test" { + acceptance_required = false + private_dns_name = "%s" + + network_load_balancer_arns = [ + aws_lb.test1.arn, + ] +} +`, dnsName)) +} diff --git a/website/docs/r/vpc_endpoint_service.html.markdown b/website/docs/r/vpc_endpoint_service.html.markdown index aae0841f7ff..b9291fb8541 100644 --- a/website/docs/r/vpc_endpoint_service.html.markdown +++ b/website/docs/r/vpc_endpoint_service.html.markdown @@ -46,6 +46,7 @@ The following arguments are supported: * `gateway_load_balancer_arns` - (Optional) Amazon Resource Names (ARNs) of one or more Gateway Load Balancers for the endpoint service. * `network_load_balancer_arns` - (Optional) Amazon Resource Names (ARNs) of one or more Network Load Balancers for the endpoint service. * `tags` - (Optional) A map of tags to assign to the resource. +* `private_dns_name` - (Optional) The private DNS name for the service. ## Attributes Reference @@ -56,10 +57,14 @@ In addition to all arguments above, the following attributes are exported: * `arn` - The Amazon Resource Name (ARN) of the VPC endpoint service. * `base_endpoint_dns_names` - The DNS names for the service. * `manages_vpc_endpoints` - Whether or not the service manages its VPC endpoints - `true` or `false`. -* `private_dns_name` - The private DNS name for the service. * `service_name` - The service name. * `service_type` - The service type, `Gateway` or `Interface`. * `state` - The state of the VPC endpoint service. +* `private_dns_name_configuration` - List of objects containing information about the endpoint service private DNS name configuration. + * `name` - Name of the record subdomain the service provider needs to create. + * `state` - Verification state of the VPC endpoint service. Consumers of the endpoint service can use the private name only when the state is `verified`. + * `type` - Endpoint service verification type, for example `TXT`. + * `value` - Value the service provider adds to the private DNS name domain record before verification. ## Import