Skip to content

Commit

Permalink
Merge pull request #3847 from terraform-providers/f-storage-table-acl
Browse files Browse the repository at this point in the history
Improvement: `azurerm_storage_table` ACL Support
  • Loading branch information
tombuildsstuff authored Jul 17, 2019
2 parents 9812c84 + 96188d6 commit 8d926f0
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 3 deletions.
139 changes: 136 additions & 3 deletions azurerm/resource_arm_storage_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import (
"regexp"

"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/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/utils"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/table/tables"
)
Expand All @@ -17,6 +19,7 @@ func resourceArmStorageTable() *schema.Resource {
Create: resourceArmStorageTableCreate,
Read: resourceArmStorageTableRead,
Delete: resourceArmStorageTableDelete,
Update: resourceArmStorageTableUpdate,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Expand All @@ -41,7 +44,42 @@ func resourceArmStorageTable() *schema.Resource {
// TODO: deprecate this in the docs
"resource_group_name": azure.SchemaResourceGroupNameDeprecated(),

// TODO: support for ACL's
"acl": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"id": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringLenBetween(1, 64),
},
"access_policy": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"start": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.NoEmptyStrings,
},
"expiry": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.NoEmptyStrings,
},
"permissions": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validate.NoEmptyStrings,
},
},
},
},
},
},
},
},
}
}
Expand All @@ -52,6 +90,8 @@ func resourceArmStorageTableCreate(d *schema.ResourceData, meta interface{}) err

tableName := d.Get("name").(string)
accountName := d.Get("storage_account_name").(string)
aclsRaw := d.Get("acl").(*schema.Set).List()
acls := expandStorageTableACLs(aclsRaw)

resourceGroup, err := storageClient.FindResourceGroup(ctx, accountName)
if err != nil {
Expand Down Expand Up @@ -82,6 +122,10 @@ func resourceArmStorageTableCreate(d *schema.ResourceData, meta interface{}) err
return fmt.Errorf("Error creating Table %q within Storage Account %q: %s", tableName, accountName, err)
}

if _, err := client.SetACL(ctx, accountName, tableName, acls); err != nil {
return fmt.Errorf("Error setting ACL's for Storage Table %q (Account %q / Resource Group %q): %+v", tableName, accountName, *resourceGroup, err)
}

d.SetId(id)
return resourceArmStorageTableRead(d, meta)
}
Expand Down Expand Up @@ -122,15 +166,19 @@ func resourceArmStorageTableRead(d *schema.ResourceData, meta interface{}) error
return fmt.Errorf("Error retrieving Table %q in Storage Account %q: %s", id.TableName, id.AccountName, err)
}

_, err = client.GetACL(ctx, id.AccountName, id.TableName)
acls, err := client.GetACL(ctx, id.AccountName, id.TableName)
if err != nil {
return fmt.Errorf("Error retrieving Table %q in Storage Account %q: %s", id.TableName, id.AccountName, err)
return fmt.Errorf("Error retrieving ACL's %q in Storage Account %q: %s", id.TableName, id.AccountName, err)
}

d.Set("name", id.TableName)
d.Set("storage_account_name", id.AccountName)
d.Set("resource_group_name", resourceGroup)

if err := d.Set("acl", flattenStorageTableACLs(acls)); err != nil {
return fmt.Errorf("Error flattening `acl`: %+v", err)
}

return nil
}

Expand Down Expand Up @@ -166,6 +214,47 @@ func resourceArmStorageTableDelete(d *schema.ResourceData, meta interface{}) err
return nil
}

func resourceArmStorageTableUpdate(d *schema.ResourceData, meta interface{}) error {
storageClient := meta.(*ArmClient).storage
ctx := meta.(*ArmClient).StopContext

id, err := tables.ParseResourceID(d.Id())
if err != nil {
return err
}

resourceGroup, err := storageClient.FindResourceGroup(ctx, id.AccountName)
if err != nil {
return fmt.Errorf("Error locating Resource Group: %s", err)
}

if resourceGroup == nil {
log.Printf("Unable to determine Resource Group for Storage Account %q (assuming removed)", id.AccountName)
d.SetId("")
return nil
}

client, err := storageClient.TablesClient(ctx, *resourceGroup, id.AccountName)
if err != nil {
return fmt.Errorf("Error building Table Client: %s", err)
}

if d.HasChange("acl") {
log.Printf("[DEBUG] Updating the ACL's for Storage Table %q (Storage Account %q)", id.TableName, id.AccountName)

aclsRaw := d.Get("acl").(*schema.Set).List()
acls := expandStorageTableACLs(aclsRaw)

if _, err := client.SetACL(ctx, id.AccountName, id.TableName, acls); err != nil {
return fmt.Errorf("Error updating ACL's for Storage Table %q (Storage Account %q): %s", id.TableName, id.AccountName, err)
}

log.Printf("[DEBUG] Updated the ACL's for Storage Table %q (Storage Account %q)", id.TableName, id.AccountName)
}

return resourceArmStorageTableRead(d, meta)
}

