Skip to content

Commit

Permalink
Merge pull request #38600 from nickdelnano/b-aws_lakeformation_permis…
Browse files Browse the repository at this point in the history
…sions-iamprincipals

aws_lakeformation_permissions support AllIAMPrincipals special principal
  • Loading branch information
johnsonaj authored Dec 18, 2024
2 parents fb9d17b + 707c23b commit 3d299e2
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 14 deletions.
3 changes: 3 additions & 0 deletions .changelog/38600.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
resource/aws_lakeformation_permissions: Add support for `IAMPrincipals` principal group
```
22 changes: 12 additions & 10 deletions internal/service/lakeformation/lakeformation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@ func TestAccLakeFormation_serial(t *testing.T) {
"readOnlyAdmins": testAccDataLakeSettingsDataSource_readOnlyAdmins,
},
"PermissionsBasic": {
acctest.CtBasic: testAccPermissions_basic,
"database": testAccPermissions_database,
"databaseIAMAllowed": testAccPermissions_databaseIAMAllowed,
"databaseMultiple": testAccPermissions_databaseMultiple,
"dataCellsFilter": testAccPermissions_dataCellsFilter,
"dataLocation": testAccPermissions_dataLocation,
acctest.CtDisappears: testAccPermissions_disappears,
"lfTag": testAccPermissions_lfTag,
"lfTagPolicy": testAccPermissions_lfTagPolicy,
"lfTagPolicyMultiple": testAccPermissions_lfTagPolicyMultiple,
acctest.CtBasic: testAccPermissions_basic,
"database": testAccPermissions_database,
"databaseIAMAllowed": testAccPermissions_databaseIAMAllowed,
"databaseIAMPrincipals": testAccPermissions_databaseIAMPrincipals,
"databaseMultiple": testAccPermissions_databaseMultiple,
"dataCellsFilter": testAccPermissions_dataCellsFilter,
"dataLocation": testAccPermissions_dataLocation,
acctest.CtDisappears: testAccPermissions_disappears,
"lfTag": testAccPermissions_lfTag,
"lfTagPolicy": testAccPermissions_lfTagPolicy,
"lfTagPolicyMultiple": testAccPermissions_lfTagPolicyMultiple,
},
"PermissionsDataSource": {
acctest.CtBasic: testAccPermissionsDataSource_basic,
Expand All @@ -55,6 +56,7 @@ func TestAccLakeFormation_serial(t *testing.T) {
"PermissionsTable": {
acctest.CtBasic: testAccPermissions_tableBasic,
"iamAllowed": testAccPermissions_tableIAMAllowed,
"iamPrincipals": testAccPermissions_tableIAMPrincipals,
"implicit": testAccPermissions_tableImplicit,
"multipleRoles": testAccPermissions_tableMultipleRoles,
"selectOnly": testAccPermissions_tableSelectOnly,
Expand Down
187 changes: 187 additions & 0 deletions internal/service/lakeformation/permissions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,35 @@ func testAccPermissions_databaseIAMAllowed(t *testing.T) {
})
}

func testAccPermissions_databaseIAMPrincipals(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_lakeformation_permissions.test"
dbName := "aws_glue_catalog_database.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, names.LakeFormation) },
ErrorCheck: acctest.ErrorCheck(t, names.LakeFormationServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckPermissionsDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccPermissionsConfig_databaseIAMPrincipals(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckPermissionsExists(ctx, resourceName),
testAccCheckIAMPrincipalsGrantPrincipal(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, "catalog_resource", acctest.CtFalse),
resource.TestCheckResourceAttr(resourceName, "database.#", "1"),
resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dbName, names.AttrName),
resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"),
resource.TestCheckResourceAttr(resourceName, "permissions.0", string(awstypes.PermissionAll)),
resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"),
),
},
},
})
}

func testAccPermissions_databaseMultiple(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
Expand Down Expand Up @@ -422,6 +451,36 @@ func testAccPermissions_tableIAMAllowed(t *testing.T) {
})
}

func testAccPermissions_tableIAMPrincipals(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_lakeformation_permissions.test"
dbName := "aws_glue_catalog_table.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, names.LakeFormation) },
ErrorCheck: acctest.ErrorCheck(t, names.LakeFormationServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckPermissionsDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccPermissionsConfig_tableIAMPrincipals(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckPermissionsExists(ctx, resourceName),
testAccCheckIAMPrincipalsGrantPrincipal(ctx, resourceName),
resource.TestCheckResourceAttr(resourceName, "catalog_resource", acctest.CtFalse),
resource.TestCheckResourceAttr(resourceName, "table.#", "1"),
resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", dbName, names.AttrDatabaseName),
resource.TestCheckResourceAttrPair(resourceName, "table.0.name", dbName, names.AttrName),
resource.TestCheckResourceAttr(resourceName, "permissions.#", "1"),
resource.TestCheckResourceAttr(resourceName, "permissions.0", string(awstypes.PermissionAll)),
resource.TestCheckResourceAttr(resourceName, "permissions_with_grant_option.#", "0"),
),
},
},
})
}

func testAccPermissions_tableImplicit(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
Expand Down Expand Up @@ -810,6 +869,27 @@ func testAccPermissions_twcWildcardSelectPlus(t *testing.T) {
})
}

func testAccCheckIAMPrincipalsGrantPrincipal(ctx context.Context, resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName]

if !ok {
return fmt.Errorf("acceptance test: resource not found: %s", resourceName)
}

if v, ok := rs.Primary.Attributes[names.AttrPrincipal]; ok && v != "" {
expectedPrincipalValue := acctest.AccountID(ctx) + ":IAMPrincipals"
if v == expectedPrincipalValue {
return nil
} else {
return fmt.Errorf("acceptance test: unexpected principal value for (%s). Is %s, should be %s", rs.Primary.ID, v, expectedPrincipalValue)
}
}

return fmt.Errorf("acceptance test: error finding IAMPrincipals grant (%s)", rs.Primary.ID)
}
}

func testAccCheckPermissionsDestroy(ctx context.Context) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := acctest.Provider.Meta().(*conns.AWSClient).LakeFormationClient(ctx)
Expand Down Expand Up @@ -1387,6 +1467,61 @@ resource "aws_lakeformation_permissions" "test" {
`, rName)
}

