diff --git a/internal/services/storage/storage_account_network_rules_resource.go b/internal/services/storage/storage_account_network_rules_resource.go index 8c83a8b5100e..84ee826abe11 100644 --- a/internal/services/storage/storage_account_network_rules_resource.go +++ b/internal/services/storage/storage_account_network_rules_resource.go @@ -2,6 +2,7 @@ package storage import ( "fmt" + "log" "strings" "time" @@ -10,6 +11,7 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" "github.com/hashicorp/terraform-provider-azurerm/internal/locks" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" @@ -23,8 +25,11 @@ func resourceStorageAccountNetworkRules() *pluginsdk.Resource { Read: resourceStorageAccountNetworkRulesRead, Update: resourceStorageAccountNetworkRulesCreateUpdate, Delete: resourceStorageAccountNetworkRulesDelete, - // TODO: replace this with an importer which validates the ID during import - Importer: pluginsdk.DefaultImporter(), + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.StorageAccountID(id) + return err + }), Timeouts: &pluginsdk.ResourceTimeout{ Create: pluginsdk.DefaultTimeout(60 * time.Minute), @@ -34,13 +39,51 @@ func resourceStorageAccountNetworkRules() *pluginsdk.Resource { }, Schema: map[string]*pluginsdk.Schema{ - "resource_group_name": azure.SchemaResourceGroupName(), + "storage_account_id": { + Type: pluginsdk.TypeString, + // TODO: Make required in 3.0 + Optional: true, + // TODO: Remove in 3.0 + Computed: true, + ForceNew: true, + ValidateFunc: validate.StorageAccountID, + // TODO: Remove in 3.0 + ConflictsWith: []string{ + "resource_group_name", + "storage_account_name", + }, + }, + // TODO: remove in 3.0 + "resource_group_name": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: azure.ValidateResourceGroupName, + Deprecated: "Deprecated in favour of `storage_account_id`", + RequiredWith: []string{ + "storage_account_name", + }, + ConflictsWith: []string{ + "storage_account_id", + }, + }, + + // TODO: remove in 3.0 "storage_account_name": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, + Computed: true, ForceNew: true, ValidateFunc: validate.StorageAccountName, + Deprecated: "Deprecated in favour of `storage_account_id`", + RequiredWith: []string{ + "resource_group_name", + }, + ConflictsWith: []string{ + "storage_account_id", + }, }, "bypass": { @@ -125,6 +168,17 @@ func resourceStorageAccountNetworkRulesCreateUpdate(d *pluginsdk.ResourceData, m storageAccountName := d.Get("storage_account_name").(string) resourceGroup := d.Get("resource_group_name").(string) + raw, ok := d.GetOk("storage_account_id") + if ok { + parsedStorageAccountId, err := parse.StorageAccountID(raw.(string)) + if err != nil { + return err + } + + storageAccountName = parsedStorageAccountId.Name + resourceGroup = parsedStorageAccountId.ResourceGroup + } + locks.ByName(storageAccountName, storageAccountResourceName) defer locks.UnlockByName(storageAccountName, storageAccountResourceName) @@ -188,9 +242,15 @@ func resourceStorageAccountNetworkRulesRead(d *pluginsdk.ResourceData, meta inte storageAccount, err := client.GetProperties(ctx, resourceGroup, storageAccountName, "") if err != nil { + if utils.ResponseWasNotFound(storageAccount.Response) { + log.Printf("[INFO] Storage Account Network Rules %q does not exist - removing from state", d.Id()) + d.SetId("") + return nil + } return fmt.Errorf("reading Storage Account Network Rules %q (Resource Group %q): %+v", storageAccountName, resourceGroup, err) } + d.Set("storage_account_id", d.Id()) d.Set("storage_account_name", storageAccountName) d.Set("resource_group_name", resourceGroup) diff --git a/internal/services/storage/storage_account_network_rules_resource_test.go b/internal/services/storage/storage_account_network_rules_resource_test.go index 86acdea1ac30..97fd688888ae 100644 --- a/internal/services/storage/storage_account_network_rules_resource_test.go +++ b/internal/services/storage/storage_account_network_rules_resource_test.go @@ -29,6 +29,21 @@ func TestAccStorageAccountNetworkRules_basic(t *testing.T) { }) } +func TestAccStorageAccountNetworkRules_id(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_storage_account_network_rules", "test") + r := StorageAccountNetworkRulesResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.id(data), + Check: acceptance.ComposeTestCheckFunc( + check.That("azurerm_storage_account.test").ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccStorageAccountNetworkRules_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_storage_account_network_rules", "test") r := StorageAccountNetworkRulesResource{} @@ -194,6 +209,55 @@ resource "azurerm_storage_account_network_rules" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomString) } +// TODO: Remove in 3.0 +func (r StorageAccountNetworkRulesResource) id(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + 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.test.location + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_subnet" "test" { + name = "acctestsubnet%d" + resource_group_name = azurerm_resource_group.test.name + virtual_network_name = azurerm_virtual_network.test.name + address_prefix = "10.0.2.0/24" + service_endpoints = ["Microsoft.Storage"] +} + +resource "azurerm_storage_account" "test" { + name = "unlikely23exst2acct%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" + + tags = { + environment = "production" + } +} + +resource "azurerm_storage_account_network_rules" "test" { + storage_account_id = azurerm_storage_account.test.id + + default_action = "Deny" + ip_rules = ["127.0.0.1"] + virtual_network_subnet_ids = [azurerm_subnet.test.id] +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomString) +} + func (r StorageAccountNetworkRulesResource) update(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/r/storage_account_network_rules.html.markdown b/website/docs/r/storage_account_network_rules.html.markdown index c875d693e4a8..90ef834398ea 100644 --- a/website/docs/r/storage_account_network_rules.html.markdown +++ b/website/docs/r/storage_account_network_rules.html.markdown @@ -66,9 +66,15 @@ resource "azurerm_storage_account_network_rules" "test" { 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. +* `storage_account_name` - (Optional) 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. +-> **NOTE:** This property has been deprecated in favour of the `storage_account_id` property and will be removed in version 3.0 of the provider. + +* `resource_group_name` - (Optional) The name of the resource group in which to create the storage account. Changing this forces a new resource to be created. + +-> **NOTE:** This property has been deprecated in favour of the `storage_account_id` property and will be removed in version 3.0 of the provider. + +* `storage_account_id` - (Optional) Specifies the ID of 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`.