hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.7k stars 9.07k forks source link

Lakeformation table wildcard permissions failing #17300

Closed danielmitchell closed 3 years ago

danielmitchell commented 3 years ago

Community Note

Terraform CLI and Terraform AWS Provider Version

Terraform: v0.13.16 Provider: v3.25.0

Affected Resource(s)

Terraform Configuration Files

Please include all Terraform configurations required to reproduce the bug. Bug reports without a functional reproduction may be closed without investigation.

locals {
  db_name    = "test-db"
  table_name = "test-table"
  principal  = "arn:aws:iam::367740853123:role/TestRole"
}

resource "aws_glue_catalog_database" "test" {
  name = local.db_name
}

resource "aws_glue_catalog_table" "test" {
  name          = local.table_name
  database_name = aws_glue_catalog_database.test.name
}

resource "aws_lakeformation_permissions" "test" {
  principal                     = local.principal
  permissions                   = ["ALL", "ALTER", "DELETE", "DESCRIBE", "DROP", "INSERT", "SELECT"]
  permissions_with_grant_option = ["ALL", "ALTER", "DELETE", "DESCRIBE", "DROP", "INSERT", "SELECT"]
  table {
    database_name = aws_glue_catalog_table.test.database_name
    wildcard      = true
  }
}

Debug Output

Panic Output

Expected Behavior

Creating an aws_lakeformation_permissions resource with table.wildcard set to true should create permissions for all tables in the database

Actual Behavior

Creating an aws_lakeformation_permissions resource with table.wildcard set to true times out after 2 minutes and triggers an error AccessDeniedException: Resource does not exist or requester is not authorized to access requested permissions.

Creating the same permissions resource with named tables (wildcard = false) works correctly. Creating the same permissions with wildcard = true manually in the console using the same role also works correctly.

# module.infrastructure.aws_lakeformation_permissions.test will be created
  + resource "aws_lakeformation_permissions" "test" {
      + catalog_resource              = false
      + id                            = (known after apply)
      + permissions                   = [
          + "ALL",
          + "ALTER",
          + "DELETE",
          + "DESCRIBE",
          + "DROP",
          + "INSERT",
          + "SELECT",
        ]
      + permissions_with_grant_option = [
          + "ALL",
          + "ALTER",
          + "DELETE",
          + "DESCRIBE",
          + "DROP",
          + "INSERT",
          + "SELECT",
        ]
      + principal                     = "arn:aws:iam::367740853123:role/TestRole"

      + data_location {
          + arn        = (known after apply)
          + catalog_id = (known after apply)
        }

      + database {
          + catalog_id = (known after apply)
          + name       = (known after apply)
        }

      + table {
          + catalog_id    = (known after apply)
          + database_name = "test-db"
          + name          = (known after apply)
          + wildcard      = true
        }

      + table_with_columns {
          + catalog_id            = (known after apply)
          + column_names          = (known after apply)
          + database_name         = (known after apply)
          + excluded_column_names = (known after apply)
          + name                  = (known after apply)
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

module.infrastructure.aws_lakeformation_permissions.test: Creating...
module.infrastructure.aws_lakeformation_permissions.test: Still creating... [10s elapsed]
module.infrastructure.aws_lakeformation_permissions.test: Still creating... [20s elapsed]
module.infrastructure.aws_lakeformation_permissions.test: Still creating... [30s elapsed]
module.infrastructure.aws_lakeformation_permissions.test: Still creating... [40s elapsed]
module.infrastructure.aws_lakeformation_permissions.test: Still creating... [50s elapsed]
module.infrastructure.aws_lakeformation_permissions.test: Still creating... [1m0s elapsed]
module.infrastructure.aws_lakeformation_permissions.test: Still creating... [1m10s elapsed]
module.infrastructure.aws_lakeformation_permissions.test: Still creating... [1m20s elapsed]
module.infrastructure.aws_lakeformation_permissions.test: Still creating... [1m30s elapsed]
module.infrastructure.aws_lakeformation_permissions.test: Still creating... [1m40s elapsed]
module.infrastructure.aws_lakeformation_permissions.test: Still creating... [1m50s elapsed]

Error: error creating Lake Formation Permissions (input: {
  Permissions: [
    "ALL",
    "ALTER",
    "DELETE",
    "DESCRIBE",
    "DROP",
    "INSERT",
    "SELECT"
  ],
  PermissionsWithGrantOption: [
    "ALL",
    "ALTER",
    "DELETE",
    "DESCRIBE",
    "DROP",
    "INSERT",
    "SELECT"
  ],
  Principal: {
    DataLakePrincipalIdentifier: "arn:aws:iam::367740853123:role/TestRole"
  },
  Resource: {
    Table: {
      DatabaseName: "test-db",
      TableWildcard: {

      }
    }
  }
}): AccessDeniedException: Resource does not exist or requester is not authorized to access requested permissions.
        status code: 400, request id: 145ed1c9-0333-4543-875b-09bf992af8ae

Steps to Reproduce

  1. Run terraform apply

Important Factoids

References

bflad commented 3 years ago

Hi @danielmitchell 👋 Thank you for raising this and sorry you ran into trouble here. We did not explicitly have an end-to-end test for table wildcard = true, so I quickly created one similar to the others with the following code:

func testAccAWSLakeFormationPermissions_table_wildcard(t *testing.T) {
    rName := acctest.RandomWithPrefix("tf-acc-test")
    resourceName := "aws_lakeformation_permissions.test"
    databaseResourceName := "aws_glue_catalog_database.test"

    resource.Test(t, resource.TestCase{
        PreCheck:     func() { testAccPreCheck(t); testAccPartitionHasServicePreCheck(lakeformation.EndpointsID, t) },
        Providers:    testAccProviders,
        CheckDestroy: testAccCheckAWSLakeFormationPermissionsDestroy,
        Steps: []resource.TestStep{
            {
                Config: testAccAWSLakeFormationPermissionsConfig_table_wildcard(rName),
                Check: resource.ComposeTestCheckFunc(
                    testAccCheckAWSLakeFormationPermissionsExists(resourceName),
                    resource.TestCheckResourceAttr(resourceName, "table.#", "1"),
                    resource.TestCheckResourceAttrPair(resourceName, "table.0.database_name", databaseResourceName, "name"),
                    resource.TestCheckResourceAttr(resourceName, "table.0.wildcard", "true"),
                ),
            },
        },
    })
}

func testAccAWSLakeFormationPermissionsConfig_table_wildcard(rName string) string {
    return fmt.Sprintf(`
data "aws_partition" "current" {}

resource "aws_iam_role" "test" {
  name = %[1]q
  path = "/"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "glue.${data.aws_partition.current.dns_suffix}"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}

data "aws_caller_identity" "current" {}

resource "aws_glue_catalog_database" "test" {
  name = %[1]q
}

resource "aws_lakeformation_data_lake_settings" "test" {
  admins = [data.aws_caller_identity.current.arn]
}

resource "aws_lakeformation_permissions" "test" {
  permissions                   = ["ALL", "ALTER", "DELETE", "DESCRIBE", "DROP", "INSERT"]
  permissions_with_grant_option = ["ALL", "ALTER", "DELETE", "DESCRIBE", "DROP", "INSERT"]
  principal                     = aws_iam_role.test.arn

  table {
    database_name = aws_glue_catalog_database.test.name
    wildcard      = true
  }
}
`, rName)
}

Which passes okay (able to grant, read, and revoke the LakeFormation Permissions with no errors/differences):

        --- PASS: TestAccAWSLakeFormation_serial/Permissions/tableWildcard (25.69s)

I was however, able to reproduce your error when the principal for Terraform run was not in the LakeFormation Data Lake Settings administrators. My guess is that either your Terraform credentials need to be granted additional permissions (e.g. using the aws_lakeformation_data_lake_settings resource) or there is some other potential issue with LakeFormation that is likely best supported by the AWS service team by raising an AWS Support case. There is also a wide variety of documentation available in the LakeFormation Developer Guide on the complex permissions model.

danielmitchell commented 3 years ago

Thanks for investigating. I was able to resolve the issue by following your advice to ensure the Terraform user was an admin in the data lake settings :thumbsup:

Odd that it had permission for each of the individual tables when added separately (which it gets automatically as the database creator) but not when using the wildcard.

ghost commented 3 years ago

I'm going to lock this issue because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues.

If you feel this issue should be reopened, we encourage creating a new issue linking back to this one for added context. Thanks!