From 150c09ff101294c0ca994b252455e42930ab2c4a Mon Sep 17 00:00:00 2001 From: bill-rich Date: Thu, 7 Jan 2021 23:10:21 -0800 Subject: [PATCH] Code review changes --- aws/resource_aws_fms_policy.go | 563 +++--------------------- aws/resource_aws_fms_policy_test.go | 198 ++++++--- website/docs/r/fms_policy.html.markdown | 41 +- 3 files changed, 234 insertions(+), 568 deletions(-) diff --git a/aws/resource_aws_fms_policy.go b/aws/resource_aws_fms_policy.go index a1ac82c9b3df..fccddfa43b48 100644 --- a/aws/resource_aws_fms_policy.go +++ b/aws/resource_aws_fms_policy.go @@ -1,16 +1,13 @@ package aws import ( - "encoding/json" "fmt" "log" - "reflect" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/fms" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" - "github.com/mitchellh/mapstructure" ) func resourceAwsFmsPolicy() *schema.Resource { @@ -27,7 +24,6 @@ func resourceAwsFmsPolicy() *schema.Resource { "name": { Type: schema.TypeString, Required: true, - ForceNew: true, }, "delete_all_policy_resources": { @@ -39,13 +35,13 @@ func resourceAwsFmsPolicy() *schema.Resource { "exclude_resource_tags": { Type: schema.TypeBool, Required: true, - ForceNew: true, }, "exclude_map": { - Type: schema.TypeSet, - MaxItems: 1, - Optional: true, + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + DiffSuppressFunc: suppressMissingOptionalConfigurationBlock, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "account": { @@ -67,9 +63,10 @@ func resourceAwsFmsPolicy() *schema.Resource { }, "include_map": { - Type: schema.TypeSet, - MaxItems: 1, - Optional: true, + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + DiffSuppressFunc: suppressMissingOptionalConfigurationBlock, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "account": { @@ -92,13 +89,12 @@ func resourceAwsFmsPolicy() *schema.Resource { "remediation_enabled": { Type: schema.TypeBool, - Required: true, + Optional: true, }, "resource_type_list": { Type: schema.TypeSet, Required: true, - ForceNew: true, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{"AWS::ApiGateway::Stage", "AWS::ElasticLoadBalancingV2::LoadBalancer", "AWS::CloudFront::Distribution"}, false), @@ -115,20 +111,19 @@ func resourceAwsFmsPolicy() *schema.Resource { "security_service_policy_data": { Type: schema.TypeList, - MaxItems: 1, Required: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "waf": wafSchema(), - "wafv2": wafV2Schema(), - "shield_advanced": { - Type: schema.TypeBool, - ExactlyOneOf: []string{"security_service_policy_data.0.waf", "security_service_policy_data.0.wafv2", "security_service_policy_data.0.shield_advanced", "security_service_policy_data.0.security_groups_common", "security_service_policy_data.0.security_groups_content_audit", "security_service_policy_data.0.security_groups_usage_audit"}, - Optional: true, + "type": { + Type: schema.TypeString, + Required: true, + }, + "managed_service_data": { + Type: schema.TypeString, + Optional: true, + DiffSuppressFunc: suppressEquivalentJsonDiffs, }, - "security_groups_common": securityGroupsCommon(), - "security_groups_content_audit": securityGroupsContentAudit(), - "security_groups_usage_audit": securityGroupsUsageAudit(), }, }, }, @@ -140,322 +135,6 @@ func resourceAwsFmsPolicy() *schema.Resource { } } -func securityGroupsCommon() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - MaxItems: 1, - ExactlyOneOf: []string{"security_service_policy_data.0.waf", "security_service_policy_data.0.wafv2", "security_service_policy_data.0.shield_advanced", "security_service_policy_data.0.security_groups_common", "security_service_policy_data.0.security_groups_content_audit", "security_service_policy_data.0.security_groups_usage_audit"}, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "revert_manual_security_group_changes": { - Type: schema.TypeBool, - Optional: true, - }, - "exclusive_resource_security_group_management": { - Type: schema.TypeBool, - Optional: true, - }, - "security_groups": { - Type: schema.TypeSet, - Required: true, - Elem: schema.TypeString, - }, - "remediation_enabled": { - Type: schema.TypeBool, - Optional: true, - }, - "resource_type": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - } -} - -func securityGroupsContentAudit() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - MaxItems: 1, - ExactlyOneOf: []string{"security_service_policy_data.0.waf", "security_service_policy_data.0.wafv2", "security_service_policy_data.0.shield_advanced", "security_service_policy_data.0.security_groups_common", "security_service_policy_data.0.security_groups_content_audit", "security_service_policy_data.0.security_groups_usage_audit"}, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "revert_manual_security_group_changes": { - Type: schema.TypeBool, - Optional: true, - }, - "security_group_action": { - Type: schema.TypeString, - Required: true, - }, - "security_groups": { - Type: schema.TypeSet, - Required: true, - Elem: schema.TypeString, - }, - "remediation_enabled": { - Type: schema.TypeBool, - Optional: true, - }, - "resource_type": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - } -} - -func securityGroupsUsageAudit() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - MaxItems: 1, - ExactlyOneOf: []string{"security_service_policy_data.0.waf", "security_service_policy_data.0.wafv2", "security_service_policy_data.0.shield_advanced", "security_service_policy_data.0.security_groups_common", "security_service_policy_data.0.security_groups_content_audit", "security_service_policy_data.0.security_groups_usage_audit"}, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "revert_manual_security_group_changes": { - Type: schema.TypeBool, - Optional: true, - }, - "security_group_action": { - Type: schema.TypeString, - Required: true, - }, - "security_groups": { - Type: schema.TypeSet, - Required: true, - Elem: schema.TypeString, - }, - "remediation_enabled": { - Type: schema.TypeBool, - Optional: true, - }, - "resource_type": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - } -} - -func wafSchema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - MaxItems: 1, - ExactlyOneOf: []string{"security_service_policy_data.0.waf", "security_service_policy_data.0.wafv2", "security_service_policy_data.0.shield_advanced", "security_service_policy_data.0.security_groups_common", "security_service_policy_data.0.security_groups_content_audit", "security_service_policy_data.0.security_groups_usage_audit"}, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "rule_groups": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "id": { - Type: schema.TypeString, - Required: true, - }, - "override_action": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "type": { - Type: schema.TypeString, - Required: true, - }, - "default_action": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -func wafV2Schema() *schema.Schema { - return &schema.Schema{ - Type: schema.TypeList, - MaxItems: 1, - ExactlyOneOf: []string{"security_service_policy_data.0.waf", "security_service_policy_data.0.wafv2", "security_service_policy_data.0.shield_advanced", "security_service_policy_data.0.security_groups_common", "security_service_policy_data.0.security_groups_content_audit", "security_service_policy_data.0.security_groups_usage_audit"}, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "preprocess_rule_groups": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "rule_group_arn": { - Type: schema.TypeString, - Required: true, - }, - "override_action": { - Type: schema.TypeString, - Required: true, - }, - }, - }, - }, - "managed_rule_group_identifier": { - Type: schema.TypeList, - MaxItems: 1, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "version": { - Type: schema.TypeString, - Optional: true, - }, - "vendor_name": { - Type: schema.TypeString, - Optional: true, - }, - "managed_rule_group_name": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - "rule_group_type": { - Type: schema.TypeString, - Optional: true, - }, - "exclude_rule_groups": { - Type: schema.TypeSet, - Optional: true, - Elem: schema.TypeString, - }, - "post_process_rule_groups": { - Type: schema.TypeSet, - Optional: true, - Elem: schema.TypeString, - }, - "default_action": { - Type: schema.TypeString, - Optional: true, - }, - "override_customer_web_acl_association": { - Type: schema.TypeBool, - Optional: true, - }, - "logging_configuration": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "log_destination_configs": { - Type: schema.TypeSet, - Required: true, - Elem: schema.TypeString, - }, - "redacted_fields": { - Type: schema.TypeSet, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "redacted_field_type": { - Type: schema.TypeString, - Required: true, - }, - "redacted_field_value": { - Type: schema.TypeString, - Optional: true, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } -} - -// Shared structs -type fmsPolicyBasicType struct { - Type string `json:"type" mapstructure:"type"` -} - -// WAF structs -type fmsPolicyManagedServiceDataWAF struct { - Type string `json:"type" mapstructure:"type"` - RuleGroups []fmsPolicyRuleGroup `json:"ruleGroups" mapstructure:"rule_groups"` -} - -type fmsPolicyRuleGroup struct { - ID string `json:"id" mapstructure:"id"` - OverrrideAction fmsPolicyBasicType `json:"overrideAction" mapstructure:"override_action"` -} - -// WAFv2 structs -type fmsPolicyManagedServiceDataWAFV2 struct { - Type string `json:"type" mapstructure:"type"` - PreProcessRuleGroups []fmsPolicyProcessRuleGroup `json:"preProcessRuleGroups" mapstructure:"preprocess_rule_groups"` - PostProcessRuleGroups []fmsPolicyProcessRuleGroup `json:"postProcessRuleGroups" mapstructure:"postprocess_rule_groups"` - DefaultAction fmsPolicyBasicType `json:"defaultAction" mapstructure:"default_action"` - OverrideCustomerWebACLAssociation bool `json:"overrideCustomerWebACLAssociation" mapstructure:"override_customer_web_acl_association"` - LoggingConfiguration fmsPolicyLoggingConfiguration `json:"loggingConfiguration" mapstructure:"logging_configuration"` -} - -type fmsPolicyLoggingConfiguration struct { - LogDestinationConfigs []string `json:"logDestinationConfigs" mapstructure:"log_destination_configs"` - RedactedFields []fmsPolicyRedactedField `json:"redactedFields" mapstructure:"redacted_fields"` -} - -type fmsPolicyRedactedField struct { - RedactedFieldType string `json:"redactedFieldType" mapstructure:"redacted_field_type"` - RedactedFieldValue string `json:"redactedFieldValue" mapstructure:"redacted_field_value"` -} - -type fmsPolicyRuleGroupIdentifier struct { - Version string `json:"version" mapstructure:"version"` - VendorName string `json:"vendorName" mapstructure:"vendor_name"` - ManagedRuleGroupName string `json:"managedRuleGroupName" mapstructure:"managed_rule_group_name"` -} - -type fmsPolicyProcessRuleGroup struct { - RuleGroupARN string `json:"ruleGroupArn" mapstructure:"rule_group_arn"` - OverrideAction fmsPolicyBasicType `json:"overrideAction" mapstructure:"override_action"` - ManagedRuleGroupIdentifier fmsPolicyRuleGroupIdentifier `json:"managedRuleGroupIdentifier" mapstructure:"managed_rule_group_identifier"` - RuleGroupType string `json:"ruleGroupType" mapstructure:"rule_group_type"` - ExcludeRules []string `json:"excludeRules" mapstructure:"excluded_rules"` -} - -// SECURITY_GROUPS_COMMON structs -type fmsPolicyManagedServiceDataSecurityGroupsCommon struct { - Type string `json:"type" mapstructure:"type"` - RevertManualSecurityGroupChanges bool `json:"revertManualSecurityGroupChanges" mapstructure:"revert_manual_security_group_changes"` - ExclusiveResourceSecurityGroupManagement bool `json:"exclusiveResourceSecurityGroupManagement" mapstructure:"exclusive_resource_security_group_management"` - SecurityGroups []fmsPolicyRuleGroup `json:"ruleGroups" mapstructure:"security_groups"` -} - -// SECURITY_GROUPS_CONTENT_AUDIT structs -type fmsPolicyManagedServiceDataSecurityGroupsContentAudit struct { - Type string `json:"type" mapstructure:"type"` - SecurityGroups []fmsPolicyRuleGroup `json:"ruleGroups" mapstructure:"security_groups"` - SecurityGroupAction fmsPolicyBasicType `json:"securityGroupAction" mapstructure:"security_group_action"` -} - -// SECURITY_GROUPS_USAGE_AUDIT structs -type fmsPolicyManagedServiceDataSecurityGroupsUsageAudit struct { - Type string `json:"type" mapstructure:"type"` - DeleteUnusedSecurityGroups bool `json:"deleteUnusedSecurityGroups" mapstructure:"delete_unused_security_groups"` - CoalesceRedundantSecurityGroups bool `json:"coalesceRedundantSecurityGroups" mapstructure:"coalesce_redundant_security_groups"` -} - func resourceAwsFmsPolicyCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).fmsconn @@ -467,11 +146,10 @@ func resourceAwsFmsPolicyCreate(d *schema.ResourceData, meta interface{}) error ExcludeResourceTags: aws.Bool(d.Get("exclude_resource_tags").(bool)), } - if v, ok := d.GetOk("security_service_policy_data"); ok { - var err error - if fmsPolicy.SecurityServicePolicyData, err = expandSecurityServicePolicyData(v.([]interface{})[0].(map[string]interface{})); err != nil { - return err - } + securityServicePolicy := d.Get("security_service_policy_data").([]interface{})[0].(map[string]interface{}) + fmsPolicy.SecurityServicePolicyData = &fms.SecurityServicePolicyData{ + ManagedServiceData: aws.String(securityServicePolicy["managed_service_data"].(string)), + Type: aws.String(securityServicePolicy["type"].(string)), } if rTags, tagsOk := d.GetOk("resource_tags"); tagsOk { @@ -479,11 +157,11 @@ func resourceAwsFmsPolicyCreate(d *schema.ResourceData, meta interface{}) error } if v, ok := d.GetOk("include_map"); ok { - fmsPolicy.IncludeMap = expandFMSPolicyMap(v.(*schema.Set)) + fmsPolicy.IncludeMap = expandFMSPolicyMap(v.([]interface{})) } if v, ok := d.GetOk("exclude_map"); ok { - fmsPolicy.ExcludeMap = expandFMSPolicyMap(v.(*schema.Set)) + fmsPolicy.ExcludeMap = expandFMSPolicyMap(v.([]interface{})) } params := &fms.PutPolicyInput{ @@ -530,17 +208,23 @@ func resourceAwsFmsPolicyRead(d *schema.ResourceData, meta interface{}) error { if err = d.Set("exclude_map", flattenFMSPolicyMap(resp.Policy.ExcludeMap)); err != nil { return err } - d.Set("include_map", flattenFMSPolicyMap(resp.Policy.IncludeMap)) + if err = d.Set("include_map", flattenFMSPolicyMap(resp.Policy.IncludeMap)); err != nil { + return err + } d.Set("remediation_enabled", aws.BoolValue(resp.Policy.RemediationEnabled)) - d.Set("resource_type_list", resp.Policy.ResourceTypeList) + if err = d.Set("resource_type_list", resp.Policy.ResourceTypeList); err != nil { + return err + } d.Set("policy_update_token", aws.StringValue(resp.Policy.PolicyUpdateToken)) - d.Set("resource_tags", flattenFMSResourceTags(resp.Policy.ResourceTags)) - - securityServicePolicyData, err := fmsPolicyUnmarshalManagedServiceData(resp.Policy.SecurityServicePolicyData) - if err != nil { + if err = d.Set("resource_tags", flattenFMSResourceTags(resp.Policy.ResourceTags)); err != nil { return err } - if err = d.Set("security_service_policy_data", securityServicePolicyData); err != nil { + + securityServicePolicy := []map[string]string{{ + "type": *resp.Policy.SecurityServicePolicyData.Type, + "managed_service_data": *resp.Policy.SecurityServicePolicyData.ManagedServiceData, + }} + if err = d.Set("security_service_policy_data", securityServicePolicy); err != nil { return err } @@ -560,35 +244,23 @@ func resourceAwsFmsPolicyUpdate(d *schema.ResourceData, meta interface{}) error ExcludeResourceTags: aws.Bool(d.Get("exclude_resource_tags").(bool)), } - requestUpdate := false + fmsPolicy.ExcludeMap = expandFMSPolicyMap(d.Get("exclude_map").([]interface{})) - if d.HasChange("exclude_map") { - fmsPolicy.ExcludeMap = expandFMSPolicyMap(d.Get("exclude_map").(*schema.Set)) - requestUpdate = true - } + fmsPolicy.IncludeMap = expandFMSPolicyMap(d.Get("include_map").([]interface{})) - if d.HasChange("include_map") { - fmsPolicy.IncludeMap = expandFMSPolicyMap(d.Get("include_map").(*schema.Set)) - requestUpdate = true - } + fmsPolicy.ResourceTags = constructResourceTags(d.Get("resource_tags")) - if d.HasChange("resource_tags") { - fmsPolicy.ResourceTags = constructResourceTags(d.Get("resource_tags")) - requestUpdate = true + securityServicePolicy := d.Get("security_service_policy_data").([]interface{})[0].(map[string]interface{}) + fmsPolicy.SecurityServicePolicyData = &fms.SecurityServicePolicyData{ + ManagedServiceData: aws.String(securityServicePolicy["managed_service_data"].(string)), + Type: aws.String(securityServicePolicy["type"].(string)), } - if requestUpdate { - var err error - if fmsPolicy.SecurityServicePolicyData, err = expandSecurityServicePolicyData(d.Get("security_service_policy_data").(*schema.Set).List()[0].(map[string]interface{})); err != nil { - return err - } + params := &fms.PutPolicyInput{Policy: fmsPolicy} + _, err := conn.PutPolicy(params) - params := &fms.PutPolicyInput{Policy: fmsPolicy} - _, err = conn.PutPolicy(params) - - if err != nil { - return fmt.Errorf("Error modifying FMS Policy Rule: %s", err) - } + if err != nil { + return fmt.Errorf("Error modifying FMS Policy Rule: %s", err) } return resourceAwsFmsPolicyRead(d, meta) @@ -614,128 +286,23 @@ func resourceAwsFmsPolicyDelete(d *schema.ResourceData, meta interface{}) error return nil } -func terraformMapDecodeHelper() mapstructure.DecodeHookFuncType { - return func(inType reflect.Type, outType reflect.Type, value interface{}) (interface{}, error) { - if inType == reflect.SliceOf(outType) && reflect.ValueOf(value).Len() == 1 { - return reflect.ValueOf(value).Index(0).Interface(), nil - } - return value, nil - } -} - -func fmsPolicyUnmarshalManagedServiceData(policyData *fms.SecurityServicePolicyData) (map[string]interface{}, error) { - var policyStruct interface{} - var securityServicePolicy map[string]interface{} - var policyType string - switch *policyData.Type { - case "WAF": - policyType = "waf" - policyStruct = fmsPolicyManagedServiceDataWAF{} - if err := json.Unmarshal([]byte(*policyData.ManagedServiceData), &policyStruct); err != nil { - return nil, err - } - case "WAFV2": - policyType = "wafv2" - policyStruct = fmsPolicyManagedServiceDataWAFV2{} - if err := json.Unmarshal([]byte(*policyData.ManagedServiceData), &policyStruct); err != nil { - return nil, err - } - case "SECURITY_GROUPS_COMMON": - policyType = "security_groups_common" - policyStruct = fmsPolicyManagedServiceDataSecurityGroupsCommon{} - if err := json.Unmarshal([]byte(*policyData.ManagedServiceData), &policyStruct); err != nil { - return nil, err - } - case "SECURITY_CONTENT_AUDIT": - policyType = "security_groups_content_audit" - policyStruct = fmsPolicyManagedServiceDataSecurityGroupsContentAudit{} - if err := json.Unmarshal([]byte(*policyData.ManagedServiceData), &policyStruct); err != nil { - return nil, err - } - case "SECURITY_GROUPS_USAGE_AUDIT": - policyType = "security_groups_usage_audit" - policyStruct = fmsPolicyManagedServiceDataSecurityGroupsUsageAudit{} - if err := json.Unmarshal([]byte(*policyData.ManagedServiceData), &policyStruct); err != nil { - return nil, err +func expandFMSPolicyMap(set []interface{}) map[string][]*string { + fmsPolicyMap := map[string][]*string{} + if len(set) > 0 { + if _, ok := set[0].(map[string]interface{}); !ok { + return fmsPolicyMap } - case "SHIELD_ADVANCED": - policyType = "security_groups_usage_audit" - policyStruct = true - } - var policyMap map[string]interface{} - err := mapstructure.Decode(policyStruct, policyMap) - securityServicePolicy[policyType] = policyMap - return securityServicePolicy, err -} - -func fmsPolicyMarshalManagedServiceData(policyMap interface{}, policyStruct interface{}) (*string, error) { - var managedServiceData []byte - var err error - decoderConfig := mapstructure.DecoderConfig{ - WeaklyTypedInput: true, - Result: &policyStruct, - DecodeHook: terraformMapDecodeHelper(), - } - weakDecoder, err := mapstructure.NewDecoder(&decoderConfig) - if err != nil { - return nil, err - } - if err = weakDecoder.Decode(policyMap); err != nil { - return nil, err - } - if managedServiceData, err = json.Marshal(policyStruct); err != nil { - return nil, err - } - return aws.String(string(managedServiceData)), nil -} - -func expandSecurityServicePolicyData(policyData map[string]interface{}) (*fms.SecurityServicePolicyData, error) { - var managedServiceData *string - var err error - var SecurityPolicyType string - switch { - case len(policyData["waf"].([]interface{})) == 1: - policyStruct := fmsPolicyManagedServiceDataWAF{} - SecurityPolicyType = "WAF" - managedServiceData, err = fmsPolicyMarshalManagedServiceData(policyData["waf"].([]interface{})[0], policyStruct) - - case len(policyData["wafv2"].([]interface{})) == 1: - policyStruct := fmsPolicyManagedServiceDataWAFV2{} - SecurityPolicyType = "WAFV2" - managedServiceData, err = fmsPolicyMarshalManagedServiceData(policyData["wafv2"].([]interface{})[0], policyStruct) - - case policyData["shield_advanced"].(bool): - SecurityPolicyType = "SHIELD_ADVANCED" - managedServiceData = aws.String("{}") - - case len(policyData["security_groups_common"].([]interface{})) == 1: - policyStruct := fmsPolicyManagedServiceDataSecurityGroupsCommon{} - SecurityPolicyType = "SECURITY_GROUPS_COMMON" - managedServiceData, err = fmsPolicyMarshalManagedServiceData(policyData["security_groups_common"].([]interface{})[0], policyStruct) - - case len(policyData["security_groups_content_audit"].([]interface{})) == 1: - policyStruct := fmsPolicyManagedServiceDataSecurityGroupsContentAudit{} - SecurityPolicyType = "SECURITY_GROUPS_CONTENT_AUDIT" - managedServiceData, err = fmsPolicyMarshalManagedServiceData(policyData["security_groups_content_audit"].([]interface{})[0], policyStruct) - - case len(policyData["security_groups_usage_audit"].([]interface{})) == 1: - policyStruct := fmsPolicyManagedServiceDataSecurityGroupsUsageAudit{} - SecurityPolicyType = "SECURITY_GROUPS_USAGE_AUDIT" - managedServiceData, err = fmsPolicyMarshalManagedServiceData(policyData["security_groups_usage_audit"].([]interface{})[0], policyStruct) - } - - return &fms.SecurityServicePolicyData{ - Type: aws.String(SecurityPolicyType), - ManagedServiceData: managedServiceData, - }, err -} + for key, listValue := range set[0].(map[string]interface{}) { + var flatKey string + switch key { + case "account": + flatKey = "ACCOUNT" + case "orgunit": + flatKey = "ORG_UNIT" + } -func expandFMSPolicyMap(set *schema.Set) map[string][]*string { - fmsPolicyMap := map[string][]*string{} - if set.Len() > 0 { - for key, listValue := range set.List()[0].(map[string]interface{}) { - for _, value := range listValue.([]interface{}) { - fmsPolicyMap[key] = append(fmsPolicyMap[key], aws.String(value.(string))) + for _, value := range listValue.(*schema.Set).List() { + fmsPolicyMap[flatKey] = append(fmsPolicyMap[flatKey], aws.String(value.(string))) } } } @@ -747,9 +314,9 @@ func flattenFMSPolicyMap(fmsPolicyMap map[string][]*string) []interface{} { for key, value := range fmsPolicyMap { switch key { - case "account": + case "ACCOUNT": flatPolicyMap["account"] = value - case "orgunit": + case "ORG_UNIT": flatPolicyMap["orgunit"] = value default: log.Printf("[WARNING] Unexpected key (%q) found in FMS policy", key) diff --git a/aws/resource_aws_fms_policy_test.go b/aws/resource_aws_fms_policy_test.go index 9194a4398ad6..db625adcf00a 100644 --- a/aws/resource_aws_fms_policy_test.go +++ b/aws/resource_aws_fms_policy_test.go @@ -31,9 +31,64 @@ func TestAccAWSFmsPolicy_basic(t *testing.T) { ), }, { + ResourceName: "aws_fms_policy.test", ImportState: true, ImportStateVerify: true, - ImportStateVerifyIgnore: []string{"policy_update_token"}, + ImportStateVerifyIgnore: []string{"policy_update_token", "delete_all_policy_resources"}, + }, + }, + }) +} + +func TestAccAWSFmsPolicy_includeMap(t *testing.T) { + fmsPolicyName := fmt.Sprintf("tf-fms-%s", acctest.RandString(5)) + wafRuleGroupName := fmt.Sprintf("tf-waf-rg-%s", acctest.RandString(5)) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsFmsPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccFmsPolicyConfig_include(fmsPolicyName, wafRuleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsFmsPolicyExists("aws_fms_policy.test"), + testAccMatchResourceAttrRegionalARN("aws_fms_policy.test", "arn", "fms", regexp.MustCompile(`policy/`)), + resource.TestCheckResourceAttr("aws_fms_policy.test", "name", fmsPolicyName), + resource.TestCheckResourceAttr("aws_fms_policy.test", "security_service_policy_data.#", "1"), + ), + }, + { + ResourceName: "aws_fms_policy.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"policy_update_token", "delete_all_policy_resources"}, + }, + }, + }) +} + +func TestAccAWSFmsPolicy_update(t *testing.T) { + fmsPolicyName := fmt.Sprintf("tf-fms-%s", acctest.RandString(5)) + fmsPolicyName2 := fmt.Sprintf("tf-fms-%s2", acctest.RandString(5)) + wafRuleGroupName := fmt.Sprintf("tf-waf-rg-%s", acctest.RandString(5)) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsFmsPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccFmsPolicyConfig(fmsPolicyName, wafRuleGroupName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsFmsPolicyExists("aws_fms_policy.test"), + testAccMatchResourceAttrRegionalARN("aws_fms_policy.test", "arn", "fms", regexp.MustCompile(`policy/`)), + resource.TestCheckResourceAttr("aws_fms_policy.test", "name", fmsPolicyName), + resource.TestCheckResourceAttr("aws_fms_policy.test", "security_service_policy_data.#", "1"), + ), + }, + { + Config: testAccFmsPolicyConfig_updated(fmsPolicyName2, wafRuleGroupName), }, }, }) @@ -114,38 +169,90 @@ func testAccCheckAwsFmsPolicyExists(name string) resource.TestCheckFunc { func testAccFmsPolicyConfig(name string, group string) string { return fmt.Sprintf(` -#resource "aws_fms_policy" "test" { -# exclude_resource_tags = false -# name = %[1]q -# remediation_enabled = false -# resource_type_list = ["AWS::ElasticLoadBalancingV2::LoadBalancer"] -# -# security_service_policy_data { -# waf { -# rule_groups {id = aws_waf_rule.wafrule.id} -# } -# } -#} - -resource "aws_waf_ipset" "ipset" { - name = "tfIPSet" - - ip_set_descriptors { - type = "IPV4" - value = "192.0.7.0/24" - } +resource "aws_fms_policy" "test" { + exclude_resource_tags = false + name = %[1]q + remediation_enabled = false + resource_type_list = ["AWS::ElasticLoadBalancingV2::LoadBalancer"] + + exclude_map { + account = [ data.aws_organizations_organization.example.accounts[0].id ] + } + + security_service_policy_data { + type = "WAF" + managed_service_data = "{\"type\": \"WAF\", \"ruleGroups\": [{\"id\":\"${aws_wafregional_rule_group.test.id}\", \"overrideAction\" : {\"type\": \"COUNT\"}}],\"defaultAction\": {\"type\": \"BLOCK\"}, \"overrideCustomerWebACLAssociation\": false}" + } } -resource "aws_waf_rule" "wafrule" { - depends_on = [aws_waf_ipset.ipset] +data "aws_organizations_organization" "example" {} + +resource "aws_wafregional_rule_group" "test" { + metric_name = "MyTest" name = %[2]q - metric_name = "tfWAFRule" +} +`, name, group) +} - predicates { - data_id = aws_waf_ipset.ipset.id - negated = false - type = "IPMatch" - } +func testAccFmsPolicyConfig_updated(name string, group string) string { + return fmt.Sprintf(` +resource "aws_fms_policy" "test" { + exclude_resource_tags = false + name = %[1]q + remediation_enabled = true + resource_type_list = ["AWS::ElasticLoadBalancingV2::LoadBalancer"] + + exclude_map { + account = [ data.aws_organizations_organization.example.accounts[0].id ] + } + + security_service_policy_data { + type = "WAF" + managed_service_data = "{\"type\": \"WAF\", \"ruleGroups\": [{\"id\":\"${aws_wafregional_rule_group.test.id}\", \"overrideAction\" : {\"type\": \"COUNT\"}}],\"defaultAction\": {\"type\": \"ALLOW\"}, \"overrideCustomerWebACLAssociation\": false}" + } + + lifecycle { + create_before_destroy = false + } +} + +data "aws_organizations_organization" "example" {} + +resource "aws_wafregional_rule_group" "test" { + metric_name = "MyTest" + name = %[2]q +} + +resource "aws_wafregional_rule_group" "test2" { + metric_name = "MyTest2" + name = %[2]q +} +`, name, group) +} + +func testAccFmsPolicyConfig_include(name string, group string) string { + return fmt.Sprintf(` +resource "aws_fms_policy" "test" { + exclude_resource_tags = false + name = %[1]q + remediation_enabled = false + resource_type_list = ["AWS::ElasticLoadBalancingV2::LoadBalancer"] + + include_map { + account = [ data.aws_organizations_organization.example.accounts[0].id ] + } + + security_service_policy_data { + type = "WAF" + managed_service_data = "{\"type\": \"WAF\", \"ruleGroups\": [{\"id\":\"${aws_wafregional_rule_group.test.id}\", \"overrideAction\" : {\"type\": \"COUNT\"}}],\"defaultAction\": {\"type\": \"BLOCK\"}, \"overrideCustomerWebACLAssociation\": false}" + } +} + +data "aws_organizations_organization" "example" {} + +resource "aws_wafregional_rule_group" "test" { + metric_name = "MyTest" + name = %[2]q } `, name, group) } @@ -158,26 +265,14 @@ resource "aws_fms_policy" "test" { remediation_enabled = false resource_type_list = ["AWS::ElasticLoadBalancingV2::LoadBalancer"] - security_service_policy_data = < Additional information about this configuration can be found in the [AWS Firewall Manager SecurityServicePolicyData API Reference](https://docs.aws.amazon.com/fms/2018-01-01/APIReference/API_SecurityServicePolicyData.html)