CloudFormationを使ってQuickSightのデータセットをS3から作成してみた

2022.03.27

いわさです。

以前、以下の記事にてQuickSightの分析とダッシュボードをCloudFormationを使って作成する方法をご紹介しました。

その際は、既にデータセットが作成されていることを前提に構築していましたが、CloudFormationではデータセットの作成も可能です。
そこで試してみたのですが、思ってた以上に苦労したのでつまづきポイントをまとめておきました。

公式ドキュメントにリファレンスは以下です。

AWS::QuickSight::DataSet - AWS CloudFormation

完成テンプレート

結論というか先に作成したテンプレートを載せておきます。
S3バケットにCSVファイルとマニフェストファイルをアップロードしている状態から、CloudFormationでデータを生成す場合の最小構成がおそらく以下のような感じになります。

AWSTemplateFormatVersion: 2010-09-09
Description: ---
Resources: 
  HogeDataSource:
    Type: AWS::QuickSight::DataSource
    Properties: 
      AwsAccountId: !Ref AWS::AccountId
      Name: hoge-datasource
      DataSourceId: hoge-datasource-id
      Type: S3
      DataSourceParameters:
        S3Parameters: 
          ManifestFileLocation: 
            Bucket: 20210327quicksightsampledata
            Key: hoge-manifest.json
      Permissions:
        - Actions:
            - quicksight:UpdateDataSourcePermissions
            - quicksight:DescribeDataSource
            - quicksight:DescribeDataSourcePermissions
            - quicksight:PassDataSource
            - quicksight:UpdateDataSource
            - quicksight:DeleteDataSource
          Principal: !Sub arn:aws:quicksight:${AWS::Region}:${AWS::AccountId}:user/default/hogeuser

  HogeDataSet:
    Type: AWS::QuickSight::DataSet
    Properties: 
      Name: hoge-dataset
      AwsAccountId: !Ref AWS::AccountId
      DataSetId: hoge-dataset-id
      ImportMode: SPICE
      PhysicalTableMap: 
        hoge-physical:
          S3Source: 
            DataSourceArn: !GetAtt HogeDataSource.Arn
            InputColumns: 
              -   Name: hoge1
                  Type: STRING
              -   Name: hoge2
                  Type: STRING
      LogicalTableMap:
        hoge-logical:
          Alias: hoge-logi
          DataTransforms: 
            - CastColumnTypeOperation:
                ColumnName: hoge2
                NewColumnType: INTEGER
          Source: 
            PhysicalTableId: hoge-physical
      Permissions:
        - Actions:
            - quicksight:UpdateDataSetPermissions
            - quicksight:DescribeDataSet
            - quicksight:DescribeDataSetPermissions
            - quicksight:PassDataSet
            - quicksight:DescribeIngestion
            - quicksight:ListIngestions
            - quicksight:UpdateDataSet
            - quicksight:DeleteDataSet
            - quicksight:CreateIngestion
            - quicksight:CancelIngestion
          Principal: !Sub arn:aws:quicksight:${AWS::Region}:${AWS::AccountId}:user/default/hogeuser

ここから派生してRDSをデータソースとしたり、複数のデータソースから結合させたりなどさせると良いと思います。

ポイント・注意点・エラー例など

マニフェストファイルの参照方法と権限

以下のような形式のマニフェストファイルをS3バケットへアップロードしています。
が、CloudFormationを実行するとエラーになります。

Messages:
  - DataSource: Resource handler returned message: "Access denied for operation 'AWS::QuickSight::DataSource'." (RequestToken: 1c829f22-1773-ac71-d809-c1c315f705ca, HandlerErrorCode: AccessDenied)

こちらはシンプルにマニフェストファイルの指定方法が誤っていました。
ManifestFileLocationBucketはバケット名で良いのですが、マニフェストファイルに引っ張られてs://で記述してしまっていました。

Messages:
  - DataSource: Resource handler returned message: "Access denied for operation 'AWS::QuickSight::DataSource'." (RequestToken: 1c829f22-1773-ac71-d809-c1c315f705ca, HandlerErrorCode: AccessDenied)

こちらは、QuickSightからS3バケットへのアクセス権限がありませんでした。
QuickSightの管理画面からS3バケットアクセス時に対象バケットへのアクセス許可を追加しましょう。

PhysicalTableMapの宣言方法に注意

Messages:
  - HogeDataSet: Resource handler returned message: "Model validation failed (#: extraneous key [DataSourceArn] is not permitted)" (RequestToken: 0f174c20-a2fa-ad95-e919-4e727af6b0cf, HandlerErrorCode: InvalidRequest)

ここはかなりハマったのですが、マップ型なんですよね。
なので、以下のような構造になってしまっていないか確認してください。

誤り

  HogeDataSet:
    Type: AWS::QuickSight::DataSet
    Properties: 
      Name: hoge-dataset
      AwsAccountId: !Ref AWS::AccountId
      DataSetId: hoge-dataset-id
      ImportMode: SPICE
      PhysicalTableMap: 
        S3Source: 
          DataSourceArn: !GetAtt HogeDataSource.Arn

正しい

  HogeDataSet:
    Type: AWS::QuickSight::DataSet
    Properties: 
      Name: hoge-dataset
      AwsAccountId: !Ref AWS::AccountId
      DataSetId: hoge-dataset-id
      ImportMode: SPICE
      PhysicalTableMap: 
        hoge-physical:
          S3Source: 
            DataSourceArn: !GetAtt HogeDataSource.Arn

