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 & Prerequisites
Why am I getting errors when trying to use Lake Formation permissions?
Lake Formation permissions are not in effect by default in AWS. You must first use aws.lakeformation.DataLakeSettings to remove IAMAllowedPrincipals default permissions and configure admins. Without this setup, using this resource will not secure your data and will result in errors.
What are the two mutually exclusive options for Lake Formation access control?
You must choose one: (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.
Common Errors & Pitfalls
What is IAMAllowedPrincipals and why does it conflict with this resource?
IAMAllowedPrincipals is a pseudo-entity group for backwards compatibility with AWS Glue that includes IAM users and roles allowed access by IAM policies. It conflicts with individual Lake Formation permissions and causes unexpected behavior, such as granting broader permissions than intended.
Why am I getting 'InvalidInputException: No permissions revoked' when destroying this resource?
This occurs when trying to revoke implicit permissions that administrators have. Implicit permissions cannot be revoked directly. Either avoid using principals with implicit permissions, or explicitly grant both permissions and permissionsWithGrantOption to overwrite implicit permissions before revoking.
Why am I getting 'InvalidInputException: Permissions modification is invalid' with columnNames?
This error occurs when granting permissions to an administrator on a table with columnNames, which narrows their implicit permissions. Instead, set wildcard to true and remove columnNames.
What happens if I use IAMAllowedPrincipals with individual Lake Formation permissions?
You’ll get unexpected permissions. For example, granting SELECT on a specific column may result in SELECT on all columns (wildcard) if IAMAllowedPrincipals permissions exist on the table.
Principal Configuration
Who should I NOT use as the principal?
Do not use Lake Formation administrators or the entity running the deployment (e.g., IAM role) as the principal. Administrators have implicit permissions that cause errors when this resource tries to revoke them. Manage administrator permissions using aws.lakeformation.DataLakeSettings instead.
What types of principals are supported?
Supported principals include IAM_ALLOWED_PRINCIPALS, IAM roles, users, groups, Federated Users, SAML groups and users, QuickSight groups, OUs, organizations, and AWS account IDs for cross-account permissions.
Permissions & Grant Options
What's the difference between permissions and permissionsWithGrantOption?
permissions are the permissions granted to the principal. permissionsWithGrantOption is a subset of permissions that the principal can pass to others (grant to other principals).
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.
How do I grant permissions using tag-based access control?
Use lfTagPolicy with resourceType and expressions containing key-value pairs. For example, you can grant permissions to resources tagged with Team: Sales and Environment: Dev or Production.
Resource Types & Immutability
What resource types can I grant permissions on?
You can grant permissions on dataLocation, database, lfTag, lfTagPolicy, table, tableWithColumns, dataCellsFilter, or catalogResource. You must specify exactly one of these.
Can I modify permissions after creating this resource?
No. The principal, permissions, permissionsWithGrantOption, and all resource type properties are immutable. You must destroy and recreate the resource to change them.

Using a different cloud?

Explore security guides for other cloud providers: