diff --git a/nsxt/resource_nsxt_firewall_section.go b/nsxt/resource_nsxt_firewall_section.go index 8b22bc63e..297c48fbf 100644 --- a/nsxt/resource_nsxt_firewall_section.go +++ b/nsxt/resource_nsxt_firewall_section.go @@ -60,7 +60,13 @@ func resourceNsxtFirewallSection() *schema.Resource { ForceNew: true, }, "applied_to": getResourceReferencesSetSchema(false, false, []string{"LogicalPort", "LogicalSwitch", "NSGroup"}, "List of objects where the rules in this section will be enforced. This will take precedence over rule level appliedTo"), - "rule": getRulesSchema(), + "insert_before": { + Type: schema.TypeString, + Description: "Id of section that should come after this one", + Optional: true, + ForceNew: true, + }, + "rule": getRulesSchema(), }, } } @@ -239,6 +245,7 @@ func resourceNsxtFirewallSectionCreate(d *schema.ResourceData, m interface{}) er appliedTos := getResourceReferencesFromSchemaSet(d, "applied_to") sectionType := d.Get("section_type").(string) stateful := d.Get("stateful").(bool) + insertBefore := d.Get("insert_before") firewallSection := manager.FirewallSectionRuleList{ FirewallSection: manager.FirewallSection{ Description: description, @@ -252,6 +259,11 @@ func resourceNsxtFirewallSectionCreate(d *schema.ResourceData, m interface{}) er } localVarOptionals := make(map[string]interface{}) + if insertBefore != "" { + localVarOptionals["operation"] = "insert_before" + localVarOptionals["id"] = insertBefore + } + var resp *http.Response var err error if len(rules) == 0 { diff --git a/nsxt/resource_nsxt_firewall_section_test.go b/nsxt/resource_nsxt_firewall_section_test.go index 35b03a235..f1b9e1900 100644 --- a/nsxt/resource_nsxt_firewall_section_test.go +++ b/nsxt/resource_nsxt_firewall_section_test.go @@ -253,6 +253,59 @@ func TestAccResourceNsxtFirewallSection_withRulesAndTos(t *testing.T) { }) } +func TestAccResourceNsxtFirewallSection_ordered(t *testing.T) { + sectionNames := [4]string{"s1", "s2", "s3", "s4"} + testResourceNames := [4]string{"nsxt_firewall_section.test1", "nsxt_firewall_section.test2", "nsxt_firewall_section.test3", "nsxt_firewall_section.test4"} + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: func(state *terraform.State) error { + for i := 0; i <= 3; i++ { + err := testAccNSXFirewallSectionCheckDestroy(state, sectionNames[i]) + if err != nil { + return err + } + } + + return nil + }, + Steps: []resource.TestStep{ + { + Config: testAccNSXFirewallSectionCreateOrderedTemplate(), + Check: resource.ComposeTestCheckFunc( + testAccNSXFirewallSectionExists(sectionNames[0], testResourceNames[0]), + resource.TestCheckResourceAttr(testResourceNames[0], "display_name", sectionNames[0]), + resource.TestCheckResourceAttr(testResourceNames[0], "section_type", "LAYER3"), + testAccNSXFirewallSectionExists(sectionNames[1], testResourceNames[1]), + resource.TestCheckResourceAttr(testResourceNames[1], "display_name", sectionNames[1]), + resource.TestCheckResourceAttr(testResourceNames[1], "section_type", "LAYER3"), + testAccNSXFirewallSectionExists(sectionNames[2], testResourceNames[2]), + resource.TestCheckResourceAttr(testResourceNames[2], "display_name", sectionNames[2]), + resource.TestCheckResourceAttr(testResourceNames[2], "section_type", "LAYER3"), + ), + }, + { + Config: testAccNSXFirewallSectionUpdateOrderedTemplate(), + Check: resource.ComposeTestCheckFunc( + testAccNSXFirewallSectionExists(sectionNames[0], testResourceNames[0]), + resource.TestCheckResourceAttr(testResourceNames[0], "display_name", sectionNames[0]), + resource.TestCheckResourceAttr(testResourceNames[0], "section_type", "LAYER3"), + testAccNSXFirewallSectionExists(sectionNames[1], testResourceNames[1]), + resource.TestCheckResourceAttr(testResourceNames[1], "display_name", sectionNames[1]), + resource.TestCheckResourceAttr(testResourceNames[1], "section_type", "LAYER3"), + testAccNSXFirewallSectionExists(sectionNames[2], testResourceNames[2]), + resource.TestCheckResourceAttr(testResourceNames[2], "display_name", sectionNames[2]), + resource.TestCheckResourceAttr(testResourceNames[2], "section_type", "LAYER3"), + testAccNSXFirewallSectionExists(sectionNames[3], testResourceNames[3]), + resource.TestCheckResourceAttr(testResourceNames[3], "display_name", sectionNames[3]), + resource.TestCheckResourceAttr(testResourceNames[3], "section_type", "LAYER3"), + ), + }, + }, + }) +} + func TestAccResourceNsxtFirewallSection_importBasic(t *testing.T) { sectionName := fmt.Sprintf("test-nsx-firewall-section-basic") testResourceName := "nsxt_firewall_section.test" @@ -475,17 +528,17 @@ resource "nsxt_firewall_section" "test" { applied_to = %s rule { - display_name = "%s", - description = "rule1", + display_name = "%s", + description = "rule1", action = "ALLOW", logged = "true", ip_protocol = "IPV4", direction = "IN" - disabled = "false" + disabled = "false" } rule { - display_name = "rule2", + display_name = "rule2", description = "rule2", action = "ALLOW", logged = "true", @@ -518,3 +571,67 @@ resource "nsxt_firewall_section" "test" { applied_to = %s }`, updatedName, tags, tos) } + +func testAccNSXFirewallSectionCreateOrderedTemplate() string { + return ` +resource "nsxt_firewall_section" "test1" { + display_name = "s1" + section_type = "LAYER3" + stateful = true +} + +resource "nsxt_firewall_section" "test2" { + display_name = "s2" + section_type = "LAYER3" + insert_before = "${nsxt_firewall_section.test1.id}" + stateful = true + + rule { + display_name = "test" + action = "ALLOW", + logged = "true", + ip_protocol = "IPV4", + direction = "IN" + } +} + +resource "nsxt_firewall_section" "test3" { + display_name = "s3" + section_type = "LAYER3" + insert_before = "${nsxt_firewall_section.test2.id}" + stateful = true +} + +` +} + +func testAccNSXFirewallSectionUpdateOrderedTemplate() string { + return ` +resource "nsxt_firewall_section" "test1" { + display_name = "s1" + section_type = "LAYER3" + insert_before = "${nsxt_firewall_section.test4.id}" + stateful = true +} + +resource "nsxt_firewall_section" "test2" { + display_name = "s2" + section_type = "LAYER3" + insert_before = "${nsxt_firewall_section.test1.id}" + stateful = true +} + +resource "nsxt_firewall_section" "test3" { + display_name = "s3" + section_type = "LAYER3" + stateful = true +} + +resource "nsxt_firewall_section" "test4" { + display_name = "s4" + section_type = "LAYER3" + stateful = true +} + +` +} diff --git a/website/docs/r/firewall_section.html.markdown b/website/docs/r/firewall_section.html.markdown index e3be80732..2a1a0fd0c 100644 --- a/website/docs/r/firewall_section.html.markdown +++ b/website/docs/r/firewall_section.html.markdown @@ -8,6 +8,7 @@ description: A resource that can be used to configure a firewall section in NSX. # nsxt_firewall_section This resource provides a way to configure a firewall section on the NSX manager. A firewall section is a collection of firewall rules that are grouped together. +Order of firewall sections can be controlled with 'insert_before' attribute. ## Example Usage @@ -26,8 +27,9 @@ resource "nsxt_firewall_section" "firewall_sect" { target_id = "${nsxt_ns_group.group1.id}" } - section_type = "LAYER3" - stateful = true + section_type = "LAYER3" + stateful = true + insert_before = "${nsxt_firewall_section.bottom_line.id}" rule { display_name = "out_rule" @@ -81,6 +83,7 @@ The following arguments are supported: * `applied_to` - (Optional) List of objects where the rules in this section will be enforced. This will take precedence over rule level applied_to. [Supported target types: "LogicalPort", "LogicalSwitch", "NSGroup"] * `section_type` - (Required) Type of the rules which a section can contain. Either LAYER2 or LAYER3. Only homogeneous sections are supported. * `stateful` - (Required) Stateful or Stateless nature of firewall section is enforced on all rules inside the section. Layer3 sections can be stateful or stateless. Layer2 sections can only be stateless. +* `insert_before` - (Optional) Firewall section id that should come immediately after this one. It is user responsibility to use this attribute in consistent manner (for example, if same value would be set in two separate sections, the outcome would depend on order of creation). Changing this attribute would force recreation of the firewall section. * `rule` - (Optional) A list of rules to be applied in this section. each rule has the following arguments: * `display_name` - (Optional) The display name of this rule. Defaults to ID if not set. * `description` - (Optional) Description of this rule.