diff --git a/internal/services/storage/storage_table_entities_data_source.go b/internal/services/storage/storage_table_entities_data_source.go index 66e4ba789642..3007fcb8252c 100644 --- a/internal/services/storage/storage_table_entities_data_source.go +++ b/internal/services/storage/storage_table_entities_data_source.go @@ -10,12 +10,18 @@ import ( "strings" "time" + "github.com/hashicorp/go-azure-helpers/lang/pointer" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/sdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/client" "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/parse" "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" + storageValidate "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" + "github.com/tombuildsstuff/giovanni/storage/2023-11-03/blob/accounts" "github.com/tombuildsstuff/giovanni/storage/2023-11-03/table/entities" + "github.com/tombuildsstuff/giovanni/storage/2023-11-03/table/tables" ) type storageTableEntitiesDataSource struct{} @@ -23,8 +29,9 @@ type storageTableEntitiesDataSource struct{} var _ sdk.DataSource = storageTableEntitiesDataSource{} type TableEntitiesDataSourceModel struct { - TableName string `tfschema:"table_name"` - StorageAccountName string `tfschema:"storage_account_name"` + StorageTableId string `tfschema:"storage_table_id"` + TableName string `tfschema:"table_name,removedInNextMajorVersion"` + StorageAccountName string `tfschema:"storage_account_name,removedInNextMajorVersion"` Filter string `tfschema:"filter"` Select []string `tfschema:"select"` Items []TableEntityDataSourceModel `tfschema:"items"` @@ -37,17 +44,11 @@ type TableEntityDataSourceModel struct { } func (k storageTableEntitiesDataSource) Arguments() map[string]*pluginsdk.Schema { - return map[string]*pluginsdk.Schema{ - "table_name": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validate.StorageTableName, - }, - - "storage_account_name": { + s := map[string]*pluginsdk.Schema{ + "storage_table_id": { Type: pluginsdk.TypeString, Required: true, - ValidateFunc: validate.StorageAccountName, + ValidateFunc: storageValidate.StorageTableDataPlaneID, }, "filter": { @@ -64,6 +65,35 @@ func (k storageTableEntitiesDataSource) Arguments() map[string]*pluginsdk.Schema }, }, } + + if !features.FourPointOhBeta() { + s["storage_table_id"].Required = false + s["storage_table_id"].Optional = true + s["storage_table_id"].Computed = true + s["storage_table_id"].ConflictsWith = []string{"table_name", "storage_account_name"} + + s["table_name"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + Deprecated: "the `table_name` and `storage_account_name` properties have been superseded by the `storage_table_id` property and will be removed in version 4.0 of the AzureRM provider", + ConflictsWith: []string{"storage_table_id"}, + RequiredWith: []string{"storage_account_name"}, + ValidateFunc: validate.StorageTableName, + } + + s["storage_account_name"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + Deprecated: "the `table_name` and `storage_account_name` properties have been superseded by the `storage_table_id` property and will be removed in version 4.0 of the AzureRM provider", + ConflictsWith: []string{"storage_table_id"}, + RequiredWith: []string{"table_name"}, + ValidateFunc: validate.StorageAccountName, + } + } + + return s } func (k storageTableEntitiesDataSource) Attributes() map[string]*pluginsdk.Schema { @@ -114,13 +144,53 @@ func (k storageTableEntitiesDataSource) Read() sdk.ResourceFunc { } storageClient := metadata.Client.Storage + subscriptionId := metadata.Client.Account.SubscriptionId + + var storageTableId *tables.TableId + var err error + if model.StorageTableId != "" { + storageTableId, err = tables.ParseTableID(model.StorageTableId, storageClient.StorageDomainSuffix) + if err != nil { + return err + } + } else if !features.FourPointOhBeta() { + // TODO: this is needed until `table_name` / `storage_account_name` are removed in favor of `storage_table_id` in v4.0 + // we will retrieve the storage account twice but this will make it easier to refactor later + storageAccountName := model.StorageAccountName + + account, err := storageClient.FindAccount(ctx, subscriptionId, storageAccountName) + if err != nil { + return fmt.Errorf("retrieving Account %q: %v", storageAccountName, err) + } + if account == nil { + return fmt.Errorf("locating Storage Account %q", storageAccountName) + } + + // Determine the table endpoint, so we can build a data plane ID + endpoint, err := account.DataPlaneEndpoint(client.EndpointTypeTable) + if err != nil { + return fmt.Errorf("determining Table endpoint: %v", err) + } + + // Parse the table endpoint as a data plane account ID + accountId, err := accounts.ParseAccountID(*endpoint, storageClient.StorageDomainSuffix) + if err != nil { + return fmt.Errorf("parsing Account ID: %v", err) + } + + storageTableId = pointer.To(tables.NewTableID(*accountId, model.TableName)) + } + + if storageTableId == nil { + return fmt.Errorf("determining storage table ID") + } - account, err := storageClient.FindAccount(ctx, metadata.Client.Account.SubscriptionId, model.StorageAccountName) + account, err := storageClient.FindAccount(ctx, subscriptionId, storageTableId.AccountId.AccountName) if err != nil { - return fmt.Errorf("retrieving Account %q for Table %q: %s", model.StorageAccountName, model.TableName, err) + return fmt.Errorf("retrieving Account %q for Table %q: %v", storageTableId.AccountId.AccountName, storageTableId.TableName, err) } if account == nil { - return fmt.Errorf("the parent Storage Account %s was not found", model.StorageAccountName) + return fmt.Errorf("the parent Storage Account %s was not found", storageTableId.AccountId.AccountName) } client, err := storageClient.TableEntityDataPlaneClient(ctx, *account, storageClient.DataPlaneOperationSupportingAnyAuthMethod()) @@ -138,11 +208,11 @@ func (k storageTableEntitiesDataSource) Read() sdk.ResourceFunc { input.PropertyNamesToSelect = &model.Select } - id := parse.NewStorageTableEntitiesId(model.StorageAccountName, storageClient.StorageDomainSuffix, model.TableName, model.Filter) + id := parse.NewStorageTableEntitiesId(storageTableId.AccountId.AccountName, storageClient.StorageDomainSuffix, storageTableId.TableName, model.Filter) - result, err := client.Query(ctx, model.TableName, input) + result, err := client.Query(ctx, storageTableId.TableName, input) if err != nil { - return fmt.Errorf("retrieving Entities (Filter %q) (Table %q in %s): %+v", model.Filter, model.TableName, account.StorageAccountId, err) + return fmt.Errorf("retrieving Entities (Filter %q) (Table %q in %s): %+v", model.Filter, storageTableId.TableName, account.StorageAccountId, err) } var flattenedEntities []TableEntityDataSourceModel diff --git a/internal/services/storage/storage_table_entities_data_source_test.go b/internal/services/storage/storage_table_entities_data_source_test.go index ac9dda3b08f9..2f16c62f2d17 100644 --- a/internal/services/storage/storage_table_entities_data_source_test.go +++ b/internal/services/storage/storage_table_entities_data_source_test.go @@ -68,8 +68,7 @@ resource "azurerm_storage_table" "test" { } resource "azurerm_storage_table_entity" "test" { - storage_account_name = azurerm_storage_account.test.name - table_name = azurerm_storage_table.test.name + storage_table_id = azurerm_storage_table.test.id partition_key = "testpartition" row_key = "testrow" @@ -81,8 +80,7 @@ resource "azurerm_storage_table_entity" "test" { } resource "azurerm_storage_table_entity" "test2" { - storage_account_name = azurerm_storage_account.test.name - table_name = azurerm_storage_table.test.name + storage_table_id = azurerm_storage_table.test.id partition_key = "testpartition" row_key = "testrow2" @@ -94,8 +92,7 @@ resource "azurerm_storage_table_entity" "test2" { } resource "azurerm_storage_table_entity" "testselector" { - storage_account_name = azurerm_storage_account.test.name - table_name = azurerm_storage_table.test.name + storage_table_id = azurerm_storage_table.test.id partition_key = "testselectorpartition" row_key = "testrow" @@ -115,9 +112,8 @@ func (d StorageTableEntitiesDataSource) basicWithDataSource(data acceptance.Test %s data "azurerm_storage_table_entities" "test" { - table_name = azurerm_storage_table_entity.test.table_name - storage_account_name = azurerm_storage_table_entity.test.storage_account_name - filter = "PartitionKey eq 'testpartition'" + storage_table_id = azurerm_storage_table.test.id + filter = "PartitionKey eq 'testpartition'" depends_on = [ azurerm_storage_table_entity.test, @@ -133,10 +129,9 @@ func (d StorageTableEntitiesDataSource) basicWithDataSourceAndSelector(data acce %s data "azurerm_storage_table_entities" "test" { - table_name = azurerm_storage_table_entity.test.table_name - storage_account_name = azurerm_storage_table_entity.test.storage_account_name - filter = "PartitionKey eq 'testselectorpartition'" - select = ["testselector"] + storage_table_id = azurerm_storage_table.test.id + filter = "PartitionKey eq 'testselectorpartition'" + select = ["testselector"] depends_on = [ azurerm_storage_table_entity.test, diff --git a/internal/services/storage/storage_table_entity_data_source.go b/internal/services/storage/storage_table_entity_data_source.go index 4a5b0c393fc5..4ac9decf425e 100644 --- a/internal/services/storage/storage_table_entity_data_source.go +++ b/internal/services/storage/storage_table_entity_data_source.go @@ -7,18 +7,22 @@ import ( "fmt" "time" + "github.com/hashicorp/go-azure-helpers/lang/pointer" "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/features" "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/client" "github.com/hashicorp/terraform-provider-azurerm/internal/services/storage/validate" + storageValidate "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" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" "github.com/tombuildsstuff/giovanni/storage/2023-11-03/blob/accounts" "github.com/tombuildsstuff/giovanni/storage/2023-11-03/table/entities" + "github.com/tombuildsstuff/giovanni/storage/2023-11-03/table/tables" ) func dataSourceStorageTableEntity() *pluginsdk.Resource { - return &pluginsdk.Resource{ + resource := &pluginsdk.Resource{ Read: dataSourceStorageTableEntityRead, Timeouts: &pluginsdk.ResourceTimeout{ @@ -26,16 +30,10 @@ func dataSourceStorageTableEntity() *pluginsdk.Resource { }, Schema: map[string]*pluginsdk.Schema{ - "table_name": { + "storage_table_id": { Type: pluginsdk.TypeString, Required: true, - ValidateFunc: validate.StorageTableName, - }, - - "storage_account_name": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validate.StorageAccountName, + ValidateFunc: storageValidate.StorageTableDataPlaneID, }, "partition_key": { @@ -59,6 +57,35 @@ func dataSourceStorageTableEntity() *pluginsdk.Resource { }, }, } + + if !features.FourPointOhBeta() { + resource.Schema["storage_table_id"].Required = false + resource.Schema["storage_table_id"].Optional = true + resource.Schema["storage_table_id"].Computed = true + resource.Schema["storage_table_id"].ConflictsWith = []string{"table_name", "storage_account_name"} + + resource.Schema["table_name"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + Deprecated: "the `table_name` and `storage_account_name` properties have been superseded by the `storage_table_id` property and will be removed in version 4.0 of the AzureRM provider", + ConflictsWith: []string{"storage_table_id"}, + RequiredWith: []string{"storage_account_name"}, + ValidateFunc: validate.StorageTableName, + } + + resource.Schema["storage_account_name"] = &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + Deprecated: "the `table_name` and `storage_account_name` properties have been superseded by the `storage_table_id` property and will be removed in version 4.0 of the AzureRM provider", + ConflictsWith: []string{"storage_table_id"}, + RequiredWith: []string{"table_name"}, + ValidateFunc: validate.StorageAccountName, + } + } + + return resource } func dataSourceStorageTableEntityRead(d *pluginsdk.ResourceData, meta interface{}) error { @@ -67,17 +94,54 @@ func dataSourceStorageTableEntityRead(d *pluginsdk.ResourceData, meta interface{ ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - accountName := d.Get("storage_account_name").(string) - tableName := d.Get("table_name").(string) partitionKey := d.Get("partition_key").(string) rowKey := d.Get("row_key").(string) - account, err := storageClient.FindAccount(ctx, subscriptionId, accountName) + var storageTableId *tables.TableId + var err error + if v, ok := d.GetOk("storage_table_id"); ok && v.(string) != "" { + storageTableId, err = tables.ParseTableID(v.(string), storageClient.StorageDomainSuffix) + if err != nil { + return err + } + } else if !features.FourPointOhBeta() { + // TODO: this is needed until `table_name` / `storage_account_name` are removed in favor of `storage_table_id` in v4.0 + // we will retrieve the storage account twice but this will make it easier to refactor later + storageAccountName := d.Get("storage_account_name").(string) + + account, err := storageClient.FindAccount(ctx, subscriptionId, storageAccountName) + if err != nil { + return fmt.Errorf("retrieving Account %q: %v", storageAccountName, err) + } + if account == nil { + return fmt.Errorf("locating Storage Account %q", storageAccountName) + } + + // Determine the table endpoint, so we can build a data plane ID + endpoint, err := account.DataPlaneEndpoint(client.EndpointTypeTable) + if err != nil { + return fmt.Errorf("determining Table endpoint: %v", err) + } + + // Parse the table endpoint as a data plane account ID + accountId, err := accounts.ParseAccountID(*endpoint, storageClient.StorageDomainSuffix) + if err != nil { + return fmt.Errorf("parsing Account ID: %v", err) + } + + storageTableId = pointer.To(tables.NewTableID(*accountId, d.Get("table_name").(string))) + } + + if storageTableId == nil { + return fmt.Errorf("determining storage table ID") + } + + account, err := storageClient.FindAccount(ctx, subscriptionId, storageTableId.AccountId.AccountName) if err != nil { - return fmt.Errorf("retrieving Account %q for Table %q: %v", accountName, tableName, err) + return fmt.Errorf("retrieving Account %q for Table %q: %v", storageTableId.AccountId.AccountName, storageTableId.TableName, err) } if account == nil { - return fmt.Errorf("the parent Storage Account %s was not found", accountName) + return fmt.Errorf("the parent Storage Account %s was not found", storageTableId.AccountId.AccountName) } dataPlaneClient, err := storageClient.TableEntityDataPlaneClient(ctx, *account, storageClient.DataPlaneOperationSupportingAnyAuthMethod()) @@ -95,7 +159,7 @@ func dataSourceStorageTableEntityRead(d *pluginsdk.ResourceData, meta interface{ return fmt.Errorf("parsing Account ID: %v", err) } - id := entities.NewEntityID(*accountId, tableName, partitionKey, rowKey) + id := entities.NewEntityID(*accountId, storageTableId.TableName, partitionKey, rowKey) input := entities.GetEntityInput{ PartitionKey: partitionKey, @@ -103,16 +167,20 @@ func dataSourceStorageTableEntityRead(d *pluginsdk.ResourceData, meta interface{ MetaDataLevel: entities.NoMetaData, } - result, err := dataPlaneClient.Get(ctx, tableName, input) + result, err := dataPlaneClient.Get(ctx, storageTableId.TableName, input) if err != nil { return fmt.Errorf("retrieving %s: %v", id, err) } - d.Set("storage_account_name", accountName) - d.Set("table_name", tableName) + d.Set("storage_table_id", storageTableId.ID()) d.Set("partition_key", partitionKey) d.Set("row_key", rowKey) + if !features.FourPointOhBeta() { + d.Set("storage_account_name", id.AccountId.AccountName) + d.Set("table_name", id.TableName) + } + if err = d.Set("entity", flattenEntity(result.Entity)); err != nil { return fmt.Errorf("setting `entity` for %s: %v", id, err) } diff --git a/internal/services/storage/storage_table_entity_data_source_test.go b/internal/services/storage/storage_table_entity_data_source_test.go index f58846f93b89..85f536688661 100644 --- a/internal/services/storage/storage_table_entity_data_source_test.go +++ b/internal/services/storage/storage_table_entity_data_source_test.go @@ -53,8 +53,7 @@ resource "azurerm_storage_table" "test" { } resource "azurerm_storage_table_entity" "test" { - storage_account_name = azurerm_storage_account.test.name - table_name = azurerm_storage_table.test.name + storage_table_id = azurerm_storage_table.test.id partition_key = "testpartition" row_key = "testrow" @@ -72,10 +71,9 @@ func (d StorageTableEntityDataSource) basicWithDataSource(data acceptance.TestDa %s data "azurerm_storage_table_entity" "test" { - table_name = azurerm_storage_table_entity.test.table_name - storage_account_name = azurerm_storage_table_entity.test.storage_account_name - partition_key = azurerm_storage_table_entity.test.partition_key - row_key = azurerm_storage_table_entity.test.row_key + storage_table_id = azurerm_storage_table.test.id + partition_key = azurerm_storage_table_entity.test.partition_key + row_key = azurerm_storage_table_entity.test.row_key } `, config) } diff --git a/website/docs/d/storage_table_entities.html.markdown b/website/docs/d/storage_table_entities.html.markdown index bba27c318301..e708dd9a970c 100644 --- a/website/docs/d/storage_table_entities.html.markdown +++ b/website/docs/d/storage_table_entities.html.markdown @@ -24,9 +24,7 @@ data "azurerm_storage_table_entities" "example" { The following arguments are supported: -* `table_name` - The name of the Table. - -* `storage_account_name` - The name of the Storage Account where the Table exists. +* `storage_table_id` - The Storage Table ID where the entities exist. * `filter` - The filter used to retrieve the entities. diff --git a/website/docs/d/storage_table_entity.html.markdown b/website/docs/d/storage_table_entity.html.markdown index 23b0372a9e14..41f19d34ad11 100644 --- a/website/docs/d/storage_table_entity.html.markdown +++ b/website/docs/d/storage_table_entity.html.markdown @@ -25,9 +25,7 @@ data "azurerm_storage_table_entity" "example" { The following arguments are supported: -* `table_name` - The name of the Table. - -* `storage_account_name` - The name of the Storage Account where the Table exists. +* `storage_table_id` - (Required) The Storage Table ID where the entity exists. * `partition_key` - The key for the partition where the entity will be retrieved. diff --git a/website/docs/guides/4.0-upgrade-guide.html.markdown b/website/docs/guides/4.0-upgrade-guide.html.markdown index dc0de0a77ead..8a648b05a40c 100644 --- a/website/docs/guides/4.0-upgrade-guide.html.markdown +++ b/website/docs/guides/4.0-upgrade-guide.html.markdown @@ -1179,6 +1179,10 @@ The deprecated data source has been superseded by `azurerm_mssql_server` and has * The deprecated properties `storage_account_name` and `table_name` have been removed in favour of the `storage_table_id` property. +### `azurerm_storage_table_entities` + +* The deprecated properties `storage_account_name` and `table_name` have been removed in favour of the `storage_table_id` property. + ### `azurerm_subnet` * The deprecated property `private_endpoint_network_policies_enabled` has been removed in favour of the `private_endpoint_network_policies` property.