diff --git a/.changelog/23532.txt b/.changelog/23532.txt new file mode 100644 index 000000000000..c5a90a461ac7 --- /dev/null +++ b/.changelog/23532.txt @@ -0,0 +1,11 @@ +```release-note:new-resource +aws_ec2_network_insights_analysis +``` + +```release-note:new-data-source +aws_ec2_network_insights_path +``` + +```release-note:new-data-source +aws_ec2_network_insights_analysis +``` \ No newline at end of file diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 6192335ce2ca..1dc8696b47c8 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -547,6 +547,8 @@ func New(_ context.Context) (*schema.Provider, error) { "aws_ec2_local_gateway": ec2.DataSourceLocalGateway(), "aws_ec2_local_gateways": ec2.DataSourceLocalGateways(), "aws_ec2_managed_prefix_list": ec2.DataSourceManagedPrefixList(), + "aws_ec2_network_insights_analysis": ec2.DataSourceNetworkInsightsAnalysis(), + "aws_ec2_network_insights_path": ec2.DataSourceNetworkInsightsPath(), "aws_ec2_serial_console_access": ec2.DataSourceSerialConsoleAccess(), "aws_ec2_spot_price": ec2.DataSourceSpotPrice(), "aws_ec2_transit_gateway": ec2.DataSourceTransitGateway(), @@ -1324,6 +1326,7 @@ func New(_ context.Context) (*schema.Provider, error) { "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_analysis": ec2.ResourceNetworkInsightsAnalysis(), "aws_ec2_network_insights_path": ec2.ResourceNetworkInsightsPath(), "aws_ec2_serial_console_access": ec2.ResourceSerialConsoleAccess(), "aws_ec2_subnet_cidr_reservation": ec2.ResourceSubnetCIDRReservation(), diff --git a/internal/service/ec2/errors.go b/internal/service/ec2/errors.go index 6b0abe6f9099..fe33d27d3a5d 100644 --- a/internal/service/ec2/errors.go +++ b/internal/service/ec2/errors.go @@ -49,6 +49,7 @@ const ( errCodeInvalidNetworkACLEntryNotFound = "InvalidNetworkAclEntry.NotFound" errCodeInvalidNetworkACLIDNotFound = "InvalidNetworkAclID.NotFound" errCodeInvalidNetworkInterfaceIDNotFound = "InvalidNetworkInterfaceID.NotFound" + errCodeInvalidNetworkInsightsAnalysisIdNotFound = "InvalidNetworkInsightsAnalysisId.NotFound" errCodeInvalidNetworkInsightsPathIdNotFound = "InvalidNetworkInsightsPathId.NotFound" errCodeInvalidParameter = "InvalidParameter" errCodeInvalidParameterException = "InvalidParameterException" diff --git a/internal/service/ec2/find.go b/internal/service/ec2/find.go index 61220b9cb2b8..b2c27e875c0b 100644 --- a/internal/service/ec2/find.go +++ b/internal/service/ec2/find.go @@ -1460,8 +1460,8 @@ func FindNetworkInterfaceSecurityGroup(conn *ec2.EC2, networkInterfaceID string, } } -func FindNetworkInsightsPath(conn *ec2.EC2, input *ec2.DescribeNetworkInsightsPathsInput) (*ec2.NetworkInsightsPath, error) { - output, err := FindNetworkInsightsPaths(conn, input) +func FindNetworkInsightsAnalysis(ctx context.Context, conn *ec2.EC2, input *ec2.DescribeNetworkInsightsAnalysesInput) (*ec2.NetworkInsightsAnalysis, error) { + output, err := FindNetworkInsightsAnalyses(ctx, conn, input) if err != nil { return nil, err @@ -1478,10 +1478,80 @@ func FindNetworkInsightsPath(conn *ec2.EC2, input *ec2.DescribeNetworkInsightsPa return output[0], nil } -func FindNetworkInsightsPaths(conn *ec2.EC2, input *ec2.DescribeNetworkInsightsPathsInput) ([]*ec2.NetworkInsightsPath, error) { +func FindNetworkInsightsAnalyses(ctx context.Context, conn *ec2.EC2, input *ec2.DescribeNetworkInsightsAnalysesInput) ([]*ec2.NetworkInsightsAnalysis, error) { + var output []*ec2.NetworkInsightsAnalysis + + err := conn.DescribeNetworkInsightsAnalysesPagesWithContext(ctx, input, func(page *ec2.DescribeNetworkInsightsAnalysesOutput, lastPage bool) bool { + if page == nil { + return !lastPage + } + + for _, v := range page.NetworkInsightsAnalyses { + if v != nil { + output = append(output, v) + } + } + + return !lastPage + }) + + if tfawserr.ErrCodeEquals(err, errCodeInvalidNetworkInsightsAnalysisIdNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + return output, nil +} + +func FindNetworkInsightsAnalysisByID(ctx context.Context, conn *ec2.EC2, id string) (*ec2.NetworkInsightsAnalysis, error) { + input := &ec2.DescribeNetworkInsightsAnalysesInput{ + NetworkInsightsAnalysisIds: aws.StringSlice([]string{id}), + } + + output, err := FindNetworkInsightsAnalysis(ctx, conn, input) + + if err != nil { + return nil, err + } + + // Eventual consistency check. + if aws.StringValue(output.NetworkInsightsAnalysisId) != id { + return nil, &resource.NotFoundError{ + LastRequest: input, + } + } + + return output, nil +} + +func FindNetworkInsightsPath(ctx context.Context, conn *ec2.EC2, input *ec2.DescribeNetworkInsightsPathsInput) (*ec2.NetworkInsightsPath, error) { + output, err := FindNetworkInsightsPaths(ctx, 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(ctx context.Context, conn *ec2.EC2, input *ec2.DescribeNetworkInsightsPathsInput) ([]*ec2.NetworkInsightsPath, error) { var output []*ec2.NetworkInsightsPath - err := conn.DescribeNetworkInsightsPathsPages(input, func(page *ec2.DescribeNetworkInsightsPathsOutput, lastPage bool) bool { + err := conn.DescribeNetworkInsightsPathsPagesWithContext(ctx, input, func(page *ec2.DescribeNetworkInsightsPathsOutput, lastPage bool) bool { if page == nil { return !lastPage } @@ -1509,12 +1579,12 @@ func FindNetworkInsightsPaths(conn *ec2.EC2, input *ec2.DescribeNetworkInsightsP return output, nil } -func FindNetworkInsightsPathByID(conn *ec2.EC2, id string) (*ec2.NetworkInsightsPath, error) { +func FindNetworkInsightsPathByID(ctx context.Context, conn *ec2.EC2, id string) (*ec2.NetworkInsightsPath, error) { input := &ec2.DescribeNetworkInsightsPathsInput{ NetworkInsightsPathIds: aws.StringSlice([]string{id}), } - output, err := FindNetworkInsightsPath(conn, input) + output, err := FindNetworkInsightsPath(ctx, conn, input) if err != nil { return nil, err diff --git a/internal/service/ec2/status.go b/internal/service/ec2/status.go index a21edad4d50d..80aa63049768 100644 --- a/internal/service/ec2/status.go +++ b/internal/service/ec2/status.go @@ -1110,6 +1110,22 @@ func StatusManagedPrefixListState(conn *ec2.EC2, id string) resource.StateRefres } } +func StatusNetworkInsightsAnalysis(ctx context.Context, conn *ec2.EC2, id string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := FindNetworkInsightsAnalysisByID(ctx, conn, id) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, aws.StringValue(output.Status), nil + } +} + func StatusNetworkInterfaceStatus(conn *ec2.EC2, id string) resource.StateRefreshFunc { return func() (interface{}, string, error) { output, err := FindNetworkInterfaceByID(conn, id) diff --git a/internal/service/ec2/vpc_network_insights_analysis.go b/internal/service/ec2/vpc_network_insights_analysis.go new file mode 100644 index 000000000000..c46f6647bed5 --- /dev/null +++ b/internal/service/ec2/vpc_network_insights_analysis.go @@ -0,0 +1,2198 @@ +package ec2 + +import ( + "context" + "log" + "time" + + "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-provider-aws/internal/conns" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + 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 ResourceNetworkInsightsAnalysis() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceNetworkInsightsAnalysisCreate, + ReadWithoutTimeout: resourceNetworkInsightsAnalysisRead, + UpdateWithoutTimeout: resourceNetworkInsightsAnalysisUpdate, + DeleteWithoutTimeout: resourceNetworkInsightsAnalysisDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "alternate_path_hints": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "component_arn": { + Type: schema.TypeString, + Computed: true, + }, + "component_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "explanations": networkInsightsAnalysisExplanationsSchema, + "filter_in_arns": { + Type: schema.TypeSet, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: verify.ValidARN, + }, + }, + "forward_path_components": networkInsightsAnalysisPathComponentsSchema, + "network_insights_path_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "path_found": { + Type: schema.TypeBool, + Computed: true, + }, + "return_path_components": networkInsightsAnalysisPathComponentsSchema, + "start_date": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "status_message": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tftags.TagsSchema(), + "tags_all": tftags.TagsSchemaComputed(), + "wait_for_completion": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "warning_message": { + Type: schema.TypeString, + Computed: true, + }, + }, + + CustomizeDiff: verify.SetTagsDiff, + } +} + +var networkInsightsAnalysisPathComponentsSchema = &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "acl_rule": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cidr": { + Type: schema.TypeString, + Computed: true, + }, + "egress": { + Type: schema.TypeBool, + Computed: true, + }, + "port_range": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from": { + Type: schema.TypeInt, + Computed: true, + }, + "to": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "protocol": { + Type: schema.TypeString, + Computed: true, + }, + "rule_action": { + Type: schema.TypeString, + Computed: true, + }, + "rule_number": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "additional_details": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "additional_detail_type": { + Type: schema.TypeString, + Computed: true, + }, + "component": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "attached_to": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "component": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "destination_vpc": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "inbound_header": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "destination_addresses": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "destination_port_ranges": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from": { + Type: schema.TypeInt, + Computed: true, + }, + "to": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "protocol": { + Type: schema.TypeString, + Computed: true, + }, + "source_addresses": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "source_port_ranges": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from": { + Type: schema.TypeInt, + Computed: true, + }, + "to": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "outbound_header": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "destination_addresses": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "destination_port_ranges": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from": { + Type: schema.TypeInt, + Computed: true, + }, + "to": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "protocol": { + Type: schema.TypeString, + Computed: true, + }, + "source_addresses": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "source_port_ranges": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from": { + Type: schema.TypeInt, + Computed: true, + }, + "to": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + }, + }, + }, + "route_table_route": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "destination_cidr": { + Type: schema.TypeString, + Computed: true, + }, + "destination_prefix_list_id": { + Type: schema.TypeString, + Computed: true, + }, + "egress_only_internet_gateway_id": { + Type: schema.TypeString, + Computed: true, + }, + "gateway_id": { + Type: schema.TypeString, + Computed: true, + }, + "instance_id": { + Type: schema.TypeString, + Computed: true, + }, + "nat_gateway_id": { + Type: schema.TypeString, + Computed: true, + }, + "network_interface_id": { + Type: schema.TypeString, + Computed: true, + }, + "origin": { + Type: schema.TypeString, + Computed: true, + }, + "transit_gateway_id": { + Type: schema.TypeString, + Computed: true, + }, + "vpc_peering_connection_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "security_group_rule": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cidr": { + Type: schema.TypeString, + Computed: true, + }, + "direction": { + Type: schema.TypeString, + Computed: true, + }, + "port_range": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from": { + Type: schema.TypeInt, + Computed: true, + }, + "to": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "prefix_list_id": { + Type: schema.TypeString, + Computed: true, + }, + "protocol": { + Type: schema.TypeString, + Computed: true, + }, + "security_group_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "sequence_number": { + Type: schema.TypeInt, + Computed: true, + }, + "source_vpc": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "subnet": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "transit_gateway": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "transit_gateway_route_table_route": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "attachment_id": { + Type: schema.TypeString, + Computed: true, + }, + "destination_cidr": { + Type: schema.TypeString, + Computed: true, + }, + "prefix_list_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + }, + "route_origin": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "vpc": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, +} + +var networkInsightsAnalysisExplanationsSchema = &schema.Schema{ + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "acl": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "acl_rule": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cidr": { + Type: schema.TypeString, + Computed: true, + }, + "egress": { + Type: schema.TypeBool, + Computed: true, + }, + "port_range": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from": { + Type: schema.TypeInt, + Computed: true, + }, + "to": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "protocol": { + Type: schema.TypeString, + Computed: true, + }, + "rule_action": { + Type: schema.TypeString, + Computed: true, + }, + "rule_number": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "address": { + Type: schema.TypeString, + Computed: true, + }, + "addresses": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "attached_to": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "availability_zones": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "cidrs": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "classic_load_balancer_listener": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "instance_port": { + Type: schema.TypeInt, + Computed: true, + }, + "load_balancer_port": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "component": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "customer_gateway": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "destination": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "destination_vpc": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "direction": { + Type: schema.TypeString, + Computed: true, + }, + "elastic_load_balancer_listener": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "explanation_code": { + Type: schema.TypeString, + Computed: true, + }, + "ingress_route_table": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "internet_gateway": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "load_balancer_arn": { + Type: schema.TypeString, + Computed: true, + }, + "load_balancer_listener_port": { + Type: schema.TypeInt, + Computed: true, + }, + "load_balancer_target_group": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "load_balancer_target_groups": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "load_balancer_target_port": { + Type: schema.TypeInt, + Computed: true, + }, + "missing_component": { + Type: schema.TypeString, + Computed: true, + }, + "nat_gateway": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "network_interface": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "packet_field": { + Type: schema.TypeString, + Computed: true, + }, + "port": { + Type: schema.TypeInt, + Computed: true, + }, + "port_ranges": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from": { + Type: schema.TypeInt, + Computed: true, + }, + "to": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "prefix_list": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "protocols": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "route_table": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "route_table_route": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "destination_cidr": { + Type: schema.TypeString, + Computed: true, + }, + "destination_prefix_list_id": { + Type: schema.TypeString, + Computed: true, + }, + "egress_only_internet_gateway_id": { + Type: schema.TypeString, + Computed: true, + }, + "gateway_id": { + Type: schema.TypeString, + Computed: true, + }, + "instance_id": { + Type: schema.TypeString, + Computed: true, + }, + "nat_gateway_id": { + Type: schema.TypeString, + Computed: true, + }, + "network_interface_id": { + Type: schema.TypeString, + Computed: true, + }, + "origin": { + Type: schema.TypeString, + Computed: true, + }, + "transit_gateway_id": { + Type: schema.TypeString, + Computed: true, + }, + "vpc_peering_connection_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "security_group": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "security_group_rule": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cidr": { + Type: schema.TypeString, + Computed: true, + }, + "direction": { + Type: schema.TypeString, + Computed: true, + }, + "port_range": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "from": { + Type: schema.TypeInt, + Computed: true, + }, + "to": { + Type: schema.TypeInt, + Computed: true, + }, + }, + }, + }, + "prefix_list_id": { + Type: schema.TypeString, + Computed: true, + }, + "protocol": { + Type: schema.TypeString, + Computed: true, + }, + "security_group_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "security_groups": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "source_vpc": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + "subnet": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "subnet_route_table": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "transit_gateway": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "transit_gateway_attachment": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "transit_gateway_route_table": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "transit_gateway_route_table_route": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "attachment_id": { + Type: schema.TypeString, + Computed: true, + }, + "destination_cidr": { + Type: schema.TypeString, + Computed: true, + }, + "prefix_list_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_id": { + Type: schema.TypeString, + Computed: true, + }, + "resource_type": { + Type: schema.TypeString, + Computed: true, + }, + "route_origin": { + Type: schema.TypeString, + Computed: true, + }, + "state": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "vpc": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "vpc_endpoint": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "vpc_peering_connection": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "vpn_connection": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "vpn_gateway": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + }, +} + +func resourceNetworkInsightsAnalysisCreate(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.StartNetworkInsightsAnalysisInput{ + NetworkInsightsPathId: aws.String(d.Get("network_insights_path_id").(string)), + TagSpecifications: tagSpecificationsFromKeyValueTags(tags, ec2.ResourceTypeNetworkInsightsAnalysis), + } + + if v, ok := d.GetOk("filter_in_arns"); ok && v.(*schema.Set).Len() > 0 { + input.FilterInArns = flex.ExpandStringSet(v.(*schema.Set)) + } + + log.Printf("[DEBUG] Creating EC2 Network Insights Analysis: %s", input) + output, err := conn.StartNetworkInsightsAnalysisWithContext(ctx, input) + + if err != nil { + return diag.Errorf("error creating EC2 Network Insights Analysis: %s", err) + } + + d.SetId(aws.StringValue(output.NetworkInsightsAnalysis.NetworkInsightsAnalysisId)) + + if d.Get("wait_for_completion").(bool) { + if _, err := WaitNetworkInsightsAnalysisCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return diag.Errorf("error waiting for EC2 Network Insights Analysis (%s) create: %s", d.Id(), err) + } + } + + return resourceNetworkInsightsAnalysisRead(ctx, d, meta) +} + +func resourceNetworkInsightsAnalysisRead(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 + + output, err := FindNetworkInsightsAnalysisByID(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EC2 Network Insights Analysis (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.Errorf("reading EC2 Network Insights Analysis (%s): %s", d.Id(), err) + } + + if err := d.Set("alternate_path_hints", flattenAlternatePathHints(output.AlternatePathHints)); err != nil { + return diag.Errorf("setting alternate_path_hints: %s", err) + } + d.Set("arn", output.NetworkInsightsAnalysisArn) + if err := d.Set("explanations", flattenExplanations(output.Explanations)); err != nil { + return diag.Errorf("setting explanations: %s", err) + } + d.Set("filter_in_arns", aws.StringValueSlice(output.FilterInArns)) + if err := d.Set("forward_path_components", flattenPathComponents(output.ForwardPathComponents)); err != nil { + return diag.Errorf("setting forward_path_components: %s", err) + } + d.Set("network_insights_path_id", output.NetworkInsightsPathId) + d.Set("path_found", output.NetworkPathFound) + if err := d.Set("return_path_components", flattenPathComponents(output.ReturnPathComponents)); err != nil { + return diag.Errorf("setting return_path_components: %s", err) + } + d.Set("start_date", output.StartDate.Format(time.RFC3339)) + d.Set("status", output.Status) + d.Set("status_message", output.StatusMessage) + d.Set("warning_message", output.WarningMessage) + + tags := KeyValueTags(output.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig) + + //lintignore:AWSR002 + if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { + return diag.Errorf("setting tags: %s", err) + } + + if err := d.Set("tags_all", tags.Map()); err != nil { + return diag.Errorf("setting tags_all: %s", err) + } + + return nil +} + +func resourceNetworkInsightsAnalysisUpdate(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 := UpdateTagsWithContext(ctx, conn, d.Id(), o, n); err != nil { + return diag.Errorf("updating EC2 Network Insights Analysis (%s) tags: %s", d.Id(), err) + } + } + + return resourceNetworkInsightsAnalysisRead(ctx, d, meta) +} + +func resourceNetworkInsightsAnalysisDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).EC2Conn + + log.Printf("[DEBUG] Deleting EC2 Network Insights Analysis: %s", d.Id()) + _, err := conn.DeleteNetworkInsightsAnalysisWithContext(ctx, &ec2.DeleteNetworkInsightsAnalysisInput{ + NetworkInsightsAnalysisId: aws.String(d.Id()), + }) + + if tfawserr.ErrCodeEquals(err, errCodeInvalidNetworkInsightsAnalysisIdNotFound) { + return nil + } + + if err != nil { + return diag.Errorf("deleting EC2 Network Insights Analysis (%s): %s", d.Id(), err) + } + + return nil +} + +func flattenAdditionalDetail(apiObject *ec2.AdditionalDetail) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.AdditionalDetailType; v != nil { + tfMap["additional_detail_type"] = aws.StringValue(v) + } + + if v := apiObject.Component; v != nil { + tfMap["component"] = []interface{}{flattenAnalysisComponent(v)} + } + + return tfMap +} + +func flattenAdditionalDetails(apiObjects []*ec2.AdditionalDetail) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, flattenAdditionalDetail(apiObject)) + } + + return tfList +} + +func flattenAlternatePathHint(apiObject *ec2.AlternatePathHint) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.ComponentArn; v != nil { + tfMap["component_arn"] = aws.StringValue(v) + } + + if v := apiObject.ComponentId; v != nil { + tfMap["component_id"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenAlternatePathHints(apiObjects []*ec2.AlternatePathHint) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, flattenAlternatePathHint(apiObject)) + } + + return tfList +} + +func flattenAnalysisAclRule(apiObject *ec2.AnalysisAclRule) map[string]interface{} { // nosemgrep:ci.caps2-in-func-name + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Cidr; v != nil { + tfMap["cidr"] = aws.StringValue(v) + } + + if v := apiObject.Egress; v != nil { + tfMap["egress"] = aws.BoolValue(v) + } + + if v := apiObject.PortRange; v != nil { + tfMap["port_range"] = []interface{}{flattenPortRange(v)} + } + + if v := apiObject.Protocol; v != nil { + tfMap["protocol"] = aws.StringValue(v) + } + + if v := apiObject.RuleAction; v != nil { + tfMap["rule_action"] = aws.StringValue(v) + } + + if v := apiObject.RuleNumber; v != nil { + tfMap["rule_number"] = aws.Int64Value(v) + } + + return tfMap +} + +func flattenAnalysisLoadBalancerListener(apiObject *ec2.AnalysisLoadBalancerListener) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.InstancePort; v != nil { + tfMap["instance_port"] = aws.Int64Value(v) + } + + if v := apiObject.LoadBalancerPort; v != nil { + tfMap["load_balancer_port"] = aws.Int64Value(v) + } + + return tfMap +} + +func flattenAnalysisComponent(apiObject *ec2.AnalysisComponent) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Arn; v != nil { + tfMap["arn"] = aws.StringValue(v) + } + + if v := apiObject.Id; v != nil { + tfMap["id"] = aws.StringValue(v) + } + + if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenAnalysisComponents(apiObjects []*ec2.AnalysisComponent) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, flattenAnalysisComponent(apiObject)) + } + + return tfList +} + +func flattenAnalysisLoadBalancerTarget(apiObject *ec2.AnalysisLoadBalancerTarget) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Address; v != nil { + tfMap["address"] = aws.StringValue(v) + } + + if v := apiObject.AvailabilityZone; v != nil { + tfMap["availability_zone"] = aws.StringValue(v) + } + + if v := apiObject.Instance; v != nil { + tfMap["instance"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.Port; v != nil { + tfMap["port"] = aws.Int64Value(v) + } + + return tfMap +} + +func flattenAnalysisPacketHeader(apiObject *ec2.AnalysisPacketHeader) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.DestinationAddresses; v != nil { + tfMap["destination_addresses"] = aws.StringValueSlice(v) + } + + if v := apiObject.DestinationPortRanges; v != nil { + tfMap["destination_port_ranges"] = flattenPortRanges(v) + } + + if v := apiObject.Protocol; v != nil { + tfMap["protocol"] = aws.StringValue(v) + } + + if v := apiObject.SourceAddresses; v != nil { + tfMap["source_addresses"] = aws.StringValueSlice(v) + } + + if v := apiObject.SourcePortRanges; v != nil { + tfMap["source_port_ranges"] = flattenPortRanges(v) + } + + return tfMap +} + +func flattenAnalysisRouteTableRoute(apiObject *ec2.AnalysisRouteTableRoute) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.DestinationCidr; v != nil { + tfMap["destination_cidr"] = aws.StringValue(v) + } + + if v := apiObject.DestinationPrefixListId; v != nil { + tfMap["destination_prefix_list_id"] = aws.StringValue(v) + } + + if v := apiObject.EgressOnlyInternetGatewayId; v != nil { + tfMap["egress_only_internet_gateway_id"] = aws.StringValue(v) + } + + if v := apiObject.GatewayId; v != nil { + tfMap["gateway_id"] = aws.StringValue(v) + } + + if v := apiObject.InstanceId; v != nil { + tfMap["instance_id"] = aws.StringValue(v) + } + + if v := apiObject.NatGatewayId; v != nil { + tfMap["nat_gateway_id"] = aws.StringValue(v) + } + + if v := apiObject.NetworkInterfaceId; v != nil { + tfMap["network_interface_id"] = aws.StringValue(v) + } + + if v := apiObject.Origin; v != nil { + tfMap["origin"] = aws.StringValue(v) + } + + if v := apiObject.TransitGatewayId; v != nil { + tfMap["transit_gateway_id"] = aws.StringValue(v) + } + + if v := apiObject.VpcPeeringConnectionId; v != nil { + tfMap["vpc_peering_connection_id"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenAnalysisSecurityGroupRule(apiObject *ec2.AnalysisSecurityGroupRule) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Cidr; v != nil { + tfMap["cidr"] = aws.StringValue(v) + } + + if v := apiObject.PortRange; v != nil { + tfMap["port_range"] = []interface{}{flattenPortRange(v)} + } + + if v := apiObject.PrefixListId; v != nil { + tfMap["prefix_list_id"] = aws.StringValue(v) + } + + if v := apiObject.Protocol; v != nil { + tfMap["protocol"] = aws.StringValue(v) + } + + if v := apiObject.SecurityGroupId; v != nil { + tfMap["security_group_id"] = aws.StringValue(v) + } + + return tfMap +} + +func flattenExplanation(apiObject *ec2.Explanation) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.Acl; v != nil { + tfMap["acl"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.AclRule; v != nil { + tfMap["acl_rule"] = []interface{}{flattenAnalysisAclRule(v)} + } + + if v := apiObject.Address; v != nil { + tfMap["address"] = aws.StringValue(v) + } + + if v := apiObject.Addresses; v != nil { + tfMap["addresses"] = aws.StringValueSlice(v) + } + + if v := apiObject.AttachedTo; v != nil { + tfMap["attached_to"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.AvailabilityZones; v != nil { + tfMap["availability_zones"] = aws.StringValueSlice(v) + } + + if v := apiObject.Cidrs; v != nil { + tfMap["cidrs"] = aws.StringValueSlice(v) + } + + if v := apiObject.ClassicLoadBalancerListener; v != nil { + tfMap["classic_load_balancer_listener"] = []interface{}{flattenAnalysisLoadBalancerListener(v)} + } + + if v := apiObject.Component; v != nil { + tfMap["component"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.CustomerGateway; v != nil { + tfMap["customer_gateway"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.Destination; v != nil { + tfMap["destination"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.DestinationVpc; v != nil { + tfMap["destination_vpc"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.Direction; v != nil { + tfMap["direction"] = aws.StringValue(v) + } + + if v := apiObject.ElasticLoadBalancerListener; v != nil { + tfMap["elastic_load_balancer_listener"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.ExplanationCode; v != nil { + tfMap["explanation_code"] = aws.StringValue(v) + } + + if v := apiObject.IngressRouteTable; v != nil { + tfMap["ingress_route_table"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.InternetGateway; v != nil { + tfMap["internet_gateway"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.LoadBalancerArn; v != nil { + tfMap["load_balancer_arn"] = aws.StringValue(v) + } + + if v := apiObject.LoadBalancerListenerPort; v != nil { + tfMap["load_balancer_listener_port"] = aws.Int64Value(v) + } + + if v := apiObject.LoadBalancerTarget; v != nil { + tfMap["load_balancer_target"] = []interface{}{flattenAnalysisLoadBalancerTarget(v)} + } + + if v := apiObject.LoadBalancerTargetGroup; v != nil { + tfMap["load_balancer_target_group"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.LoadBalancerTargetGroups; v != nil { + tfMap["load_balancer_target_group"] = flattenAnalysisComponents(v) + } + + if v := apiObject.LoadBalancerTargetPort; v != nil { + tfMap["load_balancer_target_port"] = aws.Int64Value(v) + } + + if v := apiObject.MissingComponent; v != nil { + tfMap["missing_component"] = aws.StringValue(v) + } + + if v := apiObject.NatGateway; v != nil { + tfMap["nat_gateway"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.NetworkInterface; v != nil { + tfMap["network_interface"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.PacketField; v != nil { + tfMap["packet_field"] = aws.StringValue(v) + } + + if v := apiObject.Port; v != nil { + tfMap["port"] = aws.Int64Value(v) + } + + if v := apiObject.PortRanges; v != nil { + tfMap["port_ranges"] = flattenPortRanges(v) + } + + if v := apiObject.PrefixList; v != nil { + tfMap["prefix_list"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.Protocols; v != nil { + tfMap["protocols"] = aws.StringValueSlice(v) + } + + if v := apiObject.RouteTable; v != nil { + tfMap["route_table"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.RouteTableRoute; v != nil { + tfMap["route_table_route"] = []interface{}{flattenAnalysisRouteTableRoute(v)} + } + + if v := apiObject.SecurityGroup; v != nil { + tfMap["security_group"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.SecurityGroupRule; v != nil { + tfMap["security_group_rule"] = []interface{}{flattenAnalysisSecurityGroupRule(v)} + } + + if v := apiObject.SecurityGroups; v != nil { + tfMap["security_groups"] = flattenAnalysisComponents(v) + } + + if v := apiObject.SourceVpc; v != nil { + tfMap["source_vpc"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.State; v != nil { + tfMap["state"] = aws.StringValue(v) + } + + if v := apiObject.Subnet; v != nil { + tfMap["subnet"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.SubnetRouteTable; v != nil { + tfMap["subnet_route_table"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.TransitGateway; v != nil { + tfMap["transit_gateway"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.TransitGatewayAttachment; v != nil { + tfMap["transit_gateway_attachment"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.TransitGatewayRouteTable; v != nil { + tfMap["transit_gateway_route_table"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.TransitGatewayRouteTableRoute; v != nil { + tfMap["transit_gateway_route_table_route"] = []interface{}{flattenTransitGatewayRouteTableRoute(v)} + } + + if v := apiObject.Vpc; v != nil { + tfMap["vpc"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.VpcEndpoint; v != nil { + tfMap["vpc_endpoint"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.VpcPeeringConnection; v != nil { + tfMap["vpc_peering_connection"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.VpnConnection; v != nil { + tfMap["vpn_connection"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.VpnGateway; v != nil { + tfMap["vpn_gateway"] = []interface{}{flattenAnalysisComponent(v)} + } + + return tfMap +} + +func flattenExplanations(apiObjects []*ec2.Explanation) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, flattenExplanation(apiObject)) + } + + return tfList +} + +func flattenPathComponent(apiObject *ec2.PathComponent) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.AclRule; v != nil { + tfMap["acl_rule"] = []interface{}{flattenAnalysisAclRule(v)} + } + + if v := apiObject.AdditionalDetails; v != nil { + tfMap["additional_details"] = flattenAdditionalDetails(v) + } + + if v := apiObject.Component; v != nil { + tfMap["component"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.DestinationVpc; v != nil { + tfMap["destination_vpc"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.InboundHeader; v != nil { + tfMap["inbound_header"] = []interface{}{flattenAnalysisPacketHeader(v)} + } + + if v := apiObject.OutboundHeader; v != nil { + tfMap["outbound_header"] = []interface{}{flattenAnalysisPacketHeader(v)} + } + + if v := apiObject.RouteTableRoute; v != nil { + tfMap["route_table_route"] = []interface{}{flattenAnalysisRouteTableRoute(v)} + } + + if v := apiObject.SecurityGroupRule; v != nil { + tfMap["security_group_rule"] = []interface{}{flattenAnalysisSecurityGroupRule(v)} + } + + if v := apiObject.SequenceNumber; v != nil { + tfMap["sequence_number"] = aws.Int64Value(v) + } + + if v := apiObject.SourceVpc; v != nil { + tfMap["source_vpc"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.Subnet; v != nil { + tfMap["subnet"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.TransitGateway; v != nil { + tfMap["transit_gateway"] = []interface{}{flattenAnalysisComponent(v)} + } + + if v := apiObject.TransitGatewayRouteTableRoute; v != nil { + tfMap["transit_gateway_route_table_route"] = []interface{}{flattenTransitGatewayRouteTableRoute(v)} + } + + if v := apiObject.Vpc; v != nil { + tfMap["vpc"] = []interface{}{flattenAnalysisComponent(v)} + } + + return tfMap +} + +func flattenPathComponents(apiObjects []*ec2.PathComponent) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, flattenPathComponent(apiObject)) + } + + return tfList +} + +func flattenPortRange(apiObject *ec2.PortRange) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.From; v != nil { + tfMap["from"] = aws.Int64Value(v) + } + + if v := apiObject.To; v != nil { + tfMap["to"] = aws.Int64Value(v) + } + + return tfMap +} + +func flattenPortRanges(apiObjects []*ec2.PortRange) []interface{} { + if len(apiObjects) == 0 { + return nil + } + + var tfList []interface{} + + for _, apiObject := range apiObjects { + if apiObject == nil { + continue + } + + tfList = append(tfList, flattenPortRange(apiObject)) + } + + return tfList +} + +func flattenTransitGatewayRouteTableRoute(apiObject *ec2.TransitGatewayRouteTableRoute) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.AttachmentId; v != nil { + tfMap["attachment_id"] = aws.StringValue(v) + } + + if v := apiObject.DestinationCidr; v != nil { + tfMap["destination_cidr"] = aws.StringValue(v) + } + + if v := apiObject.PrefixListId; v != nil { + tfMap["prefix_list_id"] = aws.StringValue(v) + } + + if v := apiObject.ResourceId; v != nil { + tfMap["resource_id"] = aws.StringValue(v) + } + + if v := apiObject.ResourceType; v != nil { + tfMap["resource_type"] = aws.StringValue(v) + } + + if v := apiObject.RouteOrigin; v != nil { + tfMap["route_orign"] = aws.StringValue(v) + } + + if v := apiObject.State; v != nil { + tfMap["state"] = aws.StringValue(v) + } + + return tfMap +} diff --git a/internal/service/ec2/vpc_network_insights_analysis_data_source.go b/internal/service/ec2/vpc_network_insights_analysis_data_source.go new file mode 100644 index 000000000000..8d51d2dba737 --- /dev/null +++ b/internal/service/ec2/vpc_network_insights_analysis_data_source.go @@ -0,0 +1,138 @@ +package ec2 + +import ( + "context" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "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" +) + +func DataSourceNetworkInsightsAnalysis() *schema.Resource { + return &schema.Resource{ + ReadWithoutTimeout: dataSourceNetworkInsightsAnalysisRead, + + Schema: map[string]*schema.Schema{ + "alternate_path_hints": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "component_arn": { + Type: schema.TypeString, + Computed: true, + }, + "component_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "explanations": networkInsightsAnalysisExplanationsSchema, + "filter": CustomFiltersSchema(), + "filter_in_arns": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "forward_path_components": networkInsightsAnalysisPathComponentsSchema, + "network_insights_analysis_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "network_insights_path_id": { + Type: schema.TypeString, + Computed: true, + }, + "path_found": { + Type: schema.TypeBool, + Computed: true, + }, + "return_path_components": networkInsightsAnalysisPathComponentsSchema, + "start_date": { + Type: schema.TypeString, + Computed: true, + }, + "status": { + Type: schema.TypeString, + Computed: true, + }, + "status_message": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tftags.TagsSchemaComputed(), + "warning_message": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceNetworkInsightsAnalysisRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).EC2Conn + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + + input := &ec2.DescribeNetworkInsightsAnalysesInput{} + + if v, ok := d.GetOk("network_insights_analysis_id"); ok { + input.NetworkInsightsAnalysisIds = aws.StringSlice([]string{v.(string)}) + } + + input.Filters = append(input.Filters, BuildCustomFilterList( + d.Get("filter").(*schema.Set), + )...) + + if len(input.Filters) == 0 { + // Don't send an empty filters list; the EC2 API won't accept it. + input.Filters = nil + } + + output, err := FindNetworkInsightsAnalysis(ctx, conn, input) + + if err != nil { + return diag.FromErr(tfresource.SingularDataSourceFindError("EC2 Network Insights Analysis", err)) + } + + networkInsightsAnalysisID := aws.StringValue(output.NetworkInsightsAnalysisId) + d.SetId(networkInsightsAnalysisID) + if err := d.Set("alternate_path_hints", flattenAlternatePathHints(output.AlternatePathHints)); err != nil { + return diag.Errorf("setting alternate_path_hints: %s", err) + } + d.Set("arn", output.NetworkInsightsAnalysisArn) + if err := d.Set("explanations", flattenExplanations(output.Explanations)); err != nil { + return diag.Errorf("setting explanations: %s", err) + } + d.Set("filter_in_arns", aws.StringValueSlice(output.FilterInArns)) + if err := d.Set("forward_path_components", flattenPathComponents(output.ForwardPathComponents)); err != nil { + return diag.Errorf("setting forward_path_components: %s", err) + } + d.Set("network_insights_analysis_id", networkInsightsAnalysisID) + d.Set("network_insights_path_id", output.NetworkInsightsPathId) + d.Set("path_found", output.NetworkPathFound) + if err := d.Set("return_path_components", flattenPathComponents(output.ReturnPathComponents)); err != nil { + return diag.Errorf("setting return_path_components: %s", err) + } + d.Set("start_date", output.StartDate.Format(time.RFC3339)) + d.Set("status", output.Status) + d.Set("status_message", output.StatusMessage) + d.Set("warning_message", output.WarningMessage) + + if err := d.Set("tags", KeyValueTags(output.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return diag.Errorf("setting tags: %s", err) + } + + return nil +} diff --git a/internal/service/ec2/vpc_network_insights_analysis_data_source_test.go b/internal/service/ec2/vpc_network_insights_analysis_data_source_test.go new file mode 100644 index 000000000000..d5dd6645ca7f --- /dev/null +++ b/internal/service/ec2/vpc_network_insights_analysis_data_source_test.go @@ -0,0 +1,61 @@ +package ec2_test + +import ( + "fmt" + "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-provider-aws/internal/acctest" +) + +func TestAccVPCNetworkInsightsAnalysisDataSource_basic(t *testing.T) { + resourceName := "aws_ec2_network_insights_analysis.test" + datasourceName := "data.aws_ec2_network_insights_analysis.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccVPCNetworkInsightsAnalysisDataSourceConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(datasourceName, "alternate_path_hints.#", resourceName, "alternate_path_hints.#"), + resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasourceName, "explanations.#", resourceName, "explanations.#"), + resource.TestCheckResourceAttrPair(datasourceName, "filter_in_arns.#", resourceName, "filter_in_arns.#"), + resource.TestCheckResourceAttrPair(datasourceName, "forward_path_components.#", resourceName, "forward_path_components.#"), + resource.TestCheckResourceAttrPair(datasourceName, "network_insights_analysis_id", resourceName, "id"), + resource.TestCheckResourceAttrPair(datasourceName, "network_insights_path_id", resourceName, "network_insights_path_id"), + resource.TestCheckResourceAttrPair(datasourceName, "path_found", resourceName, "path_found"), + resource.TestCheckResourceAttrPair(datasourceName, "return_path_components.#", resourceName, "return_path_components.#"), + resource.TestCheckResourceAttrPair(datasourceName, "start_date", resourceName, "start_date"), + resource.TestCheckResourceAttrPair(datasourceName, "status", resourceName, "status"), + resource.TestCheckResourceAttrPair(datasourceName, "status_message", resourceName, "status_message"), + resource.TestCheckResourceAttrPair(datasourceName, "tags.%", resourceName, "tags.%"), + resource.TestCheckResourceAttrPair(datasourceName, "warning_message", resourceName, "warning_message"), + ), + }, + }, + }) +} + +func testAccVPCNetworkInsightsAnalysisDataSourceConfig_basic(rName string) string { + return acctest.ConfigCompose(testAccVPCNetworkInsightsAnalysisConfig_base(rName), fmt.Sprintf(` +resource "aws_ec2_network_insights_analysis" "test" { + network_insights_path_id = aws_ec2_network_insights_path.test.id + wait_for_completion = true + + tags = { + Name = %[1]q + } +} + +data "aws_ec2_network_insights_analysis" "test" { + network_insights_analysis_id = aws_ec2_network_insights_analysis.test.id +} +`, rName)) +} diff --git a/internal/service/ec2/vpc_network_insights_analysis_test.go b/internal/service/ec2/vpc_network_insights_analysis_test.go new file mode 100644 index 000000000000..9ac7202ab1c6 --- /dev/null +++ b/internal/service/ec2/vpc_network_insights_analysis_test.go @@ -0,0 +1,317 @@ +package ec2_test + +import ( + "context" + "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 TestAccVPCNetworkInsightsAnalysis_basic(t *testing.T) { + resourceName := "aws_ec2_network_insights_analysis.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckNetworkInsightsAnalysisDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVPCNetworkInsightsAnalysisConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckNetworkInsightsAnalysisExists(resourceName), + acctest.MatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`network-insights-analysis/.+$`)), + resource.TestCheckResourceAttr(resourceName, "filter_in_arns.#", "0"), + resource.TestCheckResourceAttrPair(resourceName, "network_insights_path_id", "aws_ec2_network_insights_path.test", "id"), + resource.TestCheckResourceAttr(resourceName, "path_found", "true"), + acctest.CheckResourceAttrRFC3339(resourceName, "start_date"), + resource.TestCheckResourceAttr(resourceName, "status", "succeeded"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "wait_for_completion", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"wait_for_completion"}, + }, + }, + }) +} + +func TestAccVPCNetworkInsightsAnalysis_disappears(t *testing.T) { + resourceName := "aws_ec2_network_insights_analysis.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckNetworkInsightsAnalysisDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVPCNetworkInsightsAnalysisConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsAnalysisExists(resourceName), + acctest.CheckResourceDisappears(acctest.Provider, tfec2.ResourceNetworkInsightsAnalysis(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccVPCNetworkInsightsAnalysis_tags(t *testing.T) { + resourceName := "aws_ec2_network_insights_analysis.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckNetworkInsightsAnalysisDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVPCNetworkInsightsAnalysisConfig_tags1(rName, "key1", "value1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsAnalysisExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"wait_for_completion"}, + }, + { + Config: testAccVPCNetworkInsightsAnalysisConfig_tags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsAnalysisExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccVPCNetworkInsightsAnalysisConfig_tags1(rName, "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsAnalysisExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + }, + }) +} + +func TestAccVPCNetworkInsightsAnalysis_filterInARNs(t *testing.T) { + resourceName := "aws_ec2_network_insights_analysis.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckNetworkInsightsAnalysisDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVPCNetworkInsightsAnalysisConfig_filterInARNs(rName, "vpc-peering-connection/pcx-fakearn1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsAnalysisExists(resourceName), + acctest.MatchResourceAttrRegionalARN(resourceName, "filter_in_arns.0", "ec2", regexp.MustCompile(`vpc-peering-connection/pcx-fakearn1$`)), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"wait_for_completion"}, + }, + { + Config: testAccVPCNetworkInsightsAnalysisConfig_filterInARNs(rName, "vpc-peering-connection/pcx-fakearn2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsAnalysisExists(resourceName), + acctest.MatchResourceAttrRegionalARN(resourceName, "filter_in_arns.0", "ec2", regexp.MustCompile(`vpc-peering-connection/pcx-fakearn2$`)), + ), + }, + }, + }) +} + +func TestAccVPCNetworkInsightsAnalysis_waitForCompletion(t *testing.T) { + resourceName := "aws_ec2_network_insights_analysis.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, ec2.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckNetworkInsightsAnalysisDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVPCNetworkInsightsAnalysisConfig_waitForCompletion(rName, false), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsAnalysisExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "wait_for_completion", "false"), + resource.TestCheckResourceAttr(resourceName, "status", "running"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"wait_for_completion"}, + }, + { + Config: testAccVPCNetworkInsightsAnalysisConfig_waitForCompletion(rName, true), + Check: resource.ComposeTestCheckFunc( + testAccCheckNetworkInsightsAnalysisExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "wait_for_completion", "true"), + ), + }, + }, + }) +} + +func testAccCheckNetworkInsightsAnalysisExists(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 Analysis ID is set") + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn + + _, err := tfec2.FindNetworkInsightsAnalysisByID(context.Background(), conn, rs.Primary.ID) + + return err + } +} + +func testAccCheckNetworkInsightsAnalysisDestroy(s *terraform.State) error { + conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_ec2_network_insights_analysis" { + continue + } + + _, err := tfec2.FindNetworkInsightsAnalysisByID(context.Background(), conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue + } + + if err != nil { + return err + } + + return fmt.Errorf("EC2 Network Insights Analysis %s still exists", rs.Primary.ID) + } + + return nil +} + +func testAccVPCNetworkInsightsAnalysisConfig_base(rName string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` +resource "aws_network_interface" "test" { + count = 2 + + subnet_id = aws_subnet.test[0].id + + tags = { + Name = %[1]q + } +} + +resource "aws_ec2_network_insights_path" "test" { + source = aws_network_interface.test[0].id + destination = aws_network_interface.test[1].id + protocol = "tcp" + + tags = { + Name = %[1]q + } +} +`, rName)) +} + +func testAccVPCNetworkInsightsAnalysisConfig_basic(rName string) string { + return acctest.ConfigCompose(testAccVPCNetworkInsightsAnalysisConfig_base(rName), ` +resource "aws_ec2_network_insights_analysis" "test" { + network_insights_path_id = aws_ec2_network_insights_path.test.id +} +`) +} + +func testAccVPCNetworkInsightsAnalysisConfig_tags1(rName, tagKey1, tagValue1 string) string { + return acctest.ConfigCompose(testAccVPCNetworkInsightsAnalysisConfig_base(rName), fmt.Sprintf(` +resource "aws_ec2_network_insights_analysis" "test" { + network_insights_path_id = aws_ec2_network_insights_path.test.id + + tags = { + %[1]q = %[2]q + } +} +`, tagKey1, tagValue1)) +} + +func testAccVPCNetworkInsightsAnalysisConfig_tags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return acctest.ConfigCompose(testAccVPCNetworkInsightsAnalysisConfig_base(rName), fmt.Sprintf(` +resource "aws_ec2_network_insights_analysis" "test" { + network_insights_path_id = aws_ec2_network_insights_path.test.id + + tags = { + %[1]q = %[2]q + %[3]q = %[4]q + } +} +`, tagKey1, tagValue1, tagKey2, tagValue2)) +} + +func testAccVPCNetworkInsightsAnalysisConfig_filterInARNs(rName, arnSuffix string) string { + return acctest.ConfigCompose(testAccVPCNetworkInsightsAnalysisConfig_base(rName), fmt.Sprintf(` +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} +data "aws_partition" "current" {} + +resource "aws_ec2_network_insights_analysis" "test" { + network_insights_path_id = aws_ec2_network_insights_path.test.id + filter_in_arns = ["arn:${data.aws_partition.current.partition}:ec2:${data.aws_region.current.name}:${data.aws_caller_identity.current.id}:%[2]s"] + + tags = { + Name = %[1]q + } +} +`, rName, arnSuffix)) +} + +func testAccVPCNetworkInsightsAnalysisConfig_waitForCompletion(rName string, waitForCompletion bool) string { + return acctest.ConfigCompose(testAccVPCNetworkInsightsAnalysisConfig_base(rName), fmt.Sprintf(` +resource "aws_ec2_network_insights_analysis" "test" { + network_insights_path_id = aws_ec2_network_insights_path.test.id + wait_for_completion = %[2]t + + tags = { + Name = %[1]q + } +} +`, rName, waitForCompletion)) +} diff --git a/internal/service/ec2/vpc_network_insights_path.go b/internal/service/ec2/vpc_network_insights_path.go index 4f5f1f008ad9..710a04b6f496 100644 --- a/internal/service/ec2/vpc_network_insights_path.go +++ b/internal/service/ec2/vpc_network_insights_path.go @@ -99,7 +99,7 @@ func resourceNetworkInsightsPathCreate(ctx context.Context, d *schema.ResourceDa output, err := conn.CreateNetworkInsightsPathWithContext(ctx, input) if err != nil { - return diag.Errorf("error creating EC2 Network Insights Path: %s", err) + return diag.Errorf("creating EC2 Network Insights Path: %s", err) } d.SetId(aws.StringValue(output.NetworkInsightsPath.NetworkInsightsPathId)) @@ -112,7 +112,7 @@ func resourceNetworkInsightsPathRead(ctx context.Context, d *schema.ResourceData defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig - nip, err := FindNetworkInsightsPathByID(conn, d.Id()) + nip, err := FindNetworkInsightsPathByID(ctx, conn, d.Id()) if !d.IsNewResource() && tfresource.NotFound(err) { log.Printf("[WARN] EC2 Network Insights Path %s not found, removing from state", d.Id()) @@ -121,7 +121,7 @@ func resourceNetworkInsightsPathRead(ctx context.Context, d *schema.ResourceData } if err != nil { - return diag.Errorf("error reading EC2 Network Insights Path (%s): %s", d.Id(), err) + return diag.Errorf("reading EC2 Network Insights Path (%s): %s", d.Id(), err) } d.Set("arn", nip.NetworkInsightsPathArn) @@ -136,11 +136,11 @@ func resourceNetworkInsightsPathRead(ctx context.Context, d *schema.ResourceData //lintignore:AWSR002 if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { - return diag.Errorf("error setting tags: %s", err) + return diag.Errorf("setting tags: %s", err) } if err := d.Set("tags_all", tags.Map()); err != nil { - return diag.Errorf("error setting tags_all: %s", err) + return diag.Errorf("setting tags_all: %s", err) } return nil @@ -152,8 +152,8 @@ func resourceNetworkInsightsPathUpdate(ctx context.Context, d *schema.ResourceDa 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) + if err := UpdateTagsWithContext(ctx, conn, d.Id(), o, n); err != nil { + return diag.Errorf("updating EC2 Network Insights Path (%s) tags: %s", d.Id(), err) } } @@ -173,7 +173,7 @@ func resourceNetworkInsightsPathDelete(ctx context.Context, d *schema.ResourceDa } if err != nil { - return diag.Errorf("error deleting EC2 Network Insights Path (%s): %s", d.Id(), err) + return diag.Errorf("deleting EC2 Network Insights Path (%s): %s", d.Id(), err) } return nil diff --git a/internal/service/ec2/vpc_network_insights_path_data_source.go b/internal/service/ec2/vpc_network_insights_path_data_source.go new file mode 100644 index 000000000000..caa814c7a3ca --- /dev/null +++ b/internal/service/ec2/vpc_network_insights_path_data_source.go @@ -0,0 +1,100 @@ +package ec2 + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "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" +) + +func DataSourceNetworkInsightsPath() *schema.Resource { + return &schema.Resource{ + ReadWithoutTimeout: dataSourceNetworkInsightsPathRead, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "destination": { + Type: schema.TypeString, + Computed: true, + }, + "destination_ip": { + Type: schema.TypeString, + Computed: true, + }, + "destination_port": { + Type: schema.TypeInt, + Computed: true, + }, + "filter": CustomFiltersSchema(), + "network_insights_path_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "protocol": { + Type: schema.TypeString, + Computed: true, + }, + "source": { + Type: schema.TypeString, + Computed: true, + }, + "source_ip": { + Type: schema.TypeString, + Computed: true, + }, + "tags": tftags.TagsSchemaComputed(), + }, + } +} + +func dataSourceNetworkInsightsPathRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*conns.AWSClient).EC2Conn + ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig + + input := &ec2.DescribeNetworkInsightsPathsInput{} + + if v, ok := d.GetOk("network_insights_path_id"); ok { + input.NetworkInsightsPathIds = aws.StringSlice([]string{v.(string)}) + } + + input.Filters = append(input.Filters, BuildCustomFilterList( + d.Get("filter").(*schema.Set), + )...) + + if len(input.Filters) == 0 { + // Don't send an empty filters list; the EC2 API won't accept it. + input.Filters = nil + } + + nip, err := FindNetworkInsightsPath(ctx, conn, input) + + if err != nil { + return diag.FromErr(tfresource.SingularDataSourceFindError("EC2 Network Insights Path", err)) + } + + networkInsightsPathID := aws.StringValue(nip.NetworkInsightsPathId) + d.SetId(networkInsightsPathID) + d.Set("arn", nip.NetworkInsightsPathArn) + d.Set("destination", nip.Destination) + d.Set("destination_ip", nip.DestinationIp) + d.Set("destination_port", nip.DestinationPort) + d.Set("network_insights_path_id", networkInsightsPathID) + d.Set("protocol", nip.Protocol) + d.Set("source", nip.Source) + d.Set("source_ip", nip.SourceIp) + + if err := d.Set("tags", KeyValueTags(nip.Tags).IgnoreAWS().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return diag.Errorf("setting tags: %s", err) + } + + return nil +} diff --git a/internal/service/ec2/vpc_network_insights_path_data_source_test.go b/internal/service/ec2/vpc_network_insights_path_data_source_test.go new file mode 100644 index 000000000000..f7e66d2d910f --- /dev/null +++ b/internal/service/ec2/vpc_network_insights_path_data_source_test.go @@ -0,0 +1,68 @@ +package ec2_test + +import ( + "fmt" + "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-provider-aws/internal/acctest" +) + +func TestAccVPCNetworkInsightsPathDataSource_basic(t *testing.T) { + resourceName := "aws_ec2_network_insights_path.test" + datasourceName := "data.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), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccVPCNetworkInsightsPathDataSourceConfig_basic(rName), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttrPair(datasourceName, "arn", resourceName, "arn"), + resource.TestCheckResourceAttrPair(datasourceName, "destination", resourceName, "destination"), + resource.TestCheckResourceAttrPair(datasourceName, "destination_ip", resourceName, "destination_ip"), + resource.TestCheckResourceAttrPair(datasourceName, "destination_port", resourceName, "destination_port"), + resource.TestCheckResourceAttrPair(datasourceName, "network_insights_path_id", resourceName, "id"), + resource.TestCheckResourceAttrPair(datasourceName, "protocol", resourceName, "protocol"), + resource.TestCheckResourceAttrPair(datasourceName, "source", resourceName, "source"), + resource.TestCheckResourceAttrPair(datasourceName, "source_ip", resourceName, "source_ip"), + resource.TestCheckResourceAttrPair(datasourceName, "tags.%", resourceName, "tags.%"), + ), + }, + }, + }) +} + +func testAccVPCNetworkInsightsPathDataSourceConfig_basic(rName string) string { + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` +resource "aws_network_interface" "test" { + count = 2 + + subnet_id = aws_subnet.test[0].id + + tags = { + Name = %[1]q + } +} + +resource "aws_ec2_network_insights_path" "test" { + source = aws_network_interface.test[0].id + destination = aws_network_interface.test[1].id + destination_port = 443 + protocol = "tcp" + + tags = { + Name = %[1]q + } +} + +data "aws_ec2_network_insights_path" "test" { + network_insights_path_id = aws_ec2_network_insights_path.test.id +} +`, rName)) +} diff --git a/internal/service/ec2/vpc_network_insights_path_test.go b/internal/service/ec2/vpc_network_insights_path_test.go index f20528bc9385..f41cf704024d 100644 --- a/internal/service/ec2/vpc_network_insights_path_test.go +++ b/internal/service/ec2/vpc_network_insights_path_test.go @@ -1,6 +1,7 @@ package ec2_test import ( + "context" "fmt" "regexp" "testing" @@ -30,11 +31,11 @@ func TestAccVPCNetworkInsightsPath_basic(t *testing.T) { 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.TestCheckResourceAttrPair(resourceName, "destination", "aws_network_interface.test.1", "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.TestCheckResourceAttrPair(resourceName, "source", "aws_network_interface.test.0", "id"), resource.TestCheckResourceAttr(resourceName, "source_ip", ""), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), @@ -226,7 +227,7 @@ func testAccCheckNetworkInsightsPathExists(n string) resource.TestCheckFunc { conn := acctest.Provider.Meta().(*conns.AWSClient).EC2Conn - _, err := tfec2.FindNetworkInsightsPathByID(conn, rs.Primary.ID) + _, err := tfec2.FindNetworkInsightsPathByID(context.Background(), conn, rs.Primary.ID) return err } @@ -240,7 +241,7 @@ func testAccCheckNetworkInsightsPathDestroy(s *terraform.State) error { continue } - _, err := tfec2.FindNetworkInsightsPathByID(conn, rs.Primary.ID) + _, err := tfec2.FindNetworkInsightsPathByID(context.Background(), conn, rs.Primary.ID) if tfresource.NotFound(err) { continue @@ -257,34 +258,11 @@ func testAccCheckNetworkInsightsPathDestroy(s *terraform.State) error { } func testAccVPCNetworkInsightsPathConfig_basic(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 - } -} + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` +resource "aws_network_interface" "test" { + count = 2 -resource "aws_network_interface" "test_destination" { - subnet_id = aws_subnet.test.id + subnet_id = aws_subnet.test[0].id tags = { Name = %[1]q @@ -292,42 +270,19 @@ resource "aws_network_interface" "test_destination" { } resource "aws_ec2_network_insights_path" "test" { - source = aws_network_interface.test_source.id - destination = aws_network_interface.test_destination.id + source = aws_network_interface.test[0].id + destination = aws_network_interface.test[1].id protocol = %[2]q } -`, rName, protocol) +`, rName, protocol)) } func testAccVPCNetworkInsightsPathConfig_tags1(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 - } -} + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` +resource "aws_network_interface" "test" { + count = 2 -resource "aws_network_interface" "test_destination" { - subnet_id = aws_subnet.test.id + subnet_id = aws_subnet.test[0].id tags = { Name = %[1]q @@ -335,46 +290,23 @@ resource "aws_network_interface" "test_destination" { } resource "aws_ec2_network_insights_path" "test" { - source = aws_network_interface.test_source.id - destination = aws_network_interface.test_destination.id + source = aws_network_interface.test[0].id + destination = aws_network_interface.test[1].id protocol = "tcp" tags = { %[2]q = %[3]q } } -`, rName, tagKey1, tagValue1) +`, rName, tagKey1, tagValue1)) } func testAccVPCNetworkInsightsPathConfig_tags2(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 - } -} + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` +resource "aws_network_interface" "test" { + count = 2 -resource "aws_network_interface" "test_destination" { - subnet_id = aws_subnet.test.id + subnet_id = aws_subnet.test[0].id tags = { Name = %[1]q @@ -382,8 +314,8 @@ resource "aws_network_interface" "test_destination" { } resource "aws_ec2_network_insights_path" "test" { - source = aws_network_interface.test_source.id - destination = aws_network_interface.test_destination.id + source = aws_network_interface.test[0].id + destination = aws_network_interface.test[1].id protocol = "tcp" tags = { @@ -391,19 +323,11 @@ resource "aws_ec2_network_insights_path" "test" { %[4]q = %[5]q } } -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +`, rName, tagKey1, tagValue1, tagKey2, tagValue2)) } func testAccVPCNetworkInsightsPathConfig_sourceIP(rName, sourceIP string) string { - return fmt.Sprintf(` -resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" - - tags = { - Name = %[1]q - } -} - + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` resource "aws_internet_gateway" "test" { vpc_id = aws_vpc.test.id @@ -412,17 +336,8 @@ resource "aws_internet_gateway" "test" { } } -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 + subnet_id = aws_subnet.test[0].id tags = { Name = %[1]q @@ -439,19 +354,11 @@ resource "aws_ec2_network_insights_path" "test" { Name = %[1]q } } -`, rName, sourceIP) +`, rName, sourceIP)) } func testAccVPCNetworkInsightsPathConfig_destinationIP(rName, destinationIP string) string { - return fmt.Sprintf(` -resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" - - tags = { - Name = %[1]q - } -} - + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` resource "aws_internet_gateway" "test" { vpc_id = aws_vpc.test.id @@ -460,17 +367,8 @@ resource "aws_internet_gateway" "test" { } } -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 + subnet_id = aws_subnet.test[0].id tags = { Name = %[1]q @@ -487,38 +385,15 @@ resource "aws_ec2_network_insights_path" "test" { Name = %[1]q } } -`, rName, destinationIP) +`, rName, destinationIP)) } func testAccVPCNetworkInsightsPathConfig_destinationPort(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 - } -} + return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 1), fmt.Sprintf(` +resource "aws_network_interface" "test" { + count = 2 -resource "aws_network_interface" "test_destination" { - subnet_id = aws_subnet.test.id + subnet_id = aws_subnet.test[0].id tags = { Name = %[1]q @@ -526,8 +401,8 @@ resource "aws_network_interface" "test_destination" { } resource "aws_ec2_network_insights_path" "test" { - source = aws_network_interface.test_source.id - destination = aws_network_interface.test_destination.id + source = aws_network_interface.test[0].id + destination = aws_network_interface.test[1].id protocol = "tcp" destination_port = %[2]d @@ -535,5 +410,5 @@ resource "aws_ec2_network_insights_path" "test" { Name = %[1]q } } -`, rName, destinationPort) +`, rName, destinationPort)) } diff --git a/internal/service/ec2/wait.go b/internal/service/ec2/wait.go index 7148cacc6d36..78b2c82dda24 100644 --- a/internal/service/ec2/wait.go +++ b/internal/service/ec2/wait.go @@ -2270,6 +2270,27 @@ func WaitManagedPrefixListDeleted(conn *ec2.EC2, id string) (*ec2.ManagedPrefixL return nil, err } +func WaitNetworkInsightsAnalysisCreated(ctx context.Context, conn *ec2.EC2, id string, timeout time.Duration) (*ec2.NetworkInsightsAnalysis, error) { + stateConf := &resource.StateChangeConf{ + Pending: []string{ec2.AnalysisStatusRunning}, + Target: []string{ec2.AnalysisStatusSucceeded}, + Timeout: timeout, + Refresh: StatusNetworkInsightsAnalysis(ctx, conn, id), + Delay: 10 * time.Second, + MinTimeout: 5 * time.Second, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if output, ok := outputRaw.(*ec2.NetworkInsightsAnalysis); ok { + tfresource.SetLastError(err, errors.New(aws.StringValue(output.StatusMessage))) + + return output, err + } + + return nil, err +} + const ( networkInterfaceAttachedTimeout = 5 * time.Minute NetworkInterfaceDetachedTimeout = 10 * time.Minute diff --git a/website/docs/d/ec2_network_insights_analysis.html.markdown b/website/docs/d/ec2_network_insights_analysis.html.markdown new file mode 100644 index 000000000000..eb19cce99b69 --- /dev/null +++ b/website/docs/d/ec2_network_insights_analysis.html.markdown @@ -0,0 +1,52 @@ +--- +subcategory: "VPC (Virtual Private Cloud)" +layout: "aws" +page_title: "AWS: aws_ec2_network_insights_analysis" +description: |- + Provides details about a specific Network Insights Analysis. +--- + +# Data Source: aws_ec2_network_insights_analysis + +`aws_ec2_network_insights_analysis` provides details about a specific Network Insights Analysis. + +## Example Usage + +```terraform +data "aws_ec2_network_insights_analysis" "example" { + network_insights_analysis_id = aws_ec2_network_insights_analysis.example.id +} +``` + +## Argument Reference + +The arguments of this data source act as filters for querying the available +Network Insights Analyses. The given filters must match exactly one Network Insights Analysis +whose data will be exported as attributes. + +* `network_insights_analysis_id` - (Optional) The ID of the Network Insights Analysis to select. +* `filter` - (Optional) Configuration block(s) for filtering. Detailed below. + +### filter Configuration Block + +The following arguments are supported by the `filter` configuration block: + +* `name` - (Required) The name of the filter field. Valid values can be found in the EC2 [`DescribeNetworkInsightsAnalyses`](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeNetworkInsightsAnalyses.html) API Reference. +* `values` - (Required) Set of values that are accepted for the given filter field. Results will be selected if any given value matches. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `alternate_path_hints` - Potential intermediate components of a feasible path. +* `arn` - The ARN of the selected Network Insights Analysis. +* `explanations` - Explanation codes for an unreachable path. +* `filter_in_arns` - The Amazon Resource Names (ARN) of the AWS resources that the path must traverse. +* `forward_path_components` - The components in the path from source to destination. +* `network_insights_path_id` - The ID of the path. +* `path_found` - Set to `true` if the destination was reachable. +* `return_path_components` - The components in the path from destination to source. +* `start_date` - The date/time the analysis was started. +* `status` - The status of the analysis. `succeeded` means the analysis was completed, not that a path was found, for that see `path_found`. +* `status_message` - A message to provide more context when the `status` is `failed`. +* `warning_message` - The warning message. diff --git a/website/docs/d/ec2_network_insights_path.html.markdown b/website/docs/d/ec2_network_insights_path.html.markdown new file mode 100644 index 000000000000..68057909c4f6 --- /dev/null +++ b/website/docs/d/ec2_network_insights_path.html.markdown @@ -0,0 +1,48 @@ +--- +subcategory: "VPC (Virtual Private Cloud)" +layout: "aws" +page_title: "AWS: aws_ec2_network_insights_path" +description: |- + Provides details about a specific Network Insights Path. +--- + +# Data Source: aws_ec2_network_insights_path + +`aws_ec2_network_insights_path` provides details about a specific Network Insights Path. + +## Example Usage + +```terraform +data "aws_ec2_network_insights_path" "example" { + network_insights_path_id = aws_ec2_network_insights_path.example.id +} +``` + +## Argument Reference + +The arguments of this data source act as filters for querying the available +Network Insights Paths. The given filters must match exactly one Network Insights Path +whose data will be exported as attributes. + +* `network_insights_path_id` - (Optional) The ID of the Network Insights Path to select. +* `filter` - (Optional) Configuration block(s) for filtering. Detailed below. + +### filter Configuration Block + +The following arguments are supported by the `filter` configuration block: + +* `name` - (Required) The name of the filter field. Valid values can be found in the EC2 [`DescribeNetworkInsightsPaths`](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeNetworkInsightsPaths.html) API Reference. +* `values` - (Required) Set of values that are accepted for the given filter field. Results will be selected if any given value matches. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `arn` - The ARN of the selected Network Insights Path. +* `destination` - The AWS resource that is the destination of the path. +* `destination_ip` - The IP address of the AWS resource that is the destination of the path. +* `destination_port` - The destination port. +* `protocol` - The protocol. +* `source` - The AWS resource that is the source of the path. +* `source_ip` - The IP address of the AWS resource that is the source of the path. +* `tags` - A map of tags assigned to the resource. diff --git a/website/docs/r/ec2_network_insights_analysis.html.markdown b/website/docs/r/ec2_network_insights_analysis.html.markdown new file mode 100644 index 000000000000..97675bcec5f0 --- /dev/null +++ b/website/docs/r/ec2_network_insights_analysis.html.markdown @@ -0,0 +1,67 @@ +--- +subcategory: "VPC (Virtual Private Cloud)" +layout: "aws" +page_title: "AWS: aws_ec2_network_insights_analysis" +description: |- + Provides a Network Insights Analysis resource. +--- + +# Resource: aws_ec2_network_insights_analysis + +Provides a Network Insights Analysis resource. Part of the "Reachability Analyzer" service in the AWS VPC console. + +## Example Usage + +```terraform +resource "aws_ec2_network_insights_path" "path" { + source = aws_network_interface.source.id + destination = aws_network_interface.destination.id + protocol = "tcp" +} + +resource "aws_ec2_network_insights_analysis" "analysis" { + network_insights_path_id = aws_ec2_network_insights_path.path.id +} +``` + +## Argument Reference + +The following arguments are required: + +* `network_insights_path_id` - (Required) ID of the Network Insights Path to run an analysis on. + +The following arguments are optional: + +* `filter_in_arns` - (Optional) A list of ARNs for resources the path must traverse. +* `wait_for_completion` - (Optional) If enabled, the resource will wait for the Network Insights Analysis status to change to `succeeded` or `failed`. Setting this to `false` will skip the process. Default: `true`. +* `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: + +* `alternate_path_hints` - Potential intermediate components of a feasible path. Described below. +* `arn` - ARN of the Network Insights Analysis. +* `explanations` - Explanation codes for an unreachable path. See the [AWS documentation](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_Explanation.html) for details. +* `forward_path_components` - The components in the path from source to destination. See the [AWS documentation](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_PathComponent.html) for details. +* `id` - ID of the Network Insights Analysis. +* `path_found` - Set to `true` if the destination was reachable. +* `return_path_components` - The components in the path from destination to source. See the [AWS documentation](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_PathComponent.html) for details. +* `start_date` - The date/time the analysis was started. +* `status` - The status of the analysis. `succeeded` means the analysis was completed, not that a path was found, for that see `path_found`. +* `status_message` - A message to provide more context when the `status` is `failed`. +* `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). +* `warning_message` - The warning message. + +The `alternate_path_hints` object supports the following: + +* `component_arn` - The Amazon Resource Name (ARN) of the component. +* `component_id` - The ID of the component. + +## Import + +Network Insights Analyses can be imported using the `id`, e.g., + +``` +$ terraform import aws_ec2_network_insights_analysis.test nia-0462085c957f11a55 +```