Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added policy_based_vpn_mode to NAT resource #1143

Merged
merged 15 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion nsxt/resource_nsxt_policy_nat_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ var policyNATRuleFirewallMatchTypeValues = []string{
model.PolicyNatRule_FIREWALL_MATCH_BYPASS,
}

var policyNATRulePolicyBasedVpnModeTypeValues = []string{
model.PolicyNatRule_POLICY_BASED_VPN_MODE_BYPASS,
model.PolicyNatRule_POLICY_BASED_VPN_MODE_MATCH,
}

func resourceNsxtPolicyNATRule() *schema.Resource {
return &schema.Resource{
Create: resourceNsxtPolicyNATRuleCreate,
Expand Down Expand Up @@ -131,6 +136,13 @@ func resourceNsxtPolicyNATRule() *schema.Resource {
Computed: true,
Elem: getElemPolicyPathSchema(),
},
"policy_based_vpn_mode": {
Type: schema.TypeString,
Description: "Policy based vpn mode match flag. DNAT and NO_DNAT only",
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice(policyNATRulePolicyBasedVpnModeTypeValues, false),
},
},
}
}
Expand Down Expand Up @@ -185,6 +197,13 @@ func patchNsxtPolicyNATRule(sessionContext utl.SessionContext, connector client.
if err != nil {
return err
}
if nsxVersionHigherOrEqual("4.0.0") {
_, err = getPolicyBasedVpnMode(rule)
if err != nil {
return err
}
}

if isT0 {
client := t0nat.NewNatRulesClient(sessionContext, connector)
return client.Patch(gwID, natType, *rule.Id, rule)
Expand Down Expand Up @@ -214,6 +233,19 @@ func getTranslatedNetworks(rule model.PolicyNatRule) (*string, error) {
return tNets, nil
}

func policyBasedVpnModeNeeded(action string) bool {
return action == model.PolicyNatRule_ACTION_DNAT || action == model.PolicyNatRule_ACTION_NO_DNAT
}

func getPolicyBasedVpnMode(rule model.PolicyNatRule) (*string, error) {
pbvmMatch := rule.PolicyBasedVpnMode
action := rule.Action
if pbvmMatch != nil && !policyBasedVpnModeNeeded(*action) {
return pbvmMatch, fmt.Errorf("Invalid NAT rule action %s for policy based vpn mode %s. policy based vpn mode supported only on DNAT/NO_DNAT rule", *action, *pbvmMatch)
}
return pbvmMatch, nil
}

func resourceNsxtPolicyNATRuleRead(d *schema.ResourceData, m interface{}) error {
connector := getPolicyConnector(m)

Expand Down Expand Up @@ -263,7 +295,9 @@ func resourceNsxtPolicyNATRuleRead(d *schema.ResourceData, m interface{}) error
}
d.Set("translated_ports", obj.TranslatedPorts)
d.Set("scope", obj.Scope)

if nsxVersionHigherOrEqual("4.0.0") {
d.Set("policy_based_vpn_mode", obj.PolicyBasedVpnMode)
}
d.SetId(id)

return nil
Expand Down Expand Up @@ -305,6 +339,7 @@ func resourceNsxtPolicyNATRuleCreate(d *schema.ResourceData, m interface{}) erro
priority := int64(d.Get("rule_priority").(int))
service := d.Get("service").(string)
ports := d.Get("translated_ports").(string)
pbvmMatch := d.Get("policy_based_vpn_mode").(string)
dNets := stringListToCommaSeparatedString(interfaceListToStringList(d.Get("destination_networks").([]interface{})))
sNets := stringListToCommaSeparatedString(interfaceListToStringList(d.Get("source_networks").([]interface{})))
tNets := stringListToCommaSeparatedString(interfaceListToStringList(d.Get("translated_networks").([]interface{})))
Expand Down Expand Up @@ -334,6 +369,9 @@ func resourceNsxtPolicyNATRuleCreate(d *schema.ResourceData, m interface{}) erro
if ports != "" {
ruleStruct.TranslatedPorts = &ports
}
if pbvmMatch != "" && nsxVersionHigherOrEqual("4.0.0") {
ruleStruct.PolicyBasedVpnMode = &pbvmMatch
}

log.Printf("[INFO] Creating NAT Rule with ID %s", id)

Expand Down Expand Up @@ -404,6 +442,10 @@ func resourceNsxtPolicyNATRuleUpdate(d *schema.ResourceData, m interface{}) erro
if tPorts != "" {
ruleStruct.TranslatedPorts = &tPorts
}
pbvmMatch := d.Get("policy_based_vpn_mode").(string)
if pbvmMatch != "" && nsxVersionHigherOrEqual("4.0.0") {
ruleStruct.PolicyBasedVpnMode = &pbvmMatch
}

log.Printf("[INFO] Updating NAT Rule with ID %s", id)
err := patchNsxtPolicyNATRule(context, connector, gwID, ruleStruct, isT0)
Expand Down
171 changes: 135 additions & 36 deletions nsxt/resource_nsxt_policy_nat_rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,67 @@ func testAccResourceNsxtPolicyNATRuleBasicT1(t *testing.T, withContext bool, pre
})
}

