Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_storage_table - Support storage_account_id #28764

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 149 additions & 43 deletions internal/services/storage/storage_table_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import (
"fmt"
"time"

"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
"github.com/hashicorp/go-azure-sdk/resource-manager/storage/2023-05-01/tableservice"
"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"
Expand All @@ -22,11 +25,16 @@ type storageTableDataSource struct{}
var _ sdk.DataSource = storageTableDataSource{}

type TableDataSourceModel struct {
Name string `tfschema:"name"`
StorageAccountName string `tfschema:"storage_account_name"`
ACL []ACLModel `tfschema:"acl"`
Id string `tfschema:"id"`
ResourceManagerId string `tfschema:"resource_manager_id"`
Name string `tfschema:"name"`
StorageAccountId string `tfschema:"storage_account_id"`
ACL []ACLModel `tfschema:"acl"`
Id string `tfschema:"id"`

// TODO 5.0: Remove this
StorageAccountName string `tfschema:"storage_account_name"`

// TODO 5.0: Remove this
ResourceManagerId string `tfschema:"resource_manager_id"`
}

type ACLModel struct {
Expand All @@ -41,23 +49,42 @@ type AccessPolicyModel struct {
}

func (k storageTableDataSource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
r := map[string]*pluginsdk.Schema{
"name": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validate.StorageTableName,
},

"storage_account_name": {
"storage_account_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validate.StorageAccountName,
},
}

if !features.FivePointOh() {
r["storage_account_name"] = &pluginsdk.Schema{
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validate.StorageAccountName,
ExactlyOneOf: []string{"storage_account_id", "storage_account_name"},
Deprecated: "the `storage_account_name` property has been deprecated in favour of `storage_account_id` and will be removed in version 5.0 of the Provider.",
}

r["storage_account_id"] = &pluginsdk.Schema{
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: commonids.ValidateStorageAccountID,
ExactlyOneOf: []string{"storage_account_id", "storage_account_name"},
}
}

return r
}

func (k storageTableDataSource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
r := map[string]*pluginsdk.Schema{
"acl": {
Type: pluginsdk.TypeSet,
Computed: true,
Expand Down Expand Up @@ -95,12 +122,16 @@ func (k storageTableDataSource) Attributes() map[string]*pluginsdk.Schema {
Type: pluginsdk.TypeString,
Computed: true,
},
}

"resource_manager_id": {
if !features.FivePointOh() {
r["resource_manager_id"] = &pluginsdk.Schema{
Type: pluginsdk.TypeString,
Computed: true,
},
}
}

return r
}

func (k storageTableDataSource) ModelObject() interface{} {
Expand All @@ -121,66 +152,141 @@ func (k storageTableDataSource) Read() sdk.ResourceFunc {
return fmt.Errorf("decoding %+v", err)
}

storageClient := metadata.Client.Storage
if !features.FivePointOh() {
if model.StorageAccountName != "" {
storageClient := metadata.Client.Storage
account, err := storageClient.FindAccount(ctx, metadata.Client.Account.SubscriptionId, model.StorageAccountName)
if err != nil {
return fmt.Errorf("retrieving Storage Account %q for Table %q: %v", model.StorageAccountName, model.Name, err)
}
if account == nil {
return fmt.Errorf("locating Storage Account %q for Table %q", model.StorageAccountName, model.Name)
}

account, err := storageClient.FindAccount(ctx, metadata.Client.Account.SubscriptionId, model.StorageAccountName)
if err != nil {
return fmt.Errorf("retrieving Storage Account %q for Table %q: %v", model.StorageAccountName, model.Name, err)
}
if account == nil {
return fmt.Errorf("locating Storage Account %q for Table %q", model.StorageAccountName, model.Name)
}
// 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)
}

// 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)
}

id := tables.NewTableID(*accountId, model.Name)

aclClient, err := storageClient.TablesDataPlaneClient(ctx, *account, storageClient.DataPlaneOperationSupportingOnlySharedKeyAuth())
if err != nil {
return fmt.Errorf("building Tables Client: %v", err)
}

acls, err := aclClient.GetACLs(ctx, model.Name)
if err != nil {
return fmt.Errorf("retrieving ACLs for %s: %v", id, err)
}

model.ACL = flattenStorageTableACLsWithMetadataDeprecated(acls)

resourceManagerId := parse.NewStorageTableResourceManagerID(account.StorageAccountId.SubscriptionId, account.StorageAccountId.ResourceGroupName, account.StorageAccountId.StorageAccountName, "default", model.Name)
model.ResourceManagerId = resourceManagerId.ID()
metadata.SetID(id)

return metadata.Encode(&model)
}
}

// Parse the table endpoint as a data plane account ID
accountId, err := accounts.ParseAccountID(*endpoint, storageClient.StorageDomainSuffix)
tableClient := metadata.Client.Storage.ResourceManager.TableService
accountId, err := commonids.ParseStorageAccountID(model.StorageAccountId)
if err != nil {
return fmt.Errorf("parsing Account ID: %v", err)
return err
}

id := tables.NewTableID(*accountId, model.Name)

aclClient, err := storageClient.TablesDataPlaneClient(ctx, *account, storageClient.DataPlaneOperationSupportingOnlySharedKeyAuth())
id := tableservice.NewTableID(accountId.SubscriptionId, accountId.ResourceGroupName, accountId.StorageAccountName, model.Name)
resp, err := tableClient.TableGet(ctx, id)
if err != nil {
return fmt.Errorf("building Tables Client: %v", err)
return fmt.Errorf("retrieving %q: %v", id, err)
}

acls, err := aclClient.GetACLs(ctx, model.Name)
if err != nil {
return fmt.Errorf("retrieving ACLs for %s: %v", id, err)
if respModel := resp.Model; respModel != nil {
if props := respModel.Properties; props != nil {
acl, err := flattenStorageTableACLsWithMetadata(props.SignedIdentifiers)
if err != nil {
return fmt.Errorf("flattening `acl`: %v", err)
}
model.ACL = acl
}
}

model.ACL = flattenStorageTableACLsWithMetadata(acls)
if !features.FivePointOh() {
model.ResourceManagerId = id.ID()
}

resourceManagerId := parse.NewStorageTableResourceManagerID(account.StorageAccountId.SubscriptionId, account.StorageAccountId.ResourceGroupName, account.StorageAccountId.StorageAccountName, "default", model.Name)
model.ResourceManagerId = resourceManagerId.ID()
metadata.SetID(id)

return metadata.Encode(&model)
},
}
}

