Amazon QuickSight 名前空間を活用したマルチテナント構成の実装

AWS

2025.7.23

Topics

概要

Amazon QuickSight でマルチテナント環境を構築する際は、各テナントのデータとユーザーを適切に分離することは重要な要件です。
本記事では、QuickSight の名前空間機能とグループ管理を組み合わせて、セキュアなマルチテナント環境を構築する方法をご紹介します。

構成概要

今回構築するマルチテナント構成は以下の通りです。

QuickSightマルチテナント構成図(管理者視点)

  • 名前空間:default (テナント全体を管理する名前空間)
    • 管理者ユーザー(ADMIN 権限)
  • NS1 名前空間: テナント 1 用の独立した名前空間
    • グループ:GroupA
    • ユーザー:QSDemoUser1(AUTHOR 権限・IAM ユーザー)
  • NS2 名前空間: テナント 2 用の独立した名前空間
    • グループ:GroupB
    • ユーザー:QSDemoUser2(AUTHOR 権限・IAM ユーザー)

今回設定するグループの権限設定は以下の通りです。

QuickSightマルチテナント構成図(データ分離)

事前に用意した Glue のデータカタログを使って、Athena から S3 のデータを読み取ります。

  • NS1 名前空間からはテナント 1 用の S3 バケットのみアクセスを許可する
  • NS2 名前空間からはテナント 2 用の S3 バケットのみアクセスを許可する

自テナントの S3 バケットのデータにアクセスできることと、別テナントの S3 バケットのデータにアクセスできないことの両方を確認します。

Terraform による実装

カスタム名前空間の作成は、コンソールからは行えません。
また、CloudFormation を使ったとしても、ユーザーやグループのリソースがありません。
そのため、本記事では、作成時の利便性を考慮して Terraform を用いて構築します。

また、QuickSight の設定の紹介が主のため、S3 や Glue のコードについては割愛します。

IAM ユーザーの作成

それぞれの名前空間で使用するユーザーを作成します。

# IAM ユーザー作成
resource "aws_iam_user" "ns1" {
  name = "QSDemoUser1"
  path = "/"
}

resource "aws_iam_user" "ns2" {
  name = "QSDemoUser2"
  path = "/"
}

QuickSight 名前空間の作成

NS1 と NS2 の名前空間を作成します。

# 現在のAWSアカウントIDを取得するためのデータソース
data "aws_caller_identity" "current" {}

# QuickSight 名前空間 NS1
resource "aws_quicksight_namespace" "ns1" {
  namespace      = "NS1"
  aws_account_id = data.aws_caller_identity.current.account_id
  identity_store = "QUICKSIGHT"
}

# QuickSight 名前空間 NS2
resource "aws_quicksight_namespace" "ns2" {
  namespace      = "NS2"
  aws_account_id = data.aws_caller_identity.current.account_id
  identity_store = "QUICKSIGHT"
}

QuickSight ユーザーの作成

作成した IAM ユーザーを指定して、QuickSight ユーザーを作成します。

# QuickSightユーザー(NS1用)
resource "aws_quicksight_user" "user_ns1" {
  aws_account_id = data.aws_caller_identity.current.account_id
  namespace      = aws_quicksight_namespace.ns1.namespace
  email          = var.quicksight_user_email
  identity_type  = "IAM"
  user_role      = "AUTHOR"
  iam_arn        = aws_iam_user.ns1.arn
  depends_on     = [aws_quicksight_namespace.ns1]
}

# QuickSightユーザー(NS2用)
resource "aws_quicksight_user" "user_ns2" {
  aws_account_id = data.aws_caller_identity.current.account_id
  namespace      = aws_quicksight_namespace.ns2.namespace
  email          = var.quicksight_user_email
  identity_type  = "IAM"
  user_role      = "AUTHOR"
  iam_arn        = aws_iam_user.ns2.arn
  depends_on     = [aws_quicksight_namespace.ns2]
}

QuickSight グループの作成と割当

NS1 と NS2 の名前空間用にグループを作成します。

# QuickSightグループ(NS1用)
resource "aws_quicksight_group" "groups_ns1" {
  aws_account_id = data.aws_caller_identity.current.account_id
  namespace      = aws_quicksight_namespace.ns1.namespace
  group_name     = "QSDemo-NS1-GroupA"
  depends_on     = [aws_quicksight_namespace.ns1]
}