func testAccPermissionsConfig_databaseIAMPrincipals(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 = "${data.aws_caller_identity.current.account_id}:IAMPrincipals"
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 testAccPermissionsConfig_databaseMultiple(rName string) string {
return fmt.Sprintf(`
data "aws_partition" "current" {}
Expand Down Expand Up @@ -1828,6 +1963,58 @@ resource "aws_lakeformation_permissions" "test" {
`, rName)
}

func testAccPermissionsConfig_tableIAMPrincipals(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 = "${data.aws_caller_identity.current.account_id}:IAMPrincipals"
table {
database_name = aws_glue_catalog_database.test.name
name = aws_glue_catalog_table.test.name
}
}
`, rName)
}

func testAccPermissionsConfig_tableImplicit(rName string) string {
return fmt.Sprintf(`
data "aws_partition" "current" {}
Expand Down
11 changes: 11 additions & 0 deletions internal/service/lakeformation/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package lakeformation

import (
"fmt"
"strings"

"github.com/YakDriver/regexache"
"github.com/hashicorp/terraform-provider-aws/internal/verify"
Expand All @@ -17,6 +18,16 @@ func validPrincipal(v interface{}, k string) (ws []string, errors []error) {
return ws, errors
}

// IAMPrincipals special grant has format {account_id}:IAMPrincipals
if val := strings.Split(value, ":"); len(val) == 2 && val[1] == "IAMPrincipals" {
wsAccount, errorsAccount := verify.ValidAccountID(val[0], k)
if len(errorsAccount) == 0 {
return wsAccount, errorsAccount
}

ws = append(ws, wsAccount...)
}

// https://docs.aws.amazon.com/lake-formation/latest/dg/lf-permissions-reference.html
// Principal is an AWS account
// --principal DataLakePrincipalIdentifier=111122223333
Expand Down
12 changes: 8 additions & 4 deletions internal/service/lakeformation/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ func TestValidPrincipal(t *testing.T) {
}

validNames := []string{
"IAM_ALLOWED_PRINCIPALS", // Special principal
acctest.Ct12Digit, // lintignore:AWSAT005 // Example Account ID (Valid looking but not real)
"111122223333", // lintignore:AWSAT005 // Example Account ID (Valid looking but not real)
"IAM_ALLOWED_PRINCIPALS", // Special principal
"123456789012:IAMPrincipals", // Special principal, Example Account ID (Valid looking but not real)
acctest.Ct12Digit, // lintignore:AWSAT005 // Example Account ID (Valid looking but not real)
"111122223333", // lintignore:AWSAT005 // Example Account ID (Valid looking but not real)
"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:iam::123456789012:federated-user/David", // lintignore:AWSAT005 // IAM Federated User
Expand All @@ -45,7 +46,10 @@ func TestValidPrincipal(t *testing.T) {
invalidNames := []string{
"IAM_NOT_ALLOWED_PRINCIPALS", // doesn't exist
names.AttrARN,
"1234567890125", //not an account id
"1234567890125", //not an account id
"IAMPrincipals", // incorrect representation
"1234567890125:IAMPrincipals", // incorrect representation, account id invalid length
"1234567890125:IAMPrincipal",
"arn:aws",
"arn:aws:logs", //lintignore:AWSAT005
"arn:aws:logs:region:*:*", //lintignore:AWSAT005
Expand Down
15 changes: 15 additions & 0 deletions website/docs/r/lakeformation_permissions.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@ The resulting permissions depend on whether the table had `IAMAllowedPrincipals`
| ---- | ---- |
| `SELECT` column wildcard (i.e., all columns) | `SELECT` on `"event"` (as expected) |

## `ALLIAMPrincipals` group

AllIAMPrincipals is a pseudo-entity group that acts like a Lake Formation principal. The group includes all IAMs in the account that is defined.

resource "aws_lakeformation_permissions" "example" {
permissions = ["SELECT"]
principal = "123456789012:IAMPrincipals"

table_with_columns {
database_name = aws_glue_catalog_table.example.database_name
name = aws_glue_catalog_table.example.name
column_names = ["event"]
}
}

## 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.
Expand Down

0 comments on commit 3d299e2

Please sign in to comment.