以前の記事ではシンプルに分析をコピーする方法が紹介されています。
この記事ではdevelop環境でQuickSightのアセット(データセット・分析・ダッシュボードなど)をproduction環境にCloudFormationでデプロイする方法を紹介します。
実現したいこと
developで作成したQuickSightのデータセット・分析・ダッシュボードをproductionに複製したい
developでの修正を【継続的に】productionに反映したい
できるだけ個別のファイルを編集したくない
概要
developから start-asset-bundle-export-job でCloudFormation形式でエクスポートする
productionでCloudFormationを使ってデプロイする
シンプルにこれだけなのですが、productionで使えるようにするまでに細々とやることがあったのでそちらも紹介します。
develop環境にはCloudFormationでデプロイしないの?
Templateをゼロから書けばおそらくできるのですが、以下の理由からdevelopは手作業として運用しています。
分析をつくる人はエンジニアとは限らない
QuickSightの分析を手書きで定義するのはしんどい
手順
データソースのデプロイ
develop環境もproduction環境もデータソースはGUIで作成せずに、CLIやCloudFormationなどでIDを指定して作成してください。
なぜかというと、データセットが参照するデータソースのIDを外部から与えることができず、productionとdevelopで同じデータソースIDを参照させる必要があるためです。
すでにデータセットを作成済みの場合、新しく固定のIDのデータソースをデプロイしてからデータセットの update-data-set で更新することができます。
CloudFormationでRedshiftに接続するデータソースを作成するCloudFormationテンプレートです。
RedshiftDatasource:
Type: AWS::QuickSight::DataSource
Properties:
DataSourceId: !Ref DataSourceId
AwsAccountId: !Ref AWS::AccountId
Credentials:
CredentialPair:
Username: !Sub "{{resolve:secretsmanager:${QuickSightRedshiftUserCredentialsArn}:SecretString:username}}"
Password: !Sub "{{resolve:secretsmanager:${QuickSightRedshiftUserCredentialsArn}:SecretString:password}}"
DataSourceParameters:
RedshiftParameters:
ClusterId: !Ref RedshiftClusterId
Database: !Ref RedshiftDatabase
Host: !Ref RedshiftHost
Port: !Ref RedshiftPort
Name: !Sub "Redshift"
SslProperties:
DisableSsl: false
Type: REDSHIFT
VpcConnectionProperties:
VpcConnectionArn: !GetAtt VPCConnection.Arn
DataSourceId がとても重要です。ここに設定する値をdevelopとproductionで同じにしてください。
データセット・分析・ダッシュボードをdevelopで作成する
GUIでそれぞれ作成してください。ここでは、データセットのデータソースを上記で作成したデータソースにすること以外は特に注意・制限事項はありません。
developからアセットのエクスポート
start-asset-bundle-export-job コマンドでアセットをエクスポートします。
aws quicksight start-asset-bundle-export-job \
--no-include-all-dependencies \
--aws-account-id "${AWS_ACCOUNT_ID}" \
--asset-bundle-export-job-id "${JOB_ID}" \
--resource-arns "${QUICKSIGHT_RESOUCE_ARN}" \
--export-format CLOUDFORMATION_JSON
--no-include-all-dependencies : これを指定しないと、依存するリソースがすべてファイルに含まれてしまいます。 例えば複数分析が参照しているデータセットがある場合に、分析をエクスポートするとそれぞれにデータセットが含まれてしまうのでこれを防ぎます。
--asset-bundle-export-job-id : 任意の文字列です。このあとファイルをダウンロードするときに使用します。
--resource-arns : 対象のリソースのARNです。
--export-format : CloudFormation形式かQuickSight独自のjson形式かを選択します。今回はCloudFormationを使います。
このコマンドを実行するとエクスポートのJOBが開始されるだけなので、結果は describe-asset-bundle-export-job コマンドで確認します。
aws quicksight describe-asset-bundle-export-job \
--aws-account-id "${AWS_ACCOUNT_ID}" \
--asset-bundle-export-job-id "${JOB_ID}" \
--output text \
--query JobStatus
--asset-bundle-export-job-id で start-asset-bundle-export-job と同じJOB IDを指定するとエクスポートJOBの状態が取得できます。
上記コマンドで "SUCESSFUL" が返ってきたら、今度は同じコマンドで DownloadUrl を取得します。(シェルスクリプトで使う関係でこの様になっていますが、同時に取得しても問題ありません)
aws quicksight describe-asset-bundle-export-job \
--aws-account-id "${AWS_ACCOUNT_ID}" \
--asset-bundle-export-job-id "${JOB_ID}" \
--output text \
--query DownloadUrl
DownloadUrlに設定されているS3のPresignedUrlからファイルをダウンロードします。
productionに持っていくものをすべて上記コマンドでCloudFormationテンプレート形式でダウンロードします。
ダウンロードされたデータセットのテンプレート
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Generated by AWS QuickSight for 001 started at 2024-06-13 07:58:38 UTC",
"Resources" : {
"3eeff8a3b0284cbdb7eb2E3731" : {
"Properties" : {
"AwsAccountId" : {
"Fn::Sub" : "${AWS::AccountId}"
},
"DataSetId" : "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"DataSetUsageConfiguration" : {
"DisableUseAsDirectQuerySource" : false,
"DisableUseAsImportedSource" : false
},
"FieldFolders" : { },
"ImportMode" : "DIRECT_QUERY",
"LogicalTableMap" : {
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" : {
"Alias" : "table1",
"DataTransforms" : [ {
"ProjectOperation" : {
"ProjectedColumns" : [ "col1", "col2" ]
}
} ],
"Source" : {
"PhysicalTableId" : "xxxxxxxx-xxxx-xxxx-xxxx
xxxxxxxxxxxx"
}
}
},
"Name" : "サンプルデータセット",
"PhysicalTableMap" : {
"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" : {
"RelationalTable" : {
"DataSourceArn" : {
"Fn::Sub" :
"arn:${AWS::Partition}:quicksight:${AWS::Region}:${AWS::AccountId}:datasource/RedshiftDataSource"
},
"InputColumns" : [ {
"Name" : "col1",
"Type" : "STRING"
}, {
"Name" : "col2",
"Type" : "STRING"
} ],
"Name" : "hashi_table1",
"Schema" : "ofk"
}
}
}
},
"Type" : "AWS::QuickSight::DataSet"
}
}
}
ここで DataSourceArnが定義されています。RegionとAcountIdは変数になっていますが、最後のDataSourceIdの部分が変更できません。そのため、DataSourceIdをdevelopとproductionで同一にしています。
ここでひと工夫
Descriptionプロパティには start-asset-bundle-export-job の実行時刻が入っています。
このままではQuickSight上で変更していないのに常にテンプレートに差分が発生してしまうので、jqコマンドなどで除外しておいたほうが良いです。
start-asset-bundle-export-jobの--cloud-formation-override-property-configurationオプションは使えるか?
--cloud-formation-override-property-configurationオプションは、指定したプロパティをCloudFormationテンプレート化時のパラメータにしてくれるものです。
DataSourceIdを可変=CloudFormationテンプレートパラメータにすれば、前述のようにDataSourceIdを固定化する必要はなさそうに見えます。
しかしながら、パラメータ化可能なものは決まっていて任意のプロパティのみをパラメータ化することはできませんでした。
productionへのデプロイ
start-asset-bundle-export-job でCloudFormation形式で出力したので、productionへの反映はテンプレートをデプロイするだけです。
Permissionの設定
CloudFormationでデプロイしただけではPermissionが設定されていないため誰からも参照できません。管理画面から明示的に共有する必要があります。
この操作は初回のみでOKです。
分析とダッシュボードの紐づけ
分析を更新して公開するとき通常はダッシュボードと紐づけがされていますが、CloudFormationで作成した直後はこのひも付けが無いために、ダッシュボードを書き換えることができません。
以下のコマンドを使って分析とダッシュボードを紐づけます。
aws quicksight update-dashboard-links \
--aws-account-id "${AWS_ACCOUNT_ID}" \
--dashboard-id "${DASHBOARD_ID}"
--link-entities "${ANALYSIS_ARN}"
--dashboard-id : 対象のダッシュボードのID
--link-entities : 対象の分析のARN
IDとARNが混在していることに注意してください。
ここでハマったときにわかったのですが、ダッシュボード自体は分析に依存していないようです。
start-asset-bundle-export-job で得られたダッシュボードのテンプレートを見ると丸ごと分析の定義が含まれていることがわかります。
おそらくダッシュボードは、分析が公開された時点でその実態を内包して完結しているのだと思います。
さらに分析をダッシュボードとして公開するときには update-dashboard-links で設定されているリンク対象から「既存のダッシュボードを置き換える」の対象を決定しているようです。
まとめ
以上でdevelopで作成したQuickSightのアセットをproductionにデプロイできるようになりました。
以降はエクスポート → CloudFormationのStack更新で変更を反映できます。
CLIとCloudFormationでデプロイできるということは継続的デプロイメントのパイプラインに載せることも可能になってくると思います。
Comments