Lake Formationの権限付与をCLIでやる:JSONファイルで「レビューできる権限管理」にする

本記事は 夏休みクラウド自由研究2026 8/15付の記事です

データレイクの運用で地味に神経を使うのが、Lake Formationの権限付与です。データベースを1つ追加するたびに「どのプリンシパルに、どのデータベースへ、どの権限を付けるか」を決めて適用する。マネジメントコンソールからでも数分で終わる作業ですが、権限という性質上、対象を1つ間違えただけで見せてはいけないデータが見えてしまいます。

この作業をAWS CLIに寄せて、付与内容をJSONファイルとして管理する形にできないか検証しました。CloudFormationで検証用のデータレイクを立て、権限の付与・確認・剥奪・片付けまで一通り回した記録です。

なぜCLIに寄せたいのか

コンソール操作の何が困るかというと、突き詰めると「あとから見て何をやったか分からない」ことです。

データベース追加のたびに発生する権限付与を画面から行うと、操作履歴が残らないので事前レビューに乗せられません。同じ設定を検証環境と本番環境の両方に入れたいとき、手作業をもう一度やり直すことになります。「なぜこのロールにCREATE_TABLEが付いているのか」を半年後に追いかけるのも面倒です。そして何より、プルダウンから似た名前のデータベースを選び間違えるリスクが常にあります。

一方、付与内容をJSONファイルにして --cli-input-json で流し込む形にすると、そのファイルをGitに置いてPull Requestでレビューできます。同じファイルを別環境にも適用できるし、コミット履歴が「いつ誰が何を変えたか」の記録になります。要は、権限付与を「操作」から「成果物」に変えたい、というのが今回の動機です。

Lake Formation CLIの全体像

検証の前に、aws lakeformation のコマンド体系をざっと整理しました。だいたい7系統に分かれます。

カテゴリ 主なコマンド 用途
基本設定・管理者設定 register-resource / get-data-lake-settings S3ロケーション登録、データレイク管理者の設定
権限管理 grant-permissions / revoke-permissions / list-permissions 権限の付与・剥奪・一覧
LF-Tag(TBAC) create-lf-tag / add-lf-tags-to-resource タグベースの権限管理
データセルフィルタ create-data-cells-filter 行・列レベルの制御
ガバンドテーブル start-transaction / commit-transaction ACIDトランザクション管理
クエリエンジン連携 get-temporary-glue-table-credentials 一時認証情報の発行
Identity Center / SAML create-lake-formation-identity-center-configuration IdP統合

今回扱うのは2番目の権限管理だけです。データベースレベルの grant-permissions が動く形を作れれば、テーブル単位・列単位への応用は Resource の書き方を変えるだけなので、まずはここを固めます。

検証環境

検証用のリソースはCloudFormation(cfn/lakeformation-test-cfn.yaml)で作りました。手でS3バケットとGlueデータベースを作ってもよかったのですが、検証をやり直すたびに同じ状態から始めたかったのでテンプレート化しています。

作るものは4つです。

  • S3バケット(SSE-S3暗号化、パブリックアクセスブロック)
  • Glueデータベース(権限付与の対象)
  • IAMロール(権限付与の相手。同一アカウントからAssumeRoleできる)
  • AWS::LakeFormation::Resource(S3をLake Formation管理下に登録。CLIの register-resource 相当)

このうち、IAMロールの権限設計だけ少し意図があります。付与しているのは lakeformation:GetDataAccessglue:GetDatabase などのAPI呼び出し権限だけで、S3への直接アクセス権は持たせていません。実データに触れるかどうかはIAMではなくLake Formation側の権限で決まる、という構造をそのまま検証したかったからです。

Policies:
  - PolicyName: lf-data-access
    PolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Effect: Allow
          Action:
            - lakeformation:GetDataAccess
            - glue:GetDatabase
            - glue:GetDatabases
            - glue:GetTable
            - glue:GetTables
            - glue:GetPartitions
          Resource: '*'

やったこと

データレイク管理者の確認

最初にここで引っかかったので先に書きます。grant-permissions を実行するには、操作しているプリンシパルがLake Formationのデータレイク管理者(Data Lake Administrator)に登録されている必要があります。IAM側でどれだけ強い権限を持っていても、この登録がないと AccessDeniedException で弾かれます。

# 自分が誰かを確認
aws sts get-caller-identity --profile poc

# データレイク管理者の一覧
aws lakeformation get-data-lake-settings \
  --region ap-northeast-1 \
  --query 'DataLakeSettings.DataLakeAdmins'

出力に自分のARNがあればそのまま進めます。ない場合は put-data-lake-settings で追加しますが、これはリストへの追記ではなく全体の上書きです。既存の管理者を確認せずに実行すると、他の人の管理者権限を消してしまいます。必ず get-data-lake-settings の結果を控えて、それに自分を足した形で投げます。

