Skip to content

Instantly share code, notes, and snippets.

@metaskills
Created April 30, 2019 02:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save metaskills/7e5c6e6cf7d51a00eed27adf92372a82 to your computer and use it in GitHub Desktop.
Save metaskills/7e5c6e6cf7d51a00eed27adf92372a82 to your computer and use it in GitHub Desktop.
Cross-Region Replication S3 Buckets - Single CloudFormation Template.
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"
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