diff --git a/azurerm/import_arm_network_security_group_test.go b/azurerm/import_arm_network_security_group_test.go index ace2cf541f79..314992dc0be4 100644 --- a/azurerm/import_arm_network_security_group_test.go +++ b/azurerm/import_arm_network_security_group_test.go @@ -27,3 +27,45 @@ func TestAccAzureRMNetworkSecurityGroup_importBasic(t *testing.T) { }, }) } + +func TestAccAzureRMNetworkSecurityGroup_importSingleRule(t *testing.T) { + resourceName := "azurerm_network_security_group.test" + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkSecurityGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkSecurityGroup_singleRule(rInt, testLocation()), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMNetworkSecurityGroup_importMultipleRules(t *testing.T) { + resourceName := "azurerm_network_security_group.test" + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkSecurityGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkSecurityGroup_anotherRule(rInt, testLocation()), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/azurerm/network_security_rule.go b/azurerm/network_security_rule.go index f7b41d559d95..c9df9ee516f1 100644 --- a/azurerm/network_security_rule.go +++ b/azurerm/network_security_rule.go @@ -18,29 +18,3 @@ func validateNetworkSecurityRuleProtocol(v interface{}, k string) (ws []string, } return } - -func validateNetworkSecurityRuleAccess(v interface{}, k string) (ws []string, errors []error) { - value := strings.ToLower(v.(string)) - accessTypes := map[string]bool{ - "allow": true, - "deny": true, - } - - if !accessTypes[value] { - errors = append(errors, fmt.Errorf("Network Security Rule Access can only be Allow or Deny")) - } - return -} - -func validateNetworkSecurityRuleDirection(v interface{}, k string) (ws []string, errors []error) { - value := strings.ToLower(v.(string)) - directions := map[string]bool{ - "inbound": true, - "outbound": true, - } - - if !directions[value] { - errors = append(errors, fmt.Errorf("Network Security Rule Directions can only be Inbound or Outbound")) - } - return -} diff --git a/azurerm/network_security_rule_test.go b/azurerm/network_security_rule_test.go index f1f71e8f2926..8b9f21ecc4b2 100644 --- a/azurerm/network_security_rule_test.go +++ b/azurerm/network_security_rule_test.go @@ -41,75 +41,3 @@ func TestResourceAzureRMNetworkSecurityRuleProtocol_validation(t *testing.T) { } } } - -func TestResourceAzureRMNetworkSecurityRuleAccess_validation(t *testing.T) { - cases := []struct { - Value string - ErrCount int - }{ - { - Value: "Random", - ErrCount: 1, - }, - { - Value: "Allow", - ErrCount: 0, - }, - { - Value: "Deny", - ErrCount: 0, - }, - { - Value: "ALLOW", - ErrCount: 0, - }, - { - Value: "deny", - ErrCount: 0, - }, - } - - for _, tc := range cases { - _, errors := validateNetworkSecurityRuleAccess(tc.Value, "azurerm_network_security_rule") - - if len(errors) != tc.ErrCount { - t.Fatalf("Expected the Azure RM Network Security Rule access to trigger a validation error") - } - } -} - -func TestResourceAzureRMNetworkSecurityRuleDirection_validation(t *testing.T) { - cases := []struct { - Value string - ErrCount int - }{ - { - Value: "Random", - ErrCount: 1, - }, - { - Value: "Inbound", - ErrCount: 0, - }, - { - Value: "Outbound", - ErrCount: 0, - }, - { - Value: "INBOUND", - ErrCount: 0, - }, - { - Value: "Inbound", - ErrCount: 0, - }, - } - - for _, tc := range cases { - _, errors := validateNetworkSecurityRuleDirection(tc.Value, "azurerm_network_security_rule") - - if len(errors) != tc.ErrCount { - t.Fatalf("Expected the Azure RM Network Security Rule direction to trigger a validation error") - } - } -} diff --git a/azurerm/resource_arm_network_security_group.go b/azurerm/resource_arm_network_security_group.go index d3e10c4615eb..098deb001c97 100644 --- a/azurerm/resource_arm_network_security_group.go +++ b/azurerm/resource_arm_network_security_group.go @@ -1,15 +1,14 @@ package azurerm import ( - "bytes" "fmt" "log" "time" "github.com/Azure/azure-sdk-for-go/arm/network" - "github.com/hashicorp/terraform/helper/hashcode" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -37,7 +36,7 @@ func resourceArmNetworkSecurityGroup() *schema.Resource { "resource_group_name": resourceGroupNameSchema(), "security_rule": { - Type: schema.TypeSet, + Type: schema.TypeList, Optional: true, Computed: true, Elem: &schema.Resource{ @@ -48,16 +47,9 @@ func resourceArmNetworkSecurityGroup() *schema.Resource { }, "description": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - if len(value) > 140 { - errors = append(errors, fmt.Errorf( - "The network security rule description can be no longer than 140 chars")) - } - return - }, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateStringLength(140), }, "protocol": { @@ -88,32 +80,32 @@ func resourceArmNetworkSecurityGroup() *schema.Resource { }, "access": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validateNetworkSecurityRuleAccess, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.SecurityRuleAccessAllow), + string(network.SecurityRuleAccessDeny), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, }, "priority": { - Type: schema.TypeInt, - Required: true, - ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { - value := v.(int) - if value < 100 || value > 4096 { - errors = append(errors, fmt.Errorf( - "The `priority` can only be between 100 and 4096")) - } - return - }, + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(100, 4096), }, "direction": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validateNetworkSecurityRuleDirection, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.SecurityRuleDirectionInbound), + string(network.SecurityRuleDirectionOutbound), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, }, }, }, - Set: resourceArmNetworkSecurityGroupRuleHash, }, "tags": tagsSchema(), @@ -122,8 +114,7 @@ func resourceArmNetworkSecurityGroup() *schema.Resource { } func resourceArmNetworkSecurityGroupCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient) - secClient := client.secGroupClient + client := meta.(*ArmClient).secGroupClient name := d.Get("name").(string) location := d.Get("location").(string) @@ -132,7 +123,7 @@ func resourceArmNetworkSecurityGroupCreate(d *schema.ResourceData, meta interfac sgRules, sgErr := expandAzureRmSecurityRules(d) if sgErr != nil { - return fmt.Errorf("Error Building list of Network Security Group Rules: %s", sgErr) + return fmt.Errorf("Error Building list of Network Security Group Rules: %+v", sgErr) } azureRMLockByName(name, networkSecurityGroupResourceName) @@ -147,21 +138,21 @@ func resourceArmNetworkSecurityGroupCreate(d *schema.ResourceData, meta interfac Tags: expandTags(tags), } - _, error := secClient.CreateOrUpdate(resGroup, name, sg, make(chan struct{})) - err := <-error + _, createErr := client.CreateOrUpdate(resGroup, name, sg, make(chan struct{})) + err := <-createErr if err != nil { return err } - read, err := secClient.Get(resGroup, name, "") + read, err := client.Get(resGroup, name, "") if err != nil { return err } if read.ID == nil { - return fmt.Errorf("Cannot read Virtual Network %s (resource group %s) ID", name, resGroup) + return fmt.Errorf("Cannot read Virtual Network %q (resource group %q) ID", name, resGroup) } - log.Printf("[DEBUG] Waiting for NSG (%s) to become available", d.Get("name")) + log.Printf("[DEBUG] Waiting for NSG (%q) to become available", name) stateConf := &resource.StateChangeConf{ Pending: []string{"Updating", "Creating"}, Target: []string{"Succeeded"}, @@ -170,7 +161,7 @@ func resourceArmNetworkSecurityGroupCreate(d *schema.ResourceData, meta interfac MinTimeout: 15 * time.Second, } if _, err := stateConf.WaitForState(); err != nil { - return fmt.Errorf("Error waiting for NSG (%s) to become available: %s", d.Get("name"), err) + return fmt.Errorf("Error waiting for NSG (%q) to become available: %+v", name, err) } d.SetId(*read.ID) @@ -179,7 +170,7 @@ func resourceArmNetworkSecurityGroupCreate(d *schema.ResourceData, meta interfac } func resourceArmNetworkSecurityGroupRead(d *schema.ResourceData, meta interface{}) error { - secGroupClient := meta.(*ArmClient).secGroupClient + client := meta.(*ArmClient).secGroupClient id, err := parseAzureResourceID(d.Id()) if err != nil { @@ -188,29 +179,30 @@ func resourceArmNetworkSecurityGroupRead(d *schema.ResourceData, meta interface{ resGroup := id.ResourceGroup name := id.Path["networkSecurityGroups"] - resp, err := secGroupClient.Get(resGroup, name, "") + resp, err := client.Get(resGroup, name, "") if err != nil { if utils.ResponseWasNotFound(resp.Response) { d.SetId("") return nil } - return fmt.Errorf("Error making Read request on Azure Network Security Group %s: %s", name, err) + return fmt.Errorf("Error making Read request on Azure Network Security Group %q: %+v", name, err) } - if resp.SecurityGroupPropertiesFormat.SecurityRules != nil { - d.Set("security_rule", flattenNetworkSecurityRules(resp.SecurityGroupPropertiesFormat.SecurityRules)) - } - - d.Set("resource_group_name", resGroup) d.Set("name", resp.Name) + d.Set("resource_group_name", resGroup) d.Set("location", azureRMNormalizeLocation(*resp.Location)) + + if props := resp.SecurityGroupPropertiesFormat; props != nil { + d.Set("security_rule", flattenNetworkSecurityRules(props.SecurityRules)) + } + flattenAndSetTags(d, resp.Tags) return nil } func resourceArmNetworkSecurityGroupDelete(d *schema.ResourceData, meta interface{}) error { - secGroupClient := meta.(*ArmClient).secGroupClient + client := meta.(*ArmClient).secGroupClient id, err := parseAzureResourceID(d.Id()) if err != nil { @@ -219,62 +211,58 @@ func resourceArmNetworkSecurityGroupDelete(d *schema.ResourceData, meta interfac resGroup := id.ResourceGroup name := id.Path["networkSecurityGroups"] - _, error := secGroupClient.Delete(resGroup, name, make(chan struct{})) - err = <-error + _, deleteErr := client.Delete(resGroup, name, make(chan struct{})) + err = <-deleteErr return err } -func resourceArmNetworkSecurityGroupRuleHash(v interface{}) int { - var buf bytes.Buffer - m := v.(map[string]interface{}) - buf.WriteString(fmt.Sprintf("%s-", m["protocol"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["source_port_range"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["destination_port_range"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["source_address_prefix"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["destination_address_prefix"].(string))) - buf.WriteString(fmt.Sprintf("%s-", m["access"].(string))) - buf.WriteString(fmt.Sprintf("%d-", m["priority"].(int))) - buf.WriteString(fmt.Sprintf("%s-", m["direction"].(string))) - - return hashcode.String(buf.String()) -} - -func flattenNetworkSecurityRules(rules *[]network.SecurityRule) []map[string]interface{} { - result := make([]map[string]interface{}, 0, len(*rules)) - for _, rule := range *rules { - sgRule := make(map[string]interface{}) - sgRule["name"] = *rule.Name - sgRule["destination_address_prefix"] = *rule.SecurityRulePropertiesFormat.DestinationAddressPrefix - sgRule["destination_port_range"] = *rule.SecurityRulePropertiesFormat.DestinationPortRange - sgRule["source_address_prefix"] = *rule.SecurityRulePropertiesFormat.SourceAddressPrefix - sgRule["source_port_range"] = *rule.SecurityRulePropertiesFormat.SourcePortRange - sgRule["priority"] = int(*rule.SecurityRulePropertiesFormat.Priority) - sgRule["access"] = rule.SecurityRulePropertiesFormat.Access - sgRule["direction"] = rule.SecurityRulePropertiesFormat.Direction - sgRule["protocol"] = rule.SecurityRulePropertiesFormat.Protocol - - if rule.SecurityRulePropertiesFormat.Description != nil { - sgRule["description"] = *rule.SecurityRulePropertiesFormat.Description +func flattenNetworkSecurityRules(rules *[]network.SecurityRule) []interface{} { + result := make([]interface{}, 0) + + if rules != nil { + for _, rule := range *rules { + sgRule := make(map[string]interface{}) + sgRule["name"] = *rule.Name + + if props := rule.SecurityRulePropertiesFormat; props != nil { + sgRule["destination_address_prefix"] = *props.DestinationAddressPrefix + sgRule["destination_port_range"] = *props.DestinationPortRange + sgRule["source_address_prefix"] = *props.SourceAddressPrefix + sgRule["source_port_range"] = *props.SourcePortRange + sgRule["priority"] = int(*props.Priority) + sgRule["access"] = string(props.Access) + sgRule["direction"] = string(props.Direction) + sgRule["protocol"] = string(props.Protocol) + + if props.Description != nil { + sgRule["description"] = *props.Description + } + } + + result = append(result, sgRule) } - - result = append(result, sgRule) } + return result } func expandAzureRmSecurityRules(d *schema.ResourceData) ([]network.SecurityRule, error) { - sgRules := d.Get("security_rule").(*schema.Set).List() - rules := make([]network.SecurityRule, 0, len(sgRules)) + sgRules := d.Get("security_rule").([]interface{}) + rules := make([]network.SecurityRule, 0) for _, sgRaw := range sgRules { data := sgRaw.(map[string]interface{}) + name := data["name"].(string) source_port_range := data["source_port_range"].(string) destination_port_range := data["destination_port_range"].(string) source_address_prefix := data["source_address_prefix"].(string) destination_address_prefix := data["destination_address_prefix"].(string) priority := int32(data["priority"].(int)) + access := data["access"].(string) + direction := data["direction"].(string) + protocol := data["protocol"].(string) properties := network.SecurityRulePropertiesFormat{ SourcePortRange: &source_port_range, @@ -282,16 +270,15 @@ func expandAzureRmSecurityRules(d *schema.ResourceData) ([]network.SecurityRule, SourceAddressPrefix: &source_address_prefix, DestinationAddressPrefix: &destination_address_prefix, Priority: &priority, - Access: network.SecurityRuleAccess(data["access"].(string)), - Direction: network.SecurityRuleDirection(data["direction"].(string)), - Protocol: network.SecurityRuleProtocol(data["protocol"].(string)), + Access: network.SecurityRuleAccess(access), + Direction: network.SecurityRuleDirection(direction), + Protocol: network.SecurityRuleProtocol(protocol), } if v := data["description"].(string); v != "" { properties.Description = &v } - name := data["name"].(string) rule := network.SecurityRule{ Name: &name, SecurityRulePropertiesFormat: &properties, @@ -303,11 +290,11 @@ func expandAzureRmSecurityRules(d *schema.ResourceData) ([]network.SecurityRule, return rules, nil } -func networkSecurityGroupStateRefreshFunc(client *ArmClient, resourceGroupName string, sgName string) resource.StateRefreshFunc { +func networkSecurityGroupStateRefreshFunc(client network.SecurityGroupsClient, resourceGroupName string, sgName string) resource.StateRefreshFunc { return func() (interface{}, string, error) { - res, err := client.secGroupClient.Get(resourceGroupName, sgName, "") + res, err := client.Get(resourceGroupName, sgName, "") if err != nil { - return nil, "", fmt.Errorf("Error issuing read request in networkSecurityGroupStateRefreshFunc to Azure ARM for NSG '%s' (RG: '%s'): %s", sgName, resourceGroupName, err) + return nil, "", fmt.Errorf("Error issuing read request in networkSecurityGroupStateRefreshFunc for NSG '%s' (RG: '%s'): %+v", sgName, resourceGroupName, err) } return res, *res.SecurityGroupPropertiesFormat.ProvisioningState, nil diff --git a/azurerm/resource_arm_network_security_group_test.go b/azurerm/resource_arm_network_security_group_test.go index 306e87ff8240..eda9861ae21d 100644 --- a/azurerm/resource_arm_network_security_group_test.go +++ b/azurerm/resource_arm_network_security_group_test.go @@ -2,15 +2,16 @@ package azurerm import ( "fmt" - "net/http" "testing" "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) func TestAccAzureRMNetworkSecurityGroup_basic(t *testing.T) { + resourceName := "azurerm_network_security_group.test" rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -20,7 +21,50 @@ func TestAccAzureRMNetworkSecurityGroup_basic(t *testing.T) { { Config: testAccAzureRMNetworkSecurityGroup_basic(rInt, testLocation()), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMNetworkSecurityGroupExists("azurerm_network_security_group.test"), + testCheckAzureRMNetworkSecurityGroupExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMNetworkSecurityGroup_singleRule(t *testing.T) { + resourceName := "azurerm_network_security_group.test" + rInt := acctest.RandInt() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkSecurityGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkSecurityGroup_singleRule(rInt, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetworkSecurityGroupExists(resourceName), + ), + }, + }, + }) +} + +func TestAccAzureRMNetworkSecurityGroup_update(t *testing.T) { + resourceName := "azurerm_network_security_group.test" + rInt := acctest.RandInt() + location := testLocation() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMNetworkSecurityGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMNetworkSecurityGroup_singleRule(rInt, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetworkSecurityGroupExists(resourceName), + ), + }, + { + Config: testAccAzureRMNetworkSecurityGroup_basic(rInt, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMNetworkSecurityGroupExists(resourceName), ), }, }, @@ -28,6 +72,7 @@ func TestAccAzureRMNetworkSecurityGroup_basic(t *testing.T) { } func TestAccAzureRMNetworkSecurityGroup_disappears(t *testing.T) { + resourceName := "azurerm_network_security_group.test" rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -37,8 +82,8 @@ func TestAccAzureRMNetworkSecurityGroup_disappears(t *testing.T) { { Config: testAccAzureRMNetworkSecurityGroup_basic(rInt, testLocation()), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMNetworkSecurityGroupExists("azurerm_network_security_group.test"), - testCheckAzureRMNetworkSecurityGroupDisappears("azurerm_network_security_group.test"), + testCheckAzureRMNetworkSecurityGroupExists(resourceName), + testCheckAzureRMNetworkSecurityGroupDisappears(resourceName), ), ExpectNonEmptyPlan: true, }, @@ -47,6 +92,7 @@ func TestAccAzureRMNetworkSecurityGroup_disappears(t *testing.T) { } func TestAccAzureRMNetworkSecurityGroup_withTags(t *testing.T) { + resourceName := "azurerm_network_security_group.test" rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -56,24 +102,19 @@ func TestAccAzureRMNetworkSecurityGroup_withTags(t *testing.T) { { Config: testAccAzureRMNetworkSecurityGroup_withTags(rInt, testLocation()), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMNetworkSecurityGroupExists("azurerm_network_security_group.test"), - resource.TestCheckResourceAttr( - "azurerm_network_security_group.test", "tags.%", "2"), - resource.TestCheckResourceAttr( - "azurerm_network_security_group.test", "tags.environment", "Production"), - resource.TestCheckResourceAttr( - "azurerm_network_security_group.test", "tags.cost_center", "MSFT"), + testCheckAzureRMNetworkSecurityGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.environment", "Production"), + resource.TestCheckResourceAttr(resourceName, "tags.cost_center", "MSFT"), ), }, { Config: testAccAzureRMNetworkSecurityGroup_withTagsUpdate(rInt, testLocation()), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMNetworkSecurityGroupExists("azurerm_network_security_group.test"), - resource.TestCheckResourceAttr( - "azurerm_network_security_group.test", "tags.%", "1"), - resource.TestCheckResourceAttr( - "azurerm_network_security_group.test", "tags.environment", "staging"), + testCheckAzureRMNetworkSecurityGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.environment", "staging"), ), }, }, @@ -81,6 +122,7 @@ func TestAccAzureRMNetworkSecurityGroup_withTags(t *testing.T) { } func TestAccAzureRMNetworkSecurityGroup_addingExtraRules(t *testing.T) { + resourceName := "azurerm_network_security_group.test" rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -88,20 +130,18 @@ func TestAccAzureRMNetworkSecurityGroup_addingExtraRules(t *testing.T) { CheckDestroy: testCheckAzureRMNetworkSecurityGroupDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMNetworkSecurityGroup_basic(rInt, testLocation()), + Config: testAccAzureRMNetworkSecurityGroup_singleRule(rInt, testLocation()), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMNetworkSecurityGroupExists("azurerm_network_security_group.test"), - resource.TestCheckResourceAttr( - "azurerm_network_security_group.test", "security_rule.#", "1"), + testCheckAzureRMNetworkSecurityGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "security_rule.#", "1"), ), }, { Config: testAccAzureRMNetworkSecurityGroup_anotherRule(rInt, testLocation()), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMNetworkSecurityGroupExists("azurerm_network_security_group.test"), - resource.TestCheckResourceAttr( - "azurerm_network_security_group.test", "security_rule.#", "2"), + testCheckAzureRMNetworkSecurityGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "security_rule.#", "2"), ), }, }, @@ -113,24 +153,23 @@ func testCheckAzureRMNetworkSecurityGroupExists(name string) resource.TestCheckF rs, ok := s.RootModule().Resources[name] if !ok { - return fmt.Errorf("Not found: %s", name) + return fmt.Errorf("Not found: %q", name) } sgName := rs.Primary.Attributes["name"] resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for network security group: %s", sgName) + return fmt.Errorf("Bad: no resource group found in state for network security group: %q", sgName) } - conn := testAccProvider.Meta().(*ArmClient).secGroupClient - - resp, err := conn.Get(resourceGroup, sgName, "") + client := testAccProvider.Meta().(*ArmClient).secGroupClient + resp, err := client.Get(resourceGroup, sgName, "") if err != nil { - return fmt.Errorf("Bad: Get on secGroupClient: %+v", err) - } + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Network Security Group %q (resource group: %q) does not exist", name, resourceGroup) + } - if resp.StatusCode == http.StatusNotFound { - return fmt.Errorf("Bad: Network Security Group %q (resource group: %q) does not exist", name, resourceGroup) + return fmt.Errorf("Bad: Get on secGroupClient: %+v", err) } return nil @@ -148,15 +187,17 @@ func testCheckAzureRMNetworkSecurityGroupDisappears(name string) resource.TestCh sgName := rs.Primary.Attributes["name"] resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for network security group: %s", sgName) + return fmt.Errorf("Bad: no resource group found in state for network security group: %q", sgName) } - conn := testAccProvider.Meta().(*ArmClient).secGroupClient - - _, error := conn.Delete(resourceGroup, sgName, make(chan struct{})) - err := <-error + client := testAccProvider.Meta().(*ArmClient).secGroupClient + deleteResp, deleteErr := client.Delete(resourceGroup, sgName, make(chan struct{})) + resp := <-deleteResp + err := <-deleteErr if err != nil { - return fmt.Errorf("Bad: Delete on secGroupClient: %+v", err) + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Bad: Delete on secGroupClient: %+v", err) + } } return nil @@ -164,7 +205,7 @@ func testCheckAzureRMNetworkSecurityGroupDisappears(name string) resource.TestCh } func testCheckAzureRMNetworkSecurityGroupDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*ArmClient).secGroupClient + client := testAccProvider.Meta().(*ArmClient).secGroupClient for _, rs := range s.RootModule().Resources { if rs.Type != "azurerm_network_security_group" { @@ -174,15 +215,16 @@ func testCheckAzureRMNetworkSecurityGroupDestroy(s *terraform.State) error { name := rs.Primary.Attributes["name"] resourceGroup := rs.Primary.Attributes["resource_group_name"] - resp, err := conn.Get(resourceGroup, name, "") + resp, err := client.Get(resourceGroup, name, "") if err != nil { - return nil + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + return err } - if resp.StatusCode != http.StatusNotFound { - return fmt.Errorf("Network Security Group still exists:\n%#v", resp.SecurityGroupPropertiesFormat) - } + return fmt.Errorf("Network Security Group still exists:\n%#v", resp.SecurityGroupPropertiesFormat) } return nil @@ -195,6 +237,21 @@ resource "azurerm_resource_group" "test" { location = "%s" } +resource "azurerm_network_security_group" "test" { + name = "acceptanceTestSecurityGroup1" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, rInt, location) +} + +func testAccAzureRMNetworkSecurityGroup_singleRule(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + resource "azurerm_network_security_group" "test" { name = "acceptanceTestSecurityGroup1" location = "${azurerm_resource_group.test.location}" diff --git a/azurerm/resource_arm_network_security_rule.go b/azurerm/resource_arm_network_security_rule.go index 14e733dc62a4..edcdd3a32ce3 100644 --- a/azurerm/resource_arm_network_security_rule.go +++ b/azurerm/resource_arm_network_security_rule.go @@ -5,6 +5,7 @@ import ( "github.com/Azure/azure-sdk-for-go/arm/network" "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -34,22 +35,20 @@ func resourceArmNetworkSecurityRule() *schema.Resource { }, "description": { - Type: schema.TypeString, - Optional: true, - ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { - value := v.(string) - if len(value) > 140 { - errors = append(errors, fmt.Errorf( - "The network security rule description can be no longer than 140 chars")) - } - return - }, + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateStringLength(140), }, "protocol": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validateNetworkSecurityRuleProtocol, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.SecurityRuleProtocolAsterisk), + string(network.SecurityRuleProtocolTCP), + string(network.SecurityRuleProtocolUDP), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, }, "source_port_range": { @@ -73,36 +72,36 @@ func resourceArmNetworkSecurityRule() *schema.Resource { }, "access": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validateNetworkSecurityRuleAccess, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.SecurityRuleAccessAllow), + string(network.SecurityRuleAccessDeny), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, }, "priority": { - Type: schema.TypeInt, - Required: true, - ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { - value := v.(int) - if value < 100 || value > 4096 { - errors = append(errors, fmt.Errorf( - "The `priority` can only be between 100 and 4096")) - } - return - }, + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(100, 4096), }, "direction": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validateNetworkSecurityRuleDirection, + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(network.SecurityRuleDirectionInbound), + string(network.SecurityRuleDirectionOutbound), + }, true), + DiffSuppressFunc: ignoreCaseDiffSuppressFunc, }, }, } } func resourceArmNetworkSecurityRuleCreate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient) - secClient := client.secRuleClient + client := meta.(*ArmClient).secRuleClient name := d.Get("name").(string) nsgName := d.Get("network_security_group_name").(string) @@ -120,34 +119,32 @@ func resourceArmNetworkSecurityRuleCreate(d *schema.ResourceData, meta interface azureRMLockByName(nsgName, networkSecurityGroupResourceName) defer azureRMUnlockByName(nsgName, networkSecurityGroupResourceName) - properties := network.SecurityRulePropertiesFormat{ - SourcePortRange: &source_port_range, - DestinationPortRange: &destination_port_range, - SourceAddressPrefix: &source_address_prefix, - DestinationAddressPrefix: &destination_address_prefix, - Priority: &priority, - Access: network.SecurityRuleAccess(access), - Direction: network.SecurityRuleDirection(direction), - Protocol: network.SecurityRuleProtocol(protocol), + rule := network.SecurityRule{ + Name: &name, + SecurityRulePropertiesFormat: &network.SecurityRulePropertiesFormat{ + SourcePortRange: &source_port_range, + DestinationPortRange: &destination_port_range, + SourceAddressPrefix: &source_address_prefix, + DestinationAddressPrefix: &destination_address_prefix, + Priority: &priority, + Access: network.SecurityRuleAccess(access), + Direction: network.SecurityRuleDirection(direction), + Protocol: network.SecurityRuleProtocol(protocol), + }, } if v, ok := d.GetOk("description"); ok { description := v.(string) - properties.Description = &description - } - - sgr := network.SecurityRule{ - Name: &name, - SecurityRulePropertiesFormat: &properties, + rule.SecurityRulePropertiesFormat.Description = &description } - _, error := secClient.CreateOrUpdate(resGroup, nsgName, name, sgr, make(chan struct{})) - err := <-error + _, createErr := client.CreateOrUpdate(resGroup, nsgName, name, rule, make(chan struct{})) + err := <-createErr if err != nil { return err } - read, err := secClient.Get(resGroup, nsgName, name) + read, err := client.Get(resGroup, nsgName, name) if err != nil { return err } @@ -161,7 +158,7 @@ func resourceArmNetworkSecurityRuleCreate(d *schema.ResourceData, meta interface } func resourceArmNetworkSecurityRuleRead(d *schema.ResourceData, meta interface{}) error { - secRuleClient := meta.(*ArmClient).secRuleClient + client := meta.(*ArmClient).secRuleClient id, err := parseAzureResourceID(d.Id()) if err != nil { @@ -171,33 +168,35 @@ func resourceArmNetworkSecurityRuleRead(d *schema.ResourceData, meta interface{} networkSGName := id.Path["networkSecurityGroups"] sgRuleName := id.Path["securityRules"] - resp, err := secRuleClient.Get(resGroup, networkSGName, sgRuleName) + resp, err := client.Get(resGroup, networkSGName, sgRuleName) if err != nil { if utils.ResponseWasNotFound(resp.Response) { d.SetId("") return nil } - return fmt.Errorf("Error making Read request on Azure Network Security Rule %s: %s", sgRuleName, err) + return fmt.Errorf("Error making Read request on Azure Network Security Rule %q: %+v", sgRuleName, err) } - d.Set("resource_group_name", resGroup) - d.Set("access", resp.SecurityRulePropertiesFormat.Access) - d.Set("destination_address_prefix", resp.SecurityRulePropertiesFormat.DestinationAddressPrefix) - d.Set("destination_port_range", resp.SecurityRulePropertiesFormat.DestinationPortRange) - d.Set("direction", resp.SecurityRulePropertiesFormat.Direction) - d.Set("description", resp.SecurityRulePropertiesFormat.Description) d.Set("name", resp.Name) - d.Set("priority", resp.SecurityRulePropertiesFormat.Priority) - d.Set("protocol", resp.SecurityRulePropertiesFormat.Protocol) - d.Set("source_address_prefix", resp.SecurityRulePropertiesFormat.SourceAddressPrefix) - d.Set("source_port_range", resp.SecurityRulePropertiesFormat.SourcePortRange) + d.Set("resource_group_name", resGroup) + + if props := resp.SecurityRulePropertiesFormat; props != nil { + d.Set("access", string(props.Access)) + d.Set("destination_address_prefix", props.DestinationAddressPrefix) + d.Set("destination_port_range", props.DestinationPortRange) + d.Set("direction", string(props.Direction)) + d.Set("description", props.Description) + d.Set("priority", int(*props.Priority)) + d.Set("protocol", string(props.Protocol)) + d.Set("source_address_prefix", props.SourceAddressPrefix) + d.Set("source_port_range", props.SourcePortRange) + } return nil } func resourceArmNetworkSecurityRuleDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient) - secRuleClient := client.secRuleClient + client := meta.(*ArmClient).secRuleClient id, err := parseAzureResourceID(d.Id()) if err != nil { @@ -210,8 +209,8 @@ func resourceArmNetworkSecurityRuleDelete(d *schema.ResourceData, meta interface azureRMLockByName(nsgName, networkSecurityGroupResourceName) defer azureRMUnlockByName(nsgName, networkSecurityGroupResourceName) - _, error := secRuleClient.Delete(resGroup, nsgName, sgRuleName, make(chan struct{})) - err = <-error + _, deleteErr := client.Delete(resGroup, nsgName, sgRuleName, make(chan struct{})) + err = <-deleteErr return err } diff --git a/azurerm/resource_arm_network_security_rule_test.go b/azurerm/resource_arm_network_security_rule_test.go index 116b1b193271..966bfd0c16a2 100644 --- a/azurerm/resource_arm_network_security_rule_test.go +++ b/azurerm/resource_arm_network_security_rule_test.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/acctest" "github.com/hashicorp/terraform/helper/resource" "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) func TestAccAzureRMNetworkSecurityRule_basic(t *testing.T) { @@ -28,6 +29,7 @@ func TestAccAzureRMNetworkSecurityRule_basic(t *testing.T) { } func TestAccAzureRMNetworkSecurityRule_disappears(t *testing.T) { + resourceGroup := "azurerm_network_security_rule.test" rInt := acctest.RandInt() resource.Test(t, resource.TestCase{ @@ -38,8 +40,8 @@ func TestAccAzureRMNetworkSecurityRule_disappears(t *testing.T) { { Config: testAccAzureRMNetworkSecurityRule_basic(rInt, testLocation()), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMNetworkSecurityRuleExists("azurerm_network_security_rule.test"), - testCheckAzureRMNetworkSecurityRuleDisappears("azurerm_network_security_rule.test"), + testCheckAzureRMNetworkSecurityRuleExists(resourceGroup), + testCheckAzureRMNetworkSecurityRuleDisappears(resourceGroup), ), ExpectNonEmptyPlan: true, }, @@ -84,20 +86,19 @@ func testCheckAzureRMNetworkSecurityRuleExists(name string) resource.TestCheckFu sgrName := rs.Primary.Attributes["name"] resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] if !hasResourceGroup { - return fmt.Errorf("Bad: no resource group found in state for network security rule: %s", sgName) + return fmt.Errorf("Bad: no resource group found in state for network security rule: %q", sgName) } conn := testAccProvider.Meta().(*ArmClient).secRuleClient resp, err := conn.Get(resourceGroup, sgName, sgrName) if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Network Security Rule %q (resource group: %q) (network security group: %q) does not exist", sgrName, sgName, resourceGroup) + } return fmt.Errorf("Bad: Get on secRuleClient: %+v", err) } - if resp.StatusCode == http.StatusNotFound { - return fmt.Errorf("Bad: Network Security Rule %q (resource group: %q) (network security group: %q) does not exist", sgrName, sgName, resourceGroup) - } - return nil } } @@ -107,7 +108,7 @@ func testCheckAzureRMNetworkSecurityRuleDisappears(name string) resource.TestChe rs, ok := s.RootModule().Resources[name] if !ok { - return fmt.Errorf("Not found: %s", name) + return fmt.Errorf("Not found: %q", name) } sgName := rs.Primary.Attributes["network_security_group_name"] @@ -117,12 +118,14 @@ func testCheckAzureRMNetworkSecurityRuleDisappears(name string) resource.TestChe return fmt.Errorf("Bad: no resource group found in state for network security rule: %s", sgName) } - conn := testAccProvider.Meta().(*ArmClient).secRuleClient - - _, error := conn.Delete(resourceGroup, sgName, sgrName, make(chan struct{})) - err := <-error + client := testAccProvider.Meta().(*ArmClient).secRuleClient + deleteResp, deleteErr := client.Delete(resourceGroup, sgName, sgrName, make(chan struct{})) + resp := <-deleteResp + err := <-deleteErr if err != nil { - return fmt.Errorf("Bad: Delete on secRuleClient: %+v", err) + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Bad: Delete on secRuleClient: %+v", err) + } } return nil @@ -182,7 +185,6 @@ resource "azurerm_network_security_rule" "test" { resource_group_name = "${azurerm_resource_group.test.name}" network_security_group_name = "${azurerm_network_security_group.test.name}" } - `, rInt, location) } diff --git a/azurerm/validators.go b/azurerm/validators.go index f224c8a4eb2b..a6405ed8ea6b 100644 --- a/azurerm/validators.go +++ b/azurerm/validators.go @@ -63,3 +63,14 @@ func validateDBAccountName(v interface{}, k string) (ws []string, errors []error return } + +func validateStringLength(maxLength int) schema.SchemaValidateFunc { + return func(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + if len(value) > maxLength { + errors = append(errors, fmt.Errorf( + "The %q can be no longer than %d chars", k, maxLength)) + } + return + } +} diff --git a/website/docs/r/network_security_group.html.markdown b/website/docs/r/network_security_group.html.markdown index 44c6522ca5d3..1e2e9df731bd 100644 --- a/website/docs/r/network_security_group.html.markdown +++ b/website/docs/r/network_security_group.html.markdown @@ -3,12 +3,13 @@ layout: "azurerm" page_title: "Azure Resource Manager: azurerm_network_security_group" sidebar_current: "docs-azurerm-resource-network-security-group" description: |- - Create a network security group that contains a list of network security rules. Network security groups enable inbound or outbound traffic to be enabled or denied. + Manages a network security group that contains a list of network security rules. Network security groups enable inbound or outbound traffic to be enabled or denied. + --- -# azurerm\_network\_security\_group +# azurerm_network_security_group -Create a network security group that contains a list of network security rules. +Manages a network security group that contains a list of network security rules. Network security groups enable inbound or outbound traffic to be enabled or denied. ~> **NOTE on Network Security Groups and Network Security Rules:** Terraform currently provides both a standalone [Network Security Rule resource](network_security_rule.html), and allows for Network Security Rules to be defined in-line within the [Network Security Group resource](network_security_group.html). @@ -24,7 +25,7 @@ resource "azurerm_resource_group" "test" { resource "azurerm_network_security_group" "test" { name = "acceptanceTestSecurityGroup1" - location = "West US" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" security_rule { @@ -49,16 +50,13 @@ resource "azurerm_network_security_group" "test" { The following arguments are supported: -* `name` - (Required) Specifies the name of the network security group. Changing this forces a - new resource to be created. +* `name` - (Required) Specifies the name of the network security group. Changing this forces a new resource to be created. -* `resource_group_name` - (Required) The name of the resource group in which to - create the availability set. +* `resource_group_name` - (Required) The name of the resource group in which to create the availability set. Changing this forces a new resource to be created. * `location` - (Required) Specifies the supported Azure location where the resource exists. Changing this forces a new resource to be created. -* `security_rule` - (Optional) Can be specified multiple times to define multiple - security rules. Each `security_rule` block supports fields documented below. +* `security_rule` - (Optional) One or more `security_rule` blocks as defined below. * `tags` - (Optional) A mapping of tags to assign to the resource. @@ -69,21 +67,21 @@ The `security_rule` block supports: * `description` - (Optional) A description for this rule. Restricted to 140 characters. -* `protocol` - (Required) Network protocol this rule applies to. Can be Tcp, Udp or * to match both. +* `protocol` - (Required) Network protocol this rule applies to. Can be `Tcp`, `Udp` or `*` to match both. -* `source_port_range` - (Required) Source Port or Range. Integer or range between 0 and 65535 or * to match any. +* `source_port_range` - (Required) Source Port or Range. Integer or range between `0` and `65535` or `*` to match any. -* `destination_port_range` - (Required) Destination Port or Range. Integer or range between 0 and 65535 or * to match any. +* `destination_port_range` - (Required) Destination Port or Range. Integer or range between `0` and `65535` or `*` to match any. -* `source_address_prefix` - (Required) CIDR or source IP range or * to match any IP. Tags such as ‘VirtualNetwork’, ‘AzureLoadBalancer’ and ‘Internet’ can also be used. +* `source_address_prefix` - (Required) CIDR or source IP range or * to match any IP. Tags such as `VirtualNetwork`, `AzureLoadBalancer` and `Internet` can also be used. -* `destination_address_prefix` - (Required) CIDR or destination IP range or * to match any IP. Tags such as ‘VirtualNetwork’, ‘AzureLoadBalancer’ and ‘Internet’ can also be used. +* `destination_address_prefix` - (Required) CIDR or destination IP range or * to match any IP. Tags such as `VirtualNetwork`, `AzureLoadBalancer` and `Internet` can also be used. -* `access` - (Required) Specifies whether network traffic is allowed or denied. Possible values are "Allow” and "Deny”. +* `access` - (Required) Specifies whether network traffic is allowed or denied. Possible values are `Allow` and `Deny`. * `priority` - (Required) Specifies the priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule. -* `direction` - (Required) The direction specifies if rule will be evaluated on incoming or outgoing traffic. Possible values are "Inbound” and "Outbound”. +* `direction` - (Required) The direction specifies if rule will be evaluated on incoming or outgoing traffic. Possible values are `Inbound` and `Outbound`. ## Attributes Reference diff --git a/website/docs/r/network_security_rule.html.markdown b/website/docs/r/network_security_rule.html.markdown index ac65de7998e4..7f1eb92047fc 100644 --- a/website/docs/r/network_security_rule.html.markdown +++ b/website/docs/r/network_security_rule.html.markdown @@ -3,12 +3,13 @@ layout: "azurerm" page_title: "Azure Resource Manager: azurerm_network_security_rule" sidebar_current: "docs-azurerm-resource-network-security-rule" description: |- - Create a Network Security Rule. + Manages a Network Security Rule. + --- -# azurerm\_network\_security\_rule +# azurerm_network_security_rule -Create a Network Security Rule. +Manages a Network Security Rule. ~> **NOTE on Network Security Groups and Network Security Rules:** Terraform currently provides both a standalone [Network Security Rule resource](network_security_rule.html), and allows for Network Security Rules to be defined in-line within the [Network Security Group resource](network_security_group.html). @@ -24,7 +25,7 @@ resource "azurerm_resource_group" "test" { resource "azurerm_network_security_group" "test" { name = "acceptanceTestSecurityGroup1" - location = "West US" + location = "${azurerm_resource_group.test.location}" resource_group_name = "${azurerm_resource_group.test.name}" } @@ -49,28 +50,27 @@ The following arguments are supported: * `name` - (Required) The name of the security rule. This needs to be unique across all Rules in the Network Security Group. Changing this forces a new resource to be created. -* `resource_group_name` - (Required) The name of the resource group in which to - create the Network Security Rule. +* `resource_group_name` - (Required) The name of the resource group in which to create the Network Security Rule. Changing this forces a new resource to be created. -* `network_security_group_name` - (Required) The name of the Network Security Group that we want to attach the rule to. +* `network_security_group_name` - (Required) The name of the Network Security Group that we want to attach the rule to. Changing this forces a new resource to be created. * `description` - (Optional) A description for this rule. Restricted to 140 characters. -* `protocol` - (Required) Network protocol this rule applies to. Can be Tcp, Udp or * to match both. +* `protocol` - (Required) Network protocol this rule applies to. Possible values include `Tcp`, `Udp` or `*` (which matches both). -* `source_port_range` - (Required) Source Port or Range. Integer or range between 0 and 65535 or * to match any. +* `source_port_range` - (Required) Source Port or Range. Integer or range between `0` and `65535` or `*` to match any. -* `destination_port_range` - (Required) Destination Port or Range. Integer or range between 0 and 65535 or * to match any. +* `destination_port_range` - (Required) Destination Port or Range. Integer or range between `0` and `65535` or `*` to match any. * `source_address_prefix` - (Required) CIDR or source IP range or * to match any IP. Tags such as ‘VirtualNetwork’, ‘AzureLoadBalancer’ and ‘Internet’ can also be used. * `destination_address_prefix` - (Required) CIDR or destination IP range or * to match any IP. Tags such as ‘VirtualNetwork’, ‘AzureLoadBalancer’ and ‘Internet’ can also be used. -* `access` - (Required) Specifies whether network traffic is allowed or denied. Possible values are "Allow” and "Deny”. +* `access` - (Required) Specifies whether network traffic is allowed or denied. Possible values are `Allow` and `Deny`. * `priority` - (Required) Specifies the priority of the rule. The value can be between 100 and 4096. The priority number must be unique for each rule in the collection. The lower the priority number, the higher the priority of the rule. -* `direction` - (Required) The direction specifies if rule will be evaluated on incoming or outgoing traffic. Possible values are "Inbound” and "Outbound”. +* `direction` - (Required) The direction specifies if rule will be evaluated on incoming or outgoing traffic. Possible values are `Inbound` and `Outbound`. ## Attributes Reference