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 typeGoverned byNotes
Registered S3 locationLake Formation data-plane + IAMLake Formation vends temp credentials; S3 policy still applies as a floor
Unregistered S3 locationIAM + S3 bucket policy onlyLake Formation permissions not checked for unregistered paths
Glue databaseLake Formation catalog permissionsCREATE_TABLE, DESCRIBE, ALTER, DROP, SUPER
Glue tableLake Formation catalog + data-planeSELECT, INSERT, DELETE, DESCRIBE, ALTER, DROP, SUPER
Table columnsLake Formation column-level securityTableWithColumns 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

ErrorCauseFix
AlreadyExistsExceptionRegisterResourceCommand on a path already registeredCall DescribeResourceCommand first to check; skip registration if already registered
EntityNotFoundExceptionDeregisterResourceCommand or DescribeResourceCommand on an ARN not registeredCheck ListResourcesCommand output before deregistering
AccessDeniedExceptionMissing lakeformation:* or iam:CreateServiceLinkedRole permissionAdd the missing action to the MCP server IAM policy
InvalidInputExceptionResourceArn is not a valid S3 ARN, or UseServiceLinkedRole=false with no RoleArnUse format arn:aws:s3:::bucket-name or arn:aws:s3:::bucket-name/prefix/
InternalServiceExceptionTransient AWS service errorRetry with exponential backoff
OperationTimeoutExceptionRequest timed out waiting for Lake Formation serviceRetry — Lake Formation has a higher latency than S3; use 5s timeout with 3 retries

Related pages