diff --git a/aws/resource_aws_ec2_client_vpn_endpoint.go b/aws/resource_aws_ec2_client_vpn_endpoint.go index dc0b5694a2b4..dbfab48cac42 100644 --- a/aws/resource_aws_ec2_client_vpn_endpoint.go +++ b/aws/resource_aws_ec2_client_vpn_endpoint.go @@ -108,6 +108,7 @@ func resourceAwsEc2ClientVpnEndpoint() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "tags": tagsSchema(), }, } } @@ -119,6 +120,7 @@ func resourceAwsEc2ClientVpnEndpointCreate(d *schema.ResourceData, meta interfac ClientCidrBlock: aws.String(d.Get("client_cidr_block").(string)), ServerCertificateArn: aws.String(d.Get("server_certificate_arn").(string)), TransportProtocol: aws.String(d.Get("transport_protocol").(string)), + TagSpecifications: ec2TagSpecificationsFromMap(d.Get("tags").(map[string]interface{}), ec2.ResourceTypeClientVpnEndpoint), } if v, ok := d.GetOk("description"); ok { @@ -227,12 +229,17 @@ func resourceAwsEc2ClientVpnEndpointRead(d *schema.ResourceData, meta interface{ err = d.Set("authentication_options", flattenAuthOptsConfig(result.ClientVpnEndpoints[0].AuthenticationOptions)) if err != nil { - return err + return fmt.Errorf("error setting authentication_options: %s", err) } err = d.Set("connection_log_options", flattenConnLoggingConfig(result.ClientVpnEndpoints[0].ConnectionLogOptions)) if err != nil { - return err + return fmt.Errorf("error setting connection_log_options: %s", err) + } + + err = d.Set("tags", tagsToMap(result.ClientVpnEndpoints[0].Tags)) + if err != nil { + return fmt.Errorf("error setting tags: %s", err) } return nil @@ -254,6 +261,8 @@ func resourceAwsEc2ClientVpnEndpointDelete(d *schema.ResourceData, meta interfac func resourceAwsEc2ClientVpnEndpointUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn + d.Partial(true) + req := &ec2.ModifyClientVpnEndpointInput{ ClientVpnEndpointId: aws.String(d.Id()), } @@ -304,11 +313,16 @@ func resourceAwsEc2ClientVpnEndpointUpdate(d *schema.ResourceData, meta interfac } } - _, err := conn.ModifyClientVpnEndpoint(req) - if err != nil { + if _, err := conn.ModifyClientVpnEndpoint(req); err != nil { return fmt.Errorf("Error modifying Client VPN endpoint: %s", err) } + if err := setTags(conn, d); err != nil { + return err + } + d.SetPartial("tags") + + d.Partial(false) return resourceAwsEc2ClientVpnEndpointRead(d, meta) } diff --git a/aws/resource_aws_ec2_client_vpn_endpoint_test.go b/aws/resource_aws_ec2_client_vpn_endpoint_test.go index 99caba0ede52..17f2f1940da6 100644 --- a/aws/resource_aws_ec2_client_vpn_endpoint_test.go +++ b/aws/resource_aws_ec2_client_vpn_endpoint_test.go @@ -126,6 +126,42 @@ func TestAccAwsEc2ClientVpnEndpoint_withDNSServers(t *testing.T) { }) } +func TestAccAwsEc2ClientVpnEndpoint_tags(t *testing.T) { + resourceName := "aws_ec2_client_vpn_endpoint.test" + rStr := acctest.RandString(5) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProvidersWithTLS, + CheckDestroy: testAccCheckAwsEc2ClientVpnEndpointDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEc2ClientVpnEndpointConfig_tags(rStr), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsEc2ClientVpnEndpointExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.Usage", "original"), + ), + }, + { + Config: testAccEc2ClientVpnEndpointConfig_tagsChanged(rStr), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsEc2ClientVpnEndpointExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Usage", "changed"), + ), + }, + { + Config: testAccEc2ClientVpnEndpointConfig(rStr), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsEc2ClientVpnEndpointExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + }, + }) +} + func testAccCheckAwsEc2ClientVpnEndpointDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).ec2conn @@ -383,3 +419,104 @@ resource "aws_ec2_client_vpn_endpoint" "test" { } `, rName) } + +func testAccEc2ClientVpnEndpointConfig_tags(rName string) string { + return fmt.Sprintf(` +resource "tls_private_key" "example" { + algorithm = "RSA" +} + +resource "tls_self_signed_cert" "example" { + key_algorithm = "RSA" + private_key_pem = "${tls_private_key.example.private_key_pem}" + + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + + validity_period_hours = 12 + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +resource "aws_acm_certificate" "cert" { + private_key = "${tls_private_key.example.private_key_pem}" + certificate_body = "${tls_self_signed_cert.example.cert_pem}" +} + +resource "aws_ec2_client_vpn_endpoint" "test" { + description = "terraform-testacc-clientvpn-%s" + server_certificate_arn = "${aws_acm_certificate.cert.arn}" + client_cidr_block = "10.0.0.0/16" + + authentication_options { + type = "certificate-authentication" + root_certificate_chain_arn = "${aws_acm_certificate.cert.arn}" + } + + connection_log_options { + enabled = false + } + + tags = { + Environment = "production" + Usage = "original" + } +} +`, rName) +} + +func testAccEc2ClientVpnEndpointConfig_tagsChanged(rName string) string { + return fmt.Sprintf(` +resource "tls_private_key" "example" { + algorithm = "RSA" +} + +resource "tls_self_signed_cert" "example" { + key_algorithm = "RSA" + private_key_pem = "${tls_private_key.example.private_key_pem}" + + subject { + common_name = "example.com" + organization = "ACME Examples, Inc" + } + + validity_period_hours = 12 + + allowed_uses = [ + "key_encipherment", + "digital_signature", + "server_auth", + ] +} + +resource "aws_acm_certificate" "cert" { + private_key = "${tls_private_key.example.private_key_pem}" + certificate_body = "${tls_self_signed_cert.example.cert_pem}" +} + +resource "aws_ec2_client_vpn_endpoint" "test" { + description = "terraform-testacc-clientvpn-%s" + server_certificate_arn = "${aws_acm_certificate.cert.arn}" + client_cidr_block = "10.0.0.0/16" + + authentication_options { + type = "certificate-authentication" + root_certificate_chain_arn = "${aws_acm_certificate.cert.arn}" + } + + connection_log_options { + enabled = false + } + + tags = { + Usage = "changed" + } +} +`, rName) +} diff --git a/aws/tags.go b/aws/tags.go index 058511268462..41dcddc8bd12 100644 --- a/aws/tags.go +++ b/aws/tags.go @@ -476,3 +476,17 @@ func tagsMapToRaw(m map[string]string) map[string]interface{} { return raw } + +// ec2TagSpecificationsFromMap returns the tag specifications for the given map of data m and resource type t. +func ec2TagSpecificationsFromMap(m map[string]interface{}, t string) []*ec2.TagSpecification { + if len(m) == 0 { + return nil + } + + return []*ec2.TagSpecification{ + { + ResourceType: aws.String(t), + Tags: tagsFromMap(m), + }, + } +} diff --git a/website/docs/r/ec2_client_vpn_endpoint.html.markdown b/website/docs/r/ec2_client_vpn_endpoint.html.markdown index 452e62154dcf..9f0520691005 100644 --- a/website/docs/r/ec2_client_vpn_endpoint.html.markdown +++ b/website/docs/r/ec2_client_vpn_endpoint.html.markdown @@ -8,7 +8,7 @@ description: |- # aws_ec2_client_vpn_endpoint -Provides an AWS Client VPN endpoint for OpenVPN clients. For more information on usage, please see the +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). ## Example Usage @@ -43,6 +43,7 @@ The following arguments are supported: * `transport_protocol` - (Optional) The transport protocol to be used by the VPN session. Default value is `udp`. * `authentication_options` - (Required) Information about the authentication method to be used to authenticate clients. * `connection_log_options` - (Required) Information about the client connection logging options. +* `tags` - (Optional) A mapping of tags to assign to the resource. ### `authentication_options` Argument Reference @@ -64,7 +65,7 @@ One of the following arguments must be supplied: In addition to all arguments above, the following attributes are exported: -* `id` - The ID of the Client VPN endpoint. +* `id` - The ID of the Client VPN endpoint. * `dns_name` - The DNS name to be used by clients when establishing their VPN session. * `status` - The current state of the Client VPN endpoint.