概要
とあるAWSのS3バケットのオブジェクトを別のAWSアカウントのS3に同期してほしい旨の依頼を受けて対応しました。今回は別のAWSアカウントへの同期、かつ既存のオブジェクトとこれから流入してくる新規のオブジェクトを全て同期するという考慮事項がいくつかある要件だったので、自分の理解の整理も兼ねて対応した際のログを残しておきます。
当該環境ではCloudFormation(以下、CFnとします。)を利用してAWSリソースを管理しているので、コードも合わせて載せていきます。
前提として、このS3バケットには元々大量のオブジェクトが存在していたため、awscliによるs3 syncはIAMロールのセッション期間の上限の12時間を超えてしまうため利用できませんでした。
ちょうどこの相談を受けた少し前にs3 batch replicationが既存オブジェクトの同期に対応したのでこれを利用することにしました。
構成
これから順を追って作業の流れを書いていきますが、最終的な構成は以下になります。
今回はS3バケットが元から存在していたので、レプリケーション元のアカウントには、レプリケーションに利用するIAMロールとバッチオペレーションで利用するIAMロールを作成し、レプリケーション先のアカウントには対象のS3バケットにレプリケーションを許可するバケットポリシーをアタッチします。
作業の流れ
作業の流れは以下のようになります。
ここからはレプリケーション元のS3バケットをSrc S3、レプリケーション先のものをDest S3と表記します。
1.Src側のAWS作業:レプリケーションを行うためのIAMロールを作成する
2.Dest側のAWS作業:Dest S3に1.で作成したIAMロールからの操作を許可するバケットポリシーを設定する
3.Src側のAWS作業:Src S3にレプリケーションルールを設定する
4.Src側のAWS作業:バッチオペレーションを実行するためのIAMロールを作成する
5.Src側のAWS作業:バッチオペレーションを実行する
1.Src側のAWS作業:レプリケーションを行うためのIAMロールを作成する
まずはレプリケーションする際に利用するIAMロールを作成します。
必要な権限はAWS公式ドキュメントの許可のセットアップページを参考に設定しました。ここではSrc S3の情報とオブジェクト情報の取得とDest S3に対するレプリケーション操作を許可しています。
CFnのテンプレートとしては以下のようになります。
IAMRoleReplicateSrcS3:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- s3.amazonaws.com
Policies:
- PolicyDocument:
Statement:
- Effect: 'Allow'
Action:
- 's3:GetReplicationConfiguration'
- 's3:ListBucket'
- 's3:GetObjectVersionForReplication'
- 's3:GetObjectVersionAcl'
- 's3:GetObjectVersionTagging'
Resource:
- !Sub 'arn:aws:s3:::${SrcBucketName}'
- !Sub 'arn:aws:s3:::${SrcBucketName}/*'
PolicyName: 'access-source-s3'
- PolicyDocument:
Statement:
- Effect: 'Allow'
Action:
- 's3:ReplicateObject'
- 's3:ReplicateDelete'
- 's3:ReplicateTags'
Resource:
- !Sub 'arn:aws:s3:::${DestBucketName}/*'
PolicyName: 'replicate-destination-s3'
注意点としては、Src S3でバージョニングが有効化されている場合はバージョニング情報を取得する権限も必要になることです。権限が不足している場合は以下のように警告され、レプリケーションが開始されません。
この作業により、レプリケーションする際に利用するIAMロールが作成されました。
2.Dest側のAWS作業:Dest S3に2で作成したIAMロールからの操作を許可するバケットポリシーを設定する
次にDest S3にバケットポリシーを追加していきます。
コードとしては以下のようになります。やっていることは、1.の手順で作成したIAMロールに対して、レプリケーションの許可とレプリケーション先のバケット情報の取得の許可になります。
S3BucketPolicyReplicationDestS3:
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref DestBucketName
PolicyDocument:
Version: '2012-10-17'
Statement:
-
Effect: 'Allow'
Principal:
AWS: !Ref IAMRoleReplicateSrcS3Arn # 1.で作成したIAMRoleのArnを指定する
Action:
- 's3:ReplicateDelete'
- 's3:ReplicateObject'
- 's3:ReplicateTags'
Resource: !Sub 'arn:aws:s3:::${DestBucketName}/*'
-
Effect: 'Allow'
Principal:
AWS: !Ref IAMRoleReplicateSrcS3Arn # 1.で作成したIAMRoleのArnを指定する
Action:
- 's3:List*'
- 's3:GetBucketVersioning'
- 's3:PutBucketVersioning'
Resource: !Sub 'arn:aws:s3:::${DestBucketName}'
ここまででレプリケーションを行うための事前準備が整いました。現時点での構成はこのようになっています。
3.Src側のAWS作業:Src S3にレプリケーションルールを設定する
次にSrcのS3にレプリケーションルールを設定していきます。今回は既存のSrc S3の設定にReplicationConfigurationプロパティを追加していきます。レプリケーション先として、対象のアカウントID、S3バケットのArnを指定します。
Resources:
S3BucketReplicationSrc:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: !Ref SrcBucketName
#### ~~~ omit ~~~
#### ここより下を追加する
ReplicationConfiguration:
- Role: !GetAtt IAMRoleReplicateSrcS3.Arn # 1.の手順で作成したIAMロールのArnを指定する
Rules:
- Destination:
Account: !Ref DestinationAccountID
Bucket: !Sub 'arn:aws:s3:::${DestBucketName}'
Status: Enabled
この設定を反映すると、それ以降にSrc S3に新たに作成されたオブジェクトはDest S3にレプリケーションされます。
4.Src側のAWS作業:バッチオペレーションを実行するためのIAMロールを作成する
上記の手順で新規のオブジェクトが同期されるようになりましたが、既存のオブジェクトはまだ同期されていません。ここからは既存のオブジェクトを同期するための設定をしていきます。冒頭でも述べたとおり、バッチオペレーションという機能を利用することで実現可能です。
まずは、バッチオペレーションで利用するIAMロールを作成します。必要な権限は公式ドキュメントのバッチレプリケーション用の IAM ポリシーの設定を参考に設定しました。
IAMRoleBatchOperation:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- batchoperations.s3.amazonaws.com
Policies:
- PolicyName: BatchOperaionPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:GetObjectAcl
- s3:GetObjectTagging
- s3:InitiateReplication
- s3:GetReplicationConfiguration
- s3:PutInventoryConfiguration
Resource:
- !Sub 'arn:aws:s3:::${SrcBucketName}'
- !Sub 'arn:aws:s3:::${SrcBucketName}/*'
- Effect: Allow
Action:
- s3:PutObject
- s3:PutObjectAcl
- s3:PutObjectTagging
- s3:PutObjectLegalHold
- s3:PutObjectRetention
- s3:GetBucketObjectLockConfiguration
Resource:
- !Sub 'arn:aws:s3:::${DestBucketName}/*'
正直なところ、私はこのバッチオペレーション用のIAMロールが必要ということに気づけずにハマりました。新規のオブジェクトをレプリケーションするのも、既存のオブジェクトをレプリケーションするのも権限的には変わらないだろうと思い込み、バッチレプリーションを実行する際のロールに1.で作成したロールを指定していたところ、 Error occurred when preparing manifest: Failed to assume role.
というエラーが発生しました。
冷静にエラー内容を見ると、権限不足ではなく、assume roleができないことで怒られています。そのため、利用しようとしたサービスから、指定したIAMロールにassume roleできないのはなぜかという点で、もしかしたら別のロールが必要なのかも?とアタリをつけて調べて、上記のページを発見して解決することができました。
確かにバッチオペレーション用のロールでは、batchoperations.s3.amazonaws.com
にassume roleを許可しているのに対し、レプリケーション用のロールは s3.amazonaws.com
に許可しているので、全て調べ終わった後の今なら納得がいきます。
5.Src側のAWS作業:バッチオペレーションを実行する
ここからはAWSコンソール上で作業していきます。
Src S3のレプリケーションの設定を表示すると「レプリケーションルール」の画面に遷移するので、レプリケーションジョブを作成します
を押下します。S3サービスのトップ画面にあるメニューからもバッチオペレーションの操作画面に飛べるのですが、こちらから飛ぶとレプリケーションルールに設定したバケットの情報が選択された状態で進めるので、設定が少し楽になります。
次の「リージョンとマニフェストを選択」では、本来であればジョブを作成するリージョンとソースバケット、フィルター条件の指定が必要になりますが、レプリケーションルールの画面からジョブを作成している場合は、これらの項目は入力済みなのでスキップできます。
「オペレーションを選択」の画面に遷移します。レプリケート
を選択して次へ
を押下します。私の環境ではレプリケート以外は非アクティブとなっていました。
次に追加のオプションを設定します。アクセス許可
のメニューから既存のIAMロールから選択
を選び、IAMロールのプルダウンメニューから、手順4.で作成したIAMロール
を選択し、次へ
を押下します。確認画面が表示されるので問題なければ完了します。
ジョブが作成されます。初期のステータスは準備中です。準備が完了すると、ステータスが「実行のための確認待ち」に変わるため、対象のジョブを選択して「ジョブを実行」を押下します。このオペレーションが忘れがちで、行わないといつまで経ってもジョブが開始されないので、忘れずに実行します。実行後はステータスが「アクティブ」に変わり、「完了済み」になったら全てのオブジェクトのレプリケーションが完了します。
さいごに
今回は割と要件が色々乗っかったS3のレプリケーションを試しました。既存オブジェクトの同期にはバッチオペレーションの他、レプリケーションルールからも既存オブジェクトを同期できそうな設定があったのですが、うまく動きませんでした。
試行錯誤していたので設定に誤りがあったのか、このドキュメントではレプリケーションが有効化されてから実際にレプリケーションが開始されるまでに、最大で48時間かかると言及されているので、開始前に設定を変えてしまったかは定かではありません。
Note: Unlike the replication of newly uploaded object, it takes up to 48 hours for the replication to start after the replication rule is set up for existing objects.
ただ、1回の検証サイクルで48時間使うことを許容できないケースもありますし、GithubのIssueでも言及されている通り、上位互換のバッチオペレーションがリリースされてからは開発も停滞気味なので、既存オブジェクトの同期にわざわざレプリケーションルールを利用する必要はないかなという感想をもちました。