Guide · AWS Lake Formation
MCP Server AWS Lake Formation — RegisterResourceCommand, data lake settings, IAM
AWS Lake Formation is a managed service that centralizes access control for data lakes built on S3 and AWS Glue. Instead of managing S3 bucket policies and Glue resource policies separately, you register S3 locations with Lake Formation and then grant column-, row-, and table-level permissions through a single API. From an MCP server you use the @aws-sdk/client-lakeformation package. The three concepts that trip up first-time callers are the data lake settings (which IAM principals are admins and what default permissions apply to new databases), the registered resource (which S3 paths Lake Formation governs — unregistered paths fall back to S3 ACLs), and the service-linked role (AWSServiceRoleForLakeFormationDataAccess — Lake Formation uses it to vend temporary credentials to data-plane callers like Athena and Glue).
TL;DR
Install @aws-sdk/client-lakeformation. Call GetDataLakeSettingsCommand to read who the admins are. Call RegisterResourceCommand with ResourceArn: 's3://my-bucket' and UseServiceLinkedRole: true to bring an S3 bucket under Lake Formation governance. After registration, all Glue/Athena access to that bucket is mediated by Lake Formation permissions — not S3 bucket policies. Grant permissions via GrantPermissionsCommand (covered in the permissions guide).
Installing the SDK and creating the client
Lake Formation uses a dedicated package. Install it alongside @aws-sdk/client-glue and @aws-sdk/client-s3 if you manage the full data lake stack from a single MCP server.
npm install @aws-sdk/client-lakeformation
Construct the client once at module initialization — it is stateless and safe to share across concurrent tool calls.
import { LakeFormationClient } from '@aws-sdk/client-lakeformation';
const lakeFormation = new LakeFormationClient({
region: process.env.AWS_REGION ?? 'us-east-1',
// Credentials resolve from the standard chain:
// 1. AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY env vars
// 2. ~/.aws/credentials
// 3. ECS task role / Lambda execution role / EC2 instance profile (preferred)
});
Lake Formation is available in all AWS regions that support Glue and Athena. Cross-region access is not supported — register resources and grant permissions in the same region as your Glue data catalog.
What Lake Formation governs
Lake Formation adds a permission layer on top of AWS Glue Data Catalog and S3. When an S3 location is registered with Lake Formation, all services that use the Glue Data Catalog (Athena, Glue ETL, EMR, Redshift Spectrum) must have Lake Formation permissions to read or write data in that location — S3 bucket policies alone are no longer sufficient.
| Resource type | Governed by | Notes |
|---|---|---|
| Registered S3 location | Lake Formation data-plane + IAM | Lake Formation vends temp credentials; S3 policy still applies as a floor |
| Unregistered S3 location | IAM + S3 bucket policy only | Lake Formation permissions not checked for unregistered paths |
| Glue database | Lake Formation catalog permissions | CREATE_TABLE, DESCRIBE, ALTER, DROP, SUPER |
| Glue table | Lake Formation catalog + data-plane | SELECT, INSERT, DELETE, DESCRIBE, ALTER, DROP, SUPER |
| Table columns | Lake Formation column-level security | TableWithColumns resource with ColumnNames or ColumnWildcard |
The hybrid access mode (HybridAccessEnabled: true on RegisterResourceCommand) lets you transition gradually — both Lake Formation permissions and IAM/S3 policies are evaluated, and access is granted if either allows it. Set HybridAccessEnabled: false (the default) for strict Lake Formation-only governance.
Getting and updating data lake settings
GetDataLakeSettingsCommand returns the account-level settings: which IAM principals are data lake admins (they bypass permission checks), what permissions are granted by default when a new database or table is created (useful for locking these down to prevent permission drift), and whether cross-account sharing is enabled.
import {
LakeFormationClient,
GetDataLakeSettingsCommand,
PutDataLakeSettingsCommand,
type DataLakeSettings,
} from '@aws-sdk/client-lakeformation';
const lakeFormation = new LakeFormationClient({ region: process.env.AWS_REGION ?? 'us-east-1' });
export async function getDataLakeSettings() {
const response = await lakeFormation.send(
new GetDataLakeSettingsCommand({})
);
return response.DataLakeSettings;
}
// Add a new IAM role as a data lake admin
export async function addDataLakeAdmin(roleArn: string): Promise<void> {
// Always read first — PutDataLakeSettings replaces the entire settings object
const current = await lakeFormation.send(new GetDataLakeSettingsCommand({}));
const existing = current.DataLakeSettings ?? {};
const admins = existing.DataLakeAdmins ?? [];
// Avoid duplicates
if (admins.some(a => a.DataLakePrincipalIdentifier === roleArn)) return;
admins.push({ DataLakePrincipalIdentifier: roleArn });
await lakeFormation.send(new PutDataLakeSettingsCommand({
DataLakeSettings: {
...existing,
DataLakeAdmins: admins,
},
}));
}
PutDataLakeSettingsCommand is a full replacement — it replaces the entire settings object. Always read the current settings with GetDataLakeSettingsCommand first and merge your changes before writing, or you will accidentally remove existing admins.
The CreateDatabaseDefaultPermissions and CreateTableDefaultPermissions fields control what permissions are automatically granted to the IAM principal that creates a new database or table. The default is ALL for the creator — override this to enforce least-privilege from creation:
// Lock down default permissions so new tables don't auto-grant SELECT to creators
export async function lockDownDefaults(): Promise<void> {
const current = await lakeFormation.send(new GetDataLakeSettingsCommand({}));
const existing = current.DataLakeSettings ?? {};
await lakeFormation.send(new PutDataLakeSettingsCommand({
DataLakeSettings: {
...existing,
// Remove default grants — admins must explicitly grant permissions
CreateDatabaseDefaultPermissions: [],
CreateTableDefaultPermissions: [],
},
}));
}
Registering an S3 location with RegisterResourceCommand
RegisterResourceCommand brings an S3 URI under Lake Formation governance. After registration, calls to Athena, Glue, and EMR that read or write data in that path must hold Lake Formation DATA_LOCATION_ACCESS permission in addition to Glue catalog permissions.
import {
RegisterResourceCommand,
type RegisterResourceCommandInput,
} from '@aws-sdk/client-lakeformation';
export async function registerS3Bucket(bucketArn: string): Promise<void> {
const input: RegisterResourceCommandInput = {
// S3 ARN — must be a bucket or prefix ARN, not an object ARN
ResourceArn: bucketArn, // e.g. 'arn:aws:s3:::my-data-lake-bucket'
// UseServiceLinkedRole: true creates/uses AWSServiceRoleForLakeFormationDataAccess
// Set false only if you need to specify a custom RoleArn
UseServiceLinkedRole: true,
// HybridAccessEnabled: true = both LF and IAM/S3 policies checked (OR logic)
// HybridAccessEnabled: false = only LF permissions checked (default, strict)
HybridAccessEnabled: false,
};
await lakeFormation.send(new RegisterResourceCommand(input));
}
// Register with a specific IAM role instead of the service-linked role
export async function registerWithCustomRole(
bucketArn: string,
roleArn: string,
): Promise<void> {
await lakeFormation.send(new RegisterResourceCommand({
ResourceArn: bucketArn,
UseServiceLinkedRole: false,
RoleArn: roleArn, // This role must have s3:GetObject and s3:PutObject on the bucket
HybridAccessEnabled: false,
}));
}
The service-linked role (AWSServiceRoleForLakeFormationDataAccess) is created automatically on first registration if it does not exist. If your MCP server role does not have iam:CreateServiceLinkedRole permission for lakeformation.amazonaws.com, pre-create the role in advance and use UseServiceLinkedRole: true once it exists — Lake Formation will find the existing role rather than creating a new one.
You can register a prefix (arn:aws:s3:::my-bucket/datalake/) rather than the entire bucket. Registering sub-paths lets you use Lake Formation for some prefixes while other prefixes in the same bucket remain under standard S3 ACL control.
Listing registered resources
Use ListResourcesCommand to enumerate all S3 locations registered in the current account and region. The response is paginated — iterate using NextToken.
import {
ListResourcesCommand,
DescribeResourceCommand,
} from '@aws-sdk/client-lakeformation';
export async function listRegisteredResources() {
const resources = [];
let nextToken: string | undefined;
do {
const response = await lakeFormation.send(
new ListResourcesCommand({ NextToken: nextToken })
);
resources.push(...(response.ResourceInfoList ?? []));
nextToken = response.NextToken;
} while (nextToken);
return resources.map(r => ({
resourceArn: r.ResourceArn,
roleArn: r.RoleArn,
lastModified: r.LastModified,
}));
}
export async function describeResource(resourceArn: string) {
const response = await lakeFormation.send(
new DescribeResourceCommand({ ResourceArn: resourceArn })
);
return response.ResourceInfo;
}
Deregistering a resource
Use DeregisterResourceCommand to remove an S3 location from Lake Formation governance. After deregistration, access reverts to IAM and S3 bucket policies. All Lake Formation permissions that referenced DataLocation resources under this path become inert — they are not automatically removed but have no effect.
import { DeregisterResourceCommand } from '@aws-sdk/client-lakeformation';
export async function deregisterS3Bucket(resourceArn: string): Promise<void> {
await lakeFormation.send(
new DeregisterResourceCommand({ ResourceArn: resourceArn })
);
// After deregistration, Lake Formation permissions on this path
// no longer gate access — standard IAM/S3 policies take effect again.
}
Deregistering a resource that has active Glue tables pointing to it does not delete the Glue catalog entries — it just stops Lake Formation from mediating access. If Glue jobs or Athena queries relied on the Lake Formation credential vending mechanism, they will fail until you either re-register the resource or switch them to direct IAM/S3 credentials.
IAM: permissions for the MCP server role
The MCP server's IAM role needs permissions to call the Lake Formation control-plane APIs. Data lake admins have implicit access to all resources, but for a least-privilege server role, scope permissions to only the operations you need.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "LakeFormationSettings",
"Effect": "Allow",
"Action": [
"lakeformation:GetDataLakeSettings",
"lakeformation:PutDataLakeSettings"
],
"Resource": "*"
},
{
"Sid": "LakeFormationResources",
"Effect": "Allow",
"Action": [
"lakeformation:RegisterResource",
"lakeformation:DeregisterResource",
"lakeformation:ListResources",
"lakeformation:DescribeResource",
"lakeformation:UpdateResource"
],
"Resource": "*"
},
{
"Sid": "LakeFormationPermissions",
"Effect": "Allow",
"Action": [
"lakeformation:GrantPermissions",
"lakeformation:BatchGrantPermissions",
"lakeformation:RevokePermissions",
"lakeformation:BatchRevokePermissions",
"lakeformation:ListPermissions"
],
"Resource": "*"
},
{
"Sid": "LakeFormationTags",
"Effect": "Allow",
"Action": [
"lakeformation:CreateLFTag",
"lakeformation:UpdateLFTag",
"lakeformation:DeleteLFTag",
"lakeformation:GetLFTag",
"lakeformation:ListLFTags",
"lakeformation:AddLFTagsToResource",
"lakeformation:RemoveLFTagsFromResource",
"lakeformation:GetResourceLFTags"
],
"Resource": "*"
},
{
"Sid": "GlueDataCatalogRead",
"Effect": "Allow",
"Action": [
"glue:GetDatabase",
"glue:GetDatabases",
"glue:GetTable",
"glue:GetTables"
],
"Resource": "*"
},
{
"Sid": "CreateServiceLinkedRole",
"Effect": "Allow",
"Action": "iam:CreateServiceLinkedRole",
"Resource": "arn:aws:iam::*:role/aws-service-role/lakeformation.amazonaws.com/*",
"Condition": {
"StringEquals": {
"iam:AWSServiceName": "lakeformation.amazonaws.com"
}
}
}
]
}
If you pre-create the service-linked role before the first RegisterResourceCommand call, you can remove the iam:CreateServiceLinkedRole statement. The glue:GetDatabase and glue:GetTable permissions are needed if your MCP server resolves database or table ARNs from names before calling Lake Formation APIs.
Error handling reference
| Error | Cause | Fix |
|---|---|---|
AlreadyExistsException | RegisterResourceCommand on a path already registered | Call DescribeResourceCommand first to check; skip registration if already registered |
EntityNotFoundException | DeregisterResourceCommand or DescribeResourceCommand on an ARN not registered | Check ListResourcesCommand output before deregistering |
AccessDeniedException | Missing lakeformation:* or iam:CreateServiceLinkedRole permission | Add the missing action to the MCP server IAM policy |
InvalidInputException | ResourceArn is not a valid S3 ARN, or UseServiceLinkedRole=false with no RoleArn | Use format arn:aws:s3:::bucket-name or arn:aws:s3:::bucket-name/prefix/ |
InternalServiceException | Transient AWS service error | Retry with exponential backoff |
OperationTimeoutException | Request timed out waiting for Lake Formation service | Retry — Lake Formation has a higher latency than S3; use 5s timeout with 3 retries |
Related pages
- Lake Formation permissions — GrantPermissionsCommand, resource types, column security — granting SELECT/INSERT/DELETE on databases, tables, and columns
- Lake Formation LF-tags — TBAC, CreateLFTagCommand, tag expressions — tag-based access control for scalable permission management
- Lake Formation governed tables — StartTransactionCommand, UpdateTableObjects — ACID transactions on data lake objects
- AWS Glue Data Catalog for MCP servers
- Athena federated queries for MCP servers