Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aws_lakeformation_permissions support AllIAMPrincipals special principal #38600

Merged
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,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

best viewed with "hide whitespace" enabled

"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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

best viewed with "hide whitespace" enabled

"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"]
}
}

Comment on lines +90 to +104
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

## 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
Loading