Amazon QuickSight ダッシュボードをCloudFront 経由で参照する

# はじめに
とあるプロジェクトでユーザー数などのビジネスデータを可視化したいという要望がありました。管理者用の画面を作っても良かったのですが、データベースを可視化したいだけだったので、BIツールを利用することにしました。
元々ワークロードがAWS上で動作していたため、QuickSight を第一選択肢として利用しようと思いましたが、わざわざ利用者にQuickSightを開かせたくなかったのと、リーダーアカウントの料金を節約したいなと思い、CloudFront経由でQuickSightダッシュボードを表示させる方法を試してみました。
（セッション課金にするほどの利用は無いため、ユーザー課金を利用しつつ、費用を節約する必要がありました）

# 処理の流れ
アーキテクチャは以下の通りです。
![スクリーンショット 2024-05-02 12.24.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1005574/cb0911dd-6577-0c8c-94ea-72a0499d9fbe.png)

また、処理の流れは以下の通りです。
1. ユーザーが CloudFront にアクセス
2. CloudFront のビヘイビアで指定した Lambda@Edge でCognito認証の状況をチェックし、認証していない場合は Cognito の認証画面にリダイレクトさせる
3. Cognito認証後、QuickSight を iframe で組み込んだ htmlを返す Lambda を呼びだす
4. 3の htmlを表示 

2 の Lambda@Edge はクラスメソッドさんの記事を参考に実装しました。
https://dev.classmethod.jp/articles/cloud-front-cognito-auth/

また、Cognito認証ではGoogle認証を利用し、Google Workspaceのユーザーの場合は自動でユーザーが作成されるようにしました。これにより、Google Workspaceのユーザー管理だけで、BIダッシュボードのアクセスユーザーを管理でき、かつ、ちょっとした確認でquicksightユーザーを作成しなくてよくなります。
https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pools-assign-domain.html

3 のソースコードは以下の記事を参考に作成しました。
https://qiita.com/33_nag227/items/f7399bdac9b02cf209a7
ポイントは GenerateEmbedUrlForRegisteredUser を利用するところです。
```
import boto3
import base64

def lambda_handler(event, context):

  quicksight_region = "ap-northeast-1"
  aws_account_id = "XXXXXXXX"
  user_arn = "arn:aws:quicksight:ap-northeast-1:XXXXXXXX:user/XXXXXXX"
  session_minutes = 15
  dashboard_id = "XXXXXXXXXXXXXXXXX"

  quicksight = boto3.client('quicksight', region_name=quicksight_region)
  
  response = quicksight.list_namespaces(
    AwsAccountId=aws_account_id,
    MaxResults=100
  )
  
  response = quicksight.list_users(
    AwsAccountId=aws_account_id,
    MaxResults=100,
    Namespace='default'
  )
  
  print(response)

  response = quicksight.generate_embed_url_for_registered_user(
    AwsAccountId = aws_account_id,
    UserArn = user_arn,
    SessionLifetimeInMinutes = session_minutes,
    ExperienceConfiguration = {'Dashboard':{'InitialDashboardId': dashboard_id}}
  )

  return_url = response['EmbedUrl']

  str_html = '<!DOCTYPE html>'
  str_html += '<html lang="ja">'
  str_html += '<head>'
  str_html += '<title>ShipOne BI</title>'
  str_html += '<meta charset="utf-8"/>'
  str_html += '<style>'
  str_html += '* { margin: 0; padding: 0; overflow: hidden; height:100vh;}'
  str_html += ' '
  str_html += 'iframe {border:none;width:100%;height:calc(100% - 60px);}'
  str_html += '</style>'
  str_html += '</head>'
  str_html += '<body>'
  str_html += '<h2> Dashbord </h2>'
  str_html += '<iframe'
  str_html += ' frameborder="0"'
  str_html += ' style="position:absolute; top:52px"'
  str_html += ' src="' + return_url + '" '
  str_html += '</iframe>'
  str_html += '</body>'
  str_html += '</html>'
  
  response = {
    "statusCode": 200,
    "headers": {
      'Content-Type': 'text/html',
    },
    "body": str_html,
  }
  
  return response
```

# 結果
Cognito認証後、画像のように QuickSight ダッシュボードの画面が表示されました。
ダッシュボード内のタブやフィルター、ジャンプなどの正しく動いていました。
![スクリーンショット 2024-05-02 12.46.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1005574/4b6acb18-1e02-0152-9e3e-dbc9a80117a7.png)



# 感想
コストを抑えつつ、ダッシュボードの共有ができました。もちろん、QuickSight Q が利用できないなど、QuickSightを直接参照するよりも機能は大きく落ちます。
一方で、朝会でビジネス状況を表示しつつ、話し合いたい場合など単純に「見る」だけの場合は十分有用だと思いました。