🔀

Glue データカタログのクロスアカウント共有 [RAM, Lake Formation]

ある AWS アカウントに存在する Glue テーブルを別のアカウントから参照したい、といったことはないでしょうか。本記事ではこのような要件に対して、Lake Formation 管理であることを前提に、Resource Access Manager (RAM) でクロスアカウント共有する方法についてご紹介したいと思います。

想定シナリオ

本記事では、以下のようなアーキテクチャに示すシナリオを想定します。

  • AWS Organizations 組織 の管理アカウントにおいて、Cost Usage Report (CUR) の S3 データエクスポートが設定されており、データは Parquet 形式で保存されている。
  • CUR データは Glue クローラーによって、Glue テーブルとしてデータカタログ化されている。
  • CUR の Glue テーブルを RAM でリソース共有し、組織内アカウントからの Read-Only アクセスを可能にしたい。組織内の分析アカウントから、CUR データを取得・分析したい。

前提

  • 共有元アカウント A および 共有先 アカウント B の両方において、Lake Formation の Data Lake Administrators であること

実装手順

以下の手順で Glue テーブルのクロスアカウント参照を実装していきます。

  1. 【A】Glue テーブルを作成する
  2. 【A】RAM リソース共有を作成する
  3. 【A】組織内からの Glue テーブル参照を許可する
  4. 【B】リソースリンクを作成する
  5. 【B】Glue ジョブロールに Lake Formation 権限を付与する
  6. 【A】S3 バケットポリシーを更新する
  7. 【B】Glue ジョブロールに必要な IAM 権限を付与する
  8. 【B】動作確認

1.【A】Glue テーブルを作成する

まず、共有対象となる Glue テーブルを作成します。本記事では S3 にエクスポートされた CUR データに対して、Glue クローラーを使用してスキーマ抽出・テーブル作成を行います。

以下の通り cur_org.legacy_all というテーブルが作成された想定で話を進めます。

2.【A】RAM リソース共有を作成する

RAM のマネジメントコンソールを開き、[自分が共有 (Shared by me)] からリソース共有を作成します。今回は Glue テーブルへのクロスアカウント参照を実現するため、組織内のアカウントに対して以下のリソースを共有します。

