diff --git a/aws/resource_aws_ec2_traffic_mirror_target.go b/aws/resource_aws_ec2_traffic_mirror_target.go index 3d44f95490b7..d76946afac12 100644 --- a/aws/resource_aws_ec2_traffic_mirror_target.go +++ b/aws/resource_aws_ec2_traffic_mirror_target.go @@ -7,12 +7,14 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" ) func resourceAwsEc2TrafficMirrorTarget() *schema.Resource { return &schema.Resource{ Create: resourceAwsEc2TrafficMirrorTargetCreate, Read: resourceAwsEc2TrafficMirrorTargetRead, + Update: resourceAwsEc2TrafficMirrorTargetUpdate, Delete: resourceAwsEc2TrafficMirrorTargetDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, @@ -40,7 +42,9 @@ func resourceAwsEc2TrafficMirrorTarget() *schema.Resource { "network_interface_id", "network_load_balancer_arn", }, + ValidateFunc: validateArn, }, + "tags": tagsSchema(), }, } } @@ -61,6 +65,10 @@ func resourceAwsEc2TrafficMirrorTargetCreate(d *schema.ResourceData, meta interf input.NetworkLoadBalancerArn = aws.String(v.(string)) } + if v, ok := d.GetOk("tags"); ok { + input.TagSpecifications = ec2TagSpecificationsFromMap(v.(map[string]interface{}), ec2.ResourceTypeTrafficMirrorTarget) + } + out, err := conn.CreateTrafficMirrorTarget(input) if err != nil { return fmt.Errorf("Error creating traffic mirror target %v", err) @@ -71,6 +79,20 @@ func resourceAwsEc2TrafficMirrorTargetCreate(d *schema.ResourceData, meta interf return resourceAwsEc2TrafficMirrorTargetRead(d, meta) } +func resourceAwsEc2TrafficMirrorTargetUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + + if err := keyvaluetags.Ec2UpdateTags(conn, d.Id(), o, n); err != nil { + return fmt.Errorf("error updating EC2 Traffic Mirror Target (%s) tags: %s", d.Id(), err) + } + } + + return resourceAwsEc2TrafficMirrorTargetRead(d, meta) +} + func resourceAwsEc2TrafficMirrorTargetRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn @@ -101,6 +123,10 @@ func resourceAwsEc2TrafficMirrorTargetRead(d *schema.ResourceData, meta interfac d.Set("network_interface_id", target.NetworkInterfaceId) d.Set("network_load_balancer_arn", target.NetworkLoadBalancerArn) + if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(target.Tags).IgnoreAws().Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) + } + return nil } diff --git a/aws/resource_aws_ec2_traffic_mirror_target_test.go b/aws/resource_aws_ec2_traffic_mirror_target_test.go index 6fad7456d0c6..3ddd2921f83c 100644 --- a/aws/resource_aws_ec2_traffic_mirror_target_test.go +++ b/aws/resource_aws_ec2_traffic_mirror_target_test.go @@ -15,9 +15,10 @@ import ( ) func TestAccAWSEc2TrafficMirrorTarget_nlb(t *testing.T) { - resourceName := "aws_ec2_traffic_mirror_target.target" + var v ec2.TrafficMirrorTarget + resourceName := "aws_ec2_traffic_mirror_target.test" description := "test nlb target" - lbName := acctest.RandString(32) + rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { @@ -29,11 +30,12 @@ func TestAccAWSEc2TrafficMirrorTarget_nlb(t *testing.T) { Steps: []resource.TestStep{ //create { - Config: testAccTrafficMirrorTargetConfigNlb(description, lbName), + Config: testAccTrafficMirrorTargetConfigNlb(rName, description), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEc2TrafficMirrorTargetExists(resourceName), + testAccCheckAWSEc2TrafficMirrorTargetExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "description", description), resource.TestMatchResourceAttr(resourceName, "network_load_balancer_arn", regexp.MustCompile("arn:aws:elasticloadbalancing:.*")), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -46,7 +48,9 @@ func TestAccAWSEc2TrafficMirrorTarget_nlb(t *testing.T) { } func TestAccAWSEc2TrafficMirrorTarget_eni(t *testing.T) { - resourceName := "aws_ec2_traffic_mirror_target.target" + var v ec2.TrafficMirrorTarget + resourceName := "aws_ec2_traffic_mirror_target.test" + rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) description := "test eni target" resource.ParallelTest(t, resource.TestCase{ @@ -59,11 +63,12 @@ func TestAccAWSEc2TrafficMirrorTarget_eni(t *testing.T) { Steps: []resource.TestStep{ //create { - Config: testAccTrafficMirrorTargetConfigEni(description), + Config: testAccTrafficMirrorTargetConfigEni(rName, description), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSEc2TrafficMirrorTargetExists(resourceName), + testAccCheckAWSEc2TrafficMirrorTargetExists(resourceName, &v), resource.TestCheckResourceAttr(resourceName, "description", description), resource.TestMatchResourceAttr(resourceName, "network_interface_id", regexp.MustCompile("eni-.*")), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -75,7 +80,81 @@ func TestAccAWSEc2TrafficMirrorTarget_eni(t *testing.T) { }) } -func testAccCheckAWSEc2TrafficMirrorTargetExists(name string) resource.TestCheckFunc { +func TestAccAWSEc2TrafficMirrorTarget_tags(t *testing.T) { + var v ec2.TrafficMirrorTarget + resourceName := "aws_ec2_traffic_mirror_target.test" + description := "test nlb target" + rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckAWSEc2TrafficMirrorTarget(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEc2TrafficMirrorTargetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTrafficMirrorTargetConfigTags1(rName, description, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEc2TrafficMirrorTargetExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccTrafficMirrorTargetConfigTags2(rName, description, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEc2TrafficMirrorTargetExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccTrafficMirrorTargetConfigTags1(rName, description, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEc2TrafficMirrorTargetExists(resourceName, &v), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func TestAccAWSEc2TrafficMirrorTarget_disappears(t *testing.T) { + var v ec2.TrafficMirrorTarget + resourceName := "aws_ec2_traffic_mirror_target.test" + description := "test nlb target" + rName := fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccPreCheckAWSEc2TrafficMirrorTarget(t) + }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSEc2TrafficMirrorTargetDestroy, + Steps: []resource.TestStep{ + { + Config: testAccTrafficMirrorTargetConfigNlb(rName, description), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSEc2TrafficMirrorTargetExists(resourceName, &v), + testAccCheckAWSEc2TrafficMirrorTargetDisappears(&v), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccCheckAWSEc2TrafficMirrorTargetExists(name string, target *ec2.TrafficMirrorTarget) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[name] if !ok { @@ -100,11 +179,24 @@ func testAccCheckAWSEc2TrafficMirrorTargetExists(name string) resource.TestCheck return fmt.Errorf("Traffic mirror target %s not found", rs.Primary.ID) } + *target = *out.TrafficMirrorTargets[0] + return nil } } -func testAccTrafficMirrorTargetConfigNlb(description string, lbName string) string { +func testAccCheckAWSEc2TrafficMirrorTargetDisappears(target *ec2.TrafficMirrorTarget) resource.TestCheckFunc { + return func(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + _, err := conn.DeleteTrafficMirrorTarget(&ec2.DeleteTrafficMirrorTargetInput{ + TrafficMirrorTargetId: target.TrafficMirrorTargetId, + }) + + return err + } +} + +func testAccTrafficMirrorTargetConfigBase(rName string) string { return fmt.Sprintf(` data "aws_availability_zones" "azs" { state = "available" @@ -112,22 +204,38 @@ data "aws_availability_zones" "azs" { resource "aws_vpc" "vpc" { cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } } resource "aws_subnet" "sub1" { vpc_id = "${aws_vpc.vpc.id}" cidr_block = "10.0.0.0/24" availability_zone = "${data.aws_availability_zones.azs.names[0]}" + + tags = { + Name = %[1]q + } } resource "aws_subnet" "sub2" { vpc_id = "${aws_vpc.vpc.id}" cidr_block = "10.0.1.0/24" availability_zone = "${data.aws_availability_zones.azs.names[1]}" + + tags = { + Name = %[1]q + } +} +`, rName) } +func testAccTrafficMirrorTargetConfigNlb(rName, description string) string { + return testAccTrafficMirrorTargetConfigBase(rName) + fmt.Sprintf(` resource "aws_lb" "lb" { - name = "%s" + name = %[1]q internal = true load_balancer_type = "network" subnets = ["${aws_subnet.sub1.id}", "${aws_subnet.sub2.id}"] @@ -135,23 +243,20 @@ resource "aws_lb" "lb" { enable_deletion_protection = false tags = { + Name = %[1]q Environment = "production" } } -resource "aws_ec2_traffic_mirror_target" "target" { - description = "%s" +resource "aws_ec2_traffic_mirror_target" "test" { + description = %[2]q network_load_balancer_arn = "${aws_lb.lb.arn}" } -`, lbName, description) -} - -func testAccTrafficMirrorTargetConfigEni(description string) string { - return fmt.Sprintf(` -data "aws_availability_zones" "azs" { - state = "available" +`, rName, description) } +func testAccTrafficMirrorTargetConfigEni(rName, description string) string { + return testAccTrafficMirrorTargetConfigBase(rName) + fmt.Sprintf(` data "aws_ami" "amzn-linux" { most_recent = true @@ -168,33 +273,76 @@ data "aws_ami" "amzn-linux" { owners = ["137112412989"] } -resource "aws_vpc" "vpc" { - cidr_block = "10.0.0.0/16" +resource "aws_instance" "src" { + ami = "${data.aws_ami.amzn-linux.id}" + instance_type = "t2.micro" + subnet_id = "${aws_subnet.sub1.id}" + + tags = { + Name = %[1]q + } } -resource "aws_subnet" "sub1" { - vpc_id = "${aws_vpc.vpc.id}" - cidr_block = "10.0.0.0/24" - availability_zone = "${data.aws_availability_zones.azs.names[0]}" +resource "aws_ec2_traffic_mirror_target" "test" { + description = %[2]q + network_interface_id = "${aws_instance.src.primary_network_interface_id}" +} +`, rName, description) } -resource "aws_subnet" "sub2" { - vpc_id = "${aws_vpc.vpc.id}" - cidr_block = "10.0.1.0/24" - availability_zone = "${data.aws_availability_zones.azs.names[1]}" +func testAccTrafficMirrorTargetConfigTags1(rName, description, tagKey1, tagValue1 string) string { + return testAccTrafficMirrorTargetConfigBase(rName) + fmt.Sprintf(` +resource "aws_lb" "lb" { + name = %[1]q + internal = true + load_balancer_type = "network" + subnets = ["${aws_subnet.sub1.id}", "${aws_subnet.sub2.id}"] + + enable_deletion_protection = false + + tags = { + Name = %[1]q + Environment = "production" + } } -resource "aws_instance" "src" { - ami = "${data.aws_ami.amzn-linux.id}" - instance_type = "t2.micro" - subnet_id = "${aws_subnet.sub1.id}" +resource "aws_ec2_traffic_mirror_target" "test" { + description = %[2]q + network_load_balancer_arn = "${aws_lb.lb.arn}" + + tags = { + %[3]q = %[4]q + } +} +`, rName, description, tagKey1, tagValue1) } -resource "aws_ec2_traffic_mirror_target" "target" { - description = "%s" - network_interface_id = "${aws_instance.src.primary_network_interface_id}" +func testAccTrafficMirrorTargetConfigTags2(rName, description, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return testAccTrafficMirrorTargetConfigBase(rName) + fmt.Sprintf(` +resource "aws_lb" "lb" { + name = %[1]q + internal = true + load_balancer_type = "network" + subnets = ["${aws_subnet.sub1.id}", "${aws_subnet.sub2.id}"] + + enable_deletion_protection = false + + tags = { + Name = %[1]q + Environment = "production" + } +} + +resource "aws_ec2_traffic_mirror_target" "test" { + description = %[2]q + network_load_balancer_arn = "${aws_lb.lb.arn}" + + tags = { + %[3]q = %[4]q + %[5]q = %[6]q + } } -`, description) +`, rName, description, tagKey1, tagValue1, tagKey2, tagValue2) } func testAccPreCheckAWSEc2TrafficMirrorTarget(t *testing.T) { @@ -203,7 +351,7 @@ func testAccPreCheckAWSEc2TrafficMirrorTarget(t *testing.T) { _, err := conn.DescribeTrafficMirrorTargets(&ec2.DescribeTrafficMirrorTargetsInput{}) if testAccPreCheckSkipError(err) { - t.Skip("skipping traffic mirror target acceprance test: ", err) + t.Skip("skipping traffic mirror target acceptance test: ", err) } if err != nil { diff --git a/website/docs/r/ec2_traffic_mirror_target.html.markdown b/website/docs/r/ec2_traffic_mirror_target.html.markdown index 5d052e17da18..a05f70a20870 100644 --- a/website/docs/r/ec2_traffic_mirror_target.html.markdown +++ b/website/docs/r/ec2_traffic_mirror_target.html.markdown @@ -3,7 +3,7 @@ subcategory: "EC2" layout: "aws" page_title: "AWS: aws_ec2_traffic_mirror_target" description: |- - Provides an Traffic mirror target + Provides a Traffic mirror target --- # Resource: aws_ec2_traffic_mirror_target @@ -35,6 +35,7 @@ The following arguments are supported: * `description` - (Optional, Forces new) A description of the traffic mirror session. * `network_interface_id` - (Optional, Forces new) The network interface ID that is associated with the target. * `network_load_balancer_arn` - (Optional, Forces new) The Amazon Resource Name (ARN) of the Network Load Balancer that is associated with the target. +* `tags` - (Optional) Key-value mapping of resource tags. **NOTE:** Either `network_interface_id` or `network_load_balancer_arn` should be specified and both should not be specified together