物理テーブルのカラム定義で許可されている列挙体文字列を確認する

Messages:
  - HogeDataSet: Properties validation failed for resource HogeDataSet with message:
#/PhysicalTableMap/hoge/S3Source/InputColumns/0/Type: #: only 1 subschema matches out of 2
#/PhysicalTableMap/hoge/S3Source/InputColumns/0/Type: failed validation constraint for keyword [enum]
#/PhysicalTableMap/hoge/S3Source/InputColumns/1/Type: #: only 1 subschema matches out of 2
#/PhysicalTableMap/hoge/S3Source/InputColumns/1/Type: failed validation constraint for keyword [enum]
failed deploying stack 'hoge-datasource'

誤り

          S3Source: 
            DataSourceArn: !GetAtt HogeDataSource.Arn
            InputColumns: 
              -   Name: hoge1
                  Type: String
              -   Name: hoge2
                  Type: Integer

InputColumnで許可されている値が以下のパターンのみなので、それに合っているか再確認しましょう。
気づかずに先頭大文字で記述してしまっていました。

AWS::QuickSight::DataSet InputColumn - AWS CloudFormation

正しい

          S3Source: 
            DataSourceArn: !GetAtt HogeDataSource.Arn
            InputColumns: 
              -   Name: hoge1
                  Type: STRING
              -   Name: hoge2
                  Type: INTEGER

スタックの作成は出来たが、QuickSightの管理画面に表示されない

QuickSightはひとつひとつのリソースに権限設定が必要です。
APIから作成すると忘れやすいのですが明示的に許可する必要があります。

今回だとDataSetとDataSourceにそれぞれ以下のように設定を行ってやりましょう。

  HogeDataSource:
    Type: AWS::QuickSight::DataSource
    Properties: 
:
      Permissions:
        - Actions:
            - quicksight:UpdateDataSourcePermissions
            - quicksight:DescribeDataSource
            - quicksight:DescribeDataSourcePermissions
            - quicksight:PassDataSource
            - quicksight:UpdateDataSource
            - quicksight:DeleteDataSource
          Principal: !Sub arn:aws:quicksight:${AWS::Region}:${AWS::AccountId}:user/default/hoge-user-name

  HogeDataSet:
    Type: AWS::QuickSight::DataSet
    Properties: 
:
      Permissions:
        - Actions:
            - quicksight:UpdateDataSetPermissions
            - quicksight:DescribeDataSet
            - quicksight:DescribeDataSetPermissions
            - quicksight:PassDataSet
            - quicksight:DescribeIngestion
            - quicksight:ListIngestions
            - quicksight:UpdateDataSet
            - quicksight:DeleteDataSet
            - quicksight:CreateIngestion
            - quicksight:CancelIngestion
          Principal: !Sub arn:aws:quicksight:${AWS::Region}:${AWS::AccountId}:user/default/hoge-user-name

S3でJSON以外のファイル形式の場合はSTRING以外許可されていない

Messages:
  - HogeDataSet: Resource handler returned message: "Invalid request provided: Input column hoge2 in physical table hoge has invalid type. Allowed types for S3 physical table are [String] (Service: QuickSight, Status Code: 400, Request ID: 4264a095-be59-4f52-8b0d-db819f3e3a68, Extended Request ID: null)" (RequestToken: 2bb9bbb9-3041-0743-cff7-90119cd16227, HandlerErrorCode: InvalidRequest)

今回だとCSVファイルをアップロードして使用したのですが、データセットの物理テーブルとしてはSTRING以外許可されていないようです。
なので、ここではPhysicalTableMapではSTRINGですべての列を定義する必要があります。

          S3Source: 
            DataSourceArn: !GetAtt HogeDataSource.Arn
            InputColumns: 
              -   Name: hoge1
                  Type: STRING
              -   Name: hoge2
                  Type: STRING

これでエラーが出ずにデプロイ出来るようにはなるのですが、出来上がったデータセットを参照すると当然数値や日付としては利用出来ません。

対処方法として、LogicalTableMapを使うことが可能です。
PhysicalTableMapを変換や結合などを行って、実際に分析から利用する際にはLogicalTableMapとして扱います。

LogicalTableMapへのマッピングは様々なオプションがあります。詳細は以下をご確認ください。
QuickSightのデータセット上で列を追加したり、加工や計算を行ったり、テーブル間で結合したりすることがありますが、データセット作成時に行うタイプのものは、このLogicalTableMapで宣言します。

AWS::QuickSight::DataSet TransformOperation - AWS CloudFormation

ここでは以下のようにひとつの列を数値へ変換させました。

  HogeDataSet:
    Type: AWS::QuickSight::DataSet
    Properties: 
:
      PhysicalTableMap: 
        hoge-physical:
          S3Source: 
            DataSourceArn: !GetAtt HogeDataSource.Arn
            InputColumns: 
              -   Name: hoge1
                  Type: STRING
              -   Name: hoge2
                  Type: STRING
      LogicalTableMap:
        hoge-logical:
          Alias: hoge-logi
          DataTransforms: 
            - CastColumnTypeOperation:
                ColumnName: hoge2
                NewColumnType: INTEGER
          Source: 
            PhysicalTableId: hoge-physical
:

さいごに

本日はCloudFormationでQuickSightデータセットを作成する際につまづいたポイントをまとめました。
管理コンソールから手動操作したら数十秒で終わるデータセット作成ですが、CloudFormationとなると情報が少なくてほぼ自力でトライ&エラーしたのでえらい苦労しました。
この記事が同じように困った方の参考になると嬉しいです。