From 92928c0cda65f0d83fa9f3a0ebfe2f89ca0a94bd Mon Sep 17 00:00:00 2001 From: Stuart Williams Date: Mon, 29 Nov 2021 18:14:39 -0500 Subject: [PATCH 1/4] r/networkfirewall_firewall_(policy|rule_group) stateful rule ordering --- .changelog/21955.txt | 7 + .../networkfirewall/firewall_policy.go | 81 +++- .../networkfirewall/firewall_policy_test.go | 359 ++++++++++++++++-- internal/service/networkfirewall/helpers.go | 13 + .../service/networkfirewall/rule_group.go | 51 ++- .../networkfirewall/rule_group_test.go | 202 ++++++++-- ...workfirewall_firewall_policy.html.markdown | 15 +- .../networkfirewall_rule_group.html.markdown | 10 + 8 files changed, 676 insertions(+), 62 deletions(-) create mode 100644 .changelog/21955.txt diff --git a/.changelog/21955.txt b/.changelog/21955.txt new file mode 100644 index 00000000000..eaefcad2d72 --- /dev/null +++ b/.changelog/21955.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_networkfirewall_firewall_rule_group: Add `stateful_rule_options` configuration block +``` + +```release-note:enhancement +resource/aws_networkfirewall_firewall_policy: Add `stateful_default_actions` and `stateful_engine_options` configuration blocks. Add `priority` attribute to `stateful_rule_group_reference` block +``` diff --git a/internal/service/networkfirewall/firewall_policy.go b/internal/service/networkfirewall/firewall_policy.go index 49a116ecf58..c789fcb3189 100644 --- a/internal/service/networkfirewall/firewall_policy.go +++ b/internal/service/networkfirewall/firewall_policy.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/service/networkfirewall" "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -45,11 +46,35 @@ func ResourceFirewallPolicy() *schema.Resource { MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "stateful_default_actions": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "stateful_engine_options": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rule_order": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(networkfirewall.RuleOrder_Values(), false), + }, + }, + }, + }, "stateful_rule_group_reference": { Type: schema.TypeSet, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "priority": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validation.IntAtLeast(1), + }, "resource_arn": { Type: schema.TypeString, Required: true, @@ -103,7 +128,14 @@ func ResourceFirewallPolicy() *schema.Resource { }, }, - CustomizeDiff: verify.SetTagsDiff, + CustomizeDiff: customdiff.Sequence( + // The stateful rule_order default action can be explicitly or implicitly set, + // so ignore spurious diffs if toggling between the two. + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + return forceNewIfNotRuleOrderDefault("firewall_policy.0.stateful_engine_options.0.rule_order", d) + }, + verify.SetTagsDiff, + ), } } @@ -263,6 +295,21 @@ func resourceFirewallPolicyDelete(ctx context.Context, d *schema.ResourceData, m return nil } +func expandNetworkFirewallStatefulEngineOptions(l []interface{}) *networkfirewall.StatefulEngineOptions { + if len(l) == 0 || l[0] == nil { + return nil + } + + options := &networkfirewall.StatefulEngineOptions{} + + m := l[0].(map[string]interface{}) + if v, ok := m["rule_order"].(string); ok { + options.RuleOrder = aws.String(v) + } + + return options +} + func expandNetworkFirewallStatefulRuleGroupReferences(l []interface{}) []*networkfirewall.StatefulRuleGroupReference { if len(l) == 0 || l[0] == nil { return nil @@ -274,6 +321,9 @@ func expandNetworkFirewallStatefulRuleGroupReferences(l []interface{}) []*networ continue } reference := &networkfirewall.StatefulRuleGroupReference{} + if v, ok := tfMap["priority"].(int); ok && v > 0 { + reference.Priority = aws.Int64(int64(v)) + } if v, ok := tfMap["resource_arn"].(string); ok && v != "" { reference.ResourceArn = aws.String(v) } @@ -314,6 +364,14 @@ func expandNetworkFirewallFirewallPolicy(l []interface{}) *networkfirewall.Firew StatelessFragmentDefaultActions: flex.ExpandStringSet(lRaw["stateless_fragment_default_actions"].(*schema.Set)), } + if v, ok := lRaw["stateful_default_actions"].(*schema.Set); ok && v.Len() > 0 { + policy.StatefulDefaultActions = flex.ExpandStringSet(v) + } + + if v, ok := lRaw["stateful_engine_options"].([]interface{}); ok && len(v) > 0 { + policy.StatefulEngineOptions = expandNetworkFirewallStatefulEngineOptions(v) + } + if v, ok := lRaw["stateful_rule_group_reference"].(*schema.Set); ok && v.Len() > 0 { policy.StatefulRuleGroupReferences = expandNetworkFirewallStatefulRuleGroupReferences(v.List()) } @@ -334,6 +392,12 @@ func flattenNetworkFirewallFirewallPolicy(policy *networkfirewall.FirewallPolicy return []interface{}{} } p := map[string]interface{}{} + if policy.StatefulDefaultActions != nil { + p["stateful_default_actions"] = flex.FlattenStringSet(policy.StatefulDefaultActions) + } + if policy.StatefulEngineOptions != nil { + p["stateful_engine_options"] = flattenNetworkFirewallStatefulEngineOptions(policy.StatefulEngineOptions) + } if policy.StatefulRuleGroupReferences != nil { p["stateful_rule_group_reference"] = flattenNetworkFirewallPolicyStatefulRuleGroupReference(policy.StatefulRuleGroupReferences) } @@ -353,12 +417,27 @@ func flattenNetworkFirewallFirewallPolicy(policy *networkfirewall.FirewallPolicy return []interface{}{p} } +func flattenNetworkFirewallStatefulEngineOptions(options *networkfirewall.StatefulEngineOptions) []interface{} { + if options == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "rule_order": aws.StringValue(options.RuleOrder), + } + + return []interface{}{m} +} + func flattenNetworkFirewallPolicyStatefulRuleGroupReference(l []*networkfirewall.StatefulRuleGroupReference) []interface{} { references := make([]interface{}, 0, len(l)) for _, ref := range l { reference := map[string]interface{}{ "resource_arn": aws.StringValue(ref.ResourceArn), } + if ref.Priority != nil { + reference["priority"] = int(aws.Int64Value(ref.Priority)) + } references = append(references, reference) } diff --git a/internal/service/networkfirewall/firewall_policy_test.go b/internal/service/networkfirewall/firewall_policy_test.go index 1b65bdedd02..b6935503c60 100644 --- a/internal/service/networkfirewall/firewall_policy_test.go +++ b/internal/service/networkfirewall/firewall_policy_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/networkfirewall" "github.com/hashicorp/aws-sdk-go-base/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" @@ -16,6 +17,7 @@ import ( ) func TestAccNetworkFirewallFirewallPolicy_basic(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" @@ -28,7 +30,7 @@ func TestAccNetworkFirewallFirewallPolicy_basic(t *testing.T) { { Config: testAccFirewallPolicy_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "network-firewall", fmt.Sprintf("firewall-policy/%s", rName)), resource.TestCheckResourceAttr(resourceName, "description", ""), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), @@ -49,7 +51,113 @@ func TestAccNetworkFirewallFirewallPolicy_basic(t *testing.T) { }) } +func TestAccNetworkFirewallFirewallPolicy_statefulDefaultActions(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_networkfirewall_firewall_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkfirewall.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckFirewallPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccFirewallPolicy_statefulDefaultActions(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_default_actions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_default_actions.0", "aws:drop_established"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNetworkFirewallFirewallPolicy_statefulEngineOption(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_networkfirewall_firewall_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkfirewall.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckFirewallPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccFirewallPolicy_statefulEngineOptions(rName, "STRICT_ORDER"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.rule_order", networkfirewall.RuleOrderStrictOrder), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNetworkFirewallFirewallPolicy_updateStatefulEngineOption(t *testing.T) { + var firewallPolicy1, firewallPolicy2, firewallPolicy3 networkfirewall.DescribeFirewallPolicyOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_networkfirewall_firewall_policy.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkfirewall.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckFirewallPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccFirewallPolicy_statefulEngineOptions(rName, "DEFAULT_ACTION_ORDER"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy1), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.rule_order", networkfirewall.RuleOrderDefaultActionOrder), + ), + }, + { + Config: testAccFirewallPolicy_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy2), + testAccCheckFirewallPolicyNotRecreated(&firewallPolicy1, &firewallPolicy2), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + ), + }, + { + Config: testAccFirewallPolicy_statefulEngineOptions(rName, "STRICT_ORDER"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy3), + testAccCheckFirewallPolicyRecreated(&firewallPolicy2, &firewallPolicy3), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.0.rule_order", networkfirewall.RuleOrderStrictOrder), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccNetworkFirewallFirewallPolicy_statefulRuleGroupReference(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" ruleGroupResourceName := "aws_networkfirewall_rule_group.test.0" @@ -63,7 +171,7 @@ func TestAccNetworkFirewallFirewallPolicy_statefulRuleGroupReference(t *testing. { Config: testAccFirewallPolicy_statefulRuleGroupReference(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateful_rule_group_reference.*.resource_arn", ruleGroupResourceName, "arn"), @@ -79,6 +187,7 @@ func TestAccNetworkFirewallFirewallPolicy_statefulRuleGroupReference(t *testing. } func TestAccNetworkFirewallFirewallPolicy_updateStatefulRuleGroupReference(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" ruleGroupResourceName := "aws_networkfirewall_rule_group.test.0" @@ -92,13 +201,13 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatefulRuleGroupReference(t *te { Config: testAccFirewallPolicy_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), ), }, { Config: testAccFirewallPolicy_statefulRuleGroupReference(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateful_rule_group_reference.*.resource_arn", ruleGroupResourceName, "arn"), @@ -107,7 +216,7 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatefulRuleGroupReference(t *te { Config: testAccFirewallPolicy_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), ), }, { @@ -120,6 +229,7 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatefulRuleGroupReference(t *te } func TestAccNetworkFirewallFirewallPolicy_multipleStatefulRuleGroupReferences(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" ruleGroupResourceName1 := "aws_networkfirewall_rule_group.test.0" @@ -134,7 +244,7 @@ func TestAccNetworkFirewallFirewallPolicy_multipleStatefulRuleGroupReferences(t { Config: testAccFirewallPolicy_multipleStatefulRuleGroupReferences(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.#", "2"), resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateful_rule_group_reference.*.resource_arn", ruleGroupResourceName1, "arn"), @@ -144,7 +254,7 @@ func TestAccNetworkFirewallFirewallPolicy_multipleStatefulRuleGroupReferences(t { Config: testAccFirewallPolicy_singleStatefulRuleGroupReference(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateful_rule_group_reference.*.resource_arn", ruleGroupResourceName1, "arn"), @@ -159,7 +269,81 @@ func TestAccNetworkFirewallFirewallPolicy_multipleStatefulRuleGroupReferences(t }) } +func TestAccNetworkFirewallFirewallPolicy_statefulRuleGroupPriorityReference(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_networkfirewall_firewall_policy.test" + ruleGroupResourceName := "aws_networkfirewall_rule_group.test.0" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkfirewall.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckFirewallPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccFirewallPolicy_statefulRuleGroupPriorityReference(rName, "1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.0.priority", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateful_rule_group_reference.*.resource_arn", ruleGroupResourceName, "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNetworkFirewallFirewallPolicy_updateStatefulRuleGroupPriorityReference(t *testing.T) { + var firewallPolicy1, firewallPolicy2 networkfirewall.DescribeFirewallPolicyOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_networkfirewall_firewall_policy.test" + ruleGroupResourceName := "aws_networkfirewall_rule_group.test.0" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkfirewall.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckFirewallPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccFirewallPolicy_statefulRuleGroupPriorityReference(rName, "1"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy1), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.0.priority", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateful_rule_group_reference.*.resource_arn", ruleGroupResourceName, "arn"), + ), + }, + { + Config: testAccFirewallPolicy_statefulRuleGroupPriorityReference(rName, "2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy2), + testAccCheckFirewallPolicyNotRecreated(&firewallPolicy1, &firewallPolicy2), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.0.priority", "2"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateful_rule_group_reference.*.resource_arn", ruleGroupResourceName, "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccNetworkFirewallFirewallPolicy_statelessRuleGroupReference(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" ruleGroupResourceName := "aws_networkfirewall_rule_group.test.0" @@ -173,7 +357,7 @@ func TestAccNetworkFirewallFirewallPolicy_statelessRuleGroupReference(t *testing { Config: testAccFirewallPolicy_statelessRuleGroupReference(rName, 20), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_rule_group_reference.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateless_rule_group_reference.*.resource_arn", ruleGroupResourceName, "arn"), @@ -185,7 +369,7 @@ func TestAccNetworkFirewallFirewallPolicy_statelessRuleGroupReference(t *testing { Config: testAccFirewallPolicy_statelessRuleGroupReference(rName, 1), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_rule_group_reference.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "firewall_policy.0.stateless_rule_group_reference.*", map[string]string{ @@ -203,6 +387,7 @@ func TestAccNetworkFirewallFirewallPolicy_statelessRuleGroupReference(t *testing } func TestAccNetworkFirewallFirewallPolicy_updateStatelessRuleGroupReference(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" ruleGroupResourceName := "aws_networkfirewall_rule_group.test.0" @@ -216,13 +401,13 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatelessRuleGroupReference(t *t { Config: testAccFirewallPolicy_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), ), }, { Config: testAccFirewallPolicy_statelessRuleGroupReference(rName, 20), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateless_rule_group_reference.*.resource_arn", ruleGroupResourceName, "arn"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "firewall_policy.0.stateless_rule_group_reference.*", map[string]string{ @@ -233,7 +418,7 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatelessRuleGroupReference(t *t { Config: testAccFirewallPolicy_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_rule_group_reference.#", "0"), ), @@ -248,6 +433,7 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatelessRuleGroupReference(t *t } func TestAccNetworkFirewallFirewallPolicy_multipleStatelessRuleGroupReferences(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" ruleGroupResourceName1 := "aws_networkfirewall_rule_group.test.0" @@ -262,7 +448,7 @@ func TestAccNetworkFirewallFirewallPolicy_multipleStatelessRuleGroupReferences(t { Config: testAccFirewallPolicy_multipleStatelessRuleGroupReferences(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_rule_group_reference.#", "2"), resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateless_rule_group_reference.*.resource_arn", ruleGroupResourceName1, "arn"), @@ -278,7 +464,7 @@ func TestAccNetworkFirewallFirewallPolicy_multipleStatelessRuleGroupReferences(t { Config: testAccFirewallPolicy_singleStatelessRuleGroupReference(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_rule_group_reference.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "firewall_policy.0.stateless_rule_group_reference.*", map[string]string{ @@ -297,6 +483,7 @@ func TestAccNetworkFirewallFirewallPolicy_multipleStatelessRuleGroupReferences(t } func TestAccNetworkFirewallFirewallPolicy_statelessCustomAction(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" @@ -309,7 +496,7 @@ func TestAccNetworkFirewallFirewallPolicy_statelessCustomAction(t *testing.T) { { Config: testAccFirewallPolicy_statelessCustomAction(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_custom_action.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "firewall_policy.0.stateless_custom_action.*", map[string]string{ @@ -330,6 +517,7 @@ func TestAccNetworkFirewallFirewallPolicy_statelessCustomAction(t *testing.T) { } func TestAccNetworkFirewallFirewallPolicy_updateStatelessCustomAction(t *testing.T) { + var firewallPolicy1, firewallPolicy2, firewallPolicy3, firewallPolicy4 networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" @@ -342,13 +530,14 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatelessCustomAction(t *testing { Config: testAccFirewallPolicy_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy1), ), }, { Config: testAccFirewallPolicy_statelessCustomAction(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy2), + testAccCheckFirewallPolicyRecreated(&firewallPolicy1, &firewallPolicy2), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_custom_action.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "firewall_policy.0.stateless_custom_action.*", map[string]string{ @@ -362,7 +551,8 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatelessCustomAction(t *testing { Config: testAccFirewallPolicy_updateStatelessCustomAction(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy3), + testAccCheckFirewallPolicyRecreated(&firewallPolicy2, &firewallPolicy3), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_custom_action.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "firewall_policy.0.stateless_custom_action.*", map[string]string{ @@ -376,7 +566,8 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatelessCustomAction(t *testing { Config: testAccFirewallPolicy_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy4), + testAccCheckFirewallPolicyRecreated(&firewallPolicy3, &firewallPolicy4), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_custom_action.#", "0"), ), }, @@ -390,6 +581,7 @@ func TestAccNetworkFirewallFirewallPolicy_updateStatelessCustomAction(t *testing } func TestAccNetworkFirewallFirewallPolicy_multipleStatelessCustomActions(t *testing.T) { + var firewallPolicy1, firewallPolicy2 networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" @@ -402,7 +594,7 @@ func TestAccNetworkFirewallFirewallPolicy_multipleStatelessCustomActions(t *test { Config: testAccFirewallPolicy_multipleStatelessCustomActions(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy1), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_custom_action.#", "2"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "firewall_policy.0.stateless_custom_action.*", map[string]string{ @@ -422,7 +614,8 @@ func TestAccNetworkFirewallFirewallPolicy_multipleStatelessCustomActions(t *test { Config: testAccFirewallPolicy_statelessCustomAction(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy2), + testAccCheckFirewallPolicyRecreated(&firewallPolicy1, &firewallPolicy2), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateless_custom_action.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "firewall_policy.0.stateless_custom_action.*", map[string]string{ @@ -443,6 +636,7 @@ func TestAccNetworkFirewallFirewallPolicy_multipleStatelessCustomActions(t *test } func TestAccNetworkFirewallFirewallPolicy_statefulRuleGroupReferenceAndCustomAction(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" ruleGroupResourceName := "aws_networkfirewall_rule_group.test.0" @@ -456,7 +650,7 @@ func TestAccNetworkFirewallFirewallPolicy_statefulRuleGroupReferenceAndCustomAct { Config: testAccFirewallPolicy_statefulRuleGroupReferenceAndStatelessCustomAction(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateful_rule_group_reference.*.resource_arn", ruleGroupResourceName, "arn"), @@ -471,7 +665,7 @@ func TestAccNetworkFirewallFirewallPolicy_statefulRuleGroupReferenceAndCustomAct { Config: testAccFirewallPolicy_statefulRuleGroupReference(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateful_rule_group_reference.*.resource_arn", ruleGroupResourceName, "arn"), @@ -487,6 +681,7 @@ func TestAccNetworkFirewallFirewallPolicy_statefulRuleGroupReferenceAndCustomAct } func TestAccNetworkFirewallFirewallPolicy_tags(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" @@ -499,7 +694,7 @@ func TestAccNetworkFirewallFirewallPolicy_tags(t *testing.T) { { Config: testAccFirewallPolicy_oneTag(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), ), @@ -507,7 +702,7 @@ func TestAccNetworkFirewallFirewallPolicy_tags(t *testing.T) { { Config: testAccFirewallPolicy_twoTags(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), resource.TestCheckResourceAttr(resourceName, "tags.Description", "updated"), @@ -516,7 +711,7 @@ func TestAccNetworkFirewallFirewallPolicy_tags(t *testing.T) { { Config: testAccFirewallPolicy_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, @@ -530,6 +725,7 @@ func TestAccNetworkFirewallFirewallPolicy_tags(t *testing.T) { } func TestAccNetworkFirewallFirewallPolicy_disappears(t *testing.T) { + var firewallPolicy networkfirewall.DescribeFirewallPolicyOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_firewall_policy.test" @@ -542,7 +738,7 @@ func TestAccNetworkFirewallFirewallPolicy_disappears(t *testing.T) { { Config: testAccFirewallPolicy_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckFirewallPolicyExists(resourceName), + testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), acctest.CheckResourceDisappears(acctest.Provider, tfnetworkfirewall.ResourceFirewallPolicy(), resourceName), ), ExpectNonEmptyPlan: true, @@ -573,7 +769,7 @@ func testAccCheckFirewallPolicyDestroy(s *terraform.State) error { return nil } -func testAccCheckFirewallPolicyExists(n string) resource.TestCheckFunc { +func testAccCheckFirewallPolicyExists(n string, p *networkfirewall.DescribeFirewallPolicyOutput) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -594,6 +790,26 @@ func testAccCheckFirewallPolicyExists(n string) resource.TestCheckFunc { return fmt.Errorf("NetworkFirewall Firewall Policy (%s) not found", rs.Primary.ID) } + *p = *output + + return nil + } +} + +func testAccCheckFirewallPolicyNotRecreated(i, j *networkfirewall.DescribeFirewallPolicyOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.StringValue(i.FirewallPolicyResponse.FirewallPolicyId), aws.StringValue(j.FirewallPolicyResponse.FirewallPolicyId); before != after { + return fmt.Errorf("NetworkFirewall Firewall Policy was recreated. got: %s, expected: %s", after, before) + } + return nil + } +} + +func testAccCheckFirewallPolicyRecreated(i, j *networkfirewall.DescribeFirewallPolicyOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.StringValue(i.FirewallPolicyResponse.FirewallPolicyId), aws.StringValue(j.FirewallPolicyResponse.FirewallPolicyId); before == after { + return fmt.Errorf("NetworkFirewall Firewall Policy (%s) was not recreated", before) + } return nil } } @@ -655,6 +871,41 @@ resource "aws_networkfirewall_rule_group" "test" { `, count, rName) } +func testAccFirewallPolicyStatefulRuleGroupStrictDependencies(rName string, count int) string { + return fmt.Sprintf(` +resource "aws_networkfirewall_rule_group" "test" { + count = %d + capacity = 100 + name = "%s-${count.index}" + type = "STATEFUL" + rule_group { + rules_source { + stateful_rule { + action = "PASS" + header { + destination = "124.1.1.24/32" + destination_port = 53 + direction = "ANY" + protocol = "TCP" + source = "1.2.3.4/32" + source_port = 53 + } + rule_option { + keyword = "sid:1" + } + } + } + stateful_rule_options { + rule_order = "STRICT_ORDER" + } + } + lifecycle { + create_before_destroy = true + } +} +`, count, rName) +} + func testAccFirewallPolicy_basic(rName string) string { return fmt.Sprintf(` resource "aws_networkfirewall_firewall_policy" "test" { @@ -698,6 +949,37 @@ resource "aws_networkfirewall_firewall_policy" "test" { `, rName) } +func testAccFirewallPolicy_statefulEngineOptions(rName, rule_order string) string { + return fmt.Sprintf(` +resource "aws_networkfirewall_firewall_policy" "test" { + name = %[1]q + firewall_policy { + stateless_fragment_default_actions = ["aws:drop"] + stateless_default_actions = ["aws:pass"] + stateful_engine_options { + rule_order = %[2]q + } + } +} +`, rName, rule_order) +} + +func testAccFirewallPolicy_statefulDefaultActions(rName string) string { + return fmt.Sprintf(` +resource "aws_networkfirewall_firewall_policy" "test" { + name = %[1]q + firewall_policy { + stateless_fragment_default_actions = ["aws:drop"] + stateless_default_actions = ["aws:pass"] + stateful_default_actions = ["aws:drop_established"] + stateful_engine_options { + rule_order = "STRICT_ORDER" + } + } +} +`, rName) +} + func testAccFirewallPolicy_statefulRuleGroupReference(rName string) string { return acctest.ConfigCompose( testAccFirewallPolicyStatefulRuleGroupDependencies(rName, 1), @@ -735,6 +1017,27 @@ resource "aws_networkfirewall_firewall_policy" "test" { `, rName)) } +func testAccFirewallPolicy_statefulRuleGroupPriorityReference(rName, priority string) string { + return acctest.ConfigCompose( + testAccFirewallPolicyStatefulRuleGroupStrictDependencies(rName, 1), + fmt.Sprintf(` +resource "aws_networkfirewall_firewall_policy" "test" { + name = %[1]q + firewall_policy { + stateless_fragment_default_actions = ["aws:drop"] + stateless_default_actions = ["aws:pass"] + stateful_engine_options { + rule_order = "STRICT_ORDER" + } + stateful_rule_group_reference { + priority = %[2]q + resource_arn = aws_networkfirewall_rule_group.test[0].arn + } + } +} +`, rName, priority)) +} + func testAccFirewallPolicy_singleStatefulRuleGroupReference(rName string) string { return acctest.ConfigCompose( testAccFirewallPolicyStatefulRuleGroupDependencies(rName, 2), diff --git a/internal/service/networkfirewall/helpers.go b/internal/service/networkfirewall/helpers.go index 0283fc3761a..2c09b538e15 100644 --- a/internal/service/networkfirewall/helpers.go +++ b/internal/service/networkfirewall/helpers.go @@ -175,3 +175,16 @@ func flattenNetworkFirewallDimensions(d []*networkfirewall.Dimension) []interfac return dimensions } + +func forceNewIfNotRuleOrderDefault(key string, d *schema.ResourceDiff) error { + if d.Id() != "" && d.HasChange(key) { + old, new := d.GetChange(key) + defaultRuleOrderOld := old == nil || old.(string) == "" || old.(string) == networkfirewall.RuleOrderDefaultActionOrder + defaultRuleOrderNew := new == nil || new.(string) == "" || new.(string) == networkfirewall.RuleOrderDefaultActionOrder + + if (defaultRuleOrderOld && !defaultRuleOrderNew) || (defaultRuleOrderNew && !defaultRuleOrderOld) { + return d.ForceNew(key) + } + } + return nil +} diff --git a/internal/service/networkfirewall/rule_group.go b/internal/service/networkfirewall/rule_group.go index 4b3bc1a63db..25c5421f7f1 100644 --- a/internal/service/networkfirewall/rule_group.go +++ b/internal/service/networkfirewall/rule_group.go @@ -10,6 +10,7 @@ import ( "github.com/aws/aws-sdk-go/service/networkfirewall" "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" @@ -376,6 +377,20 @@ func ResourceRuleGroup() *schema.Resource { }, }, }, + "stateful_rule_options": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "rule_order": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice(networkfirewall.RuleOrder_Values(), false), + }, + }, + }, + }, }, }, }, @@ -396,7 +411,14 @@ func ResourceRuleGroup() *schema.Resource { }, }, - CustomizeDiff: verify.SetTagsDiff, + CustomizeDiff: customdiff.Sequence( + // The stateful rule_order default action can be explicitly or implicitly set, + // so ignore spurious diffs if toggling between the two. + func(_ context.Context, d *schema.ResourceDiff, meta interface{}) error { + return forceNewIfNotRuleOrderDefault("rule_group.0.stateful_rule_options.0.rule_order", d) + }, + verify.SetTagsDiff, + ), } } @@ -726,6 +748,16 @@ func expandNetworkFirewallRuleGroup(l []interface{}) *networkfirewall.RuleGroup ruleGroup.RulesSource = rulesSource } } + if tfList, ok := tfMap["stateful_rule_options"].([]interface{}); ok && len(tfList) > 0 && tfList[0] != nil { + statefulRuleOptions := &networkfirewall.StatefulRuleOptions{} + sroMap, ok := tfList[0].(map[string]interface{}) + if ok { + if v, ok := sroMap["rule_order"].(string); ok && v != "" { + statefulRuleOptions.RuleOrder = aws.String(v) + } + } + ruleGroup.StatefulRuleOptions = statefulRuleOptions + } return ruleGroup } @@ -952,8 +984,9 @@ func flattenNetworkFirewallRuleGroup(r *networkfirewall.RuleGroup) []interface{} } m := map[string]interface{}{ - "rule_variables": flattenNetworkFirewallRuleVariables(r.RuleVariables), - "rules_source": flattenNetworkFirewallRulesSource(r.RulesSource), + "rule_variables": flattenNetworkFirewallRuleVariables(r.RuleVariables), + "rules_source": flattenNetworkFirewallRulesSource(r.RulesSource), + "stateful_rule_options": flattenNetworkFirewallStatefulRulesOptions(r.StatefulRuleOptions), } return []interface{}{m} @@ -1212,3 +1245,15 @@ func flattenNetworkFirewallTCPFlags(t []*networkfirewall.TCPFlagField) []interfa return flagFields } + +func flattenNetworkFirewallStatefulRulesOptions(sro *networkfirewall.StatefulRuleOptions) []interface{} { + if sro == nil { + return []interface{}{} + } + + m := map[string]interface{}{ + "rule_order": aws.StringValue(sro.RuleOrder), + } + + return []interface{}{m} +} diff --git a/internal/service/networkfirewall/rule_group_test.go b/internal/service/networkfirewall/rule_group_test.go index ddea7c8b0a5..620f6a27df8 100644 --- a/internal/service/networkfirewall/rule_group_test.go +++ b/internal/service/networkfirewall/rule_group_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/networkfirewall" "github.com/hashicorp/aws-sdk-go-base/tfawserr" sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" @@ -16,6 +17,7 @@ import ( ) func TestAccNetworkFirewallRuleGroup_Basic_rulesSourceList(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -28,7 +30,7 @@ func TestAccNetworkFirewallRuleGroup_Basic_rulesSourceList(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_rulesSourceList(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "network-firewall", fmt.Sprintf("stateful-rulegroup/%s", rName)), resource.TestCheckResourceAttr(resourceName, "capacity", "100"), resource.TestCheckResourceAttr(resourceName, "name", rName), @@ -54,6 +56,7 @@ func TestAccNetworkFirewallRuleGroup_Basic_rulesSourceList(t *testing.T) { } func TestAccNetworkFirewallRuleGroup_Basic_statefulRule(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -66,7 +69,7 @@ func TestAccNetworkFirewallRuleGroup_Basic_statefulRule(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_statefulRule(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "network-firewall", fmt.Sprintf("stateful-rulegroup/%s", rName)), resource.TestCheckResourceAttr(resourceName, "capacity", "100"), resource.TestCheckResourceAttr(resourceName, "name", rName), @@ -101,6 +104,7 @@ func TestAccNetworkFirewallRuleGroup_Basic_statefulRule(t *testing.T) { } func TestAccNetworkFirewallRuleGroup_Basic_statelessRule(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -113,7 +117,7 @@ func TestAccNetworkFirewallRuleGroup_Basic_statelessRule(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_statelessRule(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "network-firewall", fmt.Sprintf("stateless-rulegroup/%s", rName)), resource.TestCheckResourceAttr(resourceName, "capacity", "100"), resource.TestCheckResourceAttr(resourceName, "name", rName), @@ -143,6 +147,7 @@ func TestAccNetworkFirewallRuleGroup_Basic_statelessRule(t *testing.T) { } func TestAccNetworkFirewallRuleGroup_Basic_rules(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" rules := `alert http any any -> any any (http_response_line; content:"403 Forbidden"; sid:1;)` @@ -156,7 +161,7 @@ func TestAccNetworkFirewallRuleGroup_Basic_rules(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_rules(rName, rules), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "network-firewall", fmt.Sprintf("stateful-rulegroup/%s", rName)), resource.TestCheckResourceAttr(resourceName, "capacity", "100"), resource.TestCheckResourceAttr(resourceName, "name", rName), @@ -179,7 +184,83 @@ func TestAccNetworkFirewallRuleGroup_Basic_rules(t *testing.T) { }) } +func TestAccNetworkFirewallRuleGroup_statefulRuleOptions(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_networkfirewall_rule_group.test" + rules := `alert http any any -> any any (http_response_line; content:"403 Forbidden"; sid:1;)` + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkfirewall.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckRuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNetworkFirewallRuleGroup_statefulRuleOptions(rName, rules, "STRICT_ORDER"), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &ruleGroup), + resource.TestCheckResourceAttr(resourceName, "rule_group.0.stateful_rule_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule_group.0.stateful_rule_options.0.rule_order", networkfirewall.RuleOrderStrictOrder), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccNetworkFirewallRuleGroup_updateStatefulRuleOptions(t *testing.T) { + var ruleGroup1, ruleGroup2, ruleGroup3 networkfirewall.DescribeRuleGroupOutput + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_networkfirewall_rule_group.test" + rules := `alert http any any -> any any (http_response_line; content:"403 Forbidden"; sid:1;)` + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, + ErrorCheck: acctest.ErrorCheck(t, networkfirewall.EndpointsID), + Providers: acctest.Providers, + CheckDestroy: testAccCheckRuleGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccNetworkFirewallRuleGroup_statefulRuleOptions(rName, rules, "STRICT_ORDER"), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &ruleGroup1), + resource.TestCheckResourceAttr(resourceName, "rule_group.0.stateful_rule_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule_group.0.stateful_rule_options.0.rule_order", networkfirewall.RuleOrderStrictOrder), + ), + }, + { + Config: testAccNetworkFirewallRuleGroup_statefulRuleOptions(rName, rules, "DEFAULT_ACTION_ORDER"), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &ruleGroup2), + testAccCheckRuleGroupRecreated(&ruleGroup1, &ruleGroup2), + resource.TestCheckResourceAttr(resourceName, "rule_group.0.stateful_rule_options.#", "1"), + resource.TestCheckResourceAttr(resourceName, "rule_group.0.stateful_rule_options.0.rule_order", networkfirewall.RuleOrderDefaultActionOrder), + ), + }, + { + Config: testAccNetworkFirewallRuleGroup_rulesSourceRulesString(rName, rules), + Check: resource.ComposeTestCheckFunc( + testAccCheckRuleGroupExists(resourceName, &ruleGroup3), + testAccCheckRuleGroupNotRecreated(&ruleGroup2, &ruleGroup3), + resource.TestCheckResourceAttr(resourceName, "rule_group.0.stateful_rule_options.#", "0"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccNetworkFirewallRuleGroup_statelessRuleWithCustomAction(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -192,7 +273,7 @@ func TestAccNetworkFirewallRuleGroup_statelessRuleWithCustomAction(t *testing.T) { Config: testAccNetworkFirewallRuleGroup_statelessRuleWithCustomAction(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "network-firewall", fmt.Sprintf("stateless-rulegroup/%s", rName)), resource.TestCheckResourceAttr(resourceName, "capacity", "100"), resource.TestCheckResourceAttr(resourceName, "name", rName), @@ -229,6 +310,7 @@ func TestAccNetworkFirewallRuleGroup_statelessRuleWithCustomAction(t *testing.T) // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/19414 func TestAccNetworkFirewallRuleGroup_updateRules(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -244,13 +326,13 @@ func TestAccNetworkFirewallRuleGroup_updateRules(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_rules(rName, rules), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), ), }, { Config: testAccNetworkFirewallRuleGroup_basic_rules(rName, updatedRules), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rules", updatedRules), resource.TestCheckResourceAttr(resourceName, "rule_group.#", "1"), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.#", "1"), @@ -269,6 +351,7 @@ func TestAccNetworkFirewallRuleGroup_updateRules(t *testing.T) { } func TestAccNetworkFirewallRuleGroup_updateRulesSourceList(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -281,13 +364,13 @@ func TestAccNetworkFirewallRuleGroup_updateRulesSourceList(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_rulesSourceList(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), ), }, { Config: testAccNetworkFirewallRuleGroup_updateRulesSourceList(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), acctest.CheckResourceAttrRegionalARN(resourceName, "arn", "network-firewall", fmt.Sprintf("stateful-rulegroup/%s", rName)), resource.TestCheckResourceAttr(resourceName, "capacity", "100"), resource.TestCheckResourceAttr(resourceName, "name", rName), @@ -314,6 +397,7 @@ func TestAccNetworkFirewallRuleGroup_updateRulesSourceList(t *testing.T) { } func TestAccNetworkFirewallRuleGroup_rulesSourceAndRuleVariables(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -326,7 +410,7 @@ func TestAccNetworkFirewallRuleGroup_rulesSourceAndRuleVariables(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_rulesSourceList_ruleVariables(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.#", "1"), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rule_variables.#", "1"), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.#", "1"), @@ -343,7 +427,7 @@ func TestAccNetworkFirewallRuleGroup_rulesSourceAndRuleVariables(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_rulesSourceList_updateRuleVariables(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.#", "1"), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rule_variables.#", "1"), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.#", "1"), @@ -375,7 +459,7 @@ func TestAccNetworkFirewallRuleGroup_rulesSourceAndRuleVariables(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_rulesSourceList(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.#", "1"), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rule_variables.#", "0"), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.#", "1"), @@ -393,6 +477,7 @@ func TestAccNetworkFirewallRuleGroup_rulesSourceAndRuleVariables(t *testing.T) { // TestAccNetworkFirewallRuleGroup_updateStatefulRule validates // in-place updates to a single stateful_rule configuration block func TestAccNetworkFirewallRuleGroup_updateStatefulRule(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -405,13 +490,13 @@ func TestAccNetworkFirewallRuleGroup_updateStatefulRule(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_statefulRule(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), ), }, { Config: testAccNetworkFirewallRuleGroup_updateStatefulRule(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.0.stateful_rule.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule_group.0.rules_source.0.stateful_rule.*", map[string]string{ "action": networkfirewall.StatefulActionDrop, @@ -439,6 +524,7 @@ func TestAccNetworkFirewallRuleGroup_updateStatefulRule(t *testing.T) { // in-place updates to stateful_rule configuration blocks // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/16868 func TestAccNetworkFirewallRuleGroup_updateMultipleStatefulRules(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -451,14 +537,14 @@ func TestAccNetworkFirewallRuleGroup_updateMultipleStatefulRules(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_statefulRule(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.0.stateful_rule.#", "1"), ), }, { Config: testAccNetworkFirewallRuleGroup_multipleStatefulRules(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.0.stateful_rule.#", "2"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule_group.0.rules_source.0.stateful_rule.*", map[string]string{ "action": networkfirewall.StatefulActionPass, @@ -492,7 +578,7 @@ func TestAccNetworkFirewallRuleGroup_updateMultipleStatefulRules(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_updateStatefulRule(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.0.stateful_rule.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule_group.0.rules_source.0.stateful_rule.*", map[string]string{ "action": networkfirewall.StatefulActionDrop, @@ -520,6 +606,7 @@ func TestAccNetworkFirewallRuleGroup_updateMultipleStatefulRules(t *testing.T) { // updates to the "action" argument within 1 stateful_rule configuration block // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/16868 func TestAccNetworkFirewallRuleGroup_StatefulRule_action(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -532,7 +619,7 @@ func TestAccNetworkFirewallRuleGroup_StatefulRule_action(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_statefulRule_action(rName, networkfirewall.StatefulActionAlert), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.0.stateful_rule.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule_group.0.rules_source.0.stateful_rule.*", map[string]string{ "action": networkfirewall.StatefulActionAlert, @@ -547,7 +634,7 @@ func TestAccNetworkFirewallRuleGroup_StatefulRule_action(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_statefulRule_action(rName, networkfirewall.StatefulActionPass), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.0.stateful_rule.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule_group.0.rules_source.0.stateful_rule.*", map[string]string{ "action": networkfirewall.StatefulActionPass, @@ -562,7 +649,7 @@ func TestAccNetworkFirewallRuleGroup_StatefulRule_action(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_statefulRule_action(rName, networkfirewall.StatefulActionDrop), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.0.stateful_rule.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule_group.0.rules_source.0.stateful_rule.*", map[string]string{ "action": networkfirewall.StatefulActionDrop, @@ -580,6 +667,7 @@ func TestAccNetworkFirewallRuleGroup_StatefulRule_action(t *testing.T) { // Reference: https://github.com/hashicorp/terraform-provider-aws/issues/16470 func TestAccNetworkFirewallRuleGroup_StatefulRule_header(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -592,7 +680,7 @@ func TestAccNetworkFirewallRuleGroup_StatefulRule_header(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_statefulRule_header(rName, "1990", "1994"), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.0.stateful_rule.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule_group.0.rules_source.0.stateful_rule.*", map[string]string{ "action": networkfirewall.StatefulActionPass, @@ -615,7 +703,7 @@ func TestAccNetworkFirewallRuleGroup_StatefulRule_header(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_statefulRule_header(rName, "ANY", "ANY"), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.0.stateful_rule.#", "1"), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule_group.0.rules_source.0.stateful_rule.*", map[string]string{ "action": networkfirewall.StatefulActionPass, @@ -640,6 +728,7 @@ func TestAccNetworkFirewallRuleGroup_StatefulRule_header(t *testing.T) { } func TestAccNetworkFirewallRuleGroup_updateStatelessRule(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -652,13 +741,13 @@ func TestAccNetworkFirewallRuleGroup_updateStatelessRule(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_statelessRule(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), ), }, { Config: testAccNetworkFirewallRuleGroup_updateStatelessRule(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule_group.0.rules_source.0.stateless_rules_and_custom_actions.0.stateless_rule.*", map[string]string{ "priority": "10", "rule_definition.#": "1", @@ -689,6 +778,7 @@ func TestAccNetworkFirewallRuleGroup_updateStatelessRule(t *testing.T) { } func TestAccNetworkFirewallRuleGroup_tags(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -701,7 +791,7 @@ func TestAccNetworkFirewallRuleGroup_tags(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_oneTag(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), ), @@ -709,7 +799,7 @@ func TestAccNetworkFirewallRuleGroup_tags(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_twoTags(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), resource.TestCheckResourceAttr(resourceName, "tags.Description", "updated"), @@ -718,7 +808,7 @@ func TestAccNetworkFirewallRuleGroup_tags(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_rulesSourceList(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, @@ -732,6 +822,7 @@ func TestAccNetworkFirewallRuleGroup_tags(t *testing.T) { } func TestAccNetworkFirewallRuleGroup_disappears(t *testing.T) { + var ruleGroup networkfirewall.DescribeRuleGroupOutput rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) resourceName := "aws_networkfirewall_rule_group.test" @@ -744,7 +835,7 @@ func TestAccNetworkFirewallRuleGroup_disappears(t *testing.T) { { Config: testAccNetworkFirewallRuleGroup_basic_rulesSourceList(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckRuleGroupExists(resourceName), + testAccCheckRuleGroupExists(resourceName, &ruleGroup), acctest.CheckResourceDisappears(acctest.Provider, tfnetworkfirewall.ResourceRuleGroup(), resourceName), ), ExpectNonEmptyPlan: true, @@ -775,7 +866,7 @@ func testAccCheckRuleGroupDestroy(s *terraform.State) error { return nil } -func testAccCheckRuleGroupExists(n string) resource.TestCheckFunc { +func testAccCheckRuleGroupExists(n string, r *networkfirewall.DescribeRuleGroupOutput) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[n] if !ok { @@ -796,6 +887,26 @@ func testAccCheckRuleGroupExists(n string) resource.TestCheckFunc { return fmt.Errorf("NetworkFirewall Rule Group (%s) not found", rs.Primary.ID) } + *r = *output + + return nil + } +} + +func testAccCheckRuleGroupNotRecreated(i, j *networkfirewall.DescribeRuleGroupOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.StringValue(i.RuleGroupResponse.RuleGroupId), aws.StringValue(j.RuleGroupResponse.RuleGroupId); before != after { + return fmt.Errorf("NetworkFirewall Rule Group was recreated. got: %s, expected: %s", after, before) + } + return nil + } +} + +func testAccCheckRuleGroupRecreated(i, j *networkfirewall.DescribeRuleGroupOutput) resource.TestCheckFunc { + return func(s *terraform.State) error { + if before, after := aws.StringValue(i.RuleGroupResponse.RuleGroupId), aws.StringValue(j.RuleGroupResponse.RuleGroupId); before == after { + return fmt.Errorf("NetworkFirewall Rule Group (%s) was not recreated", before) + } return nil } } @@ -1145,6 +1256,39 @@ resource "aws_networkfirewall_rule_group" "test" { `, rName, rules) } +func testAccNetworkFirewallRuleGroup_rulesSourceRulesString(rName, rules string) string { + return fmt.Sprintf(` +resource "aws_networkfirewall_rule_group" "test" { + capacity = 100 + name = %[1]q + type = "STATEFUL" + rule_group { + rules_source { + rules_string = %[2]q + } + } +} +`, rName, rules) +} + +func testAccNetworkFirewallRuleGroup_statefulRuleOptions(rName, rules, ruleOrder string) string { + return fmt.Sprintf(` +resource "aws_networkfirewall_rule_group" "test" { + capacity = 100 + name = %[1]q + type = "STATEFUL" + rule_group { + rules_source { + rules_string = %[2]q + } + stateful_rule_options { + rule_order = %[3]q + } + } +} +`, rName, rules, ruleOrder) +} + func testAccNetworkFirewallRuleGroup_statelessRuleWithCustomAction(rName string) string { return fmt.Sprintf(` resource "aws_networkfirewall_rule_group" "test" { diff --git a/website/docs/r/networkfirewall_firewall_policy.html.markdown b/website/docs/r/networkfirewall_firewall_policy.html.markdown index 9cc4c663458..8da53fe27eb 100644 --- a/website/docs/r/networkfirewall_firewall_policy.html.markdown +++ b/website/docs/r/networkfirewall_firewall_policy.html.markdown @@ -72,6 +72,10 @@ The following arguments are supported: The `firewall_policy` block supports the following arguments: +* `stateful_default_actions` - (Optional) Set of actions to take on a packet if it does not match any stateful rules in the policy. This can only be specified if the policy has a `stateful_engine_options` block with a `rule_order` value of `STRICT_ORDER`. You can specify values either of `aws:drop_strict` or `aws:drop_established`, as well as either of `aws:alert_strict` or `aws:alert_established`. + +* `stateful_engine_options` - (Optional) A configuration block that defines options on how the policy handles stateful rules. See [Stateful Engine Options](#stateful-engine-options) below for details. + * `stateful_rule_group_reference` - (Optional) Set of configuration blocks containing references to the stateful rule groups that are used in the policy. See [Stateful Rule Group Reference](#stateful-rule-group-reference) below for details. * `stateless_custom_action` - (Optional) Set of configuration blocks describing the custom action definitions that are available for use in the firewall policy's `stateless_default_actions`. See [Stateless Custom Action](#stateless-custom-action) below for details. @@ -84,9 +88,18 @@ In addition, you can specify custom actions that are compatible with your standa * `stateless_rule_group_reference` - (Optional) Set of configuration blocks containing references to the stateless rule groups that are used in the policy. See [Stateless Rule Group Reference](#stateless-rule-group-reference) below for details. +### Stateful Engine Options +The `stateful_engine_options` block supports the following argument: + +~> **NOTE:** If the `STRICT_ORDER` rule order is specified, this firewall policy can only reference stateful rule groups that utilize `STRICT_ORDER`. + +* `rule_order` - (Required) Indicates how to manage the order of stateful rule evaluation for the policy. Default value: `DEFAULT_ACTION_ORDER`. Valid values: `DEFAULT_ACTION_ORDER`, `STRICT_ORDER`. + ### Stateful Rule Group Reference -The `stateful_rule_group_reference` block supports the following argument: +The `stateful_rule_group_reference` block supports the following arguments: + +* `priority` - (Optional) An integer setting that indicates the order in which to apply the stateful rule groups in a single policy. This argument must be specified if the policy has a `stateful_engine_options` block with a `rule_order` value of `STRICT_ORDER`. AWS Network Firewall applies each stateful rule group to a packet starting with the group that has the lowest priority setting. * `resource_arn` - (Required) The Amazon Resource Name (ARN) of the stateful rule group. diff --git a/website/docs/r/networkfirewall_rule_group.html.markdown b/website/docs/r/networkfirewall_rule_group.html.markdown index 8a0ec4191de..199fa4709db 100644 --- a/website/docs/r/networkfirewall_rule_group.html.markdown +++ b/website/docs/r/networkfirewall_rule_group.html.markdown @@ -249,6 +249,8 @@ The `rule_group` block supports the following argument: * `rules_source` - (Required) A configuration block that defines the stateful or stateless rules for the rule group. See [Rules Source](#rules-source) below for details. +* `stateful_rule_options` - (Optional) A configuration block that defines stateful rule options for the rule group. See [Stateful Rule Options](#stateful-rule-options) below for details. + ### Rule Variables The `rule_variables` block supports the following arguments: @@ -299,6 +301,14 @@ The `rules_source` block supports the following arguments: * `stateless_rules_and_custom_actions` - (Optional) A configuration block containing **stateless** inspection criteria for a stateless rule group. See [Stateless Rules and Custom Actions](#stateless-rules-and-custom-actions) below for details. +### Stateful Rule Options + +The `stateful_rule_options` block supports the following argument: + +~> **NOTE:** If the `STRICT_ORDER` rule order is specified, this rule group can only be referenced in firewall policies that also utilize `STRICT_ORDER` for the stateful engine. `STRICT_ORDER` can only be specified when using a `rules_source` of `rules_string` or `stateful_rule`. + +* `rule_order` - (Required) Indicates how to manage the order of the rule evaluation for the rule group. Default value: `DEFAULT_ACTION_ORDER`. Valid values: `DEFAULT_ACTION_ORDER`, `STRICT_ORDER`. + ### Rules Source List The `rules_source_list` block supports the following arguments: From 6450f6a939d5d337d2f46e54bf188d7f3a6273b5 Mon Sep 17 00:00:00 2001 From: Stuart Williams Date: Tue, 30 Nov 2021 00:52:41 -0500 Subject: [PATCH 2/4] Fix default action docs --- website/docs/r/networkfirewall_firewall_policy.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/networkfirewall_firewall_policy.html.markdown b/website/docs/r/networkfirewall_firewall_policy.html.markdown index 8da53fe27eb..9bbaa0901ea 100644 --- a/website/docs/r/networkfirewall_firewall_policy.html.markdown +++ b/website/docs/r/networkfirewall_firewall_policy.html.markdown @@ -72,7 +72,7 @@ The following arguments are supported: The `firewall_policy` block supports the following arguments: -* `stateful_default_actions` - (Optional) Set of actions to take on a packet if it does not match any stateful rules in the policy. This can only be specified if the policy has a `stateful_engine_options` block with a `rule_order` value of `STRICT_ORDER`. You can specify values either of `aws:drop_strict` or `aws:drop_established`, as well as either of `aws:alert_strict` or `aws:alert_established`. +* `stateful_default_actions` - (Optional) Set of actions to take on a packet if it does not match any stateful rules in the policy. This can only be specified if the policy has a `stateful_engine_options` block with a `rule_order` value of `STRICT_ORDER`. You can specify one of either or neither values of `aws:drop_strict` or `aws:drop_established`, as well as any combination of `aws:alert_strict` and `aws:alert_established`. * `stateful_engine_options` - (Optional) A configuration block that defines options on how the policy handles stateful rules. See [Stateful Engine Options](#stateful-engine-options) below for details. From dc82456c8453bef46eaeb4ec35ee39c6a70f3cd8 Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Dec 2021 14:06:15 -0500 Subject: [PATCH 3/4] Minor additional attribute checks in acceptance tests. --- internal/service/networkfirewall/firewall_policy_test.go | 2 ++ internal/service/networkfirewall/rule_group_test.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/internal/service/networkfirewall/firewall_policy_test.go b/internal/service/networkfirewall/firewall_policy_test.go index b6935503c60..5ab5aada9e5 100644 --- a/internal/service/networkfirewall/firewall_policy_test.go +++ b/internal/service/networkfirewall/firewall_policy_test.go @@ -173,6 +173,8 @@ func TestAccNetworkFirewallFirewallPolicy_statefulRuleGroupReference(t *testing. Check: resource.ComposeTestCheckFunc( testAccCheckFirewallPolicyExists(resourceName, &firewallPolicy), resource.TestCheckResourceAttr(resourceName, "firewall_policy.#", "1"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_default_actions.#", "0"), + resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_engine_options.#", "0"), resource.TestCheckResourceAttr(resourceName, "firewall_policy.0.stateful_rule_group_reference.#", "1"), resource.TestCheckTypeSetElemAttrPair(resourceName, "firewall_policy.0.stateful_rule_group_reference.*.resource_arn", ruleGroupResourceName, "arn"), ), diff --git a/internal/service/networkfirewall/rule_group_test.go b/internal/service/networkfirewall/rule_group_test.go index 620f6a27df8..2c4dfea9efd 100644 --- a/internal/service/networkfirewall/rule_group_test.go +++ b/internal/service/networkfirewall/rule_group_test.go @@ -43,6 +43,7 @@ func TestAccNetworkFirewallRuleGroup_Basic_rulesSourceList(t *testing.T) { resource.TestCheckTypeSetElemAttr(resourceName, "rule_group.0.rules_source.0.rules_source_list.0.target_types.*", networkfirewall.TargetTypeHttpHost), resource.TestCheckResourceAttr(resourceName, "rule_group.0.rules_source.0.rules_source_list.0.targets.#", "1"), resource.TestCheckTypeSetElemAttr(resourceName, "rule_group.0.rules_source.0.rules_source_list.0.targets.*", "test.example.com"), + resource.TestCheckResourceAttr(resourceName, "rule_group.0.stateful_rule_options.#", "0"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, @@ -91,6 +92,7 @@ func TestAccNetworkFirewallRuleGroup_Basic_statefulRule(t *testing.T) { resource.TestCheckTypeSetElemNestedAttrs(resourceName, "rule_group.0.rules_source.0.stateful_rule.*.rule_option.*", map[string]string{ "keyword": "sid:1", }), + resource.TestCheckResourceAttr(resourceName, "rule_group.0.stateful_rule_options.#", "0"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, From a9902a38a9664128f8056180b474748c95666f3e Mon Sep 17 00:00:00 2001 From: Kit Ewbank Date: Fri, 10 Dec 2021 14:30:59 -0500 Subject: [PATCH 4/4] Call 'testAccPreCheck' for GovCloud PreCheck. --- internal/service/networkfirewall/rule_group_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/networkfirewall/rule_group_test.go b/internal/service/networkfirewall/rule_group_test.go index 2c4dfea9efd..0e1f8c07246 100644 --- a/internal/service/networkfirewall/rule_group_test.go +++ b/internal/service/networkfirewall/rule_group_test.go @@ -674,7 +674,7 @@ func TestAccNetworkFirewallRuleGroup_StatefulRule_header(t *testing.T) { resourceName := "aws_networkfirewall_rule_group.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, + PreCheck: func() { acctest.PreCheck(t); testAccPreCheck(t) }, ErrorCheck: acctest.ErrorCheck(t, networkfirewall.EndpointsID), Providers: acctest.Providers, CheckDestroy: testAccCheckRuleGroupDestroy,