デプロイとOutputsの取り込み

export AWS_PROFILE=poc
export AWS_REGION=ap-northeast-1
export AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
export STACK_NAME=test-lf

aws cloudformation deploy \
  --template-file cfn/lakeformation-test-cfn.yaml \
  --stack-name ${STACK_NAME} \
  --capabilities CAPABILITY_NAMED_IAM \
  --region ${AWS_REGION}

IAMロールを名前付きで作るので CAPABILITY_NAMED_IAM が必要です。デプロイできたら、後の手順で使う値をOutputsから環境変数に入れておきます。データベース名やロールARNを手で打つと、それ自体がタイプミスの元になるので。

export TARGET_DATABASE=$(aws cloudformation describe-stacks \
  --stack-name ${STACK_NAME} --region ${AWS_REGION} \
  --query "Stacks[0].Outputs[?OutputKey=='DatabaseName'].OutputValue" --output text)

export TARGET_PRINCIPAL_ARN=$(aws cloudformation describe-stacks \
  --stack-name ${STACK_NAME} --region ${AWS_REGION} \
  --query "Stacks[0].Outputs[?OutputKey=='AnalystRoleArn'].OutputValue" --output text)

付与前の状態を見る

付与してから「あれ、これ元から付いてたやつだっけ」と迷わないために、先に現状の情報を取っておきます。

aws lakeformation list-permissions \
  --resource "{\"Database\":{\"CatalogId\":\"${AWS_ACCOUNT_ID}\",\"Name\":\"${TARGET_DATABASE}\"}}" \
  --region ${AWS_REGION} \
  --output table

この時点で出てくるのは、データベースを作った自分自身の権限(ALL、ALTER、CREATE_TABLE、DESCRIBE、DROP がグラントオプション付き)と、IAM_ALLOWED_PRINCIPALS に対する ALL です。後者はLake Formation導入前からのIAM権限でアクセスできるようにする後方互換の設定で、新規データベースにはデフォルトで付いてきます。厳密にLake Formationで制御したい場合は、ここを外すかどうかも別途検討が必要になります。

付与内容をJSONにする

本題です。コンソールでクリックしていた内容を、そのままファイルに書きます。

{
  "Principal": {
    "DataLakePrincipalIdentifier": "arn:aws:iam::<ACCOUNT_ID>:role/test-lf-analyst-role"
  },
  "Resource": {
    "Database": {
      "CatalogId": "<ACCOUNT_ID>",
      "Name": "test_lf_db"
    }
  },
  "Permissions": ["DESCRIBE", "CREATE_TABLE"],
  "PermissionsWithGrantOption": []
}

環境変数から生成する場合はヒアドキュメントで作ります。

cat > grant-db.json <<JSON
{
  "Principal": {
    "DataLakePrincipalIdentifier": "${TARGET_PRINCIPAL_ARN}"
  },
  "Resource": {
    "Database": {
      "CatalogId": "${AWS_ACCOUNT_ID}",
      "Name": "${TARGET_DATABASE}"
    }
  },
  "Permissions": ["DESCRIBE", "CREATE_TABLE"],
  "PermissionsWithGrantOption": []
}
JSON

Permissions に指定できるもののうち、よく使うのはこのあたりです。

権限 用途
DESCRIBE メタデータの参照
CREATE_TABLE データベース配下へのテーブル作成
SELECT テーブルデータの参照(テーブルリソースに付与)
ALTER / DROP メタデータ変更・削除
ALL 上記すべて

ALL は書くのが楽なので手が伸びがちですが、あとから「このロールは何ができるのか」を読み取れなくなるので避けています。今回は参照とテーブル作成だけなので DESCRIBECREATE_TABLE です。

PermissionsWithGrantOption に入れた権限は、付与された側がさらに他の人へ再付与できるようになります。権限の伝播が追えなくなるので、明確に委譲したい場合を除いて空にしておきます。

なお、JSONの構造で悩んだらスケルトンを吐かせるのが早いです。

aws lakeformation grant-permissions --generate-cli-skeleton > skeleton.json

付与して確認する

aws lakeformation grant-permissions \
  --cli-input-json file://grant-db.json \
  --region ${AWS_REGION}

grant-permissions は成功しても何も出力しません。無反応だと不安になるので、すぐに結果を確認します。プリンシパルで絞ると見やすいです。

aws lakeformation list-permissions \
  --principal DataLakePrincipalIdentifier=${TARGET_PRINCIPAL_ARN} \
  --resource "{\"Database\":{\"CatalogId\":\"${AWS_ACCOUNT_ID}\",\"Name\":\"${TARGET_DATABASE}\"}}" \
  --region ${AWS_REGION} \
  --output table

DESCRIBECREATE_TABLE が出れば意図どおりです。

