From 7ac3848c2a72bc08fbe2aaa39f0a906d90a5adab Mon Sep 17 00:00:00 2001 From: Stijn De Haes Date: Tue, 1 Dec 2020 11:04:40 +0100 Subject: [PATCH 1/5] VPC endpoint service private dns name support Added support for setting the private dns name in an endpoint service. We also return the config that is required for users to set in their route53 configuration. Signed-off-by: Stijn De Haes --- aws/resource_aws_vpc_endpoint_service.go | 52 ++++++++++++++++- aws/resource_aws_vpc_endpoint_service_test.go | 56 +++++++++++++++++++ .../docs/r/vpc_endpoint_service.html.markdown | 10 +++- 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_vpc_endpoint_service.go b/aws/resource_aws_vpc_endpoint_service.go index a1322b374e45..3095261e4791 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_config": { + 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,39 @@ func resourceAwsVpcEndpointServiceRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("error setting allowed_principals: %s", err) } + err = d.Set("private_dns_name_config", flattenPrivateDnsNameConfiguration(svcCfg.PrivateDnsNameConfiguration)) + if err != nil { + return fmt.Errorf("error setting private_dns_name_config: %w", err) + } + return nil } +func flattenPrivateDnsNameConfiguration(privateDnsNameConfiguration *ec2.PrivateDnsNameConfiguration) []interface{} { + if privateDnsNameConfiguration == nil { + return []interface{}{} + } + m := map[string]interface{}{ + "name": aws.StringValue(privateDnsNameConfiguration.Name), + "state": aws.StringValue(privateDnsNameConfiguration.State), + "type": aws.StringValue(privateDnsNameConfiguration.Type), + "value": aws.StringValue(privateDnsNameConfiguration.Value), + } + return []interface{}{m} +} + 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 e06ab30404ba..07ce87e62e36 100644 --- a/aws/resource_aws_vpc_endpoint_service_test.go +++ b/aws/resource_aws_vpc_endpoint_service_test.go @@ -99,6 +99,11 @@ 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_config.#", "1"), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_config.0.type", ""), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_config.0.name", ""), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_config.0.value", ""), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_config.0.state", ""), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`vpc-endpoint-service/vpce-svc-.+`)), ), }, @@ -256,6 +261,42 @@ 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_config.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_config.0.type", "TXT"), + ), + }, + }, + }) +} + func testAccCheckVpcEndpointServiceDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ec2conn @@ -525,3 +566,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 aae0841f7ff6..d3c477aed88b 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,17 @@ 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_config` - The private dns name config when supplying a private dns name. + +The `private_dns_name_config` object exports the following attributes: + +* `name` - the name of the record subdomain the service provider needs to create +* `state` - the verification state of the VPC endpoint service +* `type` - the endpoint service verification type, for example TXT +* `value` - the value the service provider adds to the private DNS name domain record before verification ## Import From 6009c9e4db4069b65f759cf177a4997a42b9d8fb Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 5 Jan 2021 08:58:03 -0500 Subject: [PATCH 2/5] Apply suggestions from code review --- aws/resource_aws_vpc_endpoint_service.go | 8 ++++---- aws/resource_aws_vpc_endpoint_service_test.go | 12 +++++------- website/docs/r/vpc_endpoint_service.html.markdown | 13 +++++-------- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/aws/resource_aws_vpc_endpoint_service.go b/aws/resource_aws_vpc_endpoint_service.go index 3095261e4791..56cf8f384c78 100644 --- a/aws/resource_aws_vpc_endpoint_service.go +++ b/aws/resource_aws_vpc_endpoint_service.go @@ -81,7 +81,7 @@ func resourceAwsVpcEndpointService() *schema.Resource { Computed: true, Optional: true, }, - "private_dns_name_config": { + "private_dns_name_configuration": { Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ @@ -242,9 +242,9 @@ func resourceAwsVpcEndpointServiceRead(d *schema.ResourceData, meta interface{}) return fmt.Errorf("error setting allowed_principals: %s", err) } - err = d.Set("private_dns_name_config", flattenPrivateDnsNameConfiguration(svcCfg.PrivateDnsNameConfiguration)) + err = d.Set("private_dns_name_configuration", flattenPrivateDnsNameConfiguration(svcCfg.PrivateDnsNameConfiguration)) if err != nil { - return fmt.Errorf("error setting private_dns_name_config: %w", err) + return fmt.Errorf("error setting private_dns_name_configuration: %w", err) } return nil @@ -252,7 +252,7 @@ func resourceAwsVpcEndpointServiceRead(d *schema.ResourceData, meta interface{}) func flattenPrivateDnsNameConfiguration(privateDnsNameConfiguration *ec2.PrivateDnsNameConfiguration) []interface{} { if privateDnsNameConfiguration == nil { - return []interface{}{} + return nil } m := map[string]interface{}{ "name": aws.StringValue(privateDnsNameConfiguration.Name), diff --git a/aws/resource_aws_vpc_endpoint_service_test.go b/aws/resource_aws_vpc_endpoint_service_test.go index 07ce87e62e36..b060e837e818 100644 --- a/aws/resource_aws_vpc_endpoint_service_test.go +++ b/aws/resource_aws_vpc_endpoint_service_test.go @@ -99,11 +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_config.#", "1"), - resource.TestCheckResourceAttr(resourceName, "private_dns_name_config.0.type", ""), - resource.TestCheckResourceAttr(resourceName, "private_dns_name_config.0.name", ""), - resource.TestCheckResourceAttr(resourceName, "private_dns_name_config.0.value", ""), - resource.TestCheckResourceAttr(resourceName, "private_dns_name_config.0.state", ""), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.#", "0"), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`vpc-endpoint-service/vpce-svc-.+`)), ), }, @@ -277,7 +273,8 @@ func TestAccAWSVpcEndpointService_private_dns_name(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVpcEndpointServiceExists(resourceName, &svcCfg), resource.TestCheckResourceAttr(resourceName, "private_dns_name", "example.com"), - resource.TestCheckResourceAttr(resourceName, "private_dns_name_config.0.type", "TXT"), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.#", "1"), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.0.type", "TXT"), ), }, { @@ -290,7 +287,8 @@ func TestAccAWSVpcEndpointService_private_dns_name(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVpcEndpointServiceExists(resourceName, &svcCfg), resource.TestCheckResourceAttr(resourceName, "private_dns_name", "changed.com"), - resource.TestCheckResourceAttr(resourceName, "private_dns_name_config.0.type", "TXT"), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.#", "TXT"), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.0.type", "TXT"), ), }, }, diff --git a/website/docs/r/vpc_endpoint_service.html.markdown b/website/docs/r/vpc_endpoint_service.html.markdown index d3c477aed88b..b9291fb8541e 100644 --- a/website/docs/r/vpc_endpoint_service.html.markdown +++ b/website/docs/r/vpc_endpoint_service.html.markdown @@ -60,14 +60,11 @@ In addition to all arguments above, the following attributes are exported: * `service_name` - The service name. * `service_type` - The service type, `Gateway` or `Interface`. * `state` - The state of the VPC endpoint service. -* `private_dns_name_config` - The private dns name config when supplying a private dns name. - -The `private_dns_name_config` object exports the following attributes: - -* `name` - the name of the record subdomain the service provider needs to create -* `state` - the verification state of the VPC endpoint service -* `type` - the endpoint service verification type, for example TXT -* `value` - the value the service provider adds to the private DNS name domain record before verification +* `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 From debeacf348bb08e5ab4603bb182169aeeb0f7afa Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 5 Jan 2021 08:58:45 -0500 Subject: [PATCH 3/5] Update aws/resource_aws_vpc_endpoint_service_test.go --- aws/resource_aws_vpc_endpoint_service_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_vpc_endpoint_service_test.go b/aws/resource_aws_vpc_endpoint_service_test.go index b060e837e818..9ef445892d9a 100644 --- a/aws/resource_aws_vpc_endpoint_service_test.go +++ b/aws/resource_aws_vpc_endpoint_service_test.go @@ -287,7 +287,7 @@ func TestAccAWSVpcEndpointService_private_dns_name(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckVpcEndpointServiceExists(resourceName, &svcCfg), resource.TestCheckResourceAttr(resourceName, "private_dns_name", "changed.com"), - resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.#", "TXT"), + resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.#", "1"), resource.TestCheckResourceAttr(resourceName, "private_dns_name_configuration.0.type", "TXT"), ), }, From 82f37314cb5c0cf5f905b4efb392132246cce745 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 5 Jan 2021 10:42:08 -0500 Subject: [PATCH 4/5] Update aws/resource_aws_vpc_endpoint_service.go --- aws/resource_aws_vpc_endpoint_service.go | 29 +++++++++++++++++++----- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_vpc_endpoint_service.go b/aws/resource_aws_vpc_endpoint_service.go index 56cf8f384c78..e209d79cc86c 100644 --- a/aws/resource_aws_vpc_endpoint_service.go +++ b/aws/resource_aws_vpc_endpoint_service.go @@ -254,13 +254,30 @@ func flattenPrivateDnsNameConfiguration(privateDnsNameConfiguration *ec2.Private if privateDnsNameConfiguration == nil { return nil } - m := map[string]interface{}{ - "name": aws.StringValue(privateDnsNameConfiguration.Name), - "state": aws.StringValue(privateDnsNameConfiguration.State), - "type": aws.StringValue(privateDnsNameConfiguration.Type), - "value": aws.StringValue(privateDnsNameConfiguration.Value), + 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{}{m} + + return []interface{}{tfMap} } func resourceAwsVpcEndpointServiceUpdate(d *schema.ResourceData, meta interface{}) error { From 85790e05bfd0d5c5a0d16e67d0671fe513ada301 Mon Sep 17 00:00:00 2001 From: Brian Flad Date: Tue, 5 Jan 2021 11:03:52 -0500 Subject: [PATCH 5/5] Update aws/resource_aws_vpc_endpoint_service.go --- aws/resource_aws_vpc_endpoint_service.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aws/resource_aws_vpc_endpoint_service.go b/aws/resource_aws_vpc_endpoint_service.go index e209d79cc86c..eead52efb517 100644 --- a/aws/resource_aws_vpc_endpoint_service.go +++ b/aws/resource_aws_vpc_endpoint_service.go @@ -258,19 +258,19 @@ func flattenPrivateDnsNameConfiguration(privateDnsNameConfiguration *ec2.Private 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 {