From 03854f623a51bf2d96801f784e17bd09a3954d6a Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Tue, 30 Mar 2021 23:39:01 -0400 Subject: [PATCH 01/11] new resource: securityhub_insight --- .../service/securityhub/finder/finder.go | 21 + aws/provider.go | 1 + aws/resource_aws_securityhub_insight.go | 1241 +++++++++++++++++ aws/resource_aws_securityhub_insight_test.go | 720 ++++++++++ aws/resource_aws_securityhub_test.go | 13 + .../docs/r/securityhub_insight.html.markdown | 388 ++++++ 6 files changed, 2384 insertions(+) create mode 100644 aws/resource_aws_securityhub_insight.go create mode 100644 aws/resource_aws_securityhub_insight_test.go create mode 100644 website/docs/r/securityhub_insight.html.markdown diff --git a/aws/internal/service/securityhub/finder/finder.go b/aws/internal/service/securityhub/finder/finder.go index e13cd6a0708..ecb245bb2ad 100644 --- a/aws/internal/service/securityhub/finder/finder.go +++ b/aws/internal/service/securityhub/finder/finder.go @@ -1,6 +1,8 @@ package finder import ( + "context" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/securityhub" ) @@ -30,3 +32,22 @@ func AdminAccount(conn *securityhub.SecurityHub, adminAccountID string) (*securi return result, err } + +func Insight(ctx context.Context, conn *securityhub.SecurityHub, arn string) (*securityhub.Insight, error) { + input := &securityhub.GetInsightsInput{ + InsightArns: aws.StringSlice([]string{arn}), + MaxResults: aws.Int64(1), + } + + output, err := conn.GetInsightsWithContext(ctx, input) + + if err != nil { + return nil, err + } + + if output == nil || len(output.Insights) == 0 { + return nil, nil + } + + return output.Insights[0], nil +} diff --git a/aws/provider.go b/aws/provider.go index 9a268bf1f49..a8580fbec97 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -976,6 +976,7 @@ func Provider() *schema.Provider { "aws_security_group_rule": resourceAwsSecurityGroupRule(), "aws_securityhub_account": resourceAwsSecurityHubAccount(), "aws_securityhub_action_target": resourceAwsSecurityHubActionTarget(), + "aws_securityhub_insight": resourceAwsSecurityHubInsight(), "aws_securityhub_invite_accepter": resourceAwsSecurityHubInviteAccepter(), "aws_securityhub_member": resourceAwsSecurityHubMember(), "aws_securityhub_organization_admin_account": resourceAwsSecurityHubOrganizationAdminAccount(), diff --git a/aws/resource_aws_securityhub_insight.go b/aws/resource_aws_securityhub_insight.go new file mode 100644 index 00000000000..b12ed978009 --- /dev/null +++ b/aws/resource_aws_securityhub_insight.go @@ -0,0 +1,1241 @@ +package aws + +import ( + "context" + "fmt" + "log" + "strconv" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/securityhub" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder" +) + +func resourceAwsSecurityHubInsight() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceAwsSecurityHubInsightCreate, + ReadWithoutTimeout: resourceAwsSecurityHubInsightRead, + UpdateWithoutTimeout: resourceAwsSecurityHubInsightUpdate, + DeleteWithoutTimeout: resourceAwsSecurityHubInsightDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "filters": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "aws_account_id": stringFilterSchema(), + "company_name": stringFilterSchema(), + "compliance_status": stringFilterSchema(), + "confidence": numberFilterSchema(), + "created_at": dateFilterSchema(), + "criticality": numberFilterSchema(), + "description": stringFilterSchema(), + "finding_provider_fields_confidence": numberFilterSchema(), + "finding_provider_fields_criticality": numberFilterSchema(), + "finding_provider_fields_related_findings_id": stringFilterSchema(), + "finding_provider_fields_related_findings_product_arn": stringFilterSchema(), + "finding_provider_fields_severity_label": stringFilterSchema(), + "finding_provider_fields_severity_original": stringFilterSchema(), + "finding_provider_fields_types": stringFilterSchema(), + "first_observed_at": dateFilterSchema(), + "generator_id": stringFilterSchema(), + "id": stringFilterSchema(), + "keyword": keywordFilterSchema(), + "last_observed_at": dateFilterSchema(), + "malware_name": stringFilterSchema(), + "malware_path": stringFilterSchema(), + "malware_state": stringFilterSchema(), + "malware_type": stringFilterSchema(), + "network_destination_domain": stringFilterSchema(), + "network_destination_ipv4": ipFilterSchema(), + "network_destination_ipv6": ipFilterSchema(), + "network_destination_port": numberFilterSchema(), + "network_direction": stringFilterSchema(), + "network_protocol": stringFilterSchema(), + "network_source_domain": stringFilterSchema(), + "network_source_ipv4": ipFilterSchema(), + "network_source_ipv6": ipFilterSchema(), + "network_source_mac": stringFilterSchema(), + "network_source_port": numberFilterSchema(), + "note_text": stringFilterSchema(), + "note_updated_at": dateFilterSchema(), + "note_updated_by": stringFilterSchema(), + "process_launched_at": dateFilterSchema(), + "process_name": stringFilterSchema(), + "process_parent_pid": numberFilterSchema(), + "process_path": stringFilterSchema(), + "process_pid": numberFilterSchema(), + "process_terminated_at": dateFilterSchema(), + "product_arn": stringFilterSchema(), + "product_fields": mapFilterSchema(), + "product_name": stringFilterSchema(), + "recommendation_text": stringFilterSchema(), + "record_state": stringFilterSchema(), + "related_findings_id": stringFilterSchema(), + "related_findings_product_arn": stringFilterSchema(), + "resource_aws_ec2_instance_iam_instance_profile_arn": stringFilterSchema(), + "resource_aws_ec2_instance_image_id": stringFilterSchema(), + "resource_aws_ec2_instance_ipv4_addresses": ipFilterSchema(), + "resource_aws_ec2_instance_ipv6_addresses": ipFilterSchema(), + "resource_aws_ec2_instance_key_name": stringFilterSchema(), + "resource_aws_ec2_instance_launched_at": dateFilterSchema(), + "resource_aws_ec2_instance_subnet_id": stringFilterSchema(), + "resource_aws_ec2_instance_type": stringFilterSchema(), + "resource_aws_ec2_instance_vpc_id": stringFilterSchema(), + "resource_aws_iam_access_key_created_at": dateFilterSchema(), + "resource_aws_iam_access_key_status": stringFilterSchema(), + "resource_aws_iam_access_key_user_name": stringFilterSchema(), + "resource_aws_s3_bucket_owner_id": stringFilterSchema(), + "resource_aws_s3_bucket_owner_name": stringFilterSchema(), + "resource_container_image_id": stringFilterSchema(), + "resource_container_image_name": stringFilterSchema(), + "resource_container_launched_at": dateFilterSchema(), + "resource_container_name": stringFilterSchema(), + "resource_details_other": mapFilterSchema(), + "resource_id": stringFilterSchema(), + "resource_partition": stringFilterSchema(), + "resource_region": stringFilterSchema(), + "resource_tags": mapFilterSchema(), + "resource_type": stringFilterSchema(), + "severity_label": stringFilterSchema(), + "source_url": stringFilterSchema(), + "threat_intel_indicator_category": stringFilterSchema(), + "threat_intel_indicator_last_observed_at": dateFilterSchema(), + "threat_intel_indicator_source": stringFilterSchema(), + "threat_intel_indicator_source_url": stringFilterSchema(), + "threat_intel_indicator_type": stringFilterSchema(), + "threat_intel_indicator_value": stringFilterSchema(), + "title": stringFilterSchema(), + "type": stringFilterSchema(), + "updated_at": dateFilterSchema(), + "user_defined_values": mapFilterSchema(), + "verification_state": stringFilterSchema(), + "workflow_status": workflowStatusSchema(), + }, + }, + }, + + "group_by_attribute": { + Type: schema.TypeString, + Required: true, + }, + + "name": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func resourceAwsSecurityHubInsightCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).securityhubconn + + name := d.Get("name").(string) + + input := &securityhub.CreateInsightInput{ + GroupByAttribute: aws.String(d.Get("group_by_attribute").(string)), + Name: aws.String(name), + } + + if v, ok := d.GetOk("filters"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Filters = expandSecurityHubSecurityFindingFilters(v.([]interface{})) + } + + output, err := conn.CreateInsightWithContext(ctx, input) + + if err != nil { + return diag.FromErr(fmt.Errorf("error creating Security Hub Insight (%s): %w", name, err)) + } + + if output == nil { + return diag.FromErr(fmt.Errorf("error creating Security Hub Insight (%s): empty output", name)) + } + + d.SetId(aws.StringValue(output.InsightArn)) + + return resourceAwsSecurityHubInsightRead(ctx, d, meta) +} + +func resourceAwsSecurityHubInsightRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).securityhubconn + + insight, err := finder.Insight(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Security Hub Insight (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.FromErr(fmt.Errorf("error reading Security Hub Insight (%s): %w", d.Id(), err)) + } + + if insight == nil { + if d.IsNewResource() { + return diag.FromErr(fmt.Errorf("error reading Security Hub Insight (%s): empty output", d.Id())) + } + log.Printf("[WARN] Security Hub Insight (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("arn", insight.InsightArn) + if err := d.Set("filters", flattenSecurityHubSecurityFindingFilters(insight.Filters)); err != nil { + return diag.FromErr(fmt.Errorf("error setting filters: %w", err)) + } + d.Set("group_by_attribute", insight.GroupByAttribute) + d.Set("name", insight.Name) + + return nil +} + +func resourceAwsSecurityHubInsightUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).securityhubconn + + input := &securityhub.UpdateInsightInput{ + InsightArn: aws.String(d.Id()), + } + + if d.HasChange("filters") { + input.Filters = expandSecurityHubSecurityFindingFilters(d.Get("filters").([]interface{})) + } + + if d.HasChange("group_by_attribute") { + input.GroupByAttribute = aws.String(d.Get("group_by_attribute").(string)) + } + + if v, ok := d.GetOk("name"); ok { + input.Name = aws.String(v.(string)) + } + + _, err := conn.UpdateInsightWithContext(ctx, input) + + if err != nil { + return diag.FromErr(fmt.Errorf("error updating Security Hub Insight (%s): %w", d.Id(), err)) + } + + return resourceAwsSecurityHubInsightRead(ctx, d, meta) +} + +func resourceAwsSecurityHubInsightDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).securityhubconn + + input := &securityhub.DeleteInsightInput{ + InsightArn: aws.String(d.Id()), + } + + _, err := conn.DeleteInsightWithContext(ctx, input) + + if err != nil { + if tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) { + return nil + } + return diag.FromErr(fmt.Errorf("error deleting Security Hub Insight (%s): %w", d.Id(), err)) + } + + return nil +} + +func dateFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_range": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "unit": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(securityhub.DateRangeUnit_Values(), true), + }, + "value": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + "end": { + Type: schema.TypeString, + Optional: true, + }, + "start": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + } +} + +func ipFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cidr": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateCIDRNetworkAddress, + }, + }, + }, + } +} + +func keywordFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + } +} + +func mapFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "comparison": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(securityhub.MapFilterComparison_Values(), false), + }, + "key": { + Type: schema.TypeString, + Required: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + } +} + +func numberFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "eq": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateTypeStringNullableFloat, + }, + "gte": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateTypeStringNullableFloat, + }, + "lte": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateTypeStringNullableFloat, + }, + }, + }, + } +} + +func stringFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "comparison": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(securityhub.StringFilterComparison_Values(), false), + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + } +} + +func workflowStatusSchema() *schema.Schema { + s := stringFilterSchema() + + s.Elem.(*schema.Resource).Schema["value"].ValidateFunc = validation.StringInSlice(securityhub.WorkflowStatus_Values(), false) + + return s +} + +func expandSecurityHubDateFilterDateRange(l []interface{}) *securityhub.DateRange { + if len(l) == 0 || l[0] == nil { + return nil + } + + tfMap, ok := l[0].(map[string]interface{}) + if !ok { + return nil + } + + dr := &securityhub.DateRange{} + + if v, ok := tfMap["unit"].(string); ok && v != "" { + dr.Unit = aws.String(v) + } + + if v, ok := tfMap["value"].(int); ok { + dr.Value = aws.Int64(int64(v)) + } + + return dr +} + +func expandSecurityHubDateFilters(l []interface{}) []*securityhub.DateFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var dateFilters []*securityhub.DateFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + df := &securityhub.DateFilter{} + + if v, ok := tfMap["date_range"].([]interface{}); ok && len(v) > 0 && v[0] != nil { + df.DateRange = expandSecurityHubDateFilterDateRange(v) + } + + if v, ok := tfMap["end"].(string); ok && v != "" { + df.End = aws.String(v) + } + + if v, ok := tfMap["start"].(string); ok && v != "" { + df.Start = aws.String(v) + } + + dateFilters = append(dateFilters, df) + } + + return dateFilters +} + +func expandSecurityHubSecurityFindingFilters(l []interface{}) *securityhub.AwsSecurityFindingFilters { + if len(l) == 0 || l[0] == nil { + return nil + } + + tfMap, ok := l[0].(map[string]interface{}) + if !ok { + return nil + } + + filters := &securityhub.AwsSecurityFindingFilters{} + + if v, ok := tfMap["aws_account_id"].(*schema.Set); ok && v.Len() > 0 { + filters.AwsAccountId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["company_name"].(*schema.Set); ok && v.Len() > 0 { + filters.CompanyName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["compliance_status"].(*schema.Set); ok && v.Len() > 0 { + filters.ComplianceStatus = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["confidence"].(*schema.Set); ok && v.Len() > 0 { + filters.Confidence = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["created_at"].(*schema.Set); ok && v.Len() > 0 { + filters.CreatedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["criticality"].(*schema.Set); ok && v.Len() > 0 { + filters.Criticality = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["description"].(*schema.Set); ok && v.Len() > 0 { + filters.Description = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_confidence"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsConfidence = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_criticality"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsCriticality = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_related_findings_id"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsRelatedFindingsId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_related_findings_product_arn"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsRelatedFindingsProductArn = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_severity_label"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsSeverityLabel = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_severity_original"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsSeverityOriginal = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_types"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsTypes = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["first_observed_at"].(*schema.Set); ok && v.Len() > 0 { + filters.FirstObservedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["generator_id"].(*schema.Set); ok && v.Len() > 0 { + filters.GeneratorId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["id"].(*schema.Set); ok && v.Len() > 0 { + filters.Id = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["keyword"].(*schema.Set); ok && v.Len() > 0 { + filters.Keyword = expandSecurityHubKeywordFilters(v.List()) + } + + if v, ok := tfMap["last_observed_at"].(*schema.Set); ok && v.Len() > 0 { + filters.LastObservedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["malware_name"].(*schema.Set); ok && v.Len() > 0 { + filters.MalwareName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["malware_path"].(*schema.Set); ok && v.Len() > 0 { + filters.MalwarePath = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["malware_state"].(*schema.Set); ok && v.Len() > 0 { + filters.MalwareState = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["malware_type"].(*schema.Set); ok && v.Len() > 0 { + filters.MalwareType = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_destination_domain"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkDestinationDomain = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_destination_ipv4"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkDestinationIpV4 = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["network_destination_ipv6"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkDestinationIpV6 = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["network_destination_port"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkDestinationPort = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["network_direction"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkDirection = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_protocol"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkProtocol = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_source_domain"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkSourceDomain = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_source_ipv4"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkSourceIpV4 = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["network_source_ipv6"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkSourceIpV6 = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["network_source_mac"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkSourceMac = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_source_port"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkSourcePort = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["note_text"].(*schema.Set); ok && v.Len() > 0 { + filters.NoteText = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["note_updated_at"].(*schema.Set); ok && v.Len() > 0 { + filters.NoteUpdatedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["note_updated_by"].(*schema.Set); ok && v.Len() > 0 { + filters.NoteUpdatedBy = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["process_launched_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessLaunchedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["process_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["process_parent_pid"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessParentPid = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["process_path"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessPath = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["process_pid"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessPid = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["process_terminated_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessTerminatedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["product_arn"].(*schema.Set); ok && v.Len() > 0 { + filters.ProductArn = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["product_fields"].(*schema.Set); ok && v.Len() > 0 { + filters.ProductFields = expandSecurityHubMapFilters(v.List()) + } + + if v, ok := tfMap["product_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ProductName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["recommendation_text"].(*schema.Set); ok && v.Len() > 0 { + filters.RecommendationText = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["record_state"].(*schema.Set); ok && v.Len() > 0 { + filters.RecordState = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["related_findings_id"].(*schema.Set); ok && v.Len() > 0 { + filters.RelatedFindingsId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["related_findings_product_arn"].(*schema.Set); ok && v.Len() > 0 { + filters.RelatedFindingsProductArn = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_iam_instance_profile_arn"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceIamInstanceProfileArn = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_image_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceImageId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_ipv4_addresses"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceIpV4Addresses = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_ipv6_addresses"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceIpV6Addresses = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_key_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceKeyName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_launched_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceLaunchedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_subnet_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceSubnetId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_type"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceType = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_vpc_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceVpcId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_iam_access_key_created_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsIamAccessKeyCreatedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_iam_access_key_status"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsIamAccessKeyStatus = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_iam_access_key_user_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsIamAccessKeyUserName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_s3_bucket_owner_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsS3BucketOwnerId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_s3_bucket_owner_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsS3BucketOwnerName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_container_image_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceContainerImageId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_container_image_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceContainerImageName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_container_launched_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceContainerLaunchedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["resource_container_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceContainerName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_details_other"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceDetailsOther = expandSecurityHubMapFilters(v.List()) + } + + if v, ok := tfMap["resource_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_partition"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourcePartition = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_region"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceRegion = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_tags"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceTags = expandSecurityHubMapFilters(v.List()) + } + + if v, ok := tfMap["resource_type"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceType = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["severity_label"].(*schema.Set); ok && v.Len() > 0 { + filters.SeverityLabel = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["source_url"].(*schema.Set); ok && v.Len() > 0 { + filters.SourceUrl = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_category"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorCategory = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_last_observed_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorLastObservedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_source"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorSource = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_source_url"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorSourceUrl = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_type"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorType = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_value"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorValue = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["title"].(*schema.Set); ok && v.Len() > 0 { + filters.Title = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["type"].(*schema.Set); ok && v.Len() > 0 { + filters.Type = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["updated_at"].(*schema.Set); ok && v.Len() > 0 { + filters.UpdatedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["user_defined_values"].(*schema.Set); ok && v.Len() > 0 { + filters.UserDefinedFields = expandSecurityHubMapFilters(v.List()) + } + + if v, ok := tfMap["verification_state"].(*schema.Set); ok && v.Len() > 0 { + filters.VerificationState = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["workflow_status"].(*schema.Set); ok && v.Len() > 0 { + filters.WorkflowStatus = expandSecurityHubStringFilters(v.List()) + } + + return filters +} + +func expandSecurityHubIpFilters(l []interface{}) []*securityhub.IpFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var ipFilters []*securityhub.IpFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + ipFilter := &securityhub.IpFilter{} + + if v, ok := tfMap["cidr"].(string); ok && v != "" { + ipFilter.Cidr = aws.String(v) + } + + ipFilters = append(ipFilters, ipFilter) + } + + return ipFilters +} + +func expandSecurityHubKeywordFilters(l []interface{}) []*securityhub.KeywordFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var keywordFilters []*securityhub.KeywordFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + kf := &securityhub.KeywordFilter{} + + if v, ok := tfMap["value"].(string); ok && v != "" { + kf.Value = aws.String(v) + } + + keywordFilters = append(keywordFilters, kf) + } + + return keywordFilters +} + +func expandSecurityHubMapFilters(l []interface{}) []*securityhub.MapFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var mapFilters []*securityhub.MapFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + mf := &securityhub.MapFilter{} + + if v, ok := tfMap["comparison"].(string); ok && v != "" { + mf.Comparison = aws.String(v) + } + + if v, ok := tfMap["key"].(string); ok && v != "" { + mf.Key = aws.String(v) + } + + if v, ok := tfMap["value"].(string); ok && v != "" { + mf.Value = aws.String(v) + } + + mapFilters = append(mapFilters, mf) + } + + return mapFilters +} + +func expandSecurityHubNumberFilters(l []interface{}) []*securityhub.NumberFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var numFilters []*securityhub.NumberFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + nf := &securityhub.NumberFilter{} + + if v, ok := tfMap["eq"].(string); ok && v != "" { + val, err := strconv.ParseFloat(v, 64) + if err == nil { + nf.Eq = aws.Float64(val) + } + } + + if v, ok := tfMap["gte"].(string); ok && v != "" { + val, err := strconv.ParseFloat(v, 64) + if err == nil { + nf.Gte = aws.Float64(val) + } + } + + if v, ok := tfMap["lte"].(string); ok && v != "" { + val, err := strconv.ParseFloat(v, 64) + if err == nil { + nf.Lte = aws.Float64(val) + } + } + + numFilters = append(numFilters, nf) + } + + return numFilters +} + +func expandSecurityHubStringFilters(l []interface{}) []*securityhub.StringFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var stringFilters []*securityhub.StringFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + sf := &securityhub.StringFilter{} + + if v, ok := tfMap["comparison"].(string); ok && v != "" { + sf.Comparison = aws.String(v) + } + + if v, ok := tfMap["value"].(string); ok && v != "" { + sf.Value = aws.String(v) + } + + stringFilters = append(stringFilters, sf) + } + + return stringFilters +} + +func flattenSecurityHubDateFilterDateRange(dateRange *securityhub.DateRange) []interface{} { + if dateRange == nil { + return nil + } + + m := map[string]interface{}{ + "unit": aws.StringValue(dateRange.Unit), + "value": aws.Int64Value(dateRange.Value), + } + + return []interface{}{m} +} + +func flattenSecurityHubDateFilters(filters []*securityhub.DateFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var dateFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{ + "date_range": flattenSecurityHubDateFilterDateRange(filter.DateRange), + "end": aws.StringValue(filter.End), + "start": aws.StringValue(filter.Start), + } + + dateFilters = append(dateFilters, m) + } + + return dateFilters +} + +func flattenSecurityHubIpFilters(filters []*securityhub.IpFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var ipFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{ + "cidr": aws.StringValue(filter.Cidr), + } + + ipFilters = append(ipFilters, m) + } + + return ipFilters +} + +func flattenSecurityHubKeywordFilters(filters []*securityhub.KeywordFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var keywordFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{ + "value": aws.StringValue(filter.Value), + } + + keywordFilters = append(keywordFilters, m) + } + + return keywordFilters +} + +func flattenSecurityHubMapFilters(filters []*securityhub.MapFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var mapFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{ + "comparison": aws.StringValue(filter.Comparison), + "key": aws.StringValue(filter.Key), + "value": aws.StringValue(filter.Value), + } + + mapFilters = append(mapFilters, m) + } + + return mapFilters +} + +func flattenSecurityHubNumberFilters(filters []*securityhub.NumberFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var numFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{} + + if filter.Eq != nil { + m["eq"] = strconv.FormatFloat(aws.Float64Value(filter.Eq), 'f', -1, 64) + } + + if filter.Gte != nil { + m["gte"] = strconv.FormatFloat(aws.Float64Value(filter.Gte), 'f', -1, 64) + } + + if filter.Lte != nil { + m["lte"] = strconv.FormatFloat(aws.Float64Value(filter.Lte), 'f', -1, 64) + } + + numFilters = append(numFilters, m) + } + + return numFilters +} + +func flattenSecurityHubSecurityFindingFilters(filters *securityhub.AwsSecurityFindingFilters) []interface{} { + if filters == nil { + return nil + } + + m := map[string]interface{}{ + "aws_account_id": flattenSecurityHubStringFilters(filters.AwsAccountId), + "company_name": flattenSecurityHubStringFilters(filters.CompanyName), + "compliance_status": flattenSecurityHubStringFilters(filters.ComplianceStatus), + "confidence": flattenSecurityHubNumberFilters(filters.Confidence), + "created_at": flattenSecurityHubDateFilters(filters.CreatedAt), + "criticality": flattenSecurityHubNumberFilters(filters.Criticality), + "description": flattenSecurityHubStringFilters(filters.Description), + "finding_provider_fields_confidence": flattenSecurityHubNumberFilters(filters.FindingProviderFieldsConfidence), + "finding_provider_fields_criticality": flattenSecurityHubNumberFilters(filters.FindingProviderFieldsCriticality), + "finding_provider_fields_related_findings_id": flattenSecurityHubStringFilters(filters.FindingProviderFieldsRelatedFindingsId), + "finding_provider_fields_related_findings_product_arn": flattenSecurityHubStringFilters(filters.FindingProviderFieldsRelatedFindingsProductArn), + "finding_provider_fields_severity_label": flattenSecurityHubStringFilters(filters.FindingProviderFieldsSeverityLabel), + "finding_provider_fields_severity_original": flattenSecurityHubStringFilters(filters.FindingProviderFieldsSeverityOriginal), + "finding_provider_fields_types": flattenSecurityHubStringFilters(filters.FindingProviderFieldsTypes), + "first_observed_at": flattenSecurityHubDateFilters(filters.FirstObservedAt), + "generator_id": flattenSecurityHubStringFilters(filters.GeneratorId), + "id": flattenSecurityHubStringFilters(filters.Id), + "keyword": flattenSecurityHubKeywordFilters(filters.Keyword), + "last_observed_at": flattenSecurityHubDateFilters(filters.LastObservedAt), + "malware_name": flattenSecurityHubStringFilters(filters.MalwareName), + "malware_path": flattenSecurityHubStringFilters(filters.MalwarePath), + "malware_state": flattenSecurityHubStringFilters(filters.MalwareState), + "malware_type": flattenSecurityHubStringFilters(filters.MalwareType), + "network_destination_domain": flattenSecurityHubStringFilters(filters.NetworkDestinationDomain), + "network_destination_ipv4": flattenSecurityHubIpFilters(filters.NetworkDestinationIpV4), + "network_destination_ipv6": flattenSecurityHubIpFilters(filters.NetworkDestinationIpV6), + "network_destination_port": flattenSecurityHubNumberFilters(filters.NetworkDestinationPort), + "network_direction": flattenSecurityHubStringFilters(filters.NetworkDirection), + "network_protocol": flattenSecurityHubStringFilters(filters.NetworkProtocol), + "network_source_domain": flattenSecurityHubStringFilters(filters.NetworkSourceDomain), + "network_source_ipv4": flattenSecurityHubIpFilters(filters.NetworkSourceIpV4), + "network_source_ipv6": flattenSecurityHubIpFilters(filters.NetworkSourceIpV6), + "network_source_mac": flattenSecurityHubStringFilters(filters.NetworkSourceMac), + "network_source_port": flattenSecurityHubNumberFilters(filters.NetworkSourcePort), + "note_text": flattenSecurityHubStringFilters(filters.NoteText), + "note_updated_at": flattenSecurityHubDateFilters(filters.NoteUpdatedAt), + "note_updated_by": flattenSecurityHubStringFilters(filters.NoteUpdatedBy), + "process_launched_at": flattenSecurityHubDateFilters(filters.ProcessLaunchedAt), + "process_name": flattenSecurityHubStringFilters(filters.ProcessName), + "process_parent_pid": flattenSecurityHubNumberFilters(filters.ProcessParentPid), + "process_path": flattenSecurityHubStringFilters(filters.ProcessPath), + "process_pid": flattenSecurityHubNumberFilters(filters.ProcessPid), + "process_terminated_at": flattenSecurityHubDateFilters(filters.ProcessTerminatedAt), + "product_arn": flattenSecurityHubStringFilters(filters.ProductArn), + "product_fields": flattenSecurityHubMapFilters(filters.ProductFields), + "product_name": flattenSecurityHubStringFilters(filters.ProductName), + "recommendation_text": flattenSecurityHubStringFilters(filters.RecommendationText), + "record_state": flattenSecurityHubStringFilters(filters.RecordState), + "related_findings_id": flattenSecurityHubStringFilters(filters.RelatedFindingsId), + "related_findings_product_arn": flattenSecurityHubStringFilters(filters.RelatedFindingsProductArn), + "resource_aws_ec2_instance_iam_instance_profile_arn": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceIamInstanceProfileArn), + "resource_aws_ec2_instance_image_id": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceImageId), + "resource_aws_ec2_instance_ipv4_addresses": flattenSecurityHubIpFilters(filters.ResourceAwsEc2InstanceIpV4Addresses), + "resource_aws_ec2_instance_ipv6_addresses": flattenSecurityHubIpFilters(filters.ResourceAwsEc2InstanceIpV6Addresses), + "resource_aws_ec2_instance_key_name": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceKeyName), + "resource_aws_ec2_instance_launched_at": flattenSecurityHubDateFilters(filters.ResourceAwsEc2InstanceLaunchedAt), + "resource_aws_ec2_instance_subnet_id": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceSubnetId), + "resource_aws_ec2_instance_type": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceType), + "resource_aws_ec2_instance_vpc_id": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceVpcId), + "resource_aws_iam_access_key_created_at": flattenSecurityHubDateFilters(filters.ResourceAwsIamAccessKeyCreatedAt), + "resource_aws_iam_access_key_status": flattenSecurityHubStringFilters(filters.ResourceAwsIamAccessKeyStatus), + "resource_aws_iam_access_key_user_name": flattenSecurityHubStringFilters(filters.ResourceAwsIamAccessKeyUserName), + "resource_aws_s3_bucket_owner_id": flattenSecurityHubStringFilters(filters.ResourceAwsS3BucketOwnerId), + "resource_aws_s3_bucket_owner_name": flattenSecurityHubStringFilters(filters.ResourceAwsS3BucketOwnerName), + "resource_container_image_id": flattenSecurityHubStringFilters(filters.ResourceContainerImageId), + "resource_container_image_name": flattenSecurityHubStringFilters(filters.ResourceContainerImageName), + "resource_container_launched_at": flattenSecurityHubDateFilters(filters.ResourceContainerLaunchedAt), + "resource_container_name": flattenSecurityHubStringFilters(filters.ResourceContainerName), + "resource_details_other": flattenSecurityHubMapFilters(filters.ResourceDetailsOther), + "resource_id": flattenSecurityHubStringFilters(filters.ResourceId), + "resource_partition": flattenSecurityHubStringFilters(filters.ResourcePartition), + "resource_region": flattenSecurityHubStringFilters(filters.ResourceRegion), + "resource_tags": flattenSecurityHubMapFilters(filters.ResourceTags), + "resource_type": flattenSecurityHubStringFilters(filters.ResourceType), + "severity_label": flattenSecurityHubStringFilters(filters.SeverityLabel), + "source_url": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorSourceUrl), + "threat_intel_indicator_category": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorCategory), + "threat_intel_indicator_last_observed_at": flattenSecurityHubDateFilters(filters.ThreatIntelIndicatorLastObservedAt), + "threat_intel_indicator_source": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorSource), + "threat_intel_indicator_source_url": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorSourceUrl), + "threat_intel_indicator_type": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorType), + "threat_intel_indicator_value": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorValue), + "title": flattenSecurityHubStringFilters(filters.Title), + "type": flattenSecurityHubStringFilters(filters.Type), + "updated_at": flattenSecurityHubDateFilters(filters.UpdatedAt), + "user_defined_values": flattenSecurityHubMapFilters(filters.UserDefinedFields), + "verification_state": flattenSecurityHubStringFilters(filters.VerificationState), + "workflow_status": flattenSecurityHubStringFilters(filters.WorkflowStatus), + } + + return []interface{}{m} +} + +func flattenSecurityHubStringFilters(filters []*securityhub.StringFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var stringFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{ + "comparison": aws.StringValue(filter.Comparison), + "value": aws.StringValue(filter.Value), + } + + stringFilters = append(stringFilters, m) + } + + return stringFilters +} diff --git a/aws/resource_aws_securityhub_insight_test.go b/aws/resource_aws_securityhub_insight_test.go new file mode 100644 index 00000000000..d27216d713b --- /dev/null +++ b/aws/resource_aws_securityhub_insight_test.go @@ -0,0 +1,720 @@ +package aws + +import ( + "context" + "fmt" + "regexp" + "testing" + "time" + + "github.com/aws/aws-sdk-go/service/securityhub" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "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/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder" +) + +func testAccAwsSecurityHubInsight_basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + testAccCheckAwsSecurityHubInsightArn(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.aws_account_id.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.aws_account_id.*", map[string]string{ + "comparison": securityhub.StringFilterComparisonEquals, + "value": "1234567890", + }), + resource.TestCheckResourceAttr(resourceName, "group_by_attribute", "AwsAccountId"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsSecurityHubInsight(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_DateFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + endDate := time.Now().Add(5 * time.Minute).Format(time.RFC1123) + startDate := time.Now().Format(time.RFC1123) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_DateFilters_DateRange(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.created_at.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.created_at.*", map[string]string{ + "date_range.#": "1", + "date_range.0.unit": securityhub.DateRangeUnitDays, + "date_range.0.value": "5", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsSecurityHubInsightConfig_DateFilters_StartEnd(rName, startDate, endDate), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.created_at.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.created_at.*", map[string]string{ + "start": startDate, + "end": endDate, + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_IpFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_IpFilters(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.network_destination_ipv4.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.network_destination_ipv4.*", map[string]string{ + "cidr": "10.0.0.0/16", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_KeywordFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_KeywordFilters(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.keyword.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.keyword.*", map[string]string{ + "value": rName, + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_MapFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_MapFilters(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.product_fields.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.product_fields.*", map[string]string{ + "comparison": securityhub.MapFilterComparisonEquals, + "key": "key1", + "value": "value1", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_MultipleFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_MultipleFilters(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.aws_account_id.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.aws_account_id.*", map[string]string{ + "comparison": securityhub.StringFilterComparisonEquals, + "value": "1234567890", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.aws_account_id.*", map[string]string{ + "comparison": securityhub.StringFilterComparisonEquals, + "value": "09876543210", + }), + resource.TestCheckResourceAttr(resourceName, "filters.0.product_fields.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.product_fields.*", map[string]string{ + "comparison": securityhub.MapFilterComparisonEquals, + "key": "key1", + "value": "value1", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.product_fields.*", map[string]string{ + "comparison": securityhub.MapFilterComparisonEquals, + "key": "key2", + "value": "value2", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsSecurityHubInsightConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.aws_account_id.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.aws_account_id.*", map[string]string{ + "comparison": securityhub.StringFilterComparisonEquals, + "value": "1234567890", + }), + ), + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_Name(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + rNameUpdated := acctest.RandomWithPrefix("tf-acc-test-update") + + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + testAccCheckAwsSecurityHubInsightArn(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + ), + }, + { + Config: testAccAwsSecurityHubInsightConfig(rNameUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + testAccCheckAwsSecurityHubInsightArn(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rNameUpdated), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_NumberFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_NumberFilters(rName, "eq = 50.5"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.confidence.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.confidence.*", map[string]string{ + "eq": "50.5", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsSecurityHubInsightConfig_NumberFilters(rName, "gte = 50.5"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.confidence.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.confidence.*", map[string]string{ + "gte": "50.5", + }), + ), + }, + { + Config: testAccAwsSecurityHubInsightConfig_NumberFilters(rName, "lte = 50.5"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.confidence.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.confidence.*", map[string]string{ + "lte": "50.5", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_GroupByAttribute(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "group_by_attribute", "AwsAccountId"), + ), + }, + { + Config: testAccAwsSecurityHubInsightConfig_UpdateGroupByAttribute(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "group_by_attribute", "CompanyName"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_WorkflowStatus(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_WorkflowStatus(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.workflow_status.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.workflow_status.*", map[string]string{ + "comparison": securityhub.StringFilterComparisonEquals, + "value": securityhub.WorkflowStatusNew, + }), + resource.TestCheckResourceAttr(resourceName, "group_by_attribute", "WorkflowStatus"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAwsSecurityHubInsightDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).securityhubconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_securityhub_insight" { + continue + } + + insight, err := finder.Insight(context.Background(), conn, rs.Primary.ID) + + if err != nil { + if tfawserr.ErrMessageContains(err, securityhub.ErrCodeInvalidAccessException, "not subscribed to AWS Security Hub") { + continue + } + if tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) { + continue + } + return fmt.Errorf("error deleting Security Hub Insight (%s): %w", rs.Primary.ID, err) + } + + if insight != nil { + return fmt.Errorf("Security Hub Insight (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAwsSecurityHubInsightExists(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) + } + + conn := testAccProvider.Meta().(*AWSClient).securityhubconn + + insight, err := finder.Insight(context.Background(), conn, rs.Primary.ID) + + if err != nil { + return fmt.Errorf("error reading Security Hub Insight (%s): %w", rs.Primary.ID, err) + } + + if insight == nil { + return fmt.Errorf("error reading Security Hub Insight (%s): not found", rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckAwsSecurityHubInsightArn(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + expectedArn := regexp.MustCompile(fmt.Sprintf("insight/%s/custom/.+$", testAccGetAccountID())) + return testAccMatchResourceAttrRegionalARN(resourceName, "arn", "securityhub", expectedArn)(s) + } +} + +func testAccAwsSecurityHubInsightConfig(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + aws_account_id { + comparison = "EQUALS" + value = "1234567890" + } + } + + group_by_attribute = "AwsAccountId" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_DateFilters_DateRange(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + created_at { + date_range { + unit = "DAYS" + value = 5 + } + } + } + + group_by_attribute = "AwsAccountId" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_DateFilters_StartEnd(rName, startDate, endDate string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + created_at { + start = %q + end = %q + } + } + + group_by_attribute = "AwsAccountId" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, startDate, endDate, rName) +} + +func testAccAwsSecurityHubInsightConfig_IpFilters(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + network_destination_ipv4 { + cidr = "10.0.0.0/16" + } + } + + group_by_attribute = "AwsAccountId" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_KeywordFilters(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + keyword { + value = %[1]q + } + } + + group_by_attribute = "AwsAccountId" + + name = %[1]q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_MapFilters(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + product_fields { + comparison = "EQUALS" + key = "key1" + value = "value1" + } + } + + group_by_attribute = "AwsAccountId" + + name = %[1]q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_MultipleFilters(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + aws_account_id { + comparison = "EQUALS" + value = "1234567890" + } + + aws_account_id { + comparison = "EQUALS" + value = "09876543210" + } + + product_fields { + comparison = "EQUALS" + key = "key1" + value = "value1" + } + + product_fields { + comparison = "EQUALS" + key = "key2" + value = "value2" + } + } + + group_by_attribute = "AwsAccountId" + + name = %[1]q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_NumberFilters(rName, value string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + confidence { + %s + } + } + + group_by_attribute = "AwsAccountId" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, value, rName) +} + +func testAccAwsSecurityHubInsightConfig_UpdateGroupByAttribute(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + aws_account_id { + comparison = "EQUALS" + value = "1234567890" + } + } + + group_by_attribute = "CompanyName" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_WorkflowStatus(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + workflow_status { + comparison = "EQUALS" + value = "NEW" + } + } + + group_by_attribute = "WorkflowStatus" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} diff --git a/aws/resource_aws_securityhub_test.go b/aws/resource_aws_securityhub_test.go index 30ae8950a97..ee4601b8d31 100644 --- a/aws/resource_aws_securityhub_test.go +++ b/aws/resource_aws_securityhub_test.go @@ -19,6 +19,19 @@ func TestAccAWSSecurityHub_serial(t *testing.T) { "Description": testAccAwsSecurityHubActionTarget_Description, "Name": testAccAwsSecurityHubActionTarget_Name, }, + "Insight": { + "basic": testAccAwsSecurityHubInsight_basic, + "disappears": testAccAwsSecurityHubInsight_disappears, + "DateFilters": testAccAwsSecurityHubInsight_DateFilters, + "GroupByAttribute": testAccAwsSecurityHubInsight_GroupByAttribute, + "IpFilters": testAccAwsSecurityHubInsight_IpFilters, + "KeywordFilters": testAccAwsSecurityHubInsight_KeywordFilters, + "MapFilters": testAccAwsSecurityHubInsight_MapFilters, + "MultipleFilters": testAccAwsSecurityHubInsight_MultipleFilters, + "Name": testAccAwsSecurityHubInsight_Name, + "NumberFilters": testAccAwsSecurityHubInsight_NumberFilters, + "WorkflowStatus": testAccAwsSecurityHubInsight_WorkflowStatus, + }, "InviteAccepter": { "basic": testAccAWSSecurityHubInviteAccepter_basic, }, diff --git a/website/docs/r/securityhub_insight.html.markdown b/website/docs/r/securityhub_insight.html.markdown new file mode 100644 index 00000000000..9ab6755d80d --- /dev/null +++ b/website/docs/r/securityhub_insight.html.markdown @@ -0,0 +1,388 @@ +--- +subcategory: "Security Hub" +layout: "aws" +page_title: "AWS: aws_securityhub_insight" +description: |- + Provides a Security Hub custom insight resource. +--- + +# Resource: aws_securityhub_insight + +Provides a Security Hub custom insight resource. See the [Managing custom insights section](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-custom-insights.html) of the AWS User Guide for more information. + +## Example Usage + +### Filter by AWS account ID + +```terraform +resource "aws_securityhub_account" "example" {} + +resource "aws_securityhub_insight" "example" { + filters { + aws_account_id { + comparison = "EQUALS" + value = "1234567890" + } + + aws_account_id { + comparison = "EQUALS" + value = "09876543210" + } + } + + group_by_attribute = "AwsAccountId" + + name = "example-insight" + + depends_on = [aws_securityhub_account.example] +} +``` + +### Filter by date range + +```terraform +resource "aws_securityhub_account" "example" {} + +resource "aws_securityhub_insight" "example" { + filters { + created_at { + date_range { + unit = "DAYS" + value = 5 + } + } + } + + group_by_attribute = "CreatedAt" + + name = "example-insight" + + depends_on = [aws_securityhub_account.example] +} +``` + +### Filter by destination IPv4 address + +```terraform +resource "aws_securityhub_account" "example" {} + +resource "aws_securityhub_insight" "example" { + filters { + network_destination_ipv4 { + cidr = "10.0.0.0/16" + } + } + + group_by_attribute = "NetworkDestinationIpV4" + + name = "example-insight" + + depends_on = [aws_securityhub_account.example] +} +``` + +### Filter by finding's confidence + +```terraform +resource "aws_securityhub_account" "example" {} + +resource "aws_securityhub_insight" "example" { + filters { + confidence { + gte = "80" + } + } + + group_by_attribute = "Confidence" + + name = "example-insight" + + depends_on = [aws_securityhub_account.example] +} +``` + +### Filter by resource tags + +```terraform +resource "aws_securityhub_account" "example" {} + +resource "aws_securityhub_insight" "example" { + filters { + resource_tags { + comparison = "EQUALS" + key = "Environment" + value = "Production" + } + } + + group_by_attribute = "ResourceTags" + + name = "example-insight" + + depends_on = [aws_securityhub_account.example] +} +``` + +## Argument Reference + +The following arguments are required: + +* `filters` - (Required) A configuration block including one or more (up to 10 distinct) attributes used to filter the findings included in the insight. The insight only includes findings that match criteria defined in the filters. See below for more details. + +* `group_by_attribute` - (Required) The attribute used to group the findings for the insight e.g. if an insight is grouped by `ResourceId`, then the insight produces a list of resource identifiers. + +* `name` - (Required) The name of the custom insight. + +The `filters` block supports: + +~> **NOTE:** For each argument below, up to 20 can be provided. + +* `aws_account_id` - (Optional) The AWS account ID that a finding is generated in. See [String_Filter](#string-filter-argument-reference) below for more details. + +* `company_name` - (Optional) The name of the findings provider (company) that owns the solution (product) that generates findings. See [String_Filter](#string-filter-argument-reference) below for more details. + +* `compliance_status` - (Optional) Exclusive to findings that are generated as the result of a check run against a specific rule in a supported standard, such as CIS AWS Foundations. Contains security standard-related finding details. See [String Filter](#string-filter-argument-reference) below for more details. + +* `confidence` - (Optional) A finding's confidence. Confidence is defined as the likelihood that a finding accurately identifies the behavior or issue that it was intended to identify. Confidence is scored on a 0-100 basis using a ratio scale, where 0 means zero percent confidence and 100 means 100 percent confidence. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `created_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider captured the potential security issue that a finding captured. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `criticality` - (Optional) The level of importance assigned to the resources associated with the finding. A score of 0 means that the underlying resources have no criticality, and a score of 100 is reserved for the most critical resources. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `description` - (Optional) A finding's description. See [String Filter](#string-filter-argument-reference) below for more details. + +* `finding_provider_fields_confidence` - (Optional) The finding provider value for the finding confidence. Confidence is defined as the likelihood that a finding accurately identifies the behavior or issue that it was intended to identify. Confidence is scored on a 0-100 basis using a ratio scale, where 0 means zero percent confidence and 100 means 100 percent confidence. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `finding_provider_fields_criticality` - (Optional) The finding provider value for the level of importance assigned to the resources associated with the findings. A score of 0 means that the underlying resources have no criticality, and a score of 100 is reserved for the most critical resources. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `finding_provider_fields_related_findings_id` - (Optional) The finding identifier of a related finding that is identified by the finding provider. See [String Filter](#string-filter-argument-reference) below for more details. + +* `finding_provider_fields_related_findings_product_arn` - (Optional) The ARN of the solution that generated a related finding that is identified by the finding provider. See [String Filter](#string-filter-argument-reference) below for more details. + +* `finding_provider_fields_severity_label` - (Optional) The finding provider value for the severity label. See [String Filter](#string-filter-argument-reference) below for more details. + +* `finding_provider_fields_severity_original` - (Optional) The finding provider's original value for the severity. See [String Filter](#string-filter-argument-reference) below for more details. + +* `finding_provider_fields_types` - (Optional) One or more finding types that the finding provider assigned to the finding. Uses the format of `namespace/category/classifier` that classify a finding. Valid namespace values include: `Software and Configuration Checks`, `TTPs`, `Effects`, `Unusual Behaviors`, and `Sensitive Data Identifications`. See [String Filter](#string-filter-argument-reference) below for more details. + +* `first_observed_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider first observed the potential security issue that a finding captured. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `generator_id` - (Optional) The identifier for the solution-specific component (a discrete unit of logic) that generated a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `id` - (Optional) The security findings provider-specific identifier for a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `keyword` - (Optional) A keyword for a finding. See [Keyword Filter](#keyword-filter-argument-reference) below for more details. + +* `last_observed_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider most recently observed the potential security issue that a finding captured. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `malware_name` - (Optional) The name of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. + +* `malware_path` - (Optional) The filesystem path of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. + +* `malware_state` - (Optional) The state of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. + +* `malware_type` - (Optional) The type of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_destination_domain` - (Optional) The destination domain of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_destination_ipv4` - (Optional) The destination IPv4 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `network_destination_ipv6` - (Optional) The destination IPv6 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `network_destination_port` - (Optional) The destination port of network-related information about a finding. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `network_direction` - (Optional) Indicates the direction of network traffic associated with a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_protocol` - (Optional) The protocol of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_source_domain` - (Optional) The source domain of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_source_ipv4` - (Optional) The source IPv4 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `network_source_ipv6` - (Optional) The source IPv6 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `network_source_mac` - (Optional) The source media access control (MAC) address of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_source_port` - (Optional) The source port of network-related information about a finding. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `note_text` - (Optional) The text of a note. See [String Filter](#string-filter-argument-reference) below for more details. + +* `note_updated_at` - (Optional) The timestamp of when the note was updated. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `note_updated_by` - (Optional) The principal that created a note. See [String Filter](#string-filter-argument-reference) below for more details. + +* `process_launched_at` - (Optional) The date/time that the process was launched. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `process_name` - (Optional) The name of the process. See [String Filter](#string-filter-argument-reference) below for more details. + +* `process_parent_pid` - (Optional) The parent process ID. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `process_path` - (Optional) The path to the process executable. See [String Filter](#string-filter-argument-reference) below for more details. + +* `process_pid` - (Optional) The process ID. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `process_terminated_at` - (Optional) The date/time that the process was terminated. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `product_arn` - (Optional) The ARN generated by Security Hub that uniquely identifies a third-party company (security findings provider) after this provider's product (solution that generates findings) is registered with Security Hub. See [String Filter](#string-filter-argument-reference) below for more details. + +* `product_fields` - (Optional) A data type where security-findings providers can include additional solution-specific details that aren't part of the defined `AwsSecurityFinding` format. See [Map Filter](#map-filter-argument-reference) below for more details. + +* `product_name` - (Optional) The name of the solution (product) that generates findings. See [String Filter](#string-filter-argument-reference) below for more details. + +* `recommendation_text` - (Optional) The recommendation of what to do about the issue described in a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `record_state` - (Optional) The updated record state for the finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `related_findings_id` - (Optional) The solution-generated identifier for a related finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `related_findings_product_arn` - (Optional) The ARN of the solution that generated a related finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_iam_instance_profile_arn` - (Optional) The IAM profile ARN of the instance. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_image_id` - (Optional) The Amazon Machine Image (AMI) ID of the instance. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_ipv4_addresses` - (Optional) The IPv4 addresses associated with the instance. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_ipv6_addresses` - (Optional) The IPv6 addresses associated with the instance. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_key_name` - (Optional) The key name associated with the instance. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_launched_at` - (Optional) The date and time the instance was launched. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_subnet_id` - (Optional) The identifier of the subnet that the instance was launched in. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_type` - (Optional) The instance type of the instance. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_vpc_id` - (Optional) The identifier of the VPC that the instance was launched in. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_iam_access_key_created_at` - (Optional) The creation date/time of the IAM access key related to a finding. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `resource_aws_iam_access_key_status` - (Optional) The status of the IAM access key related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_iam_access_key_user_name` - (Optional) The user associated with the IAM access key related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_s3_bucket_owner_id` - (Optional) The canonical user ID of the owner of the S3 bucket. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_s3_bucket_owner_name` - (Optional) The display name of the owner of the S3 bucket. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_container_image_id` - (Optional) The identifier of the image related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_container_image_name` - (Optional) The name of the image related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_container_launched_at` - (Optional) The date/time that the container was started. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `resource_container_name` - (Optional) The name of the container related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_details_other` - (Optional) The details of a resource that doesn't have a specific subfield for the resource type defined. See [Map Filter](#map-filter-argument-reference) below for more details. + +* `resource_id` - (Optional) The canonical identifier for the given resource type. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_partition` - (Optional) The canonical AWS partition name that the Region is assigned to. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_region` - (Optional) The canonical AWS external Region name where this resource is located. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_tags` - (Optional) A list of AWS tags associated with a resource at the time the finding was processed. See [Map Filter](#map-filter-argument-reference) below for more details. + +* `resource_type` - (Optional) Specifies the type of the resource that details are provided for. See [String Filter](#string-filter-argument-reference) below for more details. + +* `severity_label` - (Optional) The label of a finding's severity. See [String Filter](#string-filter-argument-reference) below for more details. + +* `source_url` - (Optional) A URL that links to a page about the current finding in the security-findings provider's solution. See [String Filter](#string-filter-argument-reference) below for more details. + +* `threat_intel_indicator_category` - (Optional) The category of a threat intelligence indicator. See [String Filter](#string-filter-argument-reference) below for more details. + +* `threat_intel_indicator_last_observed_at` - (Optional) The date/time of the last observation of a threat intelligence indicator. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `threat_intel_indicator_source` - (Optional) The source of the threat intelligence. See [String Filter](#string-filter-argument-reference) below for more details. + +* `threat_intel_indicator_source_url` - (Optional) The URL for more details from the source of the threat intelligence. See [String Filter](#string-filter-argument-reference) below for more details. + +* `threat_intel_indicator_type` - (Optional) The type of a threat intelligence indicator. See [String Filter](#string-filter-argument-reference) below for more details. + +* `threat_intel_indicator_value` - (Optional) The value of a threat intelligence indicator. See [String Filter](#string-filter-argument-reference) below for more details. + +* `title` - (Optional) A finding's title. See [String Filter](#string-filter-argument-reference) below for more details. + +* `type` - (Optional) A finding type in the format of `namespace/category/classifier` that classifies a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `updated_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider last updated the finding record. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `user_defined_values` - (Optional) A list of name/value string pairs associated with the finding. These are custom, user-defined fields added to a finding. See [Map Filter](#map-filter-argument-reference) below for more details. + +* `verification_state` - (Optional) The veracity of a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `workflow_status` - (Optional) The status of the investigation into a finding. See [Workflow Status Filter](#workflow-status-filter-argument-reference) below for more details. + +### Date Filter Argument reference + +The date filter configuration block supports the following arguments: + +* `date_range` - (Optional) A configuration block of the date range for the date filter. See [date_range](#date_range-argument-reference) below for more details. +* `end` - (Optional) An end date for the date filter. Required with `start` if `date_range` is not specified. +* `start` - (Optional) A start date for the date filter. Required with `end` if `date_range` is not specified. + +### date_range Argument reference + +The `date_range` configuration block supports the following arguments: + +* `unit` - (Required) A date range unit for the date filter. Valid values: `DAYS`. +* `value` - (Required) A date range value for the date filter, provided as an Integer. + +### Ip Filter Argument Reference + +The Ip filter configuration block supports the following arguments: + +* `cidr` - (Required) A finding's CIDR value. + +### Keyword Filter Argument Reference + +The keyword filter configuration block supports the following arguments: + +* `value` - (Required) A value for the keyword. + +### Map Filter Argument reference + +The map filter configuration block supports the following arguments: + +* `comparison` - (Required) The condition to apply to a string value when querying for findings. Valid values include: `EQUALS` and `NOT_EQUALS`. +* `key` - (Required) The key of the map filter. For example, for `ResourceTags`, `Key` identifies the name of the tag. For `UserDefinedFields`, `Key` is the name of the field. +* `value` - (Required) The value for the key in the map filter. Filter values are case sensitive. For example, one of the values for a tag called `Department` might be `Security`. If you provide `security` as the filter value, then there is no match. + +### Number Filter Argument reference + +The number filter configuration block supports the following arguments: + +~> **NOTE:** Only one of `eg`, `gte`, or `lte` must be specified. + +* `eq` - (Optional) The equal-to condition to be applied to a single field when querying for findings, provided as a String. +* `gte` - (Optional) The greater-than-equal condition to be applied to a single field when querying for findings, provided as a String. +* `lte` - (Optional) The less-than-equal condition to be applied to a single field when querying for findings, provided as a String. + +### String Filter Argument reference + +The string filter configuration block supports the following arguments: + +* `comparison` - (Required) The condition to apply to a string value when querying for findings. Valid values include: `EQUALS`, `PREFIX`, `NOT_EQUALS`, `PREFIX_NOT_EQUALS`. +* `value` - (Required) The string filter value. Filter values are case sensitive. + +### Workflow Status Filter Argument reference + +The workflow status filter configuration block supports the following arguments: + +* `comparison` - (Required) The condition to apply to a string value when querying for findings. Valid values include: `EQUALS`, `PREFIX`, `NOT_EQUALS`, `PREFIX_NOT_EQUALS`. +* `value` - (Required) The string filter value. Valid values include: `NEW`, `NOTIFIED`, `SUPPRESSED`, and `RESOLVED`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The Amazon Resource Name (ARN) of the insight. +* `arn` - The Amazon Resource Name (ARN) of the insight. + +## Import + +Security Hub insights can be imported using the Amazon Resource Name (ARN), e.g. + +``` +$ terraform import aws_securityhub_insight.example arn:aws:securityhub:us-west-2:1234567890:insight/1234567890/custom/91299ed7-abd0-4e44-a858-d0b15e37141a +``` From fca6674677e5c14c476ddc6798c36b07197cc0e3 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Wed, 31 Mar 2021 00:00:01 -0400 Subject: [PATCH 02/11] terrafmt and changelog entry --- .changelog/18494.txt | 3 +++ aws/resource_aws_securityhub_insight_test.go | 26 ++++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) create mode 100644 .changelog/18494.txt diff --git a/.changelog/18494.txt b/.changelog/18494.txt new file mode 100644 index 00000000000..1cb4873fcb8 --- /dev/null +++ b/.changelog/18494.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_securityhub_insight +``` \ No newline at end of file diff --git a/aws/resource_aws_securityhub_insight_test.go b/aws/resource_aws_securityhub_insight_test.go index d27216d713b..2fcc627ac27 100644 --- a/aws/resource_aws_securityhub_insight_test.go +++ b/aws/resource_aws_securityhub_insight_test.go @@ -521,9 +521,9 @@ resource "aws_securityhub_insight" "test" { filters { created_at { date_range { - unit = "DAYS" - value = 5 - } + unit = "DAYS" + value = 5 + } } } @@ -564,7 +564,7 @@ resource "aws_securityhub_account" "test" {} resource "aws_securityhub_insight" "test" { filters { network_destination_ipv4 { - cidr = "10.0.0.0/16" + cidr = "10.0.0.0/16" } } @@ -603,10 +603,10 @@ resource "aws_securityhub_account" "test" {} resource "aws_securityhub_insight" "test" { filters { - product_fields { - comparison = "EQUALS" + product_fields { + comparison = "EQUALS" key = "key1" - value = "value1" + value = "value1" } } @@ -635,16 +635,16 @@ resource "aws_securityhub_insight" "test" { value = "09876543210" } - product_fields { - comparison = "EQUALS" + product_fields { + comparison = "EQUALS" key = "key1" - value = "value1" + value = "value1" } product_fields { - comparison = "EQUALS" + comparison = "EQUALS" key = "key2" - value = "value2" + value = "value2" } } @@ -663,7 +663,7 @@ resource "aws_securityhub_account" "test" {} resource "aws_securityhub_insight" "test" { filters { - confidence { + confidence { %s } } From 2978e619248337627dcf82012a5a68af553d25e0 Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Thu, 15 Apr 2021 13:54:50 -0400 Subject: [PATCH 03/11] update ARN check for GovCloud naming pattern --- aws/resource_aws_securityhub_insight_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_securityhub_insight_test.go b/aws/resource_aws_securityhub_insight_test.go index 2fcc627ac27..98e6d66dfbe 100644 --- a/aws/resource_aws_securityhub_insight_test.go +++ b/aws/resource_aws_securityhub_insight_test.go @@ -485,10 +485,14 @@ func testAccCheckAwsSecurityHubInsightExists(n string) resource.TestCheckFunc { } } +// testAccCheckAwsSecurityHubInsightArn checks the computed ARN value +// and accounts for differences in SecurityHub on GovCloud where the partition portion +// of the ARN is still "aws" while other services utilize the "aws-us-gov" partition func testAccCheckAwsSecurityHubInsightArn(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - expectedArn := regexp.MustCompile(fmt.Sprintf("insight/%s/custom/.+$", testAccGetAccountID())) - return testAccMatchResourceAttrRegionalARN(resourceName, "arn", "securityhub", expectedArn)(s) + expectedArn := fmt.Sprintf(`^arn:aws[^:]*:securityhub:%s:%s:insight/%s/custom/.+$`, testAccGetRegion(), testAccGetAccountID(), testAccGetAccountID()) + //lintignore:AWSAT001 + return resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile(expectedArn))(s) } } From 941df4185115a4b02642d0cadac69a034c2ae3dd Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Tue, 30 Mar 2021 23:39:01 -0400 Subject: [PATCH 04/11] new resource: securityhub_insight --- .../service/securityhub/finder/finder.go | 21 + aws/provider.go | 1 + aws/resource_aws_securityhub_insight.go | 1241 +++++++++++++++++ aws/resource_aws_securityhub_insight_test.go | 720 ++++++++++ aws/resource_aws_securityhub_test.go | 13 + .../docs/r/securityhub_insight.html.markdown | 388 ++++++ 6 files changed, 2384 insertions(+) create mode 100644 aws/resource_aws_securityhub_insight.go create mode 100644 aws/resource_aws_securityhub_insight_test.go create mode 100644 website/docs/r/securityhub_insight.html.markdown diff --git a/aws/internal/service/securityhub/finder/finder.go b/aws/internal/service/securityhub/finder/finder.go index e13cd6a0708..ecb245bb2ad 100644 --- a/aws/internal/service/securityhub/finder/finder.go +++ b/aws/internal/service/securityhub/finder/finder.go @@ -1,6 +1,8 @@ package finder import ( + "context" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/securityhub" ) @@ -30,3 +32,22 @@ func AdminAccount(conn *securityhub.SecurityHub, adminAccountID string) (*securi return result, err } + +func Insight(ctx context.Context, conn *securityhub.SecurityHub, arn string) (*securityhub.Insight, error) { + input := &securityhub.GetInsightsInput{ + InsightArns: aws.StringSlice([]string{arn}), + MaxResults: aws.Int64(1), + } + + output, err := conn.GetInsightsWithContext(ctx, input) + + if err != nil { + return nil, err + } + + if output == nil || len(output.Insights) == 0 { + return nil, nil + } + + return output.Insights[0], nil +} diff --git a/aws/provider.go b/aws/provider.go index 64ee387878e..387b84350a6 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -984,6 +984,7 @@ func Provider() *schema.Provider { "aws_security_group_rule": resourceAwsSecurityGroupRule(), "aws_securityhub_account": resourceAwsSecurityHubAccount(), "aws_securityhub_action_target": resourceAwsSecurityHubActionTarget(), + "aws_securityhub_insight": resourceAwsSecurityHubInsight(), "aws_securityhub_invite_accepter": resourceAwsSecurityHubInviteAccepter(), "aws_securityhub_member": resourceAwsSecurityHubMember(), "aws_securityhub_organization_admin_account": resourceAwsSecurityHubOrganizationAdminAccount(), diff --git a/aws/resource_aws_securityhub_insight.go b/aws/resource_aws_securityhub_insight.go new file mode 100644 index 00000000000..b12ed978009 --- /dev/null +++ b/aws/resource_aws_securityhub_insight.go @@ -0,0 +1,1241 @@ +package aws + +import ( + "context" + "fmt" + "log" + "strconv" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/securityhub" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder" +) + +func resourceAwsSecurityHubInsight() *schema.Resource { + return &schema.Resource{ + CreateWithoutTimeout: resourceAwsSecurityHubInsightCreate, + ReadWithoutTimeout: resourceAwsSecurityHubInsightRead, + UpdateWithoutTimeout: resourceAwsSecurityHubInsightUpdate, + DeleteWithoutTimeout: resourceAwsSecurityHubInsightDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + + "filters": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "aws_account_id": stringFilterSchema(), + "company_name": stringFilterSchema(), + "compliance_status": stringFilterSchema(), + "confidence": numberFilterSchema(), + "created_at": dateFilterSchema(), + "criticality": numberFilterSchema(), + "description": stringFilterSchema(), + "finding_provider_fields_confidence": numberFilterSchema(), + "finding_provider_fields_criticality": numberFilterSchema(), + "finding_provider_fields_related_findings_id": stringFilterSchema(), + "finding_provider_fields_related_findings_product_arn": stringFilterSchema(), + "finding_provider_fields_severity_label": stringFilterSchema(), + "finding_provider_fields_severity_original": stringFilterSchema(), + "finding_provider_fields_types": stringFilterSchema(), + "first_observed_at": dateFilterSchema(), + "generator_id": stringFilterSchema(), + "id": stringFilterSchema(), + "keyword": keywordFilterSchema(), + "last_observed_at": dateFilterSchema(), + "malware_name": stringFilterSchema(), + "malware_path": stringFilterSchema(), + "malware_state": stringFilterSchema(), + "malware_type": stringFilterSchema(), + "network_destination_domain": stringFilterSchema(), + "network_destination_ipv4": ipFilterSchema(), + "network_destination_ipv6": ipFilterSchema(), + "network_destination_port": numberFilterSchema(), + "network_direction": stringFilterSchema(), + "network_protocol": stringFilterSchema(), + "network_source_domain": stringFilterSchema(), + "network_source_ipv4": ipFilterSchema(), + "network_source_ipv6": ipFilterSchema(), + "network_source_mac": stringFilterSchema(), + "network_source_port": numberFilterSchema(), + "note_text": stringFilterSchema(), + "note_updated_at": dateFilterSchema(), + "note_updated_by": stringFilterSchema(), + "process_launched_at": dateFilterSchema(), + "process_name": stringFilterSchema(), + "process_parent_pid": numberFilterSchema(), + "process_path": stringFilterSchema(), + "process_pid": numberFilterSchema(), + "process_terminated_at": dateFilterSchema(), + "product_arn": stringFilterSchema(), + "product_fields": mapFilterSchema(), + "product_name": stringFilterSchema(), + "recommendation_text": stringFilterSchema(), + "record_state": stringFilterSchema(), + "related_findings_id": stringFilterSchema(), + "related_findings_product_arn": stringFilterSchema(), + "resource_aws_ec2_instance_iam_instance_profile_arn": stringFilterSchema(), + "resource_aws_ec2_instance_image_id": stringFilterSchema(), + "resource_aws_ec2_instance_ipv4_addresses": ipFilterSchema(), + "resource_aws_ec2_instance_ipv6_addresses": ipFilterSchema(), + "resource_aws_ec2_instance_key_name": stringFilterSchema(), + "resource_aws_ec2_instance_launched_at": dateFilterSchema(), + "resource_aws_ec2_instance_subnet_id": stringFilterSchema(), + "resource_aws_ec2_instance_type": stringFilterSchema(), + "resource_aws_ec2_instance_vpc_id": stringFilterSchema(), + "resource_aws_iam_access_key_created_at": dateFilterSchema(), + "resource_aws_iam_access_key_status": stringFilterSchema(), + "resource_aws_iam_access_key_user_name": stringFilterSchema(), + "resource_aws_s3_bucket_owner_id": stringFilterSchema(), + "resource_aws_s3_bucket_owner_name": stringFilterSchema(), + "resource_container_image_id": stringFilterSchema(), + "resource_container_image_name": stringFilterSchema(), + "resource_container_launched_at": dateFilterSchema(), + "resource_container_name": stringFilterSchema(), + "resource_details_other": mapFilterSchema(), + "resource_id": stringFilterSchema(), + "resource_partition": stringFilterSchema(), + "resource_region": stringFilterSchema(), + "resource_tags": mapFilterSchema(), + "resource_type": stringFilterSchema(), + "severity_label": stringFilterSchema(), + "source_url": stringFilterSchema(), + "threat_intel_indicator_category": stringFilterSchema(), + "threat_intel_indicator_last_observed_at": dateFilterSchema(), + "threat_intel_indicator_source": stringFilterSchema(), + "threat_intel_indicator_source_url": stringFilterSchema(), + "threat_intel_indicator_type": stringFilterSchema(), + "threat_intel_indicator_value": stringFilterSchema(), + "title": stringFilterSchema(), + "type": stringFilterSchema(), + "updated_at": dateFilterSchema(), + "user_defined_values": mapFilterSchema(), + "verification_state": stringFilterSchema(), + "workflow_status": workflowStatusSchema(), + }, + }, + }, + + "group_by_attribute": { + Type: schema.TypeString, + Required: true, + }, + + "name": { + Type: schema.TypeString, + Required: true, + }, + }, + } +} + +func resourceAwsSecurityHubInsightCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).securityhubconn + + name := d.Get("name").(string) + + input := &securityhub.CreateInsightInput{ + GroupByAttribute: aws.String(d.Get("group_by_attribute").(string)), + Name: aws.String(name), + } + + if v, ok := d.GetOk("filters"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + input.Filters = expandSecurityHubSecurityFindingFilters(v.([]interface{})) + } + + output, err := conn.CreateInsightWithContext(ctx, input) + + if err != nil { + return diag.FromErr(fmt.Errorf("error creating Security Hub Insight (%s): %w", name, err)) + } + + if output == nil { + return diag.FromErr(fmt.Errorf("error creating Security Hub Insight (%s): empty output", name)) + } + + d.SetId(aws.StringValue(output.InsightArn)) + + return resourceAwsSecurityHubInsightRead(ctx, d, meta) +} + +func resourceAwsSecurityHubInsightRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).securityhubconn + + insight, err := finder.Insight(ctx, conn, d.Id()) + + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) { + log.Printf("[WARN] Security Hub Insight (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + if err != nil { + return diag.FromErr(fmt.Errorf("error reading Security Hub Insight (%s): %w", d.Id(), err)) + } + + if insight == nil { + if d.IsNewResource() { + return diag.FromErr(fmt.Errorf("error reading Security Hub Insight (%s): empty output", d.Id())) + } + log.Printf("[WARN] Security Hub Insight (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + d.Set("arn", insight.InsightArn) + if err := d.Set("filters", flattenSecurityHubSecurityFindingFilters(insight.Filters)); err != nil { + return diag.FromErr(fmt.Errorf("error setting filters: %w", err)) + } + d.Set("group_by_attribute", insight.GroupByAttribute) + d.Set("name", insight.Name) + + return nil +} + +func resourceAwsSecurityHubInsightUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).securityhubconn + + input := &securityhub.UpdateInsightInput{ + InsightArn: aws.String(d.Id()), + } + + if d.HasChange("filters") { + input.Filters = expandSecurityHubSecurityFindingFilters(d.Get("filters").([]interface{})) + } + + if d.HasChange("group_by_attribute") { + input.GroupByAttribute = aws.String(d.Get("group_by_attribute").(string)) + } + + if v, ok := d.GetOk("name"); ok { + input.Name = aws.String(v.(string)) + } + + _, err := conn.UpdateInsightWithContext(ctx, input) + + if err != nil { + return diag.FromErr(fmt.Errorf("error updating Security Hub Insight (%s): %w", d.Id(), err)) + } + + return resourceAwsSecurityHubInsightRead(ctx, d, meta) +} + +func resourceAwsSecurityHubInsightDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + conn := meta.(*AWSClient).securityhubconn + + input := &securityhub.DeleteInsightInput{ + InsightArn: aws.String(d.Id()), + } + + _, err := conn.DeleteInsightWithContext(ctx, input) + + if err != nil { + if tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) { + return nil + } + return diag.FromErr(fmt.Errorf("error deleting Security Hub Insight (%s): %w", d.Id(), err)) + } + + return nil +} + +func dateFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "date_range": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "unit": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(securityhub.DateRangeUnit_Values(), true), + }, + "value": { + Type: schema.TypeInt, + Required: true, + }, + }, + }, + }, + "end": { + Type: schema.TypeString, + Optional: true, + }, + "start": { + Type: schema.TypeString, + Optional: true, + }, + }, + }, + } +} + +func ipFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cidr": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateCIDRNetworkAddress, + }, + }, + }, + } +} + +func keywordFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + } +} + +func mapFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "comparison": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(securityhub.MapFilterComparison_Values(), false), + }, + "key": { + Type: schema.TypeString, + Required: true, + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + } +} + +func numberFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "eq": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateTypeStringNullableFloat, + }, + "gte": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateTypeStringNullableFloat, + }, + "lte": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateTypeStringNullableFloat, + }, + }, + }, + } +} + +func stringFilterSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeSet, + Optional: true, + MaxItems: 20, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "comparison": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(securityhub.StringFilterComparison_Values(), false), + }, + "value": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + } +} + +func workflowStatusSchema() *schema.Schema { + s := stringFilterSchema() + + s.Elem.(*schema.Resource).Schema["value"].ValidateFunc = validation.StringInSlice(securityhub.WorkflowStatus_Values(), false) + + return s +} + +func expandSecurityHubDateFilterDateRange(l []interface{}) *securityhub.DateRange { + if len(l) == 0 || l[0] == nil { + return nil + } + + tfMap, ok := l[0].(map[string]interface{}) + if !ok { + return nil + } + + dr := &securityhub.DateRange{} + + if v, ok := tfMap["unit"].(string); ok && v != "" { + dr.Unit = aws.String(v) + } + + if v, ok := tfMap["value"].(int); ok { + dr.Value = aws.Int64(int64(v)) + } + + return dr +} + +func expandSecurityHubDateFilters(l []interface{}) []*securityhub.DateFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var dateFilters []*securityhub.DateFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + df := &securityhub.DateFilter{} + + if v, ok := tfMap["date_range"].([]interface{}); ok && len(v) > 0 && v[0] != nil { + df.DateRange = expandSecurityHubDateFilterDateRange(v) + } + + if v, ok := tfMap["end"].(string); ok && v != "" { + df.End = aws.String(v) + } + + if v, ok := tfMap["start"].(string); ok && v != "" { + df.Start = aws.String(v) + } + + dateFilters = append(dateFilters, df) + } + + return dateFilters +} + +func expandSecurityHubSecurityFindingFilters(l []interface{}) *securityhub.AwsSecurityFindingFilters { + if len(l) == 0 || l[0] == nil { + return nil + } + + tfMap, ok := l[0].(map[string]interface{}) + if !ok { + return nil + } + + filters := &securityhub.AwsSecurityFindingFilters{} + + if v, ok := tfMap["aws_account_id"].(*schema.Set); ok && v.Len() > 0 { + filters.AwsAccountId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["company_name"].(*schema.Set); ok && v.Len() > 0 { + filters.CompanyName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["compliance_status"].(*schema.Set); ok && v.Len() > 0 { + filters.ComplianceStatus = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["confidence"].(*schema.Set); ok && v.Len() > 0 { + filters.Confidence = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["created_at"].(*schema.Set); ok && v.Len() > 0 { + filters.CreatedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["criticality"].(*schema.Set); ok && v.Len() > 0 { + filters.Criticality = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["description"].(*schema.Set); ok && v.Len() > 0 { + filters.Description = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_confidence"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsConfidence = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_criticality"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsCriticality = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_related_findings_id"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsRelatedFindingsId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_related_findings_product_arn"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsRelatedFindingsProductArn = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_severity_label"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsSeverityLabel = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_severity_original"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsSeverityOriginal = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["finding_provider_fields_types"].(*schema.Set); ok && v.Len() > 0 { + filters.FindingProviderFieldsTypes = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["first_observed_at"].(*schema.Set); ok && v.Len() > 0 { + filters.FirstObservedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["generator_id"].(*schema.Set); ok && v.Len() > 0 { + filters.GeneratorId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["id"].(*schema.Set); ok && v.Len() > 0 { + filters.Id = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["keyword"].(*schema.Set); ok && v.Len() > 0 { + filters.Keyword = expandSecurityHubKeywordFilters(v.List()) + } + + if v, ok := tfMap["last_observed_at"].(*schema.Set); ok && v.Len() > 0 { + filters.LastObservedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["malware_name"].(*schema.Set); ok && v.Len() > 0 { + filters.MalwareName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["malware_path"].(*schema.Set); ok && v.Len() > 0 { + filters.MalwarePath = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["malware_state"].(*schema.Set); ok && v.Len() > 0 { + filters.MalwareState = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["malware_type"].(*schema.Set); ok && v.Len() > 0 { + filters.MalwareType = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_destination_domain"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkDestinationDomain = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_destination_ipv4"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkDestinationIpV4 = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["network_destination_ipv6"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkDestinationIpV6 = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["network_destination_port"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkDestinationPort = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["network_direction"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkDirection = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_protocol"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkProtocol = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_source_domain"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkSourceDomain = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_source_ipv4"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkSourceIpV4 = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["network_source_ipv6"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkSourceIpV6 = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["network_source_mac"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkSourceMac = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["network_source_port"].(*schema.Set); ok && v.Len() > 0 { + filters.NetworkSourcePort = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["note_text"].(*schema.Set); ok && v.Len() > 0 { + filters.NoteText = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["note_updated_at"].(*schema.Set); ok && v.Len() > 0 { + filters.NoteUpdatedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["note_updated_by"].(*schema.Set); ok && v.Len() > 0 { + filters.NoteUpdatedBy = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["process_launched_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessLaunchedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["process_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["process_parent_pid"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessParentPid = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["process_path"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessPath = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["process_pid"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessPid = expandSecurityHubNumberFilters(v.List()) + } + + if v, ok := tfMap["process_terminated_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ProcessTerminatedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["product_arn"].(*schema.Set); ok && v.Len() > 0 { + filters.ProductArn = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["product_fields"].(*schema.Set); ok && v.Len() > 0 { + filters.ProductFields = expandSecurityHubMapFilters(v.List()) + } + + if v, ok := tfMap["product_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ProductName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["recommendation_text"].(*schema.Set); ok && v.Len() > 0 { + filters.RecommendationText = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["record_state"].(*schema.Set); ok && v.Len() > 0 { + filters.RecordState = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["related_findings_id"].(*schema.Set); ok && v.Len() > 0 { + filters.RelatedFindingsId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["related_findings_product_arn"].(*schema.Set); ok && v.Len() > 0 { + filters.RelatedFindingsProductArn = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_iam_instance_profile_arn"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceIamInstanceProfileArn = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_image_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceImageId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_ipv4_addresses"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceIpV4Addresses = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_ipv6_addresses"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceIpV6Addresses = expandSecurityHubIpFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_key_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceKeyName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_launched_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceLaunchedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_subnet_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceSubnetId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_type"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceType = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_ec2_instance_vpc_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsEc2InstanceVpcId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_iam_access_key_created_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsIamAccessKeyCreatedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_iam_access_key_status"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsIamAccessKeyStatus = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_iam_access_key_user_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsIamAccessKeyUserName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_s3_bucket_owner_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsS3BucketOwnerId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_aws_s3_bucket_owner_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceAwsS3BucketOwnerName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_container_image_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceContainerImageId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_container_image_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceContainerImageName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_container_launched_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceContainerLaunchedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["resource_container_name"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceContainerName = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_details_other"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceDetailsOther = expandSecurityHubMapFilters(v.List()) + } + + if v, ok := tfMap["resource_id"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceId = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_partition"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourcePartition = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_region"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceRegion = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["resource_tags"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceTags = expandSecurityHubMapFilters(v.List()) + } + + if v, ok := tfMap["resource_type"].(*schema.Set); ok && v.Len() > 0 { + filters.ResourceType = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["severity_label"].(*schema.Set); ok && v.Len() > 0 { + filters.SeverityLabel = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["source_url"].(*schema.Set); ok && v.Len() > 0 { + filters.SourceUrl = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_category"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorCategory = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_last_observed_at"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorLastObservedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_source"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorSource = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_source_url"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorSourceUrl = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_type"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorType = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["threat_intel_indicator_value"].(*schema.Set); ok && v.Len() > 0 { + filters.ThreatIntelIndicatorValue = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["title"].(*schema.Set); ok && v.Len() > 0 { + filters.Title = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["type"].(*schema.Set); ok && v.Len() > 0 { + filters.Type = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["updated_at"].(*schema.Set); ok && v.Len() > 0 { + filters.UpdatedAt = expandSecurityHubDateFilters(v.List()) + } + + if v, ok := tfMap["user_defined_values"].(*schema.Set); ok && v.Len() > 0 { + filters.UserDefinedFields = expandSecurityHubMapFilters(v.List()) + } + + if v, ok := tfMap["verification_state"].(*schema.Set); ok && v.Len() > 0 { + filters.VerificationState = expandSecurityHubStringFilters(v.List()) + } + + if v, ok := tfMap["workflow_status"].(*schema.Set); ok && v.Len() > 0 { + filters.WorkflowStatus = expandSecurityHubStringFilters(v.List()) + } + + return filters +} + +func expandSecurityHubIpFilters(l []interface{}) []*securityhub.IpFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var ipFilters []*securityhub.IpFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + ipFilter := &securityhub.IpFilter{} + + if v, ok := tfMap["cidr"].(string); ok && v != "" { + ipFilter.Cidr = aws.String(v) + } + + ipFilters = append(ipFilters, ipFilter) + } + + return ipFilters +} + +func expandSecurityHubKeywordFilters(l []interface{}) []*securityhub.KeywordFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var keywordFilters []*securityhub.KeywordFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + kf := &securityhub.KeywordFilter{} + + if v, ok := tfMap["value"].(string); ok && v != "" { + kf.Value = aws.String(v) + } + + keywordFilters = append(keywordFilters, kf) + } + + return keywordFilters +} + +func expandSecurityHubMapFilters(l []interface{}) []*securityhub.MapFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var mapFilters []*securityhub.MapFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + mf := &securityhub.MapFilter{} + + if v, ok := tfMap["comparison"].(string); ok && v != "" { + mf.Comparison = aws.String(v) + } + + if v, ok := tfMap["key"].(string); ok && v != "" { + mf.Key = aws.String(v) + } + + if v, ok := tfMap["value"].(string); ok && v != "" { + mf.Value = aws.String(v) + } + + mapFilters = append(mapFilters, mf) + } + + return mapFilters +} + +func expandSecurityHubNumberFilters(l []interface{}) []*securityhub.NumberFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var numFilters []*securityhub.NumberFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + nf := &securityhub.NumberFilter{} + + if v, ok := tfMap["eq"].(string); ok && v != "" { + val, err := strconv.ParseFloat(v, 64) + if err == nil { + nf.Eq = aws.Float64(val) + } + } + + if v, ok := tfMap["gte"].(string); ok && v != "" { + val, err := strconv.ParseFloat(v, 64) + if err == nil { + nf.Gte = aws.Float64(val) + } + } + + if v, ok := tfMap["lte"].(string); ok && v != "" { + val, err := strconv.ParseFloat(v, 64) + if err == nil { + nf.Lte = aws.Float64(val) + } + } + + numFilters = append(numFilters, nf) + } + + return numFilters +} + +func expandSecurityHubStringFilters(l []interface{}) []*securityhub.StringFilter { + if len(l) == 0 || l[0] == nil { + return nil + } + + var stringFilters []*securityhub.StringFilter + + for _, item := range l { + tfMap, ok := item.(map[string]interface{}) + if !ok { + continue + } + + sf := &securityhub.StringFilter{} + + if v, ok := tfMap["comparison"].(string); ok && v != "" { + sf.Comparison = aws.String(v) + } + + if v, ok := tfMap["value"].(string); ok && v != "" { + sf.Value = aws.String(v) + } + + stringFilters = append(stringFilters, sf) + } + + return stringFilters +} + +func flattenSecurityHubDateFilterDateRange(dateRange *securityhub.DateRange) []interface{} { + if dateRange == nil { + return nil + } + + m := map[string]interface{}{ + "unit": aws.StringValue(dateRange.Unit), + "value": aws.Int64Value(dateRange.Value), + } + + return []interface{}{m} +} + +func flattenSecurityHubDateFilters(filters []*securityhub.DateFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var dateFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{ + "date_range": flattenSecurityHubDateFilterDateRange(filter.DateRange), + "end": aws.StringValue(filter.End), + "start": aws.StringValue(filter.Start), + } + + dateFilters = append(dateFilters, m) + } + + return dateFilters +} + +func flattenSecurityHubIpFilters(filters []*securityhub.IpFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var ipFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{ + "cidr": aws.StringValue(filter.Cidr), + } + + ipFilters = append(ipFilters, m) + } + + return ipFilters +} + +func flattenSecurityHubKeywordFilters(filters []*securityhub.KeywordFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var keywordFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{ + "value": aws.StringValue(filter.Value), + } + + keywordFilters = append(keywordFilters, m) + } + + return keywordFilters +} + +func flattenSecurityHubMapFilters(filters []*securityhub.MapFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var mapFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{ + "comparison": aws.StringValue(filter.Comparison), + "key": aws.StringValue(filter.Key), + "value": aws.StringValue(filter.Value), + } + + mapFilters = append(mapFilters, m) + } + + return mapFilters +} + +func flattenSecurityHubNumberFilters(filters []*securityhub.NumberFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var numFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{} + + if filter.Eq != nil { + m["eq"] = strconv.FormatFloat(aws.Float64Value(filter.Eq), 'f', -1, 64) + } + + if filter.Gte != nil { + m["gte"] = strconv.FormatFloat(aws.Float64Value(filter.Gte), 'f', -1, 64) + } + + if filter.Lte != nil { + m["lte"] = strconv.FormatFloat(aws.Float64Value(filter.Lte), 'f', -1, 64) + } + + numFilters = append(numFilters, m) + } + + return numFilters +} + +func flattenSecurityHubSecurityFindingFilters(filters *securityhub.AwsSecurityFindingFilters) []interface{} { + if filters == nil { + return nil + } + + m := map[string]interface{}{ + "aws_account_id": flattenSecurityHubStringFilters(filters.AwsAccountId), + "company_name": flattenSecurityHubStringFilters(filters.CompanyName), + "compliance_status": flattenSecurityHubStringFilters(filters.ComplianceStatus), + "confidence": flattenSecurityHubNumberFilters(filters.Confidence), + "created_at": flattenSecurityHubDateFilters(filters.CreatedAt), + "criticality": flattenSecurityHubNumberFilters(filters.Criticality), + "description": flattenSecurityHubStringFilters(filters.Description), + "finding_provider_fields_confidence": flattenSecurityHubNumberFilters(filters.FindingProviderFieldsConfidence), + "finding_provider_fields_criticality": flattenSecurityHubNumberFilters(filters.FindingProviderFieldsCriticality), + "finding_provider_fields_related_findings_id": flattenSecurityHubStringFilters(filters.FindingProviderFieldsRelatedFindingsId), + "finding_provider_fields_related_findings_product_arn": flattenSecurityHubStringFilters(filters.FindingProviderFieldsRelatedFindingsProductArn), + "finding_provider_fields_severity_label": flattenSecurityHubStringFilters(filters.FindingProviderFieldsSeverityLabel), + "finding_provider_fields_severity_original": flattenSecurityHubStringFilters(filters.FindingProviderFieldsSeverityOriginal), + "finding_provider_fields_types": flattenSecurityHubStringFilters(filters.FindingProviderFieldsTypes), + "first_observed_at": flattenSecurityHubDateFilters(filters.FirstObservedAt), + "generator_id": flattenSecurityHubStringFilters(filters.GeneratorId), + "id": flattenSecurityHubStringFilters(filters.Id), + "keyword": flattenSecurityHubKeywordFilters(filters.Keyword), + "last_observed_at": flattenSecurityHubDateFilters(filters.LastObservedAt), + "malware_name": flattenSecurityHubStringFilters(filters.MalwareName), + "malware_path": flattenSecurityHubStringFilters(filters.MalwarePath), + "malware_state": flattenSecurityHubStringFilters(filters.MalwareState), + "malware_type": flattenSecurityHubStringFilters(filters.MalwareType), + "network_destination_domain": flattenSecurityHubStringFilters(filters.NetworkDestinationDomain), + "network_destination_ipv4": flattenSecurityHubIpFilters(filters.NetworkDestinationIpV4), + "network_destination_ipv6": flattenSecurityHubIpFilters(filters.NetworkDestinationIpV6), + "network_destination_port": flattenSecurityHubNumberFilters(filters.NetworkDestinationPort), + "network_direction": flattenSecurityHubStringFilters(filters.NetworkDirection), + "network_protocol": flattenSecurityHubStringFilters(filters.NetworkProtocol), + "network_source_domain": flattenSecurityHubStringFilters(filters.NetworkSourceDomain), + "network_source_ipv4": flattenSecurityHubIpFilters(filters.NetworkSourceIpV4), + "network_source_ipv6": flattenSecurityHubIpFilters(filters.NetworkSourceIpV6), + "network_source_mac": flattenSecurityHubStringFilters(filters.NetworkSourceMac), + "network_source_port": flattenSecurityHubNumberFilters(filters.NetworkSourcePort), + "note_text": flattenSecurityHubStringFilters(filters.NoteText), + "note_updated_at": flattenSecurityHubDateFilters(filters.NoteUpdatedAt), + "note_updated_by": flattenSecurityHubStringFilters(filters.NoteUpdatedBy), + "process_launched_at": flattenSecurityHubDateFilters(filters.ProcessLaunchedAt), + "process_name": flattenSecurityHubStringFilters(filters.ProcessName), + "process_parent_pid": flattenSecurityHubNumberFilters(filters.ProcessParentPid), + "process_path": flattenSecurityHubStringFilters(filters.ProcessPath), + "process_pid": flattenSecurityHubNumberFilters(filters.ProcessPid), + "process_terminated_at": flattenSecurityHubDateFilters(filters.ProcessTerminatedAt), + "product_arn": flattenSecurityHubStringFilters(filters.ProductArn), + "product_fields": flattenSecurityHubMapFilters(filters.ProductFields), + "product_name": flattenSecurityHubStringFilters(filters.ProductName), + "recommendation_text": flattenSecurityHubStringFilters(filters.RecommendationText), + "record_state": flattenSecurityHubStringFilters(filters.RecordState), + "related_findings_id": flattenSecurityHubStringFilters(filters.RelatedFindingsId), + "related_findings_product_arn": flattenSecurityHubStringFilters(filters.RelatedFindingsProductArn), + "resource_aws_ec2_instance_iam_instance_profile_arn": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceIamInstanceProfileArn), + "resource_aws_ec2_instance_image_id": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceImageId), + "resource_aws_ec2_instance_ipv4_addresses": flattenSecurityHubIpFilters(filters.ResourceAwsEc2InstanceIpV4Addresses), + "resource_aws_ec2_instance_ipv6_addresses": flattenSecurityHubIpFilters(filters.ResourceAwsEc2InstanceIpV6Addresses), + "resource_aws_ec2_instance_key_name": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceKeyName), + "resource_aws_ec2_instance_launched_at": flattenSecurityHubDateFilters(filters.ResourceAwsEc2InstanceLaunchedAt), + "resource_aws_ec2_instance_subnet_id": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceSubnetId), + "resource_aws_ec2_instance_type": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceType), + "resource_aws_ec2_instance_vpc_id": flattenSecurityHubStringFilters(filters.ResourceAwsEc2InstanceVpcId), + "resource_aws_iam_access_key_created_at": flattenSecurityHubDateFilters(filters.ResourceAwsIamAccessKeyCreatedAt), + "resource_aws_iam_access_key_status": flattenSecurityHubStringFilters(filters.ResourceAwsIamAccessKeyStatus), + "resource_aws_iam_access_key_user_name": flattenSecurityHubStringFilters(filters.ResourceAwsIamAccessKeyUserName), + "resource_aws_s3_bucket_owner_id": flattenSecurityHubStringFilters(filters.ResourceAwsS3BucketOwnerId), + "resource_aws_s3_bucket_owner_name": flattenSecurityHubStringFilters(filters.ResourceAwsS3BucketOwnerName), + "resource_container_image_id": flattenSecurityHubStringFilters(filters.ResourceContainerImageId), + "resource_container_image_name": flattenSecurityHubStringFilters(filters.ResourceContainerImageName), + "resource_container_launched_at": flattenSecurityHubDateFilters(filters.ResourceContainerLaunchedAt), + "resource_container_name": flattenSecurityHubStringFilters(filters.ResourceContainerName), + "resource_details_other": flattenSecurityHubMapFilters(filters.ResourceDetailsOther), + "resource_id": flattenSecurityHubStringFilters(filters.ResourceId), + "resource_partition": flattenSecurityHubStringFilters(filters.ResourcePartition), + "resource_region": flattenSecurityHubStringFilters(filters.ResourceRegion), + "resource_tags": flattenSecurityHubMapFilters(filters.ResourceTags), + "resource_type": flattenSecurityHubStringFilters(filters.ResourceType), + "severity_label": flattenSecurityHubStringFilters(filters.SeverityLabel), + "source_url": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorSourceUrl), + "threat_intel_indicator_category": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorCategory), + "threat_intel_indicator_last_observed_at": flattenSecurityHubDateFilters(filters.ThreatIntelIndicatorLastObservedAt), + "threat_intel_indicator_source": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorSource), + "threat_intel_indicator_source_url": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorSourceUrl), + "threat_intel_indicator_type": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorType), + "threat_intel_indicator_value": flattenSecurityHubStringFilters(filters.ThreatIntelIndicatorValue), + "title": flattenSecurityHubStringFilters(filters.Title), + "type": flattenSecurityHubStringFilters(filters.Type), + "updated_at": flattenSecurityHubDateFilters(filters.UpdatedAt), + "user_defined_values": flattenSecurityHubMapFilters(filters.UserDefinedFields), + "verification_state": flattenSecurityHubStringFilters(filters.VerificationState), + "workflow_status": flattenSecurityHubStringFilters(filters.WorkflowStatus), + } + + return []interface{}{m} +} + +func flattenSecurityHubStringFilters(filters []*securityhub.StringFilter) []interface{} { + if len(filters) == 0 { + return nil + } + + var stringFilters []interface{} + + for _, filter := range filters { + if filter == nil { + continue + } + + m := map[string]interface{}{ + "comparison": aws.StringValue(filter.Comparison), + "value": aws.StringValue(filter.Value), + } + + stringFilters = append(stringFilters, m) + } + + return stringFilters +} diff --git a/aws/resource_aws_securityhub_insight_test.go b/aws/resource_aws_securityhub_insight_test.go new file mode 100644 index 00000000000..d27216d713b --- /dev/null +++ b/aws/resource_aws_securityhub_insight_test.go @@ -0,0 +1,720 @@ +package aws + +import ( + "context" + "fmt" + "regexp" + "testing" + "time" + + "github.com/aws/aws-sdk-go/service/securityhub" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" + "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/terraform-providers/terraform-provider-aws/aws/internal/service/securityhub/finder" +) + +func testAccAwsSecurityHubInsight_basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + testAccCheckAwsSecurityHubInsightArn(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.aws_account_id.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.aws_account_id.*", map[string]string{ + "comparison": securityhub.StringFilterComparisonEquals, + "value": "1234567890", + }), + resource.TestCheckResourceAttr(resourceName, "group_by_attribute", "AwsAccountId"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsSecurityHubInsight(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_DateFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + endDate := time.Now().Add(5 * time.Minute).Format(time.RFC1123) + startDate := time.Now().Format(time.RFC1123) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_DateFilters_DateRange(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.created_at.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.created_at.*", map[string]string{ + "date_range.#": "1", + "date_range.0.unit": securityhub.DateRangeUnitDays, + "date_range.0.value": "5", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsSecurityHubInsightConfig_DateFilters_StartEnd(rName, startDate, endDate), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.created_at.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.created_at.*", map[string]string{ + "start": startDate, + "end": endDate, + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_IpFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_IpFilters(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.network_destination_ipv4.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.network_destination_ipv4.*", map[string]string{ + "cidr": "10.0.0.0/16", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_KeywordFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_KeywordFilters(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.keyword.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.keyword.*", map[string]string{ + "value": rName, + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_MapFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_MapFilters(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.product_fields.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.product_fields.*", map[string]string{ + "comparison": securityhub.MapFilterComparisonEquals, + "key": "key1", + "value": "value1", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_MultipleFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_MultipleFilters(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.aws_account_id.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.aws_account_id.*", map[string]string{ + "comparison": securityhub.StringFilterComparisonEquals, + "value": "1234567890", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.aws_account_id.*", map[string]string{ + "comparison": securityhub.StringFilterComparisonEquals, + "value": "09876543210", + }), + resource.TestCheckResourceAttr(resourceName, "filters.0.product_fields.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.product_fields.*", map[string]string{ + "comparison": securityhub.MapFilterComparisonEquals, + "key": "key1", + "value": "value1", + }), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.product_fields.*", map[string]string{ + "comparison": securityhub.MapFilterComparisonEquals, + "key": "key2", + "value": "value2", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsSecurityHubInsightConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.aws_account_id.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.aws_account_id.*", map[string]string{ + "comparison": securityhub.StringFilterComparisonEquals, + "value": "1234567890", + }), + ), + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_Name(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + rNameUpdated := acctest.RandomWithPrefix("tf-acc-test-update") + + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + testAccCheckAwsSecurityHubInsightArn(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rName), + ), + }, + { + Config: testAccAwsSecurityHubInsightConfig(rNameUpdated), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + testAccCheckAwsSecurityHubInsightArn(resourceName), + resource.TestCheckResourceAttr(resourceName, "name", rNameUpdated), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_NumberFilters(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_NumberFilters(rName, "eq = 50.5"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.confidence.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.confidence.*", map[string]string{ + "eq": "50.5", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAwsSecurityHubInsightConfig_NumberFilters(rName, "gte = 50.5"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.confidence.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.confidence.*", map[string]string{ + "gte": "50.5", + }), + ), + }, + { + Config: testAccAwsSecurityHubInsightConfig_NumberFilters(rName, "lte = 50.5"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.confidence.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.confidence.*", map[string]string{ + "lte": "50.5", + }), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_GroupByAttribute(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "group_by_attribute", "AwsAccountId"), + ), + }, + { + Config: testAccAwsSecurityHubInsightConfig_UpdateGroupByAttribute(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "group_by_attribute", "CompanyName"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsSecurityHubInsight_WorkflowStatus(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_securityhub_insight.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, securityhub.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsSecurityHubInsightDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsSecurityHubInsightConfig_WorkflowStatus(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsSecurityHubInsightExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "filters.#", "1"), + resource.TestCheckResourceAttr(resourceName, "filters.0.workflow_status.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "filters.0.workflow_status.*", map[string]string{ + "comparison": securityhub.StringFilterComparisonEquals, + "value": securityhub.WorkflowStatusNew, + }), + resource.TestCheckResourceAttr(resourceName, "group_by_attribute", "WorkflowStatus"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAwsSecurityHubInsightDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).securityhubconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_securityhub_insight" { + continue + } + + insight, err := finder.Insight(context.Background(), conn, rs.Primary.ID) + + if err != nil { + if tfawserr.ErrMessageContains(err, securityhub.ErrCodeInvalidAccessException, "not subscribed to AWS Security Hub") { + continue + } + if tfawserr.ErrCodeEquals(err, securityhub.ErrCodeResourceNotFoundException) { + continue + } + return fmt.Errorf("error deleting Security Hub Insight (%s): %w", rs.Primary.ID, err) + } + + if insight != nil { + return fmt.Errorf("Security Hub Insight (%s) still exists", rs.Primary.ID) + } + } + + return nil +} + +func testAccCheckAwsSecurityHubInsightExists(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) + } + + conn := testAccProvider.Meta().(*AWSClient).securityhubconn + + insight, err := finder.Insight(context.Background(), conn, rs.Primary.ID) + + if err != nil { + return fmt.Errorf("error reading Security Hub Insight (%s): %w", rs.Primary.ID, err) + } + + if insight == nil { + return fmt.Errorf("error reading Security Hub Insight (%s): not found", rs.Primary.ID) + } + + return nil + } +} + +func testAccCheckAwsSecurityHubInsightArn(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + expectedArn := regexp.MustCompile(fmt.Sprintf("insight/%s/custom/.+$", testAccGetAccountID())) + return testAccMatchResourceAttrRegionalARN(resourceName, "arn", "securityhub", expectedArn)(s) + } +} + +func testAccAwsSecurityHubInsightConfig(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + aws_account_id { + comparison = "EQUALS" + value = "1234567890" + } + } + + group_by_attribute = "AwsAccountId" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_DateFilters_DateRange(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + created_at { + date_range { + unit = "DAYS" + value = 5 + } + } + } + + group_by_attribute = "AwsAccountId" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_DateFilters_StartEnd(rName, startDate, endDate string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + created_at { + start = %q + end = %q + } + } + + group_by_attribute = "AwsAccountId" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, startDate, endDate, rName) +} + +func testAccAwsSecurityHubInsightConfig_IpFilters(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + network_destination_ipv4 { + cidr = "10.0.0.0/16" + } + } + + group_by_attribute = "AwsAccountId" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_KeywordFilters(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + keyword { + value = %[1]q + } + } + + group_by_attribute = "AwsAccountId" + + name = %[1]q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_MapFilters(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + product_fields { + comparison = "EQUALS" + key = "key1" + value = "value1" + } + } + + group_by_attribute = "AwsAccountId" + + name = %[1]q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_MultipleFilters(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + aws_account_id { + comparison = "EQUALS" + value = "1234567890" + } + + aws_account_id { + comparison = "EQUALS" + value = "09876543210" + } + + product_fields { + comparison = "EQUALS" + key = "key1" + value = "value1" + } + + product_fields { + comparison = "EQUALS" + key = "key2" + value = "value2" + } + } + + group_by_attribute = "AwsAccountId" + + name = %[1]q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_NumberFilters(rName, value string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + confidence { + %s + } + } + + group_by_attribute = "AwsAccountId" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, value, rName) +} + +func testAccAwsSecurityHubInsightConfig_UpdateGroupByAttribute(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + aws_account_id { + comparison = "EQUALS" + value = "1234567890" + } + } + + group_by_attribute = "CompanyName" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} + +func testAccAwsSecurityHubInsightConfig_WorkflowStatus(rName string) string { + return fmt.Sprintf(` +resource "aws_securityhub_account" "test" {} + +resource "aws_securityhub_insight" "test" { + filters { + workflow_status { + comparison = "EQUALS" + value = "NEW" + } + } + + group_by_attribute = "WorkflowStatus" + + name = %q + + depends_on = [aws_securityhub_account.test] +} +`, rName) +} diff --git a/aws/resource_aws_securityhub_test.go b/aws/resource_aws_securityhub_test.go index 30ae8950a97..ee4601b8d31 100644 --- a/aws/resource_aws_securityhub_test.go +++ b/aws/resource_aws_securityhub_test.go @@ -19,6 +19,19 @@ func TestAccAWSSecurityHub_serial(t *testing.T) { "Description": testAccAwsSecurityHubActionTarget_Description, "Name": testAccAwsSecurityHubActionTarget_Name, }, + "Insight": { + "basic": testAccAwsSecurityHubInsight_basic, + "disappears": testAccAwsSecurityHubInsight_disappears, + "DateFilters": testAccAwsSecurityHubInsight_DateFilters, + "GroupByAttribute": testAccAwsSecurityHubInsight_GroupByAttribute, + "IpFilters": testAccAwsSecurityHubInsight_IpFilters, + "KeywordFilters": testAccAwsSecurityHubInsight_KeywordFilters, + "MapFilters": testAccAwsSecurityHubInsight_MapFilters, + "MultipleFilters": testAccAwsSecurityHubInsight_MultipleFilters, + "Name": testAccAwsSecurityHubInsight_Name, + "NumberFilters": testAccAwsSecurityHubInsight_NumberFilters, + "WorkflowStatus": testAccAwsSecurityHubInsight_WorkflowStatus, + }, "InviteAccepter": { "basic": testAccAWSSecurityHubInviteAccepter_basic, }, diff --git a/website/docs/r/securityhub_insight.html.markdown b/website/docs/r/securityhub_insight.html.markdown new file mode 100644 index 00000000000..9ab6755d80d --- /dev/null +++ b/website/docs/r/securityhub_insight.html.markdown @@ -0,0 +1,388 @@ +--- +subcategory: "Security Hub" +layout: "aws" +page_title: "AWS: aws_securityhub_insight" +description: |- + Provides a Security Hub custom insight resource. +--- + +# Resource: aws_securityhub_insight + +Provides a Security Hub custom insight resource. See the [Managing custom insights section](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-custom-insights.html) of the AWS User Guide for more information. + +## Example Usage + +### Filter by AWS account ID + +```terraform +resource "aws_securityhub_account" "example" {} + +resource "aws_securityhub_insight" "example" { + filters { + aws_account_id { + comparison = "EQUALS" + value = "1234567890" + } + + aws_account_id { + comparison = "EQUALS" + value = "09876543210" + } + } + + group_by_attribute = "AwsAccountId" + + name = "example-insight" + + depends_on = [aws_securityhub_account.example] +} +``` + +### Filter by date range + +```terraform +resource "aws_securityhub_account" "example" {} + +resource "aws_securityhub_insight" "example" { + filters { + created_at { + date_range { + unit = "DAYS" + value = 5 + } + } + } + + group_by_attribute = "CreatedAt" + + name = "example-insight" + + depends_on = [aws_securityhub_account.example] +} +``` + +### Filter by destination IPv4 address + +```terraform +resource "aws_securityhub_account" "example" {} + +resource "aws_securityhub_insight" "example" { + filters { + network_destination_ipv4 { + cidr = "10.0.0.0/16" + } + } + + group_by_attribute = "NetworkDestinationIpV4" + + name = "example-insight" + + depends_on = [aws_securityhub_account.example] +} +``` + +### Filter by finding's confidence + +```terraform +resource "aws_securityhub_account" "example" {} + +resource "aws_securityhub_insight" "example" { + filters { + confidence { + gte = "80" + } + } + + group_by_attribute = "Confidence" + + name = "example-insight" + + depends_on = [aws_securityhub_account.example] +} +``` + +### Filter by resource tags + +```terraform +resource "aws_securityhub_account" "example" {} + +resource "aws_securityhub_insight" "example" { + filters { + resource_tags { + comparison = "EQUALS" + key = "Environment" + value = "Production" + } + } + + group_by_attribute = "ResourceTags" + + name = "example-insight" + + depends_on = [aws_securityhub_account.example] +} +``` + +## Argument Reference + +The following arguments are required: + +* `filters` - (Required) A configuration block including one or more (up to 10 distinct) attributes used to filter the findings included in the insight. The insight only includes findings that match criteria defined in the filters. See below for more details. + +* `group_by_attribute` - (Required) The attribute used to group the findings for the insight e.g. if an insight is grouped by `ResourceId`, then the insight produces a list of resource identifiers. + +* `name` - (Required) The name of the custom insight. + +The `filters` block supports: + +~> **NOTE:** For each argument below, up to 20 can be provided. + +* `aws_account_id` - (Optional) The AWS account ID that a finding is generated in. See [String_Filter](#string-filter-argument-reference) below for more details. + +* `company_name` - (Optional) The name of the findings provider (company) that owns the solution (product) that generates findings. See [String_Filter](#string-filter-argument-reference) below for more details. + +* `compliance_status` - (Optional) Exclusive to findings that are generated as the result of a check run against a specific rule in a supported standard, such as CIS AWS Foundations. Contains security standard-related finding details. See [String Filter](#string-filter-argument-reference) below for more details. + +* `confidence` - (Optional) A finding's confidence. Confidence is defined as the likelihood that a finding accurately identifies the behavior or issue that it was intended to identify. Confidence is scored on a 0-100 basis using a ratio scale, where 0 means zero percent confidence and 100 means 100 percent confidence. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `created_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider captured the potential security issue that a finding captured. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `criticality` - (Optional) The level of importance assigned to the resources associated with the finding. A score of 0 means that the underlying resources have no criticality, and a score of 100 is reserved for the most critical resources. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `description` - (Optional) A finding's description. See [String Filter](#string-filter-argument-reference) below for more details. + +* `finding_provider_fields_confidence` - (Optional) The finding provider value for the finding confidence. Confidence is defined as the likelihood that a finding accurately identifies the behavior or issue that it was intended to identify. Confidence is scored on a 0-100 basis using a ratio scale, where 0 means zero percent confidence and 100 means 100 percent confidence. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `finding_provider_fields_criticality` - (Optional) The finding provider value for the level of importance assigned to the resources associated with the findings. A score of 0 means that the underlying resources have no criticality, and a score of 100 is reserved for the most critical resources. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `finding_provider_fields_related_findings_id` - (Optional) The finding identifier of a related finding that is identified by the finding provider. See [String Filter](#string-filter-argument-reference) below for more details. + +* `finding_provider_fields_related_findings_product_arn` - (Optional) The ARN of the solution that generated a related finding that is identified by the finding provider. See [String Filter](#string-filter-argument-reference) below for more details. + +* `finding_provider_fields_severity_label` - (Optional) The finding provider value for the severity label. See [String Filter](#string-filter-argument-reference) below for more details. + +* `finding_provider_fields_severity_original` - (Optional) The finding provider's original value for the severity. See [String Filter](#string-filter-argument-reference) below for more details. + +* `finding_provider_fields_types` - (Optional) One or more finding types that the finding provider assigned to the finding. Uses the format of `namespace/category/classifier` that classify a finding. Valid namespace values include: `Software and Configuration Checks`, `TTPs`, `Effects`, `Unusual Behaviors`, and `Sensitive Data Identifications`. See [String Filter](#string-filter-argument-reference) below for more details. + +* `first_observed_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider first observed the potential security issue that a finding captured. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `generator_id` - (Optional) The identifier for the solution-specific component (a discrete unit of logic) that generated a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `id` - (Optional) The security findings provider-specific identifier for a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `keyword` - (Optional) A keyword for a finding. See [Keyword Filter](#keyword-filter-argument-reference) below for more details. + +* `last_observed_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider most recently observed the potential security issue that a finding captured. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `malware_name` - (Optional) The name of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. + +* `malware_path` - (Optional) The filesystem path of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. + +* `malware_state` - (Optional) The state of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. + +* `malware_type` - (Optional) The type of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_destination_domain` - (Optional) The destination domain of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_destination_ipv4` - (Optional) The destination IPv4 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `network_destination_ipv6` - (Optional) The destination IPv6 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `network_destination_port` - (Optional) The destination port of network-related information about a finding. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `network_direction` - (Optional) Indicates the direction of network traffic associated with a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_protocol` - (Optional) The protocol of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_source_domain` - (Optional) The source domain of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_source_ipv4` - (Optional) The source IPv4 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `network_source_ipv6` - (Optional) The source IPv6 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `network_source_mac` - (Optional) The source media access control (MAC) address of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `network_source_port` - (Optional) The source port of network-related information about a finding. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `note_text` - (Optional) The text of a note. See [String Filter](#string-filter-argument-reference) below for more details. + +* `note_updated_at` - (Optional) The timestamp of when the note was updated. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `note_updated_by` - (Optional) The principal that created a note. See [String Filter](#string-filter-argument-reference) below for more details. + +* `process_launched_at` - (Optional) The date/time that the process was launched. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `process_name` - (Optional) The name of the process. See [String Filter](#string-filter-argument-reference) below for more details. + +* `process_parent_pid` - (Optional) The parent process ID. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `process_path` - (Optional) The path to the process executable. See [String Filter](#string-filter-argument-reference) below for more details. + +* `process_pid` - (Optional) The process ID. See [Number Filter](#number-filter-argument-reference) below for more details. + +* `process_terminated_at` - (Optional) The date/time that the process was terminated. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `product_arn` - (Optional) The ARN generated by Security Hub that uniquely identifies a third-party company (security findings provider) after this provider's product (solution that generates findings) is registered with Security Hub. See [String Filter](#string-filter-argument-reference) below for more details. + +* `product_fields` - (Optional) A data type where security-findings providers can include additional solution-specific details that aren't part of the defined `AwsSecurityFinding` format. See [Map Filter](#map-filter-argument-reference) below for more details. + +* `product_name` - (Optional) The name of the solution (product) that generates findings. See [String Filter](#string-filter-argument-reference) below for more details. + +* `recommendation_text` - (Optional) The recommendation of what to do about the issue described in a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `record_state` - (Optional) The updated record state for the finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `related_findings_id` - (Optional) The solution-generated identifier for a related finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `related_findings_product_arn` - (Optional) The ARN of the solution that generated a related finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_iam_instance_profile_arn` - (Optional) The IAM profile ARN of the instance. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_image_id` - (Optional) The Amazon Machine Image (AMI) ID of the instance. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_ipv4_addresses` - (Optional) The IPv4 addresses associated with the instance. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_ipv6_addresses` - (Optional) The IPv6 addresses associated with the instance. See [Ip Filter](#ip-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_key_name` - (Optional) The key name associated with the instance. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_launched_at` - (Optional) The date and time the instance was launched. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_subnet_id` - (Optional) The identifier of the subnet that the instance was launched in. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_type` - (Optional) The instance type of the instance. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_ec2_instance_vpc_id` - (Optional) The identifier of the VPC that the instance was launched in. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_iam_access_key_created_at` - (Optional) The creation date/time of the IAM access key related to a finding. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `resource_aws_iam_access_key_status` - (Optional) The status of the IAM access key related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_iam_access_key_user_name` - (Optional) The user associated with the IAM access key related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_s3_bucket_owner_id` - (Optional) The canonical user ID of the owner of the S3 bucket. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_aws_s3_bucket_owner_name` - (Optional) The display name of the owner of the S3 bucket. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_container_image_id` - (Optional) The identifier of the image related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_container_image_name` - (Optional) The name of the image related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_container_launched_at` - (Optional) The date/time that the container was started. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `resource_container_name` - (Optional) The name of the container related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_details_other` - (Optional) The details of a resource that doesn't have a specific subfield for the resource type defined. See [Map Filter](#map-filter-argument-reference) below for more details. + +* `resource_id` - (Optional) The canonical identifier for the given resource type. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_partition` - (Optional) The canonical AWS partition name that the Region is assigned to. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_region` - (Optional) The canonical AWS external Region name where this resource is located. See [String Filter](#string-filter-argument-reference) below for more details. + +* `resource_tags` - (Optional) A list of AWS tags associated with a resource at the time the finding was processed. See [Map Filter](#map-filter-argument-reference) below for more details. + +* `resource_type` - (Optional) Specifies the type of the resource that details are provided for. See [String Filter](#string-filter-argument-reference) below for more details. + +* `severity_label` - (Optional) The label of a finding's severity. See [String Filter](#string-filter-argument-reference) below for more details. + +* `source_url` - (Optional) A URL that links to a page about the current finding in the security-findings provider's solution. See [String Filter](#string-filter-argument-reference) below for more details. + +* `threat_intel_indicator_category` - (Optional) The category of a threat intelligence indicator. See [String Filter](#string-filter-argument-reference) below for more details. + +* `threat_intel_indicator_last_observed_at` - (Optional) The date/time of the last observation of a threat intelligence indicator. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `threat_intel_indicator_source` - (Optional) The source of the threat intelligence. See [String Filter](#string-filter-argument-reference) below for more details. + +* `threat_intel_indicator_source_url` - (Optional) The URL for more details from the source of the threat intelligence. See [String Filter](#string-filter-argument-reference) below for more details. + +* `threat_intel_indicator_type` - (Optional) The type of a threat intelligence indicator. See [String Filter](#string-filter-argument-reference) below for more details. + +* `threat_intel_indicator_value` - (Optional) The value of a threat intelligence indicator. See [String Filter](#string-filter-argument-reference) below for more details. + +* `title` - (Optional) A finding's title. See [String Filter](#string-filter-argument-reference) below for more details. + +* `type` - (Optional) A finding type in the format of `namespace/category/classifier` that classifies a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `updated_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider last updated the finding record. See [Date Filter](#date-filter-argument-reference) below for more details. + +* `user_defined_values` - (Optional) A list of name/value string pairs associated with the finding. These are custom, user-defined fields added to a finding. See [Map Filter](#map-filter-argument-reference) below for more details. + +* `verification_state` - (Optional) The veracity of a finding. See [String Filter](#string-filter-argument-reference) below for more details. + +* `workflow_status` - (Optional) The status of the investigation into a finding. See [Workflow Status Filter](#workflow-status-filter-argument-reference) below for more details. + +### Date Filter Argument reference + +The date filter configuration block supports the following arguments: + +* `date_range` - (Optional) A configuration block of the date range for the date filter. See [date_range](#date_range-argument-reference) below for more details. +* `end` - (Optional) An end date for the date filter. Required with `start` if `date_range` is not specified. +* `start` - (Optional) A start date for the date filter. Required with `end` if `date_range` is not specified. + +### date_range Argument reference + +The `date_range` configuration block supports the following arguments: + +* `unit` - (Required) A date range unit for the date filter. Valid values: `DAYS`. +* `value` - (Required) A date range value for the date filter, provided as an Integer. + +### Ip Filter Argument Reference + +The Ip filter configuration block supports the following arguments: + +* `cidr` - (Required) A finding's CIDR value. + +### Keyword Filter Argument Reference + +The keyword filter configuration block supports the following arguments: + +* `value` - (Required) A value for the keyword. + +### Map Filter Argument reference + +The map filter configuration block supports the following arguments: + +* `comparison` - (Required) The condition to apply to a string value when querying for findings. Valid values include: `EQUALS` and `NOT_EQUALS`. +* `key` - (Required) The key of the map filter. For example, for `ResourceTags`, `Key` identifies the name of the tag. For `UserDefinedFields`, `Key` is the name of the field. +* `value` - (Required) The value for the key in the map filter. Filter values are case sensitive. For example, one of the values for a tag called `Department` might be `Security`. If you provide `security` as the filter value, then there is no match. + +### Number Filter Argument reference + +The number filter configuration block supports the following arguments: + +~> **NOTE:** Only one of `eg`, `gte`, or `lte` must be specified. + +* `eq` - (Optional) The equal-to condition to be applied to a single field when querying for findings, provided as a String. +* `gte` - (Optional) The greater-than-equal condition to be applied to a single field when querying for findings, provided as a String. +* `lte` - (Optional) The less-than-equal condition to be applied to a single field when querying for findings, provided as a String. + +### String Filter Argument reference + +The string filter configuration block supports the following arguments: + +* `comparison` - (Required) The condition to apply to a string value when querying for findings. Valid values include: `EQUALS`, `PREFIX`, `NOT_EQUALS`, `PREFIX_NOT_EQUALS`. +* `value` - (Required) The string filter value. Filter values are case sensitive. + +### Workflow Status Filter Argument reference + +The workflow status filter configuration block supports the following arguments: + +* `comparison` - (Required) The condition to apply to a string value when querying for findings. Valid values include: `EQUALS`, `PREFIX`, `NOT_EQUALS`, `PREFIX_NOT_EQUALS`. +* `value` - (Required) The string filter value. Valid values include: `NEW`, `NOTIFIED`, `SUPPRESSED`, and `RESOLVED`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The Amazon Resource Name (ARN) of the insight. +* `arn` - The Amazon Resource Name (ARN) of the insight. + +## Import + +Security Hub insights can be imported using the Amazon Resource Name (ARN), e.g. + +``` +$ terraform import aws_securityhub_insight.example arn:aws:securityhub:us-west-2:1234567890:insight/1234567890/custom/91299ed7-abd0-4e44-a858-d0b15e37141a +``` From 389ca2b84244701eb37cd7996129790247aacdae Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Wed, 31 Mar 2021 00:00:01 -0400 Subject: [PATCH 05/11] terrafmt and changelog entry --- .changelog/18494.txt | 3 +++ aws/resource_aws_securityhub_insight_test.go | 26 ++++++++++---------- 2 files changed, 16 insertions(+), 13 deletions(-) create mode 100644 .changelog/18494.txt diff --git a/.changelog/18494.txt b/.changelog/18494.txt new file mode 100644 index 00000000000..1cb4873fcb8 --- /dev/null +++ b/.changelog/18494.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_securityhub_insight +``` \ No newline at end of file diff --git a/aws/resource_aws_securityhub_insight_test.go b/aws/resource_aws_securityhub_insight_test.go index d27216d713b..2fcc627ac27 100644 --- a/aws/resource_aws_securityhub_insight_test.go +++ b/aws/resource_aws_securityhub_insight_test.go @@ -521,9 +521,9 @@ resource "aws_securityhub_insight" "test" { filters { created_at { date_range { - unit = "DAYS" - value = 5 - } + unit = "DAYS" + value = 5 + } } } @@ -564,7 +564,7 @@ resource "aws_securityhub_account" "test" {} resource "aws_securityhub_insight" "test" { filters { network_destination_ipv4 { - cidr = "10.0.0.0/16" + cidr = "10.0.0.0/16" } } @@ -603,10 +603,10 @@ resource "aws_securityhub_account" "test" {} resource "aws_securityhub_insight" "test" { filters { - product_fields { - comparison = "EQUALS" + product_fields { + comparison = "EQUALS" key = "key1" - value = "value1" + value = "value1" } } @@ -635,16 +635,16 @@ resource "aws_securityhub_insight" "test" { value = "09876543210" } - product_fields { - comparison = "EQUALS" + product_fields { + comparison = "EQUALS" key = "key1" - value = "value1" + value = "value1" } product_fields { - comparison = "EQUALS" + comparison = "EQUALS" key = "key2" - value = "value2" + value = "value2" } } @@ -663,7 +663,7 @@ resource "aws_securityhub_account" "test" {} resource "aws_securityhub_insight" "test" { filters { - confidence { + confidence { %s } } From fa3b2048e26cfa1ba61daeed6942b5b2c694c03b Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Thu, 15 Apr 2021 13:54:50 -0400 Subject: [PATCH 06/11] update ARN check for GovCloud naming pattern --- aws/resource_aws_securityhub_insight_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/aws/resource_aws_securityhub_insight_test.go b/aws/resource_aws_securityhub_insight_test.go index 2fcc627ac27..98e6d66dfbe 100644 --- a/aws/resource_aws_securityhub_insight_test.go +++ b/aws/resource_aws_securityhub_insight_test.go @@ -485,10 +485,14 @@ func testAccCheckAwsSecurityHubInsightExists(n string) resource.TestCheckFunc { } } +// testAccCheckAwsSecurityHubInsightArn checks the computed ARN value +// and accounts for differences in SecurityHub on GovCloud where the partition portion +// of the ARN is still "aws" while other services utilize the "aws-us-gov" partition func testAccCheckAwsSecurityHubInsightArn(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { - expectedArn := regexp.MustCompile(fmt.Sprintf("insight/%s/custom/.+$", testAccGetAccountID())) - return testAccMatchResourceAttrRegionalARN(resourceName, "arn", "securityhub", expectedArn)(s) + expectedArn := fmt.Sprintf(`^arn:aws[^:]*:securityhub:%s:%s:insight/%s/custom/.+$`, testAccGetRegion(), testAccGetAccountID(), testAccGetAccountID()) + //lintignore:AWSAT001 + return resource.TestMatchResourceAttr(resourceName, "arn", regexp.MustCompile(expectedArn))(s) } } From 665d1c7157d4f450a872d5f045acdf64e5e3b353 Mon Sep 17 00:00:00 2001 From: angie pinilla Date: Thu, 15 Apr 2021 16:07:10 -0400 Subject: [PATCH 07/11] Update website/docs/r/securityhub_insight.html.markdown Co-authored-by: Dirk Avery <31492422+YakDriver@users.noreply.github.com> --- website/docs/r/securityhub_insight.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/securityhub_insight.html.markdown b/website/docs/r/securityhub_insight.html.markdown index 9ab6755d80d..16e8ccdc5f6 100644 --- a/website/docs/r/securityhub_insight.html.markdown +++ b/website/docs/r/securityhub_insight.html.markdown @@ -376,7 +376,7 @@ The workflow status filter configuration block supports the following arguments: In addition to all arguments above, the following attributes are exported: -* `id` - The Amazon Resource Name (ARN) of the insight. +* `id` - ARN of the insight. * `arn` - The Amazon Resource Name (ARN) of the insight. ## Import From d2ee5ab51c90ed5ecbbd486072554f2fa7094292 Mon Sep 17 00:00:00 2001 From: angie pinilla Date: Thu, 15 Apr 2021 16:08:33 -0400 Subject: [PATCH 08/11] Update website/docs/r/securityhub_insight.html.markdown Co-authored-by: Dirk Avery <31492422+YakDriver@users.noreply.github.com> --- website/docs/r/securityhub_insight.html.markdown | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/docs/r/securityhub_insight.html.markdown b/website/docs/r/securityhub_insight.html.markdown index 16e8ccdc5f6..229982e5d97 100644 --- a/website/docs/r/securityhub_insight.html.markdown +++ b/website/docs/r/securityhub_insight.html.markdown @@ -133,7 +133,9 @@ The following arguments are required: * `name` - (Required) The name of the custom insight. -The `filters` block supports: +### filters + +The `filters` configuration block supports the following arguments: ~> **NOTE:** For each argument below, up to 20 can be provided. From efe71e9db06943959ce51d48a8f7fb047675f17c Mon Sep 17 00:00:00 2001 From: angie pinilla Date: Thu, 15 Apr 2021 16:19:04 -0400 Subject: [PATCH 09/11] Update website/docs/r/securityhub_insight.html.markdown Co-authored-by: Dirk Avery <31492422+YakDriver@users.noreply.github.com> --- website/docs/r/securityhub_insight.html.markdown | 1 - 1 file changed, 1 deletion(-) diff --git a/website/docs/r/securityhub_insight.html.markdown b/website/docs/r/securityhub_insight.html.markdown index 229982e5d97..4e8cc5a8180 100644 --- a/website/docs/r/securityhub_insight.html.markdown +++ b/website/docs/r/securityhub_insight.html.markdown @@ -128,7 +128,6 @@ resource "aws_securityhub_insight" "example" { The following arguments are required: * `filters` - (Required) A configuration block including one or more (up to 10 distinct) attributes used to filter the findings included in the insight. The insight only includes findings that match criteria defined in the filters. See below for more details. - * `group_by_attribute` - (Required) The attribute used to group the findings for the insight e.g. if an insight is grouped by `ResourceId`, then the insight produces a list of resource identifiers. * `name` - (Required) The name of the custom insight. From b430ce253516c6a67c46e2e23a69ed2d94d806f9 Mon Sep 17 00:00:00 2001 From: angie pinilla Date: Thu, 15 Apr 2021 16:19:46 -0400 Subject: [PATCH 10/11] Update website/docs/r/securityhub_insight.html.markdown Co-authored-by: Dirk Avery <31492422+YakDriver@users.noreply.github.com> --- website/docs/r/securityhub_insight.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/securityhub_insight.html.markdown b/website/docs/r/securityhub_insight.html.markdown index 4e8cc5a8180..05d9835fa9c 100644 --- a/website/docs/r/securityhub_insight.html.markdown +++ b/website/docs/r/securityhub_insight.html.markdown @@ -138,7 +138,7 @@ The `filters` configuration block supports the following arguments: ~> **NOTE:** For each argument below, up to 20 can be provided. -* `aws_account_id` - (Optional) The AWS account ID that a finding is generated in. See [String_Filter](#string-filter-argument-reference) below for more details. +* `aws_account_id` - (Optional) AWS account ID that a finding is generated in. See [String_Filter](#string-filter-argument-reference) below for more details. * `company_name` - (Optional) The name of the findings provider (company) that owns the solution (product) that generates findings. See [String_Filter](#string-filter-argument-reference) below for more details. From 10213f2b42f77a2fa28d9d15f400edff368653af Mon Sep 17 00:00:00 2001 From: Angie Pinilla Date: Thu, 15 Apr 2021 16:35:59 -0400 Subject: [PATCH 11/11] update arg spacing --- .../docs/r/securityhub_insight.html.markdown | 94 +------------------ 1 file changed, 3 insertions(+), 91 deletions(-) diff --git a/website/docs/r/securityhub_insight.html.markdown b/website/docs/r/securityhub_insight.html.markdown index 05d9835fa9c..0f0e12b8339 100644 --- a/website/docs/r/securityhub_insight.html.markdown +++ b/website/docs/r/securityhub_insight.html.markdown @@ -127,9 +127,8 @@ resource "aws_securityhub_insight" "example" { The following arguments are required: -* `filters` - (Required) A configuration block including one or more (up to 10 distinct) attributes used to filter the findings included in the insight. The insight only includes findings that match criteria defined in the filters. See below for more details. +* `filters` - (Required) A configuration block including one or more (up to 10 distinct) attributes used to filter the findings included in the insight. The insight only includes findings that match criteria defined in the filters. See [filters](#filters) below for more details. * `group_by_attribute` - (Required) The attribute used to group the findings for the insight e.g. if an insight is grouped by `ResourceId`, then the insight produces a list of resource identifiers. - * `name` - (Required) The name of the custom insight. ### filters @@ -139,179 +138,92 @@ The `filters` configuration block supports the following arguments: ~> **NOTE:** For each argument below, up to 20 can be provided. * `aws_account_id` - (Optional) AWS account ID that a finding is generated in. See [String_Filter](#string-filter-argument-reference) below for more details. - * `company_name` - (Optional) The name of the findings provider (company) that owns the solution (product) that generates findings. See [String_Filter](#string-filter-argument-reference) below for more details. - * `compliance_status` - (Optional) Exclusive to findings that are generated as the result of a check run against a specific rule in a supported standard, such as CIS AWS Foundations. Contains security standard-related finding details. See [String Filter](#string-filter-argument-reference) below for more details. - * `confidence` - (Optional) A finding's confidence. Confidence is defined as the likelihood that a finding accurately identifies the behavior or issue that it was intended to identify. Confidence is scored on a 0-100 basis using a ratio scale, where 0 means zero percent confidence and 100 means 100 percent confidence. See [Number Filter](#number-filter-argument-reference) below for more details. - * `created_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider captured the potential security issue that a finding captured. See [Date Filter](#date-filter-argument-reference) below for more details. - * `criticality` - (Optional) The level of importance assigned to the resources associated with the finding. A score of 0 means that the underlying resources have no criticality, and a score of 100 is reserved for the most critical resources. See [Number Filter](#number-filter-argument-reference) below for more details. - * `description` - (Optional) A finding's description. See [String Filter](#string-filter-argument-reference) below for more details. - * `finding_provider_fields_confidence` - (Optional) The finding provider value for the finding confidence. Confidence is defined as the likelihood that a finding accurately identifies the behavior or issue that it was intended to identify. Confidence is scored on a 0-100 basis using a ratio scale, where 0 means zero percent confidence and 100 means 100 percent confidence. See [Number Filter](#number-filter-argument-reference) below for more details. - * `finding_provider_fields_criticality` - (Optional) The finding provider value for the level of importance assigned to the resources associated with the findings. A score of 0 means that the underlying resources have no criticality, and a score of 100 is reserved for the most critical resources. See [Number Filter](#number-filter-argument-reference) below for more details. - * `finding_provider_fields_related_findings_id` - (Optional) The finding identifier of a related finding that is identified by the finding provider. See [String Filter](#string-filter-argument-reference) below for more details. - * `finding_provider_fields_related_findings_product_arn` - (Optional) The ARN of the solution that generated a related finding that is identified by the finding provider. See [String Filter](#string-filter-argument-reference) below for more details. - * `finding_provider_fields_severity_label` - (Optional) The finding provider value for the severity label. See [String Filter](#string-filter-argument-reference) below for more details. - * `finding_provider_fields_severity_original` - (Optional) The finding provider's original value for the severity. See [String Filter](#string-filter-argument-reference) below for more details. - * `finding_provider_fields_types` - (Optional) One or more finding types that the finding provider assigned to the finding. Uses the format of `namespace/category/classifier` that classify a finding. Valid namespace values include: `Software and Configuration Checks`, `TTPs`, `Effects`, `Unusual Behaviors`, and `Sensitive Data Identifications`. See [String Filter](#string-filter-argument-reference) below for more details. - * `first_observed_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider first observed the potential security issue that a finding captured. See [Date Filter](#date-filter-argument-reference) below for more details. - * `generator_id` - (Optional) The identifier for the solution-specific component (a discrete unit of logic) that generated a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `id` - (Optional) The security findings provider-specific identifier for a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `keyword` - (Optional) A keyword for a finding. See [Keyword Filter](#keyword-filter-argument-reference) below for more details. - * `last_observed_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider most recently observed the potential security issue that a finding captured. See [Date Filter](#date-filter-argument-reference) below for more details. - * `malware_name` - (Optional) The name of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. - * `malware_path` - (Optional) The filesystem path of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. - * `malware_state` - (Optional) The state of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. - * `malware_type` - (Optional) The type of the malware that was observed. See [String Filter](#string-filter-argument-reference) below for more details. - * `network_destination_domain` - (Optional) The destination domain of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `network_destination_ipv4` - (Optional) The destination IPv4 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. - * `network_destination_ipv6` - (Optional) The destination IPv6 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. - * `network_destination_port` - (Optional) The destination port of network-related information about a finding. See [Number Filter](#number-filter-argument-reference) below for more details. - * `network_direction` - (Optional) Indicates the direction of network traffic associated with a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `network_protocol` - (Optional) The protocol of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `network_source_domain` - (Optional) The source domain of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `network_source_ipv4` - (Optional) The source IPv4 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. - * `network_source_ipv6` - (Optional) The source IPv6 address of network-related information about a finding. See [Ip Filter](#ip-filter-argument-reference) below for more details. - * `network_source_mac` - (Optional) The source media access control (MAC) address of network-related information about a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `network_source_port` - (Optional) The source port of network-related information about a finding. See [Number Filter](#number-filter-argument-reference) below for more details. - * `note_text` - (Optional) The text of a note. See [String Filter](#string-filter-argument-reference) below for more details. - * `note_updated_at` - (Optional) The timestamp of when the note was updated. See [Date Filter](#date-filter-argument-reference) below for more details. - * `note_updated_by` - (Optional) The principal that created a note. See [String Filter](#string-filter-argument-reference) below for more details. - * `process_launched_at` - (Optional) The date/time that the process was launched. See [Date Filter](#date-filter-argument-reference) below for more details. - * `process_name` - (Optional) The name of the process. See [String Filter](#string-filter-argument-reference) below for more details. - * `process_parent_pid` - (Optional) The parent process ID. See [Number Filter](#number-filter-argument-reference) below for more details. - * `process_path` - (Optional) The path to the process executable. See [String Filter](#string-filter-argument-reference) below for more details. - * `process_pid` - (Optional) The process ID. See [Number Filter](#number-filter-argument-reference) below for more details. - * `process_terminated_at` - (Optional) The date/time that the process was terminated. See [Date Filter](#date-filter-argument-reference) below for more details. - * `product_arn` - (Optional) The ARN generated by Security Hub that uniquely identifies a third-party company (security findings provider) after this provider's product (solution that generates findings) is registered with Security Hub. See [String Filter](#string-filter-argument-reference) below for more details. - * `product_fields` - (Optional) A data type where security-findings providers can include additional solution-specific details that aren't part of the defined `AwsSecurityFinding` format. See [Map Filter](#map-filter-argument-reference) below for more details. - * `product_name` - (Optional) The name of the solution (product) that generates findings. See [String Filter](#string-filter-argument-reference) below for more details. - * `recommendation_text` - (Optional) The recommendation of what to do about the issue described in a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `record_state` - (Optional) The updated record state for the finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `related_findings_id` - (Optional) The solution-generated identifier for a related finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `related_findings_product_arn` - (Optional) The ARN of the solution that generated a related finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_aws_ec2_instance_iam_instance_profile_arn` - (Optional) The IAM profile ARN of the instance. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_aws_ec2_instance_image_id` - (Optional) The Amazon Machine Image (AMI) ID of the instance. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_aws_ec2_instance_ipv4_addresses` - (Optional) The IPv4 addresses associated with the instance. See [Ip Filter](#ip-filter-argument-reference) below for more details. - * `resource_aws_ec2_instance_ipv6_addresses` - (Optional) The IPv6 addresses associated with the instance. See [Ip Filter](#ip-filter-argument-reference) below for more details. - * `resource_aws_ec2_instance_key_name` - (Optional) The key name associated with the instance. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_aws_ec2_instance_launched_at` - (Optional) The date and time the instance was launched. See [Date Filter](#date-filter-argument-reference) below for more details. - * `resource_aws_ec2_instance_subnet_id` - (Optional) The identifier of the subnet that the instance was launched in. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_aws_ec2_instance_type` - (Optional) The instance type of the instance. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_aws_ec2_instance_vpc_id` - (Optional) The identifier of the VPC that the instance was launched in. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_aws_iam_access_key_created_at` - (Optional) The creation date/time of the IAM access key related to a finding. See [Date Filter](#date-filter-argument-reference) below for more details. - * `resource_aws_iam_access_key_status` - (Optional) The status of the IAM access key related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_aws_iam_access_key_user_name` - (Optional) The user associated with the IAM access key related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_aws_s3_bucket_owner_id` - (Optional) The canonical user ID of the owner of the S3 bucket. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_aws_s3_bucket_owner_name` - (Optional) The display name of the owner of the S3 bucket. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_container_image_id` - (Optional) The identifier of the image related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_container_image_name` - (Optional) The name of the image related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_container_launched_at` - (Optional) The date/time that the container was started. See [Date Filter](#date-filter-argument-reference) below for more details. - * `resource_container_name` - (Optional) The name of the container related to a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_details_other` - (Optional) The details of a resource that doesn't have a specific subfield for the resource type defined. See [Map Filter](#map-filter-argument-reference) below for more details. - * `resource_id` - (Optional) The canonical identifier for the given resource type. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_partition` - (Optional) The canonical AWS partition name that the Region is assigned to. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_region` - (Optional) The canonical AWS external Region name where this resource is located. See [String Filter](#string-filter-argument-reference) below for more details. - * `resource_tags` - (Optional) A list of AWS tags associated with a resource at the time the finding was processed. See [Map Filter](#map-filter-argument-reference) below for more details. - * `resource_type` - (Optional) Specifies the type of the resource that details are provided for. See [String Filter](#string-filter-argument-reference) below for more details. - * `severity_label` - (Optional) The label of a finding's severity. See [String Filter](#string-filter-argument-reference) below for more details. - * `source_url` - (Optional) A URL that links to a page about the current finding in the security-findings provider's solution. See [String Filter](#string-filter-argument-reference) below for more details. - * `threat_intel_indicator_category` - (Optional) The category of a threat intelligence indicator. See [String Filter](#string-filter-argument-reference) below for more details. - * `threat_intel_indicator_last_observed_at` - (Optional) The date/time of the last observation of a threat intelligence indicator. See [Date Filter](#date-filter-argument-reference) below for more details. - * `threat_intel_indicator_source` - (Optional) The source of the threat intelligence. See [String Filter](#string-filter-argument-reference) below for more details. - * `threat_intel_indicator_source_url` - (Optional) The URL for more details from the source of the threat intelligence. See [String Filter](#string-filter-argument-reference) below for more details. - * `threat_intel_indicator_type` - (Optional) The type of a threat intelligence indicator. See [String Filter](#string-filter-argument-reference) below for more details. - * `threat_intel_indicator_value` - (Optional) The value of a threat intelligence indicator. See [String Filter](#string-filter-argument-reference) below for more details. - * `title` - (Optional) A finding's title. See [String Filter](#string-filter-argument-reference) below for more details. - * `type` - (Optional) A finding type in the format of `namespace/category/classifier` that classifies a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `updated_at` - (Optional) An ISO8601-formatted timestamp that indicates when the security-findings provider last updated the finding record. See [Date Filter](#date-filter-argument-reference) below for more details. - * `user_defined_values` - (Optional) A list of name/value string pairs associated with the finding. These are custom, user-defined fields added to a finding. See [Map Filter](#map-filter-argument-reference) below for more details. - * `verification_state` - (Optional) The veracity of a finding. See [String Filter](#string-filter-argument-reference) below for more details. - * `workflow_status` - (Optional) The status of the investigation into a finding. See [Workflow Status Filter](#workflow-status-filter-argument-reference) below for more details. ### Date Filter Argument reference @@ -378,11 +290,11 @@ The workflow status filter configuration block supports the following arguments: In addition to all arguments above, the following attributes are exported: * `id` - ARN of the insight. -* `arn` - The Amazon Resource Name (ARN) of the insight. +* `arn` - ARN of the insight. ## Import -Security Hub insights can be imported using the Amazon Resource Name (ARN), e.g. +Security Hub insights can be imported using the ARN, e.g. ``` $ terraform import aws_securityhub_insight.example arn:aws:securityhub:us-west-2:1234567890:insight/1234567890/custom/91299ed7-abd0-4e44-a858-d0b15e37141a