From 431f32bc8b69d559d719531aa546f053d7e5a758 Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Sat, 9 May 2020 17:42:26 +0200 Subject: [PATCH 01/31] New resource: aws_lakeformation_datalake_settings --- ...rce_aws_lakeformation_datalake_settings.go | 135 ++++++++++++++++++ ...ws_lakeformation_datalake_settings_test.go | 66 +++++++++ 2 files changed, 201 insertions(+) create mode 100644 aws/resource_aws_lakeformation_datalake_settings.go create mode 100644 aws/resource_aws_lakeformation_datalake_settings_test.go diff --git a/aws/resource_aws_lakeformation_datalake_settings.go b/aws/resource_aws_lakeformation_datalake_settings.go new file mode 100644 index 000000000000..c708abcef59c --- /dev/null +++ b/aws/resource_aws_lakeformation_datalake_settings.go @@ -0,0 +1,135 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func resourceAwsLakeFormationDataLakeSettings() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsLakeFormationDataLakeSettingsPut, + Update: resourceAwsLakeFormationDataLakeSettingsPut, + Read: resourceAwsLakeFormationDataLakeSettingsRead, + Delete: resourceAwsLakeFormationDataLakeSettingsReset, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Computed: true, + }, + "admins": { + Type: schema.TypeList, + Required: true, + MinItems: 0, + MaxItems: 10, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + }, + }, + } +} + +func resourceAwsLakeFormationDataLakeSettingsPut(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + catalogId := createAwsDataCatalogId(d, meta.(*AWSClient).accountid) + + input := &lakeformation.PutDataLakeSettingsInput{ + CatalogId: aws.String(catalogId), + DataLakeSettings: &lakeformation.DataLakeSettings{ + DataLakeAdmins: expandAdmins(d), + }, + } + + _, err := conn.PutDataLakeSettings(input) + if err != nil { + return fmt.Errorf("Error updating DataLakeSettings: %s", err) + } + + d.SetId(fmt.Sprintf("lakeformation:settings:%s", catalogId)) + d.Set("catalog_id", catalogId) + + return resourceAwsLakeFormationDataLakeSettingsRead(d, meta) +} + +func resourceAwsLakeFormationDataLakeSettingsRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + catalogId := d.Get("catalog_id").(string) + + input := &lakeformation.GetDataLakeSettingsInput{ + CatalogId: aws.String(catalogId), + } + + out, err := conn.GetDataLakeSettings(input) + if err != nil { + return fmt.Errorf("Error reading DataLakeSettings: %s", err) + } + + d.Set("catalog_id", catalogId) + if err := d.Set("admins", flattenAdmins(out.DataLakeSettings.DataLakeAdmins)); err != nil { + return fmt.Errorf("Error setting admins from DataLakeSettings: %s", err) + } + // TODO: Add CreateDatabaseDefaultPermissions and CreateTableDefaultPermissions + + return nil +} + +func resourceAwsLakeFormationDataLakeSettingsReset(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + catalogId := d.Get("catalog_id").(string) + + input := &lakeformation.PutDataLakeSettingsInput{ + CatalogId: aws.String(catalogId), + DataLakeSettings: &lakeformation.DataLakeSettings{ + DataLakeAdmins: make([]*lakeformation.DataLakePrincipal, 0), + }, + } + + _, err := conn.PutDataLakeSettings(input) + if err != nil { + return fmt.Errorf("Error reseting DataLakeSettings: %s", err) + } + + return nil +} + +func createAwsDataCatalogId(d *schema.ResourceData, accountId string) (catalogId string) { + if inputCatalogId, ok := d.GetOkExists("catalog_id"); ok { + catalogId = inputCatalogId.(string) + } else { + catalogId = accountId + } + return +} + +func expandAdmins(d *schema.ResourceData) []*lakeformation.DataLakePrincipal { + xs := d.Get("admins") + ys := make([]*lakeformation.DataLakePrincipal, len(xs.([]interface{}))) + + for i, x := range xs.([]interface{}) { + ys[i] = &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(x.(string)), + } + } + + return ys +} + +func flattenAdmins(xs []*lakeformation.DataLakePrincipal) []string { + admins := make([]string, len(xs)) + for i, x := range xs { + admins[i] = aws.StringValue(x.DataLakePrincipalIdentifier) + } + + return admins +} diff --git a/aws/resource_aws_lakeformation_datalake_settings_test.go b/aws/resource_aws_lakeformation_datalake_settings_test.go new file mode 100644 index 000000000000..d2ac3b0c5382 --- /dev/null +++ b/aws/resource_aws_lakeformation_datalake_settings_test.go @@ -0,0 +1,66 @@ +package aws + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccAWSLakeFormationDataLakeSettings_basic(t *testing.T) { + callerIdentityName := "data.aws_caller_identity.current" + resourceName := "aws_lakeformation_datalake_settings.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + // TODO: CheckDestroy: testAccCheckAWSLakeFormationDataLakeSettingsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationDataLakeSettingsConfig_basic, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), + resource.TestCheckResourceAttr(resourceName, "admins.#", "1"), + resource.TestCheckResourceAttrPair(callerIdentityName, "arn", resourceName, "admins.0"), + ), + }, + }, + }) +} + +const testAccAWSLakeFormationDataLakeSettingsConfig_basic = ` +data "aws_caller_identity" "current" {} + +resource "aws_lakeformation_datalake_settings" "test" { + admins = ["${data.aws_caller_identity.current.arn}"] +} +` + +func TestAccAWSLakeFormationDataLakeSettings_withCatalogId(t *testing.T) { + callerIdentityName := "data.aws_caller_identity.current" + resourceName := "aws_lakeformation_datalake_settings.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + // TODO: CheckDestroy: testAccCheckAWSLakeFormationDataLakeSettingsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationDataLakeSettingsConfig_withCatalogId, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), + resource.TestCheckResourceAttr(resourceName, "admins.#", "1"), + resource.TestCheckResourceAttrPair(callerIdentityName, "arn", resourceName, "admins.0"), + ), + }, + }, + }) +} + +const testAccAWSLakeFormationDataLakeSettingsConfig_withCatalogId = ` +data "aws_caller_identity" "current" {} + +resource "aws_lakeformation_datalake_settings" "test" { + catalog_id = "${data.aws_caller_identity.current.account_id}" + admins = ["${data.aws_caller_identity.current.arn}"] +} +` From 3dcb295ffbb7d0f4975b494fe39285e2bebf2732 Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Sun, 10 May 2020 14:48:21 +0200 Subject: [PATCH 02/31] Add resource documentation --- website/aws.erb | 3629 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3629 insertions(+) create mode 100644 website/aws.erb diff --git a/website/aws.erb b/website/aws.erb new file mode 100644 index 000000000000..d8a0680afd42 --- /dev/null +++ b/website/aws.erb @@ -0,0 +1,3629 @@ +<% wrap_layout :inner do %> + <% content_for :sidebar do %> + + <% end %> + <%= yield %> +<% end %> From ed879d584efefd8de572f20886ae093162e31153 Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Sun, 10 May 2020 19:09:31 +0200 Subject: [PATCH 03/31] New resource: aws_lakeformation_permissions (WIP) --- aws/provider.go | 1 + aws/resource_aws_lakeformation_permissions.go | 314 ++++++++++++++++++ ...urce_aws_lakeformation_permissions_test.go | 245 ++++++++++++++ 3 files changed, 560 insertions(+) create mode 100644 aws/resource_aws_lakeformation_permissions.go create mode 100644 aws/resource_aws_lakeformation_permissions_test.go diff --git a/aws/provider.go b/aws/provider.go index 06cc9a18dd81..476b23c49548 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -747,6 +747,7 @@ func Provider() *schema.Provider { "aws_kms_key": resourceAwsKmsKey(), "aws_kms_ciphertext": resourceAwsKmsCiphertext(), "aws_lakeformation_data_lake_settings": resourceAwsLakeFormationDataLakeSettings(), + "aws_lakeformation_permissions": resourceAwsLakeFormationPermissions(), "aws_lakeformation_resource": resourceAwsLakeFormationResource(), "aws_lambda_alias": resourceAwsLambdaAlias(), "aws_lambda_code_signing_config": resourceAwsLambdaCodeSigningConfig(), diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go new file mode 100644 index 000000000000..bca0637660f9 --- /dev/null +++ b/aws/resource_aws_lakeformation_permissions.go @@ -0,0 +1,314 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func AwsLakeFormationPermissions() []string { + return []string{ + "ALL", + "SELECT", + "ALTER", + "DROP", + "DELETE", + "INSERT", + "CREATE_DATABASE", + "CREATE_TABLE", + "DATA_LOCATION_ACCESS", + } +} + +func resourceAwsLakeFormationPermissions() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsLakeFormationPermissionsGrant, + Read: resourceAwsLakeFormationPermissionsList, + Delete: resourceAwsLakeFormationPermissionsRevoke, + + Schema: map[string]*schema.Schema{ + "catalog_id": { + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + "permissions": { + Type: schema.TypeList, + Required: true, + ForceNew: true, + MinItems: 1, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice(AwsLakeFormationPermissions(), false), + }, + }, + "permissions_with_grant_option": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringInSlice(AwsLakeFormationPermissions(), false), + }, + }, + "principal": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + "database": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"location", "table"}, + }, + "location": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateArn, + ConflictsWith: []string{"database", "table"}, + }, + "table": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"database", "location"}, + MinItems: 0, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "database": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.NoZeroValues, + }, + "column_names": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + }, + "excluded_column_names": { + Type: schema.TypeList, + Optional: true, + ForceNew: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.NoZeroValues, + }, + }, + }, + }, + }, + }, + } +} + +func resourceAwsLakeFormationPermissionsGrant(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + catalogId := createAwsDataCatalogId(d, meta.(*AWSClient).accountid) + resource := expandAwsLakeFormationResource(d) + + input := &lakeformation.GrantPermissionsInput{ + CatalogId: aws.String(catalogId), + Permissions: expandStringList(d.Get("permissions").([]interface{})), + Principal: expandAwsLakeFormationPrincipal(d), + Resource: resource, + } + if vs, ok := d.GetOk("permissions_with_grant_option"); ok { + input.PermissionsWithGrantOption = expandStringList(vs.([]interface{})) + } + + // Catalog: CREATE_DATABASE + // Location: DATA_LOCATION_ACCESS + // Database: ALTER, CREATE_TABLE, DROP, (ALL ~ Super) + // Table: ALTER, INSERT, DELETE, DROP, SELECT, (ALL ~ Super) + // TableWithColumns: SELECT + + _, err := conn.GrantPermissions(input) + if err != nil { + return fmt.Errorf("Error granting LakeFormation Permissions: %s", err) + } + + d.SetId(fmt.Sprintf("lakeformation:resource:%s", catalogId)) // FIXME + d.Set("catalog_id", catalogId) + + return resourceAwsLakeFormationPermissionsList(d, meta) +} + +func resourceAwsLakeFormationPermissionsList(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + catalogId := d.Get("catalog_id").(string) + + input := &lakeformation.ListPermissionsInput{ + CatalogId: aws.String(catalogId), + Principal: expandAwsLakeFormationPrincipal(d), + Resource: expandAwsLakeFormationResource(d), + } + + out, err := conn.ListPermissions(input) + if err != nil { + return fmt.Errorf("Error listing LakeFormation Permissions: %s", err) + } + + permissions := out.PrincipalResourcePermissions + if len(permissions) == 0 { + return fmt.Errorf("Error no LakeFormation Permissions found: %s", input) + } + + permissionsHead := permissions[0] // XXX: assuming there is only one result in the list + d.Set("catalog_id", catalogId) + d.Set("permissions", permissionsHead.Permissions) + d.Set("permissions_with_grant_option", permissionsHead.PermissionsWithGrantOption) + d.Set("principal", permissionsHead.Principal.DataLakePrincipalIdentifier) + if dataLocation := permissionsHead.Resource.DataLocation; dataLocation != nil { + d.Set("location", dataLocation.ResourceArn) + } + if database := permissionsHead.Resource.Database; database != nil { + d.Set("database", database.Name) + } + if table := permissionsHead.Resource.Table; table != nil { + d.Set("table", flattenAWSLakeFormationTable(table)) + } + if table := permissionsHead.Resource.TableWithColumns; table != nil { + d.Set("table", flattenAWSLakeFormationTableWithColumns(table)) + } + + return nil +} + +func resourceAwsLakeFormationPermissionsRevoke(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + catalogId := d.Get("catalog_id").(string) + + input := &lakeformation.RevokePermissionsInput{ + CatalogId: aws.String(catalogId), + Permissions: expandStringList(d.Get("permissions").([]interface{})), + Principal: expandAwsLakeFormationPrincipal(d), + Resource: expandAwsLakeFormationResource(d), + } + if vs, ok := d.GetOk("permissions_with_grant_option"); ok { + input.PermissionsWithGrantOption = expandStringList(vs.([]interface{})) + } + + _, err := conn.RevokePermissions(input) + if err != nil { + return fmt.Errorf("Error revoking LakeFormation Permissions: %s", err) + } + + return nil +} + +func expandAwsLakeFormationPrincipal(d *schema.ResourceData) *lakeformation.DataLakePrincipal { + return &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), + } +} + +func expandAwsLakeFormationResource(d *schema.ResourceData) *lakeformation.Resource { + resource := &lakeformation.Resource{ + // Catalog: &lakeformation.CatalogResource{}, + } + if v, ok := d.GetOk("database"); ok { + databaseName := v.(string) + if len(databaseName) > 0 { + resource.Database = &lakeformation.DatabaseResource{ + Name: aws.String(databaseName), + } + } + } + if v, ok := d.GetOk("location"); ok { + location := v.(string) + if len(location) > 0 { + resource.DataLocation = &lakeformation.DataLocationResource{ + ResourceArn: aws.String(v.(string)), + } + } + } + if vs, ok := d.GetOk("table"); ok { + tables := vs.([]interface{}) + if len(tables) > 0 { + table := tables[0].(map[string]interface{}) + + var databaseName, tableName string + var columnNames, excludedColumnNames []interface{} + if x, ok := table["database"]; ok { + databaseName = x.(string) + } + if x, ok := table["name"]; ok { + tableName = x.(string) + } + if xs, ok := table["column_names"]; ok { + columnNames = xs.([]interface{}) + } + if xs, ok := table["excluded_column_names"]; ok { + excludedColumnNames = xs.([]interface{}) + } + + if len(columnNames) > 0 || len(excludedColumnNames) > 0 { + tableWithColumns := &lakeformation.TableWithColumnsResource{ + DatabaseName: aws.String(databaseName), + Name: aws.String(tableName), + } + if len(columnNames) > 0 { + tableWithColumns.ColumnNames = expandStringList(columnNames) + } + if len(excludedColumnNames) > 0 { + tableWithColumns.ColumnWildcard = &lakeformation.ColumnWildcard{ + ExcludedColumnNames: expandStringList(excludedColumnNames), + } + } + resource.TableWithColumns = tableWithColumns + } else { + resource.Table = &lakeformation.TableResource{ + DatabaseName: aws.String(databaseName), + Name: aws.String(tableName), + } + } + } + } + + return resource +} + +func flattenAWSLakeFormationTable(tb *lakeformation.TableResource) map[string]interface{} { + m := make(map[string]interface{}) + + m["database"] = tb.DatabaseName + m["name"] = tb.Name + + return m +} + +func flattenAWSLakeFormationTableWithColumns(tb *lakeformation.TableWithColumnsResource) map[string]interface{} { + m := make(map[string]interface{}) + + m["database"] = tb.DatabaseName + m["name"] = tb.Name + if columnNames := tb.ColumnNames; columnNames != nil { + m["column_names"] = columnNames + } + if columnWildcard := tb.ColumnWildcard; columnWildcard != nil { + m["excluded_column_names"] = columnWildcard.ExcludedColumnNames + } + + return m +} diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go new file mode 100644 index 000000000000..c1e20c10ee7c --- /dev/null +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -0,0 +1,245 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" +) + +func TestAccAWSLakeFormationPermissions_full(t *testing.T) { + rName := acctest.RandomWithPrefix("lakeformation-test-bucket") + dName := acctest.RandomWithPrefix("lakeformation-test-db") + // tName := acctest.RandomWithPrefix("lakeformation-test-table") + + callerIdentityName := "data.aws_caller_identity.current" + roleName := "data.aws_iam_role.test" + resourceName := "aws_lakeformation_permissions.test" + bucketName := "aws_s3_bucket.test" + dbName := "aws_glue_catalog_database.test" + // tableName := "aws_glue_catalog_table.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + // TODO: CheckDestroy + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_location(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttrPair(bucketName, "arn", resourceName, "location"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", "DATA_LOCATION_ACCESS"), + ), + }, + { + Config: testAccAWSLakeFormationPermissionsConfig_database(rName, dName), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttrPair(dbName, "name", resourceName, "database"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALTER"), + resource.TestCheckResourceAttr(resourceName, "permissions.1", "CREATE_TABLE"), + resource.TestCheckResourceAttr(resourceName, "permissions.2", "DROP"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", "CREATE_TABLE"), + ), + }, + // FIXME: more than one permission in API read result + // { + // Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + // resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), + // resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + // resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + // resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), + // resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), + // resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + // resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), + // ), + // }, + // FIXME: WIP + // { + // Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName), + // Check: resource.ComposeTestCheckFunc( + // testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + // resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), + // resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + // resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + // resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), + // resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), + // resource.TestCheckResourceAttr(resourceName, "table.0.column_names.#", "2"), + // resource.TestCheckResourceAttr(resourceName, "table.0.column_names.0", "event"), + // resource.TestCheckResourceAttr(resourceName, "table.0.column_names.1", "timestamp"), + // resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + // resource.TestCheckResourceAttr(resourceName, "permissions.0", "SELECT"), + // ), + // }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsConfig_location(rName string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +data "aws_iam_role" "test" { + name = "AWSServiceRoleForLakeFormationDataAccess" +} + +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + +resource "aws_lakeformation_datalake_settings" "test" { + admins = [ + data.aws_caller_identity.current.arn + ] +} + +resource "aws_lakeformation_resource" "test" { + resource_arn = aws_s3_bucket.test.arn + use_service_linked_role = true +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["DATA_LOCATION_ACCESS"] + principal = data.aws_iam_role.test.arn + + location = aws_lakeformation_resource.test.resource_arn +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_database(rName, dName string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +data "aws_iam_role" "test" { + name = "AWSServiceRoleForLakeFormationDataAccess" +} + +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + +resource "aws_glue_catalog_database" "test" { + name = %[2]q +} + +resource "aws_lakeformation_datalake_settings" "test" { + admins = [ + data.aws_caller_identity.current.arn + ] +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["ALTER", "CREATE_TABLE", "DROP"] + permissions_with_grant_option = ["CREATE_TABLE"] + principal = data.aws_iam_role.test.arn + + database = aws_glue_catalog_database.test.name +} +`, rName, dName) +} + +// func testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName string) string { +// return fmt.Sprintf(` +// data "aws_caller_identity" "current" {} + +// data "aws_iam_role" "test" { +// name = "AWSServiceRoleForLakeFormationDataAccess" +// } + +// resource "aws_s3_bucket" "test" { +// bucket = %[1]q +// } + +// resource "aws_glue_catalog_database" "test" { +// name = %[2]q +// } + +// resource "aws_glue_catalog_table" "test" { +// name = %[3]q +// database_name = aws_glue_catalog_database.test.name +// } + +// resource "aws_lakeformation_datalake_settings" "test" { +// admins = [ +// data.aws_caller_identity.current.arn +// ] +// } + +// resource "aws_lakeformation_permissions" "test" { +// permissions = ["SELECT"] +// principal = data.aws_iam_role.test.arn + +// table { +// database = aws_glue_catalog_table.test.database_name +// name = aws_glue_catalog_table.test.name +// } +// } +// `, rName, dName, tName) +// } + +// func testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName string) string { +// return fmt.Sprintf(` +// data "aws_caller_identity" "current" {} + +// data "aws_iam_role" "test" { +// name = "AWSServiceRoleForLakeFormationDataAccess" +// } + +// resource "aws_s3_bucket" "test" { +// bucket = %[1]q +// } + +// resource "aws_glue_catalog_database" "test" { +// name = %[2]q +// } + +// resource "aws_glue_catalog_table" "test" { +// name = %[3]q +// database_name = aws_glue_catalog_database.test.name + +// storage_descriptor { +// columns { +// name = "event" +// type = "string" +// } +// columns { +// name = "timestamp" +// type = "date" +// } +// columns { +// name = "value" +// type = "double" +// } +// } +// } + +// resource "aws_lakeformation_datalake_settings" "test" { +// admins = [ +// data.aws_caller_identity.current.arn +// ] +// } + +// resource "aws_lakeformation_permissions" "test" { +// permissions = ["SELECT"] +// principal = data.aws_iam_role.test.arn + +// table { +// database = aws_glue_catalog_table.test.database_name +// name = aws_glue_catalog_table.test.name +// column_names = ["event", "timestamp"] +// } +// } +// `, rName, dName, tName) +// } From 01999bd83253da0880ebd9b1a1abb9b823b839bc Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Wed, 20 May 2020 00:17:32 +0200 Subject: [PATCH 04/31] Add documentation --- website/aws.erb | 3 + .../r/lakeformation_permissions.html.markdown | 180 ++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100644 website/docs/r/lakeformation_permissions.html.markdown diff --git a/website/aws.erb b/website/aws.erb index d8a0680afd42..0fad8c5a6a3e 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -2093,6 +2093,9 @@
  • aws_lakeformation_datalake_settings
  • +
  • + aws_lakeformation_permissions +
  • aws_lakeformation_resource
  • diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown new file mode 100644 index 000000000000..31e4f6918e5e --- /dev/null +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -0,0 +1,180 @@ +--- +subcategory: "Lake Formation" +layout: "aws" +page_title: "AWS: aws_lakeformation_permissions" +description: |- + Manages the permissions that a principal has on an AWS Glue Data Catalog resource (such as AWS Glue database or AWS Glue tables) +--- + +# Resource: aws_lakeformation_resource + +Manages the permissions that a principal has on an AWS Glue Data Catalog resource (such as AWS Glue database or AWS Glue tables). + +## Example Usage + +### Granting permissions on Lake Formation resource + +```hcl +data "aws_iam_role" "example" { + name = "existing_lakeformation_role" +} + +data "aws_s3_bucket" "example" { + bucket = "existing_bucket" +} + +resource "aws_lakeformation_resource" "example" { + resource_arn = data.aws_s3_bucket.example.arn + use_service_linked_role = true +} + +resource "aws_lakeformation_permissions" "example" { + permissions = ["DATA_LOCATION_ACCESS"] + principal = data.aws_iam_role.example.arn + + location = aws_lakeformation_resource.example.resource_arn +} +``` + +### Granting permissions on Lake Formation database + +```hcl +data "aws_iam_role" "example" { + name = "existing_lakeformation_role" +} + +resource "aws_glue_catalog_database" "example" { + name = "example_database" +} + +resource "aws_lakeformation_permissions" "example" { + permissions = ["ALTER", "CREATE_TABLE", "DROP"] + principal = data.aws_iam_role.example.arn + + database = aws_glue_catalog_database.example.name +} +``` + +### Granting permissions on Lake Formation table + +```hcl +data "aws_iam_role" "example" { + name = "existing_lakeformation_role" +} + +resource "aws_glue_catalog_database" "example" { + name = "example_database" +} + +resource "aws_glue_catalog_table" "example" { + name = "example_table" + database_name = aws_glue_catalog_database.example.name +} + +resource "aws_lakeformation_permissions" "example" { + permissions = ["INSERT", "DELETE", "SELECT"] + permissions_with_grant_option = ["SELECT"] + principal = data.aws_iam_role.example.arn + + table { + database = aws_glue_catalog_table.example.database_name + name = aws_glue_catalog_table.example.name + } +} +``` + +### Granting permissions on Lake Formation columns + +```hcl +data "aws_iam_role" "example" { + name = "existing_lakeformation_role" +} + +resource "aws_glue_catalog_database" "example" { + name = "example_database" +} + +resource "aws_glue_catalog_table" "example" { + name = "example_table" + database_name = aws_glue_catalog_database.example.name + + storage_descriptor { + columns { + name = "event" + type = "string" + } + columns { + name = "timestamp" + type = "date" + } + columns { + name = "value" + type = "double" + } + } +} + +resource "aws_lakeformation_permissions" "example" { + permissions = [""SELECT"] + principal = data.aws_iam_role.example.arn + + table { + database = aws_glue_catalog_table.example.database_name + name = aws_glue_catalog_table.example.name + column_names = ["event", "timestamp"] + } +} +``` + +## Argument Reference + +The following arguments are required: + +* `permissions` – (Required) The permissions granted. + +* `principal` – (Required) The AWS Lake Formation principal. + +The following arguments are optional: + +* `catalog_id` – (Optional) The identifier for the Data Catalog. By default, the account ID. + +* `permissions_with_grant_option` – (Optional) Indicates whether to grant the ability to grant permissions (as a subset of permissions granted)s. + +* `database` – (Optional) The name of the database resource. Unique to the Data Catalog. A database is a set of associated table definitions organized into a logical group. + +* `location` – (Optional) The Amazon Resource Name (ARN) of the resource (data location). + +* `table` – (Optional) A structure for the table object. A table is a metadata definition that represents your data. + +At least one of `database`, `location`, `table` must be specified. + +The `table` object supports the following: + +* `database` – (Required) The name of the database for the table. + +* `table` – (Required) The name of the table. + +* `column_names` - (Optional) The list of column names for the table. + +* `excluded_column_names` - (Optional) Excludes column names. Any column with this name will be excluded. + +The following summarizes the available Lake Formation permissions on Data Catalog resources: + +* `DATA_LOCATION_ACCESS` on registered location resources, + +* `CREATE_DATABASE` on catalog, + +* `CREATE_TABLE`, `ALTER`, `DROP` on databases, + +* `ALTER`, `INSERT`, `DELETE`, `DROP`, `SELECT` on tables, + +* `SELECT` on columns. + +`INSERT`, `DELETE`, `SELECT` permissions apply to the underlying data, the others to the metadata. + +There is also a special permission `ALL`, that enables a principal to perform every supported Lake Formation operation on the database or table on which it is granted. + +For details on each permission, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). + +~> **NOTE:** Data lake administrators and database creators have implicit Lake Formation permissions. See [Implicit Lake Formation Permissions](https://docs.aws.amazon.com/lake-formation/latest/dg/implicit-permissions.html) for more information. + From d85171b16aa91a16e78dbbecb82ebbb8bf102082 Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Wed, 20 May 2020 19:28:17 +0200 Subject: [PATCH 05/31] Define permissions at catalog level --- aws/resource_aws_lakeformation_permissions.go | 33 ++- ...urce_aws_lakeformation_permissions_test.go | 238 +++++++++++------- .../r/lakeformation_permissions.html.markdown | 15 +- 3 files changed, 176 insertions(+), 110 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index bca0637660f9..bf88969dc409 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -174,10 +174,8 @@ func resourceAwsLakeFormationPermissionsList(d *schema.ResourceData, meta interf return fmt.Errorf("Error no LakeFormation Permissions found: %s", input) } - permissionsHead := permissions[0] // XXX: assuming there is only one result in the list + permissionsHead := permissions[0] d.Set("catalog_id", catalogId) - d.Set("permissions", permissionsHead.Permissions) - d.Set("permissions_with_grant_option", permissionsHead.PermissionsWithGrantOption) d.Set("principal", permissionsHead.Principal.DataLakePrincipalIdentifier) if dataLocation := permissionsHead.Resource.DataLocation; dataLocation != nil { d.Set("location", dataLocation.ResourceArn) @@ -191,6 +189,13 @@ func resourceAwsLakeFormationPermissionsList(d *schema.ResourceData, meta interf if table := permissionsHead.Resource.TableWithColumns; table != nil { d.Set("table", flattenAWSLakeFormationTableWithColumns(table)) } + var allPermissions, allPermissionsWithGrant []*string + for _, p := range permissions { + allPermissions = append(allPermissions, p.Permissions...) + allPermissionsWithGrant = append(allPermissionsWithGrant, p.PermissionsWithGrantOption...) + } + d.Set("permissions", allPermissions) + d.Set("permissions_with_grant_option", allPermissionsWithGrant) return nil } @@ -224,22 +229,23 @@ func expandAwsLakeFormationPrincipal(d *schema.ResourceData) *lakeformation.Data } func expandAwsLakeFormationResource(d *schema.ResourceData) *lakeformation.Resource { - resource := &lakeformation.Resource{ - // Catalog: &lakeformation.CatalogResource{}, - } if v, ok := d.GetOk("database"); ok { databaseName := v.(string) if len(databaseName) > 0 { - resource.Database = &lakeformation.DatabaseResource{ - Name: aws.String(databaseName), + return &lakeformation.Resource{ + Database: &lakeformation.DatabaseResource{ + Name: aws.String(databaseName), + }, } } } if v, ok := d.GetOk("location"); ok { location := v.(string) if len(location) > 0 { - resource.DataLocation = &lakeformation.DataLocationResource{ - ResourceArn: aws.String(v.(string)), + return &lakeformation.Resource{ + DataLocation: &lakeformation.DataLocationResource{ + ResourceArn: aws.String(v.(string)), + }, } } } @@ -248,6 +254,7 @@ func expandAwsLakeFormationResource(d *schema.ResourceData) *lakeformation.Resou if len(tables) > 0 { table := tables[0].(map[string]interface{}) + resource := &lakeformation.Resource{} var databaseName, tableName string var columnNames, excludedColumnNames []interface{} if x, ok := table["database"]; ok { @@ -283,10 +290,12 @@ func expandAwsLakeFormationResource(d *schema.ResourceData) *lakeformation.Resou Name: aws.String(tableName), } } + return resource } } - - return resource + return &lakeformation.Resource{ + Catalog: &lakeformation.CatalogResource{}, + } } func flattenAWSLakeFormationTable(tb *lakeformation.TableResource) map[string]interface{} { diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index c1e20c10ee7c..41c2e6e50b84 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -11,20 +11,30 @@ import ( func TestAccAWSLakeFormationPermissions_full(t *testing.T) { rName := acctest.RandomWithPrefix("lakeformation-test-bucket") dName := acctest.RandomWithPrefix("lakeformation-test-db") - // tName := acctest.RandomWithPrefix("lakeformation-test-table") + tName := acctest.RandomWithPrefix("lakeformation-test-table") callerIdentityName := "data.aws_caller_identity.current" roleName := "data.aws_iam_role.test" resourceName := "aws_lakeformation_permissions.test" bucketName := "aws_s3_bucket.test" dbName := "aws_glue_catalog_database.test" - // tableName := "aws_glue_catalog_table.test" + tableName := "aws_glue_catalog_table.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, // TODO: CheckDestroy Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_catalog(), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", "CREATE_DATABASE"), + ), + }, { Config: testAccAWSLakeFormationPermissionsConfig_location(rName), Check: resource.ComposeTestCheckFunc( @@ -51,9 +61,22 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", "CREATE_TABLE"), ), }, + { + Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "ALL"), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), + resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), + ), + }, // FIXME: more than one permission in API read result // { - // Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName), + // Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "SELECT"), // Check: resource.ComposeTestCheckFunc( // testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), // resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), @@ -62,7 +85,7 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { // resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), // resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), // resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - // resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), + // resource.TestCheckResourceAttr(resourceName, "permissions.0", "SELECT"), // ), // }, // FIXME: WIP @@ -86,6 +109,27 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { }) } +func testAccAWSLakeFormationPermissionsConfig_catalog() string { + return ` +data "aws_caller_identity" "current" {} + +data "aws_iam_role" "test" { + name = "AWSServiceRoleForLakeFormationDataAccess" +} + +resource "aws_lakeformation_datalake_settings" "test" { + admins = [ + data.aws_caller_identity.current.arn + ] +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["CREATE_DATABASE"] + principal = data.aws_iam_role.test.arn +} +` +} + func testAccAWSLakeFormationPermissionsConfig_location(rName string) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} @@ -150,96 +194,96 @@ resource "aws_lakeformation_permissions" "test" { `, rName, dName) } -// func testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName string) string { -// return fmt.Sprintf(` -// data "aws_caller_identity" "current" {} - -// data "aws_iam_role" "test" { -// name = "AWSServiceRoleForLakeFormationDataAccess" -// } - -// resource "aws_s3_bucket" "test" { -// bucket = %[1]q -// } - -// resource "aws_glue_catalog_database" "test" { -// name = %[2]q -// } - -// resource "aws_glue_catalog_table" "test" { -// name = %[3]q -// database_name = aws_glue_catalog_database.test.name -// } - -// resource "aws_lakeformation_datalake_settings" "test" { -// admins = [ -// data.aws_caller_identity.current.arn -// ] -// } - -// resource "aws_lakeformation_permissions" "test" { -// permissions = ["SELECT"] -// principal = data.aws_iam_role.test.arn - -// table { -// database = aws_glue_catalog_table.test.database_name -// name = aws_glue_catalog_table.test.name -// } -// } -// `, rName, dName, tName) -// } - -// func testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName string) string { -// return fmt.Sprintf(` -// data "aws_caller_identity" "current" {} - -// data "aws_iam_role" "test" { -// name = "AWSServiceRoleForLakeFormationDataAccess" -// } - -// resource "aws_s3_bucket" "test" { -// bucket = %[1]q -// } - -// resource "aws_glue_catalog_database" "test" { -// name = %[2]q -// } - -// resource "aws_glue_catalog_table" "test" { -// name = %[3]q -// database_name = aws_glue_catalog_database.test.name - -// storage_descriptor { -// columns { -// name = "event" -// type = "string" -// } -// columns { -// name = "timestamp" -// type = "date" -// } -// columns { -// name = "value" -// type = "double" -// } -// } -// } - -// resource "aws_lakeformation_datalake_settings" "test" { -// admins = [ -// data.aws_caller_identity.current.arn -// ] -// } - -// resource "aws_lakeformation_permissions" "test" { -// permissions = ["SELECT"] -// principal = data.aws_iam_role.test.arn - -// table { -// database = aws_glue_catalog_table.test.database_name -// name = aws_glue_catalog_table.test.name -// column_names = ["event", "timestamp"] -// } -// } -// `, rName, dName, tName) -// } +func testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, permission string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +data "aws_iam_role" "test" { + name = "AWSServiceRoleForLakeFormationDataAccess" +} + +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + +resource "aws_glue_catalog_database" "test" { + name = %[2]q +} + +resource "aws_glue_catalog_table" "test" { + name = %[3]q + database_name = aws_glue_catalog_database.test.name +} + +resource "aws_lakeformation_datalake_settings" "test" { + admins = [ + data.aws_caller_identity.current.arn + ] +} + +resource "aws_lakeformation_permissions" "test" { + permissions = [%[4]q] + principal = data.aws_iam_role.test.arn + + table { + database = aws_glue_catalog_table.test.database_name + name = aws_glue_catalog_table.test.name + } +} +`, rName, dName, tName, permission) +} + +/* func testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +data "aws_iam_role" "test" { + name = "AWSServiceRoleForLakeFormationDataAccess" +} + +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + +resource "aws_glue_catalog_database" "test" { + name = %[2]q +} + +resource "aws_glue_catalog_table" "test" { + name = %[3]q + database_name = aws_glue_catalog_database.test.name + + storage_descriptor { + columns { + name = "event" + type = "string" + } + columns { + name = "timestamp" + type = "date" + } + columns { + name = "value" + type = "double" + } + } +} + +resource "aws_lakeformation_datalake_settings" "test" { + admins = [ + data.aws_caller_identity.current.arn + ] +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["SELECT"] + principal = data.aws_iam_role.test.arn + + table { + database = aws_glue_catalog_table.test.database_name + name = aws_glue_catalog_table.test.name + column_names = ["event", "timestamp"] + } +} +`, rName, dName, tName) +} */ diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index 31e4f6918e5e..4bee2c25ffb6 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -36,6 +36,19 @@ resource "aws_lakeformation_permissions" "example" { } ``` +### Granting permissions on Lake Formation catalog + +```hcl +data "aws_iam_role" "example" { + name = "existing_lakeformation_role" +} + +resource "aws_lakeformation_permissions" "example" { + permissions = ["CREATE_DATABASE"] + principal = data.aws_iam_role.example.arn +} +``` + ### Granting permissions on Lake Formation database ```hcl @@ -146,7 +159,7 @@ The following arguments are optional: * `table` – (Optional) A structure for the table object. A table is a metadata definition that represents your data. -At least one of `database`, `location`, `table` must be specified. +Only one of `database`, `location`, `table` can be specified at a time. If none of them is specified, permissions will be set at catalog level. See bellow for available permissions for each resource. The `table` object supports the following: From d4845eb37e0ffc401b9028de6f2d026012923c00 Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Wed, 20 May 2020 19:53:56 +0200 Subject: [PATCH 06/31] Test multiple permissions on table resource --- ...urce_aws_lakeformation_permissions_test.go | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index 41c2e6e50b84..f4d947364d9e 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -62,7 +62,7 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { ), }, { - Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "ALL"), + Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "\"ALL\""), Check: resource.ComposeTestCheckFunc( testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), @@ -74,20 +74,21 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), ), }, - // FIXME: more than one permission in API read result - // { - // Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "SELECT"), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - // resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), - // resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), - // resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - // resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), - // resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), - // resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - // resource.TestCheckResourceAttr(resourceName, "permissions.0", "SELECT"), - // ), - // }, + // FIXME: more than one permission in API read result (in acceptance tests setup) + { + Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "\"ALL\", \"SELECT\""), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), + resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "2"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), + resource.TestCheckResourceAttr(resourceName, "permissions.1", "SELECT"), + ), + }, // FIXME: WIP // { // Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName), @@ -194,7 +195,7 @@ resource "aws_lakeformation_permissions" "test" { `, rName, dName) } -func testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, permission string) string { +func testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, permissions string) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} @@ -222,7 +223,7 @@ resource "aws_lakeformation_datalake_settings" "test" { } resource "aws_lakeformation_permissions" "test" { - permissions = [%[4]q] + permissions = [%s] principal = data.aws_iam_role.test.arn table { @@ -230,7 +231,7 @@ resource "aws_lakeformation_permissions" "test" { name = aws_glue_catalog_table.test.name } } -`, rName, dName, tName, permission) +`, rName, dName, tName, permissions) } /* func testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName string) string { From fd0adb6c2f06da2d522386e996d5f6122229e077 Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Wed, 20 May 2020 23:05:49 +0200 Subject: [PATCH 07/31] Check that permissions are revoked in acceptance tests --- ...urce_aws_lakeformation_permissions_test.go | 38 +++++++++++++++++-- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index f4d947364d9e..9e55f9a8c340 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -4,8 +4,12 @@ import ( "fmt" "testing" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" ) func TestAccAWSLakeFormationPermissions_full(t *testing.T) { @@ -21,9 +25,9 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { tableName := "aws_glue_catalog_table.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - // TODO: CheckDestroy + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsRevoked, Steps: []resource.TestStep{ { Config: testAccAWSLakeFormationPermissionsConfig_catalog(), @@ -288,3 +292,31 @@ resource "aws_lakeformation_permissions" "test" { } `, rName, dName, tName) } */ + +func testAccCheckAWSLakeFormationPermissionsRevoked(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).lakeformationconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lakeformation_permissions" { + continue + } + + principal := rs.Primary.Attributes["principal"] + catalogId := rs.Primary.Attributes["catalog_id"] + + input := &lakeformation.ListPermissionsInput{ + CatalogId: aws.String(catalogId), + Principal: &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(principal), + }, + } + + out, err := conn.ListPermissions(input) + if err == nil { + fmt.Print(out) + return fmt.Errorf("Resource still registered: %s %s", catalogId, principal) + } + } + + return nil +} From 419d805c496a45b555b6427c9f109960cc93bc69 Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Wed, 20 May 2020 23:18:15 +0200 Subject: [PATCH 08/31] Add timestamp to resource ID --- aws/resource_aws_lakeformation_permissions.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index bf88969dc409..0896dbfdaba3 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/lakeformation" @@ -148,7 +149,7 @@ func resourceAwsLakeFormationPermissionsGrant(d *schema.ResourceData, meta inter return fmt.Errorf("Error granting LakeFormation Permissions: %s", err) } - d.SetId(fmt.Sprintf("lakeformation:resource:%s", catalogId)) // FIXME + d.SetId(fmt.Sprintf("lakeformation:resource:%s:%s", catalogId, time.Now().UTC().String())) d.Set("catalog_id", catalogId) return resourceAwsLakeFormationPermissionsList(d, meta) From 12bc694fff64044af29eeac4eb2f95463c6a2c14 Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Wed, 20 May 2020 23:19:18 +0200 Subject: [PATCH 09/31] Use const defined in API for permissions --- aws/resource_aws_lakeformation_permissions.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index 0896dbfdaba3..d46f7a69e47c 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -12,15 +12,15 @@ import ( func AwsLakeFormationPermissions() []string { return []string{ - "ALL", - "SELECT", - "ALTER", - "DROP", - "DELETE", - "INSERT", - "CREATE_DATABASE", - "CREATE_TABLE", - "DATA_LOCATION_ACCESS", + lakeformation.PermissionAll, + lakeformation.PermissionSelect, + lakeformation.PermissionAlter, + lakeformation.PermissionDrop, + lakeformation.PermissionDelete, + lakeformation.PermissionInsert, + lakeformation.PermissionCreateDatabase, + lakeformation.PermissionCreateTable, + lakeformation.PermissionDataLocationAccess, } } From 88588632bc3f0ef6ac95fce48239ced9c802a464 Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Wed, 20 May 2020 23:19:43 +0200 Subject: [PATCH 10/31] Cleanup --- aws/resource_aws_lakeformation_permissions.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index d46f7a69e47c..774730308f84 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -138,12 +138,6 @@ func resourceAwsLakeFormationPermissionsGrant(d *schema.ResourceData, meta inter input.PermissionsWithGrantOption = expandStringList(vs.([]interface{})) } - // Catalog: CREATE_DATABASE - // Location: DATA_LOCATION_ACCESS - // Database: ALTER, CREATE_TABLE, DROP, (ALL ~ Super) - // Table: ALTER, INSERT, DELETE, DROP, SELECT, (ALL ~ Super) - // TableWithColumns: SELECT - _, err := conn.GrantPermissions(input) if err != nil { return fmt.Errorf("Error granting LakeFormation Permissions: %s", err) From 661e2b37f1cc9fc5d3cbf44825210b331ddac282 Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Tue, 26 May 2020 23:00:07 +0200 Subject: [PATCH 11/31] Handle TableWithColumns resource I/O in ListPermissions request --- aws/resource_aws_lakeformation_permissions.go | 45 +++++++++++++++-- ...urce_aws_lakeformation_permissions_test.go | 48 +++++++++---------- 2 files changed, 66 insertions(+), 27 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index 774730308f84..f880fc74ed7f 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -153,10 +153,36 @@ func resourceAwsLakeFormationPermissionsList(d *schema.ResourceData, meta interf conn := meta.(*AWSClient).lakeformationconn catalogId := d.Get("catalog_id").(string) + // This operation does not support getting privileges on a table with columns. + // Instead, call this operation on the table, and the operation returns the + // table and the table w columns. + resource := expandAwsLakeFormationResource(d) + isTableWithColumnsResource := false + if table := resource.TableWithColumns; table != nil { + resource.Table = &lakeformation.TableResource{ + DatabaseName: resource.TableWithColumns.DatabaseName, + Name: resource.TableWithColumns.Name, + } + resource.TableWithColumns = nil + isTableWithColumnsResource = true + } + + var resourceType string + if table := resource.Catalog; table != nil { + resourceType = lakeformation.DataLakeResourceTypeCatalog + } else if location := resource.DataLocation; location != nil { + resourceType = lakeformation.DataLakeResourceTypeDataLocation + } else if DB := resource.Database; DB != nil { + resourceType = lakeformation.DataLakeResourceTypeDatabase + } else { + resourceType = lakeformation.DataLakeResourceTypeTable + } + input := &lakeformation.ListPermissionsInput{ - CatalogId: aws.String(catalogId), - Principal: expandAwsLakeFormationPrincipal(d), - Resource: expandAwsLakeFormationResource(d), + CatalogId: aws.String(catalogId), + Principal: expandAwsLakeFormationPrincipal(d), + Resource: resource, + ResourceType: &resourceType, } out, err := conn.ListPermissions(input) @@ -169,6 +195,19 @@ func resourceAwsLakeFormationPermissionsList(d *schema.ResourceData, meta interf return fmt.Errorf("Error no LakeFormation Permissions found: %s", input) } + // This operation does not support getting privileges on a table with columns. + // Instead, call this operation on the table, and the operation returns the + // table and the table w columns. + if isTableWithColumnsResource { + filtered := make([]*lakeformation.PrincipalResourcePermissions, 0) + for _, p := range permissions { + if table := p.Resource.TableWithColumns; table != nil { + filtered = append(filtered, p) + } + } + permissions = filtered + } + permissionsHead := permissions[0] d.Set("catalog_id", catalogId) d.Set("principal", permissionsHead.Principal.DataLakePrincipalIdentifier) diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index 9e55f9a8c340..772496b88419 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -78,24 +78,8 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), ), }, - // FIXME: more than one permission in API read result (in acceptance tests setup) - { - Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "\"ALL\", \"SELECT\""), - Check: resource.ComposeTestCheckFunc( - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), - resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), - resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "2"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), - resource.TestCheckResourceAttr(resourceName, "permissions.1", "SELECT"), - ), - }, - // FIXME: WIP // { - // Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName), + // Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "\"ALL\", \"SELECT\""), // Check: resource.ComposeTestCheckFunc( // testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), // resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), @@ -103,13 +87,27 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { // resource.TestCheckResourceAttr(resourceName, "table.#", "1"), // resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), // resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), - // resource.TestCheckResourceAttr(resourceName, "table.0.column_names.#", "2"), - // resource.TestCheckResourceAttr(resourceName, "table.0.column_names.0", "event"), - // resource.TestCheckResourceAttr(resourceName, "table.0.column_names.1", "timestamp"), - // resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - // resource.TestCheckResourceAttr(resourceName, "permissions.0", "SELECT"), + // resource.TestCheckResourceAttr(resourceName, "permissions.#", "2"), + // resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), + // resource.TestCheckResourceAttr(resourceName, "permissions.1", "SELECT"), // ), // }, + { + Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), + resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), + resource.TestCheckResourceAttr(resourceName, "table.0.column_names.#", "2"), + resource.TestCheckResourceAttr(resourceName, "table.0.column_names.0", "event"), + resource.TestCheckResourceAttr(resourceName, "table.0.column_names.1", "timestamp"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", "SELECT"), + ), + }, }, }) } @@ -131,6 +129,8 @@ resource "aws_lakeformation_datalake_settings" "test" { resource "aws_lakeformation_permissions" "test" { permissions = ["CREATE_DATABASE"] principal = data.aws_iam_role.test.arn + + depends_on = ["aws_lakeformation_datalake_settings.test"] } ` } @@ -238,7 +238,7 @@ resource "aws_lakeformation_permissions" "test" { `, rName, dName, tName, permissions) } -/* func testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName string) string { +func testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName string) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} @@ -291,7 +291,7 @@ resource "aws_lakeformation_permissions" "test" { } } `, rName, dName, tName) -} */ +} func testAccCheckAWSLakeFormationPermissionsRevoked(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).lakeformationconn From 708c760815ebfd605a924d184dca8b6e8ade94bb Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Tue, 26 May 2020 23:19:12 +0200 Subject: [PATCH 12/31] Fix type in HCL example --- website/docs/r/lakeformation_permissions.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index 4bee2c25ffb6..329dff20fdde 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -128,7 +128,7 @@ resource "aws_glue_catalog_table" "example" { } resource "aws_lakeformation_permissions" "example" { - permissions = [""SELECT"] + permissions = ["SELECT"] principal = data.aws_iam_role.example.arn table { From c5fd91649ff680df2a1f4ba5d296b9eafad92583 Mon Sep 17 00:00:00 2001 From: Gaylord Mazelier Date: Wed, 27 May 2020 22:24:38 +0200 Subject: [PATCH 13/31] Remove redundant check, add one test step --- ...urce_aws_lakeformation_permissions_test.go | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index 772496b88419..957f4b638593 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -17,7 +17,6 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { dName := acctest.RandomWithPrefix("lakeformation-test-db") tName := acctest.RandomWithPrefix("lakeformation-test-table") - callerIdentityName := "data.aws_caller_identity.current" roleName := "data.aws_iam_role.test" resourceName := "aws_lakeformation_permissions.test" bucketName := "aws_s3_bucket.test" @@ -33,7 +32,6 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { Config: testAccAWSLakeFormationPermissionsConfig_catalog(), Check: resource.ComposeTestCheckFunc( testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), resource.TestCheckResourceAttr(resourceName, "permissions.0", "CREATE_DATABASE"), @@ -43,7 +41,6 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { Config: testAccAWSLakeFormationPermissionsConfig_location(rName), Check: resource.ComposeTestCheckFunc( testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), resource.TestCheckResourceAttrPair(bucketName, "arn", resourceName, "location"), resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), @@ -54,7 +51,6 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { Config: testAccAWSLakeFormationPermissionsConfig_database(rName, dName), Check: resource.ComposeTestCheckFunc( testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), resource.TestCheckResourceAttrPair(dbName, "name", resourceName, "database"), resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), @@ -69,7 +65,6 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "\"ALL\""), Check: resource.ComposeTestCheckFunc( testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), resource.TestCheckResourceAttr(resourceName, "table.#", "1"), resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), @@ -78,25 +73,23 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), ), }, - // { - // Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "\"ALL\", \"SELECT\""), - // Check: resource.ComposeTestCheckFunc( - // testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - // resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), - // resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), - // resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - // resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), - // resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), - // resource.TestCheckResourceAttr(resourceName, "permissions.#", "2"), - // resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), - // resource.TestCheckResourceAttr(resourceName, "permissions.1", "SELECT"), - // ), - // }, + { + Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "\"ALL\", \"SELECT\""), + Check: resource.ComposeTestCheckFunc( + testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), + resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "2"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), + resource.TestCheckResourceAttr(resourceName, "permissions.1", "SELECT"), + ), + }, { Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName), Check: resource.ComposeTestCheckFunc( testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), resource.TestCheckResourceAttr(resourceName, "table.#", "1"), resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), @@ -156,6 +149,8 @@ resource "aws_lakeformation_datalake_settings" "test" { resource "aws_lakeformation_resource" "test" { resource_arn = aws_s3_bucket.test.arn use_service_linked_role = true + + depends_on = ["aws_lakeformation_datalake_settings.test"] } resource "aws_lakeformation_permissions" "test" { @@ -163,6 +158,8 @@ resource "aws_lakeformation_permissions" "test" { principal = data.aws_iam_role.test.arn location = aws_lakeformation_resource.test.resource_arn + + depends_on = ["aws_lakeformation_datalake_settings.test"] } `, rName) } @@ -195,6 +192,8 @@ resource "aws_lakeformation_permissions" "test" { principal = data.aws_iam_role.test.arn database = aws_glue_catalog_database.test.name + + depends_on = ["aws_lakeformation_datalake_settings.test"] } `, rName, dName) } @@ -234,6 +233,8 @@ resource "aws_lakeformation_permissions" "test" { database = aws_glue_catalog_table.test.database_name name = aws_glue_catalog_table.test.name } + + depends_on = ["aws_lakeformation_datalake_settings.test"] } `, rName, dName, tName, permissions) } @@ -289,6 +290,8 @@ resource "aws_lakeformation_permissions" "test" { name = aws_glue_catalog_table.test.name column_names = ["event", "timestamp"] } + + depends_on = ["aws_lakeformation_datalake_settings.test"] } `, rName, dName, tName) } From 2d4cb285262a7d60fff19198f4e6940d5735a9f3 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 11 Dec 2020 13:19:27 -0500 Subject: [PATCH 14/31] resource/lakeformation: Remove aws.erb from PR --- website/aws.erb | 3632 ----------------------------------------------- 1 file changed, 3632 deletions(-) delete mode 100644 website/aws.erb diff --git a/website/aws.erb b/website/aws.erb deleted file mode 100644 index 0fad8c5a6a3e..000000000000 --- a/website/aws.erb +++ /dev/null @@ -1,3632 +0,0 @@ -<% wrap_layout :inner do %> - <% content_for :sidebar do %> - - <% end %> - <%= yield %> -<% end %> From 741137021ec94d4e7143f003f84920aadcc2fe14 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 11 Dec 2020 13:31:20 -0500 Subject: [PATCH 15/31] resource/lakeformation_permissions: Upgrade to plugin SDK v2 --- aws/resource_aws_lakeformation_permissions.go | 4 +-- ...urce_aws_lakeformation_permissions_test.go | 7 ++-- go.mod | 1 + go.sum | 33 +++++++++++++++++++ 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index f880fc74ed7f..408a42cf9c17 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -6,8 +6,8 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/lakeformation" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func AwsLakeFormationPermissions() []string { diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index 957f4b638593..0e3e05be91e1 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -6,10 +6,9 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/lakeformation" - - "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" - "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) func TestAccAWSLakeFormationPermissions_full(t *testing.T) { diff --git a/go.mod b/go.mod index 597534f085fa..97986e54841b 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/hashicorp/go-hclog v0.10.0 // indirect github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/go-version v1.2.1 + github.com/hashicorp/terraform-plugin-sdk v1.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.3.0 github.com/jen20/awspolicyequivalence v1.1.0 github.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba diff --git a/go.sum b/go.sum index 3a28bf9bdf20..f9fbbe068593 100644 --- a/go.sum +++ b/go.sum @@ -59,6 +59,8 @@ github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2 github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= @@ -162,6 +164,8 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -177,6 +181,7 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= +github.com/hashicorp/go-getter v1.4.2-0.20200106182914-9813cbd4eb02/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= github.com/hashicorp/go-getter v1.5.0 h1:ciWJaeZWSMbc5OiLMpKp40MKFPqO44i0h3uyfXPBkkk= github.com/hashicorp/go-getter v1.5.0/go.mod h1:a7z7NPPfNQpJWcn4rSWFtdrSldqLdLPEF3d8nFMsSLM= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= @@ -194,22 +199,34 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f h1:UdxlrJz4JOnY8W+DbLISwf2B8WXEolNRA8BGCwI9jws= +github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= +github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90= github.com/hashicorp/hcl/v2 v2.3.0 h1:iRly8YaMwTBAKhn1Ybk7VSdzbnopghktCD031P8ggUE= github.com/hashicorp/hcl/v2 v2.3.0/go.mod h1:d+FwDBbOLvpAM3Z6J7gPj/VoAGkNe/gm352ZhjJ/Zv8= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/terraform-config-inspect v0.0.0-20191115094559-17f92b0546e8 h1:+RyjwU+Gnd/aTJBPZVDNm903eXVjjqhbaR4Ypx3xYyY= +github.com/hashicorp/terraform-config-inspect v0.0.0-20191115094559-17f92b0546e8/go.mod h1:p+ivJws3dpqbp1iP84+npOyAmTTOLMgCzrXd3GSdn/A= github.com/hashicorp/terraform-exec v0.10.0 h1:3nh/1e3u9gYRUQGOKWp/8wPR7ABlL2F14sZMZBrp+dM= github.com/hashicorp/terraform-exec v0.10.0/go.mod h1:tOT8j1J8rP05bZBGWXfMyU3HkLi1LWyqL3Bzsc3CJjo= github.com/hashicorp/terraform-json v0.5.0 h1:7TV3/F3y7QVSuN4r9BEXqnWqrAyeOtON8f0wvREtyzs= github.com/hashicorp/terraform-json v0.5.0/go.mod h1:eAbqb4w0pSlRmdvl8fOyHAi/+8jnkVYN28gJkSJrLhU= github.com/hashicorp/terraform-plugin-go v0.1.0 h1:kyXZ0nkHxiRev/q18N40IbRRk4AV0zE/MDJkDM3u8dY= github.com/hashicorp/terraform-plugin-go v0.1.0/go.mod h1:10V6F3taeDWVAoLlkmArKttR3IULlRWFAGtQIQTIDr4= +github.com/hashicorp/terraform-plugin-sdk v1.16.0 h1:NrkXMRjHErUPPTHQkZ6JIn6bByiJzGnlJzH1rVdNEuE= +github.com/hashicorp/terraform-plugin-sdk v1.16.0/go.mod h1:5sVxrwW6/xzFhZyql+Q9zXCUEJaGWcBIxBbZFLpVXOI= github.com/hashicorp/terraform-plugin-sdk/v2 v2.3.0 h1:Egv+R1tOOjPNz643KBTx3tLT6RdFGGYJcZlyLvrPcEU= github.com/hashicorp/terraform-plugin-sdk/v2 v2.3.0/go.mod h1:+12dJQebYjuU/yiq94iZUPuC66abfRBrXdpVJia3ojk= +github.com/hashicorp/terraform-plugin-test/v2 v2.1.2 h1:p96IIn+XpvVjw7AtN8y9MKxn0x69S7wtbGf7JgDJoIk= +github.com/hashicorp/terraform-plugin-test/v2 v2.1.2/go.mod h1:jerO5mrd+jVNALy8aiq+VZOg/CR8T2T1QR3jd6JKGOI= +github.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596 h1:hjyO2JsNZUKT1ym+FAdlBEkGPevazYsmVgIMw7dVELg= +github.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= @@ -249,11 +266,13 @@ github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LE github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= @@ -262,6 +281,8 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mitchellh/cli v1.1.1 h1:J64v/xD7Clql+JVKSvkYojLOXu1ibnY9ZjGLwSt/89w= github.com/mitchellh/cli v1.1.1/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= +github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -293,6 +314,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.1 h1:LrvDIY//XNo65Lq84G/akBuMGlawHvGBABv8f/ZN6DI= +github.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E= github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -300,7 +323,10 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -309,9 +335,11 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= +github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= +github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= @@ -319,9 +347,13 @@ github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0B github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= +github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= +github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8= +github.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -383,6 +415,7 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= From eeac9a39deadcd6d36d70d75153b270dbaca73cb Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 11 Dec 2020 13:35:35 -0500 Subject: [PATCH 16/31] resource/lakeformation_permissions: Remove datalake_settings from PR --- ...rce_aws_lakeformation_datalake_settings.go | 135 ------------------ ...ws_lakeformation_datalake_settings_test.go | 66 --------- aws/resource_aws_lakeformation_permissions.go | 9 ++ 3 files changed, 9 insertions(+), 201 deletions(-) delete mode 100644 aws/resource_aws_lakeformation_datalake_settings.go delete mode 100644 aws/resource_aws_lakeformation_datalake_settings_test.go diff --git a/aws/resource_aws_lakeformation_datalake_settings.go b/aws/resource_aws_lakeformation_datalake_settings.go deleted file mode 100644 index c708abcef59c..000000000000 --- a/aws/resource_aws_lakeformation_datalake_settings.go +++ /dev/null @@ -1,135 +0,0 @@ -package aws - -import ( - "fmt" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/lakeformation" - "github.com/hashicorp/terraform-plugin-sdk/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/helper/validation" -) - -func resourceAwsLakeFormationDataLakeSettings() *schema.Resource { - return &schema.Resource{ - Create: resourceAwsLakeFormationDataLakeSettingsPut, - Update: resourceAwsLakeFormationDataLakeSettingsPut, - Read: resourceAwsLakeFormationDataLakeSettingsRead, - Delete: resourceAwsLakeFormationDataLakeSettingsReset, - Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, - }, - - Schema: map[string]*schema.Schema{ - "catalog_id": { - Type: schema.TypeString, - ForceNew: true, - Optional: true, - Computed: true, - }, - "admins": { - Type: schema.TypeList, - Required: true, - MinItems: 0, - MaxItems: 10, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.NoZeroValues, - }, - }, - }, - } -} - -func resourceAwsLakeFormationDataLakeSettingsPut(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - catalogId := createAwsDataCatalogId(d, meta.(*AWSClient).accountid) - - input := &lakeformation.PutDataLakeSettingsInput{ - CatalogId: aws.String(catalogId), - DataLakeSettings: &lakeformation.DataLakeSettings{ - DataLakeAdmins: expandAdmins(d), - }, - } - - _, err := conn.PutDataLakeSettings(input) - if err != nil { - return fmt.Errorf("Error updating DataLakeSettings: %s", err) - } - - d.SetId(fmt.Sprintf("lakeformation:settings:%s", catalogId)) - d.Set("catalog_id", catalogId) - - return resourceAwsLakeFormationDataLakeSettingsRead(d, meta) -} - -func resourceAwsLakeFormationDataLakeSettingsRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - catalogId := d.Get("catalog_id").(string) - - input := &lakeformation.GetDataLakeSettingsInput{ - CatalogId: aws.String(catalogId), - } - - out, err := conn.GetDataLakeSettings(input) - if err != nil { - return fmt.Errorf("Error reading DataLakeSettings: %s", err) - } - - d.Set("catalog_id", catalogId) - if err := d.Set("admins", flattenAdmins(out.DataLakeSettings.DataLakeAdmins)); err != nil { - return fmt.Errorf("Error setting admins from DataLakeSettings: %s", err) - } - // TODO: Add CreateDatabaseDefaultPermissions and CreateTableDefaultPermissions - - return nil -} - -func resourceAwsLakeFormationDataLakeSettingsReset(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - catalogId := d.Get("catalog_id").(string) - - input := &lakeformation.PutDataLakeSettingsInput{ - CatalogId: aws.String(catalogId), - DataLakeSettings: &lakeformation.DataLakeSettings{ - DataLakeAdmins: make([]*lakeformation.DataLakePrincipal, 0), - }, - } - - _, err := conn.PutDataLakeSettings(input) - if err != nil { - return fmt.Errorf("Error reseting DataLakeSettings: %s", err) - } - - return nil -} - -func createAwsDataCatalogId(d *schema.ResourceData, accountId string) (catalogId string) { - if inputCatalogId, ok := d.GetOkExists("catalog_id"); ok { - catalogId = inputCatalogId.(string) - } else { - catalogId = accountId - } - return -} - -func expandAdmins(d *schema.ResourceData) []*lakeformation.DataLakePrincipal { - xs := d.Get("admins") - ys := make([]*lakeformation.DataLakePrincipal, len(xs.([]interface{}))) - - for i, x := range xs.([]interface{}) { - ys[i] = &lakeformation.DataLakePrincipal{ - DataLakePrincipalIdentifier: aws.String(x.(string)), - } - } - - return ys -} - -func flattenAdmins(xs []*lakeformation.DataLakePrincipal) []string { - admins := make([]string, len(xs)) - for i, x := range xs { - admins[i] = aws.StringValue(x.DataLakePrincipalIdentifier) - } - - return admins -} diff --git a/aws/resource_aws_lakeformation_datalake_settings_test.go b/aws/resource_aws_lakeformation_datalake_settings_test.go deleted file mode 100644 index d2ac3b0c5382..000000000000 --- a/aws/resource_aws_lakeformation_datalake_settings_test.go +++ /dev/null @@ -1,66 +0,0 @@ -package aws - -import ( - "testing" - - "github.com/hashicorp/terraform-plugin-sdk/helper/resource" -) - -func TestAccAWSLakeFormationDataLakeSettings_basic(t *testing.T) { - callerIdentityName := "data.aws_caller_identity.current" - resourceName := "aws_lakeformation_datalake_settings.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - // TODO: CheckDestroy: testAccCheckAWSLakeFormationDataLakeSettingsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationDataLakeSettingsConfig_basic, - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), - resource.TestCheckResourceAttr(resourceName, "admins.#", "1"), - resource.TestCheckResourceAttrPair(callerIdentityName, "arn", resourceName, "admins.0"), - ), - }, - }, - }) -} - -const testAccAWSLakeFormationDataLakeSettingsConfig_basic = ` -data "aws_caller_identity" "current" {} - -resource "aws_lakeformation_datalake_settings" "test" { - admins = ["${data.aws_caller_identity.current.arn}"] -} -` - -func TestAccAWSLakeFormationDataLakeSettings_withCatalogId(t *testing.T) { - callerIdentityName := "data.aws_caller_identity.current" - resourceName := "aws_lakeformation_datalake_settings.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - // TODO: CheckDestroy: testAccCheckAWSLakeFormationDataLakeSettingsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationDataLakeSettingsConfig_withCatalogId, - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(callerIdentityName, "account_id", resourceName, "catalog_id"), - resource.TestCheckResourceAttr(resourceName, "admins.#", "1"), - resource.TestCheckResourceAttrPair(callerIdentityName, "arn", resourceName, "admins.0"), - ), - }, - }, - }) -} - -const testAccAWSLakeFormationDataLakeSettingsConfig_withCatalogId = ` -data "aws_caller_identity" "current" {} - -resource "aws_lakeformation_datalake_settings" "test" { - catalog_id = "${data.aws_caller_identity.current.account_id}" - admins = ["${data.aws_caller_identity.current.arn}"] -} -` diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index 408a42cf9c17..841385543cf5 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -355,3 +355,12 @@ func flattenAWSLakeFormationTableWithColumns(tb *lakeformation.TableWithColumnsR return m } + +func createAwsDataCatalogId(d *schema.ResourceData, accountId string) (catalogId string) { + if inputCatalogId, ok := d.GetOkExists("catalog_id"); ok { + catalogId = inputCatalogId.(string) + } else { + catalogId = accountId + } + return +} From 1f2e58723e76614675432e904f2c7d2d922425f3 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 11 Dec 2020 13:49:57 -0500 Subject: [PATCH 17/31] resource/lakeformation_permissions: Remove PR-extraneous files --- go.mod | 1 - go.sum | 33 --------------------------------- 2 files changed, 34 deletions(-) diff --git a/go.mod b/go.mod index 97986e54841b..597534f085fa 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,6 @@ require ( github.com/hashicorp/go-hclog v0.10.0 // indirect github.com/hashicorp/go-multierror v1.1.0 github.com/hashicorp/go-version v1.2.1 - github.com/hashicorp/terraform-plugin-sdk v1.16.0 github.com/hashicorp/terraform-plugin-sdk/v2 v2.3.0 github.com/jen20/awspolicyequivalence v1.1.0 github.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba diff --git a/go.sum b/go.sum index f9fbbe068593..3a28bf9bdf20 100644 --- a/go.sum +++ b/go.sum @@ -59,8 +59,6 @@ github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2 github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk= github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM= @@ -164,8 +162,6 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -181,7 +177,6 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= github.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= -github.com/hashicorp/go-getter v1.4.2-0.20200106182914-9813cbd4eb02/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY= github.com/hashicorp/go-getter v1.5.0 h1:ciWJaeZWSMbc5OiLMpKp40MKFPqO44i0h3uyfXPBkkk= github.com/hashicorp/go-getter v1.5.0/go.mod h1:a7z7NPPfNQpJWcn4rSWFtdrSldqLdLPEF3d8nFMsSLM= github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= @@ -199,34 +194,22 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f h1:UdxlrJz4JOnY8W+DbLISwf2B8WXEolNRA8BGCwI9jws= -github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= -github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90= github.com/hashicorp/hcl/v2 v2.3.0 h1:iRly8YaMwTBAKhn1Ybk7VSdzbnopghktCD031P8ggUE= github.com/hashicorp/hcl/v2 v2.3.0/go.mod h1:d+FwDBbOLvpAM3Z6J7gPj/VoAGkNe/gm352ZhjJ/Zv8= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/terraform-config-inspect v0.0.0-20191115094559-17f92b0546e8 h1:+RyjwU+Gnd/aTJBPZVDNm903eXVjjqhbaR4Ypx3xYyY= -github.com/hashicorp/terraform-config-inspect v0.0.0-20191115094559-17f92b0546e8/go.mod h1:p+ivJws3dpqbp1iP84+npOyAmTTOLMgCzrXd3GSdn/A= github.com/hashicorp/terraform-exec v0.10.0 h1:3nh/1e3u9gYRUQGOKWp/8wPR7ABlL2F14sZMZBrp+dM= github.com/hashicorp/terraform-exec v0.10.0/go.mod h1:tOT8j1J8rP05bZBGWXfMyU3HkLi1LWyqL3Bzsc3CJjo= github.com/hashicorp/terraform-json v0.5.0 h1:7TV3/F3y7QVSuN4r9BEXqnWqrAyeOtON8f0wvREtyzs= github.com/hashicorp/terraform-json v0.5.0/go.mod h1:eAbqb4w0pSlRmdvl8fOyHAi/+8jnkVYN28gJkSJrLhU= github.com/hashicorp/terraform-plugin-go v0.1.0 h1:kyXZ0nkHxiRev/q18N40IbRRk4AV0zE/MDJkDM3u8dY= github.com/hashicorp/terraform-plugin-go v0.1.0/go.mod h1:10V6F3taeDWVAoLlkmArKttR3IULlRWFAGtQIQTIDr4= -github.com/hashicorp/terraform-plugin-sdk v1.16.0 h1:NrkXMRjHErUPPTHQkZ6JIn6bByiJzGnlJzH1rVdNEuE= -github.com/hashicorp/terraform-plugin-sdk v1.16.0/go.mod h1:5sVxrwW6/xzFhZyql+Q9zXCUEJaGWcBIxBbZFLpVXOI= github.com/hashicorp/terraform-plugin-sdk/v2 v2.3.0 h1:Egv+R1tOOjPNz643KBTx3tLT6RdFGGYJcZlyLvrPcEU= github.com/hashicorp/terraform-plugin-sdk/v2 v2.3.0/go.mod h1:+12dJQebYjuU/yiq94iZUPuC66abfRBrXdpVJia3ojk= -github.com/hashicorp/terraform-plugin-test/v2 v2.1.2 h1:p96IIn+XpvVjw7AtN8y9MKxn0x69S7wtbGf7JgDJoIk= -github.com/hashicorp/terraform-plugin-test/v2 v2.1.2/go.mod h1:jerO5mrd+jVNALy8aiq+VZOg/CR8T2T1QR3jd6JKGOI= -github.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596 h1:hjyO2JsNZUKT1ym+FAdlBEkGPevazYsmVgIMw7dVELg= -github.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= @@ -266,13 +249,11 @@ github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LE github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= @@ -281,8 +262,6 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mitchellh/cli v1.1.1 h1:J64v/xD7Clql+JVKSvkYojLOXu1ibnY9ZjGLwSt/89w= github.com/mitchellh/cli v1.1.1/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= -github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -314,8 +293,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.1 h1:LrvDIY//XNo65Lq84G/akBuMGlawHvGBABv8f/ZN6DI= -github.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E= github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs= github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -323,10 +300,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -335,11 +309,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= -github.com/ulikunitz/xz v0.5.7/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= -github.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70= @@ -347,13 +319,9 @@ github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0B github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= -github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s= github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.2.1 h1:vGMsygfmeCl4Xb6OA5U5XVAaQZ69FvoG7X2jUtQujb8= github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= -github.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8= -github.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -415,7 +383,6 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= From 94b4f5865333caa3bacc28e5ffa6c557af19ee41 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 11 Dec 2020 15:26:11 -0500 Subject: [PATCH 18/31] ds/lakeformation_resource: New data source --- aws/data_source_aws_lakeformation_resource.go | 61 +++++++++++ ..._source_aws_lakeformation_resource_test.go | 101 ++++++++++++++++++ aws/provider.go | 1 + .../d/lakeformation_resource.html.markdown | 30 ++++++ 4 files changed, 193 insertions(+) create mode 100644 aws/data_source_aws_lakeformation_resource.go create mode 100644 aws/data_source_aws_lakeformation_resource_test.go create mode 100644 website/docs/d/lakeformation_resource.html.markdown diff --git a/aws/data_source_aws_lakeformation_resource.go b/aws/data_source_aws_lakeformation_resource.go new file mode 100644 index 000000000000..5b2d0d2db47d --- /dev/null +++ b/aws/data_source_aws_lakeformation_resource.go @@ -0,0 +1,61 @@ +package aws + +import ( + "fmt" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func dataSourceAwsLakeFormationResource() *schema.Resource { + return &schema.Resource{ + Read: dataSourceAwsLakeFormationResourceRead, + + Schema: map[string]*schema.Schema{ + "last_modified": { + Type: schema.TypeString, + Computed: true, + }, + "resource_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, + }, + "role_arn": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func dataSourceAwsLakeFormationResourceRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + + input := &lakeformation.DescribeResourceInput{} + + if v, ok := d.GetOk("resource_arn"); ok { + input.ResourceArn = aws.String(v.(string)) + } + + output, err := conn.DescribeResource(input) + + if err != nil { + return fmt.Errorf("error reading data source, Lake Formation Resource (resource_arn: %s): %w", aws.StringValue(input.ResourceArn), err) + } + + if output == nil || output.ResourceInfo == nil { + return fmt.Errorf("error reading data source, Lake Formation Resource: empty response") + } + + d.SetId(aws.StringValue(input.ResourceArn)) + // d.Set("resource_arn", output.ResourceInfo.ResourceArn) // output not including resource arn currently + d.Set("role_arn", output.ResourceInfo.RoleArn) + if output.ResourceInfo.LastModified != nil { // output not including last modified currently + d.Set("last_modified", output.ResourceInfo.LastModified.Format(time.RFC3339)) + } + + return nil +} diff --git a/aws/data_source_aws_lakeformation_resource_test.go b/aws/data_source_aws_lakeformation_resource_test.go new file mode 100644 index 000000000000..73d8a2c96ea0 --- /dev/null +++ b/aws/data_source_aws_lakeformation_resource_test.go @@ -0,0 +1,101 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccAWSLakeFormationResourceDataSource_basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + dataSourceName := "data.aws_lakeformation_resource.test" + resourceName := "aws_lakeformation_resource.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ProviderFactories: testAccProviderFactories, + CheckDestroy: testAccCheckAWSLakeFormationResourceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationResourceDataSourceConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(dataSourceName, "resource_arn", resourceName, "resource_arn"), + resource.TestCheckResourceAttrPair(dataSourceName, "role_arn", resourceName, "role_arn"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationResourceDataSourceConfig_basic(rName string) string { + return fmt.Sprintf(` +resource "aws_s3_bucket" "test" { + bucket = %[1]q +} + +resource "aws_iam_role" "test" { + name = %[1]q + path = "/test/" + + assume_role_policy = < Date: Fri, 11 Dec 2020 15:28:07 -0500 Subject: [PATCH 19/31] resource/lakeformation_resource: Improve docs, error messages (minor) --- aws/resource_aws_lakeformation_resource.go | 10 +++------- website/docs/r/lakeformation_resource.html.markdown | 2 -- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/aws/resource_aws_lakeformation_resource.go b/aws/resource_aws_lakeformation_resource.go index 6aad389c1110..488b1832a2fd 100644 --- a/aws/resource_aws_lakeformation_resource.go +++ b/aws/resource_aws_lakeformation_resource.go @@ -76,21 +76,17 @@ func resourceAwsLakeFormationResourceRead(d *schema.ResourceData, meta interface output, err := conn.DescribeResource(input) if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeEntityNotFoundException) { - log.Printf("[WARN] Lake Formation Resource (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] Resource Lake Formation Resource (%s) not found, removing from state", d.Id()) d.SetId("") return nil } if err != nil { - return fmt.Errorf("error getting Lake Formation Resource (%s): %w", d.Id(), err) + return fmt.Errorf("error reading resource Lake Formation Resource (%s): %w", d.Id(), err) } if output == nil || output.ResourceInfo == nil { - return fmt.Errorf("error getting Lake Formation Resource (%s): empty response", d.Id()) - } - - if err != nil { - return fmt.Errorf("error reading Lake Formation Resource (%s): %w", d.Id(), err) + return fmt.Errorf("error reading resource Lake Formation Resource (%s): empty response", d.Id()) } // d.Set("resource_arn", output.ResourceInfo.ResourceArn) // output not including resource arn currently diff --git a/website/docs/r/lakeformation_resource.html.markdown b/website/docs/r/lakeformation_resource.html.markdown index 6444cbe713fe..22894ea64d24 100644 --- a/website/docs/r/lakeformation_resource.html.markdown +++ b/website/docs/r/lakeformation_resource.html.markdown @@ -26,8 +26,6 @@ resource "aws_lakeformation_resource" "example" { ## Argument Reference -The following arguments are required: - * `resource_arn` – (Required) Amazon Resource Name (ARN) of the resource, an S3 path. * `role_arn` – (Optional) Role that has read/write access to the resource. If not provided, the Lake Formation service-linked role must exist and is used. From 5db1eb9afe01fdbf83952038817520931b985d71 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 11 Dec 2020 17:39:21 -0500 Subject: [PATCH 20/31] docs/resource/lakeformation_permissions: Rework for design --- .../r/lakeformation_permissions.html.markdown | 193 +++++------------- 1 file changed, 53 insertions(+), 140 deletions(-) diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index 329dff20fdde..52080812b7cb 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -3,138 +3,40 @@ subcategory: "Lake Formation" layout: "aws" page_title: "AWS: aws_lakeformation_permissions" description: |- - Manages the permissions that a principal has on an AWS Glue Data Catalog resource (such as AWS Glue database or AWS Glue tables) + Grants permissions to the principal to access metadata in the Data Catalog and data organized in underlying data storage such as Amazon S3. --- -# Resource: aws_lakeformation_resource +# Resource: aws_lakeformation_permissions -Manages the permissions that a principal has on an AWS Glue Data Catalog resource (such as AWS Glue database or AWS Glue tables). +Grants permissions to the principal to access metadata in the Data Catalog and data organized in underlying data storage such as Amazon S3. Permissions are granted to a principal, in a Data Catalog, relative to a Lake Formation resource, which includes the Data Catalog, databases, and tables. For more information, see [Security and Access Control to Metadata and Data in Lake Formation](https://docs.aws.amazon.com/lake-formation/latest/dg/security-data-access.html). -## Example Usage - -### Granting permissions on Lake Formation resource - -```hcl -data "aws_iam_role" "example" { - name = "existing_lakeformation_role" -} - -data "aws_s3_bucket" "example" { - bucket = "existing_bucket" -} - -resource "aws_lakeformation_resource" "example" { - resource_arn = data.aws_s3_bucket.example.arn - use_service_linked_role = true -} - -resource "aws_lakeformation_permissions" "example" { - permissions = ["DATA_LOCATION_ACCESS"] - principal = data.aws_iam_role.example.arn - - location = aws_lakeformation_resource.example.resource_arn -} -``` - -### Granting permissions on Lake Formation catalog - -```hcl -data "aws_iam_role" "example" { - name = "existing_lakeformation_role" -} - -resource "aws_lakeformation_permissions" "example" { - permissions = ["CREATE_DATABASE"] - principal = data.aws_iam_role.example.arn -} -``` - -### Granting permissions on Lake Formation database - -```hcl -data "aws_iam_role" "example" { - name = "existing_lakeformation_role" -} +~> **NOTE:** Lake Formation grants implicit permissions to data lake administrators, database creators, and table creators. For more information, see [Implicit Lake Formation Permissions](https://docs.aws.amazon.com/lake-formation/latest/dg/implicit-permissions.html). -resource "aws_glue_catalog_database" "example" { - name = "example_database" -} - -resource "aws_lakeformation_permissions" "example" { - permissions = ["ALTER", "CREATE_TABLE", "DROP"] - principal = data.aws_iam_role.example.arn - - database = aws_glue_catalog_database.example.name -} -``` +## Example Usage -### Granting permissions on Lake Formation table +### Grant Permissions For A Lake Formation S3 Resource ```hcl -data "aws_iam_role" "example" { - name = "existing_lakeformation_role" -} - -resource "aws_glue_catalog_database" "example" { - name = "example_database" -} - -resource "aws_glue_catalog_table" "example" { - name = "example_table" - database_name = aws_glue_catalog_database.example.name -} - -resource "aws_lakeformation_permissions" "example" { - permissions = ["INSERT", "DELETE", "SELECT"] - permissions_with_grant_option = ["SELECT"] - principal = data.aws_iam_role.example.arn +resource "aws_lakeformation_permissions" "test" { + principal_arn = aws_iam_role.workflow_role.arn + permissions = ["ALL"] - table { - database = aws_glue_catalog_table.example.database_name - name = aws_glue_catalog_table.example.name + data_location { + resource_arn = aws_lakeformation_resource.test.resource_arn } } ``` -### Granting permissions on Lake Formation columns +### Grant Permissions For A Glue Catalog Database ```hcl -data "aws_iam_role" "example" { - name = "existing_lakeformation_role" -} - -resource "aws_glue_catalog_database" "example" { - name = "example_database" -} - -resource "aws_glue_catalog_table" "example" { - name = "example_table" - database_name = aws_glue_catalog_database.example.name - - storage_descriptor { - columns { - name = "event" - type = "string" - } - columns { - name = "timestamp" - type = "date" - } - columns { - name = "value" - type = "double" - } - } -} +resource "aws_lakeformation_permissions" "test" { + role = aws_iam_role.workflow_role.arn + permissions = ["CREATE_TABLE", "ALTER", "DROP"] -resource "aws_lakeformation_permissions" "example" { - permissions = ["SELECT"] - principal = data.aws_iam_role.example.arn - - table { - database = aws_glue_catalog_table.example.database_name - name = aws_glue_catalog_table.example.name - column_names = ["event", "timestamp"] + database { + name = aws_glue_catalog_database.test.name + catalog_id = "110376042874" } } ``` @@ -143,51 +45,62 @@ resource "aws_lakeformation_permissions" "example" { The following arguments are required: -* `permissions` – (Required) The permissions granted. - -* `principal` – (Required) The AWS Lake Formation principal. +* `permissions` – (Required) List of permissions granted to the principal. Valid values include `ALL`, `ALTER`, `CREATE_DATABASE`, `CREATE_TABLE`, `DATA_LOCATION_ACCESS`, `DELETE`, `DESCRIBE`, `DROP`, `INSERT`, and `SELECT`. For details on each permission, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). +* `principal_arn` – (Required) Principal to be granted the permissions on the resource. Supported principals are IAM users or IAM roles. The following arguments are optional: -* `catalog_id` – (Optional) The identifier for the Data Catalog. By default, the account ID. +* `catalog` - (Optional) Whether the permissions are to be granted for the Data Catalog. Defaults to `false`. +* `catalog_id` – (Optional) Identifier for the Data Catalog. By default, the account ID. The Data Catalog is the persistent metadata store. It contains database definitions, table definitions, and other control information to manage your Lake Formation environment. +* `data_location` - (Optional) Configuration block for data location configuration. Detailed below. +* `database` - (Optional) Configuration block for database configuration. Detailed below. +* `table` - (Optional) Configuration block for table configuration. Detailed below. +* `table_with_columns` - (Optional) Configuration block for table with columns configuration. Detailed below. + +### data_location -* `permissions_with_grant_option` – (Optional) Indicates whether to grant the ability to grant permissions (as a subset of permissions granted)s. +The following argument is required: -* `database` – (Optional) The name of the database resource. Unique to the Data Catalog. A database is a set of associated table definitions organized into a logical group. +* `resource_arn` – (Required) Amazon Resource Name (ARN) that uniquely identifies the data location resource. -* `location` – (Optional) The Amazon Resource Name (ARN) of the resource (data location). +The following argument is optional: -* `table` – (Optional) A structure for the table object. A table is a metadata definition that represents your data. +* `catalog_id` - (Optional) Identifier for the Data Catalog where the location is registered with Lake Formation. By default, it is the account ID of the caller. -Only one of `database`, `location`, `table` can be specified at a time. If none of them is specified, permissions will be set at catalog level. See bellow for available permissions for each resource. +### database -The `table` object supports the following: +The following argument is required: -* `database` – (Required) The name of the database for the table. +* `name` – (Required) Name of the database resource. Unique to the Data Catalog. -* `table` – (Required) The name of the table. +The following argument is optional: -* `column_names` - (Optional) The list of column names for the table. +* `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. -* `excluded_column_names` - (Optional) Excludes column names. Any column with this name will be excluded. +### table -The following summarizes the available Lake Formation permissions on Data Catalog resources: +The following argument is required: -* `DATA_LOCATION_ACCESS` on registered location resources, +* `database_name` – (Required) Name of the database for the table. Unique to a Data Catalog. -* `CREATE_DATABASE` on catalog, +The following arguments are optional: -* `CREATE_TABLE`, `ALTER`, `DROP` on databases, +* `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. +* `name` - (Optional) Name of the table. Not including the table name results in a wildcard representing every table under a database. -* `ALTER`, `INSERT`, `DELETE`, `DROP`, `SELECT` on tables, +### table_with_columns -* `SELECT` on columns. +The following arguments are required: -`INSERT`, `DELETE`, `SELECT` permissions apply to the underlying data, the others to the metadata. +* `database_name` – (Required) Name of the database for the table with columns resource. Unique to the Data Catalog. +* `name` – (Required) Name of the table resource. -There is also a special permission `ALL`, that enables a principal to perform every supported Lake Formation operation on the database or table on which it is granted. +The following arguments are optional: -For details on each permission, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). +* `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. +* `column_names` - (Optional) List of column names for the table. At least one of `column_names` or `excluded_column_names` is required. +* `excluded_column_names` - (Optional) List of column names for the table to exclude. At least one of `column_names` or `excluded_column_names` is required. -~> **NOTE:** Data lake administrators and database creators have implicit Lake Formation permissions. See [Implicit Lake Formation Permissions](https://docs.aws.amazon.com/lake-formation/latest/dg/implicit-permissions.html) for more information. +## Attributes Reference +In addition to the above arguments, no attributes are exported. From 60b7aef835a8f90a801810f397fe0611870e1c4c Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Tue, 15 Dec 2020 18:07:56 -0500 Subject: [PATCH 21/31] resource/lakeformation_permissions: Align with design and conventions --- aws/resource_aws_lakeformation_permissions.go | 595 +++++++++++------- ...urce_aws_lakeformation_permissions_test.go | 234 ++++++- .../r/lakeformation_permissions.html.markdown | 4 +- 3 files changed, 581 insertions(+), 252 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index 841385543cf5..b3687a16a493 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -2,42 +2,75 @@ package aws import ( "fmt" - "time" + "log" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/lakeformation" "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 AwsLakeFormationPermissions() []string { - return []string{ - lakeformation.PermissionAll, - lakeformation.PermissionSelect, - lakeformation.PermissionAlter, - lakeformation.PermissionDrop, - lakeformation.PermissionDelete, - lakeformation.PermissionInsert, - lakeformation.PermissionCreateDatabase, - lakeformation.PermissionCreateTable, - lakeformation.PermissionDataLocationAccess, - } -} - func resourceAwsLakeFormationPermissions() *schema.Resource { return &schema.Resource{ - Create: resourceAwsLakeFormationPermissionsGrant, - Read: resourceAwsLakeFormationPermissionsList, - Delete: resourceAwsLakeFormationPermissionsRevoke, + Create: resourceAwsLakeFormationPermissionsCreate, + Read: resourceAwsLakeFormationPermissionsRead, + Update: resourceAwsLakeFormationPermissionsUpdate, + Delete: resourceAwsLakeFormationPermissionsDelete, Schema: map[string]*schema.Schema{ "catalog_id": { Type: schema.TypeString, ForceNew: true, Optional: true, - Computed: 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{ + "catalog_id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + "resource_arn": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateArn, + }, + }, + }, + }, + "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, Required: true, @@ -45,7 +78,7 @@ func resourceAwsLakeFormationPermissions() *schema.Resource { MinItems: 1, Elem: &schema.Schema{ Type: schema.TypeString, - ValidateFunc: validation.StringInSlice(AwsLakeFormationPermissions(), false), + ValidateFunc: validation.StringInSlice(lakeformation.Permission_Values(), false), }, }, "permissions_with_grant_option": { @@ -55,67 +88,79 @@ func resourceAwsLakeFormationPermissions() *schema.Resource { Computed: true, Elem: &schema.Schema{ Type: schema.TypeString, - ValidateFunc: validation.StringInSlice(AwsLakeFormationPermissions(), false), + ValidateFunc: validation.StringInSlice(lakeformation.Permission_Values(), false), }, }, - "principal": { + "principal_arn": { Type: schema.TypeString, Required: true, ForceNew: true, ValidateFunc: validateArn, }, - "database": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"location", "table"}, - }, - "location": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validateArn, - ConflictsWith: []string{"database", "table"}, - }, "table": { - Type: schema.TypeList, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"database", "location"}, - MinItems: 0, - MaxItems: 1, + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "database": { + "catalog_id": { Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.NoZeroValues, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, + }, + "database_name": { + Type: schema.TypeString, + Required: true, }, "name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + }, + }, + }, + "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, - Required: true, - ForceNew: true, - ValidateFunc: validation.NoZeroValues, + Optional: true, + Computed: true, + ValidateFunc: validateAwsAccountId, }, "column_names": { Type: schema.TypeList, Optional: true, - ForceNew: true, + MinItems: 1, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.NoZeroValues, }, }, + "database_name": { + Type: schema.TypeString, + Required: true, + }, "excluded_column_names": { Type: schema.TypeList, Optional: true, - ForceNew: true, + MinItems: 1, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.NoZeroValues, }, }, + "name": { + Type: schema.TypeString, + Required: true, + }, }, }, }, @@ -123,129 +168,136 @@ func resourceAwsLakeFormationPermissions() *schema.Resource { } } -func resourceAwsLakeFormationPermissionsGrant(d *schema.ResourceData, meta interface{}) error { +func resourceAwsLakeFormationPermissionsCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).lakeformationconn - catalogId := createAwsDataCatalogId(d, meta.(*AWSClient).accountid) - resource := expandAwsLakeFormationResource(d) input := &lakeformation.GrantPermissionsInput{ - CatalogId: aws.String(catalogId), Permissions: expandStringList(d.Get("permissions").([]interface{})), - Principal: expandAwsLakeFormationPrincipal(d), - Resource: resource, + Principal: &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(d.Get("principal_arn").(string)), + }, } - if vs, ok := d.GetOk("permissions_with_grant_option"); ok { - input.PermissionsWithGrantOption = expandStringList(vs.([]interface{})) + + if v, ok := d.GetOk("catalog_id"); ok { + input.CatalogId = aws.String(v.(string)) } - _, err := conn.GrantPermissions(input) - if err != nil { - return fmt.Errorf("Error granting LakeFormation Permissions: %s", err) + if v, ok := d.GetOk("permissions_with_grant_option"); ok { + input.PermissionsWithGrantOption = expandStringList(v.([]interface{})) } - d.SetId(fmt.Sprintf("lakeformation:resource:%s:%s", catalogId, time.Now().UTC().String())) - d.Set("catalog_id", catalogId) + input.Resource = expandLakeFormationResource(d, false) - return resourceAwsLakeFormationPermissionsList(d, meta) -} + output, err := conn.GrantPermissions(input) -func resourceAwsLakeFormationPermissionsList(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).lakeformationconn - catalogId := d.Get("catalog_id").(string) - - // This operation does not support getting privileges on a table with columns. - // Instead, call this operation on the table, and the operation returns the - // table and the table w columns. - resource := expandAwsLakeFormationResource(d) - isTableWithColumnsResource := false - if table := resource.TableWithColumns; table != nil { - resource.Table = &lakeformation.TableResource{ - DatabaseName: resource.TableWithColumns.DatabaseName, - Name: resource.TableWithColumns.Name, - } - resource.TableWithColumns = nil - isTableWithColumnsResource = true + if err != nil { + return fmt.Errorf("error creating Lake Formation Permissions (input: %v): %w", input, err) } - var resourceType string - if table := resource.Catalog; table != nil { - resourceType = lakeformation.DataLakeResourceTypeCatalog - } else if location := resource.DataLocation; location != nil { - resourceType = lakeformation.DataLakeResourceTypeDataLocation - } else if DB := resource.Database; DB != nil { - resourceType = lakeformation.DataLakeResourceTypeDatabase - } else { - resourceType = lakeformation.DataLakeResourceTypeTable + if output == nil { + return fmt.Errorf("error creating Lake Formation Permissions: empty response") } + d.SetId(fmt.Sprintf("%d", hashcode.String(input.String()))) + + return resourceAwsLakeFormationPermissionsRead(d, meta) +} + +func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).lakeformationconn + + // filter results by principal and permissions input := &lakeformation.ListPermissionsInput{ - CatalogId: aws.String(catalogId), - Principal: expandAwsLakeFormationPrincipal(d), - Resource: resource, - ResourceType: &resourceType, + Principal: &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(d.Get("principal_arn").(string)), + }, } - out, err := conn.ListPermissions(input) - if err != nil { - return fmt.Errorf("Error listing LakeFormation Permissions: %s", err) + if v, ok := d.GetOk("catalog_id"); ok { + input.CatalogId = aws.String(v.(string)) } - permissions := out.PrincipalResourcePermissions - if len(permissions) == 0 { - return fmt.Errorf("Error no LakeFormation Permissions found: %s", input) - } + input.Resource = expandLakeFormationResource(d, true) - // This operation does not support getting privileges on a table with columns. - // Instead, call this operation on the table, and the operation returns the - // table and the table w columns. - if isTableWithColumnsResource { - filtered := make([]*lakeformation.PrincipalResourcePermissions, 0) - for _, p := range permissions { - if table := p.Resource.TableWithColumns; table != nil { - filtered = append(filtered, p) + log.Printf("[DEBUG] Reading Lake Formation permissions: %v", input) + var principalResourcePermissions []*lakeformation.PrincipalResourcePermissions + + err := conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { + for _, permission := range resp.PrincipalResourcePermissions { + if permission == nil { + continue } + + principalResourcePermissions = append(principalResourcePermissions, permission) } - permissions = filtered - } + return !lastPage + }) - permissionsHead := permissions[0] - d.Set("catalog_id", catalogId) - d.Set("principal", permissionsHead.Principal.DataLakePrincipalIdentifier) - if dataLocation := permissionsHead.Resource.DataLocation; dataLocation != nil { - d.Set("location", dataLocation.ResourceArn) - } - if database := permissionsHead.Resource.Database; database != nil { - d.Set("database", database.Name) - } - if table := permissionsHead.Resource.Table; table != nil { - d.Set("table", flattenAWSLakeFormationTable(table)) + if err != nil { + return fmt.Errorf("error reading Lake Formation permissions: %w", err) } - if table := permissionsHead.Resource.TableWithColumns; table != nil { - d.Set("table", flattenAWSLakeFormationTableWithColumns(table)) + + if len(principalResourcePermissions) > 1 { + return fmt.Errorf("error reading Lake Formation permissions: %s", "multiple permissions found") } - var allPermissions, allPermissionsWithGrant []*string - for _, p := range permissions { - allPermissions = append(allPermissions, p.Permissions...) - allPermissionsWithGrant = append(allPermissionsWithGrant, p.PermissionsWithGrantOption...) + + 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) + } } - d.Set("permissions", allPermissions) - d.Set("permissions_with_grant_option", allPermissionsWithGrant) return nil } -func resourceAwsLakeFormationPermissionsRevoke(d *schema.ResourceData, meta interface{}) error { +func resourceAwsLakeFormationPermissionsUpdate(d *schema.ResourceData, meta interface{}) error { + return nil +} + +func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).lakeformationconn - catalogId := d.Get("catalog_id").(string) input := &lakeformation.RevokePermissionsInput{ - CatalogId: aws.String(catalogId), Permissions: expandStringList(d.Get("permissions").([]interface{})), - Principal: expandAwsLakeFormationPrincipal(d), - Resource: expandAwsLakeFormationResource(d), + Principal: &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(d.Get("principal_arn").(string)), + }, } - if vs, ok := d.GetOk("permissions_with_grant_option"); ok { - input.PermissionsWithGrantOption = expandStringList(vs.([]interface{})) + + input.Resource = expandLakeFormationResource(d, false) + + if v, ok := d.GetOk("catalog_id"); ok { + input.CatalogId = aws.String(v.(string)) + } + + if v, ok := d.GetOk("permissions_with_grant_option"); ok { + input.PermissionsWithGrantOption = expandStringList(v.([]interface{})) } _, err := conn.RevokePermissions(input) @@ -256,111 +308,212 @@ func resourceAwsLakeFormationPermissionsRevoke(d *schema.ResourceData, meta inte return nil } -func expandAwsLakeFormationPrincipal(d *schema.ResourceData) *lakeformation.DataLakePrincipal { - return &lakeformation.DataLakePrincipal{ - DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), +func expandLakeFormationResource(d *schema.ResourceData, squashTableWithColumns bool) *lakeformation.Resource { + res := &lakeformation.Resource{} + + if v, ok := d.GetOk("catalog_resource"); ok && v.(bool) { + res.Catalog = &lakeformation.CatalogResource{} } -} -func expandAwsLakeFormationResource(d *schema.ResourceData) *lakeformation.Resource { - if v, ok := d.GetOk("database"); ok { - databaseName := v.(string) - if len(databaseName) > 0 { - return &lakeformation.Resource{ - Database: &lakeformation.DatabaseResource{ - Name: aws.String(databaseName), - }, - } - } + if v, ok := d.GetOk("data_location"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + res.DataLocation = expandLakeFormationDataLocationResource(v.([]interface{})[0].(map[string]interface{})) } - if v, ok := d.GetOk("location"); ok { - location := v.(string) - if len(location) > 0 { - return &lakeformation.Resource{ - DataLocation: &lakeformation.DataLocationResource{ - ResourceArn: aws.String(v.(string)), - }, - } - } + + if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + res.Database = expandLakeFormationDatabaseResource(v.([]interface{})[0].(map[string]interface{})) } - if vs, ok := d.GetOk("table"); ok { - tables := vs.([]interface{}) - if len(tables) > 0 { - table := tables[0].(map[string]interface{}) - resource := &lakeformation.Resource{} - var databaseName, tableName string - var columnNames, excludedColumnNames []interface{} - if x, ok := table["database"]; ok { - databaseName = x.(string) - } - if x, ok := table["name"]; ok { - tableName = x.(string) - } - if xs, ok := table["column_names"]; ok { - columnNames = xs.([]interface{}) - } - if xs, ok := table["excluded_column_names"]; ok { - excludedColumnNames = xs.([]interface{}) - } + if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + res.Table = expandLakeFormationTableResource(v.([]interface{})[0].(map[string]interface{})) + } - if len(columnNames) > 0 || len(excludedColumnNames) > 0 { - tableWithColumns := &lakeformation.TableWithColumnsResource{ - DatabaseName: aws.String(databaseName), - Name: aws.String(tableName), - } - if len(columnNames) > 0 { - tableWithColumns.ColumnNames = expandStringList(columnNames) - } - if len(excludedColumnNames) > 0 { - tableWithColumns.ColumnWildcard = &lakeformation.ColumnWildcard{ - ExcludedColumnNames: expandStringList(excludedColumnNames), - } - } - resource.TableWithColumns = tableWithColumns - } else { - resource.Table = &lakeformation.TableResource{ - DatabaseName: aws.String(databaseName), - Name: aws.String(tableName), - } - } - return resource + if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + if squashTableWithColumns { + // ListPermissions does not support getting privileges by tables with columns. Instead, + // use the table which will return both table and table with columns. + res.Table = expandLakeFormationTableResource(v.([]interface{})[0].(map[string]interface{})) + } else { + res.TableWithColumns = expandLakeFormationTableWithColumnsResource(v.([]interface{})[0].(map[string]interface{})) } } - return &lakeformation.Resource{ - Catalog: &lakeformation.CatalogResource{}, + + return res +} + +func expandLakeFormationDataLocationResource(tfMap map[string]interface{}) *lakeformation.DataLocationResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.DataLocationResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["resource_arn"].(string); ok && v != "" { + apiObject.ResourceArn = aws.String(v) } + + return apiObject } -func flattenAWSLakeFormationTable(tb *lakeformation.TableResource) map[string]interface{} { - m := make(map[string]interface{}) +func flattenLakeFormationDataLocationResource(apiObject *lakeformation.DataLocationResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } - m["database"] = tb.DatabaseName - m["name"] = tb.Name + if v := apiObject.ResourceArn; v != nil { + tfMap["resource_arn"] = aws.StringValue(v) + } - return m + return tfMap } -func flattenAWSLakeFormationTableWithColumns(tb *lakeformation.TableWithColumnsResource) map[string]interface{} { - m := make(map[string]interface{}) +func expandLakeFormationDatabaseResource(tfMap map[string]interface{}) *lakeformation.DatabaseResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.DatabaseResource{} - m["database"] = tb.DatabaseName - m["name"] = tb.Name - if columnNames := tb.ColumnNames; columnNames != nil { - m["column_names"] = columnNames + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) } - if columnWildcard := tb.ColumnWildcard; columnWildcard != nil { - m["excluded_column_names"] = columnWildcard.ExcludedColumnNames + + if v, ok := tfMap["name"].(string); ok && v != "" { + apiObject.Name = aws.String(v) } - return m + return apiObject } -func createAwsDataCatalogId(d *schema.ResourceData, accountId string) (catalogId string) { - if inputCatalogId, ok := d.GetOkExists("catalog_id"); ok { - catalogId = inputCatalogId.(string) +func flattenLakeFormationDatabaseResource(apiObject *lakeformation.DatabaseResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + return tfMap +} + +func expandLakeFormationTableResource(tfMap map[string]interface{}) *lakeformation.TableResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.TableResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["database_name"].(string); ok && v != "" { + apiObject.DatabaseName = aws.String(v) + } + + if v, ok := tfMap["name"].(string); ok && v != "" { + apiObject.Name = aws.String(v) } else { - catalogId = accountId + apiObject.TableWildcard = &lakeformation.TableWildcard{} + } + + return apiObject +} + +func flattenLakeFormationTableResource(apiObject *lakeformation.TableResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + if v := apiObject.DatabaseName; v != nil { + tfMap["database_name"] = aws.StringValue(v) + } + + if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + return tfMap +} + +func expandLakeFormationTableWithColumnsResource(tfMap map[string]interface{}) *lakeformation.TableWithColumnsResource { + if tfMap == nil { + return nil + } + + apiObject := &lakeformation.TableWithColumnsResource{} + + if v, ok := tfMap["catalog_id"].(string); ok && v != "" { + apiObject.CatalogId = aws.String(v) + } + + if v, ok := tfMap["column_names"]; ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + apiObject.ColumnNames = expandStringList(v.([]interface{})) } - return + + if v, ok := tfMap["database_name"].(string); ok && v != "" { + apiObject.DatabaseName = aws.String(v) + } + + if v, ok := tfMap["excluded_column_names"]; ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + apiObject.ColumnWildcard = &lakeformation.ColumnWildcard{ + ExcludedColumnNames: expandStringList(v.([]interface{})), + } + } + + if v, ok := tfMap["name"].(string); ok && v != "" { + apiObject.Name = aws.String(v) + } + + return apiObject +} + +func flattenLakeFormationTableWithColumnsResource(apiObject *lakeformation.TableWithColumnsResource) map[string]interface{} { + if apiObject == nil { + return nil + } + + tfMap := map[string]interface{}{} + + if v := apiObject.CatalogId; v != nil { + tfMap["catalog_id"] = aws.StringValue(v) + } + + tfMap["column_names"] = flattenStringList(apiObject.ColumnNames) + + if v := apiObject.DatabaseName; v != nil { + tfMap["database_name"] = aws.StringValue(v) + } + + if v := apiObject.ColumnWildcard; v != nil { + tfMap["excluded_column_names"] = flattenStringList(v.ExcludedColumnNames) + } + + if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + return tfMap } diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index 0e3e05be91e1..a2b39e7743e6 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -11,6 +11,50 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) +func TestAccAWSLakeFormationPermissions_basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceAddr := "aws_lakeformation_resource.test" + bucketAddr := "aws_s3_bucket.test" + roleAddr := "aws_iam_role.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceAddr), + resource.TestCheckResourceAttrPair(resourceAddr, "role_arn", roleAddr, "arn"), + resource.TestCheckResourceAttrPair(resourceAddr, "resource_arn", bucketAddr, "arn"), + ), + }, + }, + }) +} + +func TestAccAWSLakeFormationPermissions_disappears(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_resource.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsLakeFormationPermissions(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccAWSLakeFormationPermissions_full(t *testing.T) { rName := acctest.RandomWithPrefix("lakeformation-test-bucket") dName := acctest.RandomWithPrefix("lakeformation-test-db") @@ -25,7 +69,7 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsRevoked, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { Config: testAccAWSLakeFormationPermissionsConfig_catalog(), @@ -104,6 +148,166 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { }) } +func testAccCheckAWSLakeFormationPermissionsDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).lakeformationconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lakeformation_permissions" { + continue + } + + principal := rs.Primary.Attributes["principal"] + catalogId := rs.Primary.Attributes["catalog_id"] + + input := &lakeformation.ListPermissionsInput{ + CatalogId: aws.String(catalogId), + Principal: &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(principal), + }, + } + + out, err := conn.ListPermissions(input) + if err == nil { + fmt.Print(out) + return fmt.Errorf("Resource still registered: %s %s", catalogId, principal) + } + } + + return nil +} + +func testAccCheckAWSLakeFormationPermissionsExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("resource not found: %s", resourceName) + } + + conn := testAccProvider.Meta().(*AWSClient).lakeformationconn + + input := &lakeformation.ListPermissionsInput{ + MaxResults: aws.Int64(1), + Principal: &lakeformation.DataLakePrincipal{ + DataLakePrincipalIdentifier: aws.String(rs.Primary.Attributes["principal"]), + }, + } + + if rs.Primary.Attributes["catalog_resource"] == "true" { + input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeCatalog) + } + + if rs.Primary.Attributes["data_location.#"] != "0" { + input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeDataLocation) + } + + if rs.Primary.Attributes["database.#"] != "0" { + input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeDatabase) + } + + if rs.Primary.Attributes["table.#"] != "0" { + input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeTable) + } + + if rs.Primary.Attributes["table_with_columns.#"] != "0" { + input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeTable) + } + + _, err := conn.ListPermissions(input) + + if err != nil { + return fmt.Errorf("error getting Lake Formation resource (%s): %w", rs.Primary.ID, err) + } + + return nil + } +} + +func testAccAWSLakeFormationPermissionsConfig_basic(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "workflow_role" { + name = %[1]q + + assume_role_policy = < **NOTE:** Lake Formation grants implicit permissions to data lake administrators, database creators, and table creators. For more information, see [Implicit Lake Formation Permissions](https://docs.aws.amazon.com/lake-formation/latest/dg/implicit-permissions.html). +~> **NOTE:** This resource deals with explicitly granted permissions. Lake Formation grants implicit permissions to data lake administrators, database creators, and table creators. For more information, see [Implicit Lake Formation Permissions](https://docs.aws.amazon.com/lake-formation/latest/dg/implicit-permissions.html). ## Example Usage @@ -50,8 +50,8 @@ The following arguments are required: The following arguments are optional: -* `catalog` - (Optional) Whether the permissions are to be granted for the Data Catalog. Defaults to `false`. * `catalog_id` – (Optional) Identifier for the Data Catalog. By default, the account ID. The Data Catalog is the persistent metadata store. It contains database definitions, table definitions, and other control information to manage your Lake Formation environment. +* `catalog_resource` - (Optional) Whether the permissions are to be granted for the Data Catalog. Defaults to `false`. * `data_location` - (Optional) Configuration block for data location configuration. Detailed below. * `database` - (Optional) Configuration block for database configuration. Detailed below. * `table` - (Optional) Configuration block for table configuration. Detailed below. From e139a59a57b50f4592c32021408fa0940f85c78c Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 17 Dec 2020 10:12:10 -0500 Subject: [PATCH 22/31] validators: Add ValidatePrincipal for Lake Formation principals --- aws/validators.go | 19 ++++++++++++++++++ aws/validators_test.go | 44 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/aws/validators.go b/aws/validators.go index 526eb613492d..e982a1e3f20d 100644 --- a/aws/validators.go +++ b/aws/validators.go @@ -660,6 +660,25 @@ func validateAwsAccountId(v interface{}, k string) (ws []string, errors []error) return } +func validatePrincipal(v interface{}, k string) (ws []string, errors []error) { + value := v.(string) + + if value == "IAM_ALLOWED_PRINCIPALS" { + return ws, errors + } + + wsARN, errorsARN := validateArn(v, k) + ws = append(ws, wsARN...) + errors = append(errors, errorsARN...) + + pattern := `\d{12}:(role|user)/` + if !regexp.MustCompile(pattern).MatchString(value) { + errors = append(errors, fmt.Errorf("%q doesn't look like a user or role: %q", k, value)) + } + + return ws, errors +} + func validateArn(v interface{}, k string) (ws []string, errors []error) { value := v.(string) diff --git a/aws/validators_test.go b/aws/validators_test.go index 900c11308f22..e9941893c64e 100644 --- a/aws/validators_test.go +++ b/aws/validators_test.go @@ -359,6 +359,50 @@ func TestValidateArn(t *testing.T) { } } +func TestValidatePrincipal(t *testing.T) { + v := "" + _, errors := validatePrincipal(v, "arn") + if len(errors) == 0 { + t.Fatalf("%q should not be validated as a principal %d: %q", v, len(errors), errors) + } + + validNames := []string{ + "IAM_ALLOWED_PRINCIPALS", // Special principal + "arn:aws-us-gov:iam::357342307427:role/tf-acc-test-3217321001347236965", // lintignore:AWSAT005 // IAM Role + "arn:aws:iam::123456789012:user/David", // lintignore:AWSAT005 // IAM User + "arn:aws-us-gov:iam:us-west-2:357342307427:role/tf-acc-test-3217321001347236965", // lintignore:AWSAT003,AWSAT005 // Non-global IAM Role? + "arn:aws:iam:us-east-1:123456789012:user/David", // lintignore:AWSAT003,AWSAT005 // Non-global IAM User? + } + for _, v := range validNames { + _, errors := validatePrincipal(v, "arn") + if len(errors) != 0 { + t.Fatalf("%q should be a valid principal: %q", v, errors) + } + } + + invalidNames := []string{ + "IAM_NOT_ALLOWED_PRINCIPALS", // doesn't exist + "arn", + "123456789012", + "arn:aws", + "arn:aws:logs", //lintignore:AWSAT005 + "arn:aws:logs:region:*:*", //lintignore:AWSAT005 + "arn:aws:elasticbeanstalk:us-east-1:123456789012:environment/My App/MyEnvironment", // lintignore:AWSAT003,AWSAT005 // not a user or role + "arn:aws:iam::aws:policy/CloudWatchReadOnlyAccess", // lintignore:AWSAT005 // not a user or role + "arn:aws:rds:eu-west-1:123456789012:db:mysql-db", // lintignore:AWSAT003,AWSAT005 // not a user or role + "arn:aws:s3:::my_corporate_bucket/exampleobject.png", // lintignore:AWSAT005 // not a user or role + "arn:aws:events:us-east-1:319201112229:rule/rule_name", // lintignore:AWSAT003,AWSAT005 // not a user or role + "arn:aws-us-gov:ec2:us-gov-west-1:123456789012:instance/i-12345678", // lintignore:AWSAT003,AWSAT005 // not a user or role + "arn:aws-us-gov:s3:::bucket/object", // lintignore:AWSAT005 // not a user or role + } + for _, v := range invalidNames { + _, errors := validatePrincipal(v, "arn") + if len(errors) == 0 { + t.Fatalf("%q should be an invalid principal", v) + } + } +} + func TestValidateEC2AutomateARN(t *testing.T) { validNames := []string{ "arn:aws:automate:us-east-1:ec2:reboot", //lintignore:AWSAT003,AWSAT005 From cb153218451031fa44cb1a6f904a3d77972b19f3 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 17 Dec 2020 10:16:41 -0500 Subject: [PATCH 23/31] resource/lakeformation: Use ValidatePrincipal for Lake Formation principals --- ...ce_aws_lakeformation_data_lake_settings.go | 4 +- aws/resource_aws_lakeformation_permissions.go | 10 ++-- ...urce_aws_lakeformation_permissions_test.go | 48 +++++++++++-------- .../r/lakeformation_permissions.html.markdown | 8 ++-- 4 files changed, 39 insertions(+), 31 deletions(-) diff --git a/aws/resource_aws_lakeformation_data_lake_settings.go b/aws/resource_aws_lakeformation_data_lake_settings.go index 51463b4b756c..f03fc3721770 100644 --- a/aws/resource_aws_lakeformation_data_lake_settings.go +++ b/aws/resource_aws_lakeformation_data_lake_settings.go @@ -48,7 +48,7 @@ func resourceAwsLakeFormationDataLakeSettings() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validation.NoZeroValues, // can be non-ARN, e.g. "IAM_ALLOWED_PRINCIPALS" + ValidateFunc: validatePrincipal, }, }, }, @@ -73,7 +73,7 @@ func resourceAwsLakeFormationDataLakeSettings() *schema.Resource { Type: schema.TypeString, Optional: true, Computed: true, - ValidateFunc: validation.NoZeroValues, // can be non-ARN, e.g. "IAM_ALLOWED_PRINCIPALS" + ValidateFunc: validatePrincipal, }, }, }, diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index b3687a16a493..0002b132112d 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -91,11 +91,11 @@ func resourceAwsLakeFormationPermissions() *schema.Resource { ValidateFunc: validation.StringInSlice(lakeformation.Permission_Values(), false), }, }, - "principal_arn": { + "principal": { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validateArn, + ValidateFunc: validatePrincipal, }, "table": { Type: schema.TypeList, @@ -174,7 +174,7 @@ func resourceAwsLakeFormationPermissionsCreate(d *schema.ResourceData, meta inte input := &lakeformation.GrantPermissionsInput{ Permissions: expandStringList(d.Get("permissions").([]interface{})), Principal: &lakeformation.DataLakePrincipal{ - DataLakePrincipalIdentifier: aws.String(d.Get("principal_arn").(string)), + DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), }, } @@ -209,7 +209,7 @@ func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interf // filter results by principal and permissions input := &lakeformation.ListPermissionsInput{ Principal: &lakeformation.DataLakePrincipal{ - DataLakePrincipalIdentifier: aws.String(d.Get("principal_arn").(string)), + DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), }, } @@ -286,7 +286,7 @@ func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta inte input := &lakeformation.RevokePermissionsInput{ Permissions: expandStringList(d.Get("permissions").([]interface{})), Principal: &lakeformation.DataLakePrincipal{ - DataLakePrincipalIdentifier: aws.String(d.Get("principal_arn").(string)), + DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), }, } diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index a2b39e7743e6..f68c4f24e942 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -12,7 +12,7 @@ import ( ) func TestAccAWSLakeFormationPermissions_basic(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") + //rName := acctest.RandomWithPrefix("tf-acc-test") resourceAddr := "aws_lakeformation_resource.test" bucketAddr := "aws_s3_bucket.test" roleAddr := "aws_iam_role.test" @@ -23,7 +23,7 @@ func TestAccAWSLakeFormationPermissions_basic(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_basic(rName), + Config: testAccAWSLakeFormationPermissionsConfig_catalog(), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceAddr), resource.TestCheckResourceAttrPair(resourceAddr, "role_arn", roleAddr, "arn"), @@ -294,16 +294,23 @@ resource "aws_lakeformation_resource" "test" { resource_arn = aws_s3_bucket.test.arn } +data "aws_caller_identity" "current" {} + +resource "aws_lakeformation_data_lake_settings" "test" { + data_lake_admins = [data.aws_caller_identity.current.arn] +} + // grants permissions to workflow role in catalog on bucket resource "aws_lakeformation_permissions" "grants" { - principal_arn = aws_iam_role.workflow_role.arn + principal_arn = data.aws_caller_identity.current.arn permissions = ["CREATE_DATABASE"] - data_location { - resource_arn = aws_s3_bucket.test.arn - } + //data_location { + // resource_arn = aws_s3_bucket.test.arn + //} + catalog_resource = true - depends_on = ["aws_lakeformation_resource.test"] + depends_on = ["aws_lakeformation_resource.test", "aws_iam_role.workflow_role", "aws_iam_role_policy_attachment.managed"] } `, rName) } @@ -316,17 +323,18 @@ data "aws_iam_role" "test" { name = "AWSServiceRoleForLakeFormationDataAccess" } -resource "aws_lakeformation_datalake_settings" "test" { - admins = [ +resource "aws_lakeformation_data_lake_settings" "test" { + data_lake_admins = [ data.aws_caller_identity.current.arn ] } resource "aws_lakeformation_permissions" "test" { permissions = ["CREATE_DATABASE"] - principal = data.aws_iam_role.test.arn + principal_arn = data.aws_iam_role.test.arn + catalog_resource = true - depends_on = ["aws_lakeformation_datalake_settings.test"] + depends_on = ["aws_lakeformation_data_lake_settings.test"] } ` } @@ -343,7 +351,7 @@ resource "aws_s3_bucket" "test" { bucket = %[1]q } -resource "aws_lakeformation_datalake_settings" "test" { +resource "aws_lakeformation_data_lake_settings" "test" { admins = [ data.aws_caller_identity.current.arn ] @@ -353,7 +361,7 @@ resource "aws_lakeformation_resource" "test" { resource_arn = aws_s3_bucket.test.arn use_service_linked_role = true - depends_on = ["aws_lakeformation_datalake_settings.test"] + depends_on = ["aws_lakeformation_data_lake_settings.test"] } resource "aws_lakeformation_permissions" "test" { @@ -362,7 +370,7 @@ resource "aws_lakeformation_permissions" "test" { location = aws_lakeformation_resource.test.resource_arn - depends_on = ["aws_lakeformation_datalake_settings.test"] + depends_on = ["aws_lakeformation_data_lake_settings.test"] } `, rName) } @@ -383,7 +391,7 @@ resource "aws_glue_catalog_database" "test" { name = %[2]q } -resource "aws_lakeformation_datalake_settings" "test" { +resource "aws_lakeformation_data_lake_settings" "test" { admins = [ data.aws_caller_identity.current.arn ] @@ -396,7 +404,7 @@ resource "aws_lakeformation_permissions" "test" { database = aws_glue_catalog_database.test.name - depends_on = ["aws_lakeformation_datalake_settings.test"] + depends_on = ["aws_lakeformation_data_lake_settings.test"] } `, rName, dName) } @@ -422,7 +430,7 @@ resource "aws_glue_catalog_table" "test" { database_name = aws_glue_catalog_database.test.name } -resource "aws_lakeformation_datalake_settings" "test" { +resource "aws_lakeformation_data_lake_settings" "test" { admins = [ data.aws_caller_identity.current.arn ] @@ -437,7 +445,7 @@ resource "aws_lakeformation_permissions" "test" { name = aws_glue_catalog_table.test.name } - depends_on = ["aws_lakeformation_datalake_settings.test"] + depends_on = ["aws_lakeformation_data_lake_settings.test"] } `, rName, dName, tName, permissions) } @@ -478,7 +486,7 @@ resource "aws_glue_catalog_table" "test" { } } -resource "aws_lakeformation_datalake_settings" "test" { +resource "aws_lakeformation_data_lake_settings" "test" { admins = [ data.aws_caller_identity.current.arn ] @@ -494,7 +502,7 @@ resource "aws_lakeformation_permissions" "test" { column_names = ["event", "timestamp"] } - depends_on = ["aws_lakeformation_datalake_settings.test"] + depends_on = ["aws_lakeformation_data_lake_settings.test"] } `, rName, dName, tName) } diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index 3ab1c1739df9..ea37f9cf5909 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -52,10 +52,10 @@ The following arguments are optional: * `catalog_id` – (Optional) Identifier for the Data Catalog. By default, the account ID. The Data Catalog is the persistent metadata store. It contains database definitions, table definitions, and other control information to manage your Lake Formation environment. * `catalog_resource` - (Optional) Whether the permissions are to be granted for the Data Catalog. Defaults to `false`. -* `data_location` - (Optional) Configuration block for data location configuration. Detailed below. -* `database` - (Optional) Configuration block for database configuration. Detailed below. -* `table` - (Optional) Configuration block for table configuration. Detailed below. -* `table_with_columns` - (Optional) Configuration block for table with columns configuration. Detailed below. +* `data_location` - (Optional) Configuration block for a data location resource. Detailed below. +* `database` - (Optional) Configuration block for a database resource. Detailed below. +* `table` - (Optional) Configuration block for a table resource. Detailed below. +* `table_with_columns` - (Optional) Configuration block for a table with columns resource. Detailed below. ### data_location From cd9e6c20e2f3b466fa8f6274335472bc34b50d1b Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 17 Dec 2020 15:39:36 -0500 Subject: [PATCH 24/31] resource/lakeformation_permissions: Eventual consistency issues --- aws/resource_aws_lakeformation_permissions.go | 92 +++++++-- ...urce_aws_lakeformation_permissions_test.go | 190 ++++++++++++++---- .../r/lakeformation_permissions.html.markdown | 3 + 3 files changed, 227 insertions(+), 58 deletions(-) diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index 0002b132112d..748217d01cb4 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -3,9 +3,11 @@ package aws import ( "fmt" "log" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/lakeformation" + "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" @@ -15,7 +17,7 @@ func resourceAwsLakeFormationPermissions() *schema.Resource { return &schema.Resource{ Create: resourceAwsLakeFormationPermissionsCreate, Read: resourceAwsLakeFormationPermissionsRead, - Update: resourceAwsLakeFormationPermissionsUpdate, + Update: resourceAwsLakeFormationPermissionsCreate, Delete: resourceAwsLakeFormationPermissionsDelete, Schema: map[string]*schema.Schema{ @@ -188,7 +190,38 @@ func resourceAwsLakeFormationPermissionsCreate(d *schema.ResourceData, meta inte input.Resource = expandLakeFormationResource(d, false) - output, err := conn.GrantPermissions(input) + var output *lakeformation.GrantPermissionsOutput + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + var err error + output, err = conn.GrantPermissions(input) + if err != nil { + if isAWSErr(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeInvalidInputException, "Grantee has no permissions") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeInvalidInputException, "register the S3 path") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeConcurrentModificationException, "") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeOperationTimeoutException, "") { + return resource.RetryableError(err) + } + if isAWSErr(err, "AccessDeniedException", "is not authorized to access requested permissions") { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(fmt.Errorf("error creating Lake Formation Permissions: %w", err)) + } + return nil + }) + + if isResourceTimeoutError(err) { + output, err = conn.GrantPermissions(input) + } if err != nil { return fmt.Errorf("error creating Lake Formation Permissions (input: %v): %w", input, err) @@ -206,7 +239,6 @@ func resourceAwsLakeFormationPermissionsCreate(d *schema.ResourceData, meta inte func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).lakeformationconn - // filter results by principal and permissions input := &lakeformation.ListPermissionsInput{ Principal: &lakeformation.DataLakePrincipal{ DataLakePrincipalIdentifier: aws.String(d.Get("principal").(string)), @@ -222,15 +254,25 @@ func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interf log.Printf("[DEBUG] Reading Lake Formation permissions: %v", input) var principalResourcePermissions []*lakeformation.PrincipalResourcePermissions - err := conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { - for _, permission := range resp.PrincipalResourcePermissions { - if permission == nil { - continue - } + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + var err error + err = conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { + for _, permission := range resp.PrincipalResourcePermissions { + if permission == nil { + continue + } - principalResourcePermissions = append(principalResourcePermissions, permission) + 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 creating Lake Formation Permissions: %w", err)) } - return !lastPage + return nil }) if err != nil { @@ -276,10 +318,6 @@ func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interf return nil } -func resourceAwsLakeFormationPermissionsUpdate(d *schema.ResourceData, meta interface{}) error { - return nil -} - func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).lakeformationconn @@ -290,8 +328,6 @@ func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta inte }, } - input.Resource = expandLakeFormationResource(d, false) - if v, ok := d.GetOk("catalog_id"); ok { input.CatalogId = aws.String(v.(string)) } @@ -300,9 +336,29 @@ func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta inte input.PermissionsWithGrantOption = expandStringList(v.([]interface{})) } - _, err := conn.RevokePermissions(input) + input.Resource = expandLakeFormationResource(d, false) + + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + var err error + _, err = conn.RevokePermissions(input) + if err != nil { + if isAWSErr(err, lakeformation.ErrCodeInvalidInputException, "register the S3 path") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeConcurrentModificationException, "") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeOperationTimeoutException, "") { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(fmt.Errorf("unable to revoke Lake Formation Permissions: %w", err)) + } + return nil + }) + if err != nil { - return fmt.Errorf("Error revoking LakeFormation Permissions: %s", err) + return fmt.Errorf("unable to revoke LakeFormation Permissions (input: %v): %w", input, err) } return nil diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index f68c4f24e942..73eb6b2292a3 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -3,6 +3,7 @@ package aws import ( "fmt" "testing" + "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/lakeformation" @@ -12,10 +13,9 @@ import ( ) func TestAccAWSLakeFormationPermissions_basic(t *testing.T) { - //rName := acctest.RandomWithPrefix("tf-acc-test") - resourceAddr := "aws_lakeformation_resource.test" - bucketAddr := "aws_s3_bucket.test" - roleAddr := "aws_iam_role.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -23,11 +23,13 @@ func TestAccAWSLakeFormationPermissions_basic(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_catalog(), + Config: testAccAWSLakeFormationPermissionsConfig_basic(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceAddr), - resource.TestCheckResourceAttrPair(resourceAddr, "role_arn", roleAddr, "arn"), - resource.TestCheckResourceAttrPair(resourceAddr, "resource_arn", bucketAddr, "arn"), + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", "CREATE_DATABASE"), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "true"), ), }, }, @@ -36,7 +38,7 @@ func TestAccAWSLakeFormationPermissions_basic(t *testing.T) { func TestAccAWSLakeFormationPermissions_disappears(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_resource.test" + resourceName := "aws_lakeformation_permissions.test" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -55,6 +57,33 @@ func TestAccAWSLakeFormationPermissions_disappears(t *testing.T) { }) } +func TestAccAWSLakeFormationPermissions_dataLocation(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + bucketName := "aws_s3_bucket.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_dataLocation(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", "DATA_LOCATION_ACCESS"), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), + resource.TestCheckResourceAttr(resourceName, "data_location.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "data_location.0.resource_arn", bucketName, "arn"), + ), + }, + }, + }) +} + func TestAccAWSLakeFormationPermissions_full(t *testing.T) { rName := acctest.RandomWithPrefix("lakeformation-test-bucket") dName := acctest.RandomWithPrefix("lakeformation-test-db") @@ -194,10 +223,22 @@ func testAccCheckAWSLakeFormationPermissionsExists(resourceName string) resource if rs.Primary.Attributes["catalog_resource"] == "true" { input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeCatalog) + input.Resource = &lakeformation.Resource{ + Catalog: &lakeformation.CatalogResource{}, + } } if rs.Primary.Attributes["data_location.#"] != "0" { input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeDataLocation) + res := &lakeformation.DataLocationResource{ + ResourceArn: aws.String(rs.Primary.Attributes["data_location.0.resource_arn"]), + } + if rs.Primary.Attributes["data_location.0.catalog_id"] != "" { + res.CatalogId = aws.String(rs.Primary.Attributes["data_location.0.catalog_id"]) + } + input.Resource = &lakeformation.Resource{ + DataLocation: res, + } } if rs.Primary.Attributes["database.#"] != "0" { @@ -212,10 +253,40 @@ func testAccCheckAWSLakeFormationPermissionsExists(resourceName string) resource input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeTable) } - _, err := conn.ListPermissions(input) + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + var err error + _, err = conn.ListPermissions(input) + if err != nil { + if isAWSErr(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeInvalidInputException, "Grantee has no permissions") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeInvalidInputException, "register the S3 path") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeConcurrentModificationException, "") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeOperationTimeoutException, "") { + return resource.RetryableError(err) + } + if isAWSErr(err, "AccessDeniedException", "is not authorized to access requested permissions") { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(fmt.Errorf("unable to get Lake Formation Permissions: %w", err)) + } + return nil + }) + + if isResourceTimeoutError(err) { + _, err = conn.ListPermissions(input) + } if err != nil { - return fmt.Errorf("error getting Lake Formation resource (%s): %w", rs.Primary.ID, err) + return fmt.Errorf("unable to get Lake Formation permissions (%s): %w", rs.Primary.ID, err) } return nil @@ -226,7 +297,7 @@ func testAccAWSLakeFormationPermissionsConfig_basic(rName string) string { return fmt.Sprintf(` data "aws_partition" "current" {} -resource "aws_iam_role" "workflow_role" { +resource "aws_iam_role" "test" { name = %[1]q assume_role_policy = < Date: Thu, 17 Dec 2020 18:32:30 -0500 Subject: [PATCH 25/31] resource/lakeformation_permissions: Adjust for passing tests --- ...ce_aws_lakeformation_data_lake_settings.go | 4 +- ...s_lakeformation_data_lake_settings_test.go | 6 +- ...ce_aws_lakeformation_data_lake_settings.go | 6 +- ...s_lakeformation_data_lake_settings_test.go | 12 +- aws/resource_aws_lakeformation_permissions.go | 15 +- ...urce_aws_lakeformation_permissions_test.go | 414 ++++++++---------- ...formation_data_lake_settings.html.markdown | 2 +- ...formation_data_lake_settings.html.markdown | 6 +- .../r/lakeformation_permissions.html.markdown | 3 +- 9 files changed, 218 insertions(+), 250 deletions(-) diff --git a/aws/data_source_aws_lakeformation_data_lake_settings.go b/aws/data_source_aws_lakeformation_data_lake_settings.go index 01e46336a6ad..3bb0add521c1 100644 --- a/aws/data_source_aws_lakeformation_data_lake_settings.go +++ b/aws/data_source_aws_lakeformation_data_lake_settings.go @@ -54,7 +54,7 @@ func dataSourceAwsLakeFormationDataLakeSettings() *schema.Resource { }, }, }, - "data_lake_admins": { + "admins": { Type: schema.TypeList, Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, @@ -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 diff --git a/aws/data_source_aws_lakeformation_data_lake_settings_test.go b/aws/data_source_aws_lakeformation_data_lake_settings_test.go index 61597552d582..a37d389e64a5 100644 --- a/aws/data_source_aws_lakeformation_data_lake_settings_test.go +++ b/aws/data_source_aws_lakeformation_data_lake_settings_test.go @@ -34,8 +34,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"), ), }, }, @@ -47,7 +47,7 @@ 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] + admins = [data.aws_caller_identity.current.arn] } data "aws_lakeformation_data_lake_settings" "test" { diff --git a/aws/resource_aws_lakeformation_data_lake_settings.go b/aws/resource_aws_lakeformation_data_lake_settings.go index f03fc3721770..d41e5a1b148c 100644 --- a/aws/resource_aws_lakeformation_data_lake_settings.go +++ b/aws/resource_aws_lakeformation_data_lake_settings.go @@ -78,7 +78,7 @@ func resourceAwsLakeFormationDataLakeSettings() *schema.Resource { }, }, }, - "data_lake_admins": { + "admins": { Type: schema.TypeList, Computed: true, Optional: true, @@ -119,7 +119,7 @@ func resourceAwsLakeFormationDataLakeSettingsCreate(d *schema.ResourceData, meta settings.CreateTableDefaultPermissions = expandDataLakeSettingsCreateDefaultPermissions(v.([]interface{})) } - if v, ok := d.GetOk("data_lake_admins"); ok { + if v, ok := d.GetOk("admins"); ok { settings.DataLakeAdmins = expandDataLakeSettingsAdmins(v.([]interface{})) } @@ -172,7 +172,7 @@ func resourceAwsLakeFormationDataLakeSettingsRead(d *schema.ResourceData, meta i 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 diff --git a/aws/resource_aws_lakeformation_data_lake_settings_test.go b/aws/resource_aws_lakeformation_data_lake_settings_test.go index 6c2c358a5265..4592bc8587da 100644 --- a/aws/resource_aws_lakeformation_data_lake_settings_test.go +++ b/aws/resource_aws_lakeformation_data_lake_settings_test.go @@ -40,8 +40,8 @@ func testAccAWSLakeFormationDataLakeSettings_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationDataLakeSettingsExists(resourceName), 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"), ), }, }, @@ -81,8 +81,8 @@ func testAccAWSLakeFormationDataLakeSettings_withoutCatalogId(t *testing.T) { Config: testAccAWSLakeFormationDataLakeSettingsConfig_withoutCatalogId, Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationDataLakeSettingsExists(resourceName), - 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"), ), }, }, @@ -162,7 +162,7 @@ resource "aws_lakeformation_data_lake_settings" "test" { permissions = ["ALL"] } - data_lake_admins = [data.aws_caller_identity.current.arn] + admins = [data.aws_caller_identity.current.arn] trusted_resource_owners = [data.aws_caller_identity.current.account_id] } ` @@ -171,6 +171,6 @@ const testAccAWSLakeFormationDataLakeSettingsConfig_withoutCatalogId = ` data "aws_caller_identity" "current" {} resource "aws_lakeformation_data_lake_settings" "test" { - data_lake_admins = [data.aws_caller_identity.current.arn] + admins = [data.aws_caller_identity.current.arn] } ` diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index 748217d01cb4..3eefc5f28d67 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -121,6 +121,11 @@ func resourceAwsLakeFormationPermissions() *schema.Resource { Optional: true, Computed: true, }, + "wildcard": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, }, }, }, @@ -140,7 +145,6 @@ func resourceAwsLakeFormationPermissions() *schema.Resource { "column_names": { Type: schema.TypeList, Optional: true, - MinItems: 1, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.NoZeroValues, @@ -153,7 +157,6 @@ func resourceAwsLakeFormationPermissions() *schema.Resource { "excluded_column_names": { Type: schema.TypeList, Optional: true, - MinItems: 1, Elem: &schema.Schema{ Type: schema.TypeString, ValidateFunc: validation.NoZeroValues, @@ -485,7 +488,9 @@ func expandLakeFormationTableResource(tfMap map[string]interface{}) *lakeformati if v, ok := tfMap["name"].(string); ok && v != "" { apiObject.Name = aws.String(v) - } else { + } + + if v, ok := tfMap["wildcard"].(bool); ok && v { apiObject.TableWildcard = &lakeformation.TableWildcard{} } @@ -511,6 +516,10 @@ func flattenLakeFormationTableResource(apiObject *lakeformation.TableResource) m tfMap["name"] = aws.StringValue(v) } + if v := apiObject.TableWildcard; v != nil { + tfMap["wildcard"] = true + } + return tfMap } diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index 73eb6b2292a3..191013d0df0d 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -36,27 +36,6 @@ func TestAccAWSLakeFormationPermissions_basic(t *testing.T) { }) } -func TestAccAWSLakeFormationPermissions_disappears(t *testing.T) { - rName := acctest.RandomWithPrefix("tf-acc-test") - resourceName := "aws_lakeformation_permissions.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSLakeFormationPermissionsConfig_basic(rName), - Check: resource.ComposeTestCheckFunc( - testAccCheckAWSLakeFormationPermissionsExists(resourceName), - testAccCheckResourceDisappears(testAccProvider, resourceAwsLakeFormationPermissions(), resourceName), - ), - ExpectNonEmptyPlan: true, - }, - }, - }) -} - func TestAccAWSLakeFormationPermissions_dataLocation(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" @@ -84,47 +63,26 @@ func TestAccAWSLakeFormationPermissions_dataLocation(t *testing.T) { }) } -func TestAccAWSLakeFormationPermissions_full(t *testing.T) { - rName := acctest.RandomWithPrefix("lakeformation-test-bucket") - dName := acctest.RandomWithPrefix("lakeformation-test-db") - tName := acctest.RandomWithPrefix("lakeformation-test-table") - - roleName := "data.aws_iam_role.test" +func TestAccAWSLakeFormationPermissions_database(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" - bucketName := "aws_s3_bucket.test" + roleName := "aws_iam_role.test" dbName := "aws_glue_catalog_database.test" - tableName := "aws_glue_catalog_table.test" resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, Providers: testAccProviders, CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_catalog(), - Check: resource.ComposeTestCheckFunc( - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", "CREATE_DATABASE"), - ), - }, - { - Config: testAccAWSLakeFormationPermissionsConfig_location(rName), + Config: testAccAWSLakeFormationPermissionsConfig_database(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), - resource.TestCheckResourceAttrPair(bucketName, "arn", resourceName, "location"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", "DATA_LOCATION_ACCESS"), - ), - }, - { - Config: testAccAWSLakeFormationPermissionsConfig_database(rName, dName), - Check: resource.ComposeTestCheckFunc( - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), - resource.TestCheckResourceAttrPair(dbName, "name", resourceName, "database"), + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "database.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dbName, "name"), resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALTER"), resource.TestCheckResourceAttr(resourceName, "permissions.1", "CREATE_TABLE"), @@ -133,42 +91,59 @@ func TestAccAWSLakeFormationPermissions_full(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", "CREATE_TABLE"), ), }, + }, + }) +} + +func TestAccAWSLakeFormationPermissions_table(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + tableName := "aws_glue_catalog_table.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "\"ALL\""), + Config: testAccAWSLakeFormationPermissionsConfig_table(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), + testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), - resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), ), }, + }, + }) +} + +func TestAccAWSLakeFormationPermissions_tableWithColumns(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + tableName := "aws_glue_catalog_table.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_table(rName, dName, tName, "\"ALL\", \"SELECT\""), - Check: resource.ComposeTestCheckFunc( - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), - resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), - resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "2"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", "ALL"), - resource.TestCheckResourceAttr(resourceName, "permissions.1", "SELECT"), - ), - }, - { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, dName, tName), + Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName), Check: resource.ComposeTestCheckFunc( - testAccCheckResourceAttrAccountID(resourceName, "catalog_id"), - resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), - resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - resource.TestCheckResourceAttrPair(tableName, "database_name", resourceName, "table.0.database"), - resource.TestCheckResourceAttrPair(tableName, "name", resourceName, "table.0.name"), - resource.TestCheckResourceAttr(resourceName, "table.0.column_names.#", "2"), - resource.TestCheckResourceAttr(resourceName, "table.0.column_names.0", "event"), - resource.TestCheckResourceAttr(resourceName, "table.0.column_names.1", "timestamp"), + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.#", "2"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.1", "timestamp"), resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), resource.TestCheckResourceAttr(resourceName, "permissions.0", "SELECT"), ), @@ -221,14 +196,14 @@ func testAccCheckAWSLakeFormationPermissionsExists(resourceName string) resource }, } - if rs.Primary.Attributes["catalog_resource"] == "true" { + if v, ok := rs.Primary.Attributes["catalog_resource"]; ok && v != "" && v == "true" { input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeCatalog) input.Resource = &lakeformation.Resource{ Catalog: &lakeformation.CatalogResource{}, } } - if rs.Primary.Attributes["data_location.#"] != "0" { + if v, ok := rs.Primary.Attributes["data_location.#"]; ok && v != "" && v != "0" { input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeDataLocation) res := &lakeformation.DataLocationResource{ ResourceArn: aws.String(rs.Primary.Attributes["data_location.0.resource_arn"]), @@ -241,16 +216,54 @@ func testAccCheckAWSLakeFormationPermissionsExists(resourceName string) resource } } - if rs.Primary.Attributes["database.#"] != "0" { + if v, ok := rs.Primary.Attributes["database.#"]; ok && v != "" && v != "0" { input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeDatabase) + res := &lakeformation.DatabaseResource{ + Name: aws.String(rs.Primary.Attributes["database.0.name"]), + } + if rs.Primary.Attributes["database.0.catalog_id"] != "" { + res.CatalogId = aws.String(rs.Primary.Attributes["database.0.catalog_id"]) + } + input.Resource = &lakeformation.Resource{ + Database: res, + } } - if rs.Primary.Attributes["table.#"] != "0" { + if v, ok := rs.Primary.Attributes["table.#"]; ok && v != "" && v != "0" { input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeTable) + res := &lakeformation.TableResource{ + DatabaseName: aws.String(rs.Primary.Attributes["table.0.database_name"]), + } + if rs.Primary.Attributes["table.0.catalog_id"] != "" { + res.CatalogId = aws.String(rs.Primary.Attributes["table.0.catalog_id"]) + } + if rs.Primary.Attributes["table.0.name"] != "" { + res.Name = aws.String(rs.Primary.Attributes["table.0.name"]) + } + if rs.Primary.Attributes["table.0.wildcard"] == "true" { + res.TableWildcard = &lakeformation.TableWildcard{} + } + input.Resource = &lakeformation.Resource{ + Table: res, + } } - if rs.Primary.Attributes["table_with_columns.#"] != "0" { + // ListPermissions does not support getting privileges on a table with columns. + // Instead, call this operation on the table, and the operation returns the + // table and the table w columns. + // https://docs.aws.amazon.com/sdk-for-go/api/service/lakeformation/#ListPermissionsInput + if v, ok := rs.Primary.Attributes["table_with_columns.#"]; ok && v != "" && v != "0" { input.ResourceType = aws.String(lakeformation.DataLakeResourceTypeTable) + res := &lakeformation.TableResource{ + DatabaseName: aws.String(rs.Primary.Attributes["table_with_columns.0.database_name"]), + Name: aws.String(rs.Primary.Attributes["table_with_columns.0.name"]), + } + if rs.Primary.Attributes["table.0.catalog_id"] != "" { + res.CatalogId = aws.String(rs.Primary.Attributes["table.0.catalog_id"]) + } + input.Resource = &lakeformation.Resource{ + Table: res, + } } err := resource.Retry(2*time.Minute, func() *resource.RetryError { @@ -320,7 +333,7 @@ EOF data "aws_caller_identity" "current" {} resource "aws_lakeformation_data_lake_settings" "test" { - data_lake_admins = [data.aws_caller_identity.current.arn] + admins = [data.aws_caller_identity.current.arn] } resource "aws_lakeformation_permissions" "test" { @@ -356,48 +369,6 @@ resource "aws_iam_role" "test" { EOF } -resource "aws_iam_role_policy" "test" { - name = %[1]q - role = aws_iam_role.test.id - - policy = < Date: Thu, 17 Dec 2020 19:38:15 -0500 Subject: [PATCH 26/31] resource/lakeformation: Pre-merge cleanup --- ...ce_aws_lakeformation_data_lake_settings.go | 51 ++++++++++++++----- ...s_lakeformation_data_lake_settings_test.go | 2 +- ...formation_data_lake_settings.html.markdown | 2 +- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/aws/resource_aws_lakeformation_data_lake_settings.go b/aws/resource_aws_lakeformation_data_lake_settings.go index d41e5a1b148c..7b1644f3b3ec 100644 --- a/aws/resource_aws_lakeformation_data_lake_settings.go +++ b/aws/resource_aws_lakeformation_data_lake_settings.go @@ -3,10 +3,12 @@ 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" @@ -23,6 +25,15 @@ func resourceAwsLakeFormationDataLakeSettings() *schema.Resource { }, Schema: map[string]*schema.Schema{ + "admins": { + Type: schema.TypeList, + Computed: true, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validateArn, + }, + }, "catalog_id": { Type: schema.TypeString, ForceNew: true, @@ -78,15 +89,6 @@ func resourceAwsLakeFormationDataLakeSettings() *schema.Resource { }, }, }, - "admins": { - Type: schema.TypeList, - Computed: true, - Optional: true, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validateArn, - }, - }, "trusted_resource_owners": { Type: schema.TypeList, Computed: true, @@ -128,7 +130,30 @@ func resourceAwsLakeFormationDataLakeSettingsCreate(d *schema.ResourceData, meta } input.DataLakeSettings = settings - output, err := conn.PutDataLakeSettings(input) + + var output *lakeformation.PutDataLakeSettingsOutput + err := resource.Retry(2*time.Minute, func() *resource.RetryError { + var err error + output, err = conn.PutDataLakeSettings(input) + if err != nil { + if isAWSErr(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeConcurrentModificationException, "") { + return resource.RetryableError(err) + } + if isAWSErr(err, lakeformation.ErrCodeOperationTimeoutException, "") { + return resource.RetryableError(err) + } + + return resource.NonRetryableError(fmt.Errorf("error creating Lake Formation data lake settings: %w", err)) + } + return nil + }) + + if isResourceTimeoutError(err) { + output, err = conn.PutDataLakeSettings(input) + } if err != nil { return fmt.Errorf("error creating Lake Formation data lake settings: %w", err) @@ -161,11 +186,11 @@ func resourceAwsLakeFormationDataLakeSettingsRead(d *schema.ResourceData, meta i } if err != nil { - return fmt.Errorf("error reading Lake Formation data lake settings (%s): %w", d.Id(), err) + return fmt.Errorf("reading Lake Formation data lake settings (%s): %w", d.Id(), err) } if output == nil || output.DataLakeSettings == nil { - return fmt.Errorf("error reading Lake Formation data lake settings (%s): empty response", d.Id()) + return fmt.Errorf("reading Lake Formation data lake settings (%s): empty response", d.Id()) } settings := output.DataLakeSettings @@ -202,7 +227,7 @@ func resourceAwsLakeFormationDataLakeSettingsDelete(d *schema.ResourceData, meta } if err != nil { - return fmt.Errorf("error deleting Lake Formation data lake settings (%s): %w", d.Id(), err) + return fmt.Errorf("deleting Lake Formation data lake settings (%s): %w", d.Id(), err) } return nil diff --git a/aws/resource_aws_lakeformation_data_lake_settings_test.go b/aws/resource_aws_lakeformation_data_lake_settings_test.go index 4592bc8587da..93c9e729c6be 100644 --- a/aws/resource_aws_lakeformation_data_lake_settings_test.go +++ b/aws/resource_aws_lakeformation_data_lake_settings_test.go @@ -162,7 +162,7 @@ resource "aws_lakeformation_data_lake_settings" "test" { permissions = ["ALL"] } - admins = [data.aws_caller_identity.current.arn] + admins = [data.aws_caller_identity.current.arn] trusted_resource_owners = [data.aws_caller_identity.current.account_id] } ` diff --git a/website/docs/r/lakeformation_data_lake_settings.html.markdown b/website/docs/r/lakeformation_data_lake_settings.html.markdown index be99f56def84..4ac3a30d80b6 100644 --- a/website/docs/r/lakeformation_data_lake_settings.html.markdown +++ b/website/docs/r/lakeformation_data_lake_settings.html.markdown @@ -44,10 +44,10 @@ resource "aws_lakeformation_data_lake_settings" "example" { The following arguments are optional: +* `admins` – (Optional) List of ARNs of AWS Lake Formation principals (IAM users or roles). * `catalog_id` – (Optional) Identifier for the Data Catalog. By default, the account ID. * `create_database_default_permissions` - (Optional) Up to three configuration blocks of principal permissions for default create database permissions. Detailed below. * `create_table_default_permissions` - (Optional) Up to three configuration blocks of principal permissions for default create table permissions. Detailed below. -* `admins` – (Optional) List of ARNs of AWS Lake Formation principals (IAM users or roles). * `trusted_resource_owners` – (Optional) List of the resource-owning account IDs that the caller's account can use to share their user access details (user ARNs). ### create_database_default_permissions From 641fbd469a62beae6867d1c1aa66905363462b50 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Thu, 17 Dec 2020 19:46:08 -0500 Subject: [PATCH 27/31] resource/lakeformation: Fix linting --- ...ce_aws_lakeformation_data_lake_settings_test.go | 4 ++-- aws/resource_aws_lakeformation_permissions_test.go | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/aws/data_source_aws_lakeformation_data_lake_settings_test.go b/aws/data_source_aws_lakeformation_data_lake_settings_test.go index a37d389e64a5..a28ec1bdb8d8 100644 --- a/aws/data_source_aws_lakeformation_data_lake_settings_test.go +++ b/aws/data_source_aws_lakeformation_data_lake_settings_test.go @@ -46,8 +46,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 - 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" { diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index 191013d0df0d..d9b5a8748df2 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -388,7 +388,7 @@ resource "aws_lakeformation_data_lake_settings" "test" { resource "aws_lakeformation_permissions" "test" { principal = aws_iam_role.test.arn permissions = ["DATA_LOCATION_ACCESS"] - + data_location { resource_arn = aws_s3_bucket.test.arn } @@ -439,7 +439,7 @@ resource "aws_lakeformation_permissions" "test" { permissions = ["ALTER", "CREATE_TABLE", "DROP"] permissions_with_grant_option = ["CREATE_TABLE"] principal = aws_iam_role.test.arn - + database { name = aws_glue_catalog_database.test.name } @@ -498,8 +498,8 @@ resource "aws_lakeformation_permissions" "test" { principal = aws_iam_role.test.arn table { - database_name = aws_glue_catalog_table.test.database_name - name = aws_glue_catalog_table.test.name + database_name = aws_glue_catalog_table.test.database_name + name = aws_glue_catalog_table.test.name } } `, rName) @@ -565,9 +565,9 @@ resource "aws_lakeformation_permissions" "test" { principal = aws_iam_role.test.arn table_with_columns { - database_name = aws_glue_catalog_table.test.database_name - name = aws_glue_catalog_table.test.name - column_names = ["event", "timestamp"] + database_name = aws_glue_catalog_table.test.database_name + name = aws_glue_catalog_table.test.name + column_names = ["event", "timestamp"] } depends_on = ["aws_lakeformation_data_lake_settings.test"] From e170ade99836bb1166776e9cdb212d4d6ae8224a Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 18 Dec 2020 12:59:27 -0500 Subject: [PATCH 28/31] ds/lakeformation_permissions: New data source --- ...ce_aws_lakeformation_data_lake_settings.go | 10 +- ...s_lakeformation_data_lake_settings_test.go | 14 - ...ta_source_aws_lakeformation_permissions.go | 246 ++++++++++ ...urce_aws_lakeformation_permissions_test.go | 445 ++++++++++++++++++ aws/data_source_aws_lakeformation_resource.go | 16 +- ..._source_aws_lakeformation_resource_test.go | 8 +- aws/provider.go | 1 + ...s_lakeformation_data_lake_settings_test.go | 15 - aws/resource_aws_lakeformation_permissions.go | 14 +- ...urce_aws_lakeformation_permissions_test.go | 28 +- aws/resource_aws_lakeformation_resource.go | 18 +- ...esource_aws_lakeformation_resource_test.go | 20 +- aws/resource_aws_lakeformation_test.go | 40 ++ ...formation_data_lake_settings.html.markdown | 6 +- .../d/lakeformation_permissions.html.markdown | 110 +++++ .../d/lakeformation_resource.html.markdown | 4 +- ...formation_data_lake_settings.html.markdown | 6 +- .../r/lakeformation_permissions.html.markdown | 31 +- .../r/lakeformation_resource.html.markdown | 4 +- 19 files changed, 929 insertions(+), 107 deletions(-) create mode 100644 aws/data_source_aws_lakeformation_permissions.go create mode 100644 aws/data_source_aws_lakeformation_permissions_test.go create mode 100644 aws/resource_aws_lakeformation_test.go create mode 100644 website/docs/d/lakeformation_permissions.html.markdown diff --git a/aws/data_source_aws_lakeformation_data_lake_settings.go b/aws/data_source_aws_lakeformation_data_lake_settings.go index 3bb0add521c1..27244797a9cd 100644 --- a/aws/data_source_aws_lakeformation_data_lake_settings.go +++ b/aws/data_source_aws_lakeformation_data_lake_settings.go @@ -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, @@ -54,11 +59,6 @@ func dataSourceAwsLakeFormationDataLakeSettings() *schema.Resource { }, }, }, - "admins": { - Type: schema.TypeList, - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, "trusted_resource_owners": { Type: schema.TypeList, Computed: true, diff --git a/aws/data_source_aws_lakeformation_data_lake_settings_test.go b/aws/data_source_aws_lakeformation_data_lake_settings_test.go index a28ec1bdb8d8..e0ae83a99b49 100644 --- a/aws/data_source_aws_lakeformation_data_lake_settings_test.go +++ b/aws/data_source_aws_lakeformation_data_lake_settings_test.go @@ -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" diff --git a/aws/data_source_aws_lakeformation_permissions.go b/aws/data_source_aws_lakeformation_permissions.go new file mode 100644 index 000000000000..cdbbcab7d845 --- /dev/null +++ b/aws/data_source_aws_lakeformation_permissions.go @@ -0,0 +1,246 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/lakeformation" + "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 { + var err error + 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 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 +} diff --git a/aws/data_source_aws_lakeformation_permissions_test.go b/aws/data_source_aws_lakeformation_permissions_test.go new file mode 100644 index 000000000000..4b59b28c8c89 --- /dev/null +++ b/aws/data_source_aws_lakeformation_permissions_test.go @@ -0,0 +1,445 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/service/lakeformation" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func testAccAWSLakeFormationPermissionsDataSource_basic(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_basic(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + resource.TestCheckResourceAttrPair(resourceName, "catalog_resource", dataSourceName, "catalog_resource"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSource_dataLocation(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_dataLocation(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + resource.TestCheckResourceAttrPair(resourceName, "data_location.#", dataSourceName, "data_location.#"), + resource.TestCheckResourceAttrPair(resourceName, "data_location.0.arn", dataSourceName, "data_location.0.arn"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSource_database(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_database(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "database.#", dataSourceName, "database.#"), + resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dataSourceName, "database.0.name"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.1", dataSourceName, "permissions.1"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.2", dataSourceName, "permissions.2"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.#", dataSourceName, "permissions_with_grant_option.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions_with_grant_option.0", dataSourceName, "permissions_with_grant_option.0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSource_table(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_table(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "table.#", dataSourceName, "table.#"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", dataSourceName, "table.0.database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", dataSourceName, "table.0.name"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSource_tableWithColumns(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dataSourceName := "data.aws_lakeformation_permissions.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsDataSourceConfig_tableWithColumns(rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrPair(resourceName, "principal", dataSourceName, "principal"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.#", dataSourceName, "table_with_columns.#"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.database_name", dataSourceName, "table_with_columns.0.database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.name", dataSourceName, "table_with_columns.0.name"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.column_names.#", dataSourceName, "table_with_columns.0.column_names.#"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.column_names.0", dataSourceName, "table_with_columns.0.column_names.0"), + resource.TestCheckResourceAttrPair(resourceName, "table_with_columns.0.column_names.1", dataSourceName, "table_with_columns.0.column_names.1"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.#", dataSourceName, "permissions.#"), + resource.TestCheckResourceAttrPair(resourceName, "permissions.0", dataSourceName, "permissions.0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissionsDataSourceConfig_basic(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = < **NOTE:** This data source deals with explicitly granted permissions. Lake Formation grants implicit permissions to data lake administrators, database creators, and table creators. For more information, see [Implicit Lake Formation Permissions](https://docs.aws.amazon.com/lake-formation/latest/dg/implicit-permissions.html). + +## Example Usage + +### Permissions For A Lake Formation S3 Resource + +```hcl +data "aws_lakeformation_permissions" "test" { + principal_arn = aws_iam_role.workflow_role.arn + + data_location { + arn = aws_lakeformation_resource.test.arn + } +} +``` + +### Permissions For A Glue Catalog Database + +```hcl +data "aws_lakeformation_permissions" "test" { + role = aws_iam_role.workflow_role.arn + + database { + name = aws_glue_catalog_database.test.name + catalog_id = "110376042874" + } +} +``` + +## Argument Reference + +The following arguments are required: + +* `principal` – (Required) Principal to be granted the permissions on the resource. Supported principals are IAM users or IAM roles. + +One of the following is required: + +* `catalog_resource` - Whether the permissions are to be granted for the Data Catalog. Defaults to `false`. +* `data_location` - Configuration block for a data location resource. Detailed below. +* `database` - Configuration block for a database resource. Detailed below. +* `table` - Configuration block for a table resource. Detailed below. +* `table_with_columns` - Configuration block for a table with columns resource. Detailed below. + +The following arguments are optional: + +* `catalog_id` – (Optional) Identifier for the Data Catalog. By default, the account ID. The Data Catalog is the persistent metadata store. It contains database definitions, table definitions, and other control information to manage your Lake Formation environment. + +### data_location + +The following argument is required: + +* `arn` – (Required) Amazon Resource Name (ARN) that uniquely identifies the data location resource. + +The following argument is optional: + +* `catalog_id` - (Optional) Identifier for the Data Catalog where the location is registered with Lake Formation. By default, it is the account ID of the caller. + +### database + +The following argument is required: + +* `name` – (Required) Name of the database resource. Unique to the Data Catalog. + +The following argument is optional: + +* `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. + +### table + +The following argument is required: + +* `database_name` – (Required) Name of the database for the table. Unique to a Data Catalog. + +The following arguments are optional: + +* `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. +* `name` - (Optional) Name of the table. At least one of `name` or `wildcard` is required. +* `wildcard` - (Optional) Whether to use a wildcard representing every table under a database. At least one of `name` or `wildcard` is required. Defaults to `false`. + +### table_with_columns + +The following arguments are required: + +* `database_name` – (Required) Name of the database for the table with columns resource. Unique to the Data Catalog. +* `name` – (Required) Name of the table resource. + +The following arguments are optional: + +* `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. +* `column_names` - (Optional) List of column names for the table. At least one of `column_names` or `excluded_column_names` is required. +* `excluded_column_names` - (Optional) List of column names for the table to exclude. At least one of `column_names` or `excluded_column_names` is required. + +## Attributes Reference + +In addition to the above arguments, the following attribute is exported: + +* `permissions` – List of permissions granted to the principal. For details on permissions, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). +* `permissions_with_grant_option` - Subset of `permissions` which the principal can pass. diff --git a/website/docs/d/lakeformation_resource.html.markdown b/website/docs/d/lakeformation_resource.html.markdown index fa33546eb179..70285dc50191 100644 --- a/website/docs/d/lakeformation_resource.html.markdown +++ b/website/docs/d/lakeformation_resource.html.markdown @@ -14,13 +14,13 @@ Provides details about a Lake Formation resource. ```hcl data "aws_lakeformation_resource" "example" { - resource_arn = "arn:aws:s3:::tf-acc-test-9151654063908211878" + arn = "arn:aws:s3:::tf-acc-test-9151654063908211878" } ``` ## Argument Reference -* `resource_arn` – (Required) Amazon Resource Name (ARN) of the resource, an S3 path. +* `arn` – (Required) Amazon Resource Name (ARN) of the resource, an S3 path. ## Attributes Reference diff --git a/website/docs/r/lakeformation_data_lake_settings.html.markdown b/website/docs/r/lakeformation_data_lake_settings.html.markdown index 4ac3a30d80b6..509e78aa29d9 100644 --- a/website/docs/r/lakeformation_data_lake_settings.html.markdown +++ b/website/docs/r/lakeformation_data_lake_settings.html.markdown @@ -50,18 +50,20 @@ The following arguments are optional: * `create_table_default_permissions` - (Optional) Up to three configuration blocks of principal permissions for default create table permissions. Detailed below. * `trusted_resource_owners` – (Optional) List of the resource-owning account IDs that the caller's account can use to share their user access details (user ARNs). +~> **NOTE:** Although optional, not including `admins`, `create_database_default_permissions`, `create_table_default_permissions`, and/or `trusted_resource_owners` results in the setting being cleared. + ### create_database_default_permissions The following arguments are optional: -* `permissions` - (Optional) List of permissions that are granted to the principal. Valid values include `ALL`, `SELECT`, `ALTER`, `DROP`, `DELETE`, `INSERT`, `DESCRIBE`, `CREATE_DATABASE`, `CREATE_TABLE`, and `DATA_LOCATION_ACCESS`. +* `permissions` - (Optional) List of permissions that are granted to the principal. Valid values may include `ALL`, `SELECT`, `ALTER`, `DROP`, `DELETE`, `INSERT`, `DESCRIBE`, and `CREATE_TABLE`. For more details, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). * `principal` - (Optional) Principal who is granted permissions. To enforce metadata and underlying data access control only by IAM on new databases and tables set `principal` to `IAM_ALLOWED_PRINCIPALS` and `permissions` to `["ALL"]`. ### create_table_default_permissions The following arguments are optional: -* `permissions` - (Optional) List of permissions that are granted to the principal. Valid values include `ALL`, `SELECT`, `ALTER`, `DROP`, `DELETE`, `INSERT`, `DESCRIBE`, `CREATE_DATABASE`, `CREATE_TABLE`, and `DATA_LOCATION_ACCESS`. +* `permissions` - (Optional) List of permissions that are granted to the principal. Valid values may include `ALL`, `SELECT`, `ALTER`, `DROP`, `DELETE`, `INSERT`, and `DESCRIBE`. For more details, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). * `principal` - (Optional) Principal who is granted permissions. To enforce metadata and underlying data access control only by IAM on new databases and tables set `principal` to `IAM_ALLOWED_PRINCIPALS` and `permissions` to `["ALL"]`. ## Attributes Reference diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index cd7d2ef5fb1e..05acb29d66a4 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -22,7 +22,7 @@ resource "aws_lakeformation_permissions" "test" { permissions = ["ALL"] data_location { - resource_arn = aws_lakeformation_resource.test.resource_arn + arn = aws_lakeformation_resource.test.arn } } ``` @@ -45,12 +45,8 @@ resource "aws_lakeformation_permissions" "test" { The following arguments are required: -* `permissions` – (Required) List of permissions granted to the principal. Valid values include `ALL`, `ALTER`, `CREATE_DATABASE`, `CREATE_TABLE`, `DATA_LOCATION_ACCESS`, `DELETE`, `DESCRIBE`, `DROP`, `INSERT`, and `SELECT`. For details on each permission, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). -* `principal_arn` – (Required) Principal to be granted the permissions on the resource. Supported principals are IAM users or IAM roles. - -The following arguments are optional: - -* `catalog_id` – (Optional) Identifier for the Data Catalog. By default, the account ID. The Data Catalog is the persistent metadata store. It contains database definitions, table definitions, and other control information to manage your Lake Formation environment. +* `permissions` – (Required) List of permissions granted to the principal. Valid values may include `ALL`, `ALTER`, `CREATE_DATABASE`, `CREATE_TABLE`, `DATA_LOCATION_ACCESS`, `DELETE`, `DESCRIBE`, `DROP`, `INSERT`, and `SELECT`. For details on each permission, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). +* `principal` – (Required) Principal to be granted the permissions on the resource. Supported principals include IAM users and IAM roles. One of the following is required: @@ -60,11 +56,16 @@ One of the following is required: * `table` - (Optional) Configuration block for a table resource. Detailed below. * `table_with_columns` - (Optional) Configuration block for a table with columns resource. Detailed below. +The following arguments are optional: + +* `catalog_id` – (Optional) Identifier for the Data Catalog. By default, the account ID. The Data Catalog is the persistent metadata store. It contains database definitions, table definitions, and other control information to manage your Lake Formation environment. +* `permissions_with_grant_option` - (Optional) Subset of `permissions` which the principal can pass. + ### data_location The following argument is required: -* `resource_arn` – (Required) Amazon Resource Name (ARN) that uniquely identifies the data location resource. +* `arn` – (Required) Amazon Resource Name (ARN) that uniquely identifies the data location resource. The following argument is optional: @@ -86,11 +87,14 @@ The following argument is required: * `database_name` – (Required) Name of the database for the table. Unique to a Data Catalog. +At least one of the following is required: + +* `name` - (Optional) Name of the table. +* `wildcard` - (Optional) Whether to use a wildcard representing every table under a database. Defaults to `false`. + The following arguments are optional: * `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. -* `name` - (Optional) Name of the table. At least one of `name` or `wildcard` is required. -* `wildcard` - (Optional) Whether to use a wildcard representing every table under a database. At least one of `name` or `wildcard` is required. Defaults to `false`. ### table_with_columns @@ -99,11 +103,14 @@ The following arguments are required: * `database_name` – (Required) Name of the database for the table with columns resource. Unique to the Data Catalog. * `name` – (Required) Name of the table resource. +At least one of the following is required: + +* `column_names` - (Optional) List of column names for the table. +* `excluded_column_names` - (Optional) List of column names for the table to exclude. + The following arguments are optional: * `catalog_id` - (Optional) Identifier for the Data Catalog. By default, it is the account ID of the caller. -* `column_names` - (Optional) List of column names for the table. At least one of `column_names` or `excluded_column_names` is required. -* `excluded_column_names` - (Optional) List of column names for the table to exclude. At least one of `column_names` or `excluded_column_names` is required. ## Attributes Reference diff --git a/website/docs/r/lakeformation_resource.html.markdown b/website/docs/r/lakeformation_resource.html.markdown index 22894ea64d24..27eb12fc6dcc 100644 --- a/website/docs/r/lakeformation_resource.html.markdown +++ b/website/docs/r/lakeformation_resource.html.markdown @@ -20,13 +20,13 @@ data "aws_s3_bucket" "example" { } resource "aws_lakeformation_resource" "example" { - resource_arn = data.aws_s3_bucket.example.arn + arn = data.aws_s3_bucket.example.arn } ``` ## Argument Reference -* `resource_arn` – (Required) Amazon Resource Name (ARN) of the resource, an S3 path. +* `arn` – (Required) Amazon Resource Name (ARN) of the resource, an S3 path. * `role_arn` – (Optional) Role that has read/write access to the resource. If not provided, the Lake Formation service-linked role must exist and is used. ~> **NOTE:** AWS does not support registering an S3 location with an IAM role and subsequently updating the S3 location registration to a service-linked role. From 70e0cf18dced0b32f1d100caec0ca5cfdb201d36 Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 18 Dec 2020 13:22:13 -0500 Subject: [PATCH 29/31] resource/lakeformation: Fix linter issues --- aws/data_source_aws_lakeformation_permissions.go | 8 ++++++++ aws/resource_aws_lakeformation_permissions.go | 8 ++++++++ website/docs/d/lakeformation_permissions.html.markdown | 4 ++-- .../docs/r/lakeformation_data_lake_settings.html.markdown | 2 +- website/docs/r/lakeformation_permissions.html.markdown | 4 ++-- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/aws/data_source_aws_lakeformation_permissions.go b/aws/data_source_aws_lakeformation_permissions.go index cdbbcab7d845..9851ac0033d0 100644 --- a/aws/data_source_aws_lakeformation_permissions.go +++ b/aws/data_source_aws_lakeformation_permissions.go @@ -7,6 +7,7 @@ import ( "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" @@ -192,6 +193,7 @@ func dataSourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta inte } return !lastPage }) + if err != nil { if isAWSErr(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { return resource.RetryableError(err) @@ -201,6 +203,12 @@ func dataSourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta inte return nil }) + 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) } diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index 72c446df8fb1..48c37f9b70b1 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -7,6 +7,7 @@ import ( "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" @@ -269,6 +270,7 @@ func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interf } return !lastPage }) + if err != nil { if isAWSErr(err, lakeformation.ErrCodeInvalidInputException, "Invalid principal") { return resource.RetryableError(err) @@ -278,6 +280,12 @@ func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interf return nil }) + 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) } diff --git a/website/docs/d/lakeformation_permissions.html.markdown b/website/docs/d/lakeformation_permissions.html.markdown index 6e84589f9568..69872f971a26 100644 --- a/website/docs/d/lakeformation_permissions.html.markdown +++ b/website/docs/d/lakeformation_permissions.html.markdown @@ -18,7 +18,7 @@ Get permissions for a principal to access metadata in the Data Catalog and data ```hcl data "aws_lakeformation_permissions" "test" { - principal_arn = aws_iam_role.workflow_role.arn + principal = aws_iam_role.workflow_role.arn data_location { arn = aws_lakeformation_resource.test.arn @@ -30,7 +30,7 @@ data "aws_lakeformation_permissions" "test" { ```hcl data "aws_lakeformation_permissions" "test" { - role = aws_iam_role.workflow_role.arn + principal = aws_iam_role.workflow_role.arn database { name = aws_glue_catalog_database.test.name diff --git a/website/docs/r/lakeformation_data_lake_settings.html.markdown b/website/docs/r/lakeformation_data_lake_settings.html.markdown index 509e78aa29d9..0b4123bc10ec 100644 --- a/website/docs/r/lakeformation_data_lake_settings.html.markdown +++ b/website/docs/r/lakeformation_data_lake_settings.html.markdown @@ -50,7 +50,7 @@ The following arguments are optional: * `create_table_default_permissions` - (Optional) Up to three configuration blocks of principal permissions for default create table permissions. Detailed below. * `trusted_resource_owners` – (Optional) List of the resource-owning account IDs that the caller's account can use to share their user access details (user ARNs). -~> **NOTE:** Although optional, not including `admins`, `create_database_default_permissions`, `create_table_default_permissions`, and/or `trusted_resource_owners` results in the setting being cleared. +~> **NOTE:** Although optional, not including `admins`, `create_database_default_permissions`, `create_table_default_permissions`, and/or `trusted_resource_owners` results in the setting being cleared. ### create_database_default_permissions diff --git a/website/docs/r/lakeformation_permissions.html.markdown b/website/docs/r/lakeformation_permissions.html.markdown index 05acb29d66a4..610761119f53 100644 --- a/website/docs/r/lakeformation_permissions.html.markdown +++ b/website/docs/r/lakeformation_permissions.html.markdown @@ -18,8 +18,8 @@ Grants permissions to the principal to access metadata in the Data Catalog and d ```hcl resource "aws_lakeformation_permissions" "test" { - principal_arn = aws_iam_role.workflow_role.arn - permissions = ["ALL"] + principal = aws_iam_role.workflow_role.arn + permissions = ["ALL"] data_location { arn = aws_lakeformation_resource.test.arn From 3ef409de3b22d2d7ede465c908594a6d666324ad Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 18 Dec 2020 13:36:45 -0500 Subject: [PATCH 30/31] resource/lakeformation: Fix semgrep issues --- ...ta_source_aws_lakeformation_permissions.go | 16 ++++++++-- aws/data_source_aws_lakeformation_resource.go | 8 +++++ ...ce_aws_lakeformation_data_lake_settings.go | 3 -- aws/resource_aws_lakeformation_permissions.go | 32 +++++++++++++------ ...urce_aws_lakeformation_permissions_test.go | 3 -- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/aws/data_source_aws_lakeformation_permissions.go b/aws/data_source_aws_lakeformation_permissions.go index 9851ac0033d0..7773b8a552e7 100644 --- a/aws/data_source_aws_lakeformation_permissions.go +++ b/aws/data_source_aws_lakeformation_permissions.go @@ -182,8 +182,7 @@ func dataSourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta inte var principalResourcePermissions []*lakeformation.PrincipalResourcePermissions err := resource.Retry(2*time.Minute, func() *resource.RetryError { - var err error - err = conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { + err := conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { for _, permission := range resp.PrincipalResourcePermissions { if permission == nil { continue @@ -203,6 +202,19 @@ func dataSourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta inte 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("") diff --git a/aws/data_source_aws_lakeformation_resource.go b/aws/data_source_aws_lakeformation_resource.go index 0e39552789ef..ebb9ea88bd71 100644 --- a/aws/data_source_aws_lakeformation_resource.go +++ b/aws/data_source_aws_lakeformation_resource.go @@ -2,10 +2,12 @@ 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/schema" ) @@ -42,6 +44,12 @@ func dataSourceAwsLakeFormationResourceRead(d *schema.ResourceData, meta interfa output, err := conn.DescribeResource(input) + if !d.IsNewResource() && tfawserr.ErrCodeEquals(err, lakeformation.ErrCodeEntityNotFoundException) { + log.Printf("[WARN] Resource Lake Formation Resource (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + if err != nil { return fmt.Errorf("error reading data source, Lake Formation Resource (arn: %s): %w", aws.StringValue(input.ResourceArn), err) } diff --git a/aws/resource_aws_lakeformation_data_lake_settings.go b/aws/resource_aws_lakeformation_data_lake_settings.go index 7b1644f3b3ec..8e3c18d01f67 100644 --- a/aws/resource_aws_lakeformation_data_lake_settings.go +++ b/aws/resource_aws_lakeformation_data_lake_settings.go @@ -142,9 +142,6 @@ func resourceAwsLakeFormationDataLakeSettingsCreate(d *schema.ResourceData, meta if isAWSErr(err, lakeformation.ErrCodeConcurrentModificationException, "") { return resource.RetryableError(err) } - if isAWSErr(err, lakeformation.ErrCodeOperationTimeoutException, "") { - return resource.RetryableError(err) - } return resource.NonRetryableError(fmt.Errorf("error creating Lake Formation data lake settings: %w", err)) } diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index 48c37f9b70b1..151b83f9ef9b 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -211,9 +211,6 @@ func resourceAwsLakeFormationPermissionsCreate(d *schema.ResourceData, meta inte if isAWSErr(err, lakeformation.ErrCodeConcurrentModificationException, "") { return resource.RetryableError(err) } - if isAWSErr(err, lakeformation.ErrCodeOperationTimeoutException, "") { - return resource.RetryableError(err) - } if isAWSErr(err, "AccessDeniedException", "is not authorized to access requested permissions") { return resource.RetryableError(err) } @@ -259,8 +256,7 @@ func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interf var principalResourcePermissions []*lakeformation.PrincipalResourcePermissions err := resource.Retry(2*time.Minute, func() *resource.RetryError { - var err error - err = conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { + err := conn.ListPermissionsPages(input, func(resp *lakeformation.ListPermissionsOutput, lastPage bool) bool { for _, permission := range resp.PrincipalResourcePermissions { if permission == nil { continue @@ -280,6 +276,19 @@ func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interf 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("") @@ -359,15 +368,16 @@ func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta inte if isAWSErr(err, lakeformation.ErrCodeConcurrentModificationException, "") { return resource.RetryableError(err) } - if isAWSErr(err, lakeformation.ErrCodeOperationTimeoutException, "") { - return resource.RetryableError(err) - } return resource.NonRetryableError(fmt.Errorf("unable to revoke Lake Formation Permissions: %w", err)) } return nil }) + if isResourceTimeoutError(err) { + _, err = conn.RevokePermissions(input) + } + if err != nil { return fmt.Errorf("unable to revoke LakeFormation Permissions (input: %v): %w", input, err) } @@ -378,8 +388,10 @@ func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta inte func expandLakeFormationResource(d *schema.ResourceData, squashTableWithColumns bool) *lakeformation.Resource { res := &lakeformation.Resource{} - if v, ok := d.GetOk("catalog_resource"); ok && v.(bool) { - res.Catalog = &lakeformation.CatalogResource{} + if v, ok := d.GetOk("catalog_resource"); ok { + if v.(bool) { + res.Catalog = &lakeformation.CatalogResource{} + } } if v, ok := d.GetOk("data_location"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index bfaaeaee4755..2f96730804d5 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -282,9 +282,6 @@ func testAccCheckAWSLakeFormationPermissionsExists(resourceName string) resource if isAWSErr(err, lakeformation.ErrCodeConcurrentModificationException, "") { return resource.RetryableError(err) } - if isAWSErr(err, lakeformation.ErrCodeOperationTimeoutException, "") { - return resource.RetryableError(err) - } if isAWSErr(err, "AccessDeniedException", "is not authorized to access requested permissions") { return resource.RetryableError(err) } From 7ca07395ccdda788cf1d2f069bf558ca24bdc0ca Mon Sep 17 00:00:00 2001 From: Dirk Avery Date: Fri, 18 Dec 2020 14:57:55 -0500 Subject: [PATCH 31/31] Update CHANGELOG with Lake Formation --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0354baa6db98..5a289ec50244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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