func flattenStorageTableACLsWithMetadata(acls *[]tables.SignedIdentifier) []ACLModel {
func flattenStorageTableACLsWithMetadata(acls *[]tableservice.TableSignedIdentifier) ([]ACLModel, error) {
if acls == nil {
return []ACLModel{}, nil
}

output := make([]ACLModel, 0, len(*acls))
for _, acl := range *acls {
var startTime, expiryTime, permission string
var err error
if policy := acl.AccessPolicy; policy != nil {
if policy.StartTime != nil {
startTime = *policy.StartTime
startTime, err = convertTimeFormat(startTime)
if err != nil {
return nil, err
}
}
if policy.ExpiryTime != nil {
expiryTime = *policy.ExpiryTime
expiryTime, err = convertTimeFormat(expiryTime)
if err != nil {
return nil, err
}
}
permission = policy.Permission
}

var accessPolicies []AccessPolicyModel
accessPolicies = append(accessPolicies, AccessPolicyModel{
Start: startTime,
Expiry: expiryTime,
Permissions: permission,
})

output = append(output, ACLModel{
Id: acl.Id,
AccessPolicy: accessPolicies,
})
}

return output, nil
}

func flattenStorageTableACLsWithMetadataDeprecated(acls *[]tables.SignedIdentifier) []ACLModel {
if acls == nil {
return []ACLModel{}
}

output := make([]ACLModel, 0, len(*acls))
for _, acl := range *acls {
var accessPolicies []AccessPolicyModel
for _, policy := range []tables.AccessPolicy{acl.AccessPolicy} {
accessPolicies = append(accessPolicies, AccessPolicyModel{
Start: policy.Start,
Expiry: policy.Expiry,
Permissions: policy.Permission,
})
}
policy := acl.AccessPolicy
accessPolicies = append(accessPolicies, AccessPolicyModel{
Start: policy.Start,
Expiry: policy.Expiry,
Permissions: policy.Permission,
})

output = append(output, ACLModel{
Id: acl.Id,
Expand Down
73 changes: 72 additions & 1 deletion internal/services/storage/storage_table_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
"github.com/hashicorp/terraform-provider-azurerm/internal/features"
)

type StorageTableDataSource struct{}
Expand All @@ -28,14 +29,72 @@ func TestAccDataSourceStorageTable_basic(t *testing.T) {
})
}

func TestAccDataSourceStorageTable_basicDeprecated(t *testing.T) {
if features.FivePointOh() {
t.Skip("skipping as not valid in 5.0")
}

data := acceptance.BuildTestData(t, "data.azurerm_storage_table", "test")

data.DataSourceTest(t, []acceptance.TestStep{
{
Config: StorageTableDataSource{}.basicWithDataSourceDeprecated(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).Key("acl.#").HasValue("1"),
check.That(data.ResourceName).Key("acl.0.id").HasValue("MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI"),
check.That(data.ResourceName).Key("acl.0.access_policy.0.permissions").HasValue("raud"),
),
},
})
}

func (d StorageTableDataSource) basic(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

resource "azurerm_resource_group" "test" {
name = "tabledstest-%s"
name = "acctest-tableds-%s"
location = "%s"
}

resource "azurerm_storage_account" "test" {
name = "acctesttedsc%s"
resource_group_name = "${azurerm_resource_group.test.name}"

location = "${azurerm_resource_group.test.location}"
account_tier = "Standard"
account_replication_type = "LRS"
}

resource "azurerm_storage_table" "test" {
name = "tabletesttedsc%s"
storage_account_id = azurerm_storage_account.test.id

acl {
id = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI"

access_policy {
permissions = "raud"
start = "2020-11-26T08:49:37.0000000Z"
expiry = "2020-11-27T08:49:37.0000000Z"
}
}
}


`, data.RandomString, data.Locations.Primary, data.RandomString, data.RandomString)
}

func (d StorageTableDataSource) basicDeprecated(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

resource "azurerm_resource_group" "test" {
name = "acctest-tableds-%s"
location = "%s"
}

Expand Down Expand Up @@ -71,6 +130,18 @@ func (d StorageTableDataSource) basicWithDataSource(data acceptance.TestData) st
return fmt.Sprintf(`
%s

data "azurerm_storage_table" "test" {
name = azurerm_storage_table.test.name
storage_account_id = azurerm_storage_table.test.storage_account_id
}
`, config)
}

func (d StorageTableDataSource) basicWithDataSourceDeprecated(data acceptance.TestData) string {
config := d.basicDeprecated(data)
return fmt.Sprintf(`
%s

data "azurerm_storage_table" "test" {
name = azurerm_storage_table.test.name
storage_account_name = azurerm_storage_table.test.storage_account_name
Expand Down
Loading
Loading