Skip to content

Commit

Permalink
Merge pull request #13396 from gmazelier/lakeformation_permissions
Browse files Browse the repository at this point in the history
Lake Formation Permissions
  • Loading branch information
YakDriver committed Dec 18, 2020
2 parents 049dbb1 + 7ca0739 commit 37e4818
Show file tree
Hide file tree
Showing 23 changed files with 2,513 additions and 100 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ FEATURES

* **New Data Source:** `aws_ec2_managed_prefix_list` [GH-16738]
* **New Data Source:** `aws_lakeformation_data_lake_settings` [GH-13250]
* **New Data Source:** `aws_lakeformation_permissions` [GH-13396]
* **New Data Source:** `aws_lakeformation_resource` [GH-13396]
* **New Resource:** `aws_codestarconnections_connection` [GH-15990]
* **New Resource:** `aws_ec2_managed_prefix_list` [GH-14068]
* **New Resource:** `aws_lakeformation_data_lake_settings` [GH-13250]
* **New Resource:** `aws_lakeformation_permissions` [GH-13396]
* **New Resource:** `aws_lakeformation_resource` [GH-13267]

ENHANCEMENTS
Expand Down
12 changes: 6 additions & 6 deletions aws/data_source_aws_lakeformation_data_lake_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ func dataSourceAwsLakeFormationDataLakeSettings() *schema.Resource {
Read: dataSourceAwsLakeFormationDataLakeSettingsRead,

Schema: map[string]*schema.Schema{
"admins": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"catalog_id": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -54,11 +59,6 @@ func dataSourceAwsLakeFormationDataLakeSettings() *schema.Resource {
},
},
},
"data_lake_admins": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"trusted_resource_owners": {
Type: schema.TypeList,
Computed: true,
Expand Down Expand Up @@ -98,7 +98,7 @@ func dataSourceAwsLakeFormationDataLakeSettingsRead(d *schema.ResourceData, meta

d.Set("create_database_default_permissions", flattenDataLakeSettingsCreateDefaultPermissions(settings.CreateDatabaseDefaultPermissions))
d.Set("create_table_default_permissions", flattenDataLakeSettingsCreateDefaultPermissions(settings.CreateTableDefaultPermissions))
d.Set("data_lake_admins", flattenDataLakeSettingsAdmins(settings.DataLakeAdmins))
d.Set("admins", flattenDataLakeSettingsAdmins(settings.DataLakeAdmins))
d.Set("trusted_resource_owners", flattenStringList(settings.TrustedResourceOwners))

return nil
Expand Down
22 changes: 4 additions & 18 deletions aws/data_source_aws_lakeformation_data_lake_settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,6 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccAWSLakeFormationDataLakeSettingsDataSource_serial(t *testing.T) {
testCases := map[string]func(t *testing.T){
"basic": testAccAWSLakeFormationDataLakeSettingsDataSource_basic,
// if more tests are added, they should be serial (data catalog is account-shared resource)
}

for name, tc := range testCases {
tc := tc
t.Run(name, func(t *testing.T) {
tc(t)
})
}
}

func testAccAWSLakeFormationDataLakeSettingsDataSource_basic(t *testing.T) {
callerIdentityName := "data.aws_caller_identity.current"
resourceName := "data.aws_lakeformation_data_lake_settings.test"
Expand All @@ -34,8 +20,8 @@ func testAccAWSLakeFormationDataLakeSettingsDataSource_basic(t *testing.T) {
Config: testAccAWSLakeFormationDataLakeSettingsDataSourceConfig_basic,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrPair(resourceName, "catalog_id", callerIdentityName, "account_id"),
resource.TestCheckResourceAttr(resourceName, "data_lake_admins.#", "1"),
resource.TestCheckResourceAttrPair(resourceName, "data_lake_admins.0", callerIdentityName, "arn"),
resource.TestCheckResourceAttr(resourceName, "admins.#", "1"),
resource.TestCheckResourceAttrPair(resourceName, "admins.0", callerIdentityName, "arn"),
),
},
},
Expand All @@ -46,8 +32,8 @@ const testAccAWSLakeFormationDataLakeSettingsDataSourceConfig_basic = `
data "aws_caller_identity" "current" {}
resource "aws_lakeformation_data_lake_settings" "test" {
catalog_id = data.aws_caller_identity.current.account_id
data_lake_admins = [data.aws_caller_identity.current.arn]
catalog_id = data.aws_caller_identity.current.account_id
admins = [data.aws_caller_identity.current.arn]
}
data "aws_lakeformation_data_lake_settings" "test" {
Expand Down
266 changes: 266 additions & 0 deletions aws/data_source_aws_lakeformation_permissions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
package aws

import (
"fmt"
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/lakeformation"
"github.com/hashicorp/aws-sdk-go-base/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/hashcode"
)

func dataSourceAwsLakeFormationPermissions() *schema.Resource {
return &schema.Resource{
Read: dataSourceAwsLakeFormationPermissionsRead,

Schema: map[string]*schema.Schema{
"catalog_id": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validateAwsAccountId,
},
"catalog_resource": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"data_location": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"arn": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validateArn,
},
"catalog_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validateAwsAccountId,
},
},
},
},
"database": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"catalog_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validateAwsAccountId,
},
"name": {
Type: schema.TypeString,
Required: true,
},
},
},
},
"permissions": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"permissions_with_grant_option": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"principal": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validatePrincipal,
},
"table": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"catalog_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validateAwsAccountId,
},
"database_name": {
Type: schema.TypeString,
Required: true,
},
"name": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"wildcard": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
},
},
"table_with_columns": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"catalog_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validateAwsAccountId,
},
"column_names": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.NoZeroValues,
},
},
"database_name": {
Type: schema.TypeString,
Required: true,
},
"excluded_column_names": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.NoZeroValues,
},
},
"name": {
Type: schema.TypeString,
Required: true,
},
},
},
},
},
}
}

func dataSourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lakeformationconn

input := &lakeformation.ListPermissionsInput{
Principal: &lakeformation.DataLakePrincipal{
DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)),
},
}

if v, ok := d.GetOk("catalog_id"); ok {
input.CatalogId = aws.String(v.(string))
}

input.Resource = expandLakeFormationResource(d, true)

log.Printf("[DEBUG] Reading Lake Formation permissions: %v", input)
var principalResourcePermissions []*lakeformation.PrincipalResourcePermissions

err := resource.Retry(2*time.Minute, func() *resource.RetryError {
err := conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool {
for _, permission := range resp.PrincipalResourcePermissions {
if permission == nil {
continue
}

principalResourcePermissions = append(principalResourcePermissions, permission)
}
return !lastPage
})

if err != nil {
if isAWSErr(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") {
return resource.RetryableError(err)
}
return resource.NonRetryableError(fmt.Errorf("error reading Lake Formation Permissions: %w", err))
}
return nil
})

if isResourceTimeoutError(err) {
err = conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool {
for _, permission := range resp.PrincipalResourcePermissions {
if permission == nil {
continue
}

principalResourcePermissions = append(principalResourcePermissions, permission)
}
return !lastPage
})
}

if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeEntityNotFoundException) {
log.Printf("[WARN] Resource Lake Formation permissions (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading Lake Formation permissions: %w", err)
}

if len(principalResourcePermissions) > 1 {
return fmt.Errorf("error reading Lake Formation permissions: %s", "multiple permissions found")
}

d.SetId(fmt.Sprintf("%d", hashcode.String(input.String())))
for _, permissions := range principalResourcePermissions {
d.Set("principal", permissions.Principal.DataLakePrincipalIdentifier)
d.Set("permissions", permissions.Permissions)
d.Set("permissions_with_grant_option", permissions.PermissionsWithGrantOption)

if permissions.Resource.Catalog != nil {
d.Set("catalog_resource", true)
}

if permissions.Resource.DataLocation != nil {
d.Set("data_location", []interface{}{flattenLakeFormationDataLocationResource(permissions.Resource.DataLocation)})
} else {
d.Set("data_location", nil)
}

if permissions.Resource.Database != nil {
d.Set("database", []interface{}{flattenLakeFormationDatabaseResource(permissions.Resource.Database)})
} else {
d.Set("database", nil)
}

// table with columns permissions will include the table and table with columns
if permissions.Resource.TableWithColumns != nil {
d.Set("table_with_columns", []interface{}{flattenLakeFormationTableWithColumnsResource(permissions.Resource.TableWithColumns)})
} else if permissions.Resource.Table != nil {
d.Set("table_with_columns", nil)
d.Set("table", []interface{}{flattenLakeFormationTableResource(permissions.Resource.Table)})
} else {
d.Set("table", nil)
}
}

return nil
}
Loading

0 comments on commit 37e4818

Please sign in to comment.