はじめに
クロスアカウントの S3 バケット間でデータを移行する場合いくつか選択肢がありますが、
最近 AWS DataSync がクロスアカウントの移行に対応したようなので試しました。
比較的に容易に、かつ柔軟性のある設定でデータの移行ができました。
公式ブログの内容に沿って行っていますが、曖昧な箇所があり分かりづらいため日本語のドキュメントを残します。
送信先 S3 バケットの作成
移行対象の送信元 S3 バケットはすでにあるとして、送信先アカウントでバケットを作成します。
以下、 AWS CloudFormation のコードを示しながら進めます。
S3BucketDataSyncDist:
Type: 'AWS::S3::Bucket'
DeletionPolicy: 'Retain'
Properties:
AccessControl: 'Private'
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: 'AES256'
BucketName: 'datasync-dist'
VersioningConfiguration:
Status: 'Enabled'
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
IAM ロールの作成
送信先アカウントで DataSync が送信元、送信先それぞれの Location にアクセスするための IAM ロールを作成します。
実際の移行を行う DataSync の Task も送信先アカウントで実行することになります。
${S3BucketDataSyncSource}
には送信元バケット名を渡してください。
IAMRoleDataSyncSourceLocation:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Service: 'datasync.amazonaws.com'
Action: 'sts:AssumeRole'
RoleName: 'datasync-source-location-role'
Policies:
- PolicyDocument:
Statement:
- Action:
- 's3:GetBucketLocation'
- 's3:ListBucket'
- 's3:ListBucketMultipartUploads'
Effect: 'Allow'
Resource: !Sub 'arn:aws:s3:::${S3BucketDataSyncSource}'
- Action:
- 's3:AbortMultipartUpload'
- 's3:DeleteObject'
- 's3:GetObject'
- 's3:ListMultipartUploadParts'
- 's3:PutObjectTagging'
- 's3:GetObjectTagging'
- 's3:PutObject'
Effect: 'Allow'
Resource: !Sub 'arn:aws:s3:::${S3BucketDataSyncSource}/*'
PolicyName: 'datasync-source-location-policy'
IAMRoleDataSyncDistLocation:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: 'Allow'
Principal:
Service: 'datasync.amazonaws.com'
Action: 'sts:AssumeRole'
RoleName: 'datasync-dist-location-role'
Policies:
- PolicyDocument:
Statement:
- Action:
- 's3:GetBucketLocation'
- 's3:ListBucket'
- 's3:ListBucketMultipartUploads'
Effect: 'Allow'
Resource: !GetAtt S3BucketDataSyncDist.Arn
- Action:
- 's3:AbortMultipartUpload'
- 's3:DeleteObject'
- 's3:GetObject'
- 's3:ListMultipartUploadParts'
- 's3:PutObjectTagging'
- 's3:GetObjectTagging'
- 's3:PutObject'
Effect: 'Allow'
Resource: !Sub ${S3BucketDataSyncDist.Arn}/*
PolicyName: 'datasync-dist-location-policy'
送信元バケットのバケットポリシーの追加
送信元アカウントで送信元バケットのバケットポリシーに、作成した送信元 Location の IAM ロール(datasync-source-location-role
)と、
このあと aws datasync create-location-s3
を実行する送信先アカウントのユーザ(DataSyncDistAccountUserArn
)に対して権限を与えます。
YAML 内の S3BucketDataSyncSource
は送信元バケットのリソースです。
AWS CLI を実行する際のユーザの ARN は aws sts get-caller-identity
で確認することができます。
S3BucketPolicyDataSyncSource:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref S3BucketDataSyncSource
PolicyDocument:
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub 'arn:aws:iam::${DataSyncDistAccountId}:role/datasync-source-location-role'
- !Ref DataSyncDistAccountUserArn
Action:
- 's3:GetBucketLocation'
- 's3:ListBucket'
- 's3:ListBucketMultipartUploads'
- 's3:AbortMultipartUpload'
- 's3:DeleteObject'
- 's3:GetObject'
- 's3:ListMultipartUploadParts'
- 's3:PutObject'
- 's3:GetObjectTagging'
- 's3:PutObjectTagging'
Resource:
- !GetAtt S3BucketDataSyncSource.Arn
- !Sub ${S3BucketDataSyncSource.Arn}/*
DataSync Location の作成
送信先アカウントで Location を作成します。
CloudFormation に AWS::DataSync::LocationS3
リソースがありますが、
クロスアカウントの作成に対応していないようで、 Invalid request provided: Please provide a bucket in the us-east-1 region where DataSync is currently used.
といったエラーとなるため、
送信元 Location のみ AWS CLI から作成します。
AWS CLI で送信先アカウントに対して以下のコマンドで送信元 Location を作成します。
aws datasync create-location-s3 --s3-bucket-arn arn:aws:s3:::[送信元バケット名] --s3-config '{"BucketAccessRoleArn":"arn:aws:iam::[送信先アカウントID]:role/datasync-source-location-role"}'
この時、コマンドを実行するユーザはバケットポリシで許可したユーザである必要があります。
送信先 Location は問題なく CloudFormation で作成することができます。
DataSyncLocationS3DistLocation:
Type: AWS::DataSync::LocationS3
Properties:
S3BucketArn: !GetAtt S3BucketDataSyncDist.Arn
S3Config:
BucketAccessRoleArn: !GetAtt IAMRoleDataSyncDistLocation.Arn
S3 のレプリケーションを使うと Prefix によって送信元、送信先で同じフォルダ間でしか移行ができないのに対し、
DataSync は Location で Subdirectory
を指定することができるため、
送信元、送信先でそれぞれ別々のフォルダを指定することができるところが良いです。
ロググループの作成(オプション)
送信先アカウントで DataSync の Task 実行時にログを吐くためのロググループとリソースポリシーを作成します。ログが必要ないなら不要です。
LogsLogGroupDataSync:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: datasync
RetentionInDays: 14
LogsResourcePolicy:
Type: AWS::Logs::ResourcePolicy
Properties:
PolicyName: datasync-log-resource-policy
PolicyDocument: "{\"Statement\": [{\"Sid\": \"DataSyncLogsToCloudWatchLogs\", \"Effect\": \"Allow\", \"Action\": [\"logs:PutLogEvents\", \"logs:CreateLogStream\"], \"Principal\": {\"Service\": \"datasync.amazonaws.com\"}, \"Resource\": \"*\"}], \"Version\": \"2012-10-17\"}"
PolicyDocument
が String しか受け付けてくれませんが、 AWS::IAM::ManagedPolicy
のように JSON で受け付けてくれると良いですね。
DataSync Task の作成
送信先アカウントで DataSync の Task を作成します。
YAML の DataSyncSourceLocationArn
は aws datasync create-location-s3
の結果で返される LocationArn
です。
aws datasync list-locations
でも確認することができます。
AWS::DataSync::Task
の細かな仕様は公式ドキュメントを参照してください。
DataSyncTask:
Type: AWS::DataSync::Task
Properties:
SourceLocationArn: !Ref DataSyncSourceLocationArn
DestinationLocationArn: !GetAtt DataSyncLocationS3DistLocation.LocationArn
Excludes:
- FilterType: SIMPLE_PATTERN
Value: /logs
Schedule:
ScheduleExpression: 'rate(1 hour)'
Options:
OverwriteMode: NEVER
TransferMode: CHANGED
VerifyMode: ONLY_FILES_TRANSFERRED
LogLevel: BASIC
CloudWatchLogGroupArn: !Select [0, !Split [':*', !GetAtt LogsLogGroupDataSync.Arn]]
おわりに
S3 のレプリケーションだと同じフォルダ間でしか移行できなかったり、
バッチオペレーションだと定期実行ができなかったりと、不都合があった部分が DataSync によってうまく解決できました。
ただし、もちろん料金は発生するためきちんと見積もりをしてから実行するのが良いでしょう。
https://aws.amazon.com/jp/datasync/pricing/