func TestAccResourceNsxtPolicyNATRule_withPolicyBasedVpnMode(t *testing.T) {
name := getAccTestResourceName()
updateName := getAccTestResourceName()
snet := "22.1.1.2"
dnet := "33.1.1.2"
tnet := "44.1.1.2"
action := model.PolicyNatRule_ACTION_DNAT

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t); testAccNSXVersion(t, "4.0.0") },
Providers: testAccProviders,
CheckDestroy: func(state *terraform.State) error {
return testAccNsxtPolicyNATRuleCheckDestroy(state, name, false)
},
Steps: []resource.TestStep{
{
Config: testAccNsxtPolicyNATRuleTier1CreateTemplateWithPolicyBasedVpnMode(name, action, testAccResourcePolicyNATRuleSourceNet, testAccResourcePolicyNATRuleDestNet, testAccResourcePolicyNATRuleTransNet, model.PolicyNatRule_POLICY_BASED_VPN_MODE_BYPASS, false),
Check: resource.ComposeTestCheckFunc(
testAccNsxtPolicyNATRuleExists(testAccResourcePolicyNATRuleName, false),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "display_name", name),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "description", "Acceptance Test"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "destination_networks.#", "1"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "source_networks.#", "1"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "translated_networks.#", "1"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "destination_networks.0", testAccResourcePolicyNATRuleDestNet),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "source_networks.0", testAccResourcePolicyNATRuleSourceNet),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "translated_networks.0", testAccResourcePolicyNATRuleTransNet),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "tag.#", "2"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "action", action),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "logging", "false"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "firewall_match", model.PolicyNatRule_FIREWALL_MATCH_BYPASS),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "policy_based_vpn_mode", model.PolicyNatRule_POLICY_BASED_VPN_MODE_BYPASS),
resource.TestCheckResourceAttrSet(testAccResourcePolicyNATRuleName, "path"),
resource.TestCheckResourceAttrSet(testAccResourcePolicyNATRuleName, "revision"),
),
},
{
Config: testAccNsxtPolicyNATRuleTier1CreateTemplateWithPolicyBasedVpnMode(updateName, action, snet, dnet, tnet, model.PolicyNatRule_POLICY_BASED_VPN_MODE_MATCH, false),
Check: resource.ComposeTestCheckFunc(
testAccNsxtPolicyNATRuleExists(testAccResourcePolicyNATRuleName, false),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "display_name", updateName),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "description", "Acceptance Test"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "destination_networks.#", "1"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "source_networks.#", "1"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "translated_networks.#", "1"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "destination_networks.0", dnet),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "source_networks.0", snet),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "translated_networks.0", tnet),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "tag.#", "2"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "action", action),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "logging", "false"),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "firewall_match", model.PolicyNatRule_FIREWALL_MATCH_BYPASS),
resource.TestCheckResourceAttr(testAccResourcePolicyNATRuleName, "policy_based_vpn_mode", model.PolicyNatRule_POLICY_BASED_VPN_MODE_MATCH),
resource.TestCheckResourceAttrSet(testAccResourcePolicyNATRuleName, "path"),
resource.TestCheckResourceAttrSet(testAccResourcePolicyNATRuleName, "revision"),
),
},
},
})
}