Resource Type Principals
Glue Catalog {共有元アカウントAのアカウントID}
Glue Database cur_org
Glue Table cur_org/*

例として、Glue データベース cur_org 内の全てのテーブルを組織内に共有する際の手順について以下に示します。

Step-1. リソース共有の詳細を指定

リソース共有一覧画面右上の [リソース共有を作成] を押下します。共有の詳細(共有名と共有対象リソース)を指定し、[次へ] を押下します。

Step-2. マネージド型アクセス許可を関連付ける

マネージド型アクセス許可を指定しますが、必要なものがデフォルトで設定されているため、そのまま [次へ] を押下します。

Step-3. プリンシパルにアクセス権限を付与する

組織内からの全てのアカウントに共有する場合、プリンシパルタイプとして「組織」を選択し、組織 ID を入力して [追加] します。「選択されたプリンシパル」に追加されていることを確認し、[次へ] を押下します。

Step-4. 確認と作成

設定された内容に問題がないことを確認し、[リソース共有を作成] を押下します。

リソース共有が作成されると、共有先アカウント B の RAM マネジメントコンソールの [自分と共有 (Shared with me)] にリソース共有が表示されます。

3.【A】組織内からの Glue テーブル参照を許可する

リソース共有された Glue テーブルに組織内からのアクセスを許可できるよう、共有元アカウントで Lake Formation 権限を作成します。必要な権限は以下です。

Resource Type Principals Databases Tables Database Permissions
Glue Database {o-から始まる組織ID} cur_org - DESCRIBE (+Grantable)
Glue Table {o-から始まる組織ID} cur_org * SELECT (+ Grantable)
DESCRIBE (+Grantable)

Lake Formation 権限が付与されたことで、共有先アカウント B において共有された Glue テーブルが表示されるようになります。

4.【B】リソースリンクを作成する

共有先アカウント B において、共有された Glue テーブルは直接参照できないため、リソースリンク を作成します。共有先アカウント B では、このリソースリンク名を使用してデータを読み込むことができます。

Glue データベース一覧から、共有された cur_org の詳細画面を開き、[Actions] - [Create resource link] を押下します。

リソースリンク名を入力し、[Create] を押下します。
(本記事ではリソースリンク名を cur_org_linked としておきます)

5.【B】Glue ジョブロールに Lake Formation 権限を付与する

共有先アカウント B の Glue ジョブロールがデータアクセスするために必要となる、以下の Lake Formation 権限を付与します。

Resource Type Principals Databases Tables Database Permissions
Glue Database {Glueジョブロール} cur_org - DESCRIBE
Glue Table {Glueジョブロール} cur_org * SELECT, DESCRIBE
Glue Database {Glueジョブロール} cur_org_linked - DESCRIBE
Glue Table {Glueジョブロール} cur_org_linked * SELECT, DESCRIBE

6.【A】S3 バケットポリシーを更新する

共有先アカウント B の Glue ジョブがデータにアクセスするためには、Glue ジョブロールがデータソースである S3 バケット・オブジェクトを読み取り可能である必要があります。まずは S3 バケットポリシー(リソースポリシー)を修正して、適切なプリンシパルからの s3:ListBucket, s3:GetObject を許可します。以下は Terraform で修正する場合の例です。

Terraform で修正する場合の例
s3.tf
resource "aws_s3_bucket" "default" {
  bucket = var.bucket_name
  lifecycle {
    prevent_destroy = true
  }
}

# Bucket Policy
resource "aws_s3_bucket_policy" "default" {
  bucket = aws_s3_bucket.default.id
  policy = data.aws_iam_policy_document.bucket_policy.json
}

data "aws_iam_policy_document" "bucket_policy" {
  # ... (略) ...
+ statement {
+   sid    = "AllowReadOnlyAccessFromOrganization"
+   effect = "Allow"
+   actions = [
+     "s3:ListBucket",
+     "s3:GetObject",
+   ]
+   resources = [
+     "${aws_s3_bucket.default.arn}",
+     "${aws_s3_bucket.default.arn}/*"
+   ]
+   condition {
+     test     = "StringEquals"
+     variable = "aws:PrincipalOrgID"
+     values   = [var.organization_id]
+   }
+   principals {
+     type        = "AWS"
+     identifiers = ["*"]
+   }
+ }
  # ... (略) ...
}

7.【B】Glue ジョブロールに必要な IAM 権限を付与する

続いて、Glue ジョブロールに S3 読み取り権限を付与します。

Terraform で修正する場合の例
iam.tf
resource "aws_iam_role" "default" {
  name = var.glue_job_role_name
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow",
        Principal = {
          Service = "glue.amazonaws.com"
        }
        Action = "sts:AssumeRole",
      }
    ]
  })
  managed_policy_arns = [
    "arn:aws:iam::aws:policy/service-role/AWSGlueServiceRole",
  ]
  inline_policy {
    name   = "default"
    policy = data.aws_iam_policy_document.inline.json
  }
}

data "aws_iam_policy_document" "inline" {
  # ... (略) ...
  statement {
+   sid = "AllowReadOnlyAccessToOrganizationCURBucket"
+   actions = [
+     "s3:GetObject",
+     "s3:ListBucket",
+   ]
+   resources = [
+     "arn:aws:s3:::${var.buckets.cur}",
+     "arn:aws:s3:::${var.buckets.cur}/*",
+   ]
+ }
  # ... (略) ...
}

8.【B】動作確認

先述の Glue ジョブロールがアタッチされた Glue Notebook ジョブでリソースリンク名を参照し、データフレームとして読み込みます。

df = glueContext.create_dynamic_frame.from_catalog(
    database="cur_org_linked",
    table_name="legacy_all",
    push_down_predicate=f"year = '2024' and month = '5'",
).toDF()

df.printSchema()
df.head(5)

必要な権限が適切に設定されていれば、データが取得できていることを確認できます。

さいごに

Glue テーブルのクロスアカウント参照に必要な手順についてまとめてみました。

データを取得する際、Insufficient Lake Formation permissions のエラーに悩まされたことがある人も少なくないのではないでしょうか。[1] 特にクロスアカウントともなると実施すべき手順も多くなるので、本記事がトラブルシュートなどのお役に立てれば幸いです。

最後まで読んで頂き、ありがとうございました。

脚注
  1. AWS Glue の Lake Formation アクセス許可エラーをトラブルシューティングする方法を教えてください。 - AWS re:Post ↩︎

SimpleForm Tech Blog

Discussion