From 3a7cb3a73f59b5d346d9c0cecdf88554a0bf8a74 Mon Sep 17 00:00:00 2001 From: Angel Abad Date: Thu, 16 Jul 2020 22:42:29 +0200 Subject: [PATCH 01/23] Add data for Client Vpn Endpoint --- ...data_source_aws_ec2_client_vpn_endpoint.go | 189 ++++++++++++++++++ aws/provider.go | 1 + 2 files changed, 190 insertions(+) create mode 100644 aws/data_source_aws_ec2_client_vpn_endpoint.go diff --git a/aws/data_source_aws_ec2_client_vpn_endpoint.go b/aws/data_source_aws_ec2_client_vpn_endpoint.go new file mode 100644 index 000000000000..67a05d616f46 --- /dev/null +++ b/aws/data_source_aws_ec2_client_vpn_endpoint.go @@ -0,0 +1,189 @@ +package aws + +import ( + "errors" + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws/arn" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" +) + +func dataSourceAwsEc2ClientVpnEndpoint() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsEc2ClientVpnEndpointRead, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Optional: true, + }, + "authentication_options": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Computed: true, + }, + "active_directory_id": { + Type: schema.TypeString, + Computed: true, + }, + "root_certificate_chain_arn": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "client_cidr_block": { + Type: schema.TypeString, + Computed: true, + }, + "connection_log_options": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cloudwatch_log_group": { + Type: schema.TypeString, + Computed: true, + }, + "cloudwatch_log_stream": { + Type: schema.TypeString, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "dns_name": { + Type: schema.TypeString, + Computed: true, + }, + "dns_servers": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "filter": dataSourceFiltersSchema(), + "security_group_ids": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "server_certificate_arn": { + Type: schema.TypeString, + Computed: true, + }, + "split_tunnel": { + Type: schema.TypeBool, + Computed: true, + }, + "tags": tagsSchemaComputed(), + "transport_protocol": { + Type: schema.TypeString, + Computed: true, + }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + }, + "vpn_port": { + Type: schema.TypeInt, + Computed: true, + }, + "vpn_protocol": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAwsEc2ClientVpnEndpointRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + input := &ec2.DescribeClientVpnEndpointsInput{} + + if v, ok := d.GetOk("filter"); ok { + input.Filters = buildAwsDataSourceFilters(v.(*schema.Set)) + } + + if v, ok := d.GetOk("id"); ok { + input.ClientVpnEndpointIds = []*string{aws.String(v.(string))} + } + + log.Printf("[DEBUG] Reading EC2 Transit Gateways: %s", input) + + resp, err := conn.DescribeClientVpnEndpoints(input) + if err != nil { + return fmt.Errorf("Error getting Client Vpn Endpoint: %v", err) + } + + if resp == nil || len(resp.ClientVpnEndpoints) == 0 { + return errors.New("Error reading EC2 Client Vpn Endpoint: no results found") + } + + if len(resp.ClientVpnEndpoints) > 1 { + return errors.New("Error reading EC2 Client Vpn Endpoint: multiple results found, try adjusting search criteria") + } + + endpoint := resp.ClientVpnEndpoints[0] + + d.SetId(aws.StringValue(endpoint.ClientVpnEndpointId)) + d.Set("dns_name", endpoint.DnsName) + d.Set("dns_servers", endpoint.DnsServers) + d.Set("client_cidr_block", endpoint.ClientCidrBlock) + d.Set("description", endpoint.Description) + d.Set("server_certificate_arn", endpoint.ServerCertificateArn) + d.Set("split_tunnel", endpoint.SplitTunnel) + d.Set("transport_protocol", endpoint.TransportProtocol) + d.Set("security_group_ids", endpoint.SecurityGroupIds) + d.Set("vpc_id", endpoint.VpcId) + d.Set("vpn_port", endpoint.VpnPort) + d.Set("vpn_protocol", endpoint.VpnProtocol) + + err = d.Set("authentication_options", flattenAuthOptsConfig(endpoint.AuthenticationOptions)) + if err != nil { + return fmt.Errorf("error setting authentication_options: %s", err) + } + + err = d.Set("connection_log_options", flattenConnLoggingConfig(endpoint.ConnectionLogOptions)) + if err != nil { + return fmt.Errorf("error setting connection_log_options: %s", err) + } + + if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(endpoint.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("Error setting tags: %s", err) + } + + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Service: "ec2", + Region: meta.(*AWSClient).region, + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("client-vpn-endpoint/%s", d.Id()), + }.String() + d.Set("arn", arn) + + return nil +} diff --git a/aws/provider.go b/aws/provider.go index 34e5c2925841..dfda5deda16a 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -209,6 +209,7 @@ func Provider() *schema.Provider { "aws_ebs_snapshot_ids": dataSourceAwsEbsSnapshotIds(), "aws_ebs_volume": dataSourceAwsEbsVolume(), "aws_ebs_volumes": dataSourceAwsEbsVolumes(), + "aws_ec2_client_vpn_endpoint": dataSourceAwsEc2ClientVpnEndpoint(), "aws_ec2_coip_pool": dataSourceAwsEc2CoipPool(), "aws_ec2_coip_pools": dataSourceAwsEc2CoipPools(), "aws_ec2_instance_type_offering": dataSourceAwsEc2InstanceTypeOffering(), From af7478757df86d14f7e00fccfb07f77d77f77838 Mon Sep 17 00:00:00 2001 From: Angel Abad Date: Thu, 16 Jul 2020 22:43:04 +0200 Subject: [PATCH 02/23] Add tests for Client Vpn Endpoint data --- ...source_aws_ec2_client_vpn_endpoint_test.go | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 aws/data_source_aws_ec2_client_vpn_endpoint_test.go diff --git a/aws/data_source_aws_ec2_client_vpn_endpoint_test.go b/aws/data_source_aws_ec2_client_vpn_endpoint_test.go new file mode 100644 index 000000000000..b4faca641df1 --- /dev/null +++ b/aws/data_source_aws_ec2_client_vpn_endpoint_test.go @@ -0,0 +1,78 @@ +package aws + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccAWSEc2ClientVpnEndpointDataSource_basic(t *testing.T) { + datasourceName := "data.aws_ec2_client_vpn_endpoint.test" + resourceName := "aws_ec2_client_vpn_endpoint.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccAwsEc2ClientVpnEndpointDataSourceConfig_nonExistent, + ExpectError: regexp.MustCompile(`Endpoint non-existent-endpoint-id does not exist`), + }, + { + Config: testAccAwsEc2ClientVpnEndpointDataSourceConfig_basic(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(datasourceName, "authentication_options", resourceName, "authentication_options"), + resource.TestCheckResourceAttrPair(datasourceName, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(datasourceName, "client_cidr_block", resourceName, "client_cidr_block"), + resource.TestCheckResourceAttrPair(datasourceName, "connection_log_options", resourceName, "connection_log_options"), + resource.TestCheckResourceAttrPair(datasourceName, "dns_servers", resourceName, "dns_servers"), + resource.TestCheckResourceAttrPair(datasourceName, "dns_name", resourceName, "dns_name"), + resource.TestCheckResourceAttrPair(datasourceName, "server_certificate_arn", resourceName, "server_certificate_arn"), + resource.TestCheckResourceAttrPair(datasourceName, "split_tunnel", resourceName, "split_tunnel"), + resource.TestCheckResourceAttrPair(datasourceName, "transport_protocol", resourceName, "transport_protocol"), + ), + }, + }, + }) +} + +const testAccAwsEc2ClientVpnEndpointDataSourceConfig_nonExistent = ` +data "aws_ec2_client_vpn_endpoint" "test" { + id = "non-existent-endpoint-id" +} +` + +func testAccAwsEc2ClientVpnEndpointDataSourceConfig_basic() string { + key := tlsRsaPrivateKeyPem(2048) + certificate := tlsRsaX509SelfSignedCertificatePem(key, "example.com") + + return fmt.Sprintf(` +resource "aws_acm_certificate" "test" { + certificate_body = "%[1]s" + private_key = "%[2]s" +} + +resource "aws_ec2_client_vpn_endpoint" "test" { + description = "terraform-clientvpn-example" + server_certificate_arn = "${aws_acm_certificate.test.arn}" + client_cidr_block = "10.0.0.0/16" + dns_servers = ["8.8.8.8", "8.8.1.1"] + split_tunnel = true + + authentication_options { + type = "certificate-authentication" + root_certificate_chain_arn = "${aws_acm_certificate.test.arn}" + } + + connection_log_options { + enabled = false + } +} + +data "aws_ec2_client_vpn_endpoint" "test" { + id = "${aws_ec2_client_vpn_endpoint.test.id}" +} +`, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(key)) +} From ced4d00b2d75dd85b49d453d5d155cb01c4fb8d2 Mon Sep 17 00:00:00 2001 From: Angel Abad Date: Thu, 16 Jul 2020 22:43:21 +0200 Subject: [PATCH 03/23] Add documentation for Client Vpn Endpoint data --- .../d/ec2_client_vpn_endpoint.html.markdown | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 website/docs/d/ec2_client_vpn_endpoint.html.markdown diff --git a/website/docs/d/ec2_client_vpn_endpoint.html.markdown b/website/docs/d/ec2_client_vpn_endpoint.html.markdown new file mode 100644 index 000000000000..cdea2e9f7555 --- /dev/null +++ b/website/docs/d/ec2_client_vpn_endpoint.html.markdown @@ -0,0 +1,64 @@ +--- +subcategory: "EC2" +layout: "aws" +page_title: "AWS: aws_ec2_client_vpn_endpoint" +description: |- + Get information on an EC2 Client Vpn Endpoint +--- + +# Data Source: aws_ec2_client_vpn_endpoint + +Get information on an EC2 Transit Gateway. + +## Example Usage + +### By Filter + +```hcl +data "aws_ec2_client_vpn_endpoint" "example" { + filter { + name = "tag:Name" + values = ["ExampleVpn"] + } +} +``` + +### By Identifier + +```hcl +data "aws_ec2_client_vpn_endpoint" "example" { + client_vpn_endpoint_id = "cvpn-endpoint-083cf50d6eb314f21" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `filter` - (Optional) One or more configuration blocks containing name-values filters. Detailed below. +* `id` - (Optional) The ID of the Client VPN endpoint. + +### filter Argument Reference + +* `name` - (Required) Name of the filter. +* `values` - (Required) List of one or more values for the filter. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - The ARN of the Client VPN endpoint. +* `authentication_options` - Information about the authentication method to be used to authenticate clients. +* `client_cidr_block` - The IPv4 address range, in CIDR notation, from which to assign client IP addresses. +* `connection_log_options` - Information about the client connection logging options. +* `description` - Description of de Client Vpn Endpoint. +* `dns_name` - The DNS name to be used by clients when establishing their VPN session. +* `dns_servers` - Information about the DNS servers to be used for DNS resolution. +* `security_group_ids` - List VPC security groups associated with the endpoint. +* `server_certificate_arn`- The ARN of the ACM server certificate. +* `split_tunnel` - Indicates whether split-tunnel is enabled on VPN endpoint. +* `tags` - A mapping of tags of the resource. +* `transport_protocol` - The transport protocol to be used by the VPN session. +* `vpc_id` - The VPC Id associated with the endpoint. +* `vpn_port` - The vpn port. +* `vpn_protocol` - The vpn protocol. From 7ef0d4c4f1afe1830ae532a1ee69f653cef1f345 Mon Sep 17 00:00:00 2001 From: Angel Abad Date: Sun, 23 Aug 2020 11:05:36 +0200 Subject: [PATCH 04/23] Upgrade terraform plugin SDK version 2 --- aws/data_source_aws_ec2_client_vpn_endpoint.go | 8 ++++---- aws/data_source_aws_ec2_client_vpn_endpoint_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aws/data_source_aws_ec2_client_vpn_endpoint.go b/aws/data_source_aws_ec2_client_vpn_endpoint.go index 67a05d616f46..9060e0ca5c6c 100644 --- a/aws/data_source_aws_ec2_client_vpn_endpoint.go +++ b/aws/data_source_aws_ec2_client_vpn_endpoint.go @@ -9,7 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) @@ -136,15 +136,15 @@ func dataSourceAwsEc2ClientVpnEndpointRead(d *schema.ResourceData, meta interfac resp, err := conn.DescribeClientVpnEndpoints(input) if err != nil { - return fmt.Errorf("Error getting Client Vpn Endpoint: %v", err) + return fmt.Errorf("error getting Client Vpn Endpoint: %v", err) } if resp == nil || len(resp.ClientVpnEndpoints) == 0 { - return errors.New("Error reading EC2 Client Vpn Endpoint: no results found") + return errors.New("error reading EC2 Client Vpn Endpoint: no results found") } if len(resp.ClientVpnEndpoints) > 1 { - return errors.New("Error reading EC2 Client Vpn Endpoint: multiple results found, try adjusting search criteria") + return errors.New("error reading EC2 Client Vpn Endpoint: multiple results found, try adjusting search criteria") } endpoint := resp.ClientVpnEndpoints[0] diff --git a/aws/data_source_aws_ec2_client_vpn_endpoint_test.go b/aws/data_source_aws_ec2_client_vpn_endpoint_test.go index b4faca641df1..afe063ce5bd5 100644 --- a/aws/data_source_aws_ec2_client_vpn_endpoint_test.go +++ b/aws/data_source_aws_ec2_client_vpn_endpoint_test.go @@ -5,7 +5,7 @@ import ( "regexp" "testing" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) func TestAccAWSEc2ClientVpnEndpointDataSource_basic(t *testing.T) { From 3eeb3614cbbd29ec4e9643c58a46ddc3921065cf Mon Sep 17 00:00:00 2001 From: Angel Abad Date: Sun, 23 Aug 2020 11:12:48 +0200 Subject: [PATCH 05/23] Use only Terraform 0.12 syntax --- aws/data_source_aws_ec2_client_vpn_endpoint_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aws/data_source_aws_ec2_client_vpn_endpoint_test.go b/aws/data_source_aws_ec2_client_vpn_endpoint_test.go index afe063ce5bd5..fc18b625df54 100644 --- a/aws/data_source_aws_ec2_client_vpn_endpoint_test.go +++ b/aws/data_source_aws_ec2_client_vpn_endpoint_test.go @@ -56,14 +56,14 @@ resource "aws_acm_certificate" "test" { resource "aws_ec2_client_vpn_endpoint" "test" { description = "terraform-clientvpn-example" - server_certificate_arn = "${aws_acm_certificate.test.arn}" + server_certificate_arn = aws_acm_certificate.test.arn client_cidr_block = "10.0.0.0/16" dns_servers = ["8.8.8.8", "8.8.1.1"] split_tunnel = true authentication_options { type = "certificate-authentication" - root_certificate_chain_arn = "${aws_acm_certificate.test.arn}" + root_certificate_chain_arn = aws_acm_certificate.test.arn } connection_log_options { @@ -72,7 +72,7 @@ resource "aws_ec2_client_vpn_endpoint" "test" { } data "aws_ec2_client_vpn_endpoint" "test" { - id = "${aws_ec2_client_vpn_endpoint.test.id}" + id = aws_ec2_client_vpn_endpoint.test.id } `, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(key)) } From b8e77c312ba2449c9d4b5fd4a82d27f62338cde3 Mon Sep 17 00:00:00 2001 From: Angel Abad Date: Sun, 23 Aug 2020 11:17:24 +0200 Subject: [PATCH 06/23] Fix typo on website documentation --- website/docs/d/ec2_client_vpn_endpoint.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/d/ec2_client_vpn_endpoint.html.markdown b/website/docs/d/ec2_client_vpn_endpoint.html.markdown index cdea2e9f7555..f4dbc2ff4500 100644 --- a/website/docs/d/ec2_client_vpn_endpoint.html.markdown +++ b/website/docs/d/ec2_client_vpn_endpoint.html.markdown @@ -8,7 +8,7 @@ description: |- # Data Source: aws_ec2_client_vpn_endpoint -Get information on an EC2 Transit Gateway. +Get information on an EC2 Client VPN Endpoint. ## Example Usage From a922b8bd1ec4b0f8c0eb295e6d274703a12aa6a4 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 31 Jan 2022 17:21:07 -0500 Subject: [PATCH 07/23] r/aws_ec2_client_vpn_network_association: Alphabetize attributes. --- internal/service/ec2/client_vpn_network_association.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/service/ec2/client_vpn_network_association.go b/internal/service/ec2/client_vpn_network_association.go index 9e62cf1bc752..2e55cba717bc 100644 --- a/internal/service/ec2/client_vpn_network_association.go +++ b/internal/service/ec2/client_vpn_network_association.go @@ -32,11 +32,6 @@ func ResourceClientVPNNetworkAssociation() *schema.Resource { Required: true, ForceNew: true, }, - "subnet_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, "security_groups": { Type: schema.TypeSet, MinItems: 1, @@ -50,6 +45,11 @@ func ResourceClientVPNNetworkAssociation() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "subnet_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, "vpc_id": { Type: schema.TypeString, Computed: true, From 8985b369d9ca1c95469b36219bcd66f2fa3db6d6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 31 Jan 2022 17:36:06 -0500 Subject: [PATCH 08/23] r/aws_ec2_client_vpn_network_association: Start to tidy up resource Create. --- .../ec2/client_vpn_network_association.go | 27 +++++++++++-------- .../client_vpn_network_association_test.go | 2 +- internal/service/ec2/id.go | 20 -------------- 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/internal/service/ec2/client_vpn_network_association.go b/internal/service/ec2/client_vpn_network_association.go index 2e55cba717bc..895e5ffe1378 100644 --- a/internal/service/ec2/client_vpn_network_association.go +++ b/internal/service/ec2/client_vpn_network_association.go @@ -3,6 +3,7 @@ package ec2 import ( "fmt" "log" + "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" @@ -18,6 +19,7 @@ func ResourceClientVPNNetworkAssociation() *schema.Resource { Read: resourceClientVPNNetworkAssociationRead, Update: resourceClientVPNNetworkAssociationUpdate, Delete: resourceClientVPNNetworkAssociationDelete, + Importer: &schema.ResourceImporter{ State: resourceClientVPNNetworkAssociationImport, }, @@ -61,20 +63,21 @@ func ResourceClientVPNNetworkAssociation() *schema.Resource { func resourceClientVPNNetworkAssociationCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - req := &ec2.AssociateClientVpnTargetNetworkInput{ + input := &ec2.AssociateClientVpnTargetNetworkInput{ ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), SubnetId: aws.String(d.Get("subnet_id").(string)), } - log.Printf("[DEBUG] Creating Client VPN network association: %#v", req) - resp, err := conn.AssociateClientVpnTargetNetwork(req) + log.Printf("[DEBUG] Creating EC2 Client VPN Network Association: %s", input) + + output, err := conn.AssociateClientVpnTargetNetwork(input) + if err != nil { - return fmt.Errorf("Error creating Client VPN network association: %w", err) + return fmt.Errorf("error creating EC2 Client VPN Network Association: %w", err) } - d.SetId(aws.StringValue(resp.AssociationId)) + d.SetId(aws.StringValue(output.AssociationId)) - log.Printf("[DEBUG] Waiting for Client VPN endpoint to associate with target network: %s", d.Id()) targetNetwork, err := WaitClientVPNNetworkAssociationAssociated(conn, d.Id(), d.Get("client_vpn_endpoint_id").(string)) if err != nil { return fmt.Errorf("error waiting for Client VPN endpoint to associate with target network: %w", err) @@ -189,12 +192,14 @@ func DeleteClientVPNNetworkAssociation(conn *ec2.EC2, networkAssociationID, clie } func resourceClientVPNNetworkAssociationImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - endpointID, associationID, err := ClientVPNNetworkAssociationParseID(d.Id()) - if err != nil { - return nil, err + parts := strings.Split(d.Id(), ",") + + if len(parts) != 2 || parts[0] == "" || parts[1] == "" { + return nil, fmt.Errorf("unexpected format for ID (%[1]s), expected EndpointID%[2]sAssociationID", d.Id(), ",") } - d.SetId(associationID) - d.Set("client_vpn_endpoint_id", endpointID) + d.SetId(parts[1]) + d.Set("client_vpn_endpoint_id", parts[0]) + return []*schema.ResourceData{d}, nil } diff --git a/internal/service/ec2/client_vpn_network_association_test.go b/internal/service/ec2/client_vpn_network_association_test.go index 961e97aaaef6..516a7a1834c0 100644 --- a/internal/service/ec2/client_vpn_network_association_test.go +++ b/internal/service/ec2/client_vpn_network_association_test.go @@ -239,7 +239,7 @@ func testAccClientVPNNetworkAssociationImportStateIdFunc(resourceName string) re return "", fmt.Errorf("Not found: %s", resourceName) } - return tfec2.ClientVPNNetworkAssociationCreateID(rs.Primary.Attributes["client_vpn_endpoint_id"], rs.Primary.ID), nil + return fmt.Sprintf("%s,%s", rs.Primary.Attributes["client_vpn_endpoint_id"], rs.Primary.ID), nil } } diff --git a/internal/service/ec2/id.go b/internal/service/ec2/id.go index 6fcadf045eb0..93b68f3e9ce5 100644 --- a/internal/service/ec2/id.go +++ b/internal/service/ec2/id.go @@ -7,26 +7,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/create" ) -const clientVPNNetworkAssociationIDSeparator = "," - -func ClientVPNNetworkAssociationCreateID(endpointID, associationID string) string { - parts := []string{endpointID, associationID} - id := strings.Join(parts, clientVPNNetworkAssociationIDSeparator) - - return id -} - -func ClientVPNNetworkAssociationParseID(id string) (string, string, error) { - parts := strings.Split(id, clientVPNNetworkAssociationIDSeparator) - - if len(parts) == 2 && parts[0] != "" && parts[1] != "" { - return parts[0], parts[1], nil - } - - return "", "", - fmt.Errorf("unexpected format for ID (%[1]s), expected EndpointID%[2]sAssociationID", id, clientVPNNetworkAssociationIDSeparator) -} - const clientVpnRouteIDSeparator = "," func ClientVPNRouteCreateID(endpointID, targetSubnetID, destinationCidr string) string { From 08977e76058d463bec06efe6959660d9b464a291 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 31 Jan 2022 17:45:36 -0500 Subject: [PATCH 09/23] r/aws_ec2_client_vpn_network_association: Tidy up sweeper. --- internal/service/ec2/sweep.go | 48 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/internal/service/ec2/sweep.go b/internal/service/ec2/sweep.go index adf591dab62d..8494bb0619db 100644 --- a/internal/service/ec2/sweep.go +++ b/internal/service/ec2/sweep.go @@ -466,66 +466,66 @@ func sweepClientVPNEndpoints(region string) error { func sweepClientVPNNetworkAssociations(region string) error { client, err := sweep.SharedRegionalSweepClient(region) - if err != nil { return fmt.Errorf("error getting client: %w", err) } - conn := client.(*conns.AWSClient).EC2Conn - + input := &ec2.DescribeClientVpnEndpointsInput{} var sweeperErrs *multierror.Error + sweepResources := make([]*sweep.SweepResource, 0) - input := &ec2.DescribeClientVpnEndpointsInput{} err = conn.DescribeClientVpnEndpointsPages(input, func(page *ec2.DescribeClientVpnEndpointsOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, clientVpnEndpoint := range page.ClientVpnEndpoints { - + for _, v := range page.ClientVpnEndpoints { input := &ec2.DescribeClientVpnTargetNetworksInput{ - ClientVpnEndpointId: clientVpnEndpoint.ClientVpnEndpointId, + ClientVpnEndpointId: v.ClientVpnEndpointId, } + err := conn.DescribeClientVpnTargetNetworksPages(input, func(page *ec2.DescribeClientVpnTargetNetworksOutput, lastPage bool) bool { if page == nil { return !lastPage } - for _, networkAssociation := range page.ClientVpnTargetNetworks { - networkAssociationID := aws.StringValue(networkAssociation.AssociationId) - clientVpnEndpointID := aws.StringValue(networkAssociation.ClientVpnEndpointId) - - log.Printf("[INFO] Deleting Client VPN network association (%s,%s)", clientVpnEndpointID, networkAssociationID) - err := DeleteClientVPNNetworkAssociation(conn, networkAssociationID, clientVpnEndpointID) + for _, v := range page.ClientVpnTargetNetworks { + r := ResourceClientVPNNetworkAssociation() + d := r.Data(nil) + d.SetId(aws.StringValue(v.AssociationId)) + d.Set("client_vpn_endpoint_id", v.ClientVpnEndpointId) - if err != nil { - sweeperErr := fmt.Errorf("error deleting Client VPN network association (%s,%s): %w", clientVpnEndpointID, networkAssociationID, err) - log.Printf("[ERROR] %s", sweeperErr) - sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) - } + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) } return !lastPage }) if sweep.SkipSweepError(err) { - log.Printf("[WARN] Skipping Client VPN network association sweeper for %q: %s", region, err) - return false + continue } + if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Client VPN network associations: %w", err)) - return false + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EC2 Client VPN Network Associations (%s): %w", region, err)) } } return !lastPage }) + if sweep.SkipSweepError(err) { - log.Printf("[WARN] Skipping Client VPN network association sweep for %s: %s", region, err) + log.Printf("[WARN] Skipping EC2 Client VPN Network Association sweep for %s: %s", region, err) return sweeperErrs.ErrorOrNil() // In case we have completed some pages, but had errors } + + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing EC2 Client VPN Endpoints (%s): %w", region, err)) + } + + err = sweep.SweepOrchestrator(sweepResources) + if err != nil { - sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error retrieving Client VPN network associations: %w", err)) + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping EC2 Client VPN Network Associations (%s): %w", region, err)) } return sweeperErrs.ErrorOrNil() From 41faa2ce1b714dab13f43077c1e7f18a4bd12cc8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Mon, 31 Jan 2022 17:49:38 -0500 Subject: [PATCH 10/23] r/aws_ec2_client_vpn_network_association: Start to tidy up resource Delete. --- .../ec2/client_vpn_network_association.go | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/internal/service/ec2/client_vpn_network_association.go b/internal/service/ec2/client_vpn_network_association.go index 895e5ffe1378..4dffdbad4f11 100644 --- a/internal/service/ec2/client_vpn_network_association.go +++ b/internal/service/ec2/client_vpn_network_association.go @@ -165,30 +165,27 @@ func resourceClientVPNNetworkAssociationRead(d *schema.ResourceData, meta interf func resourceClientVPNNetworkAssociationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - err := DeleteClientVPNNetworkAssociation(conn, d.Id(), d.Get("client_vpn_endpoint_id").(string)) - if err != nil { - return fmt.Errorf("error deleting Client VPN network association: %w", err) - } - - return nil -} + endpointID := d.Get("client_vpn_endpoint_id").(string) -func DeleteClientVPNNetworkAssociation(conn *ec2.EC2, networkAssociationID, clientVpnEndpointID string) error { + log.Printf("[DEBUG] Deleting EC2 Client VPN Network Association: %s", d.Id()) _, err := conn.DisassociateClientVpnTargetNetwork(&ec2.DisassociateClientVpnTargetNetworkInput{ - ClientVpnEndpointId: aws.String(clientVpnEndpointID), - AssociationId: aws.String(networkAssociationID), + ClientVpnEndpointId: aws.String(endpointID), + AssociationId: aws.String(d.Id()), }) - if tfawserr.ErrMessageContains(err, ErrCodeInvalidClientVpnAssociationIdNotFound, "") || tfawserr.ErrMessageContains(err, ErrCodeInvalidClientVpnEndpointIdNotFound, "") { + if tfawserr.ErrCodeEquals(err, ErrCodeInvalidClientVpnAssociationIdNotFound, ErrCodeInvalidClientVpnEndpointIdNotFound) { return nil } + if err != nil { - return err + return fmt.Errorf("error disassociating EC2 Client VPN Network Association (%s): %w", d.Id(), err) } - _, err = WaitClientVPNNetworkAssociationDisassociated(conn, networkAssociationID, clientVpnEndpointID) + if _, err := WaitClientVPNNetworkAssociationDisassociated(conn, d.Id(), endpointID); err != nil { + return fmt.Errorf("error waiting for EC2 Client VPN Network Association (%s) delete: %w", d.Id(), err) + } - return err + return nil } func resourceClientVPNNetworkAssociationImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { From 4786bb4763cc92fdef0e097403fd5a23de66dcb6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 08:40:52 -0500 Subject: [PATCH 11/23] Revert "Fix typo on website documentation" This reverts commit b8e77c312ba2449c9d4b5fd4a82d27f62338cde3. --- website/docs/d/ec2_client_vpn_endpoint.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/d/ec2_client_vpn_endpoint.html.markdown b/website/docs/d/ec2_client_vpn_endpoint.html.markdown index f4dbc2ff4500..cdea2e9f7555 100644 --- a/website/docs/d/ec2_client_vpn_endpoint.html.markdown +++ b/website/docs/d/ec2_client_vpn_endpoint.html.markdown @@ -8,7 +8,7 @@ description: |- # Data Source: aws_ec2_client_vpn_endpoint -Get information on an EC2 Client VPN Endpoint. +Get information on an EC2 Transit Gateway. ## Example Usage From 30cceeb7b04781a41aec20da421b1a124d7ecbb9 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 08:41:00 -0500 Subject: [PATCH 12/23] Revert "Use only Terraform 0.12 syntax" This reverts commit 3eeb3614cbbd29ec4e9643c58a46ddc3921065cf. --- aws/data_source_aws_ec2_client_vpn_endpoint_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aws/data_source_aws_ec2_client_vpn_endpoint_test.go b/aws/data_source_aws_ec2_client_vpn_endpoint_test.go index fc18b625df54..afe063ce5bd5 100644 --- a/aws/data_source_aws_ec2_client_vpn_endpoint_test.go +++ b/aws/data_source_aws_ec2_client_vpn_endpoint_test.go @@ -56,14 +56,14 @@ resource "aws_acm_certificate" "test" { resource "aws_ec2_client_vpn_endpoint" "test" { description = "terraform-clientvpn-example" - server_certificate_arn = aws_acm_certificate.test.arn + server_certificate_arn = "${aws_acm_certificate.test.arn}" client_cidr_block = "10.0.0.0/16" dns_servers = ["8.8.8.8", "8.8.1.1"] split_tunnel = true authentication_options { type = "certificate-authentication" - root_certificate_chain_arn = aws_acm_certificate.test.arn + root_certificate_chain_arn = "${aws_acm_certificate.test.arn}" } connection_log_options { @@ -72,7 +72,7 @@ resource "aws_ec2_client_vpn_endpoint" "test" { } data "aws_ec2_client_vpn_endpoint" "test" { - id = aws_ec2_client_vpn_endpoint.test.id + id = "${aws_ec2_client_vpn_endpoint.test.id}" } `, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(key)) } From fb4c4473f307e1044e783d6b514165bed91d8b04 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 08:41:07 -0500 Subject: [PATCH 13/23] Revert "Upgrade terraform plugin SDK version 2" This reverts commit 7ef0d4c4f1afe1830ae532a1ee69f653cef1f345. --- aws/data_source_aws_ec2_client_vpn_endpoint.go | 8 ++++---- aws/data_source_aws_ec2_client_vpn_endpoint_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aws/data_source_aws_ec2_client_vpn_endpoint.go b/aws/data_source_aws_ec2_client_vpn_endpoint.go index 9060e0ca5c6c..67a05d616f46 100644 --- a/aws/data_source_aws_ec2_client_vpn_endpoint.go +++ b/aws/data_source_aws_ec2_client_vpn_endpoint.go @@ -9,7 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) @@ -136,15 +136,15 @@ func dataSourceAwsEc2ClientVpnEndpointRead(d *schema.ResourceData, meta interfac resp, err := conn.DescribeClientVpnEndpoints(input) if err != nil { - return fmt.Errorf("error getting Client Vpn Endpoint: %v", err) + return fmt.Errorf("Error getting Client Vpn Endpoint: %v", err) } if resp == nil || len(resp.ClientVpnEndpoints) == 0 { - return errors.New("error reading EC2 Client Vpn Endpoint: no results found") + return errors.New("Error reading EC2 Client Vpn Endpoint: no results found") } if len(resp.ClientVpnEndpoints) > 1 { - return errors.New("error reading EC2 Client Vpn Endpoint: multiple results found, try adjusting search criteria") + return errors.New("Error reading EC2 Client Vpn Endpoint: multiple results found, try adjusting search criteria") } endpoint := resp.ClientVpnEndpoints[0] diff --git a/aws/data_source_aws_ec2_client_vpn_endpoint_test.go b/aws/data_source_aws_ec2_client_vpn_endpoint_test.go index afe063ce5bd5..b4faca641df1 100644 --- a/aws/data_source_aws_ec2_client_vpn_endpoint_test.go +++ b/aws/data_source_aws_ec2_client_vpn_endpoint_test.go @@ -5,7 +5,7 @@ import ( "regexp" "testing" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" ) func TestAccAWSEc2ClientVpnEndpointDataSource_basic(t *testing.T) { From 60881c3d2cdbee637342f535f6726bfa81de9816 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 08:41:16 -0500 Subject: [PATCH 14/23] Revert "Add documentation for Client Vpn Endpoint data" This reverts commit ced4d00b2d75dd85b49d453d5d155cb01c4fb8d2. --- .../d/ec2_client_vpn_endpoint.html.markdown | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 website/docs/d/ec2_client_vpn_endpoint.html.markdown diff --git a/website/docs/d/ec2_client_vpn_endpoint.html.markdown b/website/docs/d/ec2_client_vpn_endpoint.html.markdown deleted file mode 100644 index cdea2e9f7555..000000000000 --- a/website/docs/d/ec2_client_vpn_endpoint.html.markdown +++ /dev/null @@ -1,64 +0,0 @@ ---- -subcategory: "EC2" -layout: "aws" -page_title: "AWS: aws_ec2_client_vpn_endpoint" -description: |- - Get information on an EC2 Client Vpn Endpoint ---- - -# Data Source: aws_ec2_client_vpn_endpoint - -Get information on an EC2 Transit Gateway. - -## Example Usage - -### By Filter - -```hcl -data "aws_ec2_client_vpn_endpoint" "example" { - filter { - name = "tag:Name" - values = ["ExampleVpn"] - } -} -``` - -### By Identifier - -```hcl -data "aws_ec2_client_vpn_endpoint" "example" { - client_vpn_endpoint_id = "cvpn-endpoint-083cf50d6eb314f21" -} -``` - -## Argument Reference - -The following arguments are supported: - -* `filter` - (Optional) One or more configuration blocks containing name-values filters. Detailed below. -* `id` - (Optional) The ID of the Client VPN endpoint. - -### filter Argument Reference - -* `name` - (Required) Name of the filter. -* `values` - (Required) List of one or more values for the filter. - -## Attribute Reference - -In addition to all arguments above, the following attributes are exported: - -* `arn` - The ARN of the Client VPN endpoint. -* `authentication_options` - Information about the authentication method to be used to authenticate clients. -* `client_cidr_block` - The IPv4 address range, in CIDR notation, from which to assign client IP addresses. -* `connection_log_options` - Information about the client connection logging options. -* `description` - Description of de Client Vpn Endpoint. -* `dns_name` - The DNS name to be used by clients when establishing their VPN session. -* `dns_servers` - Information about the DNS servers to be used for DNS resolution. -* `security_group_ids` - List VPC security groups associated with the endpoint. -* `server_certificate_arn`- The ARN of the ACM server certificate. -* `split_tunnel` - Indicates whether split-tunnel is enabled on VPN endpoint. -* `tags` - A mapping of tags of the resource. -* `transport_protocol` - The transport protocol to be used by the VPN session. -* `vpc_id` - The VPC Id associated with the endpoint. -* `vpn_port` - The vpn port. -* `vpn_protocol` - The vpn protocol. From 72df2ced13c01230e1a0d06532789f0d7a35cf49 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 08:41:26 -0500 Subject: [PATCH 15/23] Revert "Add tests for Client Vpn Endpoint data" This reverts commit af7478757df86d14f7e00fccfb07f77d77f77838. --- ...source_aws_ec2_client_vpn_endpoint_test.go | 78 ------------------- 1 file changed, 78 deletions(-) delete mode 100644 aws/data_source_aws_ec2_client_vpn_endpoint_test.go diff --git a/aws/data_source_aws_ec2_client_vpn_endpoint_test.go b/aws/data_source_aws_ec2_client_vpn_endpoint_test.go deleted file mode 100644 index b4faca641df1..000000000000 --- a/aws/data_source_aws_ec2_client_vpn_endpoint_test.go +++ /dev/null @@ -1,78 +0,0 @@ -package aws - -import ( - "fmt" - "regexp" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" -) - -func TestAccAWSEc2ClientVpnEndpointDataSource_basic(t *testing.T) { - datasourceName := "data.aws_ec2_client_vpn_endpoint.test" - resourceName := "aws_ec2_client_vpn_endpoint.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - Steps: []resource.TestStep{ - { - Config: testAccAwsEc2ClientVpnEndpointDataSourceConfig_nonExistent, - ExpectError: regexp.MustCompile(`Endpoint non-existent-endpoint-id does not exist`), - }, - { - Config: testAccAwsEc2ClientVpnEndpointDataSourceConfig_basic(), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(datasourceName, "authentication_options", resourceName, "authentication_options"), - resource.TestCheckResourceAttrPair(datasourceName, "description", resourceName, "description"), - resource.TestCheckResourceAttrPair(datasourceName, "client_cidr_block", resourceName, "client_cidr_block"), - resource.TestCheckResourceAttrPair(datasourceName, "connection_log_options", resourceName, "connection_log_options"), - resource.TestCheckResourceAttrPair(datasourceName, "dns_servers", resourceName, "dns_servers"), - resource.TestCheckResourceAttrPair(datasourceName, "dns_name", resourceName, "dns_name"), - resource.TestCheckResourceAttrPair(datasourceName, "server_certificate_arn", resourceName, "server_certificate_arn"), - resource.TestCheckResourceAttrPair(datasourceName, "split_tunnel", resourceName, "split_tunnel"), - resource.TestCheckResourceAttrPair(datasourceName, "transport_protocol", resourceName, "transport_protocol"), - ), - }, - }, - }) -} - -const testAccAwsEc2ClientVpnEndpointDataSourceConfig_nonExistent = ` -data "aws_ec2_client_vpn_endpoint" "test" { - id = "non-existent-endpoint-id" -} -` - -func testAccAwsEc2ClientVpnEndpointDataSourceConfig_basic() string { - key := tlsRsaPrivateKeyPem(2048) - certificate := tlsRsaX509SelfSignedCertificatePem(key, "example.com") - - return fmt.Sprintf(` -resource "aws_acm_certificate" "test" { - certificate_body = "%[1]s" - private_key = "%[2]s" -} - -resource "aws_ec2_client_vpn_endpoint" "test" { - description = "terraform-clientvpn-example" - server_certificate_arn = "${aws_acm_certificate.test.arn}" - client_cidr_block = "10.0.0.0/16" - dns_servers = ["8.8.8.8", "8.8.1.1"] - split_tunnel = true - - authentication_options { - type = "certificate-authentication" - root_certificate_chain_arn = "${aws_acm_certificate.test.arn}" - } - - connection_log_options { - enabled = false - } -} - -data "aws_ec2_client_vpn_endpoint" "test" { - id = "${aws_ec2_client_vpn_endpoint.test.id}" -} -`, tlsPemEscapeNewlines(certificate), tlsPemEscapeNewlines(key)) -} From 39362d3045ed6e4b75a497c4c139f2cee6331284 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 08:41:35 -0500 Subject: [PATCH 16/23] Revert "Add data for Client Vpn Endpoint" This reverts commit 3a7cb3a73f59b5d346d9c0cecdf88554a0bf8a74. --- ...data_source_aws_ec2_client_vpn_endpoint.go | 189 ------------------ aws/provider.go | 1 - 2 files changed, 190 deletions(-) delete mode 100644 aws/data_source_aws_ec2_client_vpn_endpoint.go diff --git a/aws/data_source_aws_ec2_client_vpn_endpoint.go b/aws/data_source_aws_ec2_client_vpn_endpoint.go deleted file mode 100644 index 67a05d616f46..000000000000 --- a/aws/data_source_aws_ec2_client_vpn_endpoint.go +++ /dev/null @@ -1,189 +0,0 @@ -package aws - -import ( - "errors" - "fmt" - "log" - - "github.com/aws/aws-sdk-go/aws/arn" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" -) - -func dataSourceAwsEc2ClientVpnEndpoint() *schema.Resource { - return &schema.Resource{ - Read: dataSourceAwsEc2ClientVpnEndpointRead, - - Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "id": { - Type: schema.TypeString, - Optional: true, - }, - "authentication_options": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "type": { - Type: schema.TypeString, - Computed: true, - }, - "active_directory_id": { - Type: schema.TypeString, - Computed: true, - }, - "root_certificate_chain_arn": { - Type: schema.TypeString, - Computed: true, - }, - }, - }, - }, - "client_cidr_block": { - Type: schema.TypeString, - Computed: true, - }, - "connection_log_options": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "cloudwatch_log_group": { - Type: schema.TypeString, - Computed: true, - }, - "cloudwatch_log_stream": { - Type: schema.TypeString, - Computed: true, - }, - "enabled": { - Type: schema.TypeBool, - Computed: true, - }, - }, - }, - }, - "description": { - Type: schema.TypeString, - Computed: true, - }, - "dns_name": { - Type: schema.TypeString, - Computed: true, - }, - "dns_servers": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "filter": dataSourceFiltersSchema(), - "security_group_ids": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "server_certificate_arn": { - Type: schema.TypeString, - Computed: true, - }, - "split_tunnel": { - Type: schema.TypeBool, - Computed: true, - }, - "tags": tagsSchemaComputed(), - "transport_protocol": { - Type: schema.TypeString, - Computed: true, - }, - "vpc_id": { - Type: schema.TypeString, - Computed: true, - }, - "vpn_port": { - Type: schema.TypeInt, - Computed: true, - }, - "vpn_protocol": { - Type: schema.TypeString, - Computed: true, - }, - }, - } -} - -func dataSourceAwsEc2ClientVpnEndpointRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).ec2conn - ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - - input := &ec2.DescribeClientVpnEndpointsInput{} - - if v, ok := d.GetOk("filter"); ok { - input.Filters = buildAwsDataSourceFilters(v.(*schema.Set)) - } - - if v, ok := d.GetOk("id"); ok { - input.ClientVpnEndpointIds = []*string{aws.String(v.(string))} - } - - log.Printf("[DEBUG] Reading EC2 Transit Gateways: %s", input) - - resp, err := conn.DescribeClientVpnEndpoints(input) - if err != nil { - return fmt.Errorf("Error getting Client Vpn Endpoint: %v", err) - } - - if resp == nil || len(resp.ClientVpnEndpoints) == 0 { - return errors.New("Error reading EC2 Client Vpn Endpoint: no results found") - } - - if len(resp.ClientVpnEndpoints) > 1 { - return errors.New("Error reading EC2 Client Vpn Endpoint: multiple results found, try adjusting search criteria") - } - - endpoint := resp.ClientVpnEndpoints[0] - - d.SetId(aws.StringValue(endpoint.ClientVpnEndpointId)) - d.Set("dns_name", endpoint.DnsName) - d.Set("dns_servers", endpoint.DnsServers) - d.Set("client_cidr_block", endpoint.ClientCidrBlock) - d.Set("description", endpoint.Description) - d.Set("server_certificate_arn", endpoint.ServerCertificateArn) - d.Set("split_tunnel", endpoint.SplitTunnel) - d.Set("transport_protocol", endpoint.TransportProtocol) - d.Set("security_group_ids", endpoint.SecurityGroupIds) - d.Set("vpc_id", endpoint.VpcId) - d.Set("vpn_port", endpoint.VpnPort) - d.Set("vpn_protocol", endpoint.VpnProtocol) - - err = d.Set("authentication_options", flattenAuthOptsConfig(endpoint.AuthenticationOptions)) - if err != nil { - return fmt.Errorf("error setting authentication_options: %s", err) - } - - err = d.Set("connection_log_options", flattenConnLoggingConfig(endpoint.ConnectionLogOptions)) - if err != nil { - return fmt.Errorf("error setting connection_log_options: %s", err) - } - - if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(endpoint.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("Error setting tags: %s", err) - } - - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Service: "ec2", - Region: meta.(*AWSClient).region, - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("client-vpn-endpoint/%s", d.Id()), - }.String() - d.Set("arn", arn) - - return nil -} diff --git a/aws/provider.go b/aws/provider.go index dfda5deda16a..34e5c2925841 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -209,7 +209,6 @@ func Provider() *schema.Provider { "aws_ebs_snapshot_ids": dataSourceAwsEbsSnapshotIds(), "aws_ebs_volume": dataSourceAwsEbsVolume(), "aws_ebs_volumes": dataSourceAwsEbsVolumes(), - "aws_ec2_client_vpn_endpoint": dataSourceAwsEc2ClientVpnEndpoint(), "aws_ec2_coip_pool": dataSourceAwsEc2CoipPool(), "aws_ec2_coip_pools": dataSourceAwsEc2CoipPools(), "aws_ec2_instance_type_offering": dataSourceAwsEc2InstanceTypeOffering(), From 3c498fa92c7604e976805cc5caf5f025b241d20a Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 09:18:56 -0500 Subject: [PATCH 17/23] d/aws_ec2_client_vpn_endpoint: New data source. Acceptance test output: % make testacc TESTARGS='-run=TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource' PKG=ec2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/ec2/... -v -count 1 -parallel 20 -run=TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource -timeout 180m === RUN TestAccEC2ClientVPNEndpoint_serial === PAUSE TestAccEC2ClientVPNEndpoint_serial === CONT TestAccEC2ClientVPNEndpoint_serial === RUN TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource === PAUSE TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource === CONT TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource --- PASS: TestAccEC2ClientVPNEndpoint_serial (0.16s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource (21.71s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 27.913s --- .changelog/14218.txt | 3 + internal/provider/provider.go | 1 + .../ec2/client_vpn_endpoint_data_source.go | 79 +++++++++++++++++++ .../client_vpn_endpoint_data_source_test.go | 64 +++++++++++++++ .../service/ec2/client_vpn_endpoint_test.go | 1 + .../d/ec2_client_vpn_endpoint.html.markdown | 55 +++++++++++++ 6 files changed, 203 insertions(+) create mode 100644 .changelog/14218.txt create mode 100644 internal/service/ec2/client_vpn_endpoint_data_source.go create mode 100644 internal/service/ec2/client_vpn_endpoint_data_source_test.go create mode 100644 website/docs/d/ec2_client_vpn_endpoint.html.markdown diff --git a/.changelog/14218.txt b/.changelog/14218.txt new file mode 100644 index 000000000000..d6d6a2efb64f --- /dev/null +++ b/.changelog/14218.txt @@ -0,0 +1,3 @@ +```release-note:new-data-source +aws_ec2_client_vpn_endpoint +``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 90d0a9c1d82d..b764368a14dd 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -450,6 +450,7 @@ func Provider() *schema.Provider { "aws_ebs_snapshot_ids": ec2.DataSourceEBSSnapshotIDs(), "aws_ebs_volume": ec2.DataSourceEBSVolume(), "aws_ebs_volumes": ec2.DataSourceEBSVolumes(), + "aws_ec2_client_vpn_endpoint": ec2.DataSourceClientVPNEndpoint(), "aws_ec2_coip_pool": ec2.DataSourceCoIPPool(), "aws_ec2_coip_pools": ec2.DataSourceCoIPPools(), "aws_ec2_host": ec2.DataSourceHost(), diff --git a/internal/service/ec2/client_vpn_endpoint_data_source.go b/internal/service/ec2/client_vpn_endpoint_data_source.go new file mode 100644 index 000000000000..6ad70047c93a --- /dev/null +++ b/internal/service/ec2/client_vpn_endpoint_data_source.go @@ -0,0 +1,79 @@ +package ec2 + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" +) + +func DataSourceClientVPNEndpoint() *schema.Resource { + return &schema.Resource{ + Read: dataSourceClientVPNEndpointRead, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "client_vpn_endpoint_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "filter": DataSourceFiltersSchema(), + "tags": tftags.TagsSchemaComputed(), + }, + } +} + +func dataSourceClientVPNEndpointRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).EC2Conn + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + + input := &ec2.DescribeClientVpnEndpointsInput{} + + if v, ok := d.GetOk("client_vpn_endpoint_id"); ok { + input.ClientVpnEndpointIds = aws.StringSlice([]string{v.(string)}) + } + + input.Filters = append(input.Filters, BuildTagFilterList( + Tags(tftags.New(d.Get("tags").(map[string]interface{}))), + )...) + + input.Filters = append(input.Filters, BuildFiltersDataSource( + d.Get("filter").(*schema.Set), + )...) + + if len(input.Filters) == 0 { + input.Filters = nil + } + + ep, err := FindClientVPNEndpoint(conn, input) + + if err != nil { + return tfresource.SingularDataSourceFindError("EC2 Client VPN Endpoint", err) + } + + d.SetId(aws.StringValue(ep.ClientVpnEndpointId)) + arn := arn.ARN{ + Partition: meta.(*conns.AWSClient).Partition, + Service: ec2.ServiceName, + Region: meta.(*conns.AWSClient).Region, + AccountID: meta.(*conns.AWSClient).AccountID, + Resource: fmt.Sprintf("client-vpn-endpoint/%s", d.Id()), + }.String() + d.Set("arn", arn) + d.Set("client_vpn_endpoint_id", ep.ClientVpnEndpointId) + + if err := d.Set("tags", KeyValueTags(ep.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %w", err) + } + + return nil +} diff --git a/internal/service/ec2/client_vpn_endpoint_data_source_test.go b/internal/service/ec2/client_vpn_endpoint_data_source_test.go new file mode 100644 index 000000000000..e0a3783bb520 --- /dev/null +++ b/internal/service/ec2/client_vpn_endpoint_data_source_test.go @@ -0,0 +1,64 @@ +package ec2_test + +import ( + "testing" + + "github.com/aws/aws-sdk-go/service/ec2" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" +) + +func testAccClientVPNEndpointDataSource_basic(t *testing.T) { + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_ec2_client_vpn_endpoint.test" + datasource1Name := "data.aws_ec2_client_vpn_endpoint.by_id" + datasource2Name := "data.aws_ec2_client_vpn_endpoint.by_filter" + datasource3Name := "data.aws_ec2_client_vpn_endpoint.by_tags" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheckClientVPNSyncronize(t); acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckClientVPNEndpointDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEc2ClientVpnEndpointDataSourceConfig(rName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(datasource1Name, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasource1Name, "client_vpn_endpoint_id", resourceName, "id"), + resource.TestCheckResourceAttrPair(datasource1Name, "tags.%", resourceName, "tags.%"), + + resource.TestCheckResourceAttrPair(datasource2Name, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasource2Name, "client_vpn_endpoint_id", resourceName, "id"), + resource.TestCheckResourceAttrPair(datasource2Name, "tags.%", resourceName, "tags.%"), + + resource.TestCheckResourceAttrPair(datasource3Name, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasource3Name, "client_vpn_endpoint_id", resourceName, "id"), + resource.TestCheckResourceAttrPair(datasource3Name, "tags.%", resourceName, "tags.%"), + ), + }, + }, + }) +} + +func testAccEc2ClientVpnEndpointDataSourceConfig(rName string) string { + return acctest.ConfigCompose(testAccEc2ClientVpnEndpointConfig(rName), ` +data "aws_ec2_client_vpn_endpoint" "by_id" { + client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id +} + +data "aws_ec2_client_vpn_endpoint" "by_tags" { + tags = { + Name = aws_ec2_client_vpn_endpoint.test.tags["Name"] + } +} + +data "aws_ec2_client_vpn_endpoint" "by_filter" { + filter { + name = "endpoint-id" + values = [aws_ec2_client_vpn_endpoint.test.id] + } +} +`) +} diff --git a/internal/service/ec2/client_vpn_endpoint_test.go b/internal/service/ec2/client_vpn_endpoint_test.go index 646c95df9b26..b4a00cf43ad4 100644 --- a/internal/service/ec2/client_vpn_endpoint_test.go +++ b/internal/service/ec2/client_vpn_endpoint_test.go @@ -45,6 +45,7 @@ func TestAccEC2ClientVPNEndpoint_serial(t *testing.T) { "tags": testAccClientVPNEndpoint_tags, "simpleAttributesUpdate": testAccClientVPNEndpoint_simpleAttributesUpdate, "selfServicePortal": testAccClientVPNEndpoint_selfServicePortal, + "basicDataSource": testAccClientVPNEndpointDataSource_basic, }, "AuthorizationRule": { "basic": testAccClientVPNAuthorizationRule_basic, diff --git a/website/docs/d/ec2_client_vpn_endpoint.html.markdown b/website/docs/d/ec2_client_vpn_endpoint.html.markdown new file mode 100644 index 000000000000..c191850fa9e3 --- /dev/null +++ b/website/docs/d/ec2_client_vpn_endpoint.html.markdown @@ -0,0 +1,55 @@ +--- +subcategory: "EC2" +layout: "aws" +page_title: "AWS: aws_ec2_client_vpn_endpoint" +description: |- + Get information on an EC2 Client VPN Endpoint +--- + +# Data Source: aws_ec2_client_vpn_endpoint + +Get information on an EC2 Client VPN Endpoint. + +## Example Usage + +### By Filter + +```hcl +data "aws_ec2_client_vpn_endpoint" "example" { + filter { + name = "tag:Name" + values = ["ExampleVpn"] + } +} +``` + +### By Identifier + +```hcl +data "aws_ec2_client_vpn_endpoint" "example" { + client_vpn_endpoint_id = "cvpn-endpoint-083cf50d6eb314f21" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `client_vpn_endpoint_id` - (Optional) The ID of the Client VPN Endpoint. +* `filter` - (Optional) One or more configuration blocks containing name-values filters. Detailed below. +* `tags` - (Optional) Map of tags, each pair of which must exactly match a pair on the desired endpoint. + +### filter + +This block allows for complex filters. You can use one or more `filter` blocks. + +The following arguments are required: + +* `name` - (Required) The name of the field to filter by, as defined by [the underlying AWS API](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeClientVpnEndpoints.html). +* `values` - (Required) Set of values that are accepted for the given field. An endpoint will be selected if any one of the given values matches. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - The ARN of the Client VPN Endpoint. From 76a00ffec8775815bd0f4c7b541220895322fb24 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 10:47:05 -0500 Subject: [PATCH 18/23] d/aws_ec2_client_vpn_endpoint: All attributes. Acceptance test output: % make testacc TESTARGS='-run=TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource' PKG=ec2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/ec2/... -v -count 1 -parallel 20 -run=TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource -timeout 180m === RUN TestAccEC2ClientVPNEndpoint_serial === PAUSE TestAccEC2ClientVPNEndpoint_serial === CONT TestAccEC2ClientVPNEndpoint_serial === RUN TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource === PAUSE TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource === CONT TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource --- PASS: TestAccEC2ClientVPNEndpoint_serial (0.07s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/Endpoint_basicDataSource (19.94s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 24.084s --- .../ec2/client_vpn_endpoint_data_source.go | 161 +++++++++++++++++- .../client_vpn_endpoint_data_source_test.go | 42 +++++ .../d/ec2_client_vpn_endpoint.html.markdown | 22 ++- 3 files changed, 220 insertions(+), 5 deletions(-) diff --git a/internal/service/ec2/client_vpn_endpoint_data_source.go b/internal/service/ec2/client_vpn_endpoint_data_source.go index 6ad70047c93a..1d46f72905c2 100644 --- a/internal/service/ec2/client_vpn_endpoint_data_source.go +++ b/internal/service/ec2/client_vpn_endpoint_data_source.go @@ -21,13 +21,134 @@ func DataSourceClientVPNEndpoint() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "authentication_options": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "active_directory_id": { + Type: schema.TypeString, + Computed: true, + }, + "root_certificate_chain_arn": { + Type: schema.TypeString, + Computed: true, + }, + "saml_provider_arn": { + Type: schema.TypeString, + Computed: true, + }, + "self_service_saml_provider_arn": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "client_cidr_block": { + Type: schema.TypeString, + Computed: true, + }, + "client_connect_options": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + "lambda_function_arn": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "client_login_banner_options": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "banner_text": { + Type: schema.TypeString, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, "client_vpn_endpoint_id": { Type: schema.TypeString, Optional: true, Computed: true, }, + "connection_log_options": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cloudwatch_log_group": { + Type: schema.TypeString, + Computed: true, + }, + "cloudwatch_log_stream": { + Type: schema.TypeString, + Computed: true, + }, + "enabled": { + Type: schema.TypeBool, + Computed: true, + }, + }, + }, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "dns_name": { + Type: schema.TypeString, + Computed: true, + }, + "dns_servers": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "filter": DataSourceFiltersSchema(), - "tags": tftags.TagsSchemaComputed(), + "self_service_portal": { + Type: schema.TypeString, + Computed: true, + }, + "server_certificate_arn": { + Type: schema.TypeString, + Computed: true, + }, + "session_timeout_hours": { + Type: schema.TypeInt, + Computed: true, + }, + "split_tunnel": { + Type: schema.TypeBool, + Computed: true, + }, + "tags": tftags.TagsSchemaComputed(), + "transport_protocol": { + Type: schema.TypeString, + Computed: true, + }, + "vpn_port": { + Type: schema.TypeInt, + Computed: true, + }, }, } } @@ -69,7 +190,45 @@ func dataSourceClientVPNEndpointRead(d *schema.ResourceData, meta interface{}) e Resource: fmt.Sprintf("client-vpn-endpoint/%s", d.Id()), }.String() d.Set("arn", arn) + if err := d.Set("authentication_options", flattenClientVpnAuthentications(ep.AuthenticationOptions)); err != nil { + return fmt.Errorf("error setting authentication_options: %w", err) + } + d.Set("client_cidr_block", ep.ClientCidrBlock) + if ep.ClientConnectOptions != nil { + if err := d.Set("client_connect_options", []interface{}{flattenClientConnectResponseOptions(ep.ClientConnectOptions)}); err != nil { + return fmt.Errorf("error setting client_connect_options: %w", err) + } + } else { + d.Set("client_connect_options", nil) + } + if ep.ClientLoginBannerOptions != nil { + if err := d.Set("client_login_banner_options", []interface{}{flattenClientLoginBannerResponseOptions(ep.ClientLoginBannerOptions)}); err != nil { + return fmt.Errorf("error setting client_login_banner_options: %w", err) + } + } else { + d.Set("client_login_banner_options", nil) + } d.Set("client_vpn_endpoint_id", ep.ClientVpnEndpointId) + if ep.ConnectionLogOptions != nil { + if err := d.Set("connection_log_options", []interface{}{flattenConnectionLogResponseOptions(ep.ConnectionLogOptions)}); err != nil { + return fmt.Errorf("error setting connection_log_options: %w", err) + } + } else { + d.Set("connection_log_options", nil) + } + d.Set("description", ep.Description) + d.Set("dns_name", ep.DnsName) + d.Set("dns_servers", aws.StringValueSlice(ep.DnsServers)) + if aws.StringValue(ep.SelfServicePortalUrl) != "" { + d.Set("self_service_portal", ec2.SelfServicePortalEnabled) + } else { + d.Set("self_service_portal", ec2.SelfServicePortalDisabled) + } + d.Set("server_certificate_arn", ep.ServerCertificateArn) + d.Set("session_timeout_hours", ep.SessionTimeoutHours) + d.Set("split_tunnel", ep.SplitTunnel) + d.Set("transport_protocol", ep.TransportProtocol) + d.Set("vpn_port", ep.VpnPort) if err := d.Set("tags", KeyValueTags(ep.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { return fmt.Errorf("error setting tags: %w", err) diff --git a/internal/service/ec2/client_vpn_endpoint_data_source_test.go b/internal/service/ec2/client_vpn_endpoint_data_source_test.go index e0a3783bb520..3d5b8ac56480 100644 --- a/internal/service/ec2/client_vpn_endpoint_data_source_test.go +++ b/internal/service/ec2/client_vpn_endpoint_data_source_test.go @@ -26,16 +26,58 @@ func testAccClientVPNEndpointDataSource_basic(t *testing.T) { Config: testAccEc2ClientVpnEndpointDataSourceConfig(rName), Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttrPair(datasource1Name, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasource1Name, "authentication_options.#", resourceName, "authentication_options.#"), + resource.TestCheckResourceAttrPair(datasource1Name, "client_cidr_block", resourceName, "client_cidr_block"), + resource.TestCheckResourceAttrPair(datasource1Name, "client_connect_options.#", resourceName, "client_connect_options.#"), + resource.TestCheckResourceAttrPair(datasource1Name, "client_login_banner_options.#", resourceName, "client_login_banner_options.#"), resource.TestCheckResourceAttrPair(datasource1Name, "client_vpn_endpoint_id", resourceName, "id"), + resource.TestCheckResourceAttrPair(datasource1Name, "connection_log_options.#", resourceName, "connection_log_options.#"), + resource.TestCheckResourceAttrPair(datasource1Name, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(datasource1Name, "dns_name", resourceName, "dns_name"), + resource.TestCheckResourceAttrPair(datasource1Name, "dns_servers.#", resourceName, "dns_servers.#"), + resource.TestCheckResourceAttrPair(datasource1Name, "self_service_portal", resourceName, "self_service_portal"), + resource.TestCheckResourceAttrPair(datasource1Name, "server_certificate_arn", resourceName, "server_certificate_arn"), + resource.TestCheckResourceAttrPair(datasource1Name, "session_timeout_hours", resourceName, "session_timeout_hours"), + resource.TestCheckResourceAttrPair(datasource1Name, "split_tunnel", resourceName, "split_tunnel"), resource.TestCheckResourceAttrPair(datasource1Name, "tags.%", resourceName, "tags.%"), + resource.TestCheckResourceAttrPair(datasource1Name, "transport_protocol", resourceName, "transport_protocol"), + resource.TestCheckResourceAttrPair(datasource1Name, "vpn_port", resourceName, "vpn_port"), resource.TestCheckResourceAttrPair(datasource2Name, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasource2Name, "authentication_options.#", resourceName, "authentication_options.#"), + resource.TestCheckResourceAttrPair(datasource2Name, "client_cidr_block", resourceName, "client_cidr_block"), + resource.TestCheckResourceAttrPair(datasource2Name, "client_connect_options.#", resourceName, "client_connect_options.#"), + resource.TestCheckResourceAttrPair(datasource2Name, "client_login_banner_options.#", resourceName, "client_login_banner_options.#"), resource.TestCheckResourceAttrPair(datasource2Name, "client_vpn_endpoint_id", resourceName, "id"), + resource.TestCheckResourceAttrPair(datasource2Name, "connection_log_options.#", resourceName, "connection_log_options.#"), + resource.TestCheckResourceAttrPair(datasource2Name, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(datasource2Name, "dns_name", resourceName, "dns_name"), + resource.TestCheckResourceAttrPair(datasource2Name, "dns_servers.#", resourceName, "dns_servers.#"), + resource.TestCheckResourceAttrPair(datasource2Name, "self_service_portal", resourceName, "self_service_portal"), + resource.TestCheckResourceAttrPair(datasource2Name, "server_certificate_arn", resourceName, "server_certificate_arn"), + resource.TestCheckResourceAttrPair(datasource2Name, "session_timeout_hours", resourceName, "session_timeout_hours"), + resource.TestCheckResourceAttrPair(datasource2Name, "split_tunnel", resourceName, "split_tunnel"), resource.TestCheckResourceAttrPair(datasource2Name, "tags.%", resourceName, "tags.%"), + resource.TestCheckResourceAttrPair(datasource2Name, "transport_protocol", resourceName, "transport_protocol"), + resource.TestCheckResourceAttrPair(datasource2Name, "vpn_port", resourceName, "vpn_port"), resource.TestCheckResourceAttrPair(datasource3Name, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasource3Name, "authentication_options.#", resourceName, "authentication_options.#"), + resource.TestCheckResourceAttrPair(datasource3Name, "client_cidr_block", resourceName, "client_cidr_block"), + resource.TestCheckResourceAttrPair(datasource3Name, "client_connect_options.#", resourceName, "client_connect_options.#"), + resource.TestCheckResourceAttrPair(datasource3Name, "client_login_banner_options.#", resourceName, "client_login_banner_options.#"), resource.TestCheckResourceAttrPair(datasource3Name, "client_vpn_endpoint_id", resourceName, "id"), + resource.TestCheckResourceAttrPair(datasource3Name, "connection_log_options.#", resourceName, "connection_log_options.#"), + resource.TestCheckResourceAttrPair(datasource3Name, "description", resourceName, "description"), + resource.TestCheckResourceAttrPair(datasource3Name, "dns_name", resourceName, "dns_name"), + resource.TestCheckResourceAttrPair(datasource3Name, "dns_servers.#", resourceName, "dns_servers.#"), + resource.TestCheckResourceAttrPair(datasource3Name, "self_service_portal", resourceName, "self_service_portal"), + resource.TestCheckResourceAttrPair(datasource3Name, "server_certificate_arn", resourceName, "server_certificate_arn"), + resource.TestCheckResourceAttrPair(datasource3Name, "session_timeout_hours", resourceName, "session_timeout_hours"), + resource.TestCheckResourceAttrPair(datasource3Name, "split_tunnel", resourceName, "split_tunnel"), resource.TestCheckResourceAttrPair(datasource3Name, "tags.%", resourceName, "tags.%"), + resource.TestCheckResourceAttrPair(datasource3Name, "transport_protocol", resourceName, "transport_protocol"), + resource.TestCheckResourceAttrPair(datasource3Name, "vpn_port", resourceName, "vpn_port"), ), }, }, diff --git a/website/docs/d/ec2_client_vpn_endpoint.html.markdown b/website/docs/d/ec2_client_vpn_endpoint.html.markdown index c191850fa9e3..89a1ba07ee8c 100644 --- a/website/docs/d/ec2_client_vpn_endpoint.html.markdown +++ b/website/docs/d/ec2_client_vpn_endpoint.html.markdown @@ -3,12 +3,12 @@ subcategory: "EC2" layout: "aws" page_title: "AWS: aws_ec2_client_vpn_endpoint" description: |- - Get information on an EC2 Client VPN Endpoint + Get information on an EC2 Client VPN endpoint --- # Data Source: aws_ec2_client_vpn_endpoint -Get information on an EC2 Client VPN Endpoint. +Get information on an EC2 Client VPN endpoint. ## Example Usage @@ -35,7 +35,7 @@ data "aws_ec2_client_vpn_endpoint" "example" { The following arguments are supported: -* `client_vpn_endpoint_id` - (Optional) The ID of the Client VPN Endpoint. +* `client_vpn_endpoint_id` - (Optional) The ID of the Client VPN endpoint. * `filter` - (Optional) One or more configuration blocks containing name-values filters. Detailed below. * `tags` - (Optional) Map of tags, each pair of which must exactly match a pair on the desired endpoint. @@ -52,4 +52,18 @@ The following arguments are required: In addition to all arguments above, the following attributes are exported: -* `arn` - The ARN of the Client VPN Endpoint. +* `arn` - The ARN of the Client VPN endpoint. +* `authentication_options` - Information about the authentication method used by the Client VPN endpoint. +* `client_cidr_block` - The IPv4 address range, in CIDR notation, from which client IP addresses are assigned. +* `client_connect_options` - The options for managing connection authorization for new client connections. +* `client_login_banner_options` - Options for enabling a customizable text banner that will be displayed on AWS provided clients when a VPN session is established. +* `connection_log_options` - Information about the client connection logging options for the Client VPN endpoint. +* `description` - A brief description of the endpoint. +* `dns_name` - The DNS name to be used by clients when connecting to the Client VPN endpoint. +* `dns_servers` - Information about the DNS servers to be used for DNS resolution. +* `self_service_portal` - Indicates whether the self-service portal for the Client VPN endpoint is enabled. +* `server_certificate_arn` - The ARN of the server certificate. +* `session_timeout_hours` - The maximum VPN session duration time in hours. +* `split_tunnel` - Indicates whether split-tunnel is enabled in the AWS Client VPN endpoint. +* `transport_protocol` - The transport protocol used by the Client VPN endpoint. +* `vpn_port` - The port number for the Client VPN endpoint. From dcb11ed95bec64d793ba01bc904ec6f6b64e8e4e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 10:49:08 -0500 Subject: [PATCH 19/23] 'FindClientVPNAuthorizationRuleByEndpointIDTargetNetworkCIDRAndGroupID' -> 'FindClientVPNAuthorizationRuleByThreePartKey'. --- internal/service/ec2/client_vpn_authorization_rule.go | 2 +- internal/service/ec2/client_vpn_authorization_rule_test.go | 4 ++-- internal/service/ec2/find.go | 3 +-- internal/service/ec2/status.go | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/internal/service/ec2/client_vpn_authorization_rule.go b/internal/service/ec2/client_vpn_authorization_rule.go index bd64b9f639db..21faaf001b06 100644 --- a/internal/service/ec2/client_vpn_authorization_rule.go +++ b/internal/service/ec2/client_vpn_authorization_rule.go @@ -113,7 +113,7 @@ func resourceClientVPNAuthorizationRuleRead(d *schema.ResourceData, meta interfa return err } - rule, err := FindClientVPNAuthorizationRuleByEndpointIDTargetNetworkCIDRAndGroupID(conn, endpointID, targetNetworkCIDR, accessGroupID) + rule, err := FindClientVPNAuthorizationRuleByThreePartKey(conn, endpointID, targetNetworkCIDR, accessGroupID) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] EC2 Client VPN Authorization Rule (%s) not found, removing from state", d.Id()) diff --git a/internal/service/ec2/client_vpn_authorization_rule_test.go b/internal/service/ec2/client_vpn_authorization_rule_test.go index ed5926323044..f8622f295630 100644 --- a/internal/service/ec2/client_vpn_authorization_rule_test.go +++ b/internal/service/ec2/client_vpn_authorization_rule_test.go @@ -235,7 +235,7 @@ func testAccCheckClientVPNAuthorizationRuleDestroy(s *terraform.State) error { return err } - _, err = tfec2.FindClientVPNAuthorizationRuleByEndpointIDTargetNetworkCIDRAndGroupID(conn, endpointID, targetNetworkCIDR, accessGroupID) + _, err = tfec2.FindClientVPNAuthorizationRuleByThreePartKey(conn, endpointID, targetNetworkCIDR, accessGroupID) if tfresource.NotFound(err) { continue @@ -270,7 +270,7 @@ func testAccCheckClientVPNAuthorizationRuleExists(name string, v *ec2.Authorizat conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn - output, err := tfec2.FindClientVPNAuthorizationRuleByEndpointIDTargetNetworkCIDRAndGroupID(conn, endpointID, targetNetworkCIDR, accessGroupID) + output, err := tfec2.FindClientVPNAuthorizationRuleByThreePartKey(conn, endpointID, targetNetworkCIDR, accessGroupID) if err != nil { return err diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index ee5657a18652..3ddc98e00c5e 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -174,7 +174,7 @@ func FindClientVPNAuthorizationRules(conn *ec2.EC2, input *ec2.DescribeClientVpn return output, nil } -func FindClientVPNAuthorizationRuleByEndpointIDTargetNetworkCIDRAndGroupID(conn *ec2.EC2, endpointID, targetNetworkCIDR, accessGroupID string) (*ec2.AuthorizationRule, error) { +func FindClientVPNAuthorizationRuleByThreePartKey(conn *ec2.EC2, endpointID, targetNetworkCIDR, accessGroupID string) (*ec2.AuthorizationRule, error) { filters := map[string]string{ "destination-cidr": targetNetworkCIDR, } @@ -187,7 +187,6 @@ func FindClientVPNAuthorizationRuleByEndpointIDTargetNetworkCIDRAndGroupID(conn } return FindClientVPNAuthorizationRule(conn, input) - } func FindClientVPNRoute(conn *ec2.EC2, endpointID, targetSubnetID, destinationCidr string) (*ec2.DescribeClientVpnRoutesOutput, error) { diff --git a/internal/service/ec2/status.go b/internal/service/ec2/status.go index 3cfac26b8c68..0fb4c85101da 100644 --- a/internal/service/ec2/status.go +++ b/internal/service/ec2/status.go @@ -111,7 +111,7 @@ func StatusClientVPNEndpointClientConnectResponseOptionsState(conn *ec2.EC2, id func StatusClientVPNAuthorizationRule(conn *ec2.EC2, endpointID, targetNetworkCIDR, accessGroupID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - output, err := FindClientVPNAuthorizationRuleByEndpointIDTargetNetworkCIDRAndGroupID(conn, endpointID, targetNetworkCIDR, accessGroupID) + output, err := FindClientVPNAuthorizationRuleByThreePartKey(conn, endpointID, targetNetworkCIDR, accessGroupID) if tfresource.NotFound(err) { return nil, "", nil From b3534135715abf7f19216e91fb647f725305fa89 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 11:36:57 -0500 Subject: [PATCH 20/23] r/aws_ec2_client_vpn_authorization_rule: Tidy up acceptance test configurations. Acceptance test output: % make testacc TESTARGS='-run=TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule' PKG=ec2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/ec2/... -v -count 1 -parallel 20 -run=TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule -timeout 180m === RUN TestAccEC2ClientVPNEndpoint_serial === PAUSE TestAccEC2ClientVPNEndpoint_serial === CONT TestAccEC2ClientVPNEndpoint_serial === RUN TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappears === PAUSE TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappears === RUN TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappearsEndpoint === PAUSE TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappearsEndpoint === RUN TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_basic === PAUSE TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_basic === RUN TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_groups === PAUSE TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_groups === RUN TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_subnets === PAUSE TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_subnets === CONT TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappears === CONT TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_groups === CONT TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_basic === CONT TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappearsEndpoint === CONT TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_subnets --- PASS: TestAccEC2ClientVPNEndpoint_serial (1.16s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappearsEndpoint (58.45s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappears (63.96s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_basic (70.03s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_subnets (99.27s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_groups (142.89s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 147.494s --- .../ec2/client_vpn_authorization_rule_test.go | 90 ++----------------- 1 file changed, 9 insertions(+), 81 deletions(-) diff --git a/internal/service/ec2/client_vpn_authorization_rule_test.go b/internal/service/ec2/client_vpn_authorization_rule_test.go index f8622f295630..3e8bb1ade03c 100644 --- a/internal/service/ec2/client_vpn_authorization_rule_test.go +++ b/internal/service/ec2/client_vpn_authorization_rule_test.go @@ -282,8 +282,11 @@ func testAccCheckClientVPNAuthorizationRuleExists(name string, v *ec2.Authorizat } } -func testAccEc2ClientVpnAuthorizationRuleVpcBase(rName string, subnetCount int) string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptInDefaultExclude(), fmt.Sprintf(` +func testAccEc2ClientVpnAuthorizationRuleBaseConfig(rName string, subnetCount int) string { + return acctest.ConfigCompose( + testAccEc2ClientVpnEndpointConfig(rName), + acctest.ConfigAvailableAZsNoOptInDefaultExclude(), + fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "10.1.0.0/16" @@ -306,47 +309,14 @@ resource "aws_subnet" "test" { `, rName, subnetCount)) } -func testAccEc2ClientVpnAuthorizationRuleAcmCertificateBase() string { - key := acctest.TLSRSAPrivateKeyPEM(2048) - certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(key, "example.com") - - return fmt.Sprintf(` -resource "aws_acm_certificate" "test" { - certificate_body = "%[1]s" - private_key = "%[2]s" -} -`, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key)) -} - func testAccEc2ClientVpnAuthorizationRuleConfigBasic(rName string) string { - return acctest.ConfigCompose( - testAccEc2ClientVpnAuthorizationRuleVpcBase(rName, 1), - testAccEc2ClientVpnAuthorizationRuleAcmCertificateBase(), - fmt.Sprintf(` + return acctest.ConfigCompose(testAccEc2ClientVpnAuthorizationRuleBaseConfig(rName, 1), ` resource "aws_ec2_client_vpn_authorization_rule" "test" { client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id target_network_cidr = aws_subnet.test[0].cidr_block authorize_all_groups = true } - -resource "aws_ec2_client_vpn_endpoint" "test" { - server_certificate_arn = aws_acm_certificate.test.arn - client_cidr_block = "10.0.0.0/16" - - authentication_options { - type = "certificate-authentication" - root_certificate_chain_arn = aws_acm_certificate.test.arn - } - - connection_log_options { - enabled = false - } - - tags = { - Name = %[1]q - } -} -`, rName)) +`) } func testAccEc2ClientVpnAuthorizationRuleConfigGroups(rName string, groupNames map[string]string) string { @@ -361,28 +331,7 @@ resource "aws_ec2_client_vpn_authorization_rule" %[1]q { `, k, v) } - return acctest.ConfigCompose( - testAccEc2ClientVpnAuthorizationRuleVpcBase(rName, 1), - testAccEc2ClientVpnAuthorizationRuleAcmCertificateBase(), - b.String(), - fmt.Sprintf(` -resource "aws_ec2_client_vpn_endpoint" "test" { - server_certificate_arn = aws_acm_certificate.test.arn - client_cidr_block = "10.0.0.0/16" - - authentication_options { - type = "certificate-authentication" - root_certificate_chain_arn = aws_acm_certificate.test.arn - } - - connection_log_options { - enabled = false - } - - tags = { - Name = %[1]q - } -}`, rName)) + return acctest.ConfigCompose(testAccEc2ClientVpnAuthorizationRuleBaseConfig(rName, 1), b.String()) } func testAccEc2ClientVpnAuthorizationRuleConfigSubnets(rName string, subnetCount int, groupNames map[string]int) string { @@ -397,26 +346,5 @@ resource "aws_ec2_client_vpn_authorization_rule" %[1]q { `, k, v) } - return acctest.ConfigCompose( - testAccEc2ClientVpnAuthorizationRuleVpcBase(rName, subnetCount), - testAccEc2ClientVpnAuthorizationRuleAcmCertificateBase(), - b.String(), - fmt.Sprintf(` -resource "aws_ec2_client_vpn_endpoint" "test" { - server_certificate_arn = aws_acm_certificate.test.arn - client_cidr_block = "10.0.0.0/16" - - authentication_options { - type = "certificate-authentication" - root_certificate_chain_arn = aws_acm_certificate.test.arn - } - - connection_log_options { - enabled = false - } - - tags = { - Name = %[1]q - } -}`, rName)) + return acctest.ConfigCompose(testAccEc2ClientVpnAuthorizationRuleBaseConfig(rName, subnetCount), b.String()) } From 871c5e0db0d04629fc0e4b16f47f3836ac46a52e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 13:55:49 -0500 Subject: [PATCH 21/23] r/aws_ec2_client_vpn_network_association: Tidy up resource Read. Acceptance test output: % make testacc TESTARGS='-run=TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_basic' PKG=ec2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/ec2/... -v -count 1 -parallel 20 -run=TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_basic -timeout 180m === RUN TestAccEC2ClientVPNEndpoint_serial === PAUSE TestAccEC2ClientVPNEndpoint_serial === CONT TestAccEC2ClientVPNEndpoint_serial === RUN TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_basic === PAUSE TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_basic === CONT TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_basic --- PASS: TestAccEC2ClientVPNEndpoint_serial (0.17s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_basic (1140.21s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 1143.768s --- .../ec2/client_vpn_network_association.go | 87 +++--- .../client_vpn_network_association_test.go | 249 +++++++----------- internal/service/ec2/find.go | 80 ++++++ internal/service/ec2/status.go | 31 +-- internal/service/ec2/wait.go | 32 +-- 5 files changed, 228 insertions(+), 251 deletions(-) diff --git a/internal/service/ec2/client_vpn_network_association.go b/internal/service/ec2/client_vpn_network_association.go index 4dffdbad4f11..5bd546ee1eb6 100644 --- a/internal/service/ec2/client_vpn_network_association.go +++ b/internal/service/ec2/client_vpn_network_association.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceClientVPNNetworkAssociation() *schema.Resource { @@ -63,8 +64,9 @@ func ResourceClientVPNNetworkAssociation() *schema.Resource { func resourceClientVPNNetworkAssociationCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn + endpointID := d.Get("client_vpn_endpoint_id").(string) input := &ec2.AssociateClientVpnTargetNetworkInput{ - ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), + ClientVpnEndpointId: aws.String(endpointID), SubnetId: aws.String(d.Get("subnet_id").(string)), } @@ -78,39 +80,23 @@ func resourceClientVPNNetworkAssociationCreate(d *schema.ResourceData, meta inte d.SetId(aws.StringValue(output.AssociationId)) - targetNetwork, err := WaitClientVPNNetworkAssociationAssociated(conn, d.Id(), d.Get("client_vpn_endpoint_id").(string)) + targetNetwork, err := WaitClientVPNNetworkAssociationCreated(conn, d.Id(), endpointID) + if err != nil { - return fmt.Errorf("error waiting for Client VPN endpoint to associate with target network: %w", err) + return fmt.Errorf("error waiting for EC2 Client VPN Network Association (%s) create: %w", d.Id(), err) } if v, ok := d.GetOk("security_groups"); ok { - sgReq := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ - ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), - VpcId: targetNetwork.VpcId, + input := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ + ClientVpnEndpointId: aws.String(endpointID), SecurityGroupIds: flex.ExpandStringSet(v.(*schema.Set)), + VpcId: targetNetwork.VpcId, } - _, err := conn.ApplySecurityGroupsToClientVpnTargetNetwork(sgReq) - if err != nil { - return fmt.Errorf("Error applying security groups to Client VPN network association: %s", err) - } - } - - return resourceClientVPNNetworkAssociationRead(d, meta) -} - -func resourceClientVPNNetworkAssociationUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).EC2Conn - - if d.HasChange("security_groups") { - input := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ - ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), - SecurityGroupIds: flex.ExpandStringSet(d.Get("security_groups").(*schema.Set)), - VpcId: aws.String(d.Get("vpc_id").(string)), - } + _, err := conn.ApplySecurityGroupsToClientVpnTargetNetwork(input) - if _, err := conn.ApplySecurityGroupsToClientVpnTargetNetwork(input); err != nil { - return fmt.Errorf("error applying security groups to Client VPN Target Network: %s", err) + if err != nil { + return fmt.Errorf("error applying Security Groups to EC2 Client VPN Network Association (%s): %w", d.Id(), err) } } @@ -119,47 +105,46 @@ func resourceClientVPNNetworkAssociationUpdate(d *schema.ResourceData, meta inte func resourceClientVPNNetworkAssociationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - var err error - result, err := conn.DescribeClientVpnTargetNetworks(&ec2.DescribeClientVpnTargetNetworksInput{ - ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), - AssociationIds: []*string{aws.String(d.Id())}, - }) + endpointID := d.Get("client_vpn_endpoint_id").(string) + network, err := FindClientVPNNetworkAssociationByIDs(conn, d.Id(), endpointID) - if tfawserr.ErrMessageContains(err, ErrCodeInvalidClientVpnAssociationIdNotFound, "") || tfawserr.ErrMessageContains(err, ErrCodeInvalidClientVpnEndpointIdNotFound, "") { + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] EC2 Client VPN Network Association (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("Error reading Client VPN network association: %w", err) - } - - if result == nil || len(result.ClientVpnTargetNetworks) == 0 || result.ClientVpnTargetNetworks[0] == nil { - log.Printf("[WARN] EC2 Client VPN Network Association (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - network := result.ClientVpnTargetNetworks[0] - if network.Status != nil && aws.StringValue(network.Status.Code) == ec2.AssociationStatusCodeDisassociated { - log.Printf("[WARN] EC2 Client VPN Network Association (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil + return fmt.Errorf("error reading EC2 Client VPN Network Association (%s): %w", d.Id(), err) } - d.Set("client_vpn_endpoint_id", network.ClientVpnEndpointId) d.Set("association_id", network.AssociationId) + d.Set("client_vpn_endpoint_id", network.ClientVpnEndpointId) + d.Set("security_groups", aws.StringValueSlice(network.SecurityGroups)) d.Set("status", network.Status.Code) d.Set("subnet_id", network.TargetNetworkId) d.Set("vpc_id", network.VpcId) - if err := d.Set("security_groups", aws.StringValueSlice(network.SecurityGroups)); err != nil { - return fmt.Errorf("error setting security_groups: %w", err) + return nil +} + +func resourceClientVPNNetworkAssociationUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).EC2Conn + + if d.HasChange("security_groups") { + input := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ + ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), + SecurityGroupIds: flex.ExpandStringSet(d.Get("security_groups").(*schema.Set)), + VpcId: aws.String(d.Get("vpc_id").(string)), + } + + if _, err := conn.ApplySecurityGroupsToClientVpnTargetNetwork(input); err != nil { + return fmt.Errorf("error applying Security Groups to EC2 Client VPN Network Association (%s): %w", d.Id(), err) + } } - return nil + return resourceClientVPNNetworkAssociationRead(d, meta) } func resourceClientVPNNetworkAssociationDelete(d *schema.ResourceData, meta interface{}) error { @@ -181,7 +166,7 @@ func resourceClientVPNNetworkAssociationDelete(d *schema.ResourceData, meta inte return fmt.Errorf("error disassociating EC2 Client VPN Network Association (%s): %w", d.Id(), err) } - if _, err := WaitClientVPNNetworkAssociationDisassociated(conn, d.Id(), endpointID); err != nil { + if _, err := WaitClientVPNNetworkAssociationDeleted(conn, d.Id(), endpointID); err != nil { return fmt.Errorf("error waiting for EC2 Client VPN Network Association (%s) delete: %w", d.Id(), err) } diff --git a/internal/service/ec2/client_vpn_network_association_test.go b/internal/service/ec2/client_vpn_network_association_test.go index 516a7a1834c0..3fad12b86c14 100644 --- a/internal/service/ec2/client_vpn_network_association_test.go +++ b/internal/service/ec2/client_vpn_network_association_test.go @@ -13,15 +13,16 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" tfec2 "github.com/hashicorp/terraform-provider-aws/internal/service/ec2" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func testAccClientVPNNetworkAssociation_basic(t *testing.T) { var assoc ec2.TargetNetwork var group ec2.SecurityGroup - rStr := sdkacctest.RandString(5) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ec2_client_vpn_network_association.test" endpointResourceName := "aws_ec2_client_vpn_endpoint.test" - subnetResourceName := "aws_subnet.test" + subnetResourceName := "aws_subnet.test1" vpcResourceName := "aws_vpc.test" defaultSecurityGroupResourceName := "aws_default_security_group.test" @@ -32,7 +33,7 @@ func testAccClientVPNNetworkAssociation_basic(t *testing.T) { CheckDestroy: testAccCheckClientVPNNetworkAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccEc2ClientVpnNetworkAssociationConfigBasic(rStr), + Config: testAccEc2ClientVpnNetworkAssociationConfigBasic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClientVPNNetworkAssociationExists(resourceName, &assoc), resource.TestMatchResourceAttr(resourceName, "association_id", regexp.MustCompile("^cvpn-assoc-[a-z0-9]+$")), @@ -58,10 +59,10 @@ func testAccClientVPNNetworkAssociation_basic(t *testing.T) { func testAccClientVPNNetworkAssociation_multipleSubnets(t *testing.T) { var assoc ec2.TargetNetwork var group ec2.SecurityGroup - rStr := sdkacctest.RandString(5) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceNames := []string{"aws_ec2_client_vpn_network_association.test", "aws_ec2_client_vpn_network_association.test2"} endpointResourceName := "aws_ec2_client_vpn_endpoint.test" - subnetResourceNames := []string{"aws_subnet.test", "aws_subnet.test2"} + subnetResourceNames := []string{"aws_subnet.test1", "aws_subnet.test2"} vpcResourceName := "aws_vpc.test" defaultSecurityGroupResourceName := "aws_default_security_group.test" @@ -72,7 +73,7 @@ func testAccClientVPNNetworkAssociation_multipleSubnets(t *testing.T) { CheckDestroy: testAccCheckClientVPNNetworkAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccEc2ClientVpnNetworkAssociationConfigMultipleSubnets(rStr), + Config: testAccEc2ClientVpnNetworkAssociationConfigMultipleSubnets(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClientVPNNetworkAssociationExists(resourceNames[0], &assoc), resource.TestMatchResourceAttr(resourceNames[0], "association_id", regexp.MustCompile("^cvpn-assoc-[a-z0-9]+$")), @@ -105,7 +106,7 @@ func testAccClientVPNNetworkAssociation_multipleSubnets(t *testing.T) { func testAccClientVPNNetworkAssociation_disappears(t *testing.T) { var assoc ec2.TargetNetwork - rStr := sdkacctest.RandString(5) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ec2_client_vpn_network_association.test" resource.ParallelTest(t, resource.TestCase{ @@ -115,7 +116,7 @@ func testAccClientVPNNetworkAssociation_disappears(t *testing.T) { CheckDestroy: testAccCheckClientVPNNetworkAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccEc2ClientVpnNetworkAssociationConfigBasic(rStr), + Config: testAccEc2ClientVpnNetworkAssociationConfigBasic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClientVPNNetworkAssociationExists(resourceName, &assoc), acctest.CheckResourceDisappears(acctest.Provider, tfec2.ResourceClientVPNNetworkAssociation(), resourceName), @@ -129,7 +130,7 @@ func testAccClientVPNNetworkAssociation_disappears(t *testing.T) { func testAccClientVPNNetworkAssociation_securityGroups(t *testing.T) { var assoc1, assoc2 ec2.TargetNetwork var group11, group12, group21 ec2.SecurityGroup - rStr := sdkacctest.RandString(5) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ec2_client_vpn_network_association.test" securityGroup1ResourceName := "aws_security_group.test1" securityGroup2ResourceName := "aws_security_group.test2" @@ -141,7 +142,7 @@ func testAccClientVPNNetworkAssociation_securityGroups(t *testing.T) { CheckDestroy: testAccCheckClientVPNNetworkAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccEc2ClientVpnNetworkAssociationTwoSecurityGroups(rStr), + Config: testAccEc2ClientVpnNetworkAssociationTwoSecurityGroups(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClientVPNNetworkAssociationExists(resourceName, &assoc1), testAccCheckDefaultSecurityGroupExists(securityGroup1ResourceName, &group11), @@ -158,7 +159,7 @@ func testAccClientVPNNetworkAssociation_securityGroups(t *testing.T) { ImportStateIdFunc: testAccClientVPNNetworkAssociationImportStateIdFunc(resourceName), }, { - Config: testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rStr), + Config: testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClientVPNNetworkAssociationExists(resourceName, &assoc2), testAccCheckDefaultSecurityGroupExists(securityGroup1ResourceName, &group21), @@ -178,22 +179,23 @@ func testAccCheckClientVPNNetworkAssociationDestroy(s *terraform.State) error { continue } - resp, _ := conn.DescribeClientVpnTargetNetworks(&ec2.DescribeClientVpnTargetNetworksInput{ - ClientVpnEndpointId: aws.String(rs.Primary.Attributes["client_vpn_endpoint_id"]), - AssociationIds: []*string{aws.String(rs.Primary.ID)}, - }) + _, err := tfec2.FindClientVPNNetworkAssociationByIDs(conn, rs.Primary.ID, rs.Primary.Attributes["client_vpn_endpoint_id"]) - for _, v := range resp.ClientVpnTargetNetworks { - if *v.AssociationId == rs.Primary.ID && !(*v.Status.Code == ec2.AssociationStatusCodeDisassociated) { - return fmt.Errorf("[DESTROY ERROR] Client VPN network association (%s) not deleted", rs.Primary.ID) - } + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err } + + return fmt.Errorf("EC2 Client VPN Network Association %s still exists", rs.Primary.ID) } return nil } -func testAccCheckClientVPNNetworkAssociationExists(name string, assoc *ec2.TargetNetwork) resource.TestCheckFunc { +func testAccCheckClientVPNNetworkAssociationExists(name string, v *ec2.TargetNetwork) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { @@ -201,28 +203,20 @@ func testAccCheckClientVPNNetworkAssociationExists(name string, assoc *ec2.Targe } if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") + return fmt.Errorf("No EC2 Client VPN Network Association ID is set") } conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn - resp, err := conn.DescribeClientVpnTargetNetworks(&ec2.DescribeClientVpnTargetNetworksInput{ - ClientVpnEndpointId: aws.String(rs.Primary.Attributes["client_vpn_endpoint_id"]), - AssociationIds: []*string{aws.String(rs.Primary.ID)}, - }) + output, err := tfec2.FindClientVPNNetworkAssociationByIDs(conn, rs.Primary.ID, rs.Primary.Attributes["client_vpn_endpoint_id"]) if err != nil { - return fmt.Errorf("Error reading Client VPN network association (%s): %w", rs.Primary.ID, err) + return err } - for _, a := range resp.ClientVpnTargetNetworks { - if *a.AssociationId == rs.Primary.ID && !(*a.Status.Code == ec2.AssociationStatusCodeDisassociated) { - *assoc = *a - return nil - } - } + *v = *output - return fmt.Errorf("Client VPN network association (%s) not found", rs.Primary.ID) + return nil } } @@ -243,191 +237,126 @@ func testAccClientVPNNetworkAssociationImportStateIdFunc(resourceName string) re } } -func testAccEc2ClientVpnNetworkAssociationConfigBasic(rName string) string { +func testAccEc2ClientVpnNetworkAssociationBaseConfig(rName string) string { return acctest.ConfigCompose( - testAccEc2ClientVpnNetworkAssociationVpcBase(rName), - testAccEc2ClientVpnNetworkAssociationAcmCertificateBase(), + testAccEc2ClientVpnEndpointConfig(rName), + acctest.ConfigAvailableAZsNoOptInDefaultExclude(), fmt.Sprintf(` -resource "aws_ec2_client_vpn_network_association" "test" { - client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id - subnet_id = aws_subnet.test.id +resource "aws_vpc" "test" { + cidr_block = "10.1.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_default_security_group" "test" { + vpc_id = aws_vpc.test.id } -resource "aws_ec2_client_vpn_endpoint" "test" { - description = "terraform-testacc-clientvpn-%[1]s" - server_certificate_arn = aws_acm_certificate.test.arn - client_cidr_block = "10.0.0.0/16" +resource "aws_subnet" "test1" { + availability_zone = data.aws_availability_zones.available.names[0] + cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, 0) + vpc_id = aws_vpc.test.id + map_public_ip_on_launch = true - authentication_options { - type = "certificate-authentication" - root_certificate_chain_arn = aws_acm_certificate.test.arn + tags = { + Name = %[1]q } +} - connection_log_options { - enabled = false +resource "aws_subnet" "test2" { + availability_zone = data.aws_availability_zones.available.names[1] + cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, 1) + vpc_id = aws_vpc.test.id + map_public_ip_on_launch = true + + tags = { + Name = %[1]q } } `, rName)) } +func testAccEc2ClientVpnNetworkAssociationConfigBasic(rName string) string { + return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationBaseConfig("test"), ` +resource "aws_ec2_client_vpn_network_association" "test" { + client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id + subnet_id = aws_subnet.test1.id +} +`) +} + func testAccEc2ClientVpnNetworkAssociationConfigMultipleSubnets(rName string) string { - return acctest.ConfigCompose( - testAccEc2ClientVpnNetworkAssociationVpcBase(rName), - testAccEc2ClientVpnNetworkAssociationAcmCertificateBase(), - fmt.Sprintf(` + return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), ` resource "aws_ec2_client_vpn_network_association" "test" { client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id - subnet_id = aws_subnet.test.id + subnet_id = aws_subnet.test1.id } resource "aws_ec2_client_vpn_network_association" "test2" { client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id subnet_id = aws_subnet.test2.id } - -resource "aws_ec2_client_vpn_endpoint" "test" { - description = "terraform-testacc-clientvpn-%[1]s" - server_certificate_arn = aws_acm_certificate.test.arn - client_cidr_block = "10.0.0.0/16" - - authentication_options { - type = "certificate-authentication" - root_certificate_chain_arn = aws_acm_certificate.test.arn - } - - connection_log_options { - enabled = false - } -} -`, rName)) +`) } func testAccEc2ClientVpnNetworkAssociationTwoSecurityGroups(rName string) string { return acctest.ConfigCompose( - testAccEc2ClientVpnNetworkAssociationVpcBase(rName), - testAccEc2ClientVpnNetworkAssociationAcmCertificateBase(), + testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), fmt.Sprintf(` resource "aws_ec2_client_vpn_network_association" "test" { client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id - subnet_id = aws_subnet.test.id + subnet_id = aws_subnet.test1.id security_groups = [aws_security_group.test1.id, aws_security_group.test2.id] } -resource "aws_ec2_client_vpn_endpoint" "test" { - description = "terraform-testacc-clientvpn-%[1]s" - server_certificate_arn = aws_acm_certificate.test.arn - client_cidr_block = "10.0.0.0/16" - - authentication_options { - type = "certificate-authentication" - root_certificate_chain_arn = aws_acm_certificate.test.arn - } +resource "aws_security_group" "test1" { + name = "%[1]s-1" + vpc_id = aws_vpc.test.id - connection_log_options { - enabled = false + tags = { + Name = %[1]q } } -resource "aws_security_group" "test1" { - name = "terraform_acceptance_test_example_1" - description = "Used in the terraform acceptance tests" - vpc_id = aws_vpc.test.id -} - resource "aws_security_group" "test2" { - name = "terraform_acceptance_test_example_2" - description = "Used in the terraform acceptance tests" - vpc_id = aws_vpc.test.id + name = "%[1]s-2" + vpc_id = aws_vpc.test.id + + tags = { + Name = %[1]q + } } `, rName)) } func testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rName string) string { return acctest.ConfigCompose( - testAccEc2ClientVpnNetworkAssociationVpcBase(rName), - testAccEc2ClientVpnNetworkAssociationAcmCertificateBase(), + testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), fmt.Sprintf(` resource "aws_ec2_client_vpn_network_association" "test" { client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id - subnet_id = aws_subnet.test.id + subnet_id = aws_subnet.test1.id security_groups = [aws_security_group.test1.id] } -resource "aws_ec2_client_vpn_endpoint" "test" { - description = "terraform-testacc-clientvpn-%[1]s" - server_certificate_arn = aws_acm_certificate.test.arn - client_cidr_block = "10.0.0.0/16" - - authentication_options { - type = "certificate-authentication" - root_certificate_chain_arn = aws_acm_certificate.test.arn - } - - connection_log_options { - enabled = false - } -} - resource "aws_security_group" "test1" { - name = "terraform_acceptance_test_example_1" - description = "Used in the terraform acceptance tests" - vpc_id = aws_vpc.test.id -} - -resource "aws_security_group" "test2" { - name = "terraform_acceptance_test_example_2" - description = "Used in the terraform acceptance tests" - vpc_id = aws_vpc.test.id -} -`, rName)) -} - -func testAccEc2ClientVpnNetworkAssociationVpcBase(rName string) string { - return acctest.ConfigCompose(acctest.ConfigAvailableAZsNoOptInDefaultExclude(), fmt.Sprintf(` -resource "aws_vpc" "test" { - cidr_block = "10.1.0.0/16" - - tags = { - Name = "terraform-testacc-subnet-%[1]s" - } -} - -resource "aws_default_security_group" "test" { + name = "%[1]s-1" vpc_id = aws_vpc.test.id -} - -resource "aws_subnet" "test" { - availability_zone = data.aws_availability_zones.available.names[0] - cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, 0) - vpc_id = aws_vpc.test.id - map_public_ip_on_launch = true tags = { - Name = "tf-acc-subnet-%[1]s" + Name = %[1]q } } -resource "aws_subnet" "test2" { - availability_zone = data.aws_availability_zones.available.names[1] - cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, 1) - vpc_id = aws_vpc.test.id - map_public_ip_on_launch = true +resource "aws_security_group" "test2" { + name = "%[1]s-2" + vpc_id = aws_vpc.test.id tags = { - Name = "tf-acc-subnet-%[1]s-2" + Name = %[1]q } } `, rName)) } - -func testAccEc2ClientVpnNetworkAssociationAcmCertificateBase() string { - key := acctest.TLSRSAPrivateKeyPEM(2048) - certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(key, "example.com") - - return fmt.Sprintf(` -resource "aws_acm_certificate" "test" { - certificate_body = "%[1]s" - private_key = "%[2]s" -} -`, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key)) -} diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index 3ddc98e00c5e..a8709ad0b868 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -189,6 +189,86 @@ func FindClientVPNAuthorizationRuleByThreePartKey(conn *ec2.EC2, endpointID, tar return FindClientVPNAuthorizationRule(conn, input) } +func FindClientVPNNetworkAssociation(conn *ec2.EC2, input *ec2.DescribeClientVpnTargetNetworksInput) (*ec2.TargetNetwork, error) { + output, err := FindClientVPNNetworkAssociations(conn, input) + + if err != nil { + return nil, err + } + + if len(output) == 0 || output[0] == nil || output[0].Status == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output[0], nil +} + +func FindClientVPNNetworkAssociations(conn *ec2.EC2, input *ec2.DescribeClientVpnTargetNetworksInput) ([]*ec2.TargetNetwork, error) { + var output []*ec2.TargetNetwork + + err := conn.DescribeClientVpnTargetNetworksPages(input, func(page *ec2.DescribeClientVpnTargetNetworksOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.ClientVpnTargetNetworks { + if v == nil { + continue + } + + output = append(output, v) + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, ErrCodeInvalidClientVpnEndpointIdNotFound, ErrCodeInvalidClientVpnAssociationIdNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func FindClientVPNNetworkAssociationByIDs(conn *ec2.EC2, associationID, endpointID string) (*ec2.TargetNetwork, error) { + input := &ec2.DescribeClientVpnTargetNetworksInput{ + AssociationIds: aws.StringSlice([]string{associationID}), + ClientVpnEndpointId: aws.String(endpointID), + } + + output, err := FindClientVPNNetworkAssociation(conn, input) + + if err != nil { + return nil, err + } + + if state := aws.StringValue(output.Status.Code); state == ec2.AssociationStatusCodeDisassociated { + return nil, &resource.NotFoundError{ + Message: state, + LastRequest: input, + } + } + + // Eventual consistency check. + if aws.StringValue(output.ClientVpnEndpointId) != endpointID || aws.StringValue(output.AssociationId) != associationID { + return nil, &resource.NotFoundError{ + LastRequest: input, + } + } + + return output, nil +} + func FindClientVPNRoute(conn *ec2.EC2, endpointID, targetSubnetID, destinationCidr string) (*ec2.DescribeClientVpnRoutesOutput, error) { filters := map[string]string{ "target-subnet": targetSubnetID, diff --git a/internal/service/ec2/status.go b/internal/service/ec2/status.go index 0fb4c85101da..ebde35ed05ad 100644 --- a/internal/service/ec2/status.go +++ b/internal/service/ec2/status.go @@ -125,36 +125,19 @@ func StatusClientVPNAuthorizationRule(conn *ec2.EC2, endpointID, targetNetworkCI } } -const ( - ClientVPNNetworkAssociationStatusNotFound = "NotFound" - - ClientVPNNetworkAssociationStatusUnknown = "Unknown" -) - -func StatusClientVPNNetworkAssociation(conn *ec2.EC2, cvnaID string, cvepID string) resource.StateRefreshFunc { +func StatusClientVPNNetworkAssociation(conn *ec2.EC2, associationID, endpointID string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - result, err := conn.DescribeClientVpnTargetNetworks(&ec2.DescribeClientVpnTargetNetworksInput{ - ClientVpnEndpointId: aws.String(cvepID), - AssociationIds: []*string{aws.String(cvnaID)}, - }) + output, err := FindClientVPNNetworkAssociationByIDs(conn, associationID, endpointID) - if tfawserr.ErrCodeEquals(err, ErrCodeInvalidClientVpnAssociationIdNotFound) || tfawserr.ErrCodeEquals(err, ErrCodeInvalidClientVpnEndpointIdNotFound) { - return nil, ClientVPNNetworkAssociationStatusNotFound, nil - } - if err != nil { - return nil, ClientVPNNetworkAssociationStatusUnknown, err - } - - if result == nil || len(result.ClientVpnTargetNetworks) == 0 || result.ClientVpnTargetNetworks[0] == nil { - return nil, ClientVPNNetworkAssociationStatusNotFound, nil + if tfresource.NotFound(err) { + return nil, "", nil } - network := result.ClientVpnTargetNetworks[0] - if network.Status == nil || network.Status.Code == nil { - return network, ClientVPNNetworkAssociationStatusUnknown, nil + if err != nil { + return nil, "", err } - return network, aws.StringValue(network.Status.Code), nil + return output, aws.StringValue(output.Status.Code), nil } } diff --git a/internal/service/ec2/wait.go b/internal/service/ec2/wait.go index ad58faba3fd2..0f862eda25b7 100644 --- a/internal/service/ec2/wait.go +++ b/internal/service/ec2/wait.go @@ -200,49 +200,49 @@ func WaitClientVPNAuthorizationRuleDeleted(conn *ec2.EC2, endpointID, targetNetw } const ( - ClientVPNNetworkAssociationAssociatedTimeout = 30 * time.Minute - - ClientVPNNetworkAssociationAssociatedDelay = 4 * time.Minute - - ClientVPNNetworkAssociationDisassociatedTimeout = 30 * time.Minute - - ClientVPNNetworkAssociationDisassociatedDelay = 4 * time.Minute - + ClientVPNNetworkAssociationCreatedTimeout = 30 * time.Minute + ClientVPNNetworkAssociationCreatedDelay = 4 * time.Minute + ClientVPNNetworkAssociationDeletedTimeout = 30 * time.Minute + ClientVPNNetworkAssociationDeletedDelay = 4 * time.Minute ClientVPNNetworkAssociationStatusPollInterval = 10 * time.Second ) -func WaitClientVPNNetworkAssociationAssociated(conn *ec2.EC2, networkAssociationID, clientVpnEndpointID string) (*ec2.TargetNetwork, error) { +func WaitClientVPNNetworkAssociationCreated(conn *ec2.EC2, associationID, endpointID string) (*ec2.TargetNetwork, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ec2.AssociationStatusCodeAssociating}, Target: []string{ec2.AssociationStatusCodeAssociated}, - Refresh: StatusClientVPNNetworkAssociation(conn, networkAssociationID, clientVpnEndpointID), - Timeout: ClientVPNNetworkAssociationAssociatedTimeout, - Delay: ClientVPNNetworkAssociationAssociatedDelay, + Refresh: StatusClientVPNNetworkAssociation(conn, associationID, endpointID), + Timeout: ClientVPNNetworkAssociationCreatedTimeout, + Delay: ClientVPNNetworkAssociationCreatedDelay, PollInterval: ClientVPNNetworkAssociationStatusPollInterval, } outputRaw, err := stateConf.WaitForState() if output, ok := outputRaw.(*ec2.TargetNetwork); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.Status.Message))) + return output, err } return nil, err } -func WaitClientVPNNetworkAssociationDisassociated(conn *ec2.EC2, networkAssociationID, clientVpnEndpointID string) (*ec2.TargetNetwork, error) { +func WaitClientVPNNetworkAssociationDeleted(conn *ec2.EC2, associationID, endpointID string) (*ec2.TargetNetwork, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ec2.AssociationStatusCodeDisassociating}, Target: []string{}, - Refresh: StatusClientVPNNetworkAssociation(conn, networkAssociationID, clientVpnEndpointID), - Timeout: ClientVPNNetworkAssociationDisassociatedTimeout, - Delay: ClientVPNNetworkAssociationDisassociatedDelay, + Refresh: StatusClientVPNNetworkAssociation(conn, associationID, endpointID), + Timeout: ClientVPNNetworkAssociationDeletedTimeout, + Delay: ClientVPNNetworkAssociationDeletedDelay, PollInterval: ClientVPNNetworkAssociationStatusPollInterval, } outputRaw, err := stateConf.WaitForState() if output, ok := outputRaw.(*ec2.TargetNetwork); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.Status.Message))) + return output, err } From 5a5f4f5ba055de7d0571ba3679db6d1aba31f5e3 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 14:25:20 -0500 Subject: [PATCH 22/23] r/aws_ec2_client_vpn_network_association: Configurable Create and Delete timeouts. Acceptance test output: % make testacc TESTARGS='-run=TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation' PKG=ec2 ==> Checking that code complies with gofmt requirements... TF_ACC=1 go test ./internal/service/ec2/... -v -count 1 -parallel 20 -run=TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation -timeout 180m === RUN TestAccEC2ClientVPNEndpoint_serial === PAUSE TestAccEC2ClientVPNEndpoint_serial === CONT TestAccEC2ClientVPNEndpoint_serial === RUN TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_basic === PAUSE TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_basic === RUN TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_multipleSubnets === PAUSE TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_multipleSubnets === RUN TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_disappears === PAUSE TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_disappears === RUN TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_securityGroups === PAUSE TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_securityGroups === CONT TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_basic === CONT TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_disappears === CONT TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_multipleSubnets === CONT TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_securityGroups --- PASS: TestAccEC2ClientVPNEndpoint_serial (0.44s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_basic (747.55s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_disappears (755.30s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_securityGroups (758.89s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/NetworkAssociation_multipleSubnets (1232.50s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 1236.938s --- .changelog/20689.txt | 3 +++ internal/service/ec2/client_vpn_network_association.go | 9 +++++++-- internal/service/ec2/wait.go | 8 ++++---- .../r/ec2_client_vpn_network_association.html.markdown | 7 +++++++ 4 files changed, 21 insertions(+), 6 deletions(-) create mode 100644 .changelog/20689.txt diff --git a/.changelog/20689.txt b/.changelog/20689.txt new file mode 100644 index 000000000000..8753867cfaf6 --- /dev/null +++ b/.changelog/20689.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +resource/aws_ec2_client_vpn_network_association: Configurable Create and Delete timeouts +``` \ No newline at end of file diff --git a/internal/service/ec2/client_vpn_network_association.go b/internal/service/ec2/client_vpn_network_association.go index 5bd546ee1eb6..d117ade6a4b8 100644 --- a/internal/service/ec2/client_vpn_network_association.go +++ b/internal/service/ec2/client_vpn_network_association.go @@ -25,6 +25,11 @@ func ResourceClientVPNNetworkAssociation() *schema.Resource { State: resourceClientVPNNetworkAssociationImport, }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(ClientVPNNetworkAssociationCreatedTimeout), + Delete: schema.DefaultTimeout(ClientVPNNetworkAssociationDeletedTimeout), + }, + Schema: map[string]*schema.Schema{ "association_id": { Type: schema.TypeString, @@ -80,7 +85,7 @@ func resourceClientVPNNetworkAssociationCreate(d *schema.ResourceData, meta inte d.SetId(aws.StringValue(output.AssociationId)) - targetNetwork, err := WaitClientVPNNetworkAssociationCreated(conn, d.Id(), endpointID) + targetNetwork, err := WaitClientVPNNetworkAssociationCreated(conn, d.Id(), endpointID, d.Timeout(schema.TimeoutCreate)) if err != nil { return fmt.Errorf("error waiting for EC2 Client VPN Network Association (%s) create: %w", d.Id(), err) @@ -166,7 +171,7 @@ func resourceClientVPNNetworkAssociationDelete(d *schema.ResourceData, meta inte return fmt.Errorf("error disassociating EC2 Client VPN Network Association (%s): %w", d.Id(), err) } - if _, err := WaitClientVPNNetworkAssociationDeleted(conn, d.Id(), endpointID); err != nil { + if _, err := WaitClientVPNNetworkAssociationDeleted(conn, d.Id(), endpointID, d.Timeout(schema.TimeoutDelete)); err != nil { return fmt.Errorf("error waiting for EC2 Client VPN Network Association (%s) delete: %w", d.Id(), err) } diff --git a/internal/service/ec2/wait.go b/internal/service/ec2/wait.go index 0f862eda25b7..243dc13b1020 100644 --- a/internal/service/ec2/wait.go +++ b/internal/service/ec2/wait.go @@ -207,12 +207,12 @@ const ( ClientVPNNetworkAssociationStatusPollInterval = 10 * time.Second ) -func WaitClientVPNNetworkAssociationCreated(conn *ec2.EC2, associationID, endpointID string) (*ec2.TargetNetwork, error) { +func WaitClientVPNNetworkAssociationCreated(conn *ec2.EC2, associationID, endpointID string, timeout time.Duration) (*ec2.TargetNetwork, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ec2.AssociationStatusCodeAssociating}, Target: []string{ec2.AssociationStatusCodeAssociated}, Refresh: StatusClientVPNNetworkAssociation(conn, associationID, endpointID), - Timeout: ClientVPNNetworkAssociationCreatedTimeout, + Timeout: timeout, Delay: ClientVPNNetworkAssociationCreatedDelay, PollInterval: ClientVPNNetworkAssociationStatusPollInterval, } @@ -228,12 +228,12 @@ func WaitClientVPNNetworkAssociationCreated(conn *ec2.EC2, associationID, endpoi return nil, err } -func WaitClientVPNNetworkAssociationDeleted(conn *ec2.EC2, associationID, endpointID string) (*ec2.TargetNetwork, error) { +func WaitClientVPNNetworkAssociationDeleted(conn *ec2.EC2, associationID, endpointID string, timeout time.Duration) (*ec2.TargetNetwork, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ec2.AssociationStatusCodeDisassociating}, Target: []string{}, Refresh: StatusClientVPNNetworkAssociation(conn, associationID, endpointID), - Timeout: ClientVPNNetworkAssociationDeletedTimeout, + Timeout: timeout, Delay: ClientVPNNetworkAssociationDeletedDelay, PollInterval: ClientVPNNetworkAssociationStatusPollInterval, } diff --git a/website/docs/r/ec2_client_vpn_network_association.html.markdown b/website/docs/r/ec2_client_vpn_network_association.html.markdown index 6f8d23558c02..89ba30d77825 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -50,6 +50,13 @@ In addition to all arguments above, the following attributes are exported: * `status` - The current state of the target network association. * `vpc_id` - The ID of the VPC in which the target subnet is located. +## Timeouts + +`aws_ec2_client_vpn_network_association` provides the following [Timeouts](https://www.terraform.io/docs/configuration/blocks/resources/syntax.html#operation-timeouts) configuration options: + +- `create` - (Default `30 minutes`) Used for network association +- `delete` - (Default `30 minutes`) Used for network disassociation + ## Import AWS Client VPN network associations can be imported using the endpoint ID and the association ID. Values are separated by a `,`. From b6e20544aa05db0ec6da2d83ff8b191375f3add1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Tue, 1 Feb 2022 15:09:05 -0500 Subject: [PATCH 23/23] Fix golangci-lint unparam error. --- internal/service/ec2/client_vpn_network_association_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/ec2/client_vpn_network_association_test.go b/internal/service/ec2/client_vpn_network_association_test.go index 3fad12b86c14..340b79047742 100644 --- a/internal/service/ec2/client_vpn_network_association_test.go +++ b/internal/service/ec2/client_vpn_network_association_test.go @@ -279,7 +279,7 @@ resource "aws_subnet" "test2" { } func testAccEc2ClientVpnNetworkAssociationConfigBasic(rName string) string { - return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationBaseConfig("test"), ` + return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), ` resource "aws_ec2_client_vpn_network_association" "test" { client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id subnet_id = aws_subnet.test1.id