Created
April 30, 2019 02:44
-
-
Save metaskills/7e5c6e6cf7d51a00eed27adf92372a82 to your computer and use it in GitHub Desktop.
Cross-Region Replication S3 Buckets - Single CloudFormation Template.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
aws cloudformation deploy \ | |
--region ${AWS_DEFAULT_REGION} \ | |
--template-file "template.yaml" \ | |
--stack-name "my-buckets-${RAILS_ENV}" \ | |
--s3-bucket "$CLOUDFORMATION_BUCKET" \ | |
--s3-prefix "my-buckets-${RAILS_ENV}" \ | |
--capabilities "CAPABILITY_IAM" \ | |
--tags \ | |
"env=${STAGE_ENV}" \ | |
"group=ecommerce" \ | |
"application=myapp" \ | |
--parameter-overrides \ | |
StageEnv="$STAGE_ENV" \ | |
NamePrefix="$NAME_PREFIX" \ | |
TagGroup="$TAG_GROUP" \ | |
TagApp="$TAG_APP" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
AWSTemplateFormatVersion: '2010-09-09' | |
Description: Cross-Region Replicated S3 Buckets | |
Parameters: | |
StageEnv: | |
Type: String | |
Default: development | |
AllowedValues: | |
- development | |
- staging | |
- prod | |
NamePrefix: | |
Type: String | |
Region1: | |
Type: String | |
Default: us-east-1 | |
Region2: | |
Type: String | |
Default: us-west-2 | |
TagGroup: | |
Type: String | |
TagApp: | |
Type: String | |
Resources: | |
# Track B | |
Region2FinFunctionTrigger: | |
Type: Custom::Region2FinFunctionTrigger | |
DependsOn: Region2FinFunction | |
Properties: | |
ServiceToken: !GetAtt Region2FinFunction.Arn | |
Region2BucketName: !Sub ${NamePrefix}-${StageEnv}-${Region2} | |
Region2BucketRegion: !Ref Region2 | |
Region2RepRole: !GetAtt Region2RepRole.Arn | |
Region1BucketName: !Sub ${NamePrefix}-${StageEnv}-${Region1} | |
Region2FinFunction: | |
Type: AWS::Lambda::Function | |
DependsOn: Region2FinFunctionRole | |
Properties: | |
Code: | |
ZipFile: | |
!Sub | | |
var aws = require("aws-sdk"); | |
var response = require("cfn-response"); | |
exports.handler = function(event, context, callback) { | |
var s3 = new aws.S3({ region: event.ResourceProperties.Region2BucketRegion }); | |
var bucketName = event.ResourceProperties.Region2BucketName; | |
if (event.RequestType === "Create") { | |
var repParams = { | |
Bucket: bucketName, | |
ReplicationConfiguration: { | |
Role: event.ResourceProperties.Region2RepRole, | |
Rules: [ | |
{ | |
ID: 'crr-reciprocal', | |
Destination: { | |
Bucket: "arn:aws:s3:::" + event.ResourceProperties.Region1BucketName, | |
StorageClass: "STANDARD" | |
}, | |
Prefix: "", | |
Status: "Enabled" | |
} | |
] | |
} | |
}; | |
s3.putBucketReplication(repParams, function(err, data) { | |
if (err) { | |
response.send(event, context, response.FAILED, err, "putBucketReplication"); | |
callback(null); | |
} else { | |
response.send(event, context, response.SUCCESS, {}, bucketName); | |
callback(null); | |
} | |
}); | |
} else { | |
response.send(event, context, response.SUCCESS, {}, bucketName); | |
callback(null); | |
} | |
}; | |
FunctionName: !Sub ${NamePrefix}-region2fin-func | |
Handler: index.handler | |
Role: !GetAtt Region2FinFunctionRole.Arn | |
Runtime: nodejs8.10 | |
Timeout: 30 | |
Region2FinFunctionRole: | |
Type: AWS::IAM::Role | |
DependsOn: Region2RepRole | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: | |
- lambda.amazonaws.com | |
Action: | |
- 'sts:AssumeRole' | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/AdministratorAccess | |
Region2RepRole: | |
Type: AWS::IAM::Role | |
DependsOn: Region1Bucket | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: | |
- s3.amazonaws.com | |
Policies: | |
- PolicyName: !Sub ${NamePrefix}-region2-reprole | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: | |
- s3:* | |
Effect: Allow | |
Resource: !Sub arn:aws:s3:::${NamePrefix}-${StageEnv}-* | |
# Track A | |
Region1Bucket: | |
Type: AWS::S3::Bucket | |
DependsOn: Region2FunctionTrigger | |
Properties: | |
BucketName: !Sub ${NamePrefix}-${StageEnv}-${AWS::Region} | |
ReplicationConfiguration: | |
Role: !GetAtt Region1RepRole.Arn | |
Rules: | |
- Destination: | |
Bucket: !Sub arn:aws:s3:::${NamePrefix}-${StageEnv}-${Region2} | |
StorageClass: STANDARD | |
Id: Region2Rep | |
Prefix: '' | |
Status: Enabled | |
VersioningConfiguration: | |
Status: Enabled | |
Tags: | |
- Key: env | |
Value: !Ref StageEnv | |
- Key: group | |
Value: !Ref TagGroup | |
- Key: application | |
Value: !Ref TagApp | |
Region2FunctionTrigger: | |
Type: Custom::Region2FunctionTrigger | |
DependsOn: Region2Function | |
Properties: | |
ServiceToken: !GetAtt Region2Function.Arn | |
Region2BucketName: !Sub ${NamePrefix}-${StageEnv}-${Region2} | |
Region2BucketRegion: !Ref Region2 | |
Region2Function: | |
Type: AWS::Lambda::Function | |
DependsOn: Region2FunctionRole | |
Properties: | |
Code: | |
ZipFile: | |
!Sub | | |
var aws = require('aws-sdk'); | |
var response = require('cfn-response'); | |
exports.handler = function(event, context, callback){ | |
var s3 = new aws.S3({region: event.ResourceProperties.Region2BucketRegion}); | |
var bucketName = event.ResourceProperties.Region2BucketName; | |
var bucketParams = { Bucket: bucketName }; | |
if (event.RequestType === 'Create'){ | |
s3.createBucket(bucketParams, function(err, data) { | |
if (err) { | |
response.send(event, context, response.FAILED, err, 'createBucket'); | |
callback('createBucket error'); | |
} else { | |
s3.putBucketVersioning({ | |
Bucket: bucketName, | |
VersioningConfiguration: { Status: 'Enabled' } | |
}, function(err, data) { | |
if (err) { | |
response.send(event, context, response.FAILED, err, 'putBucketVersioning'); | |
callback('putBucketVersioning error'); | |
} | |
else { | |
s3.putBucketTagging({ | |
Bucket: bucketName, | |
Tagging: { | |
TagSet: [ | |
{ Key: 'env', Value: '${StageEnv}' }, | |
{ Key: 'group', Value: '${TagGroup}' }, | |
{ Key: 'application', Value: '${TagApp}' } | |
] | |
} | |
}, function(err, data) { | |
if (err) { | |
response.send(event, context, response.FAILED, err, 'putBucketTagging'); | |
callback('putBucketTagging error'); | |
} | |
else { | |
response.send(event, context, response.SUCCESS, {}, bucketName); | |
callback(null); | |
} | |
}); | |
} | |
}); | |
} | |
}); | |
} else if (event.RequestType === 'Delete'){ | |
s3.deleteBucket(bucketParams, function(err, data) { | |
if (err) { | |
response.send(event, context, response.SUCCESS, {}, bucketName); | |
callback('deleteBucket error'); | |
} else { | |
response.send(event, context, response.SUCCESS, {}, bucketName); | |
callback(null); | |
} | |
}); | |
} else { | |
response.send(event, context, response.SUCCESS, {}, bucketName); | |
callback(null); | |
} | |
}; | |
FunctionName: !Sub ${NamePrefix}-region2-func | |
Handler: index.handler | |
Role: !GetAtt Region2FunctionRole.Arn | |
Runtime: nodejs8.10 | |
Timeout: 30 | |
Region2FunctionRole: | |
Type: AWS::IAM::Role | |
DependsOn: Region1RepRole | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Effect: Allow | |
Principal: | |
Service: | |
- lambda.amazonaws.com | |
Action: | |
- 'sts:AssumeRole' | |
ManagedPolicyArns: | |
- arn:aws:iam::aws:policy/AmazonS3FullAccess | |
- arn:aws:iam::aws:policy/CloudWatchLogsFullAccess | |
Region1RepRole: | |
Type: AWS::IAM::Role | |
Properties: | |
AssumeRolePolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: sts:AssumeRole | |
Effect: Allow | |
Principal: | |
Service: | |
- s3.amazonaws.com | |
Policies: | |
- PolicyName: !Sub ${NamePrefix}-region1-reprole | |
PolicyDocument: | |
Version: '2012-10-17' | |
Statement: | |
- Action: | |
- s3:* | |
Effect: Allow | |
Resource: !Sub arn:aws:s3:::${NamePrefix}-${StageEnv}-* | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment