From 5c8cba57de1d1973e38756db003b6a1953a027d1 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 09:18:02 -0500 Subject: [PATCH 01/31] Add 'testAccClientVPNNetworkAssociation_multipleSubnetsWithSecurityGroups'. --- .../service/ec2/client_vpn_endpoint_test.go | 9 ++- .../client_vpn_network_association_test.go | 76 ++++++++++++++++++- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/internal/service/ec2/client_vpn_endpoint_test.go b/internal/service/ec2/client_vpn_endpoint_test.go index f506a6d5a1d..36e4bf11ee2 100644 --- a/internal/service/ec2/client_vpn_endpoint_test.go +++ b/internal/service/ec2/client_vpn_endpoint_test.go @@ -55,10 +55,11 @@ func TestAccEC2ClientVPNEndpoint_serial(t *testing.T) { "disappearsEndpoint": testAccClientVPNAuthorizationRule_Disappears_endpoint, }, "NetworkAssociation": { - "basic": testAccClientVPNNetworkAssociation_basic, - "multipleSubnets": testAccClientVPNNetworkAssociation_multipleSubnets, - "disappears": testAccClientVPNNetworkAssociation_disappears, - "securityGroups": testAccClientVPNNetworkAssociation_securityGroups, + "basic": testAccClientVPNNetworkAssociation_basic, + "multipleSubnets": testAccClientVPNNetworkAssociation_multipleSubnets, + "disappears": testAccClientVPNNetworkAssociation_disappears, + "securityGroups": testAccClientVPNNetworkAssociation_securityGroups, + "multipleSubnetsWithSecurityGroups": testAccClientVPNNetworkAssociation_multipleSubnetsWithSecurityGroups, }, "Route": { "basic": testAccClientVPNRoute_basic, diff --git a/internal/service/ec2/client_vpn_network_association_test.go b/internal/service/ec2/client_vpn_network_association_test.go index 340b7904774..dd82ee74c43 100644 --- a/internal/service/ec2/client_vpn_network_association_test.go +++ b/internal/service/ec2/client_vpn_network_association_test.go @@ -60,7 +60,7 @@ func testAccClientVPNNetworkAssociation_multipleSubnets(t *testing.T) { var assoc ec2.TargetNetwork var group ec2.SecurityGroup rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceNames := []string{"aws_ec2_client_vpn_network_association.test", "aws_ec2_client_vpn_network_association.test2"} + resourceNames := []string{"aws_ec2_client_vpn_network_association.test1", "aws_ec2_client_vpn_network_association.test2"} endpointResourceName := "aws_ec2_client_vpn_endpoint.test" subnetResourceNames := []string{"aws_subnet.test1", "aws_subnet.test2"} vpcResourceName := "aws_vpc.test" @@ -171,6 +171,42 @@ func testAccClientVPNNetworkAssociation_securityGroups(t *testing.T) { }) } +func testAccClientVPNNetworkAssociation_multipleSubnetsWithSecurityGroups(t *testing.T) { + var assoc1, assoc2 ec2.TargetNetwork + var group1, group2 ec2.SecurityGroup + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resource1Name := "aws_ec2_client_vpn_network_association.test1" + resource2Name := "aws_ec2_client_vpn_network_association.test2" + securityGroup1ResourceName := "aws_security_group.test1" + securityGroup2ResourceName := "aws_security_group.test2" + subnet1ResourceName := "aws_subnet.test1" + subnet2ResourceName := "aws_subnet.test2" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheckClientVPNSyncronize(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckClientVPNNetworkAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEc2ClientVpnNetworkAssociationMultipleSubnetsWithSecurityGroupsConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckClientVPNNetworkAssociationExists(resource1Name, &assoc1), + testAccCheckClientVPNNetworkAssociationExists(resource2Name, &assoc2), + testAccCheckDefaultSecurityGroupExists(securityGroup1ResourceName, &group1), + testAccCheckDefaultSecurityGroupExists(securityGroup2ResourceName, &group2), + resource.TestCheckResourceAttr(resource1Name, "security_groups.#", "1"), + resource.TestCheckResourceAttr(resource2Name, "security_groups.#", "1"), + testAccCheckClientVPNNetworkAssociationSecurityGroupID(resource1Name, "security_groups.*", &group1), + testAccCheckClientVPNNetworkAssociationSecurityGroupID(resource2Name, "security_groups.*", &group2), + resource.TestCheckResourceAttrPair(resource1Name, "subnet_id", subnet1ResourceName, "id"), + resource.TestCheckResourceAttrPair(resource2Name, "subnet_id", subnet2ResourceName, "id"), + ), + }, + }, + }) +} + func testAccCheckClientVPNNetworkAssociationDestroy(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn @@ -289,7 +325,7 @@ resource "aws_ec2_client_vpn_network_association" "test" { func testAccEc2ClientVpnNetworkAssociationConfigMultipleSubnets(rName string) string { return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), ` -resource "aws_ec2_client_vpn_network_association" "test" { +resource "aws_ec2_client_vpn_network_association" "test1" { client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id subnet_id = aws_subnet.test1.id } @@ -360,3 +396,39 @@ resource "aws_security_group" "test2" { } `, rName)) } + +func testAccEc2ClientVpnNetworkAssociationMultipleSubnetsWithSecurityGroupsConfig(rName string) string { + return acctest.ConfigCompose( + testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), + fmt.Sprintf(` +resource "aws_ec2_client_vpn_network_association" "test1" { + client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id + subnet_id = aws_subnet.test1.id + security_groups = [aws_security_group.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 + security_groups = [aws_security_group.test2.id] +} + +resource "aws_security_group" "test1" { + name = "%[1]s-1" + vpc_id = aws_vpc.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_security_group" "test2" { + name = "%[1]s-2" + vpc_id = aws_vpc.test.id + + tags = { + Name = %[1]q + } +} +`, rName)) +} From 282918e0fb1f9d3da8c22ff2f23d4047ceb57aad Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 09:35:44 -0500 Subject: [PATCH 02/31] Correct test for 'ErrCodeInvalidClientVpnAssociationIdNotFound'. --- internal/service/ec2/errors.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/ec2/errors.go b/internal/service/ec2/errors.go index a3459abd609..cc720fccf69 100644 --- a/internal/service/ec2/errors.go +++ b/internal/service/ec2/errors.go @@ -21,7 +21,7 @@ const ( ErrCodeInvalidAssociationIDNotFound = "InvalidAssociationID.NotFound" ErrCodeInvalidAttachmentIDNotFound = "InvalidAttachmentID.NotFound" ErrCodeInvalidCarrierGatewayIDNotFound = "InvalidCarrierGatewayID.NotFound" - ErrCodeInvalidClientVpnAssociationIdNotFound = "InvalidClientVpnAssociationId.NotFound" + ErrCodeInvalidClientVpnAssociationIdNotFound = "InvalidClientVpnAssociationIdNotFound" ErrCodeInvalidClientVpnAuthorizationRuleNotFound = "InvalidClientVpnEndpointAuthorizationRuleNotFound" ErrCodeInvalidClientVpnEndpointIdNotFound = "InvalidClientVpnEndpointId.NotFound" ErrCodeInvalidClientVpnRouteNotFound = "InvalidClientVpnRouteNotFound" From 1a551b8b09d446d37590923e5527a9431687b4dd Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 13:42:03 -0500 Subject: [PATCH 03/31] r/aws_ec2_client_vpn_network_association: Deprecate the 'security_groups' argument. --- .changelog/#####.txt | 3 +++ .../service/ec2/client_vpn_network_association.go | 15 ++++++++------- ...2_client_vpn_network_association.html.markdown | 3 +-- 3 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 .changelog/#####.txt diff --git a/.changelog/#####.txt b/.changelog/#####.txt new file mode 100644 index 00000000000..c4a1fbc276a --- /dev/null +++ b/.changelog/#####.txt @@ -0,0 +1,3 @@ +```release-note:note +resource/aws_ec2_client_vpn_network_association: The `security_groups` argument has been deprecated. Use the `aws_ec2_client_vpn_security_group_association` resource instead +``` \ 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 f841b90322b..9496c2904b9 100644 --- a/internal/service/ec2/client_vpn_network_association.go +++ b/internal/service/ec2/client_vpn_network_association.go @@ -41,13 +41,14 @@ func ResourceClientVPNNetworkAssociation() *schema.Resource { ForceNew: true, }, "security_groups": { - Type: schema.TypeSet, - MinItems: 1, - MaxItems: 5, - Optional: true, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, + Type: schema.TypeSet, + MinItems: 1, + MaxItems: 5, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Deprecated: "Use the `aws_ec2_client_vpn_security_group_association` resource instead.", }, "status": { Type: schema.TypeString, 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 d9819acd1ff..c7438e55ea2 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -38,7 +38,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. If not specified, the VPC's default security group is assigned. +* `security_groups` - (Optional, **Deprecated** use the `aws_ec2_client_vpn_security_group_association` resource instead) 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 @@ -46,7 +46,6 @@ 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. * `status` - **Deprecated** The current state of the target network association. * `vpc_id` - The ID of the VPC in which the target subnet is located. From 7dbca773847849e250ba1f5b5a20ef11827d5d30 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 13:42:16 -0500 Subject: [PATCH 04/31] Revert "Add 'testAccClientVPNNetworkAssociation_multipleSubnetsWithSecurityGroups'." This reverts commit 97950d89a58eb32b291c450b480e3b63ed70159f. --- .../service/ec2/client_vpn_endpoint_test.go | 9 +-- .../client_vpn_network_association_test.go | 76 +------------------ 2 files changed, 6 insertions(+), 79 deletions(-) diff --git a/internal/service/ec2/client_vpn_endpoint_test.go b/internal/service/ec2/client_vpn_endpoint_test.go index 36e4bf11ee2..f506a6d5a1d 100644 --- a/internal/service/ec2/client_vpn_endpoint_test.go +++ b/internal/service/ec2/client_vpn_endpoint_test.go @@ -55,11 +55,10 @@ func TestAccEC2ClientVPNEndpoint_serial(t *testing.T) { "disappearsEndpoint": testAccClientVPNAuthorizationRule_Disappears_endpoint, }, "NetworkAssociation": { - "basic": testAccClientVPNNetworkAssociation_basic, - "multipleSubnets": testAccClientVPNNetworkAssociation_multipleSubnets, - "disappears": testAccClientVPNNetworkAssociation_disappears, - "securityGroups": testAccClientVPNNetworkAssociation_securityGroups, - "multipleSubnetsWithSecurityGroups": testAccClientVPNNetworkAssociation_multipleSubnetsWithSecurityGroups, + "basic": testAccClientVPNNetworkAssociation_basic, + "multipleSubnets": testAccClientVPNNetworkAssociation_multipleSubnets, + "disappears": testAccClientVPNNetworkAssociation_disappears, + "securityGroups": testAccClientVPNNetworkAssociation_securityGroups, }, "Route": { "basic": testAccClientVPNRoute_basic, diff --git a/internal/service/ec2/client_vpn_network_association_test.go b/internal/service/ec2/client_vpn_network_association_test.go index dd82ee74c43..340b7904774 100644 --- a/internal/service/ec2/client_vpn_network_association_test.go +++ b/internal/service/ec2/client_vpn_network_association_test.go @@ -60,7 +60,7 @@ func testAccClientVPNNetworkAssociation_multipleSubnets(t *testing.T) { var assoc ec2.TargetNetwork var group ec2.SecurityGroup rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceNames := []string{"aws_ec2_client_vpn_network_association.test1", "aws_ec2_client_vpn_network_association.test2"} + 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.test1", "aws_subnet.test2"} vpcResourceName := "aws_vpc.test" @@ -171,42 +171,6 @@ func testAccClientVPNNetworkAssociation_securityGroups(t *testing.T) { }) } -func testAccClientVPNNetworkAssociation_multipleSubnetsWithSecurityGroups(t *testing.T) { - var assoc1, assoc2 ec2.TargetNetwork - var group1, group2 ec2.SecurityGroup - rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resource1Name := "aws_ec2_client_vpn_network_association.test1" - resource2Name := "aws_ec2_client_vpn_network_association.test2" - securityGroup1ResourceName := "aws_security_group.test1" - securityGroup2ResourceName := "aws_security_group.test2" - subnet1ResourceName := "aws_subnet.test1" - subnet2ResourceName := "aws_subnet.test2" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t); testAccPreCheckClientVPNSyncronize(t) }, - ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), - Providers: acctest.Providers, - CheckDestroy: testAccCheckClientVPNNetworkAssociationDestroy, - Steps: []resource.TestStep{ - { - Config: testAccEc2ClientVpnNetworkAssociationMultipleSubnetsWithSecurityGroupsConfig(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckClientVPNNetworkAssociationExists(resource1Name, &assoc1), - testAccCheckClientVPNNetworkAssociationExists(resource2Name, &assoc2), - testAccCheckDefaultSecurityGroupExists(securityGroup1ResourceName, &group1), - testAccCheckDefaultSecurityGroupExists(securityGroup2ResourceName, &group2), - resource.TestCheckResourceAttr(resource1Name, "security_groups.#", "1"), - resource.TestCheckResourceAttr(resource2Name, "security_groups.#", "1"), - testAccCheckClientVPNNetworkAssociationSecurityGroupID(resource1Name, "security_groups.*", &group1), - testAccCheckClientVPNNetworkAssociationSecurityGroupID(resource2Name, "security_groups.*", &group2), - resource.TestCheckResourceAttrPair(resource1Name, "subnet_id", subnet1ResourceName, "id"), - resource.TestCheckResourceAttrPair(resource2Name, "subnet_id", subnet2ResourceName, "id"), - ), - }, - }, - }) -} - func testAccCheckClientVPNNetworkAssociationDestroy(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn @@ -325,7 +289,7 @@ resource "aws_ec2_client_vpn_network_association" "test" { func testAccEc2ClientVpnNetworkAssociationConfigMultipleSubnets(rName string) string { return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), ` -resource "aws_ec2_client_vpn_network_association" "test1" { +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 } @@ -396,39 +360,3 @@ resource "aws_security_group" "test2" { } `, rName)) } - -func testAccEc2ClientVpnNetworkAssociationMultipleSubnetsWithSecurityGroupsConfig(rName string) string { - return acctest.ConfigCompose( - testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), - fmt.Sprintf(` -resource "aws_ec2_client_vpn_network_association" "test1" { - client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id - subnet_id = aws_subnet.test1.id - security_groups = [aws_security_group.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 - security_groups = [aws_security_group.test2.id] -} - -resource "aws_security_group" "test1" { - name = "%[1]s-1" - vpc_id = aws_vpc.test.id - - tags = { - Name = %[1]q - } -} - -resource "aws_security_group" "test2" { - name = "%[1]s-2" - vpc_id = aws_vpc.test.id - - tags = { - Name = %[1]q - } -} -`, rName)) -} From 9eed22b9253f609ed48827ef16e0d14e8d408e61 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 13:44:31 -0500 Subject: [PATCH 05/31] Standardize resource names in acceptance test case. --- internal/service/ec2/client_vpn_network_association_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/service/ec2/client_vpn_network_association_test.go b/internal/service/ec2/client_vpn_network_association_test.go index 340b7904774..8989cc500d6 100644 --- a/internal/service/ec2/client_vpn_network_association_test.go +++ b/internal/service/ec2/client_vpn_network_association_test.go @@ -60,7 +60,7 @@ func testAccClientVPNNetworkAssociation_multipleSubnets(t *testing.T) { var assoc ec2.TargetNetwork var group ec2.SecurityGroup rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) - resourceNames := []string{"aws_ec2_client_vpn_network_association.test", "aws_ec2_client_vpn_network_association.test2"} + resourceNames := []string{"aws_ec2_client_vpn_network_association.test1", "aws_ec2_client_vpn_network_association.test2"} endpointResourceName := "aws_ec2_client_vpn_endpoint.test" subnetResourceNames := []string{"aws_subnet.test1", "aws_subnet.test2"} vpcResourceName := "aws_vpc.test" @@ -289,7 +289,7 @@ resource "aws_ec2_client_vpn_network_association" "test" { func testAccEc2ClientVpnNetworkAssociationConfigMultipleSubnets(rName string) string { return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), ` -resource "aws_ec2_client_vpn_network_association" "test" { +resource "aws_ec2_client_vpn_network_association" "test1" { client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id subnet_id = aws_subnet.test1.id } From 64db47aa82c094e87168e946b5ae78295880be32 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 13:59:24 -0500 Subject: [PATCH 06/31] r/aws_ec2_client_vpn_security_groups_association: New resource. --- .changelog/#####.txt | 4 ++ internal/provider/provider.go | 1 + .../client_vpn_security_group_association.go | 50 +++++++++++++++++++ ...ent_vpn_security_group_association_test.go | 8 +++ ..._security_groups_association.html.markdown | 11 ++++ 5 files changed, 74 insertions(+) create mode 100644 internal/service/ec2/client_vpn_security_group_association.go create mode 100644 internal/service/ec2/client_vpn_security_group_association_test.go create mode 100644 website/docs/r/ec2_client_vpn_security_groups_association.html.markdown diff --git a/.changelog/#####.txt b/.changelog/#####.txt index c4a1fbc276a..8740151ac2f 100644 --- a/.changelog/#####.txt +++ b/.changelog/#####.txt @@ -1,3 +1,7 @@ ```release-note:note resource/aws_ec2_client_vpn_network_association: The `security_groups` argument has been deprecated. Use the `aws_ec2_client_vpn_security_group_association` resource instead +``` + +```release-note:new-resource +aws_ec2_client_vpn_security_groups_association ``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index da8a8567ac6..792e37db57a 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1144,6 +1144,7 @@ func Provider() *schema.Provider { "aws_ec2_client_vpn_endpoint": ec2.ResourceClientVPNEndpoint(), "aws_ec2_client_vpn_network_association": ec2.ResourceClientVPNNetworkAssociation(), "aws_ec2_client_vpn_route": ec2.ResourceClientVPNRoute(), + "aws_ec2_client_vpn_security_groups_association": ec2.ResourceClientVPNSecurityGroupsAssociation(), "aws_ec2_fleet": ec2.ResourceFleet(), "aws_ec2_host": ec2.ResourceHost(), "aws_ec2_local_gateway_route": ec2.ResourceLocalGatewayRoute(), diff --git a/internal/service/ec2/client_vpn_security_group_association.go b/internal/service/ec2/client_vpn_security_group_association.go new file mode 100644 index 00000000000..016e69295ed --- /dev/null +++ b/internal/service/ec2/client_vpn_security_group_association.go @@ -0,0 +1,50 @@ +package ec2 + +import ( + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ResourceClientVPNSecurityGroupsAssociation() *schema.Resource { + return &schema.Resource{ + Create: resourceClientVPNSecurityGroupsAssociationCreate, + Read: resourceClientVPNSecurityGroupsAssociationRead, + Update: resourceClientVPNSecurityGroupsAssociationUpdate, + Delete: resourceClientVPNSecurityGroupsAssociationDelete, + + Schema: map[string]*schema.Schema{ + "client_vpn_endpoint_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "security_groups": { + Type: schema.TypeSet, + MinItems: 1, + MaxItems: 5, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "vpc_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceClientVPNSecurityGroupsAssociationCreate(d *schema.ResourceData, meta interface{}) error { + return nil +} + +func resourceClientVPNSecurityGroupsAssociationRead(d *schema.ResourceData, meta interface{}) error { + return nil +} + +func resourceClientVPNSecurityGroupsAssociationUpdate(d *schema.ResourceData, meta interface{}) error { + return nil +} + +func resourceClientVPNSecurityGroupsAssociationDelete(d *schema.ResourceData, meta interface{}) error { + return nil +} diff --git a/internal/service/ec2/client_vpn_security_group_association_test.go b/internal/service/ec2/client_vpn_security_group_association_test.go new file mode 100644 index 00000000000..136a76e1c57 --- /dev/null +++ b/internal/service/ec2/client_vpn_security_group_association_test.go @@ -0,0 +1,8 @@ +package ec2_test + +import ( + "testing" +) + +func testAccClientVPNSecurityGroupsAssociation_basic(t *testing.T) { +} diff --git a/website/docs/r/ec2_client_vpn_security_groups_association.html.markdown b/website/docs/r/ec2_client_vpn_security_groups_association.html.markdown new file mode 100644 index 00000000000..a2859cdebef --- /dev/null +++ b/website/docs/r/ec2_client_vpn_security_groups_association.html.markdown @@ -0,0 +1,11 @@ +--- +subcategory: "EC2" +layout: "aws" +page_title: "AWS: aws_ec2_client_vpn_security_groups_association" +description: |- + Applies security groups to the association between a Client VPN endpoint and a VPC. +--- + +# Resource: aws_ec2_client_vpn_security_groups_association + +Applies security groups to the association between a Client VPN endpoint and a VPC. For more information see the [AWS Client VPN Administrator Guide](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/cvpn-working-target.html#cvpn-working-target-apply). \ No newline at end of file From d38242aac6c76ce0a735b6d31e62c988b9ed0a49 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 14:02:17 -0500 Subject: [PATCH 07/31] Correct new resource name. --- internal/service/ec2/client_vpn_network_association.go | 3 +-- ...ssociation.go => client_vpn_security_groups_association.go} | 0 ..._test.go => client_vpn_security_groups_association_test.go} | 0 .../docs/r/ec2_client_vpn_network_association.html.markdown | 2 +- 4 files changed, 2 insertions(+), 3 deletions(-) rename internal/service/ec2/{client_vpn_security_group_association.go => client_vpn_security_groups_association.go} (100%) rename internal/service/ec2/{client_vpn_security_group_association_test.go => client_vpn_security_groups_association_test.go} (100%) diff --git a/internal/service/ec2/client_vpn_network_association.go b/internal/service/ec2/client_vpn_network_association.go index 9496c2904b9..6d2368cb2af 100644 --- a/internal/service/ec2/client_vpn_network_association.go +++ b/internal/service/ec2/client_vpn_network_association.go @@ -47,8 +47,7 @@ func ResourceClientVPNNetworkAssociation() *schema.Resource { Optional: true, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Set: schema.HashString, - Deprecated: "Use the `aws_ec2_client_vpn_security_group_association` resource instead.", + Deprecated: "Use the `aws_ec2_client_vpn_security_groups_association` resource instead.", }, "status": { Type: schema.TypeString, diff --git a/internal/service/ec2/client_vpn_security_group_association.go b/internal/service/ec2/client_vpn_security_groups_association.go similarity index 100% rename from internal/service/ec2/client_vpn_security_group_association.go rename to internal/service/ec2/client_vpn_security_groups_association.go diff --git a/internal/service/ec2/client_vpn_security_group_association_test.go b/internal/service/ec2/client_vpn_security_groups_association_test.go similarity index 100% rename from internal/service/ec2/client_vpn_security_group_association_test.go rename to internal/service/ec2/client_vpn_security_groups_association_test.go 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 c7438e55ea2..f5042cc3349 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -38,7 +38,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, **Deprecated** use the `aws_ec2_client_vpn_security_group_association` resource instead) 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. +* `security_groups` - (Optional, **Deprecated** use the `aws_ec2_client_vpn_security_groups_association` resource instead) 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 699d84b884b1d337772c2bc9c49b950a8a99cb05 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 15:32:02 -0500 Subject: [PATCH 08/31] r/aws_ec2_client_vpn_security_groups_association: Implementation. --- .../client_vpn_security_groups_association.go | 134 +++++++++++++++++- 1 file changed, 131 insertions(+), 3 deletions(-) diff --git a/internal/service/ec2/client_vpn_security_groups_association.go b/internal/service/ec2/client_vpn_security_groups_association.go index 016e69295ed..96d8d1d8a4f 100644 --- a/internal/service/ec2/client_vpn_security_groups_association.go +++ b/internal/service/ec2/client_vpn_security_groups_association.go @@ -1,7 +1,16 @@ package ec2 import ( + "fmt" + "log" + "strings" + + "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-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceClientVPNSecurityGroupsAssociation() *schema.Resource { @@ -11,13 +20,17 @@ func ResourceClientVPNSecurityGroupsAssociation() *schema.Resource { Update: resourceClientVPNSecurityGroupsAssociationUpdate, Delete: resourceClientVPNSecurityGroupsAssociationDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ "client_vpn_endpoint_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "security_groups": { + "security_group_ids": { Type: schema.TypeSet, MinItems: 1, MaxItems: 5, @@ -34,17 +47,132 @@ func ResourceClientVPNSecurityGroupsAssociation() *schema.Resource { } func resourceClientVPNSecurityGroupsAssociationCreate(d *schema.ResourceData, meta interface{}) error { - return nil + conn := meta.(*conns.AWSClient).EC2Conn + + endpointID := d.Get("client_vpn_endpoint_id").(string) + vpcID := d.Get("vpc_id").(string) + id := ClientVPNSecurityGroupsAssociationCreateResourceID(endpointID, vpcID) + input := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ + ClientVpnEndpointId: aws.String(endpointID), + SecurityGroupIds: flex.ExpandStringSet(d.Get("security_group_ids").(*schema.Set)), + VpcId: aws.String(vpcID), + } + + log.Printf("[DEBUG] Creating EC2 Client VPN Security Groups Association: %s", input) + _, err := conn.ApplySecurityGroupsToClientVpnTargetNetwork(input) + + if err != nil { + return fmt.Errorf("error creating EC2 Client VPN Security Groups Association (%s): %w", id, err) + } + + d.SetId(id) + + return resourceClientVPNSecurityGroupsAssociationRead(d, meta) } func resourceClientVPNSecurityGroupsAssociationRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).EC2Conn + + endpointID, vpcID, err := ClientVPNSecurityGroupsAssociationParseResourceID(d.Id()) + + if err != nil { + return err + } + + input := &ec2.DescribeClientVpnTargetNetworksInput{ + ClientVpnEndpointId: aws.String(endpointID), + Filters: BuildAttributeFilterList(map[string]string{ + "vpc-id": vpcID, + }), + } + + _, err = FindClientVPNNetworkAssociations(conn, input) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EC2 Client VPN Security Groups Association (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return fmt.Errorf("error reading EC2 Client VPN Security Groups Association (%s): %w", d.Id(), err) + } + + d.Set("client_vpn_endpoint_id", endpointID) + d.Set("vpc_id", vpcID) + return nil } func resourceClientVPNSecurityGroupsAssociationUpdate(d *schema.ResourceData, meta interface{}) error { - return nil + conn := meta.(*conns.AWSClient).EC2Conn + + endpointID, vpcID, err := ClientVPNSecurityGroupsAssociationParseResourceID(d.Id()) + + if err != nil { + return err + } + + input := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ + ClientVpnEndpointId: aws.String(endpointID), + SecurityGroupIds: flex.ExpandStringSet(d.Get("security_group_ids").(*schema.Set)), + VpcId: aws.String(vpcID), + } + + log.Printf("[DEBUG] Updating EC2 Client VPN Security Groups Association: %s", input) + _, err = conn.ApplySecurityGroupsToClientVpnTargetNetwork(input) + + if err != nil { + return fmt.Errorf("error updating EC2 Client VPN Security Groups Association (%s): %w", d.Id(), err) + } + + return resourceClientVPNSecurityGroupsAssociationRead(d, meta) } func resourceClientVPNSecurityGroupsAssociationDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*conns.AWSClient).EC2Conn + + endpointID, vpcID, err := ClientVPNSecurityGroupsAssociationParseResourceID(d.Id()) + + if err != nil { + return err + } + + defaultSecurityGroup, err := FindVPCDefaultSecurityGroup(conn, vpcID) + + if err != nil { + return fmt.Errorf("error reading EC2 VPC (%s) default Security Group: %w", vpcID, err) + } + + log.Printf("[DEBUG] Deleting EC2 Client VPN Security Groups Association: %s", d.Id()) + _, err = conn.ApplySecurityGroupsToClientVpnTargetNetwork(&ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ + ClientVpnEndpointId: aws.String(endpointID), + SecurityGroupIds: []*string{defaultSecurityGroup.GroupId}, + VpcId: aws.String(vpcID), + }) + + if err != nil { + return fmt.Errorf("error updating EC2 Client VPN Security Groups Association (%s): %w", d.Id(), err) + } + return nil } + +const clientVPNSecurityGroupsAssociationIDSeparator = "/" + +func ClientVPNSecurityGroupsAssociationCreateResourceID(endpointID, vpcID string) string { + parts := []string{endpointID, vpcID} + id := strings.Join(parts, clientVPNSecurityGroupsAssociationIDSeparator) + + return id +} + +func ClientVPNSecurityGroupsAssociationParseResourceID(id string) (string, string, error) { + parts := strings.Split(id, clientVPNAuthorizationRuleIDSeparator) + + 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]sVPCID", id, clientVPNAuthorizationRuleIDSeparator) +} From f4b0ec388edebd1b34d0b585cab4239ea5be724c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 15:38:33 -0500 Subject: [PATCH 09/31] Revert "r/aws_ec2_client_vpn_security_groups_association: Implementation." This reverts commit 9c7fe9c92abb021694ed73873af57ebc3079e563. --- .../client_vpn_security_groups_association.go | 134 +----------------- 1 file changed, 3 insertions(+), 131 deletions(-) diff --git a/internal/service/ec2/client_vpn_security_groups_association.go b/internal/service/ec2/client_vpn_security_groups_association.go index 96d8d1d8a4f..016e69295ed 100644 --- a/internal/service/ec2/client_vpn_security_groups_association.go +++ b/internal/service/ec2/client_vpn_security_groups_association.go @@ -1,16 +1,7 @@ package ec2 import ( - "fmt" - "log" - "strings" - - "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-provider-aws/internal/conns" - "github.com/hashicorp/terraform-provider-aws/internal/flex" - "github.com/hashicorp/terraform-provider-aws/internal/tfresource" ) func ResourceClientVPNSecurityGroupsAssociation() *schema.Resource { @@ -20,17 +11,13 @@ func ResourceClientVPNSecurityGroupsAssociation() *schema.Resource { Update: resourceClientVPNSecurityGroupsAssociationUpdate, Delete: resourceClientVPNSecurityGroupsAssociationDelete, - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - Schema: map[string]*schema.Schema{ "client_vpn_endpoint_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "security_group_ids": { + "security_groups": { Type: schema.TypeSet, MinItems: 1, MaxItems: 5, @@ -47,132 +34,17 @@ func ResourceClientVPNSecurityGroupsAssociation() *schema.Resource { } func resourceClientVPNSecurityGroupsAssociationCreate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).EC2Conn - - endpointID := d.Get("client_vpn_endpoint_id").(string) - vpcID := d.Get("vpc_id").(string) - id := ClientVPNSecurityGroupsAssociationCreateResourceID(endpointID, vpcID) - input := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ - ClientVpnEndpointId: aws.String(endpointID), - SecurityGroupIds: flex.ExpandStringSet(d.Get("security_group_ids").(*schema.Set)), - VpcId: aws.String(vpcID), - } - - log.Printf("[DEBUG] Creating EC2 Client VPN Security Groups Association: %s", input) - _, err := conn.ApplySecurityGroupsToClientVpnTargetNetwork(input) - - if err != nil { - return fmt.Errorf("error creating EC2 Client VPN Security Groups Association (%s): %w", id, err) - } - - d.SetId(id) - - return resourceClientVPNSecurityGroupsAssociationRead(d, meta) + return nil } func resourceClientVPNSecurityGroupsAssociationRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).EC2Conn - - endpointID, vpcID, err := ClientVPNSecurityGroupsAssociationParseResourceID(d.Id()) - - if err != nil { - return err - } - - input := &ec2.DescribeClientVpnTargetNetworksInput{ - ClientVpnEndpointId: aws.String(endpointID), - Filters: BuildAttributeFilterList(map[string]string{ - "vpc-id": vpcID, - }), - } - - _, err = FindClientVPNNetworkAssociations(conn, input) - - if !d.IsNewResource() && tfresource.NotFound(err) { - log.Printf("[WARN] EC2 Client VPN Security Groups Association (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } - - if err != nil { - return fmt.Errorf("error reading EC2 Client VPN Security Groups Association (%s): %w", d.Id(), err) - } - - d.Set("client_vpn_endpoint_id", endpointID) - d.Set("vpc_id", vpcID) - return nil } func resourceClientVPNSecurityGroupsAssociationUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).EC2Conn - - endpointID, vpcID, err := ClientVPNSecurityGroupsAssociationParseResourceID(d.Id()) - - if err != nil { - return err - } - - input := &ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ - ClientVpnEndpointId: aws.String(endpointID), - SecurityGroupIds: flex.ExpandStringSet(d.Get("security_group_ids").(*schema.Set)), - VpcId: aws.String(vpcID), - } - - log.Printf("[DEBUG] Updating EC2 Client VPN Security Groups Association: %s", input) - _, err = conn.ApplySecurityGroupsToClientVpnTargetNetwork(input) - - if err != nil { - return fmt.Errorf("error updating EC2 Client VPN Security Groups Association (%s): %w", d.Id(), err) - } - - return resourceClientVPNSecurityGroupsAssociationRead(d, meta) + return nil } func resourceClientVPNSecurityGroupsAssociationDelete(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*conns.AWSClient).EC2Conn - - endpointID, vpcID, err := ClientVPNSecurityGroupsAssociationParseResourceID(d.Id()) - - if err != nil { - return err - } - - defaultSecurityGroup, err := FindVPCDefaultSecurityGroup(conn, vpcID) - - if err != nil { - return fmt.Errorf("error reading EC2 VPC (%s) default Security Group: %w", vpcID, err) - } - - log.Printf("[DEBUG] Deleting EC2 Client VPN Security Groups Association: %s", d.Id()) - _, err = conn.ApplySecurityGroupsToClientVpnTargetNetwork(&ec2.ApplySecurityGroupsToClientVpnTargetNetworkInput{ - ClientVpnEndpointId: aws.String(endpointID), - SecurityGroupIds: []*string{defaultSecurityGroup.GroupId}, - VpcId: aws.String(vpcID), - }) - - if err != nil { - return fmt.Errorf("error updating EC2 Client VPN Security Groups Association (%s): %w", d.Id(), err) - } - return nil } - -const clientVPNSecurityGroupsAssociationIDSeparator = "/" - -func ClientVPNSecurityGroupsAssociationCreateResourceID(endpointID, vpcID string) string { - parts := []string{endpointID, vpcID} - id := strings.Join(parts, clientVPNSecurityGroupsAssociationIDSeparator) - - return id -} - -func ClientVPNSecurityGroupsAssociationParseResourceID(id string) (string, string, error) { - parts := strings.Split(id, clientVPNAuthorizationRuleIDSeparator) - - 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]sVPCID", id, clientVPNAuthorizationRuleIDSeparator) -} From 43f4584a7c64d087f75a16a43dbea18123311f72 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 15:38:44 -0500 Subject: [PATCH 10/31] Revert "Correct new resource name." This reverts commit baa1fcf682a94e761d07a60feb8d3a18c7d75204. --- internal/service/ec2/client_vpn_network_association.go | 3 ++- ...association.go => client_vpn_security_group_association.go} | 0 ...n_test.go => client_vpn_security_group_association_test.go} | 0 .../docs/r/ec2_client_vpn_network_association.html.markdown | 2 +- 4 files changed, 3 insertions(+), 2 deletions(-) rename internal/service/ec2/{client_vpn_security_groups_association.go => client_vpn_security_group_association.go} (100%) rename internal/service/ec2/{client_vpn_security_groups_association_test.go => client_vpn_security_group_association_test.go} (100%) diff --git a/internal/service/ec2/client_vpn_network_association.go b/internal/service/ec2/client_vpn_network_association.go index 6d2368cb2af..9496c2904b9 100644 --- a/internal/service/ec2/client_vpn_network_association.go +++ b/internal/service/ec2/client_vpn_network_association.go @@ -47,7 +47,8 @@ func ResourceClientVPNNetworkAssociation() *schema.Resource { Optional: true, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, - Deprecated: "Use the `aws_ec2_client_vpn_security_groups_association` resource instead.", + Set: schema.HashString, + Deprecated: "Use the `aws_ec2_client_vpn_security_group_association` resource instead.", }, "status": { Type: schema.TypeString, diff --git a/internal/service/ec2/client_vpn_security_groups_association.go b/internal/service/ec2/client_vpn_security_group_association.go similarity index 100% rename from internal/service/ec2/client_vpn_security_groups_association.go rename to internal/service/ec2/client_vpn_security_group_association.go diff --git a/internal/service/ec2/client_vpn_security_groups_association_test.go b/internal/service/ec2/client_vpn_security_group_association_test.go similarity index 100% rename from internal/service/ec2/client_vpn_security_groups_association_test.go rename to internal/service/ec2/client_vpn_security_group_association_test.go 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 f5042cc3349..c7438e55ea2 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -38,7 +38,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, **Deprecated** use the `aws_ec2_client_vpn_security_groups_association` resource instead) 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. +* `security_groups` - (Optional, **Deprecated** use the `aws_ec2_client_vpn_security_group_association` resource instead) 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 6a4e0ff020d042bc114ce81f68f737b49dfbaa30 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 15:39:01 -0500 Subject: [PATCH 11/31] Revert "r/aws_ec2_client_vpn_security_groups_association: New resource." This reverts commit 5b005fb2f4ab6e9513282a94110bd9dae3343ed2. --- .changelog/#####.txt | 4 -- internal/provider/provider.go | 1 - .../client_vpn_security_group_association.go | 50 ------------------- ...ent_vpn_security_group_association_test.go | 8 --- ..._security_groups_association.html.markdown | 11 ---- 5 files changed, 74 deletions(-) delete mode 100644 internal/service/ec2/client_vpn_security_group_association.go delete mode 100644 internal/service/ec2/client_vpn_security_group_association_test.go delete mode 100644 website/docs/r/ec2_client_vpn_security_groups_association.html.markdown diff --git a/.changelog/#####.txt b/.changelog/#####.txt index 8740151ac2f..c4a1fbc276a 100644 --- a/.changelog/#####.txt +++ b/.changelog/#####.txt @@ -1,7 +1,3 @@ ```release-note:note resource/aws_ec2_client_vpn_network_association: The `security_groups` argument has been deprecated. Use the `aws_ec2_client_vpn_security_group_association` resource instead -``` - -```release-note:new-resource -aws_ec2_client_vpn_security_groups_association ``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 792e37db57a..da8a8567ac6 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1144,7 +1144,6 @@ func Provider() *schema.Provider { "aws_ec2_client_vpn_endpoint": ec2.ResourceClientVPNEndpoint(), "aws_ec2_client_vpn_network_association": ec2.ResourceClientVPNNetworkAssociation(), "aws_ec2_client_vpn_route": ec2.ResourceClientVPNRoute(), - "aws_ec2_client_vpn_security_groups_association": ec2.ResourceClientVPNSecurityGroupsAssociation(), "aws_ec2_fleet": ec2.ResourceFleet(), "aws_ec2_host": ec2.ResourceHost(), "aws_ec2_local_gateway_route": ec2.ResourceLocalGatewayRoute(), diff --git a/internal/service/ec2/client_vpn_security_group_association.go b/internal/service/ec2/client_vpn_security_group_association.go deleted file mode 100644 index 016e69295ed..00000000000 --- a/internal/service/ec2/client_vpn_security_group_association.go +++ /dev/null @@ -1,50 +0,0 @@ -package ec2 - -import ( - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" -) - -func ResourceClientVPNSecurityGroupsAssociation() *schema.Resource { - return &schema.Resource{ - Create: resourceClientVPNSecurityGroupsAssociationCreate, - Read: resourceClientVPNSecurityGroupsAssociationRead, - Update: resourceClientVPNSecurityGroupsAssociationUpdate, - Delete: resourceClientVPNSecurityGroupsAssociationDelete, - - Schema: map[string]*schema.Schema{ - "client_vpn_endpoint_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - "security_groups": { - Type: schema.TypeSet, - MinItems: 1, - MaxItems: 5, - Required: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "vpc_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - }, - } -} - -func resourceClientVPNSecurityGroupsAssociationCreate(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func resourceClientVPNSecurityGroupsAssociationRead(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func resourceClientVPNSecurityGroupsAssociationUpdate(d *schema.ResourceData, meta interface{}) error { - return nil -} - -func resourceClientVPNSecurityGroupsAssociationDelete(d *schema.ResourceData, meta interface{}) error { - return nil -} diff --git a/internal/service/ec2/client_vpn_security_group_association_test.go b/internal/service/ec2/client_vpn_security_group_association_test.go deleted file mode 100644 index 136a76e1c57..00000000000 --- a/internal/service/ec2/client_vpn_security_group_association_test.go +++ /dev/null @@ -1,8 +0,0 @@ -package ec2_test - -import ( - "testing" -) - -func testAccClientVPNSecurityGroupsAssociation_basic(t *testing.T) { -} diff --git a/website/docs/r/ec2_client_vpn_security_groups_association.html.markdown b/website/docs/r/ec2_client_vpn_security_groups_association.html.markdown deleted file mode 100644 index a2859cdebef..00000000000 --- a/website/docs/r/ec2_client_vpn_security_groups_association.html.markdown +++ /dev/null @@ -1,11 +0,0 @@ ---- -subcategory: "EC2" -layout: "aws" -page_title: "AWS: aws_ec2_client_vpn_security_groups_association" -description: |- - Applies security groups to the association between a Client VPN endpoint and a VPC. ---- - -# Resource: aws_ec2_client_vpn_security_groups_association - -Applies security groups to the association between a Client VPN endpoint and a VPC. For more information see the [AWS Client VPN Administrator Guide](https://docs.aws.amazon.com/vpn/latest/clientvpn-admin/cvpn-working-target.html#cvpn-working-target-apply). \ No newline at end of file From 31f1aca144b0ddd8020ba8aa368ee9df0a336bc2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 15:43:04 -0500 Subject: [PATCH 12/31] r/aws_ec2_client_vpn_network_association: Use new 'security_group_ids' argument of the 'aws_ec2_client_vpn_endpoint' resource to replace 'security_groups'. --- .changelog/#####.txt | 2 +- internal/service/ec2/client_vpn_network_association.go | 2 +- website/docs/r/ec2_client_vpn_network_association.html.markdown | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.changelog/#####.txt b/.changelog/#####.txt index c4a1fbc276a..007f6d774bf 100644 --- a/.changelog/#####.txt +++ b/.changelog/#####.txt @@ -1,3 +1,3 @@ ```release-note:note -resource/aws_ec2_client_vpn_network_association: The `security_groups` argument has been deprecated. Use the `aws_ec2_client_vpn_security_group_association` resource instead +resource/aws_ec2_client_vpn_network_association: The `security_groups` argument has been deprecated. Use the `security_group_ids` argument of the `aws_ec2_client_vpn_endpoint` resource instead ``` \ 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 9496c2904b9..9813a239a90 100644 --- a/internal/service/ec2/client_vpn_network_association.go +++ b/internal/service/ec2/client_vpn_network_association.go @@ -48,7 +48,7 @@ func ResourceClientVPNNetworkAssociation() *schema.Resource { Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, Set: schema.HashString, - Deprecated: "Use the `aws_ec2_client_vpn_security_group_association` resource instead.", + Deprecated: "Use the `security_group_ids` attribute of the `aws_ec2_client_vpn_endpoint` resource instead.", }, "status": { Type: schema.TypeString, 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 c7438e55ea2..edf3b258357 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -38,7 +38,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, **Deprecated** use the `aws_ec2_client_vpn_security_group_association` resource instead) 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. +* `security_groups` - (Optional, **Deprecated** use the `security_group_ids` argument of the `aws_ec2_client_vpn_endpoint` resource instead) 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 35e8f7024482f7b750254e88a0024b40e3bdc495 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 16:42:41 -0500 Subject: [PATCH 13/31] r/aws_ec2_client_vpn_endpoint: Add 'security_group_ids' and 'vpc_id' arguments. --- .changelog/#####.txt | 4 +++ internal/service/ec2/client_vpn_endpoint.go | 32 +++++++++++++++++++ .../service/ec2/client_vpn_endpoint_test.go | 2 ++ .../r/ec2_client_vpn_endpoint.html.markdown | 4 +++ ...ient_vpn_network_association.html.markdown | 2 ++ 5 files changed, 44 insertions(+) diff --git a/.changelog/#####.txt b/.changelog/#####.txt index 007f6d774bf..6fc1fbdeff7 100644 --- a/.changelog/#####.txt +++ b/.changelog/#####.txt @@ -1,3 +1,7 @@ ```release-note:note resource/aws_ec2_client_vpn_network_association: The `security_groups` argument has been deprecated. Use the `security_group_ids` argument of the `aws_ec2_client_vpn_endpoint` resource instead +``` + +```release-note:enhancement +resource/aws_ec2_client_vpn_endpoint: Add `security_group_ids` and `vpc_id` arguments ``` \ No newline at end of file diff --git a/internal/service/ec2/client_vpn_endpoint.go b/internal/service/ec2/client_vpn_endpoint.go index f1c9eaf41f2..daedcec53bd 100644 --- a/internal/service/ec2/client_vpn_endpoint.go +++ b/internal/service/ec2/client_vpn_endpoint.go @@ -156,6 +156,14 @@ func ResourceClientVPNEndpoint() *schema.Resource { Optional: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "security_group_ids": { + Type: schema.TypeSet, + MinItems: 1, + MaxItems: 5, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, "self_service_portal": { Type: schema.TypeString, Optional: true, @@ -192,6 +200,12 @@ func ResourceClientVPNEndpoint() *schema.Resource { Default: ec2.TransportProtocolUdp, ValidateFunc: validation.StringInSlice(ec2.TransportProtocol_Values(), false), }, + "vpc_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + RequiredWith: []string{"security_group_ids"}, + }, "vpn_port": { Type: schema.TypeInt, Optional: true, @@ -243,6 +257,10 @@ func resourceClientVPNEndpointCreate(d *schema.ResourceData, meta interface{}) e input.DnsServers = flex.ExpandStringList(v.([]interface{})) } + if v, ok := d.GetOk("security_group_ids"); ok { + input.SecurityGroupIds = flex.ExpandStringSet(v.(*schema.Set)) + } + if v, ok := d.GetOk("self_service_portal"); ok { input.SelfServicePortal = aws.String(v.(string)) } @@ -251,6 +269,10 @@ func resourceClientVPNEndpointCreate(d *schema.ResourceData, meta interface{}) e input.SessionTimeoutHours = aws.Int64(int64(v.(int))) } + if v, ok := d.GetOk("vpc_id"); ok { + input.VpcId = aws.String(v.(string)) + } + log.Printf("[DEBUG] Creating EC2 Client VPN Endpoint: %s", input) output, err := conn.CreateClientVpnEndpoint(input) @@ -316,6 +338,7 @@ func resourceClientVPNEndpointRead(d *schema.ResourceData, meta interface{}) err d.Set("description", ep.Description) d.Set("dns_name", ep.DnsName) d.Set("dns_servers", aws.StringValueSlice(ep.DnsServers)) + d.Set("security_group_ids", aws.StringValueSlice(ep.SecurityGroupIds)) if aws.StringValue(ep.SelfServicePortalUrl) != "" { d.Set("self_service_portal", ec2.SelfServicePortalEnabled) } else { @@ -326,6 +349,7 @@ func resourceClientVPNEndpointRead(d *schema.ResourceData, meta interface{}) err d.Set("split_tunnel", ep.SplitTunnel) d.Set("status", ep.Status.Code) d.Set("transport_protocol", ep.TransportProtocol) + d.Set("vpc_id", ep.VpcId) d.Set("vpn_port", ep.VpnPort) tags := KeyValueTags(ep.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) @@ -387,6 +411,10 @@ func resourceClientVPNEndpointUpdate(d *schema.ResourceData, meta interface{}) e } } + if d.HasChange("security_group_ids") { + input.SecurityGroupIds = flex.ExpandStringSet(d.Get("security_group_ids").(*schema.Set)) + } + if d.HasChange("self_service_portal") { input.SelfServicePortal = aws.String(d.Get("self_service_portal").(string)) } @@ -407,6 +435,10 @@ func resourceClientVPNEndpointUpdate(d *schema.ResourceData, meta interface{}) e input.VpnPort = aws.Int64(int64(d.Get("vpn_port").(int))) } + if d.HasChange("vpc_id") { + input.VpcId = aws.String(d.Get("vpc_id").(string)) + } + if _, err := conn.ModifyClientVpnEndpoint(input); err != nil { return fmt.Errorf("error modifying EC2 Client VPN Endpoint (%s): %w", d.Id(), err) } diff --git a/internal/service/ec2/client_vpn_endpoint_test.go b/internal/service/ec2/client_vpn_endpoint_test.go index f506a6d5a1d..972caebcc1e 100644 --- a/internal/service/ec2/client_vpn_endpoint_test.go +++ b/internal/service/ec2/client_vpn_endpoint_test.go @@ -118,6 +118,7 @@ func testAccClientVPNEndpoint_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttrSet(resourceName, "dns_name"), resource.TestCheckResourceAttr(resourceName, "dns_servers.#", "0"), + resource.TestCheckResourceAttr(resourceName, "security_group_ids.#", "0"), resource.TestCheckResourceAttr(resourceName, "self_service_portal", "disabled"), resource.TestCheckResourceAttrSet(resourceName, "server_certificate_arn"), resource.TestCheckResourceAttr(resourceName, "session_timeout_hours", "24"), @@ -125,6 +126,7 @@ func testAccClientVPNEndpoint_basic(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "status", ec2.ClientVpnEndpointStatusCodePendingAssociate), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), resource.TestCheckResourceAttr(resourceName, "transport_protocol", "udp"), + resource.TestCheckResourceAttr(resourceName, "vpc_id", ""), resource.TestCheckResourceAttr(resourceName, "vpn_port", "443"), ), }, diff --git a/website/docs/r/ec2_client_vpn_endpoint.html.markdown b/website/docs/r/ec2_client_vpn_endpoint.html.markdown index 4e8440593c0..3b31b17a294 100644 --- a/website/docs/r/ec2_client_vpn_endpoint.html.markdown +++ b/website/docs/r/ec2_client_vpn_endpoint.html.markdown @@ -11,6 +11,8 @@ description: |- Provides an AWS Client VPN endpoint for OpenVPN clients. 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). +~> **NOTE on Client VPN endpoint target network security groups:** Terraform provides both a standalone [Client VPN endpoint network association](ec2_client_vpn_network_association.html) resource with a (deprecated) `security_groups` argument and a Client VPN endpoint resource with a `security_group_ids` argument. Do not specify security groups in both resources. Doing so will cause a conflict and will overwrite the target network security group association. + ## Example Usage ```terraform @@ -43,12 +45,14 @@ The following arguments are supported: * `connection_log_options` - (Required) Information about the client connection logging options. * `description` - (Optional) A brief description of the Client VPN endpoint. * `dns_servers` - (Optional) Information about the DNS servers to be used for DNS resolution. A Client VPN endpoint can have up to two DNS servers. If no DNS server is specified, the DNS address of the connecting device is used. +* `security_group_ids` - (Optional) The IDs of one or more security groups to apply to the target network. You must also specify the ID of the VPC that contains the security groups. * `self_service_portal` - (Optional) Specify whether to enable the self-service portal for the Client VPN endpoint. Values can be `enabled` or `disabled`. Default value is `disabled`. * `server_certificate_arn` - (Required) The ARN of the ACM server certificate. * `session_timeout_hours` - (Optional) The maximum session duration is a trigger by which end-users are required to re-authenticate prior to establishing a VPN session. Default value is `24` - Valid values: `8 | 10 | 12 | 24` * `split_tunnel` - (Optional) Indicates whether split-tunnel is enabled on VPN endpoint. Default value is `false`. * `tags` - (Optional) A mapping of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://www.terraform.io/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. * `transport_protocol` - (Optional) The transport protocol to be used by the VPN session. Default value is `udp`. +* `vpc_id` - (Optional) The ID of the VPC to associate with the Client VPN endpoint. If no security group IDs are specified in the request, the default security group for the VPC is applied. * `vpn_port` - (Optional) The port number for the Client VPN endpoint. Valid values are `443` and `1194`. Default value is `443`. ### `authentication_options` Argument Reference 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 edf3b258357..8d62045c6e9 100644 --- a/website/docs/r/ec2_client_vpn_network_association.html.markdown +++ b/website/docs/r/ec2_client_vpn_network_association.html.markdown @@ -11,6 +11,8 @@ 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). +~> **NOTE on Client VPN endpoint target network security groups:** Terraform provides both a standalone Client VPN endpoint network association resource with a (deprecated) `security_groups` argument and a [Client VPN endpoint](ec2_client_vpn_endpoint.html) resource with a `security_group_ids` argument. Do not specify security groups in both resources. Doing so will cause a conflict and will overwrite the target network security group association. + ## Example Usage ### Using default security group From 4b894965bae1f800296faaf9dd4098d49b4ad3b2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 17:02:23 -0500 Subject: [PATCH 14/31] d/aws_ec2_client_vpn_endpoint: Add 'security_group_ids' and 'vpc_id' attributes. --- .changelog/#####.txt | 4 ++++ .../service/ec2/client_vpn_endpoint_data_source.go | 13 ++++++++++++- .../ec2/client_vpn_endpoint_data_source_test.go | 6 ++++++ .../docs/d/ec2_client_vpn_endpoint.html.markdown | 2 ++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/.changelog/#####.txt b/.changelog/#####.txt index 6fc1fbdeff7..674d67ced30 100644 --- a/.changelog/#####.txt +++ b/.changelog/#####.txt @@ -4,4 +4,8 @@ resource/aws_ec2_client_vpn_network_association: The `security_groups` argument ```release-note:enhancement resource/aws_ec2_client_vpn_endpoint: Add `security_group_ids` and `vpc_id` arguments +``` + +```release-note:enhancement +data-source/aws_ec2_client_vpn_endpoint: Add `security_group_ids` and `vpc_id` attributes ``` \ No newline at end of file diff --git a/internal/service/ec2/client_vpn_endpoint_data_source.go b/internal/service/ec2/client_vpn_endpoint_data_source.go index 1d46f72905c..611a9e7f64c 100644 --- a/internal/service/ec2/client_vpn_endpoint_data_source.go +++ b/internal/service/ec2/client_vpn_endpoint_data_source.go @@ -119,11 +119,16 @@ func DataSourceClientVPNEndpoint() *schema.Resource { Computed: true, }, "dns_servers": { - Type: schema.TypeSet, + 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}, + }, "self_service_portal": { Type: schema.TypeString, Computed: true, @@ -145,6 +150,10 @@ func DataSourceClientVPNEndpoint() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + }, "vpn_port": { Type: schema.TypeInt, Computed: true, @@ -219,6 +228,7 @@ func dataSourceClientVPNEndpointRead(d *schema.ResourceData, meta interface{}) e d.Set("description", ep.Description) d.Set("dns_name", ep.DnsName) d.Set("dns_servers", aws.StringValueSlice(ep.DnsServers)) + d.Set("security_group_ids", aws.StringValueSlice(ep.SecurityGroupIds)) if aws.StringValue(ep.SelfServicePortalUrl) != "" { d.Set("self_service_portal", ec2.SelfServicePortalEnabled) } else { @@ -228,6 +238,7 @@ func dataSourceClientVPNEndpointRead(d *schema.ResourceData, meta interface{}) e d.Set("session_timeout_hours", ep.SessionTimeoutHours) d.Set("split_tunnel", ep.SplitTunnel) d.Set("transport_protocol", ep.TransportProtocol) + d.Set("vpc_id", ep.VpcId) d.Set("vpn_port", ep.VpnPort) if err := d.Set("tags", KeyValueTags(ep.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != 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 index 3d5b8ac5648..24214bb6ca6 100644 --- a/internal/service/ec2/client_vpn_endpoint_data_source_test.go +++ b/internal/service/ec2/client_vpn_endpoint_data_source_test.go @@ -35,12 +35,14 @@ func testAccClientVPNEndpointDataSource_basic(t *testing.T) { 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, "security_group_ids.#", resourceName, "security_group_ids.#"), 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, "vpc_id", resourceName, "vpc_id"), resource.TestCheckResourceAttrPair(datasource1Name, "vpn_port", resourceName, "vpn_port"), resource.TestCheckResourceAttrPair(datasource2Name, "arn", resourceName, "arn"), @@ -53,12 +55,14 @@ func testAccClientVPNEndpointDataSource_basic(t *testing.T) { 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, "security_group_ids.#", resourceName, "security_group_ids.#"), 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, "vpc_id", resourceName, "vpc_id"), resource.TestCheckResourceAttrPair(datasource2Name, "vpn_port", resourceName, "vpn_port"), resource.TestCheckResourceAttrPair(datasource3Name, "arn", resourceName, "arn"), @@ -71,12 +75,14 @@ func testAccClientVPNEndpointDataSource_basic(t *testing.T) { 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, "security_group_ids.#", resourceName, "security_group_ids.#"), 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, "vpc_id", resourceName, "vpc_id"), 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 89a1ba07ee8..8fef734ced3 100644 --- a/website/docs/d/ec2_client_vpn_endpoint.html.markdown +++ b/website/docs/d/ec2_client_vpn_endpoint.html.markdown @@ -61,9 +61,11 @@ In addition to all arguments above, the following attributes are exported: * `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. +* `security_group_ids` - The IDs of the security groups for the target network associated with the Client VPN endpoint. * `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. +* `vpc_id` - The ID of the VPC associated with the Client VPN endpoint. * `vpn_port` - The port number for the Client VPN endpoint. From 54fa458d387293bc4b58c1e4bbcde8ae88914669 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 17:05:28 -0500 Subject: [PATCH 15/31] 'testAccEc2ClientVpnEndpointMsADBase' -> 'testAccEc2ClientVpnEndpointConfigMsADBase'. --- internal/service/ec2/client_vpn_endpoint_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/service/ec2/client_vpn_endpoint_test.go b/internal/service/ec2/client_vpn_endpoint_test.go index 972caebcc1e..dfe505db97e 100644 --- a/internal/service/ec2/client_vpn_endpoint_test.go +++ b/internal/service/ec2/client_vpn_endpoint_test.go @@ -690,7 +690,7 @@ resource "aws_acm_certificate" %[1]q { `, n, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key)) } -func testAccEc2ClientVpnEndpointMsADBase(rName, domain string) string { +func testAccEc2ClientVpnEndpointConfigMsADBase(rName, domain string) string { return acctest.ConfigCompose( acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` @@ -945,7 +945,7 @@ resource "aws_ec2_client_vpn_endpoint" "test" { func testAccEc2ClientVpnEndpointConfigWithMicrosoftAD(rName, domain string) string { return acctest.ConfigCompose( testAccEc2ClientVpnEndpointConfigAcmCertificateBase("test"), - testAccEc2ClientVpnEndpointMsADBase(rName, domain), + testAccEc2ClientVpnEndpointConfigMsADBase(rName, domain), fmt.Sprintf(` resource "aws_ec2_client_vpn_endpoint" "test" { server_certificate_arn = aws_acm_certificate.test.arn @@ -970,7 +970,7 @@ resource "aws_ec2_client_vpn_endpoint" "test" { func testAccEc2ClientVpnEndpointConfigWithMutualAuthAndMicrosoftAD(rName, domain string) string { return acctest.ConfigCompose( testAccEc2ClientVpnEndpointConfigAcmCertificateBase("test"), - testAccEc2ClientVpnEndpointMsADBase(rName, domain), + testAccEc2ClientVpnEndpointConfigMsADBase(rName, domain), fmt.Sprintf(` resource "aws_ec2_client_vpn_endpoint" "test" { server_certificate_arn = aws_acm_certificate.test.arn From 539f328d7868d9376f4d47308edfbf770327fddc Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 17:26:20 -0500 Subject: [PATCH 16/31] Add 'testAccClientVPNEndpoint_vpcNoSecurityGroups'. --- internal/service/ec2/client_vpn_endpoint.go | 7 +- .../service/ec2/client_vpn_endpoint_test.go | 114 ++++++++++++++++++ 2 files changed, 117 insertions(+), 4 deletions(-) diff --git a/internal/service/ec2/client_vpn_endpoint.go b/internal/service/ec2/client_vpn_endpoint.go index daedcec53bd..83ab12553e7 100644 --- a/internal/service/ec2/client_vpn_endpoint.go +++ b/internal/service/ec2/client_vpn_endpoint.go @@ -201,10 +201,9 @@ func ResourceClientVPNEndpoint() *schema.Resource { ValidateFunc: validation.StringInSlice(ec2.TransportProtocol_Values(), false), }, "vpc_id": { - Type: schema.TypeString, - Optional: true, - Computed: true, - RequiredWith: []string{"security_group_ids"}, + Type: schema.TypeString, + Optional: true, + Computed: true, }, "vpn_port": { Type: schema.TypeInt, diff --git a/internal/service/ec2/client_vpn_endpoint_test.go b/internal/service/ec2/client_vpn_endpoint_test.go index dfe505db97e..586111d3c3b 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, + "vpcNoSecurityGroups": testAccClientVPNEndpoint_vpcNoSecurityGroups, "basicDataSource": testAccClientVPNEndpointDataSource_basic, }, "AuthorizationRule": { @@ -626,6 +627,37 @@ func testAccClientVPNEndpoint_selfServicePortal(t *testing.T) { }) } +func testAccClientVPNEndpoint_vpcNoSecurityGroups(t *testing.T) { + var v ec2.ClientVpnEndpoint + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_ec2_client_vpn_endpoint.test" + defaultSecurityGroupResourceName := "aws_default_security_group.test" + vpcResourceName := "aws_vpc.test" + + 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: testAccEc2ClientVpnEndpointConfigSecurityGroups(rName, 0), + Check: resource.ComposeTestCheckFunc( + testAccCheckClientVPNEndpointExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "security_group_ids.*", defaultSecurityGroupResourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_id", vpcResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccPreCheckClientVPNSyncronize(t *testing.T) { sync.TestAccPreCheckSyncronize(t, testAccEc2ClientVpnEndpointSemaphore, "Client VPN") } @@ -726,6 +758,53 @@ resource "aws_subnet" "test" { `, rName, domain)) } +func testAccEc2ClientVpnEndpointConfigVPCBase(rName string) string { + return acctest.ConfigCompose( + acctest.ConfigAvailableAZsNoOptIn(), + fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +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 + + tags = { + Name = %[1]q + } +} + +resource "aws_default_security_group" "test" { + vpc_id = aws_vpc.test.id +} + +resource "aws_security_group" "test1" { + name = "%[1]s-1" + vpc_id = aws_vpc.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_security_group" "test2" { + name = "%[1]s-2" + vpc_id = aws_vpc.test.id + + tags = { + Name = %[1]q + } +} +`, rName)) +} + func testAccEc2ClientVpnEndpointConfigBasic() string { return acctest.ConfigCompose(testAccEc2ClientVpnEndpointConfigAcmCertificateBase("test"), ` resource "aws_ec2_client_vpn_endpoint" "test" { @@ -1191,3 +1270,38 @@ resource "aws_ec2_client_vpn_endpoint" "test" { } `, rName, selfServicePortal, idpEntityID)) } + +func testAccEc2ClientVpnEndpointConfigSecurityGroups(rName string, nSecurityGroups int) string { + return acctest.ConfigCompose( + testAccEc2ClientVpnEndpointConfigAcmCertificateBase("test"), + testAccEc2ClientVpnEndpointConfigVPCBase(rName), + fmt.Sprintf(` +locals { + security_group_count = %[2]d + security_group_ids = local.security_group_count == 0 ? null : (local.security_group_count == 1? [aws_security_group.test1.id] : [aws_security_group.test1.id, aws_security_group.test2.id]) +} + +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 + } + + vpc_id = aws_vpc.test.id + security_group_ids = local.security_group_ids + + depends_on = [aws_subnet.test[0], aws_subnet.test[1]] +} +`, rName, nSecurityGroups)) +} From 7123c3451d4b5a045aa52c4f1246e0eac136daa8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 17:31:07 -0500 Subject: [PATCH 17/31] Correct CHANGELOG file name. --- .changelog/{#####.txt => 22911.txt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .changelog/{#####.txt => 22911.txt} (100%) diff --git a/.changelog/#####.txt b/.changelog/22911.txt similarity index 100% rename from .changelog/#####.txt rename to .changelog/22911.txt From 5b90500888add52102056866d02c8fe664f529c6 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 17:42:24 -0500 Subject: [PATCH 18/31] Add 'testAccClientVPNEndpoint_vpcSecurityGroups'. --- internal/service/ec2/client_vpn_endpoint.go | 2 + .../service/ec2/client_vpn_endpoint_test.go | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/internal/service/ec2/client_vpn_endpoint.go b/internal/service/ec2/client_vpn_endpoint.go index 83ab12553e7..bc8cc0a0ca6 100644 --- a/internal/service/ec2/client_vpn_endpoint.go +++ b/internal/service/ec2/client_vpn_endpoint.go @@ -412,6 +412,8 @@ func resourceClientVPNEndpointUpdate(d *schema.ResourceData, meta interface{}) e if d.HasChange("security_group_ids") { input.SecurityGroupIds = flex.ExpandStringSet(d.Get("security_group_ids").(*schema.Set)) + // "InvalidParameterValue: Security Groups cannot be modified without specifying Vpc Id" + input.VpcId = aws.String(d.Get("vpc_id").(string)) } if d.HasChange("self_service_portal") { diff --git a/internal/service/ec2/client_vpn_endpoint_test.go b/internal/service/ec2/client_vpn_endpoint_test.go index 586111d3c3b..bb5c5e69ce9 100644 --- a/internal/service/ec2/client_vpn_endpoint_test.go +++ b/internal/service/ec2/client_vpn_endpoint_test.go @@ -46,6 +46,7 @@ func TestAccEC2ClientVPNEndpoint_serial(t *testing.T) { "simpleAttributesUpdate": testAccClientVPNEndpoint_simpleAttributesUpdate, "selfServicePortal": testAccClientVPNEndpoint_selfServicePortal, "vpcNoSecurityGroups": testAccClientVPNEndpoint_vpcNoSecurityGroups, + "vpcSecurityGroups": testAccClientVPNEndpoint_vpcSecurityGroups, "basicDataSource": testAccClientVPNEndpointDataSource_basic, }, "AuthorizationRule": { @@ -658,6 +659,48 @@ func testAccClientVPNEndpoint_vpcNoSecurityGroups(t *testing.T) { }) } +func testAccClientVPNEndpoint_vpcSecurityGroups(t *testing.T) { + var v ec2.ClientVpnEndpoint + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_ec2_client_vpn_endpoint.test" + securityGroup1ResourceName := "aws_security_group.test1" + securityGroup2ResourceName := "aws_security_group.test2" + vpcResourceName := "aws_vpc.test" + + 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: testAccEc2ClientVpnEndpointConfigSecurityGroups(rName, 2), + Check: resource.ComposeTestCheckFunc( + testAccCheckClientVPNEndpointExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "security_group_ids.#", "2"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "security_group_ids.*", securityGroup1ResourceName, "id"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "security_group_ids.*", securityGroup2ResourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_id", vpcResourceName, "id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccEc2ClientVpnEndpointConfigSecurityGroups(rName, 1), + Check: resource.ComposeTestCheckFunc( + testAccCheckClientVPNEndpointExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "security_group_ids.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "security_group_ids.*", securityGroup1ResourceName, "id"), + resource.TestCheckResourceAttrPair(resourceName, "vpc_id", vpcResourceName, "id"), + ), + }, + }, + }) +} + func testAccPreCheckClientVPNSyncronize(t *testing.T) { sync.TestAccPreCheckSyncronize(t, testAccEc2ClientVpnEndpointSemaphore, "Client VPN") } From 8c8fa95696a033a6f51a5dfd30d60c13bd5d1772 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Wed, 2 Feb 2022 17:43:53 -0500 Subject: [PATCH 19/31] Fix terrafmt error. --- internal/service/ec2/client_vpn_endpoint_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/ec2/client_vpn_endpoint_test.go b/internal/service/ec2/client_vpn_endpoint_test.go index bb5c5e69ce9..92192538da6 100644 --- a/internal/service/ec2/client_vpn_endpoint_test.go +++ b/internal/service/ec2/client_vpn_endpoint_test.go @@ -1321,7 +1321,7 @@ func testAccEc2ClientVpnEndpointConfigSecurityGroups(rName string, nSecurityGrou fmt.Sprintf(` locals { security_group_count = %[2]d - security_group_ids = local.security_group_count == 0 ? null : (local.security_group_count == 1? [aws_security_group.test1.id] : [aws_security_group.test1.id, aws_security_group.test2.id]) + security_group_ids = local.security_group_count == 0 ? null : (local.security_group_count == 1 ? [aws_security_group.test1.id] : [aws_security_group.test1.id, aws_security_group.test2.id]) } resource "aws_ec2_client_vpn_endpoint" "test" { From 4365e8a27e6a1a89733456de3360cacec572c32e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 09:29:33 -0500 Subject: [PATCH 20/31] Standardize configuration generation function names in acceptance tests. --- .../ec2/client_vpn_network_association_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/service/ec2/client_vpn_network_association_test.go b/internal/service/ec2/client_vpn_network_association_test.go index 8989cc500d6..f08d0062e31 100644 --- a/internal/service/ec2/client_vpn_network_association_test.go +++ b/internal/service/ec2/client_vpn_network_association_test.go @@ -142,8 +142,8 @@ func testAccClientVPNNetworkAssociation_securityGroups(t *testing.T) { CheckDestroy: testAccCheckClientVPNNetworkAssociationDestroy, Steps: []resource.TestStep{ { - Config: testAccEc2ClientVpnNetworkAssociationTwoSecurityGroups(rName), - Check: resource.ComposeTestCheckFunc( + Config: testAccEc2ClientVpnNetworkAssociationConfigTwoSecurityGroups(rName), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckClientVPNNetworkAssociationExists(resourceName, &assoc1), testAccCheckDefaultSecurityGroupExists(securityGroup1ResourceName, &group11), testAccCheckDefaultSecurityGroupExists(securityGroup2ResourceName, &group12), @@ -159,8 +159,8 @@ func testAccClientVPNNetworkAssociation_securityGroups(t *testing.T) { ImportStateIdFunc: testAccClientVPNNetworkAssociationImportStateIdFunc(resourceName), }, { - Config: testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rName), - Check: resource.ComposeTestCheckFunc( + Config: testAccEc2ClientVpnNetworkAssociationConfigOneSecurityGroup(rName), + Check: resource.ComposeAggregateTestCheckFunc( testAccCheckClientVPNNetworkAssociationExists(resourceName, &assoc2), testAccCheckDefaultSecurityGroupExists(securityGroup1ResourceName, &group21), resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), @@ -301,7 +301,7 @@ resource "aws_ec2_client_vpn_network_association" "test2" { `) } -func testAccEc2ClientVpnNetworkAssociationTwoSecurityGroups(rName string) string { +func testAccEc2ClientVpnNetworkAssociationConfigTwoSecurityGroups(rName string) string { return acctest.ConfigCompose( testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), fmt.Sprintf(` @@ -331,7 +331,7 @@ resource "aws_security_group" "test2" { `, rName)) } -func testAccEc2ClientVpnNetworkAssociationOneSecurityGroup(rName string) string { +func testAccEc2ClientVpnNetworkAssociationConfigOneSecurityGroup(rName string) string { return acctest.ConfigCompose( testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), fmt.Sprintf(` From cd8ed966bd4d113aa33410547d003c1e92dcfb1e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 10:27:07 -0500 Subject: [PATCH 21/31] Add 'testAccClientVPNNetworkAssociation_securityGroupsOnEndpoint'. --- .../service/ec2/client_vpn_endpoint_test.go | 11 +++-- .../client_vpn_network_association_test.go | 46 ++++++++++++++++--- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/internal/service/ec2/client_vpn_endpoint_test.go b/internal/service/ec2/client_vpn_endpoint_test.go index 92192538da6..d8e9172a5c4 100644 --- a/internal/service/ec2/client_vpn_endpoint_test.go +++ b/internal/service/ec2/client_vpn_endpoint_test.go @@ -57,10 +57,11 @@ func TestAccEC2ClientVPNEndpoint_serial(t *testing.T) { "disappearsEndpoint": testAccClientVPNAuthorizationRule_Disappears_endpoint, }, "NetworkAssociation": { - "basic": testAccClientVPNNetworkAssociation_basic, - "multipleSubnets": testAccClientVPNNetworkAssociation_multipleSubnets, - "disappears": testAccClientVPNNetworkAssociation_disappears, - "securityGroups": testAccClientVPNNetworkAssociation_securityGroups, + "basic": testAccClientVPNNetworkAssociation_basic, + "multipleSubnets": testAccClientVPNNetworkAssociation_multipleSubnets, + "disappears": testAccClientVPNNetworkAssociation_disappears, + "securityGroups": testAccClientVPNNetworkAssociation_securityGroups, + "securityGroupsOnEndpoint": testAccClientVPNNetworkAssociation_securityGroupsOnEndpoint, }, "Route": { "basic": testAccClientVPNRoute_basic, @@ -806,7 +807,7 @@ func testAccEc2ClientVpnEndpointConfigVPCBase(rName string) string { acctest.ConfigAvailableAZsNoOptIn(), fmt.Sprintf(` resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" + cidr_block = "10.1.0.0/16" tags = { Name = %[1]q diff --git a/internal/service/ec2/client_vpn_network_association_test.go b/internal/service/ec2/client_vpn_network_association_test.go index f08d0062e31..0c889eb1a7f 100644 --- a/internal/service/ec2/client_vpn_network_association_test.go +++ b/internal/service/ec2/client_vpn_network_association_test.go @@ -171,6 +171,35 @@ func testAccClientVPNNetworkAssociation_securityGroups(t *testing.T) { }) } +func testAccClientVPNNetworkAssociation_securityGroupsOnEndpoint(t *testing.T) { + var assoc ec2.TargetNetwork + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_ec2_client_vpn_network_association.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheckClientVPNSyncronize(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckClientVPNNetworkAssociationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEc2ClientVpnNetworkAssociationConfigTwoSecurityGroupsOnEndpoint(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckClientVPNNetworkAssociationExists(resourceName, &assoc), + resource.TestCheckResourceAttr(resourceName, "security_groups.#", "2"), + resource.TestCheckResourceAttrSet(resourceName, "vpc_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccClientVPNNetworkAssociationImportStateIdFunc(resourceName), + }, + }, + }) +} + func testAccCheckClientVPNNetworkAssociationDestroy(s *terraform.State) error { conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn @@ -302,9 +331,7 @@ resource "aws_ec2_client_vpn_network_association" "test2" { } func testAccEc2ClientVpnNetworkAssociationConfigTwoSecurityGroups(rName string) string { - return acctest.ConfigCompose( - testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), - fmt.Sprintf(` + return acctest.ConfigCompose(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.test1.id @@ -332,9 +359,7 @@ resource "aws_security_group" "test2" { } func testAccEc2ClientVpnNetworkAssociationConfigOneSecurityGroup(rName string) string { - return acctest.ConfigCompose( - testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), - fmt.Sprintf(` + return acctest.ConfigCompose(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.test1.id @@ -360,3 +385,12 @@ resource "aws_security_group" "test2" { } `, rName)) } + +func testAccEc2ClientVpnNetworkAssociationConfigTwoSecurityGroupsOnEndpoint(rName string) string { + return acctest.ConfigCompose(testAccEc2ClientVpnEndpointConfigSecurityGroups(rName, 2), ` +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 +} +`) +} From 734ff7379d359bd30d4ba0e917f8da37347926c3 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 11:14:02 -0500 Subject: [PATCH 22/31] r/aws_ec2_client_vpn_route: Alphabetize attributes. --- internal/service/ec2/client_vpn_route.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/internal/service/ec2/client_vpn_route.go b/internal/service/ec2/client_vpn_route.go index 23a0c69531b..261faf37c8b 100644 --- a/internal/service/ec2/client_vpn_route.go +++ b/internal/service/ec2/client_vpn_route.go @@ -17,6 +17,7 @@ func ResourceClientVPNRoute() *schema.Resource { Create: resourceClientVPNRouteCreate, Read: resourceClientVPNRouteRead, Delete: resourceClientVPNRouteDelete, + Importer: &schema.ResourceImporter{ State: resourceClientVPNRouteImport, }, @@ -27,21 +28,16 @@ func ResourceClientVPNRoute() *schema.Resource { Required: true, ForceNew: true, }, - "destination_cidr_block": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: verify.ValidIPv4CIDRNetworkAddress, - }, "description": { Type: schema.TypeString, Optional: true, ForceNew: true, }, - "target_vpc_subnet_id": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + "destination_cidr_block": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: verify.ValidIPv4CIDRNetworkAddress, }, "origin": { Type: schema.TypeString, @@ -51,6 +47,11 @@ func ResourceClientVPNRoute() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "target_vpc_subnet_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, }, } } From c5c42e2663c52d72a188678d3f5f1eeae119dc6f Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 11:21:03 -0500 Subject: [PATCH 23/31] Rename client VPN route resource ID generate and parse functions. --- internal/service/ec2/client_vpn_route.go | 26 +++++++++++++++++++++--- internal/service/ec2/find.go | 2 +- internal/service/ec2/id.go | 19 ----------------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/internal/service/ec2/client_vpn_route.go b/internal/service/ec2/client_vpn_route.go index 261faf37c8b..8fe4cbafff0 100644 --- a/internal/service/ec2/client_vpn_route.go +++ b/internal/service/ec2/client_vpn_route.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" @@ -73,7 +74,7 @@ func resourceClientVPNRouteCreate(d *schema.ResourceData, meta interface{}) erro req.Description = aws.String(v.(string)) } - id := ClientVPNRouteCreateID(endpointID, targetSubnetID, destinationCidr) + id := ClientVPNRouteCreateResourceID(endpointID, targetSubnetID, destinationCidr) _, err := conn.CreateClientVpnRoute(req) @@ -142,7 +143,7 @@ func resourceClientVPNRouteDelete(d *schema.ResourceData, meta interface{}) erro } func deleteClientVpnRoute(conn *ec2.EC2, input *ec2.DeleteClientVpnRouteInput) error { - id := ClientVPNRouteCreateID( + id := ClientVPNRouteCreateResourceID( aws.StringValue(input.ClientVpnEndpointId), aws.StringValue(input.TargetVpcSubnetId), aws.StringValue(input.DestinationCidrBlock), @@ -162,7 +163,7 @@ func deleteClientVpnRoute(conn *ec2.EC2, input *ec2.DeleteClientVpnRouteInput) e } func resourceClientVPNRouteImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - endpointID, targetSubnetID, destinationCidr, err := ClientVPNRouteParseID(d.Id()) + endpointID, targetSubnetID, destinationCidr, err := ClientVPNRouteParseResourceID(d.Id()) if err != nil { return nil, err } @@ -173,3 +174,22 @@ func resourceClientVPNRouteImport(d *schema.ResourceData, meta interface{}) ([]* return []*schema.ResourceData{d}, nil } + +const clientVPNRouteIDSeparator = "," + +func ClientVPNRouteCreateResourceID(endpointID, targetSubnetID, destinationCIDR string) string { + parts := []string{endpointID, targetSubnetID, destinationCIDR} + id := strings.Join(parts, clientVPNRouteIDSeparator) + + return id +} + +func ClientVPNRouteParseResourceID(id string) (string, string, string, error) { + parts := strings.Split(id, clientVPNRouteIDSeparator) + + if len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != "" { + return parts[0], parts[1], parts[2], nil + } + + return "", "", "", fmt.Errorf("unexpected format for ID (%[1]s), expected EndpointID%[2]sTargetSubnetID%[2]sDestinationCIDRBlock", id, clientVPNRouteIDSeparator) +} diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index 7c5ecde82f0..ea151fed7dc 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -284,7 +284,7 @@ func FindClientVPNRoute(conn *ec2.EC2, endpointID, targetSubnetID, destinationCi } func FindClientVPNRouteByID(conn *ec2.EC2, routeID string) (*ec2.DescribeClientVpnRoutesOutput, error) { - endpointID, targetSubnetID, destinationCidr, err := ClientVPNRouteParseID(routeID) + endpointID, targetSubnetID, destinationCidr, err := ClientVPNRouteParseResourceID(routeID) if err != nil { return nil, err } diff --git a/internal/service/ec2/id.go b/internal/service/ec2/id.go index 93b68f3e9ce..7a5559610e9 100644 --- a/internal/service/ec2/id.go +++ b/internal/service/ec2/id.go @@ -7,25 +7,6 @@ import ( "github.com/hashicorp/terraform-provider-aws/internal/create" ) -const clientVpnRouteIDSeparator = "," - -func ClientVPNRouteCreateID(endpointID, targetSubnetID, destinationCidr string) string { - parts := []string{endpointID, targetSubnetID, destinationCidr} - id := strings.Join(parts, clientVpnRouteIDSeparator) - return id -} - -func ClientVPNRouteParseID(id string) (string, string, string, error) { - parts := strings.Split(id, clientVpnRouteIDSeparator) - if len(parts) == 3 && parts[0] != "" && parts[1] != "" && parts[2] != "" { - return parts[0], parts[1], parts[2], nil - } - - return "", "", "", - fmt.Errorf("unexpected format for ID (%q), expected endpoint-id"+clientVpnRouteIDSeparator+ - "target-subnet-id"+clientVpnRouteIDSeparator+"destination-cidr-block", id) -} - const managedPrefixListEntryIDSeparator = "," func ManagedPrefixListEntryCreateID(prefixListID, cidrBlock string) string { From 78ec2d61049e5343ec3bce00873696582dc8fd4c Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 11:33:52 -0500 Subject: [PATCH 24/31] r/aws_ec2_client_vpn_route: Tidy up resource Create and Delete. --- internal/service/ec2/client_vpn_route.go | 54 +++++++++++------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/internal/service/ec2/client_vpn_route.go b/internal/service/ec2/client_vpn_route.go index 8fe4cbafff0..960c7e90672 100644 --- a/internal/service/ec2/client_vpn_route.go +++ b/internal/service/ec2/client_vpn_route.go @@ -62,28 +62,28 @@ func resourceClientVPNRouteCreate(d *schema.ResourceData, meta interface{}) erro endpointID := d.Get("client_vpn_endpoint_id").(string) targetSubnetID := d.Get("target_vpc_subnet_id").(string) - destinationCidr := d.Get("destination_cidr_block").(string) - - req := &ec2.CreateClientVpnRouteInput{ + destinationCIDR := d.Get("destination_cidr_block").(string) + id := ClientVPNRouteCreateResourceID(endpointID, targetSubnetID, destinationCIDR) + input := &ec2.CreateClientVpnRouteInput{ ClientVpnEndpointId: aws.String(endpointID), - DestinationCidrBlock: aws.String(destinationCidr), + DestinationCidrBlock: aws.String(destinationCIDR), TargetVpcSubnetId: aws.String(targetSubnetID), } if v, ok := d.GetOk("description"); ok { - req.Description = aws.String(v.(string)) + input.Description = aws.String(v.(string)) } - id := ClientVPNRouteCreateResourceID(endpointID, targetSubnetID, destinationCidr) - - _, err := conn.CreateClientVpnRoute(req) + _, err := conn.CreateClientVpnRoute(input) if err != nil { - return fmt.Errorf("error creating client VPN route %q: %w", id, err) + return fmt.Errorf("error creating EC@ Client VPN Route (%s): %w", id, err) } d.SetId(id) + // TODO Wait for created. + return resourceClientVPNRouteRead(d, meta) } @@ -130,36 +130,32 @@ func resourceClientVPNRouteRead(d *schema.ResourceData, meta interface{}) error func resourceClientVPNRouteDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - err := deleteClientVpnRoute(conn, &ec2.DeleteClientVpnRouteInput{ - ClientVpnEndpointId: aws.String(d.Get("client_vpn_endpoint_id").(string)), - DestinationCidrBlock: aws.String(d.Get("destination_cidr_block").(string)), - TargetVpcSubnetId: aws.String(d.Get("target_vpc_subnet_id").(string)), - }) + endpointID, targetSubnetID, destinationCIDR, err := ClientVPNRouteParseResourceID(d.Id()) + if err != nil { - return fmt.Errorf("error deleting client VPN route %q: %w", d.Id(), err) + return err } - return nil -} - -func deleteClientVpnRoute(conn *ec2.EC2, input *ec2.DeleteClientVpnRouteInput) error { - id := ClientVPNRouteCreateResourceID( - aws.StringValue(input.ClientVpnEndpointId), - aws.StringValue(input.TargetVpcSubnetId), - aws.StringValue(input.DestinationCidrBlock), - ) + log.Printf("[DEBUG] Deleting EC2 Client VPN Route: %s", d.Id()) + _, err = conn.DeleteClientVpnRoute(&ec2.DeleteClientVpnRouteInput{ + ClientVpnEndpointId: aws.String(endpointID), + DestinationCidrBlock: aws.String(destinationCIDR), + TargetVpcSubnetId: aws.String(targetSubnetID), + }) - _, err := conn.DeleteClientVpnRoute(input) - if tfawserr.ErrMessageContains(err, ErrCodeInvalidClientVpnRouteNotFound, "") { + if tfawserr.ErrCodeEquals(err, ErrCodeInvalidClientVpnEndpointIdNotFound, ErrCodeInvalidClientVpnRouteNotFound) { return nil } + if err != nil { - return err + return fmt.Errorf("error deleting EC2 Client VPN Route (%s): %w", d.Id(), err) } - _, err = WaitClientVPNRouteDeleted(conn, id) + if _, err := WaitClientVPNRouteDeleted(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for EC2 Client VPN Route (%s) delete: %w", d.Id(), err) + } - return err + return nil } func resourceClientVPNRouteImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { From ecdd81319985ee9dd349d4b21cfa1392461e1914 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 12:01:18 -0500 Subject: [PATCH 25/31] r/aws_ec2_client_vpn_route: Tidy up resource Read. --- internal/service/ec2/client_vpn_route.go | 60 ++++++------------ internal/service/ec2/client_vpn_route_test.go | 43 ++++++++----- internal/service/ec2/find.go | 62 +++++++++++++++---- internal/service/ec2/status.go | 32 +++------- internal/service/ec2/wait.go | 26 +++++++- 5 files changed, 131 insertions(+), 92 deletions(-) diff --git a/internal/service/ec2/client_vpn_route.go b/internal/service/ec2/client_vpn_route.go index 960c7e90672..e691c14b02b 100644 --- a/internal/service/ec2/client_vpn_route.go +++ b/internal/service/ec2/client_vpn_route.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" ) @@ -18,9 +19,8 @@ func ResourceClientVPNRoute() *schema.Resource { Create: resourceClientVPNRouteCreate, Read: resourceClientVPNRouteRead, Delete: resourceClientVPNRouteDelete, - Importer: &schema.ResourceImporter{ - State: resourceClientVPNRouteImport, + State: schema.ImportStatePassthrough, }, Schema: map[string]*schema.Schema{ @@ -44,15 +44,15 @@ func ResourceClientVPNRoute() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "type": { - Type: schema.TypeString, - Computed: true, - }, "target_vpc_subnet_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, + "type": { + Type: schema.TypeString, + Computed: true, + }, }, } } @@ -77,12 +77,14 @@ func resourceClientVPNRouteCreate(d *schema.ResourceData, meta interface{}) erro _, err := conn.CreateClientVpnRoute(input) if err != nil { - return fmt.Errorf("error creating EC@ Client VPN Route (%s): %w", id, err) + return fmt.Errorf("error creating EC2 Client VPN Route (%s): %w", id, err) } d.SetId(id) - // TODO Wait for created. + if _, err := WaitClientVPNRouteCreated(conn, endpointID, targetSubnetID, destinationCIDR); err != nil { + return fmt.Errorf("error waiting for EC2 Client VPN Route (%s) create: %w", d.Id(), err) + } return resourceClientVPNRouteRead(d, meta) } @@ -90,38 +92,29 @@ func resourceClientVPNRouteCreate(d *schema.ResourceData, meta interface{}) erro func resourceClientVPNRouteRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*conns.AWSClient).EC2Conn - resp, err := FindClientVPNRoute(conn, - d.Get("client_vpn_endpoint_id").(string), - d.Get("target_vpc_subnet_id").(string), - d.Get("destination_cidr_block").(string), - ) - - if tfawserr.ErrMessageContains(err, ErrCodeInvalidClientVpnRouteNotFound, "") { - log.Printf("[WARN] EC2 Client VPN Route (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil - } + endpointID, targetSubnetID, destinationCIDR, err := ClientVPNRouteParseResourceID(d.Id()) if err != nil { - return fmt.Errorf("error reading client VPN route (%s): %w", d.Id(), err) + return err } - if resp == nil || len(resp.Routes) == 0 || resp.Routes[0] == nil { + route, err := FindClientVPNRouteByThreePartKey(conn, endpointID, targetSubnetID, destinationCIDR) + + if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] EC2 Client VPN Route (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - if len(resp.Routes) > 1 { - return fmt.Errorf("internal error: found %d results for Client VPN route (%s) status, need 1", len(resp.Routes), d.Id()) + if err != nil { + return fmt.Errorf("error reading EC2 Client VPN Route (%s): %w", d.Id(), err) } - route := resp.Routes[0] d.Set("client_vpn_endpoint_id", route.ClientVpnEndpointId) - d.Set("destination_cidr_block", route.DestinationCidr) d.Set("description", route.Description) - d.Set("target_vpc_subnet_id", route.TargetSubnet) + d.Set("destination_cidr_block", route.DestinationCidr) d.Set("origin", route.Origin) + d.Set("target_vpc_subnet_id", route.TargetSubnet) d.Set("type", route.Type) return nil @@ -151,26 +144,13 @@ func resourceClientVPNRouteDelete(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("error deleting EC2 Client VPN Route (%s): %w", d.Id(), err) } - if _, err := WaitClientVPNRouteDeleted(conn, d.Id()); err != nil { + if _, err := WaitClientVPNRouteDeleted(conn, endpointID, targetSubnetID, destinationCIDR); err != nil { return fmt.Errorf("error waiting for EC2 Client VPN Route (%s) delete: %w", d.Id(), err) } return nil } -func resourceClientVPNRouteImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - endpointID, targetSubnetID, destinationCidr, err := ClientVPNRouteParseResourceID(d.Id()) - if err != nil { - return nil, err - } - - d.Set("client_vpn_endpoint_id", endpointID) - d.Set("target_vpc_subnet_id", targetSubnetID) - d.Set("destination_cidr_block", destinationCidr) - - return []*schema.ResourceData{d}, nil -} - const clientVPNRouteIDSeparator = "," func ClientVPNRouteCreateResourceID(endpointID, targetSubnetID, destinationCIDR string) string { diff --git a/internal/service/ec2/client_vpn_route_test.go b/internal/service/ec2/client_vpn_route_test.go index 08870a0bd35..f4269c780a7 100644 --- a/internal/service/ec2/client_vpn_route_test.go +++ b/internal/service/ec2/client_vpn_route_test.go @@ -5,13 +5,13 @@ import ( "testing" "github.com/aws/aws-sdk-go/service/ec2" - "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" "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 testAccClientVPNRoute_basic(t *testing.T) { @@ -113,41 +113,56 @@ func testAccCheckClientVPNRouteDestroy(s *terraform.State) error { continue } - _, err := tfec2.FindClientVPNRouteByID(conn, rs.Primary.ID) - if err == nil { - return fmt.Errorf("Client VPN route (%s) still exists", rs.Primary.ID) + endpointID, targetSubnetID, destinationCIDR, err := tfec2.ClientVPNRouteParseResourceID(rs.Primary.ID) + + if err != nil { + return err } - if tfawserr.ErrMessageContains(err, tfec2.ErrCodeInvalidClientVpnRouteNotFound, "") { + + _, err = tfec2.FindClientVPNRouteByThreePartKey(conn, endpointID, targetSubnetID, destinationCIDR) + + if tfresource.NotFound(err) { continue } + + if err != nil { + return err + } + + return fmt.Errorf("EC2 Client VPN Route %s still exists", rs.Primary.ID) } return nil } -func testAccCheckClientVPNRouteExists(name string, route *ec2.ClientVpnRoute) resource.TestCheckFunc { +func testAccCheckClientVPNRouteExists(name string, v *ec2.ClientVpnRoute) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { return fmt.Errorf("Not found: %s", name) } + if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") + return fmt.Errorf("No EC2 Client VPN Route ID is set") } - conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn + endpointID, targetSubnetID, destinationCIDR, err := tfec2.ClientVPNRouteParseResourceID(rs.Primary.ID) - resp, err := tfec2.FindClientVPNRouteByID(conn, rs.Primary.ID) if err != nil { - return fmt.Errorf("Error reading Client VPN route (%s): %w", rs.Primary.ID, err) + return err } - if resp != nil || len(resp.Routes) == 1 || resp.Routes[0] != nil { - *route = *resp.Routes[0] - return nil + conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn + + output, err := tfec2.FindClientVPNRouteByThreePartKey(conn, endpointID, targetSubnetID, destinationCIDR) + + if err != nil { + return err } - return fmt.Errorf("Client VPN route (%s) not found", rs.Primary.ID) + *v = *output + + return nil } } diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index ea151fed7dc..d176a9676c1 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -269,27 +269,67 @@ func FindClientVPNNetworkAssociationByIDs(conn *ec2.EC2, associationID, endpoint return output, nil } -func FindClientVPNRoute(conn *ec2.EC2, endpointID, targetSubnetID, destinationCidr string) (*ec2.DescribeClientVpnRoutesOutput, error) { - filters := map[string]string{ - "target-subnet": targetSubnetID, - "destination-cidr": destinationCidr, +func FindClientVPNRoute(conn *ec2.EC2, input *ec2.DescribeClientVpnRoutesInput) (*ec2.ClientVpnRoute, error) { + output, err := FindClientVPNRoutes(conn, input) + + if err != nil { + return nil, err } - input := &ec2.DescribeClientVpnRoutesInput{ - ClientVpnEndpointId: aws.String(endpointID), - Filters: BuildAttributeFilterList(filters), + if len(output) == 0 || output[0] == nil || output[0].Status == nil { + return nil, tfresource.NewEmptyResultError(input) } - return conn.DescribeClientVpnRoutes(input) + if count := len(output); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output[0], nil } -func FindClientVPNRouteByID(conn *ec2.EC2, routeID string) (*ec2.DescribeClientVpnRoutesOutput, error) { - endpointID, targetSubnetID, destinationCidr, err := ClientVPNRouteParseResourceID(routeID) +func FindClientVPNRoutes(conn *ec2.EC2, input *ec2.DescribeClientVpnRoutesInput) ([]*ec2.ClientVpnRoute, error) { + var output []*ec2.ClientVpnRoute + + err := conn.DescribeClientVpnRoutesPages(input, func(page *ec2.DescribeClientVpnRoutesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.Routes { + if v == nil { + continue + } + + output = append(output, v) + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, ErrCodeInvalidClientVpnEndpointIdNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + if err != nil { return nil, err } - return FindClientVPNRoute(conn, endpointID, targetSubnetID, destinationCidr) + return output, nil +} + +func FindClientVPNRouteByThreePartKey(conn *ec2.EC2, endpointID, targetSubnetID, destinationCIDR string) (*ec2.ClientVpnRoute, error) { + input := &ec2.DescribeClientVpnRoutesInput{ + ClientVpnEndpointId: aws.String(endpointID), + Filters: BuildAttributeFilterList(map[string]string{ + "destination-cidr": destinationCIDR, + "target-subnet": targetSubnetID, + }), + } + + return FindClientVPNRoute(conn, input) } func FindCOIPPools(conn *ec2.EC2, input *ec2.DescribeCoipPoolsInput) ([]*ec2.CoipPool, error) { diff --git a/internal/service/ec2/status.go b/internal/service/ec2/status.go index 4c829ca0837..fb736cdcad9 100644 --- a/internal/service/ec2/status.go +++ b/internal/service/ec2/status.go @@ -141,37 +141,19 @@ func StatusClientVPNNetworkAssociation(conn *ec2.EC2, associationID, endpointID } } -const ( - ClientVPNRouteStatusNotFound = "NotFound" - - ClientVPNRouteStatusUnknown = "Unknown" -) - -// StatusClientVPNRoute fetches the Client VPN route and its Status -func StatusClientVPNRoute(conn *ec2.EC2, routeID string) resource.StateRefreshFunc { +func StatusClientVPNRoute(conn *ec2.EC2, endpointID, targetSubnetID, destinationCIDR string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - result, err := FindClientVPNRouteByID(conn, routeID) - if tfawserr.ErrCodeEquals(err, ErrCodeInvalidClientVpnRouteNotFound) { - return nil, ClientVPNRouteStatusNotFound, nil - } - if err != nil { - return nil, ClientVPNRouteStatusUnknown, err - } + output, err := FindClientVPNRouteByThreePartKey(conn, endpointID, targetSubnetID, destinationCIDR) - if result == nil || len(result.Routes) == 0 || result.Routes[0] == nil { - return nil, ClientVPNRouteStatusNotFound, nil - } - - if len(result.Routes) > 1 { - return nil, ClientVPNRouteStatusUnknown, fmt.Errorf("internal error: found %d results for Client VPN route (%s) status, need 1", len(result.Routes), routeID) + if tfresource.NotFound(err) { + return nil, "", nil } - rule := result.Routes[0] - if rule.Status == nil || rule.Status.Code == nil { - return rule, ClientVPNRouteStatusUnknown, nil + if err != nil { + return nil, "", err } - return rule, aws.StringValue(rule.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 e09605eb183..f6757628c57 100644 --- a/internal/service/ec2/wait.go +++ b/internal/service/ec2/wait.go @@ -250,20 +250,42 @@ func WaitClientVPNNetworkAssociationDeleted(conn *ec2.EC2, associationID, endpoi } const ( + ClientVPNRouteCreatedTimeout = 1 * time.Minute ClientVPNRouteDeletedTimeout = 1 * time.Minute ) -func WaitClientVPNRouteDeleted(conn *ec2.EC2, routeID string) (*ec2.ClientVpnRoute, error) { +func WaitClientVPNRouteCreated(conn *ec2.EC2, endpointID, targetSubnetID, destinationCIDR string) (*ec2.ClientVpnRoute, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ec2.ClientVpnRouteStatusCodeCreating}, + Target: []string{ec2.ClientVpnRouteStatusCodeActive}, + Refresh: StatusClientVPNRoute(conn, endpointID, targetSubnetID, destinationCIDR), + Timeout: ClientVPNRouteCreatedTimeout, + } + + outputRaw, err := stateConf.WaitForState() + + if output, ok := outputRaw.(*ec2.ClientVpnRoute); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.Status.Message))) + + return output, err + } + + return nil, err +} + +func WaitClientVPNRouteDeleted(conn *ec2.EC2, endpointID, targetSubnetID, destinationCIDR string) (*ec2.ClientVpnRoute, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ec2.ClientVpnRouteStatusCodeActive, ec2.ClientVpnRouteStatusCodeDeleting}, Target: []string{}, - Refresh: StatusClientVPNRoute(conn, routeID), + Refresh: StatusClientVPNRoute(conn, endpointID, targetSubnetID, destinationCIDR), Timeout: ClientVPNRouteDeletedTimeout, } outputRaw, err := stateConf.WaitForState() if output, ok := outputRaw.(*ec2.ClientVpnRoute); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.Status.Message))) + return output, err } From f7a0837e19c3dfcdb818031a028d308ef1a5064b Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 13:04:28 -0500 Subject: [PATCH 26/31] r/aws_ec2_client_vpn_route: Tidy up acceptance tests. Acceptance test output: % make testacc TESTARGS='-run=TestAccEC2ClientVPNEndpoint_serial/Route' 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/Route -timeout 180m === RUN TestAccEC2ClientVPNEndpoint_serial === PAUSE TestAccEC2ClientVPNEndpoint_serial === CONT TestAccEC2ClientVPNEndpoint_serial === RUN TestAccEC2ClientVPNEndpoint_serial/Route_basic === PAUSE TestAccEC2ClientVPNEndpoint_serial/Route_basic === RUN TestAccEC2ClientVPNEndpoint_serial/Route_description === PAUSE TestAccEC2ClientVPNEndpoint_serial/Route_description === RUN TestAccEC2ClientVPNEndpoint_serial/Route_disappears === PAUSE TestAccEC2ClientVPNEndpoint_serial/Route_disappears === CONT TestAccEC2ClientVPNEndpoint_serial/Route_basic === CONT TestAccEC2ClientVPNEndpoint_serial/Route_disappears === CONT TestAccEC2ClientVPNEndpoint_serial/Route_description --- PASS: TestAccEC2ClientVPNEndpoint_serial (0.38s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/Route_disappears (729.08s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/Route_description (744.76s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/Route_basic (795.42s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 799.404s --- .../client_vpn_network_association_test.go | 10 +- internal/service/ec2/client_vpn_route_test.go | 160 ++++++------------ 2 files changed, 55 insertions(+), 115 deletions(-) diff --git a/internal/service/ec2/client_vpn_network_association_test.go b/internal/service/ec2/client_vpn_network_association_test.go index 0c889eb1a7f..163bde2d146 100644 --- a/internal/service/ec2/client_vpn_network_association_test.go +++ b/internal/service/ec2/client_vpn_network_association_test.go @@ -266,7 +266,7 @@ func testAccClientVPNNetworkAssociationImportStateIdFunc(resourceName string) re } } -func testAccEc2ClientVpnNetworkAssociationBaseConfig(rName string) string { +func testAccEc2ClientVpnNetworkAssociationConfigBase(rName string) string { return acctest.ConfigCompose( testAccEc2ClientVpnEndpointConfig(rName), acctest.ConfigAvailableAZsNoOptInDefaultExclude(), @@ -308,7 +308,7 @@ resource "aws_subnet" "test2" { } func testAccEc2ClientVpnNetworkAssociationConfigBasic(rName string) string { - return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), ` + return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationConfigBase(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 @@ -317,7 +317,7 @@ resource "aws_ec2_client_vpn_network_association" "test" { } func testAccEc2ClientVpnNetworkAssociationConfigMultipleSubnets(rName string) string { - return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), ` + return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationConfigBase(rName), ` resource "aws_ec2_client_vpn_network_association" "test1" { client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id subnet_id = aws_subnet.test1.id @@ -331,7 +331,7 @@ resource "aws_ec2_client_vpn_network_association" "test2" { } func testAccEc2ClientVpnNetworkAssociationConfigTwoSecurityGroups(rName string) string { - return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationConfigBase(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.test1.id @@ -359,7 +359,7 @@ resource "aws_security_group" "test2" { } func testAccEc2ClientVpnNetworkAssociationConfigOneSecurityGroup(rName string) string { - return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationBaseConfig(rName), fmt.Sprintf(` + return acctest.ConfigCompose(testAccEc2ClientVpnNetworkAssociationConfigBase(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.test1.id diff --git a/internal/service/ec2/client_vpn_route_test.go b/internal/service/ec2/client_vpn_route_test.go index f4269c780a7..e5d68bca7f7 100644 --- a/internal/service/ec2/client_vpn_route_test.go +++ b/internal/service/ec2/client_vpn_route_test.go @@ -16,8 +16,7 @@ import ( func testAccClientVPNRoute_basic(t *testing.T) { var v ec2.ClientVpnRoute - rStr := sdkacctest.RandString(5) - + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ec2_client_vpn_route.test" endpointResourceName := "aws_ec2_client_vpn_endpoint.test" subnetResourceName := "aws_subnet.test.0" @@ -29,14 +28,14 @@ func testAccClientVPNRoute_basic(t *testing.T) { CheckDestroy: testAccCheckClientVPNRouteDestroy, Steps: []resource.TestStep{ { - Config: testAccEc2ClientVpnRouteConfigBasic(rStr), + Config: testAccEc2ClientVpnRouteConfigBasic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClientVPNRouteExists(resourceName, &v), resource.TestCheckResourceAttrPair(resourceName, "client_vpn_endpoint_id", endpointResourceName, "id"), - resource.TestCheckResourceAttrPair(resourceName, "target_vpc_subnet_id", subnetResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "destination_cidr_block", "0.0.0.0/0"), resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "destination_cidr_block", "0.0.0.0/0"), resource.TestCheckResourceAttr(resourceName, "origin", "add-route"), + resource.TestCheckResourceAttrPair(resourceName, "target_vpc_subnet_id", subnetResourceName, "id"), resource.TestCheckResourceAttr(resourceName, "type", "Nat"), ), }, @@ -49,13 +48,10 @@ func testAccClientVPNRoute_basic(t *testing.T) { }) } -func testAccClientVPNRoute_description(t *testing.T) { +func testAccClientVPNRoute_disappears(t *testing.T) { var v ec2.ClientVpnRoute - rStr := sdkacctest.RandString(5) - + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ec2_client_vpn_route.test" - endpointResourceName := "aws_ec2_client_vpn_endpoint.test" - subnetResourceName := "aws_subnet.test.0" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheckClientVPNSyncronize(t); acctest.PreCheck(t) }, @@ -64,27 +60,20 @@ func testAccClientVPNRoute_description(t *testing.T) { CheckDestroy: testAccCheckClientVPNRouteDestroy, Steps: []resource.TestStep{ { - Config: testAccEc2ClientVpnRouteConfigDescription(rStr), + Config: testAccEc2ClientVpnRouteConfigBasic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClientVPNRouteExists(resourceName, &v), - resource.TestCheckResourceAttrPair(resourceName, "client_vpn_endpoint_id", endpointResourceName, "id"), - resource.TestCheckResourceAttrPair(resourceName, "target_vpc_subnet_id", subnetResourceName, "id"), - resource.TestCheckResourceAttr(resourceName, "description", "test client VPN route"), + acctest.CheckResourceDisappears(acctest.Provider, tfec2.ResourceClientVPNRoute(), resourceName), ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, + ExpectNonEmptyPlan: true, }, }, }) } -func testAccClientVPNRoute_disappears(t *testing.T) { +func testAccClientVPNRoute_description(t *testing.T) { var v ec2.ClientVpnRoute - rStr := sdkacctest.RandString(5) - + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_ec2_client_vpn_route.test" resource.ParallelTest(t, resource.TestCase{ @@ -94,12 +83,16 @@ func testAccClientVPNRoute_disappears(t *testing.T) { CheckDestroy: testAccCheckClientVPNRouteDestroy, Steps: []resource.TestStep{ { - Config: testAccEc2ClientVpnRouteConfigBasic(rStr), + Config: testAccEc2ClientVpnRouteConfigDescription(rName), Check: resource.ComposeTestCheckFunc( testAccCheckClientVPNRouteExists(resourceName, &v), - acctest.CheckResourceDisappears(acctest.Provider, tfec2.ResourceClientVPNRoute(), resourceName), + resource.TestCheckResourceAttr(resourceName, "description", "test client VPN route"), ), - ExpectNonEmptyPlan: true, + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) @@ -166,88 +159,16 @@ func testAccCheckClientVPNRouteExists(name string, v *ec2.ClientVpnRoute) resour } } -func testAccEc2ClientVpnRouteConfigBasic(rName string) string { +func testAccEc2ClientVpnRouteConfigBase(rName string, subnetCount int) string { return acctest.ConfigCompose( - testAccEc2ClientVpnRouteVpcBase(rName, 1), - testAccEc2ClientVpnRouteAcmCertificateBase(), + testAccEc2ClientVpnEndpointConfig(rName), + acctest.ConfigAvailableAZsNoOptInDefaultExclude(), fmt.Sprintf(` -resource "aws_ec2_client_vpn_route" "test" { - client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id - destination_cidr_block = "0.0.0.0/0" - target_vpc_subnet_id = aws_subnet.test[0].id - - depends_on = [ - aws_ec2_client_vpn_network_association.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 -} - -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 testAccEc2ClientVpnRouteConfigDescription(rName string) string { - return acctest.ConfigCompose( - testAccEc2ClientVpnRouteVpcBase(rName, 1), - testAccEc2ClientVpnRouteAcmCertificateBase(), - fmt.Sprintf(` -resource "aws_ec2_client_vpn_route" "test" { - client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.test.id - destination_cidr_block = "0.0.0.0/0" - target_vpc_subnet_id = aws_subnet.test[0].id - description = "test client VPN route" - - depends_on = [ - aws_ec2_client_vpn_network_association.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 -} - -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 testAccEc2ClientVpnRouteVpcBase(rName string, subnetCount int) 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" + Name = %[1]q } } @@ -259,20 +180,39 @@ resource "aws_subnet" "test" { map_public_ip_on_launch = true tags = { - Name = "tf-acc-subnet-%[1]s" + Name = %[1]q } } `, rName, subnetCount)) } -func testAccEc2ClientVpnRouteAcmCertificateBase() string { - key := acctest.TLSRSAPrivateKeyPEM(2048) - certificate := acctest.TLSRSAX509SelfSignedCertificatePEM(key, "example.com") +func testAccEc2ClientVpnRouteConfigBasic(rName string) string { + return acctest.ConfigCompose(testAccEc2ClientVpnRouteConfigBase(rName, 1), ` +resource "aws_ec2_client_vpn_route" "test" { + client_vpn_endpoint_id = aws_ec2_client_vpn_network_association.test.client_vpn_endpoint_id + destination_cidr_block = "0.0.0.0/0" + target_vpc_subnet_id = aws_subnet.test[0].id +} - return fmt.Sprintf(` -resource "aws_acm_certificate" "test" { - certificate_body = "%[1]s" - private_key = "%[2]s" +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 +} +`) +} + +func testAccEc2ClientVpnRouteConfigDescription(rName string) string { + return acctest.ConfigCompose(testAccEc2ClientVpnRouteConfigBase(rName, 1), ` +resource "aws_ec2_client_vpn_route" "test" { + client_vpn_endpoint_id = aws_ec2_client_vpn_network_association.test.client_vpn_endpoint_id + destination_cidr_block = "0.0.0.0/0" + target_vpc_subnet_id = aws_subnet.test[0].id + description = "test client VPN route" +} + +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 } -`, acctest.TLSPEMEscapeNewlines(certificate), acctest.TLSPEMEscapeNewlines(key)) +`) } From 30367c86a0bede1638cdc656c8525b1f80db36a2 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 13:48:32 -0500 Subject: [PATCH 27/31] r/aws_ec2_client_vpn_route: Add custom 'timeouts' block. --- .changelog/22911.txt | 4 ++++ internal/service/ec2/client_vpn_route.go | 10 ++++++++-- internal/service/ec2/wait.go | 8 ++++---- website/docs/r/ec2_client_vpn_route.html.markdown | 7 +++++++ 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/.changelog/22911.txt b/.changelog/22911.txt index 674d67ced30..0e90e02b8f0 100644 --- a/.changelog/22911.txt +++ b/.changelog/22911.txt @@ -8,4 +8,8 @@ resource/aws_ec2_client_vpn_endpoint: Add `security_group_ids` and `vpc_id` argu ```release-note:enhancement data-source/aws_ec2_client_vpn_endpoint: Add `security_group_ids` and `vpc_id` attributes +``` + +```release-note:note +resource/aws_ec2_client_vpn_route: Add [custom `timeouts`](https://www.terraform.io/docs/language/resources/syntax.html#operation-timeouts) block ``` \ No newline at end of file diff --git a/internal/service/ec2/client_vpn_route.go b/internal/service/ec2/client_vpn_route.go index e691c14b02b..6ab8a61f678 100644 --- a/internal/service/ec2/client_vpn_route.go +++ b/internal/service/ec2/client_vpn_route.go @@ -23,6 +23,11 @@ func ResourceClientVPNRoute() *schema.Resource { State: schema.ImportStatePassthrough, }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(ClientVPNRouteCreatedTimeout), + Delete: schema.DefaultTimeout(ClientVPNRouteDeletedTimeout), + }, + Schema: map[string]*schema.Schema{ "client_vpn_endpoint_id": { Type: schema.TypeString, @@ -74,6 +79,7 @@ func resourceClientVPNRouteCreate(d *schema.ResourceData, meta interface{}) erro input.Description = aws.String(v.(string)) } + log.Printf("[DEBUG] Creating EC2 Client VPN Route: %s", input) _, err := conn.CreateClientVpnRoute(input) if err != nil { @@ -82,7 +88,7 @@ func resourceClientVPNRouteCreate(d *schema.ResourceData, meta interface{}) erro d.SetId(id) - if _, err := WaitClientVPNRouteCreated(conn, endpointID, targetSubnetID, destinationCIDR); err != nil { + if _, err := WaitClientVPNRouteCreated(conn, endpointID, targetSubnetID, destinationCIDR, d.Timeout(schema.TimeoutCreate)); err != nil { return fmt.Errorf("error waiting for EC2 Client VPN Route (%s) create: %w", d.Id(), err) } @@ -144,7 +150,7 @@ func resourceClientVPNRouteDelete(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("error deleting EC2 Client VPN Route (%s): %w", d.Id(), err) } - if _, err := WaitClientVPNRouteDeleted(conn, endpointID, targetSubnetID, destinationCIDR); err != nil { + if _, err := WaitClientVPNRouteDeleted(conn, endpointID, targetSubnetID, destinationCIDR, d.Timeout(schema.TimeoutDelete)); err != nil { return fmt.Errorf("error waiting for EC2 Client VPN Route (%s) delete: %w", d.Id(), err) } diff --git a/internal/service/ec2/wait.go b/internal/service/ec2/wait.go index f6757628c57..987af95a044 100644 --- a/internal/service/ec2/wait.go +++ b/internal/service/ec2/wait.go @@ -254,12 +254,12 @@ const ( ClientVPNRouteDeletedTimeout = 1 * time.Minute ) -func WaitClientVPNRouteCreated(conn *ec2.EC2, endpointID, targetSubnetID, destinationCIDR string) (*ec2.ClientVpnRoute, error) { +func WaitClientVPNRouteCreated(conn *ec2.EC2, endpointID, targetSubnetID, destinationCIDR string, timeout time.Duration) (*ec2.ClientVpnRoute, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ec2.ClientVpnRouteStatusCodeCreating}, Target: []string{ec2.ClientVpnRouteStatusCodeActive}, Refresh: StatusClientVPNRoute(conn, endpointID, targetSubnetID, destinationCIDR), - Timeout: ClientVPNRouteCreatedTimeout, + Timeout: timeout, } outputRaw, err := stateConf.WaitForState() @@ -273,12 +273,12 @@ func WaitClientVPNRouteCreated(conn *ec2.EC2, endpointID, targetSubnetID, destin return nil, err } -func WaitClientVPNRouteDeleted(conn *ec2.EC2, endpointID, targetSubnetID, destinationCIDR string) (*ec2.ClientVpnRoute, error) { +func WaitClientVPNRouteDeleted(conn *ec2.EC2, endpointID, targetSubnetID, destinationCIDR string, timeout time.Duration) (*ec2.ClientVpnRoute, error) { stateConf := &resource.StateChangeConf{ Pending: []string{ec2.ClientVpnRouteStatusCodeActive, ec2.ClientVpnRouteStatusCodeDeleting}, Target: []string{}, Refresh: StatusClientVPNRoute(conn, endpointID, targetSubnetID, destinationCIDR), - Timeout: ClientVPNRouteDeletedTimeout, + Timeout: timeout, } outputRaw, err := stateConf.WaitForState() diff --git a/website/docs/r/ec2_client_vpn_route.html.markdown b/website/docs/r/ec2_client_vpn_route.html.markdown index 3effe220809..0264f2ae595 100644 --- a/website/docs/r/ec2_client_vpn_route.html.markdown +++ b/website/docs/r/ec2_client_vpn_route.html.markdown @@ -58,6 +58,13 @@ In addition to all arguments above, the following attributes are exported: * `origin` - Indicates how the Client VPN route was added. Will be `add-route` for routes created by this resource. * `type` - The type of the route. +## Timeouts + +`aws_ec2_client_vpn_route` provides the following [Timeouts](https://www.terraform.io/docs/configuration/blocks/resources/syntax.html#operation-timeouts) configuration options: + +- `create` - (Default `1 minute`) Used for route creation +- `delete` - (Default `1 minute`) Used for route deletion + ## Import AWS Client VPN routes can be imported using the endpoint ID, target subnet ID, and destination CIDR block. All values are separated by a `,`. From 23d18cfdcd5bf431c450ba4da468b93a4ccf6f29 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 13:57:15 -0500 Subject: [PATCH 28/31] r/aws_ec2_client_vpn_route: Retry route creation when 'InvalidClientVpnActiveAssociationNotFound' is returned. --- internal/service/ec2/client_vpn_route.go | 4 +++- internal/service/ec2/errors.go | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/service/ec2/client_vpn_route.go b/internal/service/ec2/client_vpn_route.go index 6ab8a61f678..56b8a1d910b 100644 --- a/internal/service/ec2/client_vpn_route.go +++ b/internal/service/ec2/client_vpn_route.go @@ -80,7 +80,9 @@ func resourceClientVPNRouteCreate(d *schema.ResourceData, meta interface{}) erro } log.Printf("[DEBUG] Creating EC2 Client VPN Route: %s", input) - _, err := conn.CreateClientVpnRoute(input) + _, err := tfresource.RetryWhenAWSErrCodeEquals(PropagationTimeout, func() (interface{}, error) { + return conn.CreateClientVpnRoute(input) + }, ErrCodeInvalidClientVpnActiveAssociationNotFound) if err != nil { return fmt.Errorf("error creating EC2 Client VPN Route (%s): %w", id, err) diff --git a/internal/service/ec2/errors.go b/internal/service/ec2/errors.go index cc720fccf69..b79cd3028a6 100644 --- a/internal/service/ec2/errors.go +++ b/internal/service/ec2/errors.go @@ -21,6 +21,7 @@ const ( ErrCodeInvalidAssociationIDNotFound = "InvalidAssociationID.NotFound" ErrCodeInvalidAttachmentIDNotFound = "InvalidAttachmentID.NotFound" ErrCodeInvalidCarrierGatewayIDNotFound = "InvalidCarrierGatewayID.NotFound" + ErrCodeInvalidClientVpnActiveAssociationNotFound = "InvalidClientVpnActiveAssociationNotFound" ErrCodeInvalidClientVpnAssociationIdNotFound = "InvalidClientVpnAssociationIdNotFound" ErrCodeInvalidClientVpnAuthorizationRuleNotFound = "InvalidClientVpnEndpointAuthorizationRuleNotFound" ErrCodeInvalidClientVpnEndpointIdNotFound = "InvalidClientVpnEndpointId.NotFound" From 9150caac264066ff3cf0f01dd17c306f65b537ee Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 14:14:48 -0500 Subject: [PATCH 29/31] r/aws_ec2_client_vpn_route: Retry route creation and deletion when 'ConcurrentMutationLimitExceeded' is returned. --- internal/conns/conns.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/internal/conns/conns.go b/internal/conns/conns.go index dda1293923b..d0ce21b612a 100644 --- a/internal/conns/conns.go +++ b/internal/conns/conns.go @@ -1718,26 +1718,29 @@ func (c *Config) Client() (interface{}, error) { }) client.EC2Conn.Handlers.Retry.PushBack(func(r *request.Request) { - if r.Operation.Name == "CreateClientVpnEndpoint" { - if tfawserr.ErrMessageContains(r.Error, "OperationNotPermitted", "Endpoint cannot be created while another endpoint is being created") { + switch err := r.Error; r.Operation.Name { + case "AttachVpnGateway", "DetachVpnGateway": + if tfawserr.ErrMessageContains(err, "InvalidParameterValue", "This call cannot be completed because there are pending VPNs or Virtual Interfaces") { r.Retryable = aws.Bool(true) } - } - if r.Operation.Name == "CreateVpnConnection" { - if tfawserr.ErrMessageContains(r.Error, "VpnConnectionLimitExceeded", "maximum number of mutating objects has been reached") { + case "CreateClientVpnEndpoint": + if tfawserr.ErrMessageContains(err, "OperationNotPermitted", "Endpoint cannot be created while another endpoint is being created") { r.Retryable = aws.Bool(true) } - } - if r.Operation.Name == "CreateVpnGateway" { - if tfawserr.ErrMessageContains(r.Error, "VpnGatewayLimitExceeded", "maximum number of mutating objects has been reached") { + case "CreateClientVpnRoute", "DeleteClientVpnRoute": + if tfawserr.ErrMessageContains(err, "ConcurrentMutationLimitExceeded", "Cannot initiate another change for this endpoint at this time") { + r.Retryable = aws.Bool(true) + } + + case "CreateVpnConnection": + if tfawserr.ErrMessageContains(err, "VpnConnectionLimitExceeded", "maximum number of mutating objects has been reached") { r.Retryable = aws.Bool(true) } - } - if r.Operation.Name == "AttachVpnGateway" || r.Operation.Name == "DetachVpnGateway" { - if tfawserr.ErrMessageContains(r.Error, "InvalidParameterValue", "This call cannot be completed because there are pending VPNs or Virtual Interfaces") { + case "CreateVpnGateway": + if tfawserr.ErrMessageContains(err, "VpnGatewayLimitExceeded", "maximum number of mutating objects has been reached") { r.Retryable = aws.Bool(true) } } From c5d156f7cd40ec00b1ce87228d1f7f007921277d Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 14:38:41 -0500 Subject: [PATCH 30/31] Acceptance test output: % make testacc TESTARGS='-run=TestAccEC2ClientVPNEndpoint_serial/Route' 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/Route -timeout 180m === RUN TestAccEC2ClientVPNEndpoint_serial === PAUSE TestAccEC2ClientVPNEndpoint_serial === CONT TestAccEC2ClientVPNEndpoint_serial === RUN TestAccEC2ClientVPNEndpoint_serial/Route_basic === PAUSE TestAccEC2ClientVPNEndpoint_serial/Route_basic === RUN TestAccEC2ClientVPNEndpoint_serial/Route_description === PAUSE TestAccEC2ClientVPNEndpoint_serial/Route_description === RUN TestAccEC2ClientVPNEndpoint_serial/Route_disappears === PAUSE TestAccEC2ClientVPNEndpoint_serial/Route_disappears === CONT TestAccEC2ClientVPNEndpoint_serial/Route_basic === CONT TestAccEC2ClientVPNEndpoint_serial/Route_disappears === CONT TestAccEC2ClientVPNEndpoint_serial/Route_description --- PASS: TestAccEC2ClientVPNEndpoint_serial (0.41s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/Route_basic (945.84s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/Route_disappears (1137.36s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/Route_description (1223.14s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 1227.110s From 5685620113f7455dea0125c94c98626eb8d9f492 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Thu, 3 Feb 2022 14:46:38 -0500 Subject: [PATCH 31/31] r/aws_ec2_client_vpn_authorization_rule: No ','s allowed in 'access_group_id'. 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_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 === RUN TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappears === PAUSE TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappears === RUN TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappearsEndpoint === PAUSE TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappearsEndpoint === CONT TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_basic === CONT TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappears === CONT TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_subnets === CONT TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappearsEndpoint === CONT TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_groups --- PASS: TestAccEC2ClientVPNEndpoint_serial (1.94s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappearsEndpoint (61.28s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_disappears (65.88s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_basic (68.49s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_subnets (100.60s) --- PASS: TestAccEC2ClientVPNEndpoint_serial/AuthorizationRule_groups (133.82s) PASS ok github.com/hashicorp/terraform-provider-aws/internal/service/ec2 140.874s --- internal/service/ec2/client_vpn_authorization_rule.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/service/ec2/client_vpn_authorization_rule.go b/internal/service/ec2/client_vpn_authorization_rule.go index 359580cc4a3..e4e8a169b24 100644 --- a/internal/service/ec2/client_vpn_authorization_rule.go +++ b/internal/service/ec2/client_vpn_authorization_rule.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" @@ -33,6 +34,7 @@ func ResourceClientVPNAuthorizationRule() *schema.Resource { Type: schema.TypeString, Optional: true, ForceNew: true, + ValidateFunc: validation.StringDoesNotContainAny(","), ExactlyOneOf: []string{"access_group_id", "authorize_all_groups"}, }, "authorize_all_groups": {