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

Improvement: azurerm_storage_table ACL Support #3847

Merged
merged 1 commit into from
Jul 17, 2019
Merged
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
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