-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Allow ADLS File System to have ACLs added to the root #9917
Changes from all commits
3cb04cc
139b8eb
f153811
56c016f
649298e
e6df439
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,69 @@ | ||||||||||||||
package parse | ||||||||||||||
|
||||||||||||||
import ( | ||||||||||||||
"github.com/google/uuid" | ||||||||||||||
"github.com/tombuildsstuff/giovanni/storage/accesscontrol" | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
func ExpandDataLakeGen2AceList(input []interface{}) (*accesscontrol.ACL, error) { | ||||||||||||||
if len(input) == 0 { | ||||||||||||||
return nil, nil | ||||||||||||||
} | ||||||||||||||
aceList := make([]accesscontrol.ACE, len(input)) | ||||||||||||||
|
||||||||||||||
for i := 0; i < len(input); i++ { | ||||||||||||||
v := input[i].(map[string]interface{}) | ||||||||||||||
|
||||||||||||||
isDefault := false | ||||||||||||||
if scopeRaw, ok := v["scope"]; ok { | ||||||||||||||
if scopeRaw.(string) == "default" { | ||||||||||||||
isDefault = true | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
tagType := accesscontrol.TagType(v["type"].(string)) | ||||||||||||||
|
||||||||||||||
var id *uuid.UUID | ||||||||||||||
if raw, ok := v["id"]; ok && raw != "" { | ||||||||||||||
idTemp, err := uuid.Parse(raw.(string)) | ||||||||||||||
if err != nil { | ||||||||||||||
return nil, err | ||||||||||||||
} | ||||||||||||||
id = &idTemp | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
permissions := v["permissions"].(string) | ||||||||||||||
|
||||||||||||||
ace := accesscontrol.ACE{ | ||||||||||||||
IsDefault: isDefault, | ||||||||||||||
TagType: tagType, | ||||||||||||||
TagQualifier: id, | ||||||||||||||
Permissions: permissions, | ||||||||||||||
} | ||||||||||||||
aceList[i] = ace | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
return &accesscontrol.ACL{Entries: aceList}, nil | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
func FlattenDataLakeGen2AceList(acl accesscontrol.ACL) []interface{} { | ||||||||||||||
output := make([]interface{}, len(acl.Entries)) | ||||||||||||||
|
||||||||||||||
for i, v := range acl.Entries { | ||||||||||||||
ace := make(map[string]interface{}) | ||||||||||||||
|
||||||||||||||
scope := "access" | ||||||||||||||
if v.IsDefault { | ||||||||||||||
scope = "default" | ||||||||||||||
} | ||||||||||||||
ace["scope"] = scope | ||||||||||||||
ace["type"] = string(v.TagType) | ||||||||||||||
if v.TagQualifier != nil { | ||||||||||||||
ace["id"] = v.TagQualifier.String() | ||||||||||||||
} | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the
Suggested change
|
||||||||||||||
ace["permissions"] = v.Permissions | ||||||||||||||
|
||||||||||||||
output[i] = ace | ||||||||||||||
} | ||||||||||||||
return output | ||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -8,13 +8,16 @@ import ( | |||
"time" | ||||
|
||||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||||
"github.com/hashicorp/terraform-plugin-sdk/helper/validation" | ||||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" | ||||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" | ||||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage/parse" | ||||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/storage/validate" | ||||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" | ||||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" | ||||
"github.com/tombuildsstuff/giovanni/storage/2019-12-12/datalakestore/filesystems" | ||||
"github.com/tombuildsstuff/giovanni/storage/2019-12-12/datalakestore/paths" | ||||
"github.com/tombuildsstuff/giovanni/storage/accesscontrol" | ||||
) | ||||
|
||||
func resourceStorageDataLakeGen2FileSystem() *schema.Resource { | ||||
|
@@ -73,13 +76,45 @@ func resourceStorageDataLakeGen2FileSystem() *schema.Resource { | |||
}, | ||||
|
||||
"properties": MetaDataSchema(), | ||||
|
||||
"ace": { | ||||
Type: schema.TypeList, | ||||
Optional: true, | ||||
Computed: true, | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can remove this, since this shouldn't be configured unless a user opts to do so:
Suggested change
|
||||
Elem: &schema.Resource{ | ||||
Schema: map[string]*schema.Schema{ | ||||
"scope": { | ||||
Type: schema.TypeString, | ||||
Optional: true, | ||||
ValidateFunc: validation.StringInSlice([]string{"default", "access"}, false), | ||||
Default: "access", | ||||
}, | ||||
"type": { | ||||
Type: schema.TypeString, | ||||
Required: true, | ||||
ValidateFunc: validation.StringInSlice([]string{"user", "group", "mask", "other"}, false), | ||||
}, | ||||
"id": { | ||||
Type: schema.TypeString, | ||||
Optional: true, | ||||
ValidateFunc: validation.IsUUID, | ||||
}, | ||||
"permissions": { | ||||
Type: schema.TypeString, | ||||
Required: true, | ||||
ValidateFunc: validate.ADLSAccessControlPermissions, | ||||
}, | ||||
}, | ||||
}, | ||||
}, | ||||
}, | ||||
} | ||||
} | ||||
|
||||
func resourceStorageDataLakeGen2FileSystemCreate(d *schema.ResourceData, meta interface{}) error { | ||||
accountsClient := meta.(*clients.Client).Storage.AccountsClient | ||||
client := meta.(*clients.Client).Storage.FileSystemsClient | ||||
pathClient := meta.(*clients.Client).Storage.ADLSGen2PathsClient | ||||
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) | ||||
defer cancel() | ||||
|
||||
|
@@ -88,6 +123,12 @@ func resourceStorageDataLakeGen2FileSystemCreate(d *schema.ResourceData, meta in | |||
return err | ||||
} | ||||
|
||||
aceRaw := d.Get("ace").([]interface{}) | ||||
acl, err := parse.ExpandDataLakeGen2AceList(aceRaw) | ||||
if err != nil { | ||||
return fmt.Errorf("Error parsing ace list: %s", err) | ||||
} | ||||
|
||||
// confirm the storage account exists, otherwise Data Plane API requests will fail | ||||
storageAccount, err := accountsClient.GetProperties(ctx, storageID.ResourceGroup, storageID.Name, "") | ||||
if err != nil { | ||||
|
@@ -123,13 +164,29 @@ func resourceStorageDataLakeGen2FileSystemCreate(d *schema.ResourceData, meta in | |||
return fmt.Errorf("Error creating File System %q in Storage Account %q: %s", fileSystemName, storageID.Name, err) | ||||
} | ||||
|
||||
if acl != nil { | ||||
log.Printf("[INFO] Creating acl %q in File System %q in Storage Account %q.", acl, fileSystemName, storageID.Name) | ||||
var aclString *string | ||||
v := acl.String() | ||||
aclString = &v | ||||
accessControlInput := paths.SetAccessControlInput{ | ||||
ACL: aclString, | ||||
Owner: nil, | ||||
Group: nil, | ||||
} | ||||
if _, err := pathClient.SetAccessControl(ctx, storageID.Name, fileSystemName, "/", accessControlInput); err != nil { | ||||
return fmt.Errorf("Error setting access control for root path in File System %q in Storage Account %q: %s", fileSystemName, storageID.Name, err) | ||||
} | ||||
} | ||||
|
||||
d.SetId(id) | ||||
return resourceStorageDataLakeGen2FileSystemRead(d, meta) | ||||
} | ||||
|
||||
func resourceStorageDataLakeGen2FileSystemUpdate(d *schema.ResourceData, meta interface{}) error { | ||||
accountsClient := meta.(*clients.Client).Storage.AccountsClient | ||||
client := meta.(*clients.Client).Storage.FileSystemsClient | ||||
pathClient := meta.(*clients.Client).Storage.ADLSGen2PathsClient | ||||
ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) | ||||
defer cancel() | ||||
|
||||
|
@@ -143,6 +200,12 @@ func resourceStorageDataLakeGen2FileSystemUpdate(d *schema.ResourceData, meta in | |||
return err | ||||
} | ||||
|
||||
aceRaw := d.Get("ace").([]interface{}) | ||||
acl, err := parse.ExpandDataLakeGen2AceList(aceRaw) | ||||
if err != nil { | ||||
return fmt.Errorf("Error parsing ace list: %s", err) | ||||
} | ||||
|
||||
// confirm the storage account exists, otherwise Data Plane API requests will fail | ||||
storageAccount, err := accountsClient.GetProperties(ctx, storageID.ResourceGroup, storageID.Name, "") | ||||
if err != nil { | ||||
|
@@ -164,12 +227,28 @@ func resourceStorageDataLakeGen2FileSystemUpdate(d *schema.ResourceData, meta in | |||
return fmt.Errorf("Error updating Properties for File System %q in Storage Account %q: %s", id.DirectoryName, id.AccountName, err) | ||||
} | ||||
|
||||
if acl != nil { | ||||
log.Printf("[INFO] Creating acl %q in File System %q in Storage Account %q.", acl, id.DirectoryName, id.AccountName) | ||||
var aclString *string | ||||
v := acl.String() | ||||
aclString = &v | ||||
accessControlInput := paths.SetAccessControlInput{ | ||||
ACL: aclString, | ||||
Owner: nil, | ||||
Group: nil, | ||||
} | ||||
if _, err := pathClient.SetAccessControl(ctx, id.AccountName, id.DirectoryName, "/", accessControlInput); err != nil { | ||||
return fmt.Errorf("Error setting access control for root path in File System %q in Storage Account %q: %s", id.DirectoryName, id.AccountName, err) | ||||
} | ||||
} | ||||
|
||||
return resourceStorageDataLakeGen2FileSystemRead(d, meta) | ||||
} | ||||
|
||||
func resourceStorageDataLakeGen2FileSystemRead(d *schema.ResourceData, meta interface{}) error { | ||||
accountsClient := meta.(*clients.Client).Storage.AccountsClient | ||||
client := meta.(*clients.Client).Storage.FileSystemsClient | ||||
pathClient := meta.(*clients.Client).Storage.ADLSGen2PathsClient | ||||
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) | ||||
defer cancel() | ||||
|
||||
|
@@ -213,6 +292,25 @@ func resourceStorageDataLakeGen2FileSystemRead(d *schema.ResourceData, meta inte | |||
return fmt.Errorf("Error setting `properties`: %+v", err) | ||||
} | ||||
|
||||
// The above `getStatus` API request doesn't return the ACLs | ||||
// Have to make a `getAccessControl` request, but that doesn't return all fields either! | ||||
pathResponse, err := pathClient.GetProperties(ctx, id.AccountName, id.DirectoryName, "/", paths.GetPropertiesActionGetAccessControl) | ||||
if err != nil { | ||||
if utils.ResponseWasNotFound(pathResponse.Response) { | ||||
log.Printf("[INFO] Root path does not exist in File System %q in Storage Account %q - removing from state...", id.DirectoryName, id.AccountName) | ||||
d.SetId("") | ||||
return nil | ||||
} | ||||
|
||||
return fmt.Errorf("Error retrieving ACLs for Root path in File System %q in Storage Account %q: %+v", id.DirectoryName, id.AccountName, err) | ||||
} | ||||
|
||||
acl, err := accesscontrol.ParseACL(pathResponse.ACL) | ||||
if err != nil { | ||||
return fmt.Errorf("Error parsing response ACL %q: %s", pathResponse.ACL, err) | ||||
} | ||||
d.Set("ace", parse.FlattenDataLakeGen2AceList(acl)) | ||||
|
||||
return nil | ||||
} | ||||
|
||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor we're using the
parse
folder for Resource ID Parsers rather than for Schema -> Object parsing - could we move this back up a level