func validateArmStorageTableName(v interface{}, k string) (warnings []string, errors []error) {
value := v.(string)
if value == "table" {
Expand All @@ -181,3 +270,47 @@ func validateArmStorageTableName(v interface{}, k string) (warnings []string, er

return warnings, errors
}

func expandStorageTableACLs(input []interface{}) []tables.SignedIdentifier {
results := make([]tables.SignedIdentifier, 0)

for _, v := range input {
vals := v.(map[string]interface{})

policies := vals["access_policy"].([]interface{})
policy := policies[0].(map[string]interface{})

identifier := tables.SignedIdentifier{
Id: vals["id"].(string),
AccessPolicy: tables.AccessPolicy{
Start: policy["start"].(string),
Expiry: policy["expiry"].(string),
Permission: policy["permissions"].(string),
},
}
results = append(results, identifier)
}

return results
}

func flattenStorageTableACLs(input tables.GetACLResult) []interface{} {
result := make([]interface{}, 0)

for _, v := range input.SignedIdentifiers {
output := map[string]interface{}{
"id": v.Id,
"access_policy": []interface{}{
map[string]interface{}{
"start": v.AccessPolicy.Start,
"expiry": v.AccessPolicy.Expiry,
"permissions": v.AccessPolicy.Permission,
},
},
}

result = append(result, output)
}

return result
}
121 changes: 121 additions & 0 deletions azurerm/resource_arm_storage_table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,45 @@ func TestAccAzureRMStorageTable_disappears(t *testing.T) {
})
}

func TestAccAzureRMStorageTable_acl(t *testing.T) {
var table storage.Table

ri := tf.AccRandTimeInt()
rs := strings.ToLower(acctest.RandString(11))
location := testLocation()
resourceName := "azurerm_storage_table.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMStorageTableDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMStorageTable_acl(ri, rs, location),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMStorageTableExists(resourceName, &table),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccAzureRMStorageTable_aclUpdated(ri, rs, location),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMStorageTableExists(resourceName, &table),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testCheckAzureRMStorageTableExists(resourceName string, t *storage.Table) resource.TestCheckFunc {
return func(s *terraform.State) error {

Expand Down Expand Up @@ -291,3 +330,85 @@ resource "azurerm_storage_table" "import" {
}
`, template)
}

func testAccAzureRMStorageTable_acl(rInt int, rString string, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}
resource "azurerm_storage_account" "test" {
name = "acctestacc%s"
resource_group_name = "${azurerm_resource_group.test.name}"
location = "${azurerm_resource_group.test.location}"
account_tier = "Standard"
account_replication_type = "LRS"
tags = {
environment = "staging"
}
}
resource "azurerm_storage_table" "test" {
name = "acctestst%d"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_name = "${azurerm_storage_account.test.name}"
acl {
id = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI"
access_policy {
permissions = "raud"
start = "2020-11-26T08:49:37.0000000Z"
expiry = "2020-11-27T08:49:37.0000000Z"
}
}
}
`, rInt, location, rString, rInt)
}

func testAccAzureRMStorageTable_aclUpdated(rInt int, rString string, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}
resource "azurerm_storage_account" "test" {
name = "acctestacc%s"
resource_group_name = "${azurerm_resource_group.test.name}"
location = "${azurerm_resource_group.test.location}"
account_tier = "Standard"
account_replication_type = "LRS"
tags = {
environment = "staging"
}
}
resource "azurerm_storage_table" "test" {
name = "acctestst%d"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_name = "${azurerm_storage_account.test.name}"
acl {
id = "AAAANDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI"
access_policy {
permissions = "raud"
start = "2020-11-26T08:49:37.0000000Z"
expiry = "2020-11-27T08:49:37.0000000Z"
}
}
acl {
id = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI"
access_policy {
permissions = "raud"
start = "2019-07-02T09:38:21.0000000Z"
expiry = "2019-07-02T10:38:21.0000000Z"
}
}
}
`, rInt, location, rString, rInt)
}
21 changes: 21 additions & 0 deletions website/docs/r/storage_table.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,27 @@ The following arguments are supported:

* `resource_group_name` - (Optional / **Deprecated**) The name of the resource group in which to create the storage table.

* `acl` - (Optional) One or more `acl` blocks as defined below.

---

A `acl` block supports the following:

* `id` - (Required) The ID which should be used for this Shared Identifier.

* `access_policy` - (Required) An `access_policy` block as defined below.

---

A `access_policy` block supports the following:

* `expiry` - (Required) The ISO8061 UTC time at which this Access Policy should be valid until.

* `permissions` - (Required) The permissions which should associated with this Shared Identifier.

* `start` - (Required) The ISO8061 UTC time at which this Access Policy should be valid from.


## Attributes Reference

The following attributes are exported in addition to the arguments listed above:
Expand Down

0 comments on commit 8d926f0

Please sign in to comment.