diff --git a/azurerm/helpers/validate/network.go b/azurerm/helpers/validate/network.go index 390d5721f679..609a23a33ae3 100644 --- a/azurerm/helpers/validate/network.go +++ b/azurerm/helpers/validate/network.go @@ -47,14 +47,26 @@ func MACAddress(i interface{}, k string) (_ []string, errors []error) { } func PortNumber(i interface{}, k string) (_ []string, errors []error) { + return validatePortNumber(i, k, false) +} + +func PortNumberOrZero(i interface{}, k string) (_ []string, errors []error) { + return validatePortNumber(i, k, true) +} + +func validatePortNumber(i interface{}, k string, allowZero bool) (_ []string, errors []error) { v, ok := i.(int) if !ok { errors = append(errors, fmt.Errorf("expected type of %q to be int", k)) return } + if allowZero && v == 0 { + return + } + if v < 1 || 65535 < v { - errors = append(errors, fmt.Errorf("%q is not a valid port number: %q", k, i)) + errors = append(errors, fmt.Errorf("%q is not a valid port number: %d", k, v)) } return diff --git a/azurerm/helpers/validate/network_test.go b/azurerm/helpers/validate/network_test.go index b2e91fdd7bb5..3911319b0b77 100644 --- a/azurerm/helpers/validate/network_test.go +++ b/azurerm/helpers/validate/network_test.go @@ -196,3 +196,49 @@ func TestPortNumber(t *testing.T) { }) } } + +func TestPortNumberOrZero(t *testing.T) { + cases := []struct { + Port int + Errors int + }{ + { + Port: -1, + Errors: 1, + }, + { + Port: 0, + Errors: 0, + }, + { + Port: 1, + Errors: 0, + }, + { + Port: 8477, + Errors: 0, + }, + { + Port: 65535, + Errors: 0, + }, + { + Port: 65536, + Errors: 1, + }, + { + Port: 7000000, + Errors: 1, + }, + } + + for _, tc := range cases { + t.Run(strconv.Itoa(tc.Port), func(t *testing.T) { + _, errors := PortNumberOrZero(tc.Port, "test") + + if len(errors) != tc.Errors { + t.Fatalf("Expected PortNumberOrZero to return %d error(s) not %d", len(errors), tc.Errors) + } + }) + } +} diff --git a/azurerm/resource_arm_loadbalancer_rule.go b/azurerm/resource_arm_loadbalancer_rule.go index 44ca3c0b3322..7142156cfad5 100644 --- a/azurerm/resource_arm_loadbalancer_rule.go +++ b/azurerm/resource_arm_loadbalancer_rule.go @@ -78,13 +78,13 @@ func resourceArmLoadBalancerRule() *schema.Resource { "frontend_port": { Type: schema.TypeInt, Required: true, - ValidateFunc: validate.PortNumber, + ValidateFunc: validate.PortNumberOrZero, }, "backend_port": { Type: schema.TypeInt, Required: true, - ValidateFunc: validate.PortNumber, + ValidateFunc: validate.PortNumberOrZero, }, "probe_id": { diff --git a/website/docs/r/loadbalancer_rule.html.markdown b/website/docs/r/loadbalancer_rule.html.markdown index 5f0ef7980af5..a9e013fee428 100644 --- a/website/docs/r/loadbalancer_rule.html.markdown +++ b/website/docs/r/loadbalancer_rule.html.markdown @@ -58,8 +58,8 @@ The following arguments are supported: * `loadbalancer_id` - (Required) The ID of the Load Balancer in which to create the Rule. * `frontend_ip_configuration_name` - (Required) The name of the frontend IP configuration to which the rule is associated. * `protocol` - (Required) The transport protocol for the external endpoint. Possible values are `Tcp`, `Udp` or `All`. -* `frontend_port` - (Required) The port for the external endpoint. Port numbers for each Rule must be unique within the Load Balancer. Possible values range between 1 and 65534, inclusive. -* `backend_port` - (Required) The port used for internal connections on the endpoint. Possible values range between 1 and 65535, inclusive. +* `frontend_port` - (Required) The port for the external endpoint. Port numbers for each Rule must be unique within the Load Balancer. Possible values range between 0 and 65534, inclusive. +* `backend_port` - (Required) The port used for internal connections on the endpoint. Possible values range between 0 and 65535, inclusive. * `backend_address_pool_id` - (Optional) A reference to a Backend Address Pool over which this Load Balancing Rule operates. * `probe_id` - (Optional) A reference to a Probe used by this Load Balancing Rule. * `enable_floating_ip` - (Optional) Floating IP is pertinent to failover scenarios: a "floating” IP is reassigned to a secondary server in case the primary server fails. Floating IP is required for SQL AlwaysOn.