# QuickSightグループ(NS2用)
resource "aws_quicksight_group" "groups_ns2" {
  aws_account_id = data.aws_caller_identity.current.account_id
  namespace      = aws_quicksight_namespace.ns2.namespace
  group_name     = "QSDemo-NS2-GroupB"
  depends_on     = [aws_quicksight_namespace.ns2]
}

作成したグループに各テナント用のユーザーを紐づけます。

# NS1グループメンバーシップ
resource "aws_quicksight_group_membership" "authors_ns1_membership" {
  aws_account_id = data.aws_caller_identity.current.account_id
  namespace      = aws_quicksight_namespace.ns1.namespace
  group_name     = aws_quicksight_group.groups_ns1.group_name
  member_name    = aws_iam_user.ns1.name
  depends_on = [
    aws_quicksight_group.groups_ns1,
    aws_quicksight_user.user_ns1
  ]
}

# NS2グループメンバーシップ
resource "aws_quicksight_group_membership" "authors_ns2_membership" {
  aws_account_id = data.aws_caller_identity.current.account_id
  namespace      = aws_quicksight_namespace.ns2.namespace
  group_name     = aws_quicksight_group.groups_ns2.group_name
  member_name    = aws_iam_user.ns2.name
  depends_on = [
    aws_quicksight_group.groups_ns2,
    aws_quicksight_user.user_ns2
  ]
}

IAM ポリシーの定義と割り当て

基本的には同じポリシーですが、S3 のリソースはテナントごとに作成したバケットのみを許可するポリシーにしています。

# QuickSight GroupA用IAMポリシー
resource "aws_iam_policy" "quicksight_group_a_policy" {
  name        = "QuickSightGroupAPolicy"
  description = "Policy for QuickSight Author access - Group A (NS1)"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:*"
        ]
        Resource = [
          "${aws_s3_bucket.ns1.arn}",
          "${aws_s3_bucket.ns1.arn}/*",
          "arn:aws:s3:::aws-athena-query-results-us-east-1-${data.aws_caller_identity.current.account_id}",
          "arn:aws:s3:::aws-athena-query-results-us-east-1-${data.aws_caller_identity.current.account_id}/*",
        ]
      },
      {
        Effect = "Allow"
        Action = [
          "athena:*"
        ]
        Resource = "*"
      },
      {
        Effect = "Allow"
        Action = [
          "glue:*"
        ]
        Resource = "*"
      }
    ]
  })
}

# QuickSight GroupB用IAMポリシー
resource "aws_iam_policy" "quicksight_group_b_policy" {
  name        = "QuickSightGroupBPolicy"
  description = "Policy for QuickSight Author access - Group B (NS2)"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:*"
        ]
        Resource = [
          "${aws_s3_bucket.ns2.arn}",
          "${aws_s3_bucket.ns2.arn}/*",
          "arn:aws:s3:::aws-athena-query-results-us-east-1-${data.aws_caller_identity.current.account_id}",
          "arn:aws:s3:::aws-athena-query-results-us-east-1-${data.aws_caller_identity.current.account_id}/*",
        ]
      },
      {
        Effect = "Allow"
        Action = [
          "athena:*"
        ]
        Resource = "*"
      },
      {
        Effect = "Allow"
        Action = [
          "glue:*"
        ]
        Resource = "*"
      }
    ]
  })
}

# NS1グループへのIAMポリシー割り当て
resource "aws_quicksight_iam_policy_assignment" "group_ns1" {
  assignment_name   = "group1"
  assignment_status = "ENABLED"
  policy_arn        = aws_iam_policy.quicksight_group_a_policy.arn
  namespace         = aws_quicksight_namespace.ns1.namespace
  identities {
    group = [aws_quicksight_group.groups_ns1.group_name]
  }
}

# NS2グループへのIAMポリシー割り当て
resource "aws_quicksight_iam_policy_assignment" "group_ns2" {
  assignment_name   = "group2"
  assignment_status = "ENABLED"
  policy_arn        = aws_iam_policy.quicksight_group_b_policy.arn
  namespace         = aws_quicksight_namespace.ns2.namespace
  identities {
    group = [aws_quicksight_group.groups_ns2.group_name]
  }
}

作成したユーザーでログインを確認

作成した IAM ユーザーのコンソールログインを有効化して、QuickSight のセットアップを完了させます。
アカウント名がそれぞれ名前空間の名前になっています。

QSDemoUser1 でログインした画面

QuickSight NS1ログイン画面

QSDemoUser2 でログインした画面

QuickSight NS2ログイン画面

権限管理の設定

