Data FirehoseからS3へクロスアカウントでデータ送信してみた
はじめに
最近Data Firehoseから別アカウントのS3へデータを送信したいと思うことがありました。 具体的にはAmazon Monitronで収集したデータを別アカウントのS3バケットに送信したいというものでした。
こちらを実装するのに、ポリシー周りで非常に苦労したので方法を残しておきたいと思います。 最終的なゴールはこの構成です。
実際に必要なものを追加するとこんな感じです。
それでは1つずつ手順を追いながら作成していきましょう! どのアカウントで何が何に対して操作する権限なのかを考えながら進めていきましょう。
注意:Data Firehoseで動的パーティショニングを有効化している場合は、クロスアカウントでのデータ配信がサポートされていません(執筆時点)
アカウントBでS3の作成
まずはアカウントBでS3バケットを作成します。
デフォルトの設定でS3バケットを作成します。 こちらのS3バケットは一旦arnだけ控えておきます。
後ほどバケットポリシーを変更します。 (この時点でバケットポリシーを変更しようとすると存在しないロールを指定することになりエラーになる)
アカウントAでKinesis Data Streamを作成
アカウントAでData Streamを作成します。 こちらは今回おまけ程度なのであまり気にせず進みます。
今回は検証なので、コストの低いプロビジョンドモードで作成します。
他はデフォルトです。
アカウントAでData Firehoseを作成
次にアカウントAでData Firehoseを作成します。 ソースは先ほど作成したData Streamです。
次に送信先は適当なS3を選択します。 コンソール画面からは別アカウントのS3を選択できないので、後ほどaws cliを使用して送信先のS3を変更します。
送信先のS3を変更
それでは送信先のS3を変更します。 まずは送信先の変更時に必要なVersionIdとDestinationIdを取得します。 <>内の値はそれぞれ読み替えてください。
aws firehose describe-delivery-stream --delivery-stream-name <your-firehose-name> | jq .DeliveryStreamDescription.VersionId
aws firehose describe-delivery-stream --delivery-stream-name <your-firehose-name> | jq '.DeliveryStreamDescription.Destinations[].DestinationId'
それぞれ出力された値を控えます。 次にFirehoseの出力先を変更します。
aws firehose update-destination --delivery-stream-name <your-firehose-name> --current-delivery-stream-version-id <VersionId> --destination-id <DestinationId> --s3-destination-update BucketARN=<アカウントBのS3のarn>
これで出力先のS3が変更できました。
アカウントAでFirehose用のIAMロール修正
アカウントAでFirehoseにアタッチされたIAMロールの内容を修正します。 アカウントBのS3を操作する権限をアタッチするためのIAMロールです。
対象のfirehose→設定タブ→サービスアクセスの順に進みます。
Firehose作成時に自動的に作成されるIAMロールがアタッチされていることがわかります。
こちらのIAMロールのポリシーを編集します。 以下の部分を
{ "Sid": "", "Effect": "Allow", "Action": [ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::demo-bucket", "arn:aws:s3:::demo-bucket/*" ] },
以下のように編集します。
{ "Sid": "", "Effect": "Allow", "Action": [ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:PutObject" "s3:PutObjectAcl" ], "Resource": [ "<アカウントBのバケットarn>", "<アカウントBのバケットarn>/*" ] },
許可一覧にs3:PutObjectAcl
を追加しているのと、Resourceの対象バケットをアカウントBのバケットに修正しています。
クロスアカウントでデータを送信する際はこのs3:PutObjectAcl
が追加で必要になるようです。
アカウントBでバケットポリシー編集
アカウントBでS3のバケットポリシーを編集します。 バケットポリシーではアカウントAのIAMロールがバケットを操作することを許可するポリシーを記載します。
アカウントBで最初に作成したS3バケットを開きます。
作成したバケットの アクセス許可>バケットポリシーの編集 をクリックします。 以下のバケットポリシーを入力します。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "AWS": "<アカウントAのIAMロールのarn>" }, "Action": [ "s3:AbortMultipartUpload", "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:PutObject", "s3:PutObjectAcl" ], "Resource": [ "<アカウントBのバケットのarn>", "<アカウントBのバケットのarn>/*" ] } ] }
<アカウントAのIAMロールのarn>には先ほど編集したIAMロールのarnを <アカウントBのバケットarn>には現在編集しているバケットのarnを入力します。
ダミーデータ送信
準備が整ったので、アカウントAでData Streamにデータを送信してみます。
以下のコマンドを実行します。 --dataと--partition-keyは適当な値を入れてます。(なんでもいいです)
aws kinesis put-record --stream-name <your-stream-name> --data "VGhpcyBpcyBhIHBlbg==" --partition-key 1
アカウントBでS3にデータが保存されているか確認しましょう。 データが保存されていますね。
Firehoseのデモデータ送信機能もあるので、こちらを使ってみるのもいいと思います。
まとめ
図だけを見ると、大したことないな〜と思っていましたが、実際はめちゃくちゃハマりました。 クロスアカウントはポリシーの設定がややこしく、コンソール上から設定ができない部分もあるのが非常に難しい点です。 こういった複雑な設定は図におこしながら1つずつ設定を確認していくのが一番の近道だと再認識しました。