diff --git a/.changelog/23330.txt b/.changelog/23330.txt new file mode 100644 index 00000000000..ae16522bfb0 --- /dev/null +++ b/.changelog/23330.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_ec2_network_insights_path +``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index ecad29a1638..c731b6f741d 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -1199,6 +1199,7 @@ func Provider() *schema.Provider { "aws_ec2_local_gateway_route_table_vpc_association": ec2.ResourceLocalGatewayRouteTableVPCAssociation(), "aws_ec2_managed_prefix_list": ec2.ResourceManagedPrefixList(), "aws_ec2_managed_prefix_list_entry": ec2.ResourceManagedPrefixListEntry(), + "aws_ec2_network_insights_path": ec2.ResourceNetworkInsightsPath(), "aws_ec2_subnet_cidr_reservation": ec2.ResourceSubnetCIDRReservation(), "aws_ec2_tag": ec2.ResourceTag(), "aws_ec2_traffic_mirror_filter": ec2.ResourceTrafficMirrorFilter(), diff --git a/internal/service/ec2/errors.go b/internal/service/ec2/errors.go index 985c7d04818..a18260d0a39 100644 --- a/internal/service/ec2/errors.go +++ b/internal/service/ec2/errors.go @@ -38,6 +38,7 @@ const ( ErrCodeInvalidNetworkAclEntryNotFound = "InvalidNetworkAclEntry.NotFound" ErrCodeInvalidNetworkAclIDNotFound = "InvalidNetworkAclID.NotFound" ErrCodeInvalidNetworkInterfaceIDNotFound = "InvalidNetworkInterfaceID.NotFound" + ErrCodeInvalidNetworkInsightsPathIdNotFound = "InvalidNetworkInsightsPathId.NotFound" ErrCodeInvalidParameter = "InvalidParameter" ErrCodeInvalidParameterException = "InvalidParameterException" ErrCodeInvalidParameterValue = "InvalidParameterValue" diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index 601b0338b76..e5708f54544 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -978,6 +978,76 @@ func FindNetworkInterfaceSecurityGroup(conn *ec2.EC2, networkInterfaceID string, } } +func FindNetworkInsightsPath(conn *ec2.EC2, input *ec2.DescribeNetworkInsightsPathsInput) (*ec2.NetworkInsightsPath, error) { + output, err := FindNetworkInsightsPaths(conn, input) + + if err != nil { + return nil, err + } + + if len(output) == 0 || output[0] == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + if count := len(output); count > 1 { + return nil, tfresource.NewTooManyResultsError(count, input) + } + + return output[0], nil +} + +func FindNetworkInsightsPaths(conn *ec2.EC2, input *ec2.DescribeNetworkInsightsPathsInput) ([]*ec2.NetworkInsightsPath, error) { + var output []*ec2.NetworkInsightsPath + + err := conn.DescribeNetworkInsightsPathsPages(input, func(page *ec2.DescribeNetworkInsightsPathsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.NetworkInsightsPaths { + if v != nil { + output = append(output, v) + } + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, ErrCodeInvalidNetworkInsightsPathIdNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func FindNetworkInsightsPathByID(conn *ec2.EC2, id string) (*ec2.NetworkInsightsPath, error) { + input := &ec2.DescribeNetworkInsightsPathsInput{ + NetworkInsightsPathIds: aws.StringSlice([]string{id}), + } + + output, err := FindNetworkInsightsPath(conn, input) + + if err != nil { + return nil, err + } + + // Eventual consistency check. + if aws.StringValue(output.NetworkInsightsPathId) != id { + return nil, &resource.NotFoundError{ + LastRequest: input, + } + } + + return output, nil +} + // FindMainRouteTableAssociationByID returns the main route table association corresponding to the specified identifier. // Returns NotFoundError if no route table association is found. func FindMainRouteTableAssociationByID(conn *ec2.EC2, associationID string) (*ec2.RouteTableAssociation, error) { diff --git a/internal/service/ec2/network_insights_path.go b/internal/service/ec2/network_insights_path.go new file mode 100644 index 00000000000..14281584bd3 --- /dev/null +++ b/internal/service/ec2/network_insights_path.go @@ -0,0 +1,180 @@ +package ec2 + +import ( + "context" + "log" + + "github.com/aws/aws-sdk-go/aws" + "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/diag" + "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" + tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/internal/verify" +) + +func ResourceNetworkInsightsPath() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceNetworkInsightsPathCreate, + ReadWithoutTimeout: resourceNetworkInsightsPathRead, + UpdateWithoutTimeout: resourceNetworkInsightsPathUpdate, + DeleteWithoutTimeout: resourceNetworkInsightsPathDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "destination": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "destination_ip": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "destination_port": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + }, + "protocol": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(ec2.Protocol_Values(), false), + }, + "source": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "source_ip": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "tags": tftags.TagsSchema(), + "tags_all": tftags.TagsSchemaComputed(), + }, + + CustomizeDiff: verify.SetTagsDiff, + } +} + +func resourceNetworkInsightsPathCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).EC2Conn + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + tags := defaultTagsConfig.MergeTags(tftags.New(d.Get("tags").(map[string]interface{}))) + + input := &ec2.CreateNetworkInsightsPathInput{ + Destination: aws.String(d.Get("destination").(string)), + Protocol: aws.String(d.Get("protocol").(string)), + Source: aws.String(d.Get("source").(string)), + TagSpecifications: ec2TagSpecificationsFromKeyValueTags(tags, ec2.ResourceTypeNetworkInsightsPath), + } + + if v, ok := d.GetOk("destination_ip"); ok { + input.DestinationIp = aws.String(v.(string)) + } + + if v, ok := d.GetOk("destination_port"); ok { + input.DestinationPort = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("source_ip"); ok { + input.SourceIp = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Creating EC2 Network Insights Path: %s", input) + output, err := conn.CreateNetworkInsightsPathWithContext(ctx, input) + + if err != nil { + return diag.Errorf("error creating EC2 Network Insights Path: %s", err) + } + + d.SetId(aws.StringValue(output.NetworkInsightsPath.NetworkInsightsPathId)) + + return resourceNetworkInsightsPathRead(ctx, d, meta) +} + +func resourceNetworkInsightsPathRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).EC2Conn + defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + + nip, err := FindNetworkInsightsPathByID(conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EC2 Network Insights Path %s not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.Errorf("error reading EC2 Network Insights Path (%s): %s", d.Id(), err) + } + + d.Set("arn", nip.NetworkInsightsPathArn) + d.Set("destination", nip.Destination) + d.Set("destination_ip", nip.DestinationIp) + d.Set("destination_port", nip.DestinationPort) + d.Set("protocol", nip.Protocol) + d.Set("source", nip.Source) + d.Set("source_ip", nip.SourceIp) + + tags := KeyValueTags(nip.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return diag.Errorf("error setting tags: %s", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return diag.Errorf("error setting tags_all: %s", err) + } + + return nil +} + +func resourceNetworkInsightsPathUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).EC2Conn + + if d.HasChange("tags_all") { + o, n := d.GetChange("tags_all") + + if err := UpdateTags(conn, d.Id(), o, n); err != nil { + return diag.Errorf("error updating EC2 Network Insights Path (%s) tags: %s", d.Id(), err) + } + } + + return resourceNetworkInsightsPathRead(ctx, d, meta) +} + +func resourceNetworkInsightsPathDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).EC2Conn + + log.Printf("[DEBUG] Deleting EC2 Network Insights Path: %s", d.Id()) + _, err := conn.DeleteNetworkInsightsPathWithContext(ctx, &ec2.DeleteNetworkInsightsPathInput{ + NetworkInsightsPathId: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, ErrCodeInvalidNetworkInsightsPathIdNotFound) { + return nil + } + + if err != nil { + return diag.Errorf("error deleting EC2 Network Insights Path (%s): %s", d.Id(), err) + } + + return nil +} diff --git a/internal/service/ec2/network_insights_path_test.go b/internal/service/ec2/network_insights_path_test.go new file mode 100644 index 00000000000..267e4863f26 --- /dev/null +++ b/internal/service/ec2/network_insights_path_test.go @@ -0,0 +1,543 @@ +package ec2_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/aws/aws-sdk-go/service/ec2" + sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-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 TestAccNetworkInsightsPath_basic(t *testing.T) { + resourceName := "aws_ec2_network_insights_path.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckNetworkInsightsPathDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEC2NetworkInsightsPathConfig(rName, "tcp"), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckNetworkInsightsPathExists(resourceName), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`network-insights-path/.+$`)), + resource.TestCheckResourceAttrPair(resourceName, "destination", "aws_network_interface.test_destination", "id"), + resource.TestCheckResourceAttr(resourceName, "destination_ip", ""), + resource.TestCheckResourceAttr(resourceName, "destination_port", "0"), + resource.TestCheckResourceAttr(resourceName, "protocol", "tcp"), + resource.TestCheckResourceAttrPair(resourceName, "source", "aws_network_interface.test_source", "id"), + resource.TestCheckResourceAttr(resourceName, "source_ip", ""), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNetworkInsightsPath_disappears(t *testing.T) { + resourceName := "aws_ec2_network_insights_path.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckNetworkInsightsPathDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEC2NetworkInsightsPathConfig(rName, "udp"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsPathExists(resourceName), + acctest.CheckResourceDisappears(acctest.Provider, tfec2.ResourceNetworkInsightsPath(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccNetworkInsightsPath_tags(t *testing.T) { + resourceName := "aws_ec2_network_insights_path.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckNetworkInsightsPathDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEC2NetworkInsightsPathConfigTags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsPathExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccEC2NetworkInsightsPathConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsPathExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccEC2NetworkInsightsPathConfigTags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsPathExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func TestAccNetworkInsightsPath_sourceIP(t *testing.T) { + resourceName := "aws_ec2_network_insights_path.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckNetworkInsightsPathDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEC2NetworkInsightsPathSourceIPConfig(rName, "1.1.1.1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsPathExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "source_ip", "1.1.1.1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccEC2NetworkInsightsPathSourceIPConfig(rName, "8.8.8.8"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsPathExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "source_ip", "8.8.8.8"), + ), + }, + }, + }) +} + +func TestAccNetworkInsightsPath_destinationIP(t *testing.T) { + resourceName := "aws_ec2_network_insights_path.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckNetworkInsightsPathDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEC2NetworkInsightsPathDestinationIPConfig(rName, "1.1.1.1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsPathExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "destination_ip", "1.1.1.1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccEC2NetworkInsightsPathDestinationIPConfig(rName, "8.8.8.8"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsPathExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "destination_ip", "8.8.8.8"), + ), + }, + }, + }) +} + +func TestAccNetworkInsightsPath_destinationPort(t *testing.T) { + resourceName := "aws_ec2_network_insights_path.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccCheckNetworkInsightsPathDestroy, + Steps: []resource.TestStep{ + { + Config: testAccEC2NetworkInsightsPathDestinationPortConfig(rName, 80), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsPathExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "destination_port", "80"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccEC2NetworkInsightsPathDestinationPortConfig(rName, 443), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsPathExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "destination_port", "443"), + ), + }, + }, + }) +} + +func testAccCheckNetworkInsightsPathExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No EC2 Network Insights Path ID is set") + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn + + _, err := tfec2.FindNetworkInsightsPathByID(conn, rs.Primary.ID) + + if err != nil { + return err + } + + return nil + } +} + +func testAccCheckNetworkInsightsPathDestroy(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_ec2_network_insights_path" { + continue + } + + _, err := tfec2.FindNetworkInsightsPathByID(conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("EC2 Network Insights Path %s still exists", rs.Primary.ID) + } + + return nil +} + +func testAccEC2NetworkInsightsPathConfig(rName, protocol string) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test" { + vpc_id = aws_vpc.test.id + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_network_interface" "test_source" { + subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_network_interface" "test_destination" { + subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_ec2_network_insights_path" "test" { + source = aws_network_interface.test_source.id + destination = aws_network_interface.test_destination.id + protocol = %[2]q +} +`, rName, protocol) +} + +func testAccEC2NetworkInsightsPathConfigTags1(rName, tagKey1, tagValue1 string) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test" { + vpc_id = aws_vpc.test.id + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_network_interface" "test_source" { + subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_network_interface" "test_destination" { + subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_ec2_network_insights_path" "test" { + source = aws_network_interface.test_source.id + destination = aws_network_interface.test_destination.id + protocol = "tcp" + + tags = { + %[2]q = %[3]q + } +} +`, rName, tagKey1, tagValue1) +} + +func testAccEC2NetworkInsightsPathConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test" { + vpc_id = aws_vpc.test.id + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_network_interface" "test_source" { + subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_network_interface" "test_destination" { + subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_ec2_network_insights_path" "test" { + source = aws_network_interface.test_source.id + destination = aws_network_interface.test_destination.id + protocol = "tcp" + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +} + +func testAccEC2NetworkInsightsPathSourceIPConfig(rName, sourceIP string) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_internet_gateway" "test" { + vpc_id = aws_vpc.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test" { + vpc_id = aws_vpc.test.id + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_network_interface" "test" { + subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_ec2_network_insights_path" "test" { + source = aws_internet_gateway.test.id + destination = aws_network_interface.test.id + protocol = "tcp" + source_ip = %[2]q + + tags = { + Name = %[1]q + } +} +`, rName, sourceIP) +} + +func testAccEC2NetworkInsightsPathDestinationIPConfig(rName, destinationIP string) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_internet_gateway" "test" { + vpc_id = aws_vpc.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test" { + vpc_id = aws_vpc.test.id + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_network_interface" "test" { + subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_ec2_network_insights_path" "test" { + source = aws_network_interface.test.id + destination = aws_internet_gateway.test.id + protocol = "tcp" + destination_ip = %[2]q + + tags = { + Name = %[1]q + } +} +`, rName, destinationIP) +} + +func testAccEC2NetworkInsightsPathDestinationPortConfig(rName string, destinationPort int) string { + return fmt.Sprintf(` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_subnet" "test" { + vpc_id = aws_vpc.test.id + cidr_block = "10.0.0.0/16" + + tags = { + Name = %[1]q + } +} + +resource "aws_network_interface" "test_source" { + subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_network_interface" "test_destination" { + subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } +} + +resource "aws_ec2_network_insights_path" "test" { + source = aws_network_interface.test_source.id + destination = aws_network_interface.test_destination.id + protocol = "tcp" + destination_port = %[2]d + + tags = { + Name = %[1]q + } +} +`, rName, destinationPort) +} diff --git a/internal/service/ec2/sweep.go b/internal/service/ec2/sweep.go index c237a837e6a..cbd59886692 100644 --- a/internal/service/ec2/sweep.go +++ b/internal/service/ec2/sweep.go @@ -147,6 +147,11 @@ func init() { }, }) + resource.AddTestSweepers("aws_ec2_network_insights_path", &resource.Sweeper{ + Name: "aws_ec2_network_insights_path", + F: sweepNetworkInsightsPaths, + }) + resource.AddTestSweepers("aws_placement_group", &resource.Sweeper{ Name: "aws_placement_group", F: sweepPlacementGroups, @@ -1178,6 +1183,45 @@ func sweepNetworkInterfaces(region string) error { return nil } +func sweepNetworkInsightsPaths(region string) error { + client, err := sweep.SharedRegionalSweepClient(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*conns.AWSClient).EC2Conn + sweepResources := make([]*sweep.SweepResource, 0) + var errs *multierror.Error + + err = conn.DescribeNetworkInsightsPathsPages(&ec2.DescribeNetworkInsightsPathsInput{}, func(page *ec2.DescribeNetworkInsightsPathsOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, nip := range page.NetworkInsightsPaths { + id := aws.StringValue(nip.NetworkInsightsPathId) + + r := ResourceNetworkInsightsPath() + d := r.Data(nil) + + d.SetId(id) + sweepResources = append(sweepResources, sweep.NewSweepResource(r, d, client)) + } + + return !lastPage + }) + if err != nil { + errs = multierror.Append(errs, fmt.Errorf("error listing Network Insights Paths for %s: %w", region, err)) + } + if err := sweep.SweepOrchestrator(sweepResources); err != nil { + errs = multierror.Append(errs, fmt.Errorf("error sweeping Network Insights Paths for %s: %w", region, err)) + } + if sweep.SkipSweepError(err) { + log.Printf("[WARN] Skipping Network Insights Path sweep for %s: %s", region, errs) + return nil + } + return errs.ErrorOrNil() +} + func sweepPlacementGroups(region string) error { client, err := sweep.SharedRegionalSweepClient(region) if err != nil { diff --git a/website/docs/r/ec2_network_insights_path.html.markdown b/website/docs/r/ec2_network_insights_path.html.markdown new file mode 100644 index 00000000000..c4997f65b0e --- /dev/null +++ b/website/docs/r/ec2_network_insights_path.html.markdown @@ -0,0 +1,52 @@ +--- +subcategory: "VPC" +layout: "aws" +page_title: "AWS: aws_ec2_network_insights_path" +description: |- + Provides a Network Insights Path resource. +--- + +# Resource: aws_ec2_network_insights_path + +Provides a Network Insights Path resource. Part of the "Reachability Analyzer" service in the AWS VPC console. + +## Example Usage + +```terraform +resource "aws_ec2_network_insights_path" "test" { + source = aws_network_interface.source.id + destination = aws_network_interface.destination.id + protocol = "tcp" +} +``` + +## Argument Reference + +The following arguments are required: + +* `source` - (Required) ID of the resource which is the source of the path. Can be an Instance, Internet Gateway, Network Interface, Transit Gateway, VPC Endpoint, VPC Peering Connection or VPN Gateway. +* `destination` - (Required) ID of the resource which is the source of the path. Can be an Instance, Internet Gateway, Network Interface, Transit Gateway, VPC Endpoint, VPC Peering Connection or VPN Gateway. +* `protocol` - (Required) Protocol to use for analysis. Valid options are `tcp` or `udp`. + +The following arguments are optional: + +* `source_ip` - (Optional) IP address of the source resource. +* `destination_ip` - (Optional) IP address of the destination resource. +* `destination_port` - (Optional) Destination port to analyze access to. +* `tags` - (Optional) Map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - ARN of the Network Insights Path. +* `id` - ID of the Network Insights Path. +* `tags_all` - Map of tags assigned to the resource, including those inherited from the provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block). + +## Import + +Network Insights Paths can be imported using the `id`, e.g., + +``` +$ terraform import aws_ec2_network_insights_path.test nip-00edfba169923aefd +```