グループを作成しましたが、リソースを作成しただけでは意図した権限設定が適用されないため、コンソールから QuickSight 自体の設定を変更します。

QuickSight では、以下の 3 つの設定を行い詳細な権限を定義します。

  • QuickSight の AWS サービスへのアクセス
  • デフォルトのリソースアクセス
  • 個々のユーザーとグループのリソースへのアクセス

AWS リソースへのアクセス – Amazon QuickSight

QuickSightセキュリティとアクセス許可設定画面

「QuickSight の AWS サービスへのアクセス」で、今回作成した S3 バケットを 2 つともにチェックを付けます。

Amazon S3バケット選択ダイアログ

続いて、「デフォルトのリソースアクセス」でアクセスを拒否する設定に変更します。

  • すべてのユーザーおよびグループに対しすべての AWS のデータおよびリソースに対するアクセスを拒否する

こうすることにより、「個々のユーザーとグループのリソースへのアクセス」で詳細な権限設定が可能になります。

QuickSightデフォルトリソースアクセス設定

最後に、「個々のユーザーとグループのリソースへのアクセス」を設定を確認します。
Terraform で名前空間と合わせて作成しているため、すでに作成してある状態です。

なお、デフォルトの名前空間からポリシーを確認していますが、別の名前空間にあるグループを設定した場合だと正しくグループ名が表示されません。
ただし、実際にはグループにポリシーが適用されています。

QuickSightユーザーグループ管理画面

それぞれのポリシーで、テナントごとの S3 のみのアクセスを許可しています。

テナント 1 用の Group A ポリシー

QuickSightGroupAPolicy IAMポリシー設定画面

テナント 2 用の Group B ポリシー

QuickSightGroupBPolicy IAMポリシー設定画面

名前空間の動作確認

では、準備が整ったためそれぞれの名前空間でデータセットを作成してデータに正しくアクセスできるかを確認します。

想定の挙動

  • テナント 1(NS1 名前空間)での確認
    • テナント 1 用の S3 バケットのデータ:アクセス可能
    • テナント 2 用の S3 バケットのデータ:アクセス拒否
  • テナント 2(NS2 名前空間)での確認
    • テナント 2 用の S3 バケットのデータ:アクセス可能
    • テナント 1 用の S3 バケットのデータ:アクセス拒否

テナント 1(NS1 名前空間)での確認

データセットで Athena から、テナント 1 用のテーブルを選択します。

QuickSightテーブル選択ダイアログ(NS1)

このデータセットは、権限で許可されているためデータが表示されます。

NS1 名前空間のテナント1バケットの結果

続いては、権限が与えられていないテナント 2 用のテーブルを参照します。

QuickSightテーブル選択ダイアログ(NS2)

データセットは作成できますが、データが表示されずにエラーになっています。

NS1 名前空間のテナント2バケットの結果

テナント 2(NS2 名前空間)での確認

こちらも同じ動作になりますが、テナント 2 用のバケットだけデータが表示されて、テナント 1 用のバケットはエラーになってます。

テナント 2 用のバケット

NS2 名前空間のテナント2バケットの結果

テナント 1 用のバケット

NS2 名前空間のテナント1バケットの結果

まとめ

名前空間を使うことで、マルチテナント用にセキュアな環境を作成することができます。
さらに、グループ管理を行うことにより、詳細な権限管理を楽に運用することができます。

また、Terraform を活用することで、各リソースの作成・管理作業も楽になるため合わせて使用することをおすすめします。

参考情報

ハンズオン資料

名前空間の作成 :: Amazon QuickSight ハンズオン

セッション資料

QuickSight の SaaS での活用
Amazon QuickSight の運用設計
Amazon QuickSight アカウント/ユーザー管理におけるポイント

Blog

Amazon S3 と Amazon Athena に対する Amazon QuickSight のきめ細かなアクセス制御のご紹介 | AWS ビッグデータブログ
Amazon QuickSight の分析機能をマルチテナントのアプリケーションに埋め込む | Amazon Web Services ブログ
Amazon QuickSight を利用した SaaS 環境でのマルチテナントアプリケーションのサポート | Amazon Web Services ブログ

テックブログ新着情報のほか、AWSやGoogle Cloudに関するお役立ち情報を配信中!

Cold-Airflow

2021年新卒入社。インフラエンジニアです。RDBが三度の飯より好きです。 主にデータベースやAWSのサーバレスについて書く予定です。あと寒いのは苦手です。

Recommends

こちらもおすすめ

Special Topics

注目記事はこちら