実際にアクセスしてみる

権限一覧に出ているだけでは、本当にアクセスできるかは分かりません。付与先のロールをAssumeRoleして、データベースが見えるか確認します。

CREDS=$(aws sts assume-role \
  --role-arn ${TARGET_PRINCIPAL_ARN} \
  --role-session-name lf-test \
  --query 'Credentials' --output json)

export AWS_ACCESS_KEY_ID=$(echo $CREDS | jq -r '.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo $CREDS | jq -r '.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo $CREDS | jq -r '.SessionToken')

aws glue get-database --name ${TARGET_DATABASE} --region ${AWS_REGION}

# 元のプロファイルに戻す
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN

一時認証情報を環境変数に入れたまま次の作業に進むと、管理者権限で実行するつもりのコマンドがAnalystRoleで動いて謎の権限エラーになります。確認が終わったら unset するところまでを1セットにしておくのが安全です。

剥奪と片付け

剥奪は、付与に使ったJSONをそのまま流用できます。付与と剥奪が同じ定義で対称になるのは、ファイル方式の地味に嬉しいところです。

aws lakeformation revoke-permissions \
  --cli-input-json file://grant-db.json \
  --region ${AWS_REGION}

剥奪後にもう一度 list-permissions を叩いて、権限が消えていることを確認します。あとはスタックを削除して終わりですが、S3バケットにオブジェクトが残っていると削除に失敗するので、先に空にします。

DATALAKE_BUCKET=$(aws cloudformation describe-stacks \
  --stack-name ${STACK_NAME} --region ${AWS_REGION} \
  --query "Stacks[0].Outputs[?OutputKey=='DataLakeBucketName'].OutputValue" --output text)
aws s3 rm s3://${DATALAKE_BUCKET} --recursive

aws cloudformation delete-stack --stack-name ${STACK_NAME} --region ${AWS_REGION}
aws cloudformation wait stack-delete-complete --stack-name ${STACK_NAME} --region ${AWS_REGION}

管理者設定を変更していた場合は、元の状態に戻すのも忘れないようにします。

つまずいたところ

管理者設定はIAMとは別レイヤー。冒頭にも書きましたが、AccessDeniedException の原因がIAMポリシーではなくLake Formationの管理者登録だった、というのが最初の足止めでした。IAMで見ても原因が見つからないので、Lake Formation側にもう一段レイヤーがあることを知らないと結構悩みます。

put-data-lake-settings は上書き。追記のつもりで叩くと既存の管理者が消えます。検証アカウントで気付けたのは運が良かったところです。本番でやる場合は、事前に get-data-lake-settings の出力をファイルに保存しておいて、それを編集して投げるのが確実です。

インラインJSONのエスケープ--resource にシェルから直接JSONを渡すと、\" のエスケープが崩れて list-permissions が空を返します。エラーにならず「該当なし」として返ってくるので、権限が付いていないのかコマンドが間違っているのか切り分けに時間を使いました。結局、ファイル方式に統一するのが確実でした。レビューできるという当初の目的にも合っています。

遭遇した事象をまとめるとこうなります。

症状 原因 対処
grant-permissionsAccessDeniedException Lake Formation管理者に登録されていない get-data-lake-settings で確認して追加
EntityNotFoundException データベース名/カタログIDの誤り スタックOutputsから取り直す
list-permissions が空を返す インラインJSONのエスケープミス ファイル方式に切り替える

おわりに

やってみて一番効果を感じたのは、コマンドがCLIになったこと自体より、付与内容がファイルとして残るようになったことでした。grant-db.json をリポジトリに置いてレビューしてもらえば、「このロールにCREATE_TABLEを付けていいか」という議論がコードレビューと同じ場でできます。適用したあとも、そのファイルが仕様書として残ります。

運用に乗せるうえで守っているのは2つだけです。ひとつは、事前に list-permissions で現状を見て、付与して、もう一度確認する流れを飛ばさないこと。もうひとつは、Permissions を最小限から始めること。どちらも当たり前ですが、コンソール作業だとどちらも省略しがちなので、CLI + ファイルの手順に組み込んでしまうのが結局早いと思います。

今回はデータベースレベルまでですが、ResourceTableWildcardTableWithColumns に差し替えれば、テーブル単位・列単位の制御も同じ形で書けます。LF-Tagを使ったタグベースの権限管理まで含めれば、権限設計全体をファイルで管理する形に持っていけそうです。そのあたりは次の検証で試します。


検証環境

  • AWS CLI v2(aws lakeformation / aws glue / aws cloudformation
  • AWS Lake Formation / AWS Glue Data Catalog / Amazon S3
  • AWS CloudFormation(検証リソースのIaC)
  • リージョン:ap-northeast-1

参考

×
タイトルとURLをコピーしました