Skip to content

Commit

Permalink
aws_lakeformation_permissions support IAMPrincipals special principal
Browse files Browse the repository at this point in the history
  • Loading branch information
nickdelnano committed Jul 30, 2024
1 parent ba58557 commit 32819ff
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 4 deletions.
189 changes: 189 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", "false"),
resource.TestCheckResourceAttr(resourceName, "database.#", "1"),
resource.TestCheckResourceAttrPair(resourceName, "database.0.name", dbName, "name"),
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,37 @@ func testAccPermissions_tableIAMAllowed(t *testing.T) {
})
}

// TODO remove capital T on acceptance test name
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", "false"),
resource.TestCheckResourceAttr(resourceName, "table.#", "1"),
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", 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 @@ -809,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["principal"]; ok && v != "" {
expectedPrincipalValue := acctest.AccountID() + ":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 @@ -1334,6 +1415,7 @@ resource "aws_lakeformation_permissions" "test" {

func testAccPermissionsConfig_databaseIAMAllowed(rName string) string {
return fmt.Sprintf(`
data "aws_partition" "current" {}
data "aws_caller_identity" "current" {}
Expand Down Expand Up @@ -1386,6 +1468,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 @@ -1827,6 +1964,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
9 changes: 9 additions & 0 deletions internal/service/lakeformation/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ func validPrincipal(v interface{}, k string) (ws []string, errors []error) {
return ws, errors
}

// IAMPrincipals special grant has format {account_id}:IAMPrincipals
if len(value) == 26 && value[13:26] == "IAMPrincipals" && value[12] == ':' {
return ws, errors
wsAccount, errorsAccount := verify.ValidAccountID(value[0:12], k)
if len(errorsAccount) == 0 {
return wsAccount, errorsAccount
}
}

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

validNames := []string{
"IAM_ALLOWED_PRINCIPALS", // Special principal
"123456789012", // 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)
"123456789012", // 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-us-gov:iam:us-west-2:357342307427:role/tf-acc-test-3217321001347236965", // lintignore:AWSAT003,AWSAT005 // Non-global IAM Role?
Expand All @@ -42,7 +43,9 @@ 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
"arn:aws",
"arn:aws:logs", //lintignore:AWSAT005
"arn:aws:logs:region:*:*", //lintignore:AWSAT005
Expand Down

0 comments on commit 32819ff

Please sign in to comment.