AWS Lake Formation
データレイクのセキュリティ・ガバナンス・アクセス制御基盤
AWS Lake Formation は、Amazon S3 データレイクのセキュリティ・アクセス制御・ガバナンスをフルマネージドで提供するサービス です。Glue Data Catalog・Athena・EMR・Redshift Spectrum が参照するデータへの列レベル・行レベル・セルレベルのきめ細かいアクセス制御(FGAC)を一元管理し、エンタープライズデータレイクのガバナンス基盤となります。このページでは Lake Formation の概念・アーキテクチャ・実装・近年の動向を体系的に解説します。
このページの目的
このページでは以下を対象としています。
- 初心者向け:Lake Formation とは何か、IAM との違いを学びたい方
- データエンジニア向け:Glue Catalog と Lake Formation の連携・権限設計
- セキュリティアーキテクト向け:細粒度アクセス制御・RBAC・ABAC の実装
- コンプライアンス向け:データ分類・監査ログ・規制対応
- 意思決定者向け:Apache Ranger・Unity Catalog・Snowflake RBAC との比較
概要と本質
初心者向けメモ:Lake Formation は「IAM では実現できない列・行レベルのアクセス制御を、データレイク全体で一元管理するサービス」です。
従来:IAM ポリシー
├── S3 バケット全体:アクセス OK / NG
└── 欠点:テーブル・列・行のレベルで制御不可
Lake Formation:
├── テーブル:SELECT 可能
├── 列:email・phone は見えない
├── 行:region='Japan' のみ表示
└── 統一管理:Athena・EMR・Redshift に自動適用
graph TB
subgraph DataSources["データソース"]
S3["Amazon S3<br/>データレイク"]
RDS["RDS/DynamoDB<br/>Redshift"]
end
subgraph Catalog["メタデータ管理"]
Glue["Glue Data Catalog<br/>スキーマ定義"]
end
subgraph Governance["ガバナンス"]
LF["Lake Formation<br/>アクセス制御"]
end
subgraph Services["分析サービス"]
Athena["Amazon Athena"]
EMR["Amazon EMR"]
Redshift["Redshift<br/>Spectrum"]
end
DataSources -->|メタデータ| Catalog
Catalog -->|権限管理| Governance
Governance -->|統一制御| Services
課題解決
| 課題 |
従来のアプローチ |
Lake Formation による解決 |
| 列・行レベルアクセス |
IAM では実現不可 |
Lake Formation で 統一制御 |
| 複数サービスへの権限適用 |
Athena・EMR・Redshift に個別設定 |
一度の設定で全サービスに自動適用 |
| PII 保護 |
オンプレミス DB でのマスク |
Lake Formation の行・列フィルター自動適用 |
| クロスアカウント共有 |
S3 バケットポリシー複雑化 |
Lake Formation クロスアカウント権限で簡潔 |
| GDPR/HIPAA 準拠 |
手作業でアクセス制御 |
Lake Formation 監査ログで自動追跡 |
| データメッシュ実現 |
部門別 DB 管理が分散・複雑 |
Lake Formation + DataZone で データ民主化 |
主な特徴
1. 階層的アクセス制御(Hierarchical Permissions)
Database
├── Table_A
│ ├── Column_1 (SELECT)
│ └── Column_2(SELECT + INSERT)
└── Table_B
├── Column_X (SELECT)
└── Column_Y(見えない)
→ 1つのテーブルで複数の列に異なる権限を適用可能
2. データフィルター(Row-Level & Cell-Level)
CREATE ROW FILTER japan_filter ON table_orders AS "region = 'Japan'"
GRANT SELECT ON table_orders WITH ROW FILTER japan_filter TO japan_team_role;
GRANT SELECT (order_id, amount) ON table_orders
WITH ROW FILTER japan_filter
TO analyst_role;
タグ付与:
Classification: [PII, Public, Confidential, Internal]
Department: [Finance, Sales, Engineering, Marketing]
DataSensitivity: [Low, Medium, High, Critical]
権限設定:
Finance_Role:
→ Classification=Internal AND Department=Finance
→ すべての関連データへのアクセス自動付与
4. Hybrid Access Mode(段階的導入)
段階 1:IAM オンリー(既存状態)
→ 全テーブル・全ユーザーは IAM のみで動作
段階 2:Hybrid Mode
→ finance_table:Lake Formation で制御開始
→ その他テーブル:IAM で継続
段階 3:Lake Formation オンリー
→ すべてのテーブル・権限を Lake Formation 管理
アーキテクチャ
graph TB
subgraph Sources["データソース"]
S3["Amazon S3<br/>Raw / Processed Data"]
RDS["RDS<br/>PostgreSQL/MySQL"]
DDB["DynamoDB"]
Redshift["Redshift"]
end
subgraph LFCore["Lake Formation"]
Admin["データレイク管理者"]
Perms["権限管理<br/>テーブル・列・行"]
Tags["LF-TAGS<br/>タグベース RBAC"]
Audit["監査ログ<br/>CloudTrail"]
end
subgraph Catalog["Glue Data Catalog"]
Metadata["メタデータ<br/>スキーマ・パーティション"]
end
subgraph Services["分析サービス"]
Athena["Athena"]
EMR["EMR"]
Spectrum["Redshift Spectrum"]
SageMaker["SageMaker"]
end
subgraph Users["ユーザー"]
DataEng["データエンジニア"]
Analyst["アナリスト"]
DataSci["データサイエンティスト"]
end
Sources -->|Glue Crawler| Metadata
Metadata -->|権限確認| Perms
Perms -->|ポリシー適用| Services
Tags -->|自動タグ付与| Perms
Services -->|アクセス要求| Users
Services -->|監査イベント| Audit
コアコンポーネント
1. データレイク管理者(Data Lake Administrator)
責務:
- Lake Formation 全体の初期化・セットアップ
- IAM Role の作成・権限委譲
- Glue Data Catalog の管理
- LF-TAGS の定義・運用ルール決定
- 監査ログ監視・コンプライアンス確認
IAM 権限:
- lakeformation:*
- glue:CreateCatalog, glue:CreateDatabase, glue:CreateTable
- s3:CreateBucket, s3:GetObject, s3:PutObject
2. Glue Data Catalog との統合
Catalog 管理:
Database:
- raw_data:生データ
- processed_data:加工済みデータ
- analytics:BI 用データ
Table:
- customers:顧客マスター
- orders:注文トランザクション
- products:商品マスター
Lake Formation 権限層:
→ Catalog メタデータアクセス = Lake Formation で制御
3. Governed Tables(管理対象テーブル)
Standard Table:
└── S3 オブジェクト = テーブル定義が分散
Governed Table:
├── S3 オブジェクト ← 厳密なスキーマ管理
├── ACID トランザクション(Iceberg・Hudi)
├── 行・列レベルセキュリティ
└── データバージョニング
4. S3 Data Locations(ストレージ登録)
Lake Formation に S3 ロケーション登録:
s3://data-lake/raw/ → Lake Formation 管理下
s3://data-lake/processed/ → Lake Formation 管理下
s3://data-lake/external/ → IAM ポリシーで制御(除外)
→ Lake Formation 管理下のデータにのみ権限ルール適用
アクセス制御モデル
| 項目 |
IAM(S3 ベース) |
Lake Formation |
| 制御粒度 |
バケット・オブジェクト |
テーブル・列・行・セル |
| 適用単位 |
IAM Role / User |
IAM Role + Lake Formation Catalog |
| 複数サービス統一 |
個別設定が必要 |
自動同期(Athena・EMR・Redshift) |
| パフォーマンス |
ポリシー評価オーバーヘッド |
Catalog 統合で効率的 |
| クロスアカウント |
複雑(信頼関係設定) |
簡潔(Lake Formation 権限) |
テーブルレベルのアクセス制御
基本的な権限付与(SQL)
GRANT SELECT ON TABLE customers TO ROLE analyst_role;
GRANT DESCRIBE ON TABLE customers TO ROLE analyst_role;
GRANT INSERT, UPDATE, DELETE ON TABLE orders TO ROLE etl_role;
GRANT ALTER ON TABLE customers TO ROLE admin_role;
SHOW GRANT ON TABLE customers;
REVOKE SELECT ON TABLE customers FROM ROLE analyst_role;
Python / Boto3 による権限付与
import boto3
import json
lf = boto3.client('lakeformation', region_name='ap-northeast-1')
lf.grant_permissions(
CatalogId='123456789012',
Principal={
'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/analyst_role'
},
Resource={
'Table': {
'CatalogId': '123456789012',
'DatabaseName': 'sales_db',
'Name': 'orders'
}
},
Permissions=['SELECT', 'DESCRIBE']
)
lf.revoke_permissions(
CatalogId='123456789012',
Principal={
'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/analyst_role'
},
Resource={
'Table': {
'CatalogId': '123456789012',
'DatabaseName': 'sales_db',
'Name': 'orders'
}
},
Permissions=['SELECT']
)
列レベルセキュリティ
実装例
GRANT SELECT (order_id, customer_id, amount, created_at)
ON TABLE orders
TO ROLE analyst_role;
SELECT
order_id,
customer_id,
amount,
created_at
FROM orders;
複数列の異なる権限
GRANT SELECT (order_id, amount, total_revenue)
ON TABLE orders
TO ROLE finance_role;
GRANT SELECT (order_id, customer_id, satisfaction_score)
ON TABLE orders
TO ROLE marketing_role;
GRANT SELECT (order_id, customer_id, amount, total_revenue, satisfaction_score)
ON TABLE orders
TO ROLE security_role;
行レベルセキュリティ
Data Filtering(行制限)
CREATE ROW FILTER japan_region_filter AS (region = 'Japan')
ON TABLE orders;
GRANT SELECT ON TABLE orders
WITH ROW FILTER japan_region_filter
TO ROLE japan_team_role;
CREATE ROW FILTER us_region_filter AS (region = 'US')
ON TABLE orders;
GRANT SELECT ON TABLE orders
WITH ROW FILTER us_region_filter
TO ROLE us_team_role;
パラメータ化された行フィルター
CREATE ROW FILTER user_region_filter AS
(region = session.get_user_property('Region'))
ON TABLE orders;
GRANT SELECT ON TABLE orders
WITH ROW FILTER user_region_filter
TO ROLE regional_teams;
セルレベルセキュリティ
列 + 行の組み合わせフィルター
CREATE ROW FILTER japan_only_filter AS (region = 'Japan')
ON TABLE customer_accounts;
GRANT SELECT (account_id, balance, account_type)
ON TABLE customer_accounts
WITH ROW FILTER japan_only_filter
TO ROLE general_analyst;
GRANT SELECT (account_id, customer_name, ssn, balance, account_type)
ON TABLE customer_accounts
TO ROLE compliance_officer;
LF-TAG 定義と運用
import boto3
lf = boto3.client('lakeformation')
lf.create_lf_tag(
TagKey='Classification',
TagValues=['PII', 'Internal', 'Public', 'Confidential']
)
lf.create_lf_tag(
TagKey='Department',
TagValues=['Finance', 'Sales', 'Engineering', 'Marketing']
)
lf.create_lf_tag(
TagKey='DataOwner',
TagValues=['team-finance', 'team-sales', 'team-eng']
)
lf.tag_resource(
ResourceInfo={
'Catalog': {},
'Database': {
'CatalogId': '123456789012',
'Name': 'finance_db'
}
},
TagsToAdd=[
{'TagKey': 'Department', 'TagValue': 'Finance'},
{'TagKey': 'Classification', 'TagValue': 'Confidential'}
]
)
lf.tag_resource(
ResourceInfo={
'Table': {
'CatalogId': '123456789012',
'DatabaseName': 'finance_db',
'TableWildcard': {}
}
},
TagsToAdd=[
{'TagKey': 'Department', 'TagValue': 'Finance'},
{'TagKey': 'Classification', 'TagValue': 'Internal'}
]
)
lf.tag_resource(
ResourceInfo={
'TableWithColumns': {
'CatalogId': '123456789012',
'DatabaseName': 'finance_db',
'Name': 'accounts',
'ColumnNames': ['ssn', 'customer_name', 'phone']
}
},
TagsToAdd=[
{'TagKey': 'Classification', 'TagValue': 'PII'}
]
)
LF-TAG ベース権限付与
lf.grant_permissions(
Principal={
'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/finance_analyst'
},
Resource={
'LFTagOnDatabase': {
'CatalogId': '123456789012',
'TagKey': 'Department',
'TagValues': ['Finance']
}
},
Permissions=['SELECT', 'DESCRIBE'],
PermissionsWithGrantOption=[]
)
属性ベースアクセス制御(ABAC)
ABAC 実装(IAM タグ連携)
import boto3
iam = boto3.client('iam')
lf = boto3.client('lakeformation')
iam.tag_role(
RoleName='analyst-role',
Tags=[
{'Key': 'Department', 'Value': 'Sales'},
{'Key': 'Region', 'Value': 'Tokyo'},
{'Key': 'AccessLevel', 'Value': 'Standard'}
]
)
lf.grant_permissions(
Principal={
'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/analyst-role'
},
Resource={
'LFTag': {
'TagKey': 'Department',
'TagValues': ['Sales']
}
},
Permissions=['SELECT']
)
Blueprints:データ取り込みの自動化
RDS から S3 への自動取り込み
import boto3
lf = boto3.client('lakeformation')
lf.create_data_lake_settings(
DataLakeSettings={
'DataLakeAdmins': [
{'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/lake-admin'}
],
'CreateDatabaseDefaultPermissions': [
{
'Principal': {'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/etl-role'},
'Permissions': ['CREATE_TABLE', 'ALTER']
}
],
'CreateTableDefaultPermissions': [
{
'Principal': {'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/analyst-role'},
'Permissions': ['SELECT']
}
]
}
)
glue = boto3.client('glue')
glue.create_workflow(
Name='rds-to-s3-daily',
Description='Daily RDS snapshot to S3 via Glue Catalog',
DefaultRunProperties={'database': 'rds_imported'}
)
glue.create_crawler(
Name='rds-crawler',
Role='arn:aws:iam::123456789012:role/glue-role',
DatabaseName='rds_imported',
Targets={
'S3Targets': [
{'Path': 's3://data-lake/rds-imports/'}
]
},
SchemaChangePolicy={
'UpdateBehavior': 'UPDATE_IN_DATABASE',
'DeleteBehavior': 'LOG'
}
)
クロスアカウント・クロスオーガニゼーション共有
クロスアカウント権限設定
lf_producer = boto3.client('lakeformation', region_name='ap-northeast-1')
lf_producer.grant_permissions(
Principal={
'DataLakePrincipalIdentifier': 'arn:aws:iam::999999999999:role/consumer-analyst'
},
Resource={
'Table': {
'CatalogId': '111111111111',
'DatabaseName': 'shared_data',
'Name': 'orders'
}
},
Permissions=['SELECT', 'DESCRIBE']
)
lf_consumer = boto3.client('lakeformation', region_name='ap-northeast-1')
Organizations 経由のデータ共有
lf.register_resource(
ResourceArn='arn:aws:s3:::org-data-lake',
UseServiceLinkedRole=False,
RoleArn='arn:aws:iam::111111111111:role/lake-formation-service-role'
)
lf.grant_permissions(
Principal={
'DataLakePrincipalIdentifier': 'arn:aws:organizations::111111111111:ou/ou-xxxxxxxxx'
},
Resource={
'DataLocation': {
'Catalog': {},
'ResourceArn': 'arn:aws:s3:::org-data-lake'
}
},
Permissions=['DATA_LOCATION_ACCESS']
)
Hybrid Access Mode
import boto3
lf = boto3.client('lakeformation')
lf.put_data_lake_settings(
DataLakeSettings={
'AllowExternalDataFiltering': True,
'AuthorizedSessionTagValueList': ['arn:aws:iam::123456789012:role/etl-role']
}
)
lf.grant_permissions(
Principal={'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/analyst'},
Resource={
'Table': {
'CatalogId': '123456789012',
'DatabaseName': 'analytics',
'Name': 'customer_segments'
}
},
Permissions=['SELECT']
)
外部メタストア・フェデレーション
Redshift データの Catalog 化
lf = boto3.client('lakeformation')
lf.create_lake_formation_identity_center_configuration(
InstanceArn='arn:aws:redshift:ap-northeast-1:123456789012:cluster:my-redshift',
ExternalFilteringEnabled=True
)
glue = boto3.client('glue')
glue.create_table(
CatalogId='123456789012',
DatabaseName='redshift_federated',
TableInput={
'Name': 'customer_360',
'StorageDescriptor': {
'Columns': [
{'Name': 'customer_id', 'Type': 'bigint'},
{'Name': 'name', 'Type': 'string'},
{'Name': 'segment', 'Type': 'string'}
],
'Location': 'redshift://my-redshift/public/customer_360',
'InputFormat': 'com.amazon.emr.redshift.hive.RedshiftInputFormat',
'OutputFormat': 'com.amazon.emr.redshift.hive.RedshiftOutputFormat',
'SerdeInfo': {
'SerializationLibrary': 'com.amazon.redshift.hive.serde.RedshiftHiveSerDe'
}
}
}
)
lf.grant_permissions(
Principal={'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/analyst'},
Resource={
'Table': {
'CatalogId': '123456789012',
'DatabaseName': 'redshift_federated',
'Name': 'customer_360'
}
},
Permissions=['SELECT']
)
glue = boto3.client('glue')
glue.create_connection(
Name='hive-metastore-conn',
Description='Connect to external Hive Metastore',
ConnectionType='JDBC',
ConnectionProperties={
'JDBC_DRIVER_JAR_URI': 's3://my-bucket/hive-metastore-driver.jar',
'JDBC_URL': 'jdbc:hive2://hive-server:10000',
'USERNAME': 'hive_user'
}
)
lf.grant_permissions(
Principal={'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/analyst'},
Resource={
'Table': {
'CatalogId': '123456789012',
'DatabaseName': 'external_hive_db',
'Name': 'external_table'
}
},
Permissions=['SELECT']
)
Redshift 統合
redshift = boto3.client('redshift-data')
redshift.execute_statement(
ClusterIdentifier='my-redshift',
Sql="""
CREATE EXTERNAL TABLE orders (
order_id BIGINT,
customer_id BIGINT,
amount DECIMAL(10, 2)
)
STORED AS PARQUET
LOCATION 's3://data-lake/orders/'
TABLE PROPERTIES ('classification'='parquet');
"""
)
ACID テーブル(Iceberg・Hudi・Delta Lake)
import boto3
lf = boto3.client('lakeformation')
lf.grant_permissions(
Principal={'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/etl-role'},
Resource={
'Table': {
'CatalogId': '123456789012',
'DatabaseName': 'transactions',
'Name': 'orders_iceberg'
}
},
Permissions=['SELECT', 'INSERT', 'UPDATE', 'DELETE']
)
spark.sql("""
-- Hudi Upsert:Lake Formation SELECT・UPDATE 権限を確認
INSERT INTO TABLE default.hudi_orders
SELECT * FROM source_table
""")
監査・コンプライアンス・CloudTrail
import boto3
lf = boto3.client('lakeformation')
s3 = boto3.client('s3')
cloudtrail = boto3.client('cloudtrail')
cloudtrail.create_trail(
Name='lake-formation-audit',
S3BucketName='audit-logs-bucket',
IsMultiRegionTrail=True,
EnableLogFileValidation=True
)
cloudtrail.put_event_selectors(
TrailName='lake-formation-audit',
EventSelectors=[
{
'ReadWriteType': 'All',
'IncludeManagementEvents': True,
'DataResources': [
{
'Type': 'AWS::S3::Object',
'Values': ['arn:aws:s3:::data-lake/*']
}
]
}
]
)
cloudtrail_events = cloudtrail.lookup_events(
LookupAttributes=[
{
'AttributeKey': 'EventSource',
'AttributeValue': 'lakeformation.amazonaws.com'
}
]
)
for event in cloudtrail_events['Events']:
print(f"Time: {event['EventTime']}")
print(f"User: {event['Username']}")
print(f"Action: {event['EventName']}")
print(f"Resource: {event['Resources']}")
アクセス監査ダッシュボード(CloudWatch)
import boto3
cloudwatch = boto3.client('cloudwatch')
cloudwatch.put_metric_data(
Namespace='LakeFormation',
MetricData=[
{
'MetricName': 'AccessDenied',
'Value': 1,
'Unit': 'Count',
'Dimensions': [
{'Name': 'Resource', 'Value': 'orders_table'},
{'Name': 'Principal', 'Value': 'analyst_role'}
]
}
]
)
logs = boto3.client('logs')
response = logs.start_query(
logGroupName='/aws/lakeformation/audit',
startTime=1609459200,
endTime=1609545600,
queryString="""
fields @timestamp, @message
| filter eventName like /AccessDenied/
| stats count by Principal, Resource
"""
)
セキュリティベストプラクティス
✅ 推奨事項
| 項目 |
実装 |
効果 |
| データレイク管理者限定 |
複数管理者で運用・定期ローテーション |
権限集中のリスク低減 |
| LF-TAGS 設計 |
組織全体で統一的なタグ体系 |
スケーラブルな RBAC 実現 |
| Hybrid Mode 活用 |
段階的な Lake Formation 導入 |
既存環境への影響最小化 |
| Row-Level Filter テスト |
テスト環境で全フィルター条件検証 |
本番環境でのデータ漏洩防止 |
| CloudTrail 有効化 |
全 Lake Formation API を記録 |
コンプライアンス監査対応 |
| Catalog 権限委譲 |
データベース・テーブルごとに委譲 |
権限運用の分散化 |
| 定期的な権限レビュー |
月次で不要権限を確認・削除 |
過剰権限の排除 |
| DataZone 連携 |
Lake Formation + DataZone でガバナンス統一 |
データメッシュ実現 |
| 暗号化設定 |
KMS で S3 データ暗号化 |
保存時セキュリティ強化 |
| VPC Endpoint |
プライベート接続(IAM は不要) |
ネットワークセキュリティ向上 |
❌ 反パターン
| 反パターン |
理由 |
改善策 |
| すべてのユーザーに管理者権限 |
セキュリティリスク・監査不可 |
役割分離・最小権限の原則 |
| IAM のみで列・行制御を実現 |
実装困難・複雑・誤設定リスク |
Lake Formation 導入 |
| LF-TAGS なし・全テーブルに個別権限 |
スケール不可・運用負担 |
LF-TAGS ベース RBAC 設計 |
| 監査ログなし |
コンプライアンス未対応 |
CloudTrail 有効化・ログ監視 |
| テーブル削除時に権限を確認せず |
想定外の権限委譲が残存 |
削除前に権限確認・削除 |
CLI 実装例
aws lakeformation put-data-lake-settings \
--data-lake-settings '{
"DataLakeAdmins": [
{
"DataLakePrincipalIdentifier": "arn:aws:iam::123456789012:role/lake-admin"
}
],
"CreateDatabaseDefaultPermissions": [
{
"Principal": {
"DataLakePrincipalIdentifier": "arn:aws:iam::123456789012:role/etl-role"
},
"Permissions": ["CREATE_TABLE", "ALTER"]
}
]
}'
aws lakeformation register-resource \
--resource-arn arn:aws:s3:::data-lake \
--role-arn arn:aws:iam::123456789012:role/lf-service-role
aws lakeformation grant-permissions \
--principal DataLakePrincipalIdentifier=arn:aws:iam::123456789012:role/analyst-role \
--resource Table='{CatalogId=123456789012,DatabaseName=analytics,Name=orders}' \
--permissions SELECT DESCRIBE
aws lakeformation create-lf-tag \
--tag-key Classification \
--tag-values PII Internal Public Confidential
aws lakeformation tag-resource \
--resource-info Table='{CatalogId=123456789012,DatabaseName=analytics,Name=orders}' \
--tags-to-add '{"Key":"Classification","Value":"Internal"}'
aws lakeformation grant-permissions \
--principal DataLakePrincipalIdentifier=arn:aws:iam::123456789012:role/finance-role \
--resource LFTagOnDatabase='{CatalogId=123456789012,TagKey=Department,TagValues=[Finance]}' \
--permissions SELECT
SDK 実装例
Python Boto3
import boto3
lf = boto3.client('lakeformation', region_name='ap-northeast-1')
glue = boto3.client('glue')
def setup_lake_formation:
"""Lake Formation 初期セットアップ"""
lf.put_data_lake_settings(
DataLakeSettings={
'DataLakeAdmins': [
{'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/lake-admin'}
],
'AllowExternalDataFiltering': True
}
)
lf.register_resource(
ResourceArn='arn:aws:s3:::data-lake',
RoleArn='arn:aws:iam::123456789012:role/lf-service-role'
)
lf.create_lf_tag(TagKey='Classification', TagValues=['PII', 'Internal', 'Public'])
lf.create_lf_tag(TagKey='Department', TagValues=['Finance', 'Sales', 'Engineering'])
lf.grant_permissions(
Principal={'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/analyst'},
Resource={
'Table': {
'CatalogId': '123456789012',
'DatabaseName': 'analytics',
'Name': 'orders'
}
},
Permissions=['SELECT', 'DESCRIBE']
)
lf.grant_permissions(
Principal={'DataLakePrincipalIdentifier': 'arn:aws:iam::123456789012:role/japan-team'},
Resource={
'Table': {
'CatalogId': '123456789012',
'DatabaseName': 'analytics',
'Name': 'orders'
}
},
Permissions=['SELECT'],
PermissionsWithGrantOption=[],
)
print("Lake Formation setup completed!")
def list_lake_formation_permissions:
"""Lake Formation 権限一覧"""
resources = lf.list_resources(ResourceType='TABLE')
for resource in resources['ResourceInfoList']:
print(f"Resource: {resource['ResourceInfo']['Table']}")
perms = lf.list_permissions(
Resource=resource['ResourceInfo']
)
for perm in perms['PrincipalResourcePermissions']:
print(f" Principal: {perm['Principal']}")
print(f" Permissions: {perm['Permissions']}")
if __name__ == '__main__':
setup_lake_formation
list_lake_formation_permissions
Infrastructure as Code
# main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
# Lake Formation Admin Role
resource "aws_iam_role" "lf_admin" {
name = "lake-formation-admin"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "lakeformation.amazonaws.com"
}
}]
})
}
resource "aws_iam_role_policy_attachment" "lf_admin_policy" {
role = aws_iam_role.lf_admin.name
policy_arn = "arn:aws:iam::aws:policy/service-role/LakeFormationServiceLinkedRolePolicy"
}
# Lake Formation Data Lake Settings
resource "aws_lakeformation_data_lake_settings" "example" {
admins = [aws_iam_role.lf_admin.arn]
}
# S3 Data Lake Location
resource "aws_s3_bucket" "data_lake" {
bucket = "my-data-lake-${data.aws_caller_identity.current.account_id}"
}
resource "aws_lakeformation_resource" "data_lake_location" {
arn = aws_s3_bucket.data_lake.arn
role_arn = aws_iam_role.lf_admin.arn
depends_on = [aws_lakeformation_data_lake_settings.example]
}
# LF-TAG
resource "aws_lakeformation_lf_tag" "classification" {
key = "Classification"
values = ["PII", "Internal", "Public", "Confidential"]
}
resource "aws_lakeformation_lf_tag" "department" {
key = "Department"
values = ["Finance", "Sales", "Engineering"]
}
# Analyst Role
resource "aws_iam_role" "analyst" {
name = "analyst-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::123456789012:user/analyst-user"
}
}]
})
}
# Grant Table Permissions
resource "aws_lakeformation_permissions" "analyst_orders_select" {
principal = aws_iam_role.analyst.arn
permissions = ["SELECT"]
table {
database_name = "analytics"
name = "orders"
}
depends_on = [aws_lakeformation_resource.data_lake_location]
}
data "aws_caller_identity" "current" {}
比較
| 項目 |
Lake Formation |
Apache Ranger |
| クラウド |
AWS ネイティブ(S3・Redshift) |
オンプレ・ハイブリッド |
| 運用 |
マネージドサービス |
セルフマネージド |
| ABAC |
LF-TAGS で実装 |
Ranger Attributes |
| 監査 |
CloudTrail 統合 |
別途 Audit DB 必要 |
| コスト |
AWS 課金に含む |
オンプレ 運用コスト |
| 採用 |
AWS クラウド中心企業 |
Hadoop エコシステム企業 |
| 項目 |
Lake Formation |
Unity Catalog |
| プロバイダー |
AWS |
Databricks |
| 基盤 |
Glue Data Catalog |
Metastore |
| マルチクラウド |
AWS のみ |
AWS・Azure・GCP |
| ACID テーブル |
Iceberg・Hudi・Delta サポート |
Delta Lake 最適化 |
| アクセス制御 |
細粒度(列・行・セル) |
同等機能 |
| コスト |
AWS クレジット |
Databricks 別課金 |
| Lock-in |
AWS 依存度高い |
マルチクラウド対応 |
トラブルシューティング
| 症状 |
原因 |
解決策 |
| Column not found / Access Denied |
列レベル権限が見えない |
SELECT (列名 ) 権限を明示的に付与 |
| Row Filter が適用されない |
SQL 構文エラー・フィルター条件不正 |
Row Filter テーブルで EXPLAIN で確認 |
| LF-TAG が見つからない |
タグが削除・誤字 |
list_lf_tags で確認・再作成 |
| Cross-Account Access が失敗 |
Trust Relationship 未設定 |
Consumer Account IAM Role の信頼関係を確認 |
| CloudTrail に Lake Formation イベントなし |
CloudTrail が無効 |
CloudTrail を有効化・S3 ロケーション確認 |
| Athena / EMR で列・行フィルター未適用 |
Hybrid Mode / Lake Formation 権限なし |
権限確認・Hybrid Mode 確認 |
| Glue Crawler が新列を検出・LF-TAG が引き継がれない |
スキーマ更新時に LF-TAG を再適用 |
Crawler 実行後に LF-TAG を確認・更新 |
近年の動向
1. Row-Level Lineage(データ系統追跡)
- 2026 新機能:Iceberg v3 との統合で行単位のデータ系統を追跡
- ✅ どの行がどこから来たかを自動記録
- ✅ データ品質・規制対応で重要
2. Generative AI による権限推奨
- 2026 以降の予想:
- ✅ CloudTrail + Bedrock で「このロールには何を見せるべき?」を AI が提案
- ✅ 権限設計の自動化・運用効率化
3. S3 Tables との統合
- 2026 新機能:S3 Tables がネイティブ Apache Iceberg サポート
- ✅ S3 テーブルを Lake Formation で直接管理
- ✅ セットアップ簡素化
学習リソース
公式ドキュメント
AWS ブログ・記事
技術ブログ・コミュニティ
実装チェックリスト
フェーズ 1:導入準備
- [ ] Lake Formation vs IAM only での権限設計比較
- [ ] 既存データ分類・タグ体系の設計
- [ ] LF-TAGS キーの定義(Classification・Department など)
- [ ] Glue Data Catalog への移行スケジュール
フェーズ 2:パイロット実装
- [ ] テスト環境で Lake Formation セットアップ
- [ ] LF-TAG 定義・テーブル分類完了
- [ ] 数個テーブルに列・行レベル権限設定
- [ ] Athena / EMR での権限適用確認
フェーズ 3:本番運用
- [ ] Hybrid Mode で既存環境との並行運用
- [ ] CloudTrail 有効化・監査ログ監視
- [ ] Row-Level Filter テスト・本番適用
- [ ] DataZone との連携設定
フェーズ 4:最適化・高度な運用
- [ ] Iceberg / Hudi による ACID テーブル統合
- [ ] クロスアカウント・クロスオーガニゼーション共有導入
- [ ] 自動化スクリプト(権限管理・監査報告)
- [ ] データメッシュ戦略への統合
まとめ
AWS Lake Formation は 「S3 データレイクの細粒度アクセス制御・ガバナンス基盤」。IAM では実現できない列・行・セルレベルのアクセス制御を一元管理し、Athena・EMR・Redshift に統一的に適用できます。
成功の鍵:
- LF-TAGS 設計で スケーラブルな RBAC 実現
- Hybrid Mode で既存環境への影響を最小化
- CloudTrail 監査 でコンプライアンス対応
- Iceberg・Hudi との ACID 統合
- DataZone との組み合わせでデータメッシュ実現
2025-2026 年は Generative AI による権限推奨・S3 Tables ネイティブ統合など、運用効率化が進む予定です。