func TestAccResourceNsxtPolicyNATRule_basicT0(t *testing.T) {
name := getAccTestResourceName()
updateName := getAccTestResourceName()
Expand Down Expand Up @@ -455,16 +516,16 @@ data "nsxt_policy_service" "test" {

resource "nsxt_policy_nat_rule" "test" {
%s
display_name = "%s"
description = "Acceptance Test"
gateway_path = nsxt_policy_tier1_gateway.test.path
action = "%s"
source_networks = ["%s"]
destination_networks = ["%s"]
translated_networks = ["%s"]
logging = false
firewall_match = "%s"
service = data.nsxt_policy_service.test.path
display_name = "%s"
description = "Acceptance Test"
gateway_path = nsxt_policy_tier1_gateway.test.path
action = "%s"
source_networks = ["%s"]
destination_networks = ["%s"]
translated_networks = ["%s"]
logging = false
firewall_match = "%s"
service = data.nsxt_policy_service.test.path

tag {
scope = "scope1"
Expand All @@ -479,6 +540,44 @@ resource "nsxt_policy_nat_rule" "test" {
`, context, name, action, sourceNet, destNet, translatedNet, model.PolicyNatRule_FIREWALL_MATCH_BYPASS)
}

func testAccNsxtPolicyNATRuleTier1CreateTemplateWithPolicyBasedVpnMode(name string, action string, sourceNet string, destNet string, translatedNet string, policyBasedVpnMode string, withContext bool) string {
context := ""
if withContext {
context = testAccNsxtPolicyMultitenancyContext()
}
return testAccNsxtPolicyEdgeClusterReadTemplate(getEdgeClusterName()) +
testAccNsxtPolicyTier1WithEdgeClusterTemplate("test", false, withContext) + fmt.Sprintf(`
data "nsxt_policy_service" "test" {
display_name = "DNS-UDP"
}

resource "nsxt_policy_nat_rule" "test" {
%s
display_name = "%s"
description = "Acceptance Test"
gateway_path = nsxt_policy_tier1_gateway.test.path
action = "%s"
source_networks = ["%s"]
destination_networks = ["%s"]
translated_networks = ["%s"]
logging = false
firewall_match = "%s"
service = data.nsxt_policy_service.test.path
policy_based_vpn_mode = "%s"

tag {
scope = "scope1"
tag = "tag1"
}

tag {
scope = "scope2"
tag = "tag2"
}
}
`, context, name, action, sourceNet, destNet, translatedNet, model.PolicyNatRule_FIREWALL_MATCH_BYPASS, policyBasedVpnMode)
}

func testAccNsxtPolicyNATRuleTier1UpdateMultipleSourceNetworksTemplate(name string, action string, sourceNet1 string, sourceNet2 string, destNet string, translatedNet string, withContext bool) string {
context := ""
if withContext {
Expand All @@ -488,15 +587,15 @@ func testAccNsxtPolicyNATRuleTier1UpdateMultipleSourceNetworksTemplate(name stri
testAccNsxtPolicyTier1WithEdgeClusterTemplate("test", false, withContext) + fmt.Sprintf(`
resource "nsxt_policy_nat_rule" "test" {
%s
display_name = "%s"
description = "Acceptance Test"
gateway_path = nsxt_policy_tier1_gateway.test.path
action = "%s"
source_networks = ["%s", "%s"]
destination_networks = ["%s"]
translated_networks = ["%s"]
logging = false
firewall_match = "%s"
display_name = "%s"
description = "Acceptance Test"
gateway_path = nsxt_policy_tier1_gateway.test.path
action = "%s"
source_networks = ["%s", "%s"]
destination_networks = ["%s"]
translated_networks = ["%s"]
logging = false
firewall_match = "%s"

tag {
scope = "scope1"
Expand Down Expand Up @@ -549,15 +648,15 @@ resource "nsxt_policy_tier0_gateway_interface" "test" {
}

resource "nsxt_policy_nat_rule" "test" {
display_name = "%s"
description = "Acceptance Test"
gateway_path = nsxt_policy_tier0_gateway.test.path
action = "%s"
source_networks = ["%s"]
translated_networks = ["%s"]
logging = false
firewall_match = "%s"
scope = [nsxt_policy_tier0_gateway_interface.test[1].path, nsxt_policy_tier0_gateway_interface.test[0].path]
display_name = "%s"
description = "Acceptance Test"
gateway_path = nsxt_policy_tier0_gateway.test.path
action = "%s"
source_networks = ["%s"]
translated_networks = ["%s"]
logging = false
firewall_match = "%s"
scope = [nsxt_policy_tier0_gateway_interface.test[1].path, nsxt_policy_tier0_gateway_interface.test[0].path]

tag {
scope = "scope1"
Expand All @@ -577,14 +676,14 @@ func testAccNsxPolicyNatRuleNoTranslatedNetworkTemplate(name string, action stri
return testAccNsxtPolicyEdgeClusterReadTemplate(getEdgeClusterName()) +
testAccNsxtPolicyTier1WithEdgeClusterTemplate("test", false, false) + fmt.Sprintf(`
resource "nsxt_policy_nat_rule" "test" {
display_name = "%s"
description = "Acceptance Test"
gateway_path = nsxt_policy_tier1_gateway.test.path
action = "%s"
source_networks = ["%s"]
destination_networks = ["%s"]
logging = false
firewall_match = "%s"
display_name = "%s"
description = "Acceptance Test"
gateway_path = nsxt_policy_tier1_gateway.test.path
action = "%s"
source_networks = ["%s"]
destination_networks = ["%s"]
logging = false
firewall_match = "%s"

tag {
scope = "scope1"
Expand Down
35 changes: 19 additions & 16 deletions website/docs/r/policy_nat_rule.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ This resource is applicable to NSX Global Manager, NSX Policy Manager and VMC.

```hcl
resource "nsxt_policy_nat_rule" "dnat1" {
display_name = "dnat_rule1"
action = "DNAT"
source_networks = ["9.1.1.1", "9.2.1.1"]
destination_networks = ["11.1.1.1"]
translated_networks = ["10.1.1.1"]
gateway_path = nsxt_policy_tier1_gateway.t1gateway.path
logging = false
firewall_match = "MATCH_INTERNAL_ADDRESS"
display_name = "dnat_rule1"
action = "DNAT"
source_networks = ["9.1.1.1", "9.2.1.1"]
destination_networks = ["11.1.1.1"]
translated_networks = ["10.1.1.1"]
gateway_path = nsxt_policy_tier1_gateway.t1gateway.path
logging = false
firewall_match = "MATCH_INTERNAL_ADDRESS"
policy_based_vpn_mode = "BYPASS"

tag {
scope = "color"
Expand All @@ -42,14 +43,15 @@ resource "nsxt_policy_nat_rule" "dnat1" {
context {
project_id = data.nsxt_policy_project.demoproj.id
}
display_name = "dnat_rule1"
action = "DNAT"
source_networks = ["9.1.1.1", "9.2.1.1"]
destination_networks = ["11.1.1.1"]
translated_networks = ["10.1.1.1"]
gateway_path = nsxt_policy_tier1_gateway.t1gateway.path
logging = false
firewall_match = "MATCH_INTERNAL_ADDRESS"
display_name = "dnat_rule1"
action = "DNAT"
source_networks = ["9.1.1.1", "9.2.1.1"]
destination_networks = ["11.1.1.1"]
translated_networks = ["10.1.1.1"]
gateway_path = nsxt_policy_tier1_gateway.t1gateway.path
logging = false
firewall_match = "MATCH_INTERNAL_ADDRESS"
policy_based_vpn_mode = "BYPASS"

tag {
scope = "color"
Expand Down Expand Up @@ -80,6 +82,7 @@ The following arguments are supported:
* `translated_networks` - (Optional) A list of translated network IP addresses or CIDR.
* `translated_ports` - (Optional) Port number or port range. For use with `DNAT` action only.
* `scope` - (Optional) A list of paths to interfaces and/or labels where the NAT Rule is enforced.
* `policy_based_vpn_mode` - (Optional) Policy based VPN mode. One of `BYPASS`, `MATCH`. For use with `DNAT` and `NO_DNAT` actions only. Defaults to `BYPASS` when applicable. This argument is supported for NSX 4.0.0 and above.

## Attributes Reference

Expand Down
Loading