From 8d6ef30b10e8e4ebfe8ecc73a83dcc5b85f26bc0 Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Sun, 10 Feb 2019 19:26:11 -0600 Subject: [PATCH 01/14] r/aws_ec2_client_vpn_endpoint_network_association: Adding option to apply additional security groups to target network --- ..._aws_ec2_client_vpn_network_association.go | 30 +++- ...ec2_client_vpn_network_association_test.go | 143 ++++++++++++++++++ ...ient_vpn_network_association.html.markdown | 9 +- 3 files changed, 176 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_ec2_client_vpn_network_association.go b/aws/resource_aws_ec2_client_vpn_network_association.go index 6210a0c0257..82d92817617 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association.go +++ b/aws/resource_aws_ec2_client_vpn_network_association.go @@ -1,6 +1,7 @@ package aws import ( + "errors" "fmt" "log" @@ -29,14 +30,19 @@ func resourceAwsEc2ClientVpnNetworkAssociation() *schema.Resource { }, "security_groups": { Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, Computed: true, + ForceNew: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, }, - "status": { + "vpc_id": { Type: schema.TypeString, + Optional: true, Computed: true, + ForceNew: true, }, - "vpc_id": { + "status": { Type: schema.TypeString, Computed: true, }, @@ -73,6 +79,24 @@ func resourceAwsEc2ClientVpnNetworkAssociationCreate(d *schema.ResourceData, met return fmt.Errorf("Error waiting for Client VPN endpoint to associate with target network: %s", err) } + sgIDs, okSgIDs := d.GetOk("security_groups") + vpcID, okVpcID := d.GetOk("vpc_id") + if okSgIDs && okVpcID { + sgReq := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ + ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), + VpcId: aws.String(vpcID.(string)), + SecurityGroupIds: expandStringList(sgIDs.(*schema.Set).List()), + } + + _, err := conn.ApplySecurityGroupsToClientVpnTargetNetwork(sgReq) + if err != nil { + return fmt.Errorf("Error applying security groups to Client VPN network association: %s", err) + } + } + if (okSgIDs && !okVpcID) || (!okSgIDs && okVpcID) { + return errors.New("both vpc_id and security_groups must be set in order to attach additional security groups to this target network") + } + return resourceAwsEc2ClientVpnNetworkAssociationRead(d, meta) } diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index 7d1f13c96dc..65d2d0d34e8 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -52,6 +52,26 @@ func TestAccAwsEc2ClientVpnNetworkAssociation_disappears(t *testing.T) { }) } +func TestAccAwsEc2ClientVpnNetworkAssociation_securityGroups(t *testing.T) { + var assoc1 ec2.TargetNetwork + rStr := acctest.RandString(5) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersWithTLS, + CheckDestroy: testAccCheckAwsEc2ClientVpnNetworkAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEc2ClientVpnNetworkAssociationSecurityGroups(rStr), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsEc2ClientVpnNetworkAssociationExists("aws_ec2_client_vpn_network_association.test", &assoc1), + resource.TestCheckResourceAttr("aws_ec2_client_vpn_network_association.test", "security_groups.#", "2"), + ), + }, + }, + }) +} + func testAccCheckAwsEc2ClientVpnNetworkAssociationDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ec2conn @@ -200,3 +220,126 @@ resource "aws_ec2_client_vpn_network_association" "test" { } `, rName, rName, rName) } + +func testAccEc2ClientVpnNetworkAssociationSecurityGroups(rName string) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.1.0.0/16" + tags = { + Name = "terraform-testacc-subnet-%s" + } +} + +resource "aws_subnet" "test" { + cidr_block = "10.1.1.0/24" + vpc_id = "${aws_vpc.test.id}" + map_public_ip_on_launch = true + tags = { + Name = "tf-acc-subnet-%s" + } +} + +resource "aws_security_group" "test1" { + name = "terraform_acceptance_test_example_1" + description = "Used in the terraform acceptance tests" + vpc_id = "${aws_vpc.test.id}" + + ingress { + protocol = "tcp" + from_port = 22 + to_port = 22 + cidr_blocks = ["10.1.1.0/24"] + } + + ingress { + protocol = "tcp" + from_port = 80 + to_port = 80 + cidr_blocks = ["10.1.1.0/24"] + } + + egress { + protocol = "-1" + from_port = 0 + to_port = 0 + cidr_blocks = ["10.1.0.0/16"] + } +} + +resource "aws_security_group" "test2" { + name = "terraform_acceptance_test_example_2" + description = "Used in the terraform acceptance tests" + vpc_id = "${aws_vpc.test.id}" + + ingress { + protocol = "tcp" + from_port = 22 + to_port = 22 + cidr_blocks = ["10.1.2.0/24"] + } + + ingress { + protocol = "tcp" + from_port = 80 + to_port = 80 + cidr_blocks = ["10.1.2.0/24"] + } + + egress { + protocol = "-1" + from_port = 0 + to_port = 0 + cidr_blocks = ["10.1.0.0/16"] + } +} + +resource "tls_private_key" "example" { + algorithm = "RSA" +} + +resource "tls_self_signed_cert" "example" { + key_algorithm = "RSA" + private_key_pem = "${tls_private_key.example.private_key_pem}" + + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + + validity_period_hours = 12 + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +resource "aws_acm_certificate" "cert" { + private_key = "${tls_private_key.example.private_key_pem}" + certificate_body = "${tls_self_signed_cert.example.cert_pem}" +} + +resource "aws_ec2_client_vpn_endpoint" "test" { + description = "terraform-testacc-clientvpn-%s" + server_certificate_arn = "${aws_acm_certificate.cert.arn}" + client_cidr_block = "10.0.0.0/16" + + authentication_options { + type = "certificate-authentication" + root_certificate_chain_arn = "${aws_acm_certificate.cert.arn}" + } + + connection_log_options { + enabled = false + } +} + +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}" + security_groups = ["${aws_security_group.test1.id}", "${aws_security_group.test2.id}"] + vpc_id = "${aws_vpc.test.id}" +} +`, rName, rName, rName) +} 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 7533e2ffcd9..433301db278 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -15,8 +15,9 @@ Provides network associations for AWS Client VPN endpoints. For more information ```hcl resource "aws_ec2_client_vpn_network_association" "example" { - client_vpn_endpoint_id = "${aws_ec2_client_vpn_endpoint.example.id}" - subnet_id = "${aws_subnet.example.id}" + client_vpn_endpoint_id = "${aws_ec2_client_vpn_endpoint.example.id}" + subnet_id = "${aws_subnet.example.id}" + } ``` @@ -26,6 +27,8 @@ The following arguments are supported: * `client_vpn_endpoint_id` - (Required) The ID of the Client VPN endpoint. * `subnet_id` - (Required) The ID of the subnet to associate with the Client VPN endpoint. +* `security_groups` - (Optional) A list of up to five security groups to apply to the target network. +* `vpc_id` - (Optional) The ID of the VPC in which the target network is locatated. This is a required argument for assigning security groups to a network association. ## Attributes Reference @@ -33,5 +36,5 @@ In addition to all arguments above, the following attributes are exported: * `id` - The unique ID of the target network association. * `security_groups` - The IDs of the security groups applied to the target network association. +* `vpc_id` - The ID of the VPC in which the target network (subnet) is located. * `status` - The current state of the target network association. -* `vpc_id` - The ID of the VPC in which the target network (subnet) is located. From a0580d2b19b374dfb6ad6d4ecb4f1306c787882e Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Sun, 10 Feb 2019 19:38:45 -0600 Subject: [PATCH 02/14] spaces/tabs --- ...ec2_client_vpn_network_association_test.go | 108 +++++++++--------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index 65d2d0d34e8..3b9f79ec78b 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -224,73 +224,73 @@ resource "aws_ec2_client_vpn_network_association" "test" { func testAccEc2ClientVpnNetworkAssociationSecurityGroups(rName string) string { return fmt.Sprintf(` resource "aws_vpc" "test" { - cidr_block = "10.1.0.0/16" - tags = { - Name = "terraform-testacc-subnet-%s" - } + cidr_block = "10.1.0.0/16" + tags = { + Name = "terraform-testacc-subnet-%s" + } } resource "aws_subnet" "test" { - cidr_block = "10.1.1.0/24" - vpc_id = "${aws_vpc.test.id}" - map_public_ip_on_launch = true - tags = { - Name = "tf-acc-subnet-%s" - } + cidr_block = "10.1.1.0/24" + vpc_id = "${aws_vpc.test.id}" + map_public_ip_on_launch = true + tags = { + Name = "tf-acc-subnet-%s" + } } resource "aws_security_group" "test1" { - name = "terraform_acceptance_test_example_1" - description = "Used in the terraform acceptance tests" - vpc_id = "${aws_vpc.test.id}" - - ingress { - protocol = "tcp" - from_port = 22 - to_port = 22 - cidr_blocks = ["10.1.1.0/24"] - } + name = "terraform_acceptance_test_example_1" + description = "Used in the terraform acceptance tests" + vpc_id = "${aws_vpc.test.id}" + + ingress { + protocol = "tcp" + from_port = 22 + to_port = 22 + cidr_blocks = ["10.1.1.0/24"] + } - ingress { - protocol = "tcp" - from_port = 80 - to_port = 80 - cidr_blocks = ["10.1.1.0/24"] - } + ingress { + protocol = "tcp" + from_port = 80 + to_port = 80 + cidr_blocks = ["10.1.1.0/24"] + } - egress { - protocol = "-1" - from_port = 0 - to_port = 0 - cidr_blocks = ["10.1.0.0/16"] - } + egress { + protocol = "-1" + from_port = 0 + to_port = 0 + cidr_blocks = ["10.1.0.0/16"] + } } resource "aws_security_group" "test2" { - name = "terraform_acceptance_test_example_2" - description = "Used in the terraform acceptance tests" - vpc_id = "${aws_vpc.test.id}" - - ingress { - protocol = "tcp" - from_port = 22 - to_port = 22 - cidr_blocks = ["10.1.2.0/24"] - } + name = "terraform_acceptance_test_example_2" + description = "Used in the terraform acceptance tests" + vpc_id = "${aws_vpc.test.id}" + + ingress { + protocol = "tcp" + from_port = 22 + to_port = 22 + cidr_blocks = ["10.1.2.0/24"] + } - ingress { - protocol = "tcp" - from_port = 80 - to_port = 80 - cidr_blocks = ["10.1.2.0/24"] - } + ingress { + protocol = "tcp" + from_port = 80 + to_port = 80 + cidr_blocks = ["10.1.2.0/24"] + } - egress { - protocol = "-1" - from_port = 0 - to_port = 0 - cidr_blocks = ["10.1.0.0/16"] - } + egress { + protocol = "-1" + from_port = 0 + to_port = 0 + cidr_blocks = ["10.1.0.0/16"] + } } resource "tls_private_key" "example" { From a0bb484cf0f865894bb8435451ccf84240feb0cc Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Tue, 12 Feb 2019 09:04:41 -0600 Subject: [PATCH 03/14] changes based on review feedback --- ..._aws_ec2_client_vpn_network_association.go | 44 ++++++++++------- ...ec2_client_vpn_network_association_test.go | 49 +++++++++---------- ...ient_vpn_network_association.html.markdown | 3 +- 3 files changed, 52 insertions(+), 44 deletions(-) diff --git a/aws/resource_aws_ec2_client_vpn_network_association.go b/aws/resource_aws_ec2_client_vpn_network_association.go index 82d92817617..211472b244b 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association.go +++ b/aws/resource_aws_ec2_client_vpn_network_association.go @@ -1,7 +1,6 @@ package aws import ( - "errors" "fmt" "log" @@ -15,6 +14,7 @@ func resourceAwsEc2ClientVpnNetworkAssociation() *schema.Resource { return &schema.Resource{ Create: resourceAwsEc2ClientVpnNetworkAssociationCreate, Read: resourceAwsEc2ClientVpnNetworkAssociationRead, + Update: resourceAwsEc2ClientVpnNetworkAssociationUpdate, Delete: resourceAwsEc2ClientVpnNetworkAssociationDelete, Schema: map[string]*schema.Schema{ @@ -30,18 +30,13 @@ func resourceAwsEc2ClientVpnNetworkAssociation() *schema.Resource { }, "security_groups": { Type: schema.TypeSet, + MinItems: 1, + MaxItems: 5, Optional: true, Computed: true, - ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, }, - "vpc_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - ForceNew: true, - }, "status": { Type: schema.TypeString, Computed: true, @@ -74,18 +69,18 @@ func resourceAwsEc2ClientVpnNetworkAssociationCreate(d *schema.ResourceData, met } log.Printf("[DEBUG] Waiting for Client VPN endpoint to associate with target network: %s", d.Id()) - _, err = stateConf.WaitForState() + targetNetworkRaw, err := stateConf.WaitForState() if err != nil { return fmt.Errorf("Error waiting for Client VPN endpoint to associate with target network: %s", err) } - sgIDs, okSgIDs := d.GetOk("security_groups") - vpcID, okVpcID := d.GetOk("vpc_id") - if okSgIDs && okVpcID { + targetNetwork := targetNetworkRaw.(*ec2.TargetNetwork) + + if v, ok := d.GetOk("security_groups"); ok { sgReq := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), - VpcId: aws.String(vpcID.(string)), - SecurityGroupIds: expandStringList(sgIDs.(*schema.Set).List()), + VpcId: targetNetwork.VpcId, + SecurityGroupIds: expandStringSet(v.(*schema.Set)), } _, err := conn.ApplySecurityGroupsToClientVpnTargetNetwork(sgReq) @@ -93,9 +88,6 @@ func resourceAwsEc2ClientVpnNetworkAssociationCreate(d *schema.ResourceData, met return fmt.Errorf("Error applying security groups to Client VPN network association: %s", err) } } - if (okSgIDs && !okVpcID) || (!okSgIDs && okVpcID) { - return errors.New("both vpc_id and security_groups must be set in order to attach additional security groups to this target network") - } return resourceAwsEc2ClientVpnNetworkAssociationRead(d, meta) } @@ -143,6 +135,24 @@ func resourceAwsEc2ClientVpnNetworkAssociationRead(d *schema.ResourceData, meta return nil } +func resourceAwsEc2ClientVpnNetworkAssociationUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + if d.HasChange("security_groups") { + input := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ + ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), + SecurityGroupIds: 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 Client VPN Target Network: %s", err) + } + } + + return resourceAwsEc2ClientVpnNetworkAssociationRead(d, meta) +} + func resourceAwsEc2ClientVpnNetworkAssociationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index 3b9f79ec78b..bc68ce14f82 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -231,8 +231,8 @@ resource "aws_vpc" "test" { } resource "aws_subnet" "test" { - cidr_block = "10.1.1.0/24" - vpc_id = "${aws_vpc.test.id}" + cidr_block = "10.1.1.0/24" + vpc_id = "${aws_vpc.test.id}" map_public_ip_on_launch = true tags = { Name = "tf-acc-subnet-%s" @@ -240,15 +240,15 @@ resource "aws_subnet" "test" { } resource "aws_security_group" "test1" { - name = "terraform_acceptance_test_example_1" + name = "terraform_acceptance_test_example_1" description = "Used in the terraform acceptance tests" - vpc_id = "${aws_vpc.test.id}" + vpc_id = "${aws_vpc.test.id}" ingress { - protocol = "tcp" - from_port = 22 - to_port = 22 - cidr_blocks = ["10.1.1.0/24"] + protocol = "tcp" + from_port = 22 + to_port = 22 + cidr_blocks = ["10.1.1.0/24"] } ingress { @@ -267,28 +267,28 @@ resource "aws_security_group" "test1" { } resource "aws_security_group" "test2" { - name = "terraform_acceptance_test_example_2" + name = "terraform_acceptance_test_example_2" description = "Used in the terraform acceptance tests" - vpc_id = "${aws_vpc.test.id}" + vpc_id = "${aws_vpc.test.id}" ingress { - protocol = "tcp" - from_port = 22 - to_port = 22 + protocol = "tcp" + from_port = 22 + to_port = 22 cidr_blocks = ["10.1.2.0/24"] } ingress { - protocol = "tcp" - from_port = 80 - to_port = 80 + protocol = "tcp" + from_port = 80 + to_port = 80 cidr_blocks = ["10.1.2.0/24"] } egress { - protocol = "-1" - from_port = 0 - to_port = 0 + protocol = "-1" + from_port = 0 + to_port = 0 cidr_blocks = ["10.1.0.0/16"] } } @@ -321,12 +321,12 @@ resource "aws_acm_certificate" "cert" { } resource "aws_ec2_client_vpn_endpoint" "test" { - description = "terraform-testacc-clientvpn-%s" + description = "terraform-testacc-clientvpn-%s" server_certificate_arn = "${aws_acm_certificate.cert.arn}" - client_cidr_block = "10.0.0.0/16" + client_cidr_block = "10.0.0.0/16" authentication_options { - type = "certificate-authentication" + type = "certificate-authentication" root_certificate_chain_arn = "${aws_acm_certificate.cert.arn}" } @@ -337,9 +337,8 @@ resource "aws_ec2_client_vpn_endpoint" "test" { 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}" - security_groups = ["${aws_security_group.test1.id}", "${aws_security_group.test2.id}"] - vpc_id = "${aws_vpc.test.id}" + subnet_id = "${aws_subnet.test.id}" + security_groups = ["${aws_security_group.test1.id}", "${aws_security_group.test2.id}"] } `, rName, rName, rName) } 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 433301db278..570bb36c43f 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -27,8 +27,7 @@ The following arguments are supported: * `client_vpn_endpoint_id` - (Required) The ID of the Client VPN endpoint. * `subnet_id` - (Required) The ID of the subnet to associate with the Client VPN endpoint. -* `security_groups` - (Optional) A list of up to five security groups to apply to the target network. -* `vpc_id` - (Optional) The ID of the VPC in which the target network is locatated. This is a required argument for assigning security groups to a network association. +* `security_groups` - (Optional) A list of up to five custom security groups to apply to the target network. Not populating this parameter will result in the subnet inheriting the default VPC security group. ## Attributes Reference From 40ff75a1730a3de27e815eceee60a909c13b3943 Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Tue, 12 Feb 2019 21:10:37 -0600 Subject: [PATCH 04/14] more changes based on review feedback --- ..._aws_ec2_client_vpn_network_association.go | 43 +++--- ...ec2_client_vpn_network_association_test.go | 136 +++++++++++++++++- ...ient_vpn_network_association.html.markdown | 14 +- 3 files changed, 168 insertions(+), 25 deletions(-) diff --git a/aws/resource_aws_ec2_client_vpn_network_association.go b/aws/resource_aws_ec2_client_vpn_network_association.go index 211472b244b..58d04fdb04e 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association.go +++ b/aws/resource_aws_ec2_client_vpn_network_association.go @@ -30,9 +30,8 @@ func resourceAwsEc2ClientVpnNetworkAssociation() *schema.Resource { }, "security_groups": { Type: schema.TypeSet, - MinItems: 1, MaxItems: 5, - Optional: true, + Required: true, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, @@ -41,6 +40,10 @@ func resourceAwsEc2ClientVpnNetworkAssociation() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -92,6 +95,24 @@ func resourceAwsEc2ClientVpnNetworkAssociationCreate(d *schema.ResourceData, met return resourceAwsEc2ClientVpnNetworkAssociationRead(d, meta) } +func resourceAwsEc2ClientVpnNetworkAssociationUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + if d.HasChange("security_groups") { + input := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ + ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), + SecurityGroupIds: 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 Client VPN Target Network: %s", err) + } + } + + return resourceAwsEc2ClientVpnNetworkAssociationRead(d, meta) +} + func resourceAwsEc2ClientVpnNetworkAssociationRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn var err error @@ -135,24 +156,6 @@ func resourceAwsEc2ClientVpnNetworkAssociationRead(d *schema.ResourceData, meta return nil } -func resourceAwsEc2ClientVpnNetworkAssociationUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).ec2conn - - if d.HasChange("security_groups") { - input := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ - ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), - SecurityGroupIds: 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 Client VPN Target Network: %s", err) - } - } - - return resourceAwsEc2ClientVpnNetworkAssociationRead(d, meta) -} - func resourceAwsEc2ClientVpnNetworkAssociationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index bc68ce14f82..648b0eb6d1a 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -62,12 +62,19 @@ func TestAccAwsEc2ClientVpnNetworkAssociation_securityGroups(t *testing.T) { CheckDestroy: testAccCheckAwsEc2ClientVpnNetworkAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccEc2ClientVpnNetworkAssociationSecurityGroups(rStr), + Config: testAccEc2ClientVpnNetworkAssociationTwoSecurityGroups(rStr), Check: resource.ComposeTestCheckFunc( testAccCheckAwsEc2ClientVpnNetworkAssociationExists("aws_ec2_client_vpn_network_association.test", &assoc1), resource.TestCheckResourceAttr("aws_ec2_client_vpn_network_association.test", "security_groups.#", "2"), ), }, + { + Config: testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rStr), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsEc2ClientVpnNetworkAssociationExists("aws_ec2_client_vpn_network_association.test", &assoc1), + resource.TestCheckResourceAttr("aws_ec2_client_vpn_network_association.test", "security_groups.#", "1"), + ), + }, }, }) } @@ -216,12 +223,13 @@ resource "aws_ec2_client_vpn_endpoint" "test" { 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.test.id}" + security_groups = [""] } `, rName, rName, rName) } -func testAccEc2ClientVpnNetworkAssociationSecurityGroups(rName string) string { +func testAccEc2ClientVpnNetworkAssociationTwoSecurityGroups(rName string) string { return fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "10.1.0.0/16" @@ -342,3 +350,125 @@ resource "aws_ec2_client_vpn_network_association" "test" { } `, rName, rName, rName) } + +func testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rName string) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.1.0.0/16" + tags = { + Name = "terraform-testacc-subnet-%s" + } +} + +resource "aws_subnet" "test" { + cidr_block = "10.1.1.0/24" + vpc_id = "${aws_vpc.test.id}" + map_public_ip_on_launch = true + tags = { + Name = "tf-acc-subnet-%s" + } +} + +resource "aws_security_group" "test1" { + name = "terraform_acceptance_test_example_1" + description = "Used in the terraform acceptance tests" + vpc_id = "${aws_vpc.test.id}" + + ingress { + protocol = "tcp" + from_port = 22 + to_port = 22 + cidr_blocks = ["10.1.1.0/24"] + } + + ingress { + protocol = "tcp" + from_port = 80 + to_port = 80 + cidr_blocks = ["10.1.1.0/24"] + } + + egress { + protocol = "-1" + from_port = 0 + to_port = 0 + cidr_blocks = ["10.1.0.0/16"] + } +} + +resource "aws_security_group" "test2" { + name = "terraform_acceptance_test_example_2" + description = "Used in the terraform acceptance tests" + vpc_id = "${aws_vpc.test.id}" + + ingress { + protocol = "tcp" + from_port = 22 + to_port = 22 + cidr_blocks = ["10.1.2.0/24"] + } + + ingress { + protocol = "tcp" + from_port = 80 + to_port = 80 + cidr_blocks = ["10.1.2.0/24"] + } + + egress { + protocol = "-1" + from_port = 0 + to_port = 0 + cidr_blocks = ["10.1.0.0/16"] + } +} + +resource "tls_private_key" "example" { + algorithm = "RSA" +} + +resource "tls_self_signed_cert" "example" { + key_algorithm = "RSA" + private_key_pem = "${tls_private_key.example.private_key_pem}" + + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + + validity_period_hours = 12 + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +resource "aws_acm_certificate" "cert" { + private_key = "${tls_private_key.example.private_key_pem}" + certificate_body = "${tls_self_signed_cert.example.cert_pem}" +} + +resource "aws_ec2_client_vpn_endpoint" "test" { + description = "terraform-testacc-clientvpn-%s" + server_certificate_arn = "${aws_acm_certificate.cert.arn}" + client_cidr_block = "10.0.0.0/16" + + authentication_options { + type = "certificate-authentication" + root_certificate_chain_arn = "${aws_acm_certificate.cert.arn}" + } + + connection_log_options { + enabled = false + } +} + +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}" + security_groups = ["${aws_security_group.test1.id}"] +} +`, rName, rName, rName) +} 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 570bb36c43f..2c64f64f4ea 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -11,13 +11,23 @@ description: |- Provides network associations for AWS Client VPN endpoints. For more information on usage, please see the [AWS Client VPN Administrator's Guide](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/what-is.html). -## Example Usage +## Example Usage (w/ default VPC security group) ```hcl resource "aws_ec2_client_vpn_network_association" "example" { client_vpn_endpoint_id = "${aws_ec2_client_vpn_endpoint.example.id}" subnet_id = "${aws_subnet.example.id}" + security_groups = [""] +} +``` +## Example Usage (w/ custom security groups) + +```hcl +resource "aws_ec2_client_vpn_network_association" "example" { + client_vpn_endpoint_id = "${aws_ec2_client_vpn_endpoint.example.id}" + subnet_id = "${aws_subnet.example.id}" + security_groups = ["${aws_security_group.example1.id}", "${aws_security_group.example2.id}"] } ``` @@ -27,7 +37,7 @@ The following arguments are supported: * `client_vpn_endpoint_id` - (Required) The ID of the Client VPN endpoint. * `subnet_id` - (Required) The ID of the subnet to associate with the Client VPN endpoint. -* `security_groups` - (Optional) A list of up to five custom security groups to apply to the target network. Not populating this parameter will result in the subnet inheriting the default VPC security group. +* `security_groups` - (Required) A list of up to five custom security groups to apply to the target network. Not populating this parameter will result in the subnet inheriting the default VPC security group. Regardless of what you choose, you must define this parameter due to the AWS API automatically assigning the default VPC security group if no other groups are included in the call. ## Attributes Reference From 4fe42d6d1ee459f90dc8fe50ccb078a2f21f61d7 Mon Sep 17 00:00:00 2001 From: "Aaron J. Smith" Date: Tue, 12 Feb 2019 21:25:53 -0600 Subject: [PATCH 05/14] clearing travis failure --- aws/resource_aws_ec2_client_vpn_network_association.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws/resource_aws_ec2_client_vpn_network_association.go b/aws/resource_aws_ec2_client_vpn_network_association.go index 58d04fdb04e..1ca3adef5d8 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association.go +++ b/aws/resource_aws_ec2_client_vpn_network_association.go @@ -31,7 +31,7 @@ func resourceAwsEc2ClientVpnNetworkAssociation() *schema.Resource { "security_groups": { Type: schema.TypeSet, MaxItems: 5, - Required: true, + Optional: true, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, From 0f0c3fbc362f625efb5a13b38300669cb5911eb1 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 9 Jul 2020 12:49:18 -0700 Subject: [PATCH 06/14] Adds more checks to Client VPN network association basic test --- ...resource_aws_ec2_client_vpn_network_association_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index abaa20bc208..523a5440c2a 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -15,6 +15,9 @@ func testAccAwsEc2ClientVpnNetworkAssociation_basic(t *testing.T) { var assoc1 ec2.TargetNetwork rStr := acctest.RandString(5) resourceName := "aws_ec2_client_vpn_network_association.test" + endpointResourceName := "aws_ec2_client_vpn_endpoint.test" + subnetResourceName := "aws_subnet.test" + vpcResourceName := "aws_vpc.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckClientVPNSyncronize(t) }, @@ -25,6 +28,10 @@ func testAccAwsEc2ClientVpnNetworkAssociation_basic(t *testing.T) { Config: testAccEc2ClientVpnNetworkAssociationConfigBasic(rStr), Check: resource.ComposeTestCheckFunc( testAccCheckAwsEc2ClientVpnNetworkAssociationExists(resourceName, &assoc1), + resource.TestCheckResourceAttrPair(resourceName, "client_vpn_endpoint_id", endpointResourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "subnet_id", subnetResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_id", vpcResourceName, "id"), ), }, }, From 20fc0ed0a16f7ee51feb94294ca389f7eba8dd0e Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 9 Jul 2020 13:29:34 -0700 Subject: [PATCH 07/14] Testing tweaks --- GNUmakefile | 3 ++- aws/internal/experimental/sync/sync.go | 8 +++++++- aws/resource_aws_ec2_client_vpn_endpoint_test.go | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index d932dc7151e..07be2f6f27f 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -5,6 +5,7 @@ GOFMT_FILES?=$$(find . -name '*.go' |grep -v vendor) PKG_NAME=aws WEBSITE_REPO=github.com/hashicorp/terraform-website TEST_COUNT?=1 +ACCTEST_TIMEOUT?=120m default: build @@ -23,7 +24,7 @@ test: fmtcheck go test $(TEST) $(TESTARGS) -timeout=120s -parallel=4 testacc: fmtcheck - TF_ACC=1 go test $(TEST) -v -count $(TEST_COUNT) -parallel 20 $(TESTARGS) -timeout 120m + TF_ACC=1 go test $(TEST) -v -count $(TEST_COUNT) -parallel 20 $(TESTARGS) -timeout $(ACCTEST_TIMEOUT) fmt: @echo "==> Fixing source code with gofmt..." diff --git a/aws/internal/experimental/sync/sync.go b/aws/internal/experimental/sync/sync.go index bf363e89b08..a50df8d9ece 100644 --- a/aws/internal/experimental/sync/sync.go +++ b/aws/internal/experimental/sync/sync.go @@ -2,6 +2,7 @@ package sync import ( "fmt" + "log" "os" "strconv" "testing" @@ -34,7 +35,12 @@ func (s Semaphore) Wait() { // Notify releases a semaphore // NOTE: this is currently an experimental feature and is likely to change. DO NOT USE. func (s Semaphore) Notify() { - <-s + // Make the Notify non-blocking. This can happen if a Wait was never issued + select { + case <-s: + default: + log.Println("[WARN] Notifying semaphore without Wait") + } } // TestAccPreCheckSyncronized waits for a semaphore and skips the test if there is no capacity diff --git a/aws/resource_aws_ec2_client_vpn_endpoint_test.go b/aws/resource_aws_ec2_client_vpn_endpoint_test.go index 4d5b657e8d3..34b6b3e87bf 100644 --- a/aws/resource_aws_ec2_client_vpn_endpoint_test.go +++ b/aws/resource_aws_ec2_client_vpn_endpoint_test.go @@ -101,6 +101,7 @@ func TestAccAwsEc2ClientVpn(t *testing.T) { }, } + t.Parallel() for group, m := range testCases { m := m for name, tc := range m { From 81b0c98f86fdf16e52f00c5e96eb400cc085dfd2 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 9 Jul 2020 14:07:04 -0700 Subject: [PATCH 08/14] Adds Client VPN network association security group tests to synchronization --- aws/resource_aws_ec2_client_vpn_endpoint_test.go | 5 +++-- ...esource_aws_ec2_client_vpn_network_association_test.go | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_ec2_client_vpn_endpoint_test.go b/aws/resource_aws_ec2_client_vpn_endpoint_test.go index 34b6b3e87bf..3fbdcbf8b6e 100644 --- a/aws/resource_aws_ec2_client_vpn_endpoint_test.go +++ b/aws/resource_aws_ec2_client_vpn_endpoint_test.go @@ -96,8 +96,9 @@ func TestAccAwsEc2ClientVpn(t *testing.T) { "disappears": testAccAwsEc2ClientVpnAuthorizationRule_disappears, }, "NetworkAssociation": { - "basic": testAccAwsEc2ClientVpnNetworkAssociation_basic, - "disappears": testAccAwsEc2ClientVpnNetworkAssociation_disappears, + "basic": testAccAwsEc2ClientVpnNetworkAssociation_basic, + "disappears": testAccAwsEc2ClientVpnNetworkAssociation_disappears, + "securityGroups": testAccAwsEc2ClientVpnNetworkAssociation_securityGroups, }, } diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index 523a5440c2a..f9ae91e135b 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -60,13 +60,13 @@ func testAccAwsEc2ClientVpnNetworkAssociation_disappears(t *testing.T) { }) } -func TestAccAwsEc2ClientVpnNetworkAssociation_securityGroups(t *testing.T) { - var assoc1 ec2.TargetNetwork +func testAccAwsEc2ClientVpnNetworkAssociation_securityGroups(t *testing.T) { + var assoc1, assoc2 ec2.TargetNetwork rStr := acctest.RandString(5) resourceName := "aws_ec2_client_vpn_network_association.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { testAccPreCheck(t); testAccPreCheckClientVPNSyncronize(t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAwsEc2ClientVpnNetworkAssociationDestroy, Steps: []resource.TestStep{ @@ -80,7 +80,7 @@ func TestAccAwsEc2ClientVpnNetworkAssociation_securityGroups(t *testing.T) { { Config: testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rStr), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsEc2ClientVpnNetworkAssociationExists(resourceName, &assoc1), + testAccCheckAwsEc2ClientVpnNetworkAssociationExists(resourceName, &assoc2), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), ), }, From 1274ff65a093efd444a22d9aa1c56960eb65b7a3 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 9 Jul 2020 14:20:15 -0700 Subject: [PATCH 09/14] Adds check for default security group ID in basic test case --- ...ec2_client_vpn_network_association_test.go | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index f9ae91e135b..2a3a860f3ed 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -9,15 +9,18 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfawsresource" ) func testAccAwsEc2ClientVpnNetworkAssociation_basic(t *testing.T) { - var assoc1 ec2.TargetNetwork + var assoc ec2.TargetNetwork + var group ec2.SecurityGroup rStr := acctest.RandString(5) resourceName := "aws_ec2_client_vpn_network_association.test" endpointResourceName := "aws_ec2_client_vpn_endpoint.test" subnetResourceName := "aws_subnet.test" vpcResourceName := "aws_vpc.test" + defaultSecurityGroupResourceName := "aws_default_security_group.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckClientVPNSyncronize(t) }, @@ -27,10 +30,12 @@ func testAccAwsEc2ClientVpnNetworkAssociation_basic(t *testing.T) { { Config: testAccEc2ClientVpnNetworkAssociationConfigBasic(rStr), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsEc2ClientVpnNetworkAssociationExists(resourceName, &assoc1), + testAccCheckAwsEc2ClientVpnNetworkAssociationExists(resourceName, &assoc), + testAccCheckAWSDefaultSecurityGroupExists(defaultSecurityGroupResourceName, &group), resource.TestCheckResourceAttrPair(resourceName, "client_vpn_endpoint_id", endpointResourceName, "id"), resource.TestCheckResourceAttrPair(resourceName, "subnet_id", subnetResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + testAccCheckAwsEc2ClientVpnNetworkAssociationSecurityGroupID(resourceName, "security_groups.*", &group), resource.TestCheckResourceAttrPair(resourceName, "vpc_id", vpcResourceName, "id"), ), }, @@ -39,7 +44,7 @@ func testAccAwsEc2ClientVpnNetworkAssociation_basic(t *testing.T) { } func testAccAwsEc2ClientVpnNetworkAssociation_disappears(t *testing.T) { - var assoc1 ec2.TargetNetwork + var assoc ec2.TargetNetwork rStr := acctest.RandString(5) resourceName := "aws_ec2_client_vpn_network_association.test" @@ -51,7 +56,7 @@ func testAccAwsEc2ClientVpnNetworkAssociation_disappears(t *testing.T) { { Config: testAccEc2ClientVpnNetworkAssociationConfigBasic(rStr), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsEc2ClientVpnNetworkAssociationExists(resourceName, &assoc1), + testAccCheckAwsEc2ClientVpnNetworkAssociationExists(resourceName, &assoc), testAccCheckResourceDisappears(testAccProvider, resourceAwsEc2ClientVpnNetworkAssociation(), resourceName), ), ExpectNonEmptyPlan: true, @@ -146,6 +151,12 @@ func testAccCheckAwsEc2ClientVpnNetworkAssociationExists(name string, assoc *ec2 } } +func testAccCheckAwsEc2ClientVpnNetworkAssociationSecurityGroupID(name, key string, group *ec2.SecurityGroup) resource.TestCheckFunc { + return func(s *terraform.State) error { + return tfawsresource.TestCheckTypeSetElemAttr(name, key, aws.StringValue(group.GroupId))(s) + } +} + func testAccEc2ClientVpnNetworkAssociationConfigBasic(rName string) string { return composeConfig( testAccEc2ClientVpnNetworkAssociationVpcBase(rName, 1), @@ -169,7 +180,12 @@ resource "aws_ec2_client_vpn_endpoint" "test" { connection_log_options { enabled = false } -}`, rName)) +} + +resource "aws_default_security_group" "test" { + vpc_id = aws_vpc.test.id +} +`, rName)) } func testAccEc2ClientVpnNetworkAssociationVpcBase(rName string, subnetCount int) string { From 229e0da42916a9c1750b345cd31411cda17349bb Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Thu, 9 Jul 2020 17:25:09 -0700 Subject: [PATCH 10/14] Adds sweeper for Client VPN network association --- aws/internal/service/ec2/waiter/status.go | 33 ++++++++ aws/internal/service/ec2/waiter/waiter.go | 43 ++++++++++ ...source_aws_ec2_client_vpn_endpoint_test.go | 41 ++++----- ..._aws_ec2_client_vpn_network_association.go | 84 ++++++------------- ...ec2_client_vpn_network_association_test.go | 79 +++++++++++++++++ aws/resource_aws_workspaces_workspace_test.go | 4 +- 6 files changed, 204 insertions(+), 80 deletions(-) diff --git a/aws/internal/service/ec2/waiter/status.go b/aws/internal/service/ec2/waiter/status.go index 6a238c9370e..539edc36cd1 100644 --- a/aws/internal/service/ec2/waiter/status.go +++ b/aws/internal/service/ec2/waiter/status.go @@ -114,3 +114,36 @@ func ClientVpnAuthorizationRuleStatus(conn *ec2.EC2, authorizationRuleID string) return rule, aws.StringValue(rule.Status.Code), nil } } + +const ( + ClientVpnNetworkAssociationStatusNotFound = "NotFound" + + ClientVpnNetworkAssociationStatusUnknown = "Unknown" +) + +func ClientVpnNetworkAssociationStatus(conn *ec2.EC2, cvnaID string, cvepID string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + result, err := conn.DescribeClientVpnTargetNetworks(&ec2.DescribeClientVpnTargetNetworksInput{ + ClientVpnEndpointId: aws.String(cvepID), + AssociationIds: []*string{aws.String(cvnaID)}, + }) + + if tfec2.ErrCodeEquals(err, tfec2.ErrCodeClientVpnAssociationIdNotFound) || tfec2.ErrCodeEquals(err, tfec2.ErrCodeClientVpnEndpointIdNotFound) { + 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 + } + + network := result.ClientVpnTargetNetworks[0] + if network.Status == nil || network.Status.Code == nil { + return network, ClientVpnNetworkAssociationStatusUnknown, nil + } + + return network, aws.StringValue(network.Status.Code), nil + } +} diff --git a/aws/internal/service/ec2/waiter/waiter.go b/aws/internal/service/ec2/waiter/waiter.go index 531e33b8da1..045da3f6cff 100644 --- a/aws/internal/service/ec2/waiter/waiter.go +++ b/aws/internal/service/ec2/waiter/waiter.go @@ -111,3 +111,46 @@ func ClientVpnAuthorizationRuleRevoked(conn *ec2.EC2, authorizationRuleID string return nil, err } + +const ( + ClientVpnNetworkAssociationAssociatedTimeout = 5 * time.Minute + + ClientVpnNetworkAssociationDisassociatedTimeout = 5 * time.Minute + + ClientVpnNetworkAssociationDisassociatedMinTimeout = 2 * time.Minute +) + +func ClientVpnNetworkAssociationAssociated(conn *ec2.EC2, networkAssociationID, clientVpnEndpointID string) (*ec2.TargetNetwork, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ec2.AssociationStatusCodeAssociating}, + Target: []string{ec2.AssociationStatusCodeAssociated}, + Refresh: ClientVpnNetworkAssociationStatus(conn, networkAssociationID, clientVpnEndpointID), + Timeout: ClientVpnNetworkAssociationAssociatedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*ec2.TargetNetwork); ok { + return output, err + } + + return nil, err +} + +func ClientVpnNetworkAssociationDisassociated(conn *ec2.EC2, networkAssociationID, clientVpnEndpointID string) (*ec2.TargetNetwork, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ec2.AssociationStatusCodeDisassociating}, + Target: []string{}, + Refresh: ClientVpnNetworkAssociationStatus(conn, networkAssociationID, clientVpnEndpointID), + Timeout: ClientVpnNetworkAssociationDisassociatedTimeout, + MinTimeout: ClientVpnNetworkAssociationDisassociatedMinTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*ec2.TargetNetwork); ok { + return output, err + } + + return nil, err +} diff --git a/aws/resource_aws_ec2_client_vpn_endpoint_test.go b/aws/resource_aws_ec2_client_vpn_endpoint_test.go index 3fbdcbf8b6e..b00de16263f 100644 --- a/aws/resource_aws_ec2_client_vpn_endpoint_test.go +++ b/aws/resource_aws_ec2_client_vpn_endpoint_test.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" @@ -28,6 +29,7 @@ func init() { F: testSweepEc2ClientVpnEndpoints, Dependencies: []string{ "aws_directory_service_directory", + "aws_ec2_client_vpn_network_association", }, }) } @@ -40,37 +42,38 @@ func testSweepEc2ClientVpnEndpoints(region string) error { } conn := client.(*AWSClient).ec2conn - input := &ec2.DescribeClientVpnEndpointsInput{} - - for { - output, err := conn.DescribeClientVpnEndpoints(input) - if testSweepSkipSweepError(err) { - log.Printf("[WARN] Skipping Client VPN Endpoint sweep for %s: %s", region, err) - return nil - } + var sweeperErrs *multierror.Error - if err != nil { - return fmt.Errorf("error retrieving Client VPN Endpoints: %w", err) + input := &ec2.DescribeClientVpnEndpointsInput{} + err = conn.DescribeClientVpnEndpointsPages(input, func(page *ec2.DescribeClientVpnEndpointsOutput, isLast bool) bool { + if page == nil { + return !isLast } - for _, clientVpnEndpoint := range output.ClientVpnEndpoints { + for _, clientVpnEndpoint := range page.ClientVpnEndpoints { id := aws.StringValue(clientVpnEndpoint.ClientVpnEndpointId) - log.Printf("[INFO] Deleting Client VPN Endpoint: %s", id) + log.Printf("[INFO] Deleting Client VPN endpoint: %s", id) err := deleteClientVpnEndpoint(conn, id) if err != nil { - return fmt.Errorf("error deleting Client VPN Endpoint (%s): %w", id, err) + sweeperErr := fmt.Errorf("error deleting Client VPN endpoint (%s): %w", id, err) + log.Printf("[ERROR] %s", sweeperErr) + sweeperErrs = multierror.Append(sweeperErrs, sweeperErr) + continue } } - if aws.StringValue(output.NextToken) == "" { - break - } - - input.NextToken = output.NextToken + return !isLast + }) + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping Client VPN endpoint 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 retrieving Client VPN endpoints: %w", err)) } - return nil + return sweeperErrs.ErrorOrNil() } // This is part of an experimental feature, do not use this as a starting point for tests diff --git a/aws/resource_aws_ec2_client_vpn_network_association.go b/aws/resource_aws_ec2_client_vpn_network_association.go index 8869300938e..9414e96f4c2 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association.go +++ b/aws/resource_aws_ec2_client_vpn_network_association.go @@ -6,9 +6,9 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2/waiter" ) func resourceAwsEc2ClientVpnNetworkAssociation() *schema.Resource { @@ -63,23 +63,14 @@ func resourceAwsEc2ClientVpnNetworkAssociationCreate(d *schema.ResourceData, met return fmt.Errorf("Error creating Client VPN network association: %w", err) } - d.SetId(*resp.AssociationId) - - stateConf := &resource.StateChangeConf{ - Pending: []string{ec2.AssociationStatusCodeAssociating}, - Target: []string{ec2.AssociationStatusCodeAssociated}, - Refresh: clientVpnNetworkAssociationRefreshFunc(conn, d.Id(), d.Get("client_vpn_endpoint_id").(string)), - Timeout: d.Timeout(schema.TimeoutCreate), - } + d.SetId(aws.StringValue(resp.AssociationId)) log.Printf("[DEBUG] Waiting for Client VPN endpoint to associate with target network: %s", d.Id()) - targetNetworkRaw, err := stateConf.WaitForState() + targetNetwork, err := waiter.ClientVpnNetworkAssociationAssociated(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) + return fmt.Errorf("error waiting for Client VPN endpoint to associate with target network: %w", err) } - targetNetwork := targetNetworkRaw.(*ec2.TargetNetwork) - if v, ok := d.GetOk("security_groups"); ok { sgReq := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), @@ -139,18 +130,19 @@ func resourceAwsEc2ClientVpnNetworkAssociationRead(d *schema.ResourceData, meta return nil } - if result.ClientVpnTargetNetworks[0].Status != nil && aws.StringValue(result.ClientVpnTargetNetworks[0].Status.Code) == ec2.AssociationStatusCodeDisassociated { + 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 } - d.Set("client_vpn_endpoint_id", result.ClientVpnTargetNetworks[0].ClientVpnEndpointId) - d.Set("status", result.ClientVpnTargetNetworks[0].Status.Code) - d.Set("subnet_id", result.ClientVpnTargetNetworks[0].TargetNetworkId) - d.Set("vpc_id", result.ClientVpnTargetNetworks[0].VpcId) + d.Set("client_vpn_endpoint_id", network.ClientVpnEndpointId) + 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(result.ClientVpnTargetNetworks[0].SecurityGroups)); err != nil { + if err := d.Set("security_groups", aws.StringValueSlice(network.SecurityGroups)); err != nil { return fmt.Errorf("error setting security_groups: %w", err) } @@ -160,54 +152,28 @@ func resourceAwsEc2ClientVpnNetworkAssociationRead(d *schema.ResourceData, meta func resourceAwsEc2ClientVpnNetworkAssociationDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*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 +} + +func deleteClientVpnNetworkAssociation(conn *ec2.EC2, networkAssociationID, clientVpnEndpointID string) error { _, err := conn.DisassociateClientVpnTargetNetwork(&ec2.DisassociateClientVpnTargetNetworkInput{ - ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), - AssociationId: aws.String(d.Id()), + ClientVpnEndpointId: aws.String(clientVpnEndpointID), + AssociationId: aws.String(networkAssociationID), }) if isAWSErr(err, tfec2.ErrCodeClientVpnAssociationIdNotFound, "") || isAWSErr(err, tfec2.ErrCodeClientVpnEndpointIdNotFound, "") { return nil } - if err != nil { - return fmt.Errorf("Error deleting Client VPN network association: %w", err) + return err } - stateConf := &resource.StateChangeConf{ - Pending: []string{ec2.AssociationStatusCodeDisassociating}, - Target: []string{ec2.AssociationStatusCodeDisassociated}, - Refresh: clientVpnNetworkAssociationRefreshFunc(conn, d.Id(), d.Get("client_vpn_endpoint_id").(string)), - Timeout: d.Timeout(schema.TimeoutDelete), - } + _, err = waiter.ClientVpnNetworkAssociationDisassociated(conn, networkAssociationID, clientVpnEndpointID) - log.Printf("[DEBUG] Waiting for Client VPN endpoint to disassociate with target network: %s", d.Id()) - _, err = stateConf.WaitForState() - if err != nil { - return fmt.Errorf("Error waiting for Client VPN endpoint to disassociate with target network: %w", err) - } - - return nil -} - -func clientVpnNetworkAssociationRefreshFunc(conn *ec2.EC2, cvnaID string, cvepID string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - resp, err := conn.DescribeClientVpnTargetNetworks(&ec2.DescribeClientVpnTargetNetworksInput{ - ClientVpnEndpointId: aws.String(cvepID), - AssociationIds: []*string{aws.String(cvnaID)}, - }) - - if isAWSErr(err, tfec2.ErrCodeClientVpnAssociationIdNotFound, "") || isAWSErr(err, tfec2.ErrCodeClientVpnEndpointIdNotFound, "") { - return 42, ec2.AssociationStatusCodeDisassociated, nil - } - - if err != nil { - return nil, "", err - } - - if resp == nil || len(resp.ClientVpnTargetNetworks) == 0 || resp.ClientVpnTargetNetworks[0] == nil { - return 42, ec2.AssociationStatusCodeDisassociated, nil - } - - return resp.ClientVpnTargetNetworks[0], aws.StringValue(resp.ClientVpnTargetNetworks[0].Status.Code), nil - } + return err } diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index 2a3a860f3ed..f83183045f2 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -2,16 +2,95 @@ package aws import ( "fmt" + "log" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfawsresource" ) +func init() { + resource.AddTestSweepers("aws_ec2_client_vpn_network_association", &resource.Sweeper{ + Name: "aws_ec2_client_vpn_network_association", + F: testSweepEc2ClientVpnNetworkAssociations, + Dependencies: []string{ + "aws_directory_service_directory", + }, + }) +} + +func testSweepEc2ClientVpnNetworkAssociations(region string) error { + client, err := sharedClientForRegion(region) + + if err != nil { + return fmt.Errorf("error getting client: %w", err) + } + + conn := client.(*AWSClient).ec2conn + + var sweeperErrs *multierror.Error + + input := &ec2.DescribeClientVpnEndpointsInput{} + err = conn.DescribeClientVpnEndpointsPages(input, func(page *ec2.DescribeClientVpnEndpointsOutput, isLast bool) bool { + if page == nil { + return !isLast + } + + for _, clientVpnEndpoint := range page.ClientVpnEndpoints { + + input := &ec2.DescribeClientVpnTargetNetworksInput{ + ClientVpnEndpointId: clientVpnEndpoint.ClientVpnEndpointId, + } + err := conn.DescribeClientVpnTargetNetworksPages(input, func(page *ec2.DescribeClientVpnTargetNetworksOutput, isLast bool) bool { + if page == nil { + return !isLast + } + + 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) + + 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) + } + } + + return !isLast + }) + + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping Client VPN network association sweeper for %q: %s", region, err) + return false + } + if err != nil { + sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Client VPN network associations: %w", err)) + return false + } + } + + return !isLast + }) + if testSweepSkipSweepError(err) { + log.Printf("[WARN] Skipping 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 retrieving Client VPN network associations: %w", err)) + } + + return sweeperErrs.ErrorOrNil() +} + func testAccAwsEc2ClientVpnNetworkAssociation_basic(t *testing.T) { var assoc ec2.TargetNetwork var group ec2.SecurityGroup diff --git a/aws/resource_aws_workspaces_workspace_test.go b/aws/resource_aws_workspaces_workspace_test.go index ca41d084cb3..3266c39de07 100644 --- a/aws/resource_aws_workspaces_workspace_test.go +++ b/aws/resource_aws_workspaces_workspace_test.go @@ -16,8 +16,8 @@ import ( ) func init() { - resource.AddTestSweepers("aws_workspace", &resource.Sweeper{ - Name: "aws_workspace", + resource.AddTestSweepers("aws_workspaces_workspace", &resource.Sweeper{ + Name: "aws_workspaces_workspace", F: testSweepWorkspaces, }) } From be3557aa0d9668e0ae5509229b0a3aa600516eea Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 10 Jul 2020 12:50:00 -0700 Subject: [PATCH 11/14] Adds test for custom security groups --- aws/internal/service/ec2/waiter/waiter.go | 31 ++- ..._aws_ec2_client_vpn_network_association.go | 1 + ...ec2_client_vpn_network_association_test.go | 227 ++++++------------ ...ient_vpn_network_association.html.markdown | 2 +- 4 files changed, 97 insertions(+), 164 deletions(-) diff --git a/aws/internal/service/ec2/waiter/waiter.go b/aws/internal/service/ec2/waiter/waiter.go index 045da3f6cff..3d39245768c 100644 --- a/aws/internal/service/ec2/waiter/waiter.go +++ b/aws/internal/service/ec2/waiter/waiter.go @@ -113,19 +113,25 @@ func ClientVpnAuthorizationRuleRevoked(conn *ec2.EC2, authorizationRuleID string } const ( - ClientVpnNetworkAssociationAssociatedTimeout = 5 * time.Minute + ClientVpnNetworkAssociationAssociatedTimeout = 10 * time.Minute - ClientVpnNetworkAssociationDisassociatedTimeout = 5 * time.Minute + ClientVpnNetworkAssociationAssociatedDelay = 4 * time.Minute - ClientVpnNetworkAssociationDisassociatedMinTimeout = 2 * time.Minute + ClientVpnNetworkAssociationDisassociatedTimeout = 10 * time.Minute + + ClientVpnNetworkAssociationDisassociatedDelay = 4 * time.Minute + + ClientVpnNetworkAssociationStatusPollInterval = 10 * time.Second ) func ClientVpnNetworkAssociationAssociated(conn *ec2.EC2, networkAssociationID, clientVpnEndpointID string) (*ec2.TargetNetwork, error) { stateConf := &resource.StateChangeConf{ - Pending: []string{ec2.AssociationStatusCodeAssociating}, - Target: []string{ec2.AssociationStatusCodeAssociated}, - Refresh: ClientVpnNetworkAssociationStatus(conn, networkAssociationID, clientVpnEndpointID), - Timeout: ClientVpnNetworkAssociationAssociatedTimeout, + Pending: []string{ec2.AssociationStatusCodeAssociating}, + Target: []string{ec2.AssociationStatusCodeAssociated}, + Refresh: ClientVpnNetworkAssociationStatus(conn, networkAssociationID, clientVpnEndpointID), + Timeout: ClientVpnNetworkAssociationAssociatedTimeout, + Delay: ClientVpnNetworkAssociationAssociatedDelay, + PollInterval: ClientVpnNetworkAssociationStatusPollInterval, } outputRaw, err := stateConf.WaitForState() @@ -139,11 +145,12 @@ func ClientVpnNetworkAssociationAssociated(conn *ec2.EC2, networkAssociationID, func ClientVpnNetworkAssociationDisassociated(conn *ec2.EC2, networkAssociationID, clientVpnEndpointID string) (*ec2.TargetNetwork, error) { stateConf := &resource.StateChangeConf{ - Pending: []string{ec2.AssociationStatusCodeDisassociating}, - Target: []string{}, - Refresh: ClientVpnNetworkAssociationStatus(conn, networkAssociationID, clientVpnEndpointID), - Timeout: ClientVpnNetworkAssociationDisassociatedTimeout, - MinTimeout: ClientVpnNetworkAssociationDisassociatedMinTimeout, + Pending: []string{ec2.AssociationStatusCodeDisassociating}, + Target: []string{}, + Refresh: ClientVpnNetworkAssociationStatus(conn, networkAssociationID, clientVpnEndpointID), + Timeout: ClientVpnNetworkAssociationDisassociatedTimeout, + Delay: ClientVpnNetworkAssociationDisassociatedDelay, + PollInterval: ClientVpnNetworkAssociationStatusPollInterval, } outputRaw, err := stateConf.WaitForState() diff --git a/aws/resource_aws_ec2_client_vpn_network_association.go b/aws/resource_aws_ec2_client_vpn_network_association.go index 9414e96f4c2..ee6cf24c51b 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association.go +++ b/aws/resource_aws_ec2_client_vpn_network_association.go @@ -31,6 +31,7 @@ func resourceAwsEc2ClientVpnNetworkAssociation() *schema.Resource { }, "security_groups": { Type: schema.TypeSet, + MinItems: 1, MaxItems: 5, Optional: true, Computed: true, diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index f83183045f2..76313aa5a3a 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -146,8 +146,11 @@ func testAccAwsEc2ClientVpnNetworkAssociation_disappears(t *testing.T) { func testAccAwsEc2ClientVpnNetworkAssociation_securityGroups(t *testing.T) { var assoc1, assoc2 ec2.TargetNetwork + var group11, group12, group21 ec2.SecurityGroup rStr := acctest.RandString(5) resourceName := "aws_ec2_client_vpn_network_association.test" + securityGroup1ResourceName := "aws_security_group.test1" + securityGroup2ResourceName := "aws_security_group.test2" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPreCheckClientVPNSyncronize(t) }, @@ -158,14 +161,20 @@ func testAccAwsEc2ClientVpnNetworkAssociation_securityGroups(t *testing.T) { Config: testAccEc2ClientVpnNetworkAssociationTwoSecurityGroups(rStr), Check: resource.ComposeTestCheckFunc( testAccCheckAwsEc2ClientVpnNetworkAssociationExists(resourceName, &assoc1), + testAccCheckAWSDefaultSecurityGroupExists(securityGroup1ResourceName, &group11), + testAccCheckAWSDefaultSecurityGroupExists(securityGroup2ResourceName, &group12), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "2"), + testAccCheckAwsEc2ClientVpnNetworkAssociationSecurityGroupID(resourceName, "security_groups.*", &group11), + testAccCheckAwsEc2ClientVpnNetworkAssociationSecurityGroupID(resourceName, "security_groups.*", &group12), ), }, { Config: testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rStr), Check: resource.ComposeTestCheckFunc( testAccCheckAwsEc2ClientVpnNetworkAssociationExists(resourceName, &assoc2), + testAccCheckAWSDefaultSecurityGroupExists(securityGroup1ResourceName, &group21), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + testAccCheckAwsEc2ClientVpnNetworkAssociationSecurityGroupID(resourceName, "security_groups.*", &group21), ), }, }, @@ -260,115 +269,68 @@ resource "aws_ec2_client_vpn_endpoint" "test" { enabled = false } } - -resource "aws_default_security_group" "test" { - vpc_id = aws_vpc.test.id -} `, rName)) } -func testAccEc2ClientVpnNetworkAssociationVpcBase(rName string, subnetCount int) string { - return fmt.Sprintf(` -data "aws_availability_zones" "available" { - # InvalidParameterValue: AZ us-west-2d is not currently supported. Please choose another az in this region - exclude_zone_ids = ["usw2-az4"] - state = "available" - - filter { - name = "opt-in-status" - values = ["opt-in-not-required"] - } -} - -resource "aws_vpc" "test" { - cidr_block = "10.1.0.0/16" - - tags = { - Name = "terraform-testacc-subnet-%[1]s" - } -} - -resource "aws_subnet" "test" { - count = %[2]d - availability_zone = data.aws_availability_zones.available.names[count.index] - cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, count.index) - vpc_id = aws_vpc.test.id - map_public_ip_on_launch = true - - tags = { - Name = "tf-acc-subnet-%[1]s" - } -} -`, rName, subnetCount) -} - func testAccEc2ClientVpnNetworkAssociationTwoSecurityGroups(rName string) string { return composeConfig( testAccEc2ClientVpnNetworkAssociationVpcBase(rName, 1), testAccEc2ClientVpnNetworkAssociationAcmCertificateBase(), fmt.Sprintf(` -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_ec2_client_vpn_network_association" "test" { + client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id + subnet_id = aws_subnet.test[0].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" - ingress { - protocol = "tcp" - from_port = 22 - to_port = 22 - cidr_blocks = ["10.1.1.0/24"] + authentication_options { + type = "certificate-authentication" + root_certificate_chain_arn = aws_acm_certificate.test.arn } - ingress { - protocol = "tcp" - from_port = 80 - to_port = 80 - cidr_blocks = ["10.1.1.0/24"] + connection_log_options { + enabled = false } +} - egress { - protocol = "-1" - from_port = 0 - to_port = 0 - cidr_blocks = ["10.1.0.0/16"] - } +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}" - - ingress { - protocol = "tcp" - from_port = 22 - to_port = 22 - cidr_blocks = ["10.1.2.0/24"] - } - - ingress { - protocol = "tcp" - from_port = 80 - to_port = 80 - cidr_blocks = ["10.1.2.0/24"] - } + vpc_id = aws_vpc.test.id +} +`, rName)) +} - egress { - protocol = "-1" - from_port = 0 - to_port = 0 - cidr_blocks = ["10.1.0.0/16"] - } +func testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rName string) string { + return composeConfig( + testAccEc2ClientVpnNetworkAssociationVpcBase(rName, 1), + testAccEc2ClientVpnNetworkAssociationAcmCertificateBase(), + 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[0].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}" + 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}" + root_certificate_chain_arn = aws_acm_certificate.test.arn } connection_log_options { @@ -376,94 +338,57 @@ resource "aws_ec2_client_vpn_endpoint" "test" { } } -resource "aws_ec2_client_vpn_network_association" "test" { - client_vpn_endpoint_id = "${aws_ec2_client_vpn_endpoint.test.id}" - subnet_id = "${aws_subnet.test[0].id}" - security_groups = ["${aws_security_group.test1.id}", "${aws_security_group.test2.id}"] -} -`, rName)) -} - -func testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rName string) string { - return composeConfig( - testAccEc2ClientVpnNetworkAssociationVpcBase(rName, 1), - testAccEc2ClientVpnNetworkAssociationAcmCertificateBase(), - fmt.Sprintf(` resource "aws_security_group" "test1" { name = "terraform_acceptance_test_example_1" description = "Used in the terraform acceptance tests" - vpc_id = "${aws_vpc.test.id}" - - ingress { - protocol = "tcp" - from_port = 22 - to_port = 22 - cidr_blocks = ["10.1.1.0/24"] - } - - ingress { - protocol = "tcp" - from_port = 80 - to_port = 80 - cidr_blocks = ["10.1.1.0/24"] - } - - egress { - protocol = "-1" - from_port = 0 - to_port = 0 - cidr_blocks = ["10.1.0.0/16"] - } + 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}" - - ingress { - protocol = "tcp" - from_port = 22 - to_port = 22 - cidr_blocks = ["10.1.2.0/24"] - } + vpc_id = aws_vpc.test.id +} +`, rName)) +} - ingress { - protocol = "tcp" - from_port = 80 - to_port = 80 - cidr_blocks = ["10.1.2.0/24"] - } +func testAccEc2ClientVpnNetworkAssociationVpcBase(rName string, subnetCount int) string { + return fmt.Sprintf(` +data "aws_availability_zones" "available" { + # InvalidParameterValue: AZ us-west-2d is not currently supported. Please choose another az in this region + exclude_zone_ids = ["usw2-az4"] + state = "available" - egress { - protocol = "-1" - from_port = 0 - to_port = 0 - cidr_blocks = ["10.1.0.0/16"] + filter { + name = "opt-in-status" + values = ["opt-in-not-required"] } } -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_vpc" "test" { + cidr_block = "10.1.0.0/16" - authentication_options { - type = "certificate-authentication" - root_certificate_chain_arn = "${aws_acm_certificate.test.arn}" + tags = { + Name = "terraform-testacc-subnet-%[1]s" } +} - connection_log_options { - enabled = false - } +resource "aws_default_security_group" "test" { + vpc_id = aws_vpc.test.id } -resource "aws_ec2_client_vpn_network_association" "test" { - client_vpn_endpoint_id = "${aws_ec2_client_vpn_endpoint.test.id}" - subnet_id = "${aws_subnet.test[0].id}" - security_groups = ["${aws_security_group.test1.id}"] +resource "aws_subnet" "test" { + count = %[2]d + availability_zone = data.aws_availability_zones.available.names[count.index] + cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, count.index) + vpc_id = aws_vpc.test.id + map_public_ip_on_launch = true + + tags = { + Name = "tf-acc-subnet-%[1]s" + } } -`, rName)) +`, rName, subnetCount) } func testAccEc2ClientVpnNetworkAssociationAcmCertificateBase() string { 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 641deb205c1..ffa8a031cd3 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -39,7 +39,7 @@ The following arguments are supported: * `client_vpn_endpoint_id` - (Required) The ID of the Client VPN endpoint. * `subnet_id` - (Required) The ID of the subnet to associate with the Client VPN endpoint. -* `security_groups` - (Required) A list of up to five custom security groups to apply to the target network. Not populating this parameter will result in the subnet inheriting the default VPC security group. Regardless of what you choose, you must define this parameter due to the AWS API automatically assigning the default VPC security group if no other groups are included in the call. +* `security_groups` - (Optional) A list of up to five custom security groups to apply to the target network. If not specified, the VPC's default security group is assigned. ## Attributes Reference From 347f53378363cd7f72b9a00646fc24d7eabee1c9 Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 10 Jul 2020 12:52:42 -0700 Subject: [PATCH 12/14] Adds `association_id` parameter --- aws/resource_aws_ec2_client_vpn_network_association.go | 5 +++++ aws/resource_aws_ec2_client_vpn_network_association_test.go | 5 ++++- .../docs/r/ec2_client_vpn_network_association.html.markdown | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_ec2_client_vpn_network_association.go b/aws/resource_aws_ec2_client_vpn_network_association.go index ee6cf24c51b..5b3e6ab1640 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association.go +++ b/aws/resource_aws_ec2_client_vpn_network_association.go @@ -19,6 +19,10 @@ func resourceAwsEc2ClientVpnNetworkAssociation() *schema.Resource { Delete: resourceAwsEc2ClientVpnNetworkAssociationDelete, Schema: map[string]*schema.Schema{ + "association_id": { + Type: schema.TypeString, + Computed: true, + }, "client_vpn_endpoint_id": { Type: schema.TypeString, Required: true, @@ -139,6 +143,7 @@ func resourceAwsEc2ClientVpnNetworkAssociationRead(d *schema.ResourceData, meta } d.Set("client_vpn_endpoint_id", network.ClientVpnEndpointId) + d.Set("association_id", network.AssociationId) d.Set("status", network.Status.Code) d.Set("subnet_id", network.TargetNetworkId) d.Set("vpc_id", network.VpcId) diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index 76313aa5a3a..31eefa2dc4a 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "log" + "regexp" "testing" "github.com/aws/aws-sdk-go/aws" @@ -110,9 +111,11 @@ func testAccAwsEc2ClientVpnNetworkAssociation_basic(t *testing.T) { Config: testAccEc2ClientVpnNetworkAssociationConfigBasic(rStr), Check: resource.ComposeTestCheckFunc( testAccCheckAwsEc2ClientVpnNetworkAssociationExists(resourceName, &assoc), - testAccCheckAWSDefaultSecurityGroupExists(defaultSecurityGroupResourceName, &group), + resource.TestMatchResourceAttr(resourceName, "association_id", regexp.MustCompile("^cvpn-assoc-[a-z0-9]+$")), + resource.TestCheckResourceAttrPair(resourceName, "id", resourceName, "association_id"), resource.TestCheckResourceAttrPair(resourceName, "client_vpn_endpoint_id", endpointResourceName, "id"), resource.TestCheckResourceAttrPair(resourceName, "subnet_id", subnetResourceName, "id"), + testAccCheckAWSDefaultSecurityGroupExists(defaultSecurityGroupResourceName, &group), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), testAccCheckAwsEc2ClientVpnNetworkAssociationSecurityGroupID(resourceName, "security_groups.*", &group), resource.TestCheckResourceAttrPair(resourceName, "vpc_id", vpcResourceName, "id"), 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 ffa8a031cd3..82b15e68b5c 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -46,6 +46,7 @@ The following arguments are supported: In addition to all arguments above, the following attributes are exported: * `id` - The unique ID of the target network association. +* `association_id` - The unique ID of the target network association. * `security_groups` - The IDs of the security groups applied to the target network association. * `vpc_id` - The ID of the VPC in which the target network (subnet) is located. * `status` - The current state of the target network association. From 21af6d8a35893e45788166fe400485e25efb042d Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 10 Jul 2020 14:15:29 -0700 Subject: [PATCH 13/14] Adds import --- aws/internal/service/ec2/id.go | 19 +++++++++++++++ ..._aws_ec2_client_vpn_network_association.go | 14 +++++++++++ ...ec2_client_vpn_network_association_test.go | 24 +++++++++++++++++++ ...ient_vpn_network_association.html.markdown | 10 +++++++- 4 files changed, 66 insertions(+), 1 deletion(-) diff --git a/aws/internal/service/ec2/id.go b/aws/internal/service/ec2/id.go index ada06090f63..24e8d761cf7 100644 --- a/aws/internal/service/ec2/id.go +++ b/aws/internal/service/ec2/id.go @@ -30,3 +30,22 @@ func ClientVpnAuthorizationRuleParseID(id string) (string, string, string, error "target-network-cidr or endpoint-id"+clientVpnAuthorizationRuleIDSeparator+"target-network-cidr"+ clientVpnAuthorizationRuleIDSeparator+"group-id", id) } + +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 (%q), expected endpoint-id"+clientVpnNetworkAssociationIDSeparator+ + "association-id", id) +} diff --git a/aws/resource_aws_ec2_client_vpn_network_association.go b/aws/resource_aws_ec2_client_vpn_network_association.go index 5b3e6ab1640..862380aafc4 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association.go +++ b/aws/resource_aws_ec2_client_vpn_network_association.go @@ -17,6 +17,9 @@ func resourceAwsEc2ClientVpnNetworkAssociation() *schema.Resource { Read: resourceAwsEc2ClientVpnNetworkAssociationRead, Update: resourceAwsEc2ClientVpnNetworkAssociationUpdate, Delete: resourceAwsEc2ClientVpnNetworkAssociationDelete, + Importer: &schema.ResourceImporter{ + State: resourceAwsEc2ClientVpnNetworkAssociationImport, + }, Schema: map[string]*schema.Schema{ "association_id": { @@ -183,3 +186,14 @@ func deleteClientVpnNetworkAssociation(conn *ec2.EC2, networkAssociationID, clie return err } + +func resourceAwsEc2ClientVpnNetworkAssociationImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + endpointID, associationID, err := tfec2.ClientVpnNetworkAssociationParseID(d.Id()) + if err != nil { + return nil, err + } + + d.SetId(associationID) + d.Set("client_vpn_endpoint_id", endpointID) + return []*schema.ResourceData{d}, nil +} diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index 31eefa2dc4a..d20d801f323 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" + tfec2 "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/ec2" "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfawsresource" ) @@ -121,6 +122,12 @@ func testAccAwsEc2ClientVpnNetworkAssociation_basic(t *testing.T) { resource.TestCheckResourceAttrPair(resourceName, "vpc_id", vpcResourceName, "id"), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsEc2ClientVpnNetworkAssociationImportStateIdFunc(resourceName), + }, }, }) } @@ -171,6 +178,12 @@ func testAccAwsEc2ClientVpnNetworkAssociation_securityGroups(t *testing.T) { testAccCheckAwsEc2ClientVpnNetworkAssociationSecurityGroupID(resourceName, "security_groups.*", &group12), ), }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccAwsEc2ClientVpnNetworkAssociationImportStateIdFunc(resourceName), + }, { Config: testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rStr), Check: resource.ComposeTestCheckFunc( @@ -248,6 +261,17 @@ func testAccCheckAwsEc2ClientVpnNetworkAssociationSecurityGroupID(name, key stri } } +func testAccAwsEc2ClientVpnNetworkAssociationImportStateIdFunc(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not found: %s", resourceName) + } + + return tfec2.ClientVpnNetworkAssociationCreateID(rs.Primary.Attributes["client_vpn_endpoint_id"], rs.Primary.ID), nil + } +} + func testAccEc2ClientVpnNetworkAssociationConfigBasic(rName string) string { return composeConfig( testAccEc2ClientVpnNetworkAssociationVpcBase(rName, 1), 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 82b15e68b5c..512f958217c 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -48,5 +48,13 @@ In addition to all arguments above, the following attributes are exported: * `id` - The unique ID of the target network association. * `association_id` - The unique ID of the target network association. * `security_groups` - The IDs of the security groups applied to the target network association. -* `vpc_id` - The ID of the VPC in which the target network (subnet) is located. * `status` - The current state of the target network association. +* `vpc_id` - The ID of the VPC in which the target subnet is located. + +## Import + +AWS Client VPN network associations can be imported using the endpoint ID and the association ID. Values are separated by a `,`. + +``` +$ terraform import aws_ec2_client_vpn_authorization_rule.example cvpn-endpoint-0ac3a1abbccddd666,vpn-assoc-0b8db902465d069ad +``` From b725908891b129619a6f953cac77046ec2164ced Mon Sep 17 00:00:00 2001 From: Graham Davison Date: Fri, 14 Aug 2020 16:22:47 -0700 Subject: [PATCH 14/14] Acceptance test cleanup --- ..._ec2_client_vpn_authorization_rule_test.go | 4 ++-- ...source_aws_ec2_client_vpn_endpoint_test.go | 21 ++++++++----------- ...ec2_client_vpn_network_association_test.go | 21 +++++++++---------- aws/resource_aws_ec2_client_vpn_route_test.go | 4 ++-- 4 files changed, 23 insertions(+), 27 deletions(-) diff --git a/aws/resource_aws_ec2_client_vpn_authorization_rule_test.go b/aws/resource_aws_ec2_client_vpn_authorization_rule_test.go index e48048036db..0ddb922fd15 100644 --- a/aws/resource_aws_ec2_client_vpn_authorization_rule_test.go +++ b/aws/resource_aws_ec2_client_vpn_authorization_rule_test.go @@ -17,7 +17,7 @@ func testAccAwsEc2ClientVpnAuthorizationRule_basic(t *testing.T) { var v ec2.AuthorizationRule rStr := acctest.RandString(5) resourceName := "aws_ec2_client_vpn_authorization_rule.test" - subnetResourceName := "aws_subnet.test" + subnetResourceName := "aws_subnet.test.0" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheckClientVPNSyncronize(t); testAccPreCheck(t) }, @@ -47,7 +47,7 @@ func testAccAwsEc2ClientVpnAuthorizationRule_groups(t *testing.T) { rStr := acctest.RandString(5) resource1Name := "aws_ec2_client_vpn_authorization_rule.test1" resource2Name := "aws_ec2_client_vpn_authorization_rule.test2" - subnetResourceName := "aws_subnet.test" + subnetResourceName := "aws_subnet.test.0" group1Name := "group_one" group2Name := "group_two" diff --git a/aws/resource_aws_ec2_client_vpn_endpoint_test.go b/aws/resource_aws_ec2_client_vpn_endpoint_test.go index a825a857f9e..ddeb7c22f54 100644 --- a/aws/resource_aws_ec2_client_vpn_endpoint_test.go +++ b/aws/resource_aws_ec2_client_vpn_endpoint_test.go @@ -238,6 +238,8 @@ func testAccAwsEc2ClientVpnEndpoint_withLogGroup(t *testing.T) { var v1, v2 ec2.ClientVpnEndpoint rStr := acctest.RandString(5) resourceName := "aws_ec2_client_vpn_endpoint.test" + logGroupResourceName := "aws_cloudwatch_log_group.lg" + logStreamResourceName := "aws_cloudwatch_log_stream.ls" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheckClientVPNSyncronize(t); testAccPreCheck(t) }, @@ -256,8 +258,8 @@ func testAccAwsEc2ClientVpnEndpoint_withLogGroup(t *testing.T) { testAccCheckAwsEc2ClientVpnEndpointExists(resourceName, &v2), resource.TestCheckResourceAttr(resourceName, "connection_log_options.#", "1"), resource.TestCheckResourceAttr(resourceName, "connection_log_options.0.enabled", "true"), - resource.TestCheckResourceAttrSet(resourceName, "connection_log_options.0.cloudwatch_log_group"), - resource.TestCheckResourceAttrSet(resourceName, "connection_log_options.0.cloudwatch_log_stream"), + resource.TestCheckResourceAttrPair(resourceName, "connection_log_options.0.cloudwatch_log_group", logGroupResourceName, "name"), + resource.TestCheckResourceAttrPair(resourceName, "connection_log_options.0.cloudwatch_log_stream", logStreamResourceName, "name"), ), }, { @@ -450,16 +452,11 @@ resource "aws_vpc" "test" { cidr_block = "10.0.0.0/16" } -resource "aws_subnet" "test1" { +resource "aws_subnet" "test" { + count = 2 + availability_zone = data.aws_availability_zones.available.names[count.index] + cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, count.index) vpc_id = aws_vpc.test.id - cidr_block = "10.0.1.0/24" - availability_zone = data.aws_availability_zones.available.names[0] -} - -resource "aws_subnet" "test2" { - vpc_id = aws_vpc.test.id - cidr_block = "10.0.2.0/24" - availability_zone = data.aws_availability_zones.available.names[1] } resource "aws_directory_service_directory" "test" { @@ -469,7 +466,7 @@ resource "aws_directory_service_directory" "test" { vpc_settings { vpc_id = aws_vpc.test.id - subnet_ids = [aws_subnet.test1.id, aws_subnet.test2.id] + subnet_ids = aws_subnet.test[*].id } } ` diff --git a/aws/resource_aws_ec2_client_vpn_network_association_test.go b/aws/resource_aws_ec2_client_vpn_network_association_test.go index 6bdc0f91de8..06a94e22781 100644 --- a/aws/resource_aws_ec2_client_vpn_network_association_test.go +++ b/aws/resource_aws_ec2_client_vpn_network_association_test.go @@ -272,12 +272,12 @@ func testAccAwsEc2ClientVpnNetworkAssociationImportStateIdFunc(resourceName stri func testAccEc2ClientVpnNetworkAssociationConfigBasic(rName string) string { return composeConfig( - testAccEc2ClientVpnNetworkAssociationVpcBase(rName, 1), + testAccEc2ClientVpnNetworkAssociationVpcBase(rName), testAccEc2ClientVpnNetworkAssociationAcmCertificateBase(), 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[0].id + subnet_id = aws_subnet.test.id } resource "aws_ec2_client_vpn_endpoint" "test" { @@ -299,12 +299,12 @@ resource "aws_ec2_client_vpn_endpoint" "test" { func testAccEc2ClientVpnNetworkAssociationTwoSecurityGroups(rName string) string { return composeConfig( - testAccEc2ClientVpnNetworkAssociationVpcBase(rName, 1), + testAccEc2ClientVpnNetworkAssociationVpcBase(rName), testAccEc2ClientVpnNetworkAssociationAcmCertificateBase(), 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[0].id + subnet_id = aws_subnet.test.id security_groups = [aws_security_group.test1.id, aws_security_group.test2.id] } @@ -339,12 +339,12 @@ resource "aws_security_group" "test2" { func testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rName string) string { return composeConfig( - testAccEc2ClientVpnNetworkAssociationVpcBase(rName, 1), + testAccEc2ClientVpnNetworkAssociationVpcBase(rName), testAccEc2ClientVpnNetworkAssociationAcmCertificateBase(), 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[0].id + subnet_id = aws_subnet.test.id security_groups = [aws_security_group.test1.id] } @@ -377,7 +377,7 @@ resource "aws_security_group" "test2" { `, rName)) } -func testAccEc2ClientVpnNetworkAssociationVpcBase(rName string, subnetCount int) string { +func testAccEc2ClientVpnNetworkAssociationVpcBase(rName string) string { return fmt.Sprintf(` data "aws_availability_zones" "available" { # InvalidParameterValue: AZ us-west-2d is not currently supported. Please choose another az in this region @@ -403,9 +403,8 @@ resource "aws_default_security_group" "test" { } resource "aws_subnet" "test" { - count = %[2]d - availability_zone = data.aws_availability_zones.available.names[count.index] - cidr_block = cidrsubnet(aws_vpc.test.cidr_block, 8, count.index) + 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 @@ -413,7 +412,7 @@ resource "aws_subnet" "test" { Name = "tf-acc-subnet-%[1]s" } } -`, rName, subnetCount) +`, rName) } func testAccEc2ClientVpnNetworkAssociationAcmCertificateBase() string { diff --git a/aws/resource_aws_ec2_client_vpn_route_test.go b/aws/resource_aws_ec2_client_vpn_route_test.go index e7a1b263736..d5858a77e68 100644 --- a/aws/resource_aws_ec2_client_vpn_route_test.go +++ b/aws/resource_aws_ec2_client_vpn_route_test.go @@ -18,7 +18,7 @@ func testAccAwsEc2ClientVpnRoute_basic(t *testing.T) { resourceName := "aws_ec2_client_vpn_route.test" endpointResourceName := "aws_ec2_client_vpn_endpoint.test" - subnetResourceName := "aws_subnet.test" + subnetResourceName := "aws_subnet.test.0" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheckClientVPNSyncronize(t); testAccPreCheck(t) }, @@ -52,7 +52,7 @@ func testAccAwsEc2ClientVpnRoute_description(t *testing.T) { resourceName := "aws_ec2_client_vpn_route.test" endpointResourceName := "aws_ec2_client_vpn_endpoint.test" - subnetResourceName := "aws_subnet.test" + subnetResourceName := "aws_subnet.test.0" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheckClientVPNSyncronize(t); testAccPreCheck(t) },