Configure AWS Lake Formation Permissions

The aws:lakeformation/permissions:Permissions resource, part of the Pulumi AWS provider, grants Lake Formation permissions to principals for accessing Data Catalog metadata and underlying S3 data. This guide focuses on three capabilities: S3 data location access, database-level permissions, and tag-based access control.

Lake Formation permissions reference existing IAM roles, Glue databases, registered S3 resources, and LF-tags. The examples are intentionally small. Combine them with your own IAM roles, Data Catalog resources, and Lake Formation configuration.

Grant access to S3 data locations

Data lake workflows often begin by granting principals access to S3 locations registered with Lake Formation, enabling them to read or write data through Lake Formation’s permission model.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.lakeformation.Permissions("example", {
    principal: workflowRole.arn,
    permissions: ["DATA_LOCATION_ACCESS"],
    dataLocation: {
        arn: exampleAwsLakeformationResource.arn,
    },
});
import pulumi
import pulumi_aws as aws

example = aws.lakeformation.Permissions("example",
    principal=workflow_role["arn"],
    permissions=["DATA_LOCATION_ACCESS"],
    data_location={
        "arn": example_aws_lakeformation_resource["arn"],
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lakeformation"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := lakeformation.NewPermissions(ctx, "example", &lakeformation.PermissionsArgs{
			Principal: pulumi.Any(workflowRole.Arn),
			Permissions: pulumi.StringArray{
				pulumi.String("DATA_LOCATION_ACCESS"),
			},
			DataLocation: &lakeformation.PermissionsDataLocationArgs{
				Arn: pulumi.Any(exampleAwsLakeformationResource.Arn),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.LakeFormation.Permissions("example", new()
    {
        Principal = workflowRole.Arn,
        PermissionDetails = new[]
        {
            "DATA_LOCATION_ACCESS",
        },
        DataLocation = new Aws.LakeFormation.Inputs.PermissionsDataLocationArgs
        {
            Arn = exampleAwsLakeformationResource.Arn,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lakeformation.Permissions;
import com.pulumi.aws.lakeformation.PermissionsArgs;
import com.pulumi.aws.lakeformation.inputs.PermissionsDataLocationArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new Permissions("example", PermissionsArgs.builder()
            .principal(workflowRole.arn())
            .permissions("DATA_LOCATION_ACCESS")
            .dataLocation(PermissionsDataLocationArgs.builder()
                .arn(exampleAwsLakeformationResource.arn())
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:lakeformation:Permissions
    properties:
      principal: ${workflowRole.arn}
      permissions:
        - DATA_LOCATION_ACCESS
      dataLocation:
        arn: ${exampleAwsLakeformationResource.arn}

The dataLocation block references an S3 resource registered with Lake Formation. The DATA_LOCATION_ACCESS permission allows the principal to access data at that location. The principal property specifies the IAM role ARN that receives the permission.

Grant database-level permissions

Teams building data pipelines grant database permissions to roles that create tables, modify schemas, or manage database objects within the Glue Data Catalog.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.lakeformation.Permissions("example", {
    principal: workflowRole.arn,
    permissions: [
        "CREATE_TABLE",
        "ALTER",
        "DROP",
    ],
    database: {
        name: exampleAwsGlueCatalogDatabase.name,
        catalogId: "110376042874",
    },
});
import pulumi
import pulumi_aws as aws

example = aws.lakeformation.Permissions("example",
    principal=workflow_role["arn"],
    permissions=[
        "CREATE_TABLE",
        "ALTER",
        "DROP",
    ],
    database={
        "name": example_aws_glue_catalog_database["name"],
        "catalog_id": "110376042874",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lakeformation"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := lakeformation.NewPermissions(ctx, "example", &lakeformation.PermissionsArgs{
			Principal: pulumi.Any(workflowRole.Arn),
			Permissions: pulumi.StringArray{
				pulumi.String("CREATE_TABLE"),
				pulumi.String("ALTER"),
				pulumi.String("DROP"),
			},
			Database: &lakeformation.PermissionsDatabaseArgs{
				Name:      pulumi.Any(exampleAwsGlueCatalogDatabase.Name),
				CatalogId: pulumi.String("110376042874"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.LakeFormation.Permissions("example", new()
    {
        Principal = workflowRole.Arn,
        PermissionDetails = new[]
        {
            "CREATE_TABLE",
            "ALTER",
            "DROP",
        },
        Database = new Aws.LakeFormation.Inputs.PermissionsDatabaseArgs
        {
            Name = exampleAwsGlueCatalogDatabase.Name,
            CatalogId = "110376042874",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lakeformation.Permissions;
import com.pulumi.aws.lakeformation.PermissionsArgs;
import com.pulumi.aws.lakeformation.inputs.PermissionsDatabaseArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new Permissions("example", PermissionsArgs.builder()
            .principal(workflowRole.arn())
            .permissions(            
                "CREATE_TABLE",
                "ALTER",
                "DROP")
            .database(PermissionsDatabaseArgs.builder()
                .name(exampleAwsGlueCatalogDatabase.name())
                .catalogId("110376042874")
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:lakeformation:Permissions
    properties:
      principal: ${workflowRole.arn}
      permissions:
        - CREATE_TABLE
        - ALTER
        - DROP
      database:
        name: ${exampleAwsGlueCatalogDatabase.name}
        catalogId: '110376042874'

The database block identifies the Glue Catalog database by name and catalog ID. The permissions array specifies what operations the principal can perform: CREATE_TABLE for adding tables, ALTER for modifying schemas, and DROP for removing objects. These permissions apply to the database and its contents.

Grant permissions using LF-tag policies

Organizations managing many databases and tables use LF-tag policies to grant permissions based on metadata tags rather than individual resource ARNs, simplifying access control at scale.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const test = new aws.lakeformation.Permissions("test", {
    principal: salesRole.arn,
    permissions: [
        "CREATE_TABLE",
        "ALTER",
        "DROP",
    ],
    lfTagPolicy: {
        resourceType: "DATABASE",
        expressions: [
            {
                key: "Team",
                values: ["Sales"],
            },
            {
                key: "Environment",
                values: [
                    "Dev",
                    "Production",
                ],
            },
        ],
    },
});
import pulumi
import pulumi_aws as aws

test = aws.lakeformation.Permissions("test",
    principal=sales_role["arn"],
    permissions=[
        "CREATE_TABLE",
        "ALTER",
        "DROP",
    ],
    lf_tag_policy={
        "resource_type": "DATABASE",
        "expressions": [
            {
                "key": "Team",
                "values": ["Sales"],
            },
            {
                "key": "Environment",
                "values": [
                    "Dev",
                    "Production",
                ],
            },
        ],
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lakeformation"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := lakeformation.NewPermissions(ctx, "test", &lakeformation.PermissionsArgs{
			Principal: pulumi.Any(salesRole.Arn),
			Permissions: pulumi.StringArray{
				pulumi.String("CREATE_TABLE"),
				pulumi.String("ALTER"),
				pulumi.String("DROP"),
			},
			LfTagPolicy: &lakeformation.PermissionsLfTagPolicyArgs{
				ResourceType: pulumi.String("DATABASE"),
				Expressions: lakeformation.PermissionsLfTagPolicyExpressionArray{
					&lakeformation.PermissionsLfTagPolicyExpressionArgs{
						Key: pulumi.String("Team"),
						Values: pulumi.StringArray{
							pulumi.String("Sales"),
						},
					},
					&lakeformation.PermissionsLfTagPolicyExpressionArgs{
						Key: pulumi.String("Environment"),
						Values: pulumi.StringArray{
							pulumi.String("Dev"),
							pulumi.String("Production"),
						},
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var test = new Aws.LakeFormation.Permissions("test", new()
    {
        Principal = salesRole.Arn,
        PermissionDetails = new[]
        {
            "CREATE_TABLE",
            "ALTER",
            "DROP",
        },
        LfTagPolicy = new Aws.LakeFormation.Inputs.PermissionsLfTagPolicyArgs
        {
            ResourceType = "DATABASE",
            Expressions = new[]
            {
                new Aws.LakeFormation.Inputs.PermissionsLfTagPolicyExpressionArgs
                {
                    Key = "Team",
                    Values = new[]
                    {
                        "Sales",
                    },
                },
                new Aws.LakeFormation.Inputs.PermissionsLfTagPolicyExpressionArgs
                {
                    Key = "Environment",
                    Values = new[]
                    {
                        "Dev",
                        "Production",
                    },
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lakeformation.Permissions;
import com.pulumi.aws.lakeformation.PermissionsArgs;
import com.pulumi.aws.lakeformation.inputs.PermissionsLfTagPolicyArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var test = new Permissions("test", PermissionsArgs.builder()
            .principal(salesRole.arn())
            .permissions(            
                "CREATE_TABLE",
                "ALTER",
                "DROP")
            .lfTagPolicy(PermissionsLfTagPolicyArgs.builder()
                .resourceType("DATABASE")
                .expressions(                
                    PermissionsLfTagPolicyExpressionArgs.builder()
                        .key("Team")
                        .values("Sales")
                        .build(),
                    PermissionsLfTagPolicyExpressionArgs.builder()
                        .key("Environment")
                        .values(                        
                            "Dev",
                            "Production")
                        .build())
                .build())
            .build());

    }
}
resources:
  test:
    type: aws:lakeformation:Permissions
    properties:
      principal: ${salesRole.arn}
      permissions:
        - CREATE_TABLE
        - ALTER
        - DROP
      lfTagPolicy:
        resourceType: DATABASE
        expressions:
          - key: Team
            values:
              - Sales
          - key: Environment
            values:
              - Dev
              - Production

The lfTagPolicy block grants permissions to all resources matching the specified tag expressions. The resourceType property sets whether the policy applies to databases, tables, or other resource types. The expressions array defines tag key-value pairs that resources must match. This configuration grants permissions to all databases tagged with Team=Sales and Environment matching either Dev or Production.

Beyond these examples

These snippets focus on specific Lake Formation permission features: S3 data location access, database-level permissions, and tag-based access control. They’re intentionally minimal rather than full data lake access control configurations.

The examples reference pre-existing infrastructure such as IAM roles for principals, Glue Catalog databases, S3 resources registered with Lake Formation, and LF-tags attached to resources. They focus on granting permissions rather than provisioning the underlying resources.

To keep things focused, common permission patterns are omitted, including:

  • Grant options (permissionsWithGrantOption)
  • Table and column-level permissions
  • Data cells filter permissions
  • Catalog-wide permissions (catalogResource)
  • IAMAllowedPrincipals configuration and removal

These omissions are intentional: the goal is to illustrate how each permission type is wired, not provide drop-in access control modules. See the Lake Formation Permissions resource reference for all available configuration options.

Let's configure AWS Lake Formation Permissions

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Setup & Default Behavior
Why aren't my Lake Formation permissions working?
Lake Formation permissions are not in effect by default within AWS. You must change the default security settings using aws.lakeformation.DataLakeSettings and remove existing IAMAllowedPrincipals permissions, or your data will not be secured and you’ll encounter errors.
What are my two options for using Lake Formation permissions?
You must choose ONE mutually exclusive option: (1) Use aws.lakeformation.Permissions with aws.lakeformation.DataLakeSettings to remove IAMAllowedPrincipals, OR (2) Use IAMAllowedPrincipals without aws.lakeformation.Permissions. Combining them causes unexpected behavior and errors.
How do I remove IAMAllowedPrincipals permissions?
Use the AWS Lake Formation Console or AWS CLI to remove existing IAMAllowedPrincipals permissions. Then configure aws.lakeformation.DataLakeSettings to clear createDatabaseDefaultPermissions and createTableDefaultPermissions.
Principal & Administrator Permissions
Why shouldn't I use a Lake Formation administrator as the principal?
Administrators have implicit permissions that cannot be managed with this resource. Using an admin as principal will cause errors when the resource is destroyed because you cannot revoke implicit permissions. Manage administrator rights using aws.lakeformation.DataLakeSettings instead.
Can the entity running my deployment be a Lake Formation administrator?
Yes, the entity running the deployment (e.g., IAM role) will likely need to be a Lake Formation administrator. However, this entity should NOT be the principal receiving permissions in this resource, since it already has implicit permissions.
IAMAllowedPrincipals & Pseudo-Groups
What is IAMAllowedPrincipals?
IAMAllowedPrincipals is a pseudo-entity group for backwards compatibility with AWS Glue. It includes any IAM users and roles allowed access to your Data Catalog resources by your IAM policies. It conflicts with individual Lake Formation permissions.
What happens if I mix IAMAllowedPrincipals and individual permissions?
Combining them results in unexpected permissions. For example, granting SELECT on a specific column will grant SELECT on all columns (wildcard) if the table has IAMAllowedPrincipals permissions, instead of just the specified column.
What is AllIAMPrincipals and how do I use it?
AllIAMPrincipals is a pseudo-entity group that includes all IAMs in the account. Use the format 123456789012:IAMPrincipals as the principal value to grant permissions to all IAM entities in account 123456789012.
Common Errors
Why am I getting 'InvalidInputException: No permissions revoked' when destroying this resource?
This error occurs when the resource attempts to revoke implicit permissions granted to administrators, database creators, or table creators. You cannot revoke implicit permissions directly. Either avoid using this resource with principals that have implicit permissions, or grant explicit permissions and permissionsWithGrantOption to overwrite the implicit ones.
Why am I getting 'InvalidInputException: Permissions modification is invalid' for table columns?
This error occurs when granting permissions on table columns with columnNames to a principal who is also an administrator. This narrows their implicit permissions, which is not allowed. Instead, set wildcard to true and remove columnNames.
Configuration & Resource Types
What permissions can I grant with this resource?
Valid permissions include: ALL, ALTER, ASSOCIATE, CREATE_DATABASE, CREATE_TABLE, DATA_LOCATION_ACCESS, DELETE, DESCRIBE, DROP, INSERT, and SELECT. Use permissionsWithGrantOption to specify which permissions the principal can pass to others.
Can I change the resource type after creating permissions?
No, all resource types (dataLocation, database, lfTag, lfTagPolicy, table, tableWithColumns) are immutable after creation. You must destroy and recreate the resource to change the resource type.

Using a different cloud?

Explore security guides for other cloud providers: