From c4cec0736fd48cbcb146a5a1923920f580f8fa0f Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Wed, 4 Dec 2019 16:45:23 -0800 Subject: [PATCH 1/6] Start of storage account network rule set --- azurerm/provider.go | 1 + azurerm/resource_arm_storage_account.go | 9 + ...source_arm_storage_account_network_rule.go | 277 ++++++++++++++++++ ...e_arm_storage_account_network_rule_test.go | 80 +++++ 4 files changed, 367 insertions(+) create mode 100644 azurerm/resource_arm_storage_account_network_rule.go create mode 100644 azurerm/resource_arm_storage_account_network_rule_test.go diff --git a/azurerm/provider.go b/azurerm/provider.go index 0dd643d69a38..e7c1f8b15a94 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -454,6 +454,7 @@ func Provider() terraform.ResourceProvider { "azurerm_sql_server": resourceArmSqlServer(), "azurerm_sql_virtual_network_rule": resourceArmSqlVirtualNetworkRule(), "azurerm_storage_account": resourceArmStorageAccount(), + "azurerm_storage_account_network_rule": resourceArmStorageAccountNetworkRule(), "azurerm_storage_blob": resourceArmStorageBlob(), "azurerm_storage_container": resourceArmStorageContainer(), "azurerm_storage_data_lake_gen2_filesystem": resourceArmStorageDataLakeGen2FileSystem(), diff --git a/azurerm/resource_arm_storage_account.go b/azurerm/resource_arm_storage_account.go index 37bee03f7fa6..f6c7dcf120b3 100644 --- a/azurerm/resource_arm_storage_account.go +++ b/azurerm/resource_arm_storage_account.go @@ -625,6 +625,9 @@ func resourceArmStorageAccountCreate(d *schema.ResourceData, meta interface{}) e storageAccountName := d.Get("name").(string) resourceGroupName := d.Get("resource_group_name").(string) + locks.ByName(storageAccountName, storageAccountResourceName) + defer locks.UnlockByName(storageAccountName, storageAccountResourceName) + if features.ShouldResourcesBeImported() { existing, err := client.GetProperties(ctx, resourceGroupName, storageAccountName, "") if err != nil { @@ -795,6 +798,9 @@ func resourceArmStorageAccountUpdate(d *schema.ResourceData, meta interface{}) e storageAccountName := id.Path["storageAccounts"] resourceGroupName := id.ResourceGroup + locks.ByName(storageAccountName, iothubResourceName) + defer locks.UnlockByName(storageAccountName, iothubResourceName) + accountTier := d.Get("account_tier").(string) replicationType := d.Get("account_replication_type").(string) storageType := fmt.Sprintf("%s_%s", accountTier, replicationType) @@ -1188,6 +1194,9 @@ func resourceArmStorageAccountDelete(d *schema.ResourceData, meta interface{}) e name := id.Path["storageAccounts"] resourceGroup := id.ResourceGroup + locks.ByName(name, storageAccountResourceName) + defer locks.UnlockByName(name, storageAccountResourceName) + read, err := client.GetProperties(ctx, resourceGroup, name, "") if err != nil { if utils.ResponseWasNotFound(read.Response) { diff --git a/azurerm/resource_arm_storage_account_network_rule.go b/azurerm/resource_arm_storage_account_network_rule.go new file mode 100644 index 000000000000..ca5182f57365 --- /dev/null +++ b/azurerm/resource_arm_storage_account_network_rule.go @@ -0,0 +1,277 @@ +package azurerm + +import ( + "fmt" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +var storageAccountResourceName = "azurerm_storage_account" + +func resourceArmStorageAccountNetworkRule() *schema.Resource { + return &schema.Resource{ + Create: resourceArmStorageAccountNetworkRuleCreateUpdate, + Read: resourceArmStorageAccountNetworkRuleRead, + Update: resourceArmStorageAccountNetworkRuleCreateUpdate, + Delete: resourceArmStorageAccountNetworkRuleDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "resource_group_name": azure.SchemaResourceGroupName(), + + "storage_account_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArmStorageAccountName, + }, + + "bypass": { + Type: schema.TypeSet, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice([]string{ + string(storage.AzureServices), + string(storage.Logging), + string(storage.Metrics), + string(storage.None), + }, true), + }, + Set: schema.HashString, + }, + + "ip_rules": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "virtual_network_subnet_ids": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "default_action": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(storage.DefaultActionAllow), + string(storage.DefaultActionDeny), + }, false), + }, + }, + } +} + +func resourceArmStorageAccountNetworkRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Storage.AccountsClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*ArmClient).StopContext, d) + defer cancel() + + storageAccountName := d.Get("storage_account_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + locks.ByName(storageAccountName, storageAccountResourceName) + defer locks.UnlockByName(storageAccountName, storageAccountResourceName) + + storageAccount, err := client.GetProperties(ctx, resourceGroup, storageAccountName, "") + if err != nil { + if utils.ResponseWasNotFound(storageAccount.Response) { + return fmt.Errorf("Storage Account %q (Resource Group %q) was not found", storageAccountName, resourceGroup) + } + + return fmt.Errorf("Error loading Storage Account %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err) + } + + if features.ShouldResourcesBeImported() { + if checkForNonDefaultStorageAccountNetworkRule(storageAccount.NetworkRuleSet) { + return tf.ImportAsExistsError("azurerm_storage_account_network_rule", *storageAccount.ID) + } + } + + resourceId := fmt.Sprintf("%s/NetworkRules", *storageAccount.ID) + + rules := storageAccount.NetworkRuleSet + if rules == nil { + rules = &storage.NetworkRuleSet{} + } + + rules.DefaultAction = storage.DefaultAction(d.Get("default_action").(string)) + + if v, ok := d.GetOk("bypass"); ok { + rules.Bypass = expandStorageAccountNetworkRuleBypass(v.(*schema.Set).List()) + } + + if v, ok := d.GetOk("ip_rules"); ok { + rules.IPRules = expandStorageAccountNetworkRuleIpRules(v.([]interface{})) + } + + if v, ok := d.GetOk("virtual_network_subnet_ids"); ok { + rules.VirtualNetworkRules = expandStorageAccountNetworkRuleVirtualRules(v.([]interface{})) + } + + opts := storage.AccountUpdateParameters{ + AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ + NetworkRuleSet: rules, + }, + } + + if _, err := client.Update(ctx, resourceGroup, storageAccountName, opts); err != nil { + return fmt.Errorf("Error updating Azure Storage Account Network Rules %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err) + } + + d.SetId(resourceId) + + return resourceArmStorageAccountNetworkRuleRead(d, meta) +} + +func resourceArmStorageAccountNetworkRuleRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Storage.AccountsClient + ctx, cancel := timeouts.ForRead(meta.(*ArmClient).StopContext, d) + defer cancel() + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := id.ResourceGroup + storageAccountName := id.Path["storageAccounts"] + + storageAccount, err := client.GetProperties(ctx, resourceGroup, storageAccountName, "") + if err != nil { + return fmt.Errorf("Error reading Storage Account Network Rules %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err) + } + + d.Set("storage_account_name", storageAccountName) + d.Set("resource_group_name", resourceGroup) + + if rules := storageAccount.NetworkRuleSet; rules != nil { + if err := d.Set("ip_rules", flattenStorageAccountIPRules(rules.IPRules)); err != nil { + return fmt.Errorf("Error setting `ip_rules`: %+v", err) + } + if err := d.Set("virtual_network_subnet_ids", flattenStorageAccountVirtualNetworks(rules.VirtualNetworkRules)); err != nil { + return fmt.Errorf("Error setting `virtual_network_subnet_ids`: %+v", err) + } + d.Set("bypass", schema.NewSet(schema.HashString, flattenStorageAccountBypass(rules.Bypass))) + d.Set("default_action", string(rules.DefaultAction)) + } + + return nil +} + +func resourceArmStorageAccountNetworkRuleDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).Storage.AccountsClient + ctx, cancel := timeouts.ForDelete(meta.(*ArmClient).StopContext, d) + defer cancel() + + parsedStorageAccountNetworkRuleId, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + resourceGroup := parsedStorageAccountNetworkRuleId.ResourceGroup + storageAccountName := parsedStorageAccountNetworkRuleId.Path["storageAccounts"] + + locks.ByName(storageAccountName, storageAccountResourceName) + defer locks.UnlockByName(storageAccountName, storageAccountResourceName) + + storageAccount, err := client.GetProperties(ctx, resourceGroup, storageAccountName, "") + if err != nil { + if utils.ResponseWasNotFound(storageAccount.Response) { + return fmt.Errorf("Storage Account %q (Resource Group %q) was not found", storageAccountName, resourceGroup) + } + + return fmt.Errorf("Error loading Storage Account %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err) + } + + if storageAccount.NetworkRuleSet == nil { + return nil + } + + // We can't delete a network rule set so we'll just update it back to the default instead + opts := storage.AccountUpdateParameters{ + AccountPropertiesUpdateParameters: &storage.AccountPropertiesUpdateParameters{ + NetworkRuleSet: &storage.NetworkRuleSet{ + Bypass: storage.AzureServices, + DefaultAction: storage.DefaultActionDeny, + }, + }, + } + + if _, err := client.Update(ctx, resourceGroup, storageAccountName, opts); err != nil { + return fmt.Errorf("Error deleting Azure Storage Account Network Rule %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err) + } + + return nil +} + +// To make sure that someone isn't overriding their existing network rules, we'll check for a non default network rule +func checkForNonDefaultStorageAccountNetworkRule(rule *storage.NetworkRuleSet) bool { + if rule == nil { + return false + } + + if rule.IPRules != nil || len(*rule.IPRules) != 0 || + rule.VirtualNetworkRules != nil || len(*rule.VirtualNetworkRules) == 0 || + rule.Bypass != "AzureServices" || rule.DefaultAction != "Allow" { + return true + } + + return false +} + +func expandStorageAccountNetworkRuleBypass(bypass []interface{}) storage.Bypass { + var bypassValues []string + for _, bypassConfig := range bypass { + bypassValues = append(bypassValues, bypassConfig.(string)) + } + + return storage.Bypass(strings.Join(bypassValues, ", ")) +} + +func expandStorageAccountNetworkRuleIpRules(ipRulesInfo []interface{}) *[]storage.IPRule { + ipRules := make([]storage.IPRule, len(ipRulesInfo)) + + for i, ipRuleConfig := range ipRulesInfo { + attrs := ipRuleConfig.(string) + ipRule := storage.IPRule{ + IPAddressOrRange: utils.String(attrs), + Action: storage.Allow, + } + ipRules[i] = ipRule + } + + return &ipRules +} + +func expandStorageAccountNetworkRuleVirtualRules(virtualNetworkInfo []interface{}) *[]storage.VirtualNetworkRule { + virtualNetworks := make([]storage.VirtualNetworkRule, len(virtualNetworkInfo)) + + for i, virtualNetworkConfig := range virtualNetworkInfo { + attrs := virtualNetworkConfig.(string) + virtualNetwork := storage.VirtualNetworkRule{ + VirtualNetworkResourceID: utils.String(attrs), + Action: storage.Allow, + } + virtualNetworks[i] = virtualNetwork + } + + return &virtualNetworks +} diff --git a/azurerm/resource_arm_storage_account_network_rule_test.go b/azurerm/resource_arm_storage_account_network_rule_test.go new file mode 100644 index 000000000000..b617f596f8a2 --- /dev/null +++ b/azurerm/resource_arm_storage_account_network_rule_test.go @@ -0,0 +1,80 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMStorageAccountNetworkRule_basic(t *testing.T) { + resourceName := "azurerm_storage_account_network_rule.test" + rInt := tf.AccRandTimeInt() + rs := acctest.RandString(4) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageAccountNetworkRule_basic(rInt, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists("azurerm_storage_account.test"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMStorageAccountNetworkRule_basic(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestRG-storage-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.testrg.location}" + resource_group_name = "${azurerm_resource_group.testrg.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctestsubnet%d" + resource_group_name = "${azurerm_resource_group.testrg.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" + service_endpoints = ["Microsoft.Storage"] +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags = { + environment = "production" + } +} + +resource "azurerm_storage_account_network_rule" "test" { + resource_group_name = "${azurerm_resource_group.testrg.name}" + storage_account_name = "${azurerm_storage_account.testsa.name}" + + default_action = "Deny" + ip_rules = ["127.0.0.1"] + virtual_network_subnet_ids = ["${azurerm_subnet.test.id}"] +} +`, rInt, location, rInt, rInt, rString) +} From fa0f4ab72df9accbac7824926edbc9b82c64e7aa Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 5 Dec 2019 08:52:47 -0800 Subject: [PATCH 2/6] Finalizing storage account network rules --- azurerm/provider.go | 2 +- ...e_arm_storage_account_network_rule_test.go | 80 --------- ...urce_arm_storage_account_network_rules.go} | 22 ++- ..._arm_storage_account_network_rules_test.go | 162 ++++++++++++++++++ website/azurerm.erb | 4 + website/docs/r/storage_account.html.markdown | 2 + ...torage_account_network_rules.html.markdown | 95 ++++++++++ 7 files changed, 274 insertions(+), 93 deletions(-) delete mode 100644 azurerm/resource_arm_storage_account_network_rule_test.go rename azurerm/{resource_arm_storage_account_network_rule.go => resource_arm_storage_account_network_rules.go} (92%) create mode 100644 azurerm/resource_arm_storage_account_network_rules_test.go create mode 100644 website/docs/r/storage_account_network_rules.html.markdown diff --git a/azurerm/provider.go b/azurerm/provider.go index e7c1f8b15a94..e3ef4f8ec745 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -454,7 +454,7 @@ func Provider() terraform.ResourceProvider { "azurerm_sql_server": resourceArmSqlServer(), "azurerm_sql_virtual_network_rule": resourceArmSqlVirtualNetworkRule(), "azurerm_storage_account": resourceArmStorageAccount(), - "azurerm_storage_account_network_rule": resourceArmStorageAccountNetworkRule(), + "azurerm_storage_account_network_rules": resourceArmStorageAccountNetworkRules(), "azurerm_storage_blob": resourceArmStorageBlob(), "azurerm_storage_container": resourceArmStorageContainer(), "azurerm_storage_data_lake_gen2_filesystem": resourceArmStorageDataLakeGen2FileSystem(), diff --git a/azurerm/resource_arm_storage_account_network_rule_test.go b/azurerm/resource_arm_storage_account_network_rule_test.go deleted file mode 100644 index b617f596f8a2..000000000000 --- a/azurerm/resource_arm_storage_account_network_rule_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package azurerm - -import ( - "fmt" - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" -) - -func TestAccAzureRMStorageAccountNetworkRule_basic(t *testing.T) { - resourceName := "azurerm_storage_account_network_rule.test" - rInt := tf.AccRandTimeInt() - rs := acctest.RandString(4) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMStorageAccountDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAzureRMStorageAccountNetworkRule_basic(rInt, rs, testLocation()), - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMStorageAccountExists("azurerm_storage_account.test"), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func testAccAzureRMStorageAccountNetworkRule_basic(rInt int, rString string, location string) string { - return fmt.Sprintf(` -resource "azurerm_resource_group" "testrg" { - name = "acctestRG-storage-%d" - location = "%s" -} - -resource "azurerm_virtual_network" "test" { - name = "acctestvirtnet%d" - address_space = ["10.0.0.0/16"] - location = "${azurerm_resource_group.testrg.location}" - resource_group_name = "${azurerm_resource_group.testrg.name}" -} - -resource "azurerm_subnet" "test" { - name = "acctestsubnet%d" - resource_group_name = "${azurerm_resource_group.testrg.name}" - virtual_network_name = "${azurerm_virtual_network.test.name}" - address_prefix = "10.0.2.0/24" - service_endpoints = ["Microsoft.Storage"] -} - -resource "azurerm_storage_account" "testsa" { - name = "unlikely23exst2acct%s" - resource_group_name = "${azurerm_resource_group.testrg.name}" - location = "${azurerm_resource_group.testrg.location}" - account_tier = "Standard" - account_replication_type = "LRS" - - tags = { - environment = "production" - } -} - -resource "azurerm_storage_account_network_rule" "test" { - resource_group_name = "${azurerm_resource_group.testrg.name}" - storage_account_name = "${azurerm_storage_account.testsa.name}" - - default_action = "Deny" - ip_rules = ["127.0.0.1"] - virtual_network_subnet_ids = ["${azurerm_subnet.test.id}"] -} -`, rInt, location, rInt, rInt, rString) -} diff --git a/azurerm/resource_arm_storage_account_network_rule.go b/azurerm/resource_arm_storage_account_network_rules.go similarity index 92% rename from azurerm/resource_arm_storage_account_network_rule.go rename to azurerm/resource_arm_storage_account_network_rules.go index ca5182f57365..a1e500adf603 100644 --- a/azurerm/resource_arm_storage_account_network_rule.go +++ b/azurerm/resource_arm_storage_account_network_rules.go @@ -17,12 +17,12 @@ import ( var storageAccountResourceName = "azurerm_storage_account" -func resourceArmStorageAccountNetworkRule() *schema.Resource { +func resourceArmStorageAccountNetworkRules() *schema.Resource { return &schema.Resource{ - Create: resourceArmStorageAccountNetworkRuleCreateUpdate, - Read: resourceArmStorageAccountNetworkRuleRead, - Update: resourceArmStorageAccountNetworkRuleCreateUpdate, - Delete: resourceArmStorageAccountNetworkRuleDelete, + Create: resourceArmStorageAccountNetworkRulesCreateUpdate, + Read: resourceArmStorageAccountNetworkRulesRead, + Update: resourceArmStorageAccountNetworkRulesCreateUpdate, + Delete: resourceArmStorageAccountNetworkRulesDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -79,7 +79,7 @@ func resourceArmStorageAccountNetworkRule() *schema.Resource { } } -func resourceArmStorageAccountNetworkRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceArmStorageAccountNetworkRulesCreateUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).Storage.AccountsClient ctx, cancel := timeouts.ForCreateUpdate(meta.(*ArmClient).StopContext, d) defer cancel() @@ -105,8 +105,6 @@ func resourceArmStorageAccountNetworkRuleCreateUpdate(d *schema.ResourceData, me } } - resourceId := fmt.Sprintf("%s/NetworkRules", *storageAccount.ID) - rules := storageAccount.NetworkRuleSet if rules == nil { rules = &storage.NetworkRuleSet{} @@ -136,12 +134,12 @@ func resourceArmStorageAccountNetworkRuleCreateUpdate(d *schema.ResourceData, me return fmt.Errorf("Error updating Azure Storage Account Network Rules %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err) } - d.SetId(resourceId) + d.SetId(*storageAccount.ID) - return resourceArmStorageAccountNetworkRuleRead(d, meta) + return resourceArmStorageAccountNetworkRulesRead(d, meta) } -func resourceArmStorageAccountNetworkRuleRead(d *schema.ResourceData, meta interface{}) error { +func resourceArmStorageAccountNetworkRulesRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).Storage.AccountsClient ctx, cancel := timeouts.ForRead(meta.(*ArmClient).StopContext, d) defer cancel() @@ -176,7 +174,7 @@ func resourceArmStorageAccountNetworkRuleRead(d *schema.ResourceData, meta inter return nil } -func resourceArmStorageAccountNetworkRuleDelete(d *schema.ResourceData, meta interface{}) error { +func resourceArmStorageAccountNetworkRulesDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*ArmClient).Storage.AccountsClient ctx, cancel := timeouts.ForDelete(meta.(*ArmClient).StopContext, d) defer cancel() diff --git a/azurerm/resource_arm_storage_account_network_rules_test.go b/azurerm/resource_arm_storage_account_network_rules_test.go new file mode 100644 index 000000000000..b80ff60ef075 --- /dev/null +++ b/azurerm/resource_arm_storage_account_network_rules_test.go @@ -0,0 +1,162 @@ +package azurerm + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMStorageAccountNetworkRules_basic(t *testing.T) { + resourceName := "azurerm_storage_account_network_rules.test" + rInt := tf.AccRandTimeInt() + rs := acctest.RandString(4) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageAccountNetworkRules_basic(rInt, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists("azurerm_storage_account.testsa"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageAccountNetworkRules_update(t *testing.T) { + resourceName := "azurerm_storage_account_network_rules.test" + rInt := tf.AccRandTimeInt() + rs := acctest.RandString(4) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageAccountNetworkRules_basic(rInt, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists("azurerm_storage_account.testsa"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMStorageAccountNetworkRules_update(rInt, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists("azurerm_storage_account.testsa"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAzureRMStorageAccountNetworkRules_basic(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestRG-storage-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.testrg.location}" + resource_group_name = "${azurerm_resource_group.testrg.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctestsubnet%d" + resource_group_name = "${azurerm_resource_group.testrg.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" + service_endpoints = ["Microsoft.Storage"] +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags = { + environment = "production" + } +} + +resource "azurerm_storage_account_network_rules" "test" { + resource_group_name = "${azurerm_resource_group.testrg.name}" + storage_account_name = "${azurerm_storage_account.testsa.name}" + + default_action = "Deny" + ip_rules = ["127.0.0.1"] + virtual_network_subnet_ids = ["${azurerm_subnet.test.id}"] +} +`, rInt, location, rInt, rInt, rString) +} + +func testAccAzureRMStorageAccountNetworkRules_update(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestRG-storage-%d" + location = "%s" +} + +resource "azurerm_virtual_network" "test" { + name = "acctestvirtnet%d" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.testrg.location}" + resource_group_name = "${azurerm_resource_group.testrg.name}" +} + +resource "azurerm_subnet" "test" { + name = "acctestsubnet%d" + resource_group_name = "${azurerm_resource_group.testrg.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.2.0/24" + service_endpoints = ["Microsoft.Storage"] +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags = { + environment = "production" + } +} + +resource "azurerm_storage_account_network_rules" "test" { + resource_group_name = "${azurerm_resource_group.testrg.name}" + storage_account_name = "${azurerm_storage_account.testsa.name}" + + default_action = "Allow" + ip_rules = ["127.0.0.1"] + virtual_network_subnet_ids = ["${azurerm_subnet.test.id}"] + bypass = ["Metrics"] +} +`, rInt, location, rInt, rInt, rString) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index ef2fd79eb8a6..436afbfe6b34 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -1959,6 +1959,10 @@ azurerm_storage_account +
  • + azurerm_storage_account_network_rules +
  • +
  • azurerm_storage_blob
  • diff --git a/website/docs/r/storage_account.html.markdown b/website/docs/r/storage_account.html.markdown index 3fb65883970d..3e9a6626b4f3 100644 --- a/website/docs/r/storage_account.html.markdown +++ b/website/docs/r/storage_account.html.markdown @@ -200,6 +200,8 @@ any combination of `Logging`, `Metrics`, `AzureServices`, or `None`. ~> **Note:** If specifying `network_rules`, one of either `ip_rules` or `virtual_network_subnet_ids` must be specified and `default_action` must be set to `Deny`. +~> **NOTE:** Network Rules can be defined either directly on the `azurerm_storage_account` resource, or using the `azurerm_storage_account_network_rules` resource - but the two cannot be used together. If both are used against the same Storage Account, spurious changes will occur. + ~> **Note:** [More information on Validation is available here](https://docs.microsoft.com/en-gb/azure/storage/blobs/storage-custom-domain-name) --- diff --git a/website/docs/r/storage_account_network_rules.html.markdown b/website/docs/r/storage_account_network_rules.html.markdown new file mode 100644 index 000000000000..dd11cae81cf4 --- /dev/null +++ b/website/docs/r/storage_account_network_rules.html.markdown @@ -0,0 +1,95 @@ +--- +subcategory: "Storage" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_storage_account_network_rules" +sidebar_current: "docs-azurerm-resource-storage-account-network-rules" +description: |- + Manages network rules inside of a Azure Storage Account. +--- + +# azurerm_storage_account + +Manages network rules inside of a Azure Storage Account. + +~> **NOTE:** Network Rules can be defined either directly on the `azurerm_storage_account` resource, or using the `azurerm_storage_account_network_rules` resource - but the two cannot be used together. Spurious changes will occur if both are used against the same Storage Account. + +~> **NOTE:** Only one `azurerm_storage_account_network_rules` can be tied to an `azurerm_storage_account`. Spurious changes will occur if more than `azurerm_storage_account_network_rules` is tied to the same `azurerm_storage_account`. + +~> **NOTE:** Deleting this resource updates the storage account back to the default values it had when the storage account was created. + +## Example Usage + +```hcl +resource "azurerm_resource_group" "example" { + name = "example-resources" + location = "West Europe" +} + +resource "azurerm_virtual_network" "example" { + name = "example-vnet" + address_space = ["10.0.0.0/16"] + location = "${azurerm_resource_group.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" +} + +resource "azurerm_subnet" "example" { + name = "example-subnet" + resource_group_name = "${azurerm_resource_group.example.name}" + virtual_network_name = "${azurerm_virtual_network.example.name}" + address_prefix = "10.0.2.0/24" + service_endpoints = ["Microsoft.Storage"] +} + +resource "azurerm_storage_account" "example" { + name = "storageaccountname" + resource_group_name = "${azurerm_resource_group.example.name}" + location = "${azurerm_resource_group.example.location}" + account_tier = "Standard" + account_replication_type = "GRS" + + tags = { + environment = "staging" + } +} + +resource "azurerm_storage_account_network_rules" "test" { + resource_group_name = "${azurerm_resource_group.testrg.name}" + storage_account_name = "${azurerm_storage_account.testsa.name}" + + default_action = "Allow" + ip_rules = ["127.0.0.1"] + virtual_network_subnet_ids = ["${azurerm_subnet.test.id}"] + bypass = ["Metrics"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `storage_account_name` - (Required) Specifies the name of the storage account. Changing this forces a new resource to be created. This must be unique across the entire Azure service, not just within the resource group. + +* `resource_group_name` - (Required) The name of the resource group in which to create the storage account. Changing this forces a new resource to be created. + +* `default_action` - (Required) Specifies the default action of allow or deny when no other rules match. Valid options are `Deny` or `Allow`. + +* `bypass` - (Optional) Specifies whether traffic is bypassed for Logging/Metrics/AzureServices. Valid options are +any combination of `Logging`, `Metrics`, `AzureServices`, or `None`. + +* `ip_rules` - (Optional) List of public IP or IP ranges in CIDR Format. Only IPV4 addresses are allowed. Private IP address ranges (as defined in [RFC 1918](https://tools.ietf.org/html/rfc1918#section-3)) are not allowed. + +* `virtual_network_subnet_ids` - (Optional) A list of resource ids for subnets. + +## Attributes Reference + +The following attributes are exported in addition to the arguments listed above: + +* `id` - The storage account Resource ID. + +## Import + +Storage Account Network Rules can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_storage_account_network_rules.storageAcc1 /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myresourcegroup/providers/Microsoft.Storage/storageAccounts/myaccount +``` From 779d9d88161ee141c52295e050912661525f5550 Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 5 Dec 2019 09:13:33 -0800 Subject: [PATCH 3/6] Docs changes --- website/azurerm.erb | 2 +- website/docs/r/storage_account_network_rules.html.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/website/azurerm.erb b/website/azurerm.erb index 436afbfe6b34..ce560aa3b7e9 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -1961,7 +1961,7 @@
  • azurerm_storage_account_network_rules -
  • +
  • azurerm_storage_blob diff --git a/website/docs/r/storage_account_network_rules.html.markdown b/website/docs/r/storage_account_network_rules.html.markdown index dd11cae81cf4..1888136e30f3 100644 --- a/website/docs/r/storage_account_network_rules.html.markdown +++ b/website/docs/r/storage_account_network_rules.html.markdown @@ -78,7 +78,7 @@ any combination of `Logging`, `Metrics`, `AzureServices`, or `None`. * `ip_rules` - (Optional) List of public IP or IP ranges in CIDR Format. Only IPV4 addresses are allowed. Private IP address ranges (as defined in [RFC 1918](https://tools.ietf.org/html/rfc1918#section-3)) are not allowed. -* `virtual_network_subnet_ids` - (Optional) A list of resource ids for subnets. +* `virtual_network_subnet_ids` - (Optional) A list of virtual network subnet ids to to secure the storage account. ## Attributes Reference From 7e8925dece612436b93f86f684ff8c3a4b4c8128 Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 5 Dec 2019 11:10:59 -0800 Subject: [PATCH 4/6] Addressing review --- ...ource_arm_storage_account_network_rules.go | 56 +++++++++---- ..._arm_storage_account_network_rules_test.go | 79 ++++++++++++++++++- ...torage_account_network_rules.html.markdown | 3 +- 3 files changed, 116 insertions(+), 22 deletions(-) diff --git a/azurerm/resource_arm_storage_account_network_rules.go b/azurerm/resource_arm_storage_account_network_rules.go index a1e500adf603..b13bbdabb117 100644 --- a/azurerm/resource_arm_storage_account_network_rules.go +++ b/azurerm/resource_arm_storage_account_network_rules.go @@ -2,7 +2,9 @@ package azurerm import ( "fmt" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "strings" + "time" "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-04-01/storage" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" @@ -27,6 +29,13 @@ func resourceArmStorageAccountNetworkRules() *schema.Resource { State: schema.ImportStatePassthrough, }, + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(60 * time.Minute), + Delete: schema.DefaultTimeout(60 * time.Minute), + }, + Schema: map[string]*schema.Schema{ "resource_group_name": azure.SchemaResourceGroupName(), @@ -38,9 +47,10 @@ func resourceArmStorageAccountNetworkRules() *schema.Resource { }, "bypass": { - Type: schema.TypeSet, - Optional: true, - Computed: true, + Type: schema.TypeSet, + Optional: true, + Computed: true, + ConfigMode: schema.SchemaConfigModeAttr, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.StringInSlice([]string{ @@ -48,23 +58,33 @@ func resourceArmStorageAccountNetworkRules() *schema.Resource { string(storage.Logging), string(storage.Metrics), string(storage.None), - }, true), + }, false), }, Set: schema.HashString, }, "ip_rules": { - Type: schema.TypeList, - Optional: true, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, + Type: schema.TypeSet, + Optional: true, + Computed: true, + ConfigMode: schema.SchemaConfigModeAttr, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validate.IPv4Address, + }, + Set: schema.HashString, }, "virtual_network_subnet_ids": { - Type: schema.TypeList, - Optional: true, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, + Type: schema.TypeSet, + Optional: true, + Computed: true, + ConfigMode: schema.SchemaConfigModeAttr, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: azure.ValidateResourceID, + }, + Set: schema.HashString, }, "default_action": { @@ -117,11 +137,11 @@ func resourceArmStorageAccountNetworkRulesCreateUpdate(d *schema.ResourceData, m } if v, ok := d.GetOk("ip_rules"); ok { - rules.IPRules = expandStorageAccountNetworkRuleIpRules(v.([]interface{})) + rules.IPRules = expandStorageAccountNetworkRuleIpRules(v.(*schema.Set).List()) } if v, ok := d.GetOk("virtual_network_subnet_ids"); ok { - rules.VirtualNetworkRules = expandStorageAccountNetworkRuleVirtualRules(v.([]interface{})) + rules.VirtualNetworkRules = expandStorageAccountNetworkRuleVirtualRules(v.(*schema.Set).List()) } opts := storage.AccountUpdateParameters{ @@ -161,13 +181,15 @@ func resourceArmStorageAccountNetworkRulesRead(d *schema.ResourceData, meta inte d.Set("resource_group_name", resourceGroup) if rules := storageAccount.NetworkRuleSet; rules != nil { - if err := d.Set("ip_rules", flattenStorageAccountIPRules(rules.IPRules)); err != nil { + if err := d.Set("ip_rules", schema.NewSet(schema.HashString, flattenStorageAccountIPRules(rules.IPRules))); err != nil { return fmt.Errorf("Error setting `ip_rules`: %+v", err) } - if err := d.Set("virtual_network_subnet_ids", flattenStorageAccountVirtualNetworks(rules.VirtualNetworkRules)); err != nil { + if err := d.Set("virtual_network_subnet_ids", schema.NewSet(schema.HashString, flattenStorageAccountVirtualNetworks(rules.VirtualNetworkRules))); err != nil { return fmt.Errorf("Error setting `virtual_network_subnet_ids`: %+v", err) } - d.Set("bypass", schema.NewSet(schema.HashString, flattenStorageAccountBypass(rules.Bypass))) + if err := d.Set("bypass", schema.NewSet(schema.HashString, flattenStorageAccountBypass(rules.Bypass))); err != nil { + return fmt.Errorf("Error setting `bypass`: %+v", err) + } d.Set("default_action", string(rules.DefaultAction)) } diff --git a/azurerm/resource_arm_storage_account_network_rules_test.go b/azurerm/resource_arm_storage_account_network_rules_test.go index b80ff60ef075..5dce52d5c351 100644 --- a/azurerm/resource_arm_storage_account_network_rules_test.go +++ b/azurerm/resource_arm_storage_account_network_rules_test.go @@ -66,6 +66,42 @@ func TestAccAzureRMStorageAccountNetworkRules_update(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + { + Config: testAccAzureRMStorageAccountNetworkRules_basic(rInt, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists("azurerm_storage_account.testsa"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStorageAccountNetworkRules_empty(t *testing.T) { + resourceName := "azurerm_storage_account_network_rules.test" + rInt := tf.AccRandTimeInt() + rs := acctest.RandString(4) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStorageAccountDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStorageAccountNetworkRules_empty(rInt, rs, testLocation()), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStorageAccountExists("azurerm_storage_account.testsa"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } @@ -137,6 +173,14 @@ resource "azurerm_subnet" "test" { service_endpoints = ["Microsoft.Storage"] } +resource "azurerm_subnet" "test2" { + name = "acctestsubnet2%d" + resource_group_name = "${azurerm_resource_group.testrg.name}" + virtual_network_name = "${azurerm_virtual_network.test.name}" + address_prefix = "10.0.3.0/24" + service_endpoints = ["Microsoft.Storage"] +} + resource "azurerm_storage_account" "testsa" { name = "unlikely23exst2acct%s" resource_group_name = "${azurerm_resource_group.testrg.name}" @@ -154,9 +198,38 @@ resource "azurerm_storage_account_network_rules" "test" { storage_account_name = "${azurerm_storage_account.testsa.name}" default_action = "Allow" - ip_rules = ["127.0.0.1"] - virtual_network_subnet_ids = ["${azurerm_subnet.test.id}"] + ip_rules = ["127.0.0.2", "127.0.0.3"] + virtual_network_subnet_ids = ["${azurerm_subnet.test.id}", "${azurerm_subnet.test2.id}"] bypass = ["Metrics"] } -`, rInt, location, rInt, rInt, rString) +`, rInt, location, rInt, rInt, rInt, rString) +} + +func testAccAzureRMStorageAccountNetworkRules_empty(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "testrg" { + name = "acctestRG-storage-%d" + location = "%s" +} + +resource "azurerm_storage_account" "testsa" { + name = "unlikely23exst2acct%s" + resource_group_name = "${azurerm_resource_group.testrg.name}" + location = "${azurerm_resource_group.testrg.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags = { + environment = "production" + } +} + +resource "azurerm_storage_account_network_rules" "test" { + resource_group_name = "${azurerm_resource_group.testrg.name}" + storage_account_name = "${azurerm_storage_account.testsa.name}" + + default_action = "Deny" + bypass = ["Metrics"] +} +`, rInt, location, rString) } diff --git a/website/docs/r/storage_account_network_rules.html.markdown b/website/docs/r/storage_account_network_rules.html.markdown index 1888136e30f3..83568b21ede4 100644 --- a/website/docs/r/storage_account_network_rules.html.markdown +++ b/website/docs/r/storage_account_network_rules.html.markdown @@ -73,8 +73,7 @@ The following arguments are supported: * `default_action` - (Required) Specifies the default action of allow or deny when no other rules match. Valid options are `Deny` or `Allow`. -* `bypass` - (Optional) Specifies whether traffic is bypassed for Logging/Metrics/AzureServices. Valid options are -any combination of `Logging`, `Metrics`, `AzureServices`, or `None`. +* `bypass` - (Optional) Specifies whether traffic is bypassed for Logging/Metrics/AzureServices. Valid options are any combination of `Logging`, `Metrics`, `AzureServices`, or `None`. * `ip_rules` - (Optional) List of public IP or IP ranges in CIDR Format. Only IPV4 addresses are allowed. Private IP address ranges (as defined in [RFC 1918](https://tools.ietf.org/html/rfc1918#section-3)) are not allowed. From 8b6dc054f27e685f4a57706ba07b80e1c79707dd Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 5 Dec 2019 11:58:31 -0800 Subject: [PATCH 5/6] goimports --- azurerm/resource_arm_storage_account_network_rules.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/azurerm/resource_arm_storage_account_network_rules.go b/azurerm/resource_arm_storage_account_network_rules.go index b13bbdabb117..c1bfe34e8705 100644 --- a/azurerm/resource_arm_storage_account_network_rules.go +++ b/azurerm/resource_arm_storage_account_network_rules.go @@ -2,7 +2,6 @@ package azurerm import ( "fmt" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "strings" "time" @@ -11,10 +10,12 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + ) var storageAccountResourceName = "azurerm_storage_account" From e5a7fb4858d087ebdd98751d607c025d8f3c5e3b Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 5 Dec 2019 12:27:42 -0800 Subject: [PATCH 6/6] Fmt --- azurerm/resource_arm_storage_account_network_rules.go | 1 - 1 file changed, 1 deletion(-) diff --git a/azurerm/resource_arm_storage_account_network_rules.go b/azurerm/resource_arm_storage_account_network_rules.go index c1bfe34e8705..3c14e5d93627 100644 --- a/azurerm/resource_arm_storage_account_network_rules.go +++ b/azurerm/resource_arm_storage_account_network_rules.go @@ -15,7 +15,6 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/locks" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" - ) var storageAccountResourceName = "azurerm_storage_account"