diff --git a/.changelog/20108.txt b/.changelog/20108.txt new file mode 100644 index 000000000000..5edc7273ce89 --- /dev/null +++ b/.changelog/20108.txt @@ -0,0 +1,7 @@ +```release-note:bug +resource/aws_lakeformation_permissions: Fix various problems with permissions including select-only +``` + +```release-note:bug +data-source/aws_lakeformation_permissions: Fix various problems with permissions including select-only +``` \ No newline at end of file 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 fa1fe4ab7d4a..09b424fe346a 100644 --- a/aws/data_source_aws_lakeformation_data_lake_settings_test.go +++ b/aws/data_source_aws_lakeformation_data_lake_settings_test.go @@ -8,7 +8,6 @@ import ( ) func testAccAWSLakeFormationDataLakeSettingsDataSource_basic(t *testing.T) { - callerIdentityName := "data.aws_caller_identity.current" resourceName := "data.aws_lakeformation_data_lake_settings.test" resource.Test(t, resource.TestCase{ @@ -20,9 +19,9 @@ func testAccAWSLakeFormationDataLakeSettingsDataSource_basic(t *testing.T) { { Config: testAccAWSLakeFormationDataLakeSettingsDataSourceConfig_basic, Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrPair(resourceName, "catalog_id", callerIdentityName, "account_id"), + resource.TestCheckResourceAttrPair(resourceName, "catalog_id", "data.aws_caller_identity.current", "account_id"), resource.TestCheckResourceAttr(resourceName, "admins.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "admins.0", callerIdentityName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "admins.0", "data.aws_iam_session_context.current", "issuer_arn"), ), }, }, @@ -32,9 +31,13 @@ func testAccAWSLakeFormationDataLakeSettingsDataSource_basic(t *testing.T) { const testAccAWSLakeFormationDataLakeSettingsDataSourceConfig_basic = ` data "aws_caller_identity" "current" {} +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + resource "aws_lakeformation_data_lake_settings" "test" { catalog_id = data.aws_caller_identity.current.account_id - admins = [data.aws_caller_identity.current.arn] + admins = [data.aws_iam_session_context.current.issuer_arn] } 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 index 9a9c48e777c9..97963b217158 100644 --- a/aws/data_source_aws_lakeformation_permissions.go +++ b/aws/data_source_aws_lakeformation_permissions.go @@ -277,6 +277,18 @@ func dataSourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta inte if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 { // since perm list could include TableWithColumns, get the right one for _, perm := range cleanPermissions { + if perm.Resource == nil { + continue + } + + if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnWildcard != nil { + if err := d.Set("table", []interface{}{flattenLakeFormationTableWithColumnsResourceAsTable(perm.Resource.TableWithColumns)}); err != nil { + return fmt.Errorf("error setting table: %w", err) + } + tableSet = true + break + } + if perm.Resource.Table != nil { if err := d.Set("table", []interface{}{flattenLakeFormationTableResource(perm.Resource.Table)}); err != nil { return fmt.Errorf("error setting table: %w", err) diff --git a/aws/data_source_aws_lakeformation_permissions_test.go b/aws/data_source_aws_lakeformation_permissions_test.go index fe111e051e23..47d2a77ca5af 100644 --- a/aws/data_source_aws_lakeformation_permissions_test.go +++ b/aws/data_source_aws_lakeformation_permissions_test.go @@ -148,28 +148,28 @@ data "aws_partition" "current" {} resource "aws_iam_role" "test" { name = %[1]q + path = "/" - assume_role_policy = < 0 && aws.StringValue(perm.Permissions[0]) == lakeformation.PermissionSelect { @@ -94,7 +92,7 @@ func FilterLakeFormationTablePermissions(table *lakeformation.TableResource, all return cleanPermissions } -func FilterLakeFormationTableWithColumnsPermissions(twc *lakeformation.TableResource, columnNames []*string, excludedColumnNames []*string, columnWildcard bool, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { +func FilterLakeFormationTableWithColumnsPermissions(principal *string, twc *lakeformation.TableResource, columnNames []*string, excludedColumnNames []*string, columnWildcard bool, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { // CREATE PERMS (in) = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT, SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard // LIST PERMS (out) = ALL, ALTER, DELETE, DESCRIBE, DROP, INSERT on Table, Name = (Table Name) // LIST PERMS (out) = SELECT on TableWithColumns, Name = (Table Name), ColumnWildcard @@ -102,6 +100,10 @@ func FilterLakeFormationTableWithColumnsPermissions(twc *lakeformation.TableReso var cleanPermissions []*lakeformation.PrincipalResourcePermissions for _, perm := range allPermissions { + if aws.StringValue(principal) != aws.StringValue(perm.Principal.DataLakePrincipalIdentifier) { + continue + } + if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnNames != nil { if StringSlicesEqualIgnoreOrder(perm.Resource.TableWithColumns.ColumnNames, columnNames) { cleanPermissions = append(cleanPermissions, perm) @@ -130,10 +132,14 @@ func FilterLakeFormationTableWithColumnsPermissions(twc *lakeformation.TableReso return cleanPermissions } -func FilterLakeFormationCatalogPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { +func FilterLakeFormationCatalogPermissions(principal *string, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { var cleanPermissions []*lakeformation.PrincipalResourcePermissions for _, perm := range allPermissions { + if aws.StringValue(principal) != aws.StringValue(perm.Principal.DataLakePrincipalIdentifier) { + continue + } + if perm.Resource.Catalog != nil { cleanPermissions = append(cleanPermissions, perm) } @@ -142,10 +148,14 @@ func FilterLakeFormationCatalogPermissions(allPermissions []*lakeformation.Princ return cleanPermissions } -func FilterLakeFormationDataLocationPermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { +func FilterLakeFormationDataLocationPermissions(principal *string, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { var cleanPermissions []*lakeformation.PrincipalResourcePermissions for _, perm := range allPermissions { + if aws.StringValue(principal) != aws.StringValue(perm.Principal.DataLakePrincipalIdentifier) { + continue + } + if perm.Resource.DataLocation != nil { cleanPermissions = append(cleanPermissions, perm) } @@ -154,10 +164,14 @@ func FilterLakeFormationDataLocationPermissions(allPermissions []*lakeformation. return cleanPermissions } -func FilterLakeFormationDatabasePermissions(allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { +func FilterLakeFormationDatabasePermissions(principal *string, allPermissions []*lakeformation.PrincipalResourcePermissions) []*lakeformation.PrincipalResourcePermissions { var cleanPermissions []*lakeformation.PrincipalResourcePermissions for _, perm := range allPermissions { + if aws.StringValue(principal) != aws.StringValue(perm.Principal.DataLakePrincipalIdentifier) { + continue + } + if perm.Resource.Database != nil { cleanPermissions = append(cleanPermissions, perm) } diff --git a/aws/internal/service/lakeformation/waiter/status.go b/aws/internal/service/lakeformation/waiter/status.go index fca281e92165..80beaa016a8f 100644 --- a/aws/internal/service/lakeformation/waiter/status.go +++ b/aws/internal/service/lakeformation/waiter/status.go @@ -3,6 +3,7 @@ package waiter import ( "fmt" + "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" @@ -19,6 +20,10 @@ func PermissionsStatus(conn *lakeformation.LakeFormation, input *lakeformation.L continue } + if aws.StringValue(input.Principal.DataLakePrincipalIdentifier) != aws.StringValue(permission.Principal.DataLakePrincipalIdentifier) { + continue + } + permissions = append(permissions, permission) } return !lastPage diff --git a/aws/internal/service/lakeformation/waiter/waiter.go b/aws/internal/service/lakeformation/waiter/waiter.go index 80e66a211594..ba7343e01abc 100644 --- a/aws/internal/service/lakeformation/waiter/waiter.go +++ b/aws/internal/service/lakeformation/waiter/waiter.go @@ -9,7 +9,7 @@ import ( const ( PermissionsReadyTimeout = 1 * time.Minute - PermissionsDeleteRetryTimeout = 3 * time.Minute + PermissionsDeleteRetryTimeout = 30 * time.Second StatusAvailable = "AVAILABLE" StatusNotFound = "NOT FOUND" diff --git a/aws/resource_aws_lakeformation_data_lake_settings_test.go b/aws/resource_aws_lakeformation_data_lake_settings_test.go index 3c3ec5375ae2..fcbfc1cb6366 100644 --- a/aws/resource_aws_lakeformation_data_lake_settings_test.go +++ b/aws/resource_aws_lakeformation_data_lake_settings_test.go @@ -12,7 +12,6 @@ import ( ) func testAccAWSLakeFormationDataLakeSettings_basic(t *testing.T) { - callerIdentityName := "data.aws_caller_identity.current" resourceName := "aws_lakeformation_data_lake_settings.test" resource.Test(t, resource.TestCase{ @@ -25,9 +24,9 @@ func testAccAWSLakeFormationDataLakeSettings_basic(t *testing.T) { Config: testAccAWSLakeFormationDataLakeSettingsConfig_basic, Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationDataLakeSettingsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "catalog_id", callerIdentityName, "account_id"), + resource.TestCheckResourceAttrPair(resourceName, "catalog_id", "data.aws_caller_identity.current", "account_id"), resource.TestCheckResourceAttr(resourceName, "admins.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "admins.0", callerIdentityName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "admins.0", "data.aws_iam_session_context.current", "issuer_arn"), ), }, }, @@ -56,7 +55,6 @@ func testAccAWSLakeFormationDataLakeSettings_disappears(t *testing.T) { } func testAccAWSLakeFormationDataLakeSettings_withoutCatalogId(t *testing.T) { - callerIdentityName := "data.aws_caller_identity.current" resourceName := "aws_lakeformation_data_lake_settings.test" resource.Test(t, resource.TestCase{ @@ -70,7 +68,7 @@ func testAccAWSLakeFormationDataLakeSettings_withoutCatalogId(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationDataLakeSettingsExists(resourceName), resource.TestCheckResourceAttr(resourceName, "admins.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "admins.0", callerIdentityName, "arn"), + resource.TestCheckResourceAttrPair(resourceName, "admins.0", "data.aws_iam_session_context.current", "issuer_arn"), ), }, }, @@ -137,6 +135,10 @@ func testAccCheckAWSLakeFormationDataLakeSettingsExists(resourceName string) res const testAccAWSLakeFormationDataLakeSettingsConfig_basic = ` data "aws_caller_identity" "current" {} +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + resource "aws_lakeformation_data_lake_settings" "test" { catalog_id = data.aws_caller_identity.current.account_id @@ -150,7 +152,7 @@ resource "aws_lakeformation_data_lake_settings" "test" { permissions = ["ALL"] } - admins = [data.aws_caller_identity.current.arn] + admins = [data.aws_iam_session_context.current.issuer_arn] trusted_resource_owners = [data.aws_caller_identity.current.account_id] } ` @@ -158,7 +160,11 @@ resource "aws_lakeformation_data_lake_settings" "test" { const testAccAWSLakeFormationDataLakeSettingsConfig_withoutCatalogId = ` data "aws_caller_identity" "current" {} +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + resource "aws_lakeformation_data_lake_settings" "test" { - admins = [data.aws_caller_identity.current.arn] + admins = [data.aws_iam_session_context.current.issuer_arn] } ` diff --git a/aws/resource_aws_lakeformation_permissions.go b/aws/resource_aws_lakeformation_permissions.go index 93dc46901588..0b95cc9ab0d6 100644 --- a/aws/resource_aws_lakeformation_permissions.go +++ b/aws/resource_aws_lakeformation_permissions.go @@ -484,6 +484,18 @@ func resourceAwsLakeFormationPermissionsRead(d *schema.ResourceData, meta interf if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 { // since perm list could include TableWithColumns, get the right one for _, perm := range cleanPermissions { + if perm.Resource == nil { + continue + } + + if perm.Resource.TableWithColumns != nil && perm.Resource.TableWithColumns.ColumnWildcard != nil { + if err := d.Set("table", []interface{}{flattenLakeFormationTableWithColumnsResourceAsTable(perm.Resource.TableWithColumns)}); err != nil { + return fmt.Errorf("error setting table: %w", err) + } + tableSet = true + break + } + if perm.Resource.Table != nil { if err := d.Set("table", []interface{}{flattenLakeFormationTableResource(perm.Resource.Table)}); err != nil { return fmt.Errorf("error setting table: %w", err) @@ -585,7 +597,7 @@ func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta inte _, err = conn.RevokePermissions(input) } - if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "No permissions revoked. Grantee has no") { + if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "No permissions revoked. Grantee") { return nil } @@ -616,7 +628,11 @@ func resourceAwsLakeFormationPermissionsDelete(d *schema.ResourceData, meta inte _, err = conn.RevokePermissions(input) } - if err != nil && !tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "No permissions revoked. Grantee has no") { + if tfawserr.ErrMessageContains(err, lakeformation.ErrCodeInvalidInputException, "No permissions revoked. Grantee") { + return nil + } + + if err != nil { return fmt.Errorf("unable to revoke LakeFormation Permissions (input: %v): %w", input, err) } @@ -844,6 +860,33 @@ func flattenLakeFormationTableWithColumnsResource(apiObject *lakeformation.Table return tfMap } +// This only happens in very specific situations: +// (Select) TWC + ColumnWildcard = (Select) Table +// (Select) TWC + ColumnWildcard + ALL_TABLES = (Select) Table + TableWildcard +func flattenLakeFormationTableWithColumnsResourceAsTable(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) + } + + if v := apiObject.DatabaseName; v != nil { + tfMap["database_name"] = aws.StringValue(v) + } + + if v := apiObject.Name; v != nil && aws.StringValue(v) == tflakeformation.TableNameAllTables && apiObject.ColumnWildcard != nil { + tfMap["wildcard"] = true + } else if v := apiObject.Name; v != nil { + tfMap["name"] = aws.StringValue(v) + } + + return tfMap +} + func flattenLakeFormationPermissions(apiObjects []*lakeformation.PrincipalResourcePermissions) []string { if apiObjects == nil { return nil diff --git a/aws/resource_aws_lakeformation_permissions_test.go b/aws/resource_aws_lakeformation_permissions_test.go index bfd87e3fd9fe..57db85a834a0 100644 --- a/aws/resource_aws_lakeformation_permissions_test.go +++ b/aws/resource_aws_lakeformation_permissions_test.go @@ -53,7 +53,7 @@ func testAccAWSLakeFormationPermissions_disappears(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, "\"event\", \"timestamp\""), + Config: testAccAWSLakeFormationPermissionsConfig_twcBasic(rName, "\"event\", \"timestamp\""), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), testAccCheckResourceDisappears(testAccProvider, resourceAwsLakeFormationPermissions(), resourceName), @@ -64,11 +64,11 @@ func testAccAWSLakeFormationPermissions_disappears(t *testing.T) { }) } -func testAccAWSLakeFormationPermissions_dataLocation(t *testing.T) { +func testAccAWSLakeFormationPermissions_database(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" - bucketName := "aws_s3_bucket.test" + dbName := "aws_glue_catalog_database.test" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -77,25 +77,60 @@ func testAccAWSLakeFormationPermissions_dataLocation(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_dataLocation(rName), + Config: testAccAWSLakeFormationPermissionsConfig_database(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionDataLocationAccess), resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), - resource.TestCheckResourceAttr(resourceName, "data_location.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "data_location.0.arn", bucketName, "arn"), + 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", lakeformation.PermissionAlter), + resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionCreateTable), + resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDrop), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", lakeformation.PermissionCreateTable), ), }, }, }) } -func testAccAWSLakeFormationPermissions_database(t *testing.T) { +func testAccAWSLakeFormationPermissions_databaseIAMAllowed(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + dbName := "aws_glue_catalog_database.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_databaseIAMAllowed(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "principal", tflakeformation.IAMAllowedPrincipals), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), + resource.TestCheckResourceAttr(resourceName, "database.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dbName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAll), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_databaseMultiple(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" + resourceName2 := "aws_lakeformation_permissions.test2" roleName := "aws_iam_role.test" + roleName2 := "aws_iam_role.test2" dbName := "aws_glue_catalog_database.test" resource.Test(t, resource.TestCase{ @@ -105,7 +140,7 @@ func testAccAWSLakeFormationPermissions_database(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_database(rName), + Config: testAccAWSLakeFormationPermissionsConfig_databaseMultiple(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), @@ -114,18 +149,56 @@ func testAccAWSLakeFormationPermissions_database(t *testing.T) { 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.0", lakeformation.PermissionAlter), resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionCreateTable), resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDrop), resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "1"), resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.0", lakeformation.PermissionCreateTable), + testAccCheckAWSLakeFormationPermissionsExists(resourceName2), + resource.TestCheckResourceAttrPair(resourceName2, "principal", roleName2, "arn"), + resource.TestCheckResourceAttr(resourceName2, "catalog_resource", "false"), + resource.TestCheckResourceAttrPair(resourceName2, "principal", roleName2, "arn"), + resource.TestCheckResourceAttr(resourceName2, "database.#", "1"), + resource.TestCheckResourceAttrPair(resourceName2, "database.0.name", dbName, "name"), + resource.TestCheckResourceAttr(resourceName2, "permissions.#", "2"), + resource.TestCheckResourceAttr(resourceName2, "permissions.0", lakeformation.PermissionAlter), + resource.TestCheckResourceAttr(resourceName2, "permissions.1", lakeformation.PermissionDrop), + resource.TestCheckResourceAttr(resourceName2, "permissions_with_grant_option.#", "0"), + ), + }, + }, + }) +} + +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.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + 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", lakeformation.PermissionDataLocationAccess), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), + resource.TestCheckResourceAttr(resourceName, "data_location.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "data_location.0.arn", bucketName, "arn"), ), }, }, }) } -func testAccAWSLakeFormationPermissions_tableName(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableBasic(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" @@ -138,7 +211,7 @@ func testAccAWSLakeFormationPermissions_tableName(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableName(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableBasic(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), @@ -155,10 +228,10 @@ func testAccAWSLakeFormationPermissions_tableName(t *testing.T) { }) } -func testAccAWSLakeFormationPermissions_tableWildcard(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableIAMAllowed(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" - databaseResourceName := "aws_glue_catalog_database.test" + dbName := "aws_glue_catalog_table.test" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -167,19 +240,24 @@ func testAccAWSLakeFormationPermissions_tableWildcard(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWildcard(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableIAMAllowed(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "principal", tflakeformation.IAMAllowedPrincipals), + resource.TestCheckResourceAttr(resourceName, "catalog_resource", "false"), resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", databaseResourceName, "name"), - resource.TestCheckResourceAttr(resourceName, "table.0.wildcard", "true"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", dbName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", dbName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAll), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), ), }, }, }) } -func testAccAWSLakeFormationPermissions_tableWithColumns(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableImplicit(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" @@ -192,61 +270,80 @@ func testAccAWSLakeFormationPermissions_tableWithColumns(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, "\"event\", \"timestamp\""), + Config: testAccAWSLakeFormationPermissionsConfig_tableImplicit(rName), Check: resource.ComposeTestCheckFunc( 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", lakeformation.PermissionSelect), - ), - }, - { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, "\"timestamp\", \"event\""), - Check: resource.ComposeTestCheckFunc( - 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", lakeformation.PermissionSelect), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), ), }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_tableMultipleRoles(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + resourceName2 := "aws_lakeformation_permissions.test2" + roleName := "aws_iam_role.test" + roleName2 := "aws_iam_role.test2" + tableName := "aws_glue_catalog_table.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, "\"timestamp\", \"event\", \"transactionamount\""), + Config: testAccAWSLakeFormationPermissionsConfig_tableMultipleRoles(rName), Check: resource.ComposeTestCheckFunc( 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.#", "3"), - 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, "table_with_columns.0.column_names.2", "transactionamount"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "3"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionAlter), + resource.TestCheckResourceAttr(resourceName, "permissions.1", lakeformation.PermissionDelete), + resource.TestCheckResourceAttr(resourceName, "permissions.2", lakeformation.PermissionDescribe), + testAccCheckAWSLakeFormationPermissionsExists(resourceName2), + resource.TestCheckResourceAttrPair(roleName2, "arn", resourceName2, "principal"), + resource.TestCheckResourceAttr(resourceName2, "table.#", "1"), + resource.TestCheckResourceAttrPair(resourceName2, "table.0.database_name", tableName, "database_name"), + resource.TestCheckResourceAttrPair(resourceName2, "table.0.name", tableName, "name"), + resource.TestCheckResourceAttr(resourceName2, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName2, "permissions.0", lakeformation.PermissionSelect), ), }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_tableSelectOnly(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.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWithColumns(rName, "\"event\""), + Config: testAccAWSLakeFormationPermissionsConfig_tableSelectOnly(rName), Check: resource.ComposeTestCheckFunc( 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.#", "1"), - resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), + resource.TestCheckResourceAttrPair(roleName, "arn", resourceName, "principal"), + resource.TestCheckResourceAttr(resourceName, "table.#", "1"), + 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", lakeformation.PermissionSelect), ), @@ -255,11 +352,10 @@ func testAccAWSLakeFormationPermissions_tableWithColumns(t *testing.T) { }) } -func testAccAWSLakeFormationPermissions_implicitTableWithColumnsPermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableSelectPlus(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.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -268,14 +364,10 @@ func testAccAWSLakeFormationPermissions_implicitTableWithColumnsPermissions(t *t CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_implicitTableWithColumnsPermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableSelectPlus(rName), Check: resource.ComposeTestCheckFunc( 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.wildcard", "true"), resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), ), @@ -284,11 +376,10 @@ func testAccAWSLakeFormationPermissions_implicitTableWithColumnsPermissions(t *t }) } -func testAccAWSLakeFormationPermissions_implicitTablePermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableWildcardNoSelect(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" + databaseResourceName := "aws_glue_catalog_database.test" resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -297,22 +388,19 @@ func testAccAWSLakeFormationPermissions_implicitTablePermissions(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_implicitTablePermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableWildcardNoSelect(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), - resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), resource.TestCheckResourceAttr(resourceName, "table.#", "1"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", tableName, "database_name"), - resource.TestCheckResourceAttrPair(resourceName, "table.0.name", tableName, "name"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), + resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", databaseResourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "table.0.wildcard", "true"), ), }, }, }) } -func testAccAWSLakeFormationPermissions_selectPermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableWildcardSelectOnly(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" @@ -324,19 +412,20 @@ func testAccAWSLakeFormationPermissions_selectPermissions(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_selectPermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableWildcardSelectOnly(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), ), }, }, }) } -func testAccAWSLakeFormationPermissions_tableWildcardPermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_tableWildcardSelectPlus(t *testing.T) { rName := acctest.RandomWithPrefix("tf-acc-test") resourceName := "aws_lakeformation_permissions.test" roleName := "aws_iam_role.test" @@ -348,7 +437,7 @@ func testAccAWSLakeFormationPermissions_tableWildcardPermissions(t *testing.T) { CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_tableWildcardPermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_tableWildcardSelectPlus(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), @@ -360,10 +449,11 @@ func testAccAWSLakeFormationPermissions_tableWildcardPermissions(t *testing.T) { }) } -func testAccAWSLakeFormationPermissions_columnWildcardPermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_twcBasic(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.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -372,22 +462,74 @@ func testAccAWSLakeFormationPermissions_columnWildcardPermissions(t *testing.T) CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_columnWildcardPermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_twcBasic(rName, "\"event\", \"timestamp\""), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), + 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", lakeformation.PermissionSelect), + ), + }, + { + Config: testAccAWSLakeFormationPermissionsConfig_twcBasic(rName, "\"timestamp\", \"event\""), + Check: resource.ComposeTestCheckFunc( + 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", lakeformation.PermissionSelect), + ), + }, + { + Config: testAccAWSLakeFormationPermissionsConfig_twcBasic(rName, "\"timestamp\", \"event\", \"transactionamount\""), + Check: resource.ComposeTestCheckFunc( + 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.#", "3"), + 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, "table_with_columns.0.column_names.2", "transactionamount"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + ), + }, + { + Config: testAccAWSLakeFormationPermissionsConfig_twcBasic(rName, "\"event\""), + Check: resource.ComposeTestCheckFunc( + 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.#", "1"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.column_names.0", "event"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), ), }, }, }) } -func testAccAWSLakeFormationPermissions_columnWildcardExcludedColumnsPermissions(t *testing.T) { +func testAccAWSLakeFormationPermissions_twcImplicit(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.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, @@ -396,34 +538,116 @@ func testAccAWSLakeFormationPermissions_columnWildcardExcludedColumnsPermissions CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLakeFormationPermissionsConfig_columnWildcardExcludedColumnsPermissions(rName), + Config: testAccAWSLakeFormationPermissionsConfig_twcImplicit(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLakeFormationPermissionsExists(resourceName), resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), - resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), - resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), + 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.wildcard", "true"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "7"), ), }, }, }) } -func testAccCheckAWSLakeFormationPermissionsDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).lakeformationconn +func testAccAWSLakeFormationPermissions_twcWildcardExcludedColumns(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_lakeformation_permissions" { - continue - } + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_twcWildcardExcludedColumns(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_twcWildcardSelectOnly(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.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_twcWildcardSelectOnly(rName), + Check: resource.ComposeTestCheckFunc( + 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.#", "0"), + resource.TestCheckResourceAttr(resourceName, "table_with_columns.0.wildcard", "true"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "permissions.0", lakeformation.PermissionSelect), + ), + }, + }, + }) +} + +func testAccAWSLakeFormationPermissions_twcWildcardSelectPlus(t *testing.T) { + rName := acctest.RandomWithPrefix("tf-acc-test") + resourceName := "aws_lakeformation_permissions.test" + roleName := "aws_iam_role.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) }, + ErrorCheck: testAccErrorCheck(t, lakeformation.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLakeFormationPermissionsConfig_twcWildcardSelectPlus(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLakeFormationPermissionsExists(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "principal", roleName, "arn"), + resource.TestCheckResourceAttr(resourceName, "permissions.#", "7"), + resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"), + ), + }, + }, + }) +} + +func testAccCheckAWSLakeFormationPermissionsDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).lakeformationconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_lakeformation_permissions" { + continue + } permCount, err := permissionCountForLakeFormationResource(conn, rs) if err != nil { - return fmt.Errorf("error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) + return fmt.Errorf("acceptance test: error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) } if permCount != 0 { - return fmt.Errorf("Lake Formation permissions (%s) still exist: %d", rs.Primary.ID, permCount) + return fmt.Errorf("acceptance test: Lake Formation permissions (%s) still exist: %d", rs.Primary.ID, permCount) } return nil @@ -435,8 +659,9 @@ func testAccCheckAWSLakeFormationPermissionsDestroy(s *terraform.State) error { 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) + return fmt.Errorf("acceptance test: resource not found: %s", resourceName) } conn := testAccProvider.Meta().(*AWSClient).lakeformationconn @@ -444,11 +669,11 @@ func testAccCheckAWSLakeFormationPermissionsExists(resourceName string) resource permCount, err := permissionCountForLakeFormationResource(conn, rs) if err != nil { - return fmt.Errorf("error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) + return fmt.Errorf("acceptance test: error listing Lake Formation permissions (%s): %w", rs.Primary.ID, err) } if permCount == 0 { - return fmt.Errorf("Lake Formation permissions (%s) do not exist or could not be found", rs.Primary.ID) + return fmt.Errorf("acceptance test: Lake Formation permissions (%s) do not exist or could not be found", rs.Primary.ID) } return nil @@ -463,12 +688,18 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, Resource: &lakeformation.Resource{}, } + noResource := true + if v, ok := rs.Primary.Attributes["catalog_id"]; ok && v != "" { input.CatalogId = aws.String(v) + + noResource = false } if v, ok := rs.Primary.Attributes["catalog_resource"]; ok && v != "" && v == "true" { input.Resource.Catalog = expandLakeFormationCatalogResource() + + noResource = false } if v, ok := rs.Primary.Attributes["data_location.#"]; ok && v != "" && v != "0" { @@ -483,6 +714,8 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, } input.Resource.DataLocation = expandLakeFormationDataLocationResource(tfMap) + + noResource = false } if v, ok := rs.Primary.Attributes["database.#"]; ok && v != "" && v != "0" { @@ -497,6 +730,8 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, } input.Resource.Database = expandLakeFormationDatabaseResource(tfMap) + + noResource = false } tableType := "" @@ -523,6 +758,8 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, } input.Resource.Table = expandLakeFormationTableResource(tfMap) + + noResource = false } if v, ok := rs.Primary.Attributes["table_with_columns.#"]; ok && v != "" && v != "0" { @@ -543,6 +780,13 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, } input.Resource.Table = expandLakeFormationTableWithColumnsResourceAsTable(tfMap) + + noResource = false + } + + if noResource { + // if after read, there is no resource, it has been deleted + return 0, nil } log.Printf("[DEBUG] Reading Lake Formation permissions: %v", input) @@ -565,7 +809,7 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, return resource.RetryableError(err) } - return resource.NonRetryableError(fmt.Errorf("error listing Lake Formation Permissions: %w", err)) + return resource.NonRetryableError(fmt.Errorf("acceptance test: error listing Lake Formation Permissions getting permission count: %w", err)) } return nil }) @@ -592,7 +836,7 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, } if err != nil { - return 0, fmt.Errorf("error listing Lake Formation permissions after retry: %w", err) + return 0, fmt.Errorf("acceptance test: error listing Lake Formation permissions after retry %v: %w", input, err) } columnNames := make([]*string, 0) @@ -610,7 +854,7 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, colCount, err = strconv.Atoi(rs.Primary.Attributes["table_with_columns.0.column_names.#"]) if err != nil { - return 0, fmt.Errorf("could not convert string (%s) Atoi for column_names: %w", rs.Primary.Attributes["table_with_columns.0.column_names.#"], err) + return 0, fmt.Errorf("acceptance test: could not convert string (%s) Atoi for column_names: %w", rs.Primary.Attributes["table_with_columns.0.column_names.#"], err) } } @@ -624,7 +868,7 @@ func permissionCountForLakeFormationResource(conn *lakeformation.LakeFormation, colCount, err = strconv.Atoi(rs.Primary.Attributes["table_with_columns.0.excluded_column_names.#"]) if err != nil { - return 0, fmt.Errorf("could not convert string (%s) Atoi for excluded_column_names: %w", rs.Primary.Attributes["table_with_columns.0.excluded_column_names.#"], err) + return 0, fmt.Errorf("acceptance test: could not convert string (%s) Atoi for excluded_column_names: %w", rs.Primary.Attributes["table_with_columns.0.excluded_column_names.#"], err) } } @@ -643,36 +887,455 @@ func testAccAWSLakeFormationPermissionsConfig_basic(rName string) string { return fmt.Sprintf(` data "aws_partition" "current" {} -resource "aws_iam_role" "test" { - name = %[1]q +resource "aws_iam_role" "test" { + name = %[1]q + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_lakeformation_permissions" "test" { + principal = aws_iam_role.test.arn + permissions = ["CREATE_DATABASE"] + catalog_resource = true + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_database(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +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 + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_databaseIAMAllowed(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +resource "aws_glue_catalog_table" "test" { + name = %[1]q + database_name = aws_glue_catalog_database.test.name + + storage_descriptor { + columns { + name = "event" + type = "string" + } + + columns { + name = "timestamp" + type = "date" + } + + columns { + name = "transactionamount" + type = "double" + } + } +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["ALL"] + principal = "IAM_ALLOWED_PRINCIPALS" + + database { + name = aws_glue_catalog_database.test.name + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_databaseMultiple(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_iam_role" "test2" { + name = "%[1]s-2" + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +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 + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} + +resource "aws_lakeformation_permissions" "test2" { + permissions = ["ALTER", "DROP"] + principal = aws_iam_role.test2.arn + + database { + name = aws_glue_catalog_database.test.name + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_dataLocation(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }, { + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "s3.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_s3_bucket" "test" { + bucket = %[1]q + acl = "private" + force_destroy = true +} + +resource "aws_lakeformation_resource" "test" { + arn = aws_s3_bucket.test.arn + role_arn = aws_iam_role.test.arn +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_lakeformation_permissions" "test" { + principal = aws_iam_role.test.arn + permissions = ["DATA_LOCATION_ACCESS"] + + data_location { + arn = aws_s3_bucket.test.arn + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_tableBasic(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +resource "aws_glue_catalog_table" "test" { + name = %[1]q + database_name = aws_glue_catalog_database.test.name +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["ALTER", "DELETE", "DESCRIBE"] + principal = aws_iam_role.test.arn + + table { + database_name = aws_glue_catalog_table.test.database_name + name = aws_glue_catalog_table.test.name + } + + # for consistency, ensure that admins are setup before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_tableIAMAllowed(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +resource "aws_glue_catalog_table" "test" { + name = %[1]q + database_name = aws_glue_catalog_database.test.name + + storage_descriptor { + columns { + name = "event" + type = "string" + } + + columns { + name = "timestamp" + type = "date" + } + + columns { + name = "transactionamount" + type = "double" + } + } +} + +resource "aws_lakeformation_permissions" "test" { + permissions = ["ALL"] + principal = "IAM_ALLOWED_PRINCIPALS" + + table { + database_name = aws_glue_catalog_database.test.name + name = aws_glue_catalog_table.test.name + } +} +`, rName) +} + +func testAccAWSLakeFormationPermissionsConfig_tableImplicit(rName string) string { + return fmt.Sprintf(` +data "aws_partition" "current" {} + +resource "aws_iam_role" "test" { + name = %[1]q + path = "/" + + assume_role_policy = jsonencode({ + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = "glue.${data.aws_partition.current.dns_suffix}" + } + }] + Version = "2012-10-17" + }) +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +resource "aws_glue_catalog_table" "test" { + name = %[1]q + database_name = aws_glue_catalog_database.test.name + + storage_descriptor { + columns { + name = "event" + type = "string" + } + + columns { + name = "timestamp" + type = "date" + } - assume_role_policy = < **WARNING:** Lake Formation permissions are not in effect by default within AWS. Using this resource will not secure your data and will result in errors if you do not change the security settings for existing resources and the default security settings for new resources. See [Default Behavior and `IAMAllowedPrincipals`](#default-behavior-and-iamallowedprincipals) for additional details. + ~> **NOTE:** In general, the `principal` should _NOT_ be a Lake Formation administrator or the entity (e.g., IAM role) that is running Terraform. Administrators have implicit permissions. These should be managed by granting or not granting administrator rights using `aws_lakeformation_data_lake_settings`, _not_ with this resource. +## Default Behavior and `IAMAllowedPrincipals` + +**_Lake Formation permissions are not in effect by default within AWS._** `IAMAllowedPrincipals` (i.e., `IAM_ALLOWED_PRINCIPALS`) conflicts with individual Lake Formation permissions (i.e., non-`IAMAllowedPrincipals` permissions), will cause unexpected behavior, and may result in errors. + +When using Lake Formation, choose ONE of the following options as they are mutually exclusive: + +1. Use this resource (`aws_lakeformation_permissions`), change the default security settings using [`aws_lakeformation_data_lake_settings`](/docs/providers/aws/r/lakeformation_data_lake_settings.html), and remove existing `IAMAllowedPrincipals` permissions +2. Use `IAMAllowedPrincipals` without `aws_lakeformation_permissions` + +This example shows removing the `IAMAllowedPrincipals` default security settings and making the caller a Lake Formation admin. Since `create_database_default_permissions` and `create_table_default_permissions` are not set in the [`aws_lakeformation_data_lake_settings`](/docs/providers/aws/r/lakeformation_data_lake_settings.html) resource, they are cleared. + +```terraform +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} +``` + +To remove existing `IAMAllowedPrincipals` permissions, use the [AWS Lake Formation Console](https://console.aws.amazon.com/lakeformation/) or [AWS CLI](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/lakeformation/batch-revoke-permissions.html). + +`IAMAllowedPrincipals` is a hook to maintain backwards compatibility with AWS Glue. `IAMAllowedPrincipals` is a pseudo-entity group that acts like a Lake Formation principal. The group includes any IAM users and roles that are allowed access to your Data Catalog resources by your IAM policies. + +This is Lake Formation's default behavior: + +* Lake Formation grants `Super` permission to `IAMAllowedPrincipals` on all existing AWS Glue Data Catalog resources. +* Lake Formation enables "Use only IAM access control" for new Data Catalog resources. + +For more details, see [Changing the Default Security Settings for Your Data Lake](https://docs.aws.amazon.com/lake-formation/latest/dg/change-settings.html). + +### Problem Using `IAMAllowedPrincipals` + +AWS does not support combining `IAMAllowedPrincipals` permissions and non-`IAMAllowedPrincipals` permissions. Doing so results in unexpected permissions and behaviors. For example, this configuration grants a user `SELECT` on a column in a table. + +```terraform +resource "aws_glue_catalog_database" "example" { + name = "sadabate" +} + +resource "aws_glue_catalog_table" "example" { + name = "abelt" + database_name = aws_glue_catalog_database.test.name + + storage_descriptor { + columns { + name = "event" + type = "string" + } + } +} + +resource "aws_lakeformation_permissions" "example" { + permissions = ["SELECT"] + principal = "arn:aws:iam:us-east-1:123456789012:user/SanHolo" + + table_with_columns { + database_name = aws_glue_catalog_table.example.database_name + name = aws_glue_catalog_table.example.name + column_names = ["event"] + } +} +``` + +The resulting permissions depend on whether the table had `IAMAllowedPrincipals` (IAP) permissions or not. + +| Result With IAP | Result Without IAP | +| ---- | ---- | +| `SELECT` column wildcard (i.e., all columns) | `SELECT` on `"event"` (as expected) | + ## Using Lake Formation Permissions Lake Formation grants implicit permissions to data lake administrators, database creators, and table creators. These implicit permissions cannot be revoked _per se_. If this resource reads implicit permissions, it will attempt to revoke them, which causes an error when the resource is destroyed. @@ -25,12 +100,12 @@ If the `principal` is also a data lake administrator, AWS grants implicit permis ### Grant Permissions For A Lake Formation S3 Resource ```terraform -resource "aws_lakeformation_permissions" "test" { +resource "aws_lakeformation_permissions" "example" { principal = aws_iam_role.workflow_role.arn permissions = ["ALL"] data_location { - arn = aws_lakeformation_resource.test.arn + arn = aws_lakeformation_resource.example.arn } } ``` @@ -38,12 +113,12 @@ resource "aws_lakeformation_permissions" "test" { ### Grant Permissions For A Glue Catalog Database ```terraform -resource "aws_lakeformation_permissions" "test" { +resource "aws_lakeformation_permissions" "example" { role = aws_iam_role.workflow_role.arn permissions = ["CREATE_TABLE", "ALTER", "DROP"] database { - name = aws_glue_catalog_database.test.name + name = aws_glue_catalog_database.example.name catalog_id = "110376042874" } } @@ -54,7 +129,7 @@ resource "aws_lakeformation_permissions" "test" { The following arguments are required: * `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 roles, users, groups, SAML groups and users, QuickSight groups, OUs, and organizations as well as AWS account IDs for cross-account permissions. For more information, 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_ALLOWED_PRINCIPALS` (see [Default Behavior and `IAMAllowedPrincipals`](#default-behavior-and-iamallowedprincipals) above), IAM roles, users, groups, SAML groups and users, QuickSight groups, OUs, and organizations as well as AWS account IDs for cross-account permissions. For more information, see [Lake Formation Permissions Reference](https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html). ~> **NOTE:** We highly recommend that the `principal` _NOT_ be a Lake Formation administrator (granted using `aws_lakeformation_data_lake_settings`). The entity (e.g., IAM role) running Terraform will most likely need to be a Lake Formation administrator. As such, the entity will have implicit permissions and does not need permissions granted through this resource.