Restructure everything and automate deployment. Fix nasty Javascript.
This commit is contained in:
parent
c718808aee
commit
1de3a78cc2
22 changed files with 2081 additions and 1061 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -27,5 +27,6 @@ build/Release
|
|||
node_modules
|
||||
|
||||
# Other local things to ignore
|
||||
cloudformation-helpers.zip
|
||||
.DS_Store
|
||||
|
||||
/out
|
||||
|
|
|
@ -1,134 +0,0 @@
|
|||
var Promise = require('bluebird'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
apiGateway = Promise.promisifyAll(new AWS.APIGateway());
|
||||
|
||||
// Exposes the SNS.subscribe API method
|
||||
function CreateApi(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
CreateApi.prototype = Object.create(base.Handler.prototype);
|
||||
CreateApi.prototype.handleCreate = function() {
|
||||
var p = this.event.ResourceProperties;
|
||||
var rootObject = this;
|
||||
return apiGateway.createRestApiAsync({
|
||||
name: p.name,
|
||||
description: p.description
|
||||
})
|
||||
.then(function(apiData) {
|
||||
return rootObject.setReferenceData({ restApiId: apiData.id }) // Set this immediately, in case later calls fail
|
||||
.then(function() {
|
||||
return apiGateway.getResourcesAsync({
|
||||
restApiId: apiData.id
|
||||
})
|
||||
.then(function(resourceData) {
|
||||
return setupEndpoints(p.endpoints, resourceData.items[0].id, apiData.id)
|
||||
.then(function(endpointsData) {
|
||||
return apiGateway.createDeploymentAsync({
|
||||
restApiId: apiData.id,
|
||||
stageName: p.version
|
||||
})
|
||||
.then(function(deploymentData) {
|
||||
// Total hack: there are limits to the number of API Gateway API calls you can make. So this function
|
||||
// will fail if a CloudFormation template includes two or more APIs. Attempting to avoid this by blocking.
|
||||
var until = new Date();
|
||||
until.setSeconds(until.getSeconds() + 60);
|
||||
while (new Date() < until) {
|
||||
// Wait...
|
||||
}
|
||||
// AWS.config.region is a bit of a hack, but I can't figure out how else to dynamically
|
||||
// detect the region of the API - seems to be nothing in API Gateway or AWS Lambda context.
|
||||
// Could possibly get it from the CloudFormation stack, but that seems wrong.
|
||||
return {
|
||||
baseUrl: "https://" + apiData.id + ".execute-api." + AWS.config.region + ".amazonaws.com/" + p.version,
|
||||
restApiId: apiData.id
|
||||
};
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
CreateApi.prototype.handleDelete = function(referenceData) {
|
||||
return Promise.try(function() {
|
||||
if (referenceData && referenceData.restApiId) {
|
||||
// Can simply delete the entire API - don't need to delete each individual component
|
||||
return apiGateway.deleteRestApiAsync({
|
||||
restApiId: referenceData.restApiId
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
function setupEndpoints(config, parentResourceId, restApiId) {
|
||||
return Promise.map(
|
||||
Object.keys(config),
|
||||
function(key) {
|
||||
switch (key.toUpperCase()) {
|
||||
case 'GET':
|
||||
case 'HEAD':
|
||||
case 'DELETE':
|
||||
case 'OPTIONS':
|
||||
case 'PATCH':
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
var params = config[key];
|
||||
params["httpMethod"] = key.toUpperCase()
|
||||
params["resourceId"] = parentResourceId
|
||||
params["restApiId"] = restApiId
|
||||
params["apiKeyRequired"] = params["apiKeyRequired"] == "true" // Passing through CloudFormation, booleans become strings :(
|
||||
var integration = params["integration"]
|
||||
delete params.integration
|
||||
return apiGateway.putMethodAsync(params)
|
||||
.then(function() {
|
||||
return Promise.try(function() {
|
||||
if (integration) {
|
||||
var contentType = integration["contentType"]
|
||||
if (!contentType) {
|
||||
throw "Integration config must include response contentType."
|
||||
}
|
||||
delete integration.contentType
|
||||
integration["httpMethod"] = key.toUpperCase()
|
||||
integration["resourceId"] = parentResourceId
|
||||
integration["restApiId"] = restApiId
|
||||
return apiGateway.putIntegrationAsync(integration)
|
||||
.then(function(integrationData) {
|
||||
var responseContentTypes = {}
|
||||
responseContentTypes[contentType] = "Empty"
|
||||
return apiGateway.putMethodResponseAsync({
|
||||
httpMethod: key.toUpperCase(),
|
||||
resourceId: parentResourceId,
|
||||
restApiId: restApiId,
|
||||
statusCode: '200',
|
||||
responseModels: responseContentTypes
|
||||
})
|
||||
.then(function(methodResponseData) {
|
||||
responseContentTypes[contentType] = ""
|
||||
return apiGateway.putIntegrationResponseAsync({
|
||||
httpMethod: key.toUpperCase(),
|
||||
resourceId: parentResourceId,
|
||||
restApiId: restApiId,
|
||||
statusCode: '200',
|
||||
responseTemplates: responseContentTypes
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
default:
|
||||
return apiGateway.createResourceAsync({
|
||||
parentId: parentResourceId,
|
||||
pathPart: key,
|
||||
restApiId: restApiId,
|
||||
})
|
||||
.then(function(resourceData) {
|
||||
return setupEndpoints(config[key], resourceData.id, restApiId);
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
exports.createApi = function(event, context) {
|
||||
handler = new CreateApi(event, context);
|
||||
handler.handle();
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
var Promise = require('bluebird'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
helpers = require('lib/helpers'),
|
||||
dynamoDB = Promise.promisifyAll(new AWS.DynamoDB());
|
||||
|
||||
// Exposes the SNS.subscribe API method
|
||||
function PutItems(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
PutItems.prototype = Object.create(base.Handler.prototype);
|
||||
PutItems.prototype.handleCreate = function() {
|
||||
var p = this.event.ResourceProperties;
|
||||
return dynamoDB.describeTableAsync({
|
||||
TableName: p.TableName
|
||||
})
|
||||
.then(function(tableData) {
|
||||
return Promise
|
||||
.map(
|
||||
p.Items,
|
||||
function(item) {
|
||||
return dynamoDB.putItemAsync({
|
||||
TableName: p.TableName,
|
||||
Item: helpers.formatForDynamo(item, true)
|
||||
})
|
||||
.then(function() {
|
||||
var key = {};
|
||||
tableData.Table.KeySchema.forEach(function(keyMember) {
|
||||
key[keyMember.AttributeName] = item[keyMember.AttributeName]
|
||||
});
|
||||
return {
|
||||
TableName: p.TableName,
|
||||
Key: key
|
||||
};
|
||||
});
|
||||
}
|
||||
)
|
||||
.then(function(itemsInserted) {
|
||||
return {
|
||||
ItemsInserted: itemsInserted
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
PutItems.prototype.handleDelete = function(referenceData) {
|
||||
return Promise.try(function() {
|
||||
if (referenceData) {
|
||||
return Promise
|
||||
.map(
|
||||
referenceData.ItemsInserted,
|
||||
function(item) {
|
||||
return dynamoDB
|
||||
.deleteItemAsync({
|
||||
TableName: item.TableName,
|
||||
Key: helpers.formatForDynamo(item.Key, true)
|
||||
})
|
||||
.then(function(data) {
|
||||
return item.Key;
|
||||
});
|
||||
}
|
||||
)
|
||||
.then(function(itemsDeleted) {
|
||||
return {
|
||||
ItemsDeleted: itemsDeleted
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.putItems = function(event, context) {
|
||||
handler = new PutItems(event, context);
|
||||
handler.handle();
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
var Promise = require('bluebird'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
kinesis = Promise.promisifyAll(new AWS.Kinesis());
|
||||
|
||||
// Exposes the SNS.subscribe API method
|
||||
function CreateStream(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
CreateStream.prototype = Object.create(base.Handler.prototype);
|
||||
CreateStream.prototype.handleCreate = function() {
|
||||
var p = this.event.ResourceProperties;
|
||||
delete p.ServiceToken;
|
||||
p.ShardCount = parseInt(p.ShardCount);
|
||||
return kinesis.createStreamAsync(p)
|
||||
.then(function() {
|
||||
return waitWhileStatus(p.StreamName, "CREATING");
|
||||
})
|
||||
.then(function(arn) {
|
||||
return {
|
||||
StreamName: p.StreamName,
|
||||
Arn: arn
|
||||
}
|
||||
});
|
||||
}
|
||||
CreateStream.prototype.handleDelete = function(referenceData) {
|
||||
var p = this.event.ResourceProperties;
|
||||
return kinesis.deleteStreamAsync({StreamName: p.StreamName})
|
||||
.then(function() {
|
||||
return waitWhileStatus(p.StreamName, "DELETING")
|
||||
})
|
||||
.catch(function(err) {
|
||||
return err;
|
||||
});
|
||||
}
|
||||
exports.createStream = function(event, context) {
|
||||
handler = new CreateStream(event, context);
|
||||
handler.handle();
|
||||
}
|
||||
// Watch until the given status is no longer the status of the stream.
|
||||
function waitWhileStatus(streamName, status) {
|
||||
return Promise.try(function() {
|
||||
var validStatuses = ["CREATING", "DELETING", "ACTIVE", "UPDATING"]
|
||||
if (validStatuses.indexOf(status) >= 0) {
|
||||
return kinesis.describeStreamAsync({StreamName: streamName})
|
||||
.then(function(data) {
|
||||
console.log("Current status for [" + streamName +"]: " + data.StreamDescription.StreamStatus);
|
||||
if (data.StreamDescription.StreamStatus == status) {
|
||||
return Promise.delay(2000)
|
||||
.then(function() {
|
||||
return waitWhileStatus(streamName, status);
|
||||
});
|
||||
} else {
|
||||
return data.StreamDescription.StreamARN;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw "status [" + status + "] not one of [" + validStatuses.join(", ") + "]";
|
||||
}
|
||||
});
|
||||
}
|
80
aws/s3.js
80
aws/s3.js
|
@ -1,80 +0,0 @@
|
|||
var Promise = require('bluebird'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
helpers = require('lib/helpers'),
|
||||
s3 = Promise.promisifyAll(new AWS.S3());
|
||||
|
||||
// Exposes the SNS.subscribe API method
|
||||
function PutObject(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
PutObject.prototype = Object.create(base.Handler.prototype);
|
||||
PutObject.prototype.handleCreate = function() {
|
||||
var p = this.event.ResourceProperties;
|
||||
delete p.ServiceToken;
|
||||
return s3.putObjectAsync(p);
|
||||
}
|
||||
PutObject.prototype.handleDelete = function(referenceData) {
|
||||
var p = this.event.ResourceProperties;
|
||||
return Promise.try(function() {
|
||||
if (p.Key.endsWith("/")) {
|
||||
s3.listObjectsAsync({
|
||||
Bucket: p.Bucket,
|
||||
Prefix: p.Key
|
||||
})
|
||||
.then(function(subObjects) {
|
||||
return Promise
|
||||
.map(
|
||||
subObjects.Contents,
|
||||
function(item) {
|
||||
return s3.deleteObjectAsync({
|
||||
Bucket: p.Bucket,
|
||||
Key: item.Key
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
.then(function() {
|
||||
return s3.deleteObjectAsync({
|
||||
Bucket: p.Bucket,
|
||||
Key: p.Key
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.putObject = function(event, context) {
|
||||
handler = new PutObject(event, context);
|
||||
handler.handle();
|
||||
}
|
||||
|
||||
// Exposes the S3.putBucketPolicy API method
|
||||
function PutBucketPolicy(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
PutBucketPolicy.prototype = Object.create(base.Handler.prototype);
|
||||
PutBucketPolicy.prototype.handleCreate = function() {
|
||||
var p = this.event.ResourceProperties;
|
||||
delete p.ServiceToken;
|
||||
return s3.putBucketPolicyAsync(p)
|
||||
.then(function() {
|
||||
return {
|
||||
BucketName : p.Bucket
|
||||
}
|
||||
});
|
||||
}
|
||||
PutBucketPolicy.prototype.handleDelete = function(referencedData) {
|
||||
return Promise.try(function() {
|
||||
if(referencedData) {
|
||||
return s3.deleteBucketPolicyAsync({
|
||||
Bucket : referencedData.BucketName
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.putBucketPolicy = function(event, context) {
|
||||
console.log(JSON.stringify(event));
|
||||
handler = new PutBucketPolicy(event, context);
|
||||
handler.handle();
|
||||
}
|
||||
|
39
aws/ses.js
39
aws/ses.js
|
@ -1,39 +0,0 @@
|
|||
var Promise = require('bluebird'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
helpers = require('lib/helpers'),
|
||||
ses = Promise.promisifyAll(new AWS.SES());
|
||||
|
||||
// Exposes the SES.createReceiptRule API method
|
||||
function CreateReceiptRule(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
CreateReceiptRule.prototype = Object.create(base.Handler.prototype);
|
||||
CreateReceiptRule.prototype.handleCreate = function() {
|
||||
var p = this.event.ResourceProperties;
|
||||
delete p.ServiceToken;
|
||||
p.Rule.Enabled = ("true" === p.Rule.Enabled );
|
||||
p.Rule.ScanEnabled = ("true" === p.Rule.ScanEnabled );
|
||||
return ses.createReceiptRuleAsync(p)
|
||||
.then(function() {
|
||||
return {
|
||||
RuleSetName : p.RuleSetName,
|
||||
RuleName : p.Rule.Name
|
||||
}
|
||||
});
|
||||
}
|
||||
CreateReceiptRule.prototype.handleDelete = function(referenceData) {
|
||||
return Promise.try(function() {
|
||||
if (referenceData) {
|
||||
return ses.deleteReceiptRuleAsync({
|
||||
RuleSetName : referenceData.RuleSetName,
|
||||
RuleName : referenceData.RuleName
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.createReceiptRule = function(event, context) {
|
||||
console.log(JSON.stringify(event));
|
||||
handler = new CreateReceiptRule(event, context);
|
||||
handler.handle();
|
||||
}
|
31
aws/sns.js
31
aws/sns.js
|
@ -1,31 +0,0 @@
|
|||
var Promise = require('bluebird'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
sns = Promise.promisifyAll(new AWS.SNS());
|
||||
|
||||
// Exposes the SNS.subscribe API method
|
||||
function Subscribe(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
Subscribe.prototype = Object.create(base.Handler.prototype);
|
||||
Subscribe.prototype.handleCreate = function() {
|
||||
var p = this.event.ResourceProperties;
|
||||
return sns.subscribeAsync({
|
||||
Endpoint: p.Endpoint,
|
||||
Protocol: p.Protocol,
|
||||
TopicArn: p.TopicArn
|
||||
});
|
||||
}
|
||||
Subscribe.prototype.handleDelete = function(referenceData) {
|
||||
return Promise.try(function() {
|
||||
if (referenceData && referenceData.SubscriptionArn) {
|
||||
return sns.unsubscribeAsync({
|
||||
SubscriptionArn: referenceData.SubscriptionArn
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
exports.subscribe = function(event, context) {
|
||||
handler = new Subscribe(event, context);
|
||||
handler.handle();
|
||||
}
|
|
@ -1,533 +0,0 @@
|
|||
{
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Resources": {
|
||||
"ReferenceDB": {
|
||||
"Type": "AWS::DynamoDB::Table",
|
||||
"Properties": {
|
||||
"AttributeDefinitions": [
|
||||
{
|
||||
"AttributeName": "key",
|
||||
"AttributeType": "S"
|
||||
}
|
||||
],
|
||||
"KeySchema": [
|
||||
{
|
||||
"AttributeName": "key",
|
||||
"KeyType": "HASH"
|
||||
}
|
||||
],
|
||||
"ProvisionedThroughput": {
|
||||
"ReadCapacityUnits": 1,
|
||||
"WriteCapacityUnits": 1
|
||||
},
|
||||
"TableName": { "Fn::Join": [ "-", [ { "Ref" : "AWS::StackName" }, "reference" ] ] }
|
||||
}
|
||||
},
|
||||
"RoleBasePolicy": {
|
||||
"Type": "AWS::IAM::ManagedPolicy",
|
||||
"Properties": {
|
||||
"Description" : { "Fn::Join": [ " ", [ "Base policy for all Lambda function roles in", { "Ref" : "AWS::StackName" }, "." ] ] },
|
||||
"PolicyDocument" : {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"logs:CreateLogGroup",
|
||||
"logs:CreateLogStream",
|
||||
"logs:PutLogEvents"
|
||||
],
|
||||
"Resource": "arn:aws:logs:*:*:*"
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"dynamodb:GetItem",
|
||||
"dynamodb:PutItem",
|
||||
"dynamodb:Scan"
|
||||
],
|
||||
"Resource": { "Fn::Join": [ "", [ "arn:aws:dynamodb:", { "Ref": "AWS::Region" }, ":", { "Ref": "AWS::AccountId" } , ":table/", { "Ref": "ReferenceDB" } ] ] }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"ApiGatewayCreateApiFunctionRole": {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
"AssumeRolePolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": [ "lambda.amazonaws.com" ]
|
||||
},
|
||||
"Action": [ "sts:AssumeRole" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ManagedPolicyArns": [
|
||||
{ "Ref": "RoleBasePolicy" }
|
||||
],
|
||||
"Policies": [
|
||||
{
|
||||
"PolicyName": "ApiGatewayWriter",
|
||||
"PolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"apigateway:*"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"ApiGatewayCreateApiFunction": {
|
||||
"Type": "AWS::Lambda::Function",
|
||||
"Properties": {
|
||||
"Code": {
|
||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
||||
},
|
||||
"Description": "Used to create a full API in Api Gateway.",
|
||||
"Handler": "aws/apiGateway.createApi",
|
||||
"Role": {"Fn::GetAtt" : [ "ApiGatewayCreateApiFunctionRole", "Arn" ] },
|
||||
"Runtime": "nodejs4.3",
|
||||
"Timeout": 30
|
||||
},
|
||||
"DependsOn": [
|
||||
"ApiGatewayCreateApiFunctionRole"
|
||||
]
|
||||
},
|
||||
"CloudWatchLogsPutMetricFilterFunctionRole": {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
"AssumeRolePolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": [ "lambda.amazonaws.com" ]
|
||||
},
|
||||
"Action": [ "sts:AssumeRole" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ManagedPolicyArns": [
|
||||
{ "Ref": "RoleBasePolicy" }
|
||||
],
|
||||
"Policies": [
|
||||
{
|
||||
"PolicyName": "LogFilterCreator",
|
||||
"PolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"logs:DeleteMetricFilter",
|
||||
"logs:PutMetricFilter"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"CloudWatchLogsPutMetricFilterFunction": {
|
||||
"Type": "AWS::Lambda::Function",
|
||||
"Properties": {
|
||||
"Code": {
|
||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
||||
},
|
||||
"Description": "Used to populate a DynamoDB database from CloudFormation",
|
||||
"Handler": "aws/cloudWatchLogs.putMetricFilter",
|
||||
"Role": {"Fn::GetAtt" : [ "CloudWatchLogsPutMetricFilterFunctionRole", "Arn" ] },
|
||||
"Runtime": "nodejs4.3",
|
||||
"Timeout": 30
|
||||
},
|
||||
"DependsOn": [
|
||||
"CloudWatchLogsPutMetricFilterFunctionRole"
|
||||
]
|
||||
},
|
||||
"DynamoDBPutItemsFunctionRole": {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
"AssumeRolePolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": [ "lambda.amazonaws.com" ]
|
||||
},
|
||||
"Action": [ "sts:AssumeRole" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ManagedPolicyArns": [
|
||||
{ "Ref": "RoleBasePolicy" }
|
||||
],
|
||||
"Policies": [
|
||||
{
|
||||
"PolicyName": "DBWriter",
|
||||
"PolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"dynamodb:DeleteItem",
|
||||
"dynamodb:DescribeTable",
|
||||
"dynamodb:PutItem"
|
||||
],
|
||||
"Resource": { "Fn::Join": [ "", [ "arn:aws:dynamodb:", { "Ref": "AWS::Region" }, ":", { "Ref": "AWS::AccountId" } , ":table/*" ] ] }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"DynamoDBPutItemsFunction": {
|
||||
"Type": "AWS::Lambda::Function",
|
||||
"Properties": {
|
||||
"Code": {
|
||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
||||
},
|
||||
"Description": "Used to populate a DynamoDB database from CloudFormation",
|
||||
"Handler": "aws/dynamo.putItems",
|
||||
"Role": {"Fn::GetAtt" : [ "DynamoDBPutItemsFunctionRole", "Arn" ] },
|
||||
"Runtime": "nodejs4.3",
|
||||
"Timeout": 30
|
||||
},
|
||||
"DependsOn": [
|
||||
"DynamoDBPutItemsFunctionRole"
|
||||
]
|
||||
},
|
||||
"KinesisCreateStreamFunctionRole": {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
"AssumeRolePolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": [ "lambda.amazonaws.com" ]
|
||||
},
|
||||
"Action": [ "sts:AssumeRole" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ManagedPolicyArns": [
|
||||
{ "Ref": "RoleBasePolicy" }
|
||||
],
|
||||
"Policies": [
|
||||
{
|
||||
"PolicyName": "KinesisCreator",
|
||||
"PolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"kinesis:CreateStream",
|
||||
"kinesis:DeleteStream",
|
||||
"kinesis:DescribeStream"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"KinesisCreateStreamFunction": {
|
||||
"Type": "AWS::Lambda::Function",
|
||||
"Properties": {
|
||||
"Code": {
|
||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
||||
},
|
||||
"Description": "Used to create a Kinesis stream",
|
||||
"Handler": "aws/kinesis.createStream",
|
||||
"Role": {"Fn::GetAtt" : [ "KinesisCreateStreamFunctionRole", "Arn" ] },
|
||||
"Runtime": "nodejs4.3",
|
||||
"Timeout": 180
|
||||
},
|
||||
"DependsOn": [
|
||||
"KinesisCreateStreamFunctionRole"
|
||||
]
|
||||
},
|
||||
"S3PutObjectFunctionRole": {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
"AssumeRolePolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": [ "lambda.amazonaws.com" ]
|
||||
},
|
||||
"Action": [ "sts:AssumeRole" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ManagedPolicyArns": [
|
||||
{ "Ref": "RoleBasePolicy" }
|
||||
],
|
||||
"Policies": [
|
||||
{
|
||||
"PolicyName": "S3Writer",
|
||||
"PolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:DeleteObject",
|
||||
"s3:ListBucket",
|
||||
"s3:PutObject"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"S3PutObjectFunction": {
|
||||
"Type": "AWS::Lambda::Function",
|
||||
"Properties": {
|
||||
"Code": {
|
||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
||||
},
|
||||
"Description": "Used to put objects into S3.",
|
||||
"Handler": "aws/s3.putObject",
|
||||
"Role": {"Fn::GetAtt" : [ "S3PutObjectFunctionRole", "Arn" ] },
|
||||
"Runtime": "nodejs4.3",
|
||||
"Timeout": 30
|
||||
},
|
||||
"DependsOn": [
|
||||
"S3PutObjectFunctionRole"
|
||||
]
|
||||
},
|
||||
"S3PutBucketPolicyFunctionRole": {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
"AssumeRolePolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": [ "lambda.amazonaws.com" ]
|
||||
},
|
||||
"Action": [ "sts:AssumeRole" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ManagedPolicyArns": [
|
||||
{ "Ref": "RoleBasePolicy" }
|
||||
],
|
||||
"Policies": [
|
||||
{
|
||||
"PolicyName": "S3PolicyWriter",
|
||||
"PolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:ListBucket",
|
||||
"s3:PutBucketPolicy",
|
||||
"s3:DeleteBucketPolicy"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"S3PutBucketPolicyFunction": {
|
||||
"Type": "AWS::Lambda::Function",
|
||||
"Properties": {
|
||||
"Code": {
|
||||
"S3Bucket": "com.gilt.public.backoffice",
|
||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
||||
},
|
||||
"Description": "Used to put S3 bucket policy.",
|
||||
"Handler": "aws/s3.putBucketPolicy",
|
||||
"Role": {"Fn::GetAtt" : [ "S3PutBucketPolicyFunctionRole", "Arn" ] },
|
||||
"Runtime": "nodejs4.3",
|
||||
"Timeout": 30
|
||||
},
|
||||
"DependsOn": [
|
||||
"S3PutBucketPolicyFunctionRole"
|
||||
]
|
||||
},
|
||||
"SnsSubscribeFunctionRole": {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
"AssumeRolePolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": [ "lambda.amazonaws.com" ]
|
||||
},
|
||||
"Action": [ "sts:AssumeRole" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ManagedPolicyArns": [
|
||||
{ "Ref": "RoleBasePolicy" }
|
||||
],
|
||||
"Policies": [
|
||||
{
|
||||
"PolicyName": "SNSSubscriber",
|
||||
"PolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"sns:subscribe",
|
||||
"sns:unsubscribe"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"SnsSubscribeFunction": {
|
||||
"Type": "AWS::Lambda::Function",
|
||||
"Properties": {
|
||||
"Code": {
|
||||
"S3Bucket": { "Fn::Join": [ ".", [ "com.gilt.public.backoffice", { "Ref" : "AWS::Region" } ] ] },
|
||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
||||
},
|
||||
"Description": "Used to subscribe to existing SNS topics.",
|
||||
"Handler": "aws/sns.subscribe",
|
||||
"Role": {"Fn::GetAtt" : [ "SnsSubscribeFunctionRole", "Arn" ] },
|
||||
"Runtime": "nodejs4.3",
|
||||
"Timeout": 30
|
||||
},
|
||||
"DependsOn": [
|
||||
"SnsSubscribeFunctionRole"
|
||||
]
|
||||
},
|
||||
"SesCreateReceiptRuleFunctionRole": {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
"AssumeRolePolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": [ "lambda.amazonaws.com" ]
|
||||
},
|
||||
"Action": [ "sts:AssumeRole" ]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ManagedPolicyArns": [
|
||||
{ "Ref": "RoleBasePolicy" }
|
||||
],
|
||||
"Policies": [
|
||||
{
|
||||
"PolicyName": "SESReceiptRuleModifier",
|
||||
"PolicyDocument": {
|
||||
"Version" : "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"ses:CreateReceiptRule",
|
||||
"ses:DeleteReceiptRule"
|
||||
],
|
||||
"Resource": "*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"SesCreateReceiptRuleFunction": {
|
||||
"Type": "AWS::Lambda::Function",
|
||||
"Properties": {
|
||||
"Code": {
|
||||
"S3Bucket": "com.gilt.public.backoffice",
|
||||
"S3Key": "lambda_functions/cloudformation-helpers.zip"
|
||||
},
|
||||
"Description": "Used to create SES receipt rules.",
|
||||
"Handler": "aws/ses.createReceiptRule",
|
||||
"Role": {"Fn::GetAtt" : [ "SesCreateReceiptRuleFunctionRole", "Arn" ] },
|
||||
"Runtime": "nodejs4.3",
|
||||
"Timeout": 30
|
||||
},
|
||||
"DependsOn": [
|
||||
"SesCreateReceiptRuleFunctionRole"
|
||||
]
|
||||
}
|
||||
},
|
||||
"Outputs": {
|
||||
"ApiGatewayCreateApiFunctionArn": {
|
||||
"Description": "The ARN of the ApiGatewayCreateApiFunction, for use in other CloudFormation templates.",
|
||||
"Value": { "Fn::GetAtt" : ["ApiGatewayCreateApiFunction", "Arn"] }
|
||||
},
|
||||
"CloudWatchLogsPutMetricFilterFunctionArn": {
|
||||
"Description": "The ARN of the CloudWatchLogsPutMetricFilterFunction, for use in other CloudFormation templates.",
|
||||
"Value": { "Fn::GetAtt" : ["CloudWatchLogsPutMetricFilterFunction", "Arn"] }
|
||||
},
|
||||
"DynamoDBPutItemsFunctionArn": {
|
||||
"Description": "The ARN of the DynamoDBPutItemsFunction, for use in other CloudFormation templates.",
|
||||
"Value": { "Fn::GetAtt" : ["DynamoDBPutItemsFunction", "Arn"] }
|
||||
},
|
||||
"KinesisCreateStreamFunctionArn": {
|
||||
"Description": "The ARN of the KinesisCreateStreamFunction, for use in other CloudFormation templates.",
|
||||
"Value": { "Fn::GetAtt" : ["KinesisCreateStreamFunction", "Arn"] }
|
||||
},
|
||||
"SnsSubscribeFunctionArn": {
|
||||
"Description": "The ARN of the SnsSubscribeFunction, for use in other CloudFormation templates.",
|
||||
"Value": { "Fn::GetAtt" : ["SnsSubscribeFunction", "Arn"] }
|
||||
},
|
||||
"S3PutObjectFunctionArn": {
|
||||
"Description": "The ARN of the S3PutObjectFunction, for use in other CloudFormation templates.",
|
||||
"Value": { "Fn::GetAtt" : ["S3PutObjectFunction", "Arn"] }
|
||||
},
|
||||
"S3PutBucketPolicyFunctionArn": {
|
||||
"Description": "The ARN of the S3PutBucketPolicyFunction, for use in other CloudFormation templates.",
|
||||
"Value": { "Fn::GetAtt" : ["S3PutBucketPolicyFunction", "Arn"] }
|
||||
},
|
||||
"SesCreateReceiptRuleFunctionArn": {
|
||||
"Description": "The ARN of the SesCreateReceiptRuleFunction, for use in other CloudFormation templates.",
|
||||
"Value": { "Fn::GetAtt" : ["SesCreateReceiptRuleFunction", "Arn"] }
|
||||
}
|
||||
}
|
||||
}
|
345
create_cloudformation_helper_functions.template.yml
Normal file
345
create_cloudformation_helper_functions.template.yml
Normal file
|
@ -0,0 +1,345 @@
|
|||
AWSTemplateFormatVersion: 2010-09-09
|
||||
Transform: 'AWS::Serverless-2016-10-31'
|
||||
|
||||
Resources:
|
||||
ReferenceDB:
|
||||
Type: 'AWS::DynamoDB::Table'
|
||||
Properties:
|
||||
AttributeDefinitions:
|
||||
- AttributeName: key
|
||||
AttributeType: S
|
||||
KeySchema:
|
||||
- AttributeName: key
|
||||
KeyType: HASH
|
||||
ProvisionedThroughput:
|
||||
ReadCapacityUnits: 1
|
||||
WriteCapacityUnits: 1
|
||||
TableName: !Sub '${AWS::StackName}-reference'
|
||||
RoleBasePolicy:
|
||||
Type: 'AWS::IAM::ManagedPolicy'
|
||||
Properties:
|
||||
Description: !Sub 'Base policy for all Lambda function roles in ${AWS::StackName}.'
|
||||
PolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- logs:CreateLogGroup
|
||||
- logs:CreateLogStream
|
||||
- logs:PutLogEvents
|
||||
Resource: 'arn:aws:logs:*:*:*'
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- dynamodb:GetItem
|
||||
- dynamodb:PutItem
|
||||
- dynamodb:Scan
|
||||
Resource: !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${ReferenceDB}'
|
||||
ApiGatewayCreateApiFunctionRole:
|
||||
Type: 'AWS::IAM::Role'
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service:
|
||||
- lambda.amazonaws.com
|
||||
Action:
|
||||
- 'sts:AssumeRole'
|
||||
ManagedPolicyArns:
|
||||
- !Ref 'RoleBasePolicy'
|
||||
Policies:
|
||||
- PolicyName: ApiGatewayWriter
|
||||
PolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- 'apigateway:*'
|
||||
Resource: '*'
|
||||
ApiGatewayCreateApiFunction:
|
||||
Type: 'AWS::Serverless::Function'
|
||||
Properties:
|
||||
CodeUri: out/cloudformation-helpers
|
||||
Description: Used to create a full API in Api Gateway.
|
||||
Handler: aws/apiGateway.createApi
|
||||
Runtime: nodejs12.x
|
||||
Role: !GetAtt 'ApiGatewayCreateApiFunctionRole.Arn'
|
||||
Timeout: 30
|
||||
DependsOn:
|
||||
- ApiGatewayCreateApiFunctionRole
|
||||
CloudWatchLogsPutMetricFilterFunctionRole:
|
||||
Type: 'AWS::IAM::Role'
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service:
|
||||
- lambda.amazonaws.com
|
||||
Action:
|
||||
- 'sts:AssumeRole'
|
||||
ManagedPolicyArns:
|
||||
- !Ref 'RoleBasePolicy'
|
||||
Policies:
|
||||
- PolicyName: LogFilterCreator
|
||||
PolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- 'logs:DeleteMetricFilter'
|
||||
- 'logs:PutMetricFilter'
|
||||
Resource: '*'
|
||||
CloudWatchLogsPutMetricFilterFunction:
|
||||
Type: 'AWS::Serverless::Function'
|
||||
Properties:
|
||||
CodeUri: out/cloudformation-helpers
|
||||
Description: Used to populate a DynamoDB database from CloudFormation
|
||||
Handler: aws/cloudWatchLogs.putMetricFilter
|
||||
Runtime: nodejs12.x
|
||||
Role: !GetAtt 'CloudWatchLogsPutMetricFilterFunctionRole.Arn'
|
||||
Timeout: 30
|
||||
DependsOn:
|
||||
- CloudWatchLogsPutMetricFilterFunctionRole
|
||||
DynamoDBPutItemsFunctionRole:
|
||||
Type: 'AWS::IAM::Role'
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service:
|
||||
- lambda.amazonaws.com
|
||||
Action:
|
||||
- 'sts:AssumeRole'
|
||||
ManagedPolicyArns:
|
||||
- !Ref 'RoleBasePolicy'
|
||||
Policies:
|
||||
- PolicyName: DBWriter
|
||||
PolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- 'dynamodb:DeleteItem'
|
||||
- 'dynamodb:DescribeTable'
|
||||
- 'dynamodb:PutItem'
|
||||
Resource: !Sub 'arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/*'
|
||||
DynamoDBPutItemsFunction:
|
||||
Type: 'AWS::Serverless::Function'
|
||||
Properties:
|
||||
CodeUri: out/cloudformation-helpers
|
||||
Description: Used to populate a DynamoDB database from CloudFormation
|
||||
Handler: aws/dynamo.putItems
|
||||
Runtime: nodejs12.x
|
||||
Role: !GetAtt 'DynamoDBPutItemsFunctionRole.Arn'
|
||||
Timeout: 30
|
||||
DependsOn:
|
||||
- DynamoDBPutItemsFunctionRole
|
||||
KinesisCreateStreamFunctionRole:
|
||||
Type: 'AWS::IAM::Role'
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service:
|
||||
- lambda.amazonaws.com
|
||||
Action:
|
||||
- 'sts:AssumeRole'
|
||||
ManagedPolicyArns:
|
||||
- !Ref 'RoleBasePolicy'
|
||||
Policies:
|
||||
- PolicyName: KinesisCreator
|
||||
PolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- 'kinesis:CreateStream'
|
||||
- 'kinesis:DeleteStream'
|
||||
- 'kinesis:DescribeStream'
|
||||
Resource: '*'
|
||||
KinesisCreateStreamFunction:
|
||||
Type: 'AWS::Serverless::Function'
|
||||
Properties:
|
||||
CodeUri: out/cloudformation-helpers
|
||||
Description: Used to create a Kinesis stream
|
||||
Handler: aws/kinesis.createStream
|
||||
Runtime: nodejs12.x
|
||||
Role: !GetAtt 'KinesisCreateStreamFunctionRole.Arn'
|
||||
Timeout: 180
|
||||
DependsOn:
|
||||
- KinesisCreateStreamFunctionRole
|
||||
S3PutObjectFunctionRole:
|
||||
Type: 'AWS::IAM::Role'
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service:
|
||||
- lambda.amazonaws.com
|
||||
Action:
|
||||
- 'sts:AssumeRole'
|
||||
ManagedPolicyArns:
|
||||
- !Ref 'RoleBasePolicy'
|
||||
Policies:
|
||||
- PolicyName: S3Writer
|
||||
PolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- 's3:DeleteObject'
|
||||
- 's3:ListBucket'
|
||||
- 's3:PutObject'
|
||||
Resource: '*'
|
||||
S3PutObjectFunction:
|
||||
Type: 'AWS::Serverless::Function'
|
||||
Properties:
|
||||
CodeUri: out/cloudformation-helpers
|
||||
Description: Used to put objects into S3.
|
||||
Handler: aws/s3.putObject
|
||||
Runtime: nodejs12.x
|
||||
Role: !GetAtt 'S3PutObjectFunctionRole.Arn'
|
||||
Timeout: 30
|
||||
DependsOn:
|
||||
- S3PutObjectFunctionRole
|
||||
S3PutBucketPolicyFunctionRole:
|
||||
Type: 'AWS::IAM::Role'
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service:
|
||||
- lambda.amazonaws.com
|
||||
Action:
|
||||
- 'sts:AssumeRole'
|
||||
ManagedPolicyArns:
|
||||
- !Ref 'RoleBasePolicy'
|
||||
Policies:
|
||||
- PolicyName: S3PolicyWriter
|
||||
PolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- 's3:ListBucket'
|
||||
- 's3:PutBucketPolicy'
|
||||
- 's3:DeleteBucketPolicy'
|
||||
Resource: '*'
|
||||
S3PutBucketPolicyFunction:
|
||||
Type: 'AWS::Serverless::Function'
|
||||
Properties:
|
||||
CodeUri: out/cloudformation-helpers
|
||||
Description: Used to put S3 bucket policy.
|
||||
Handler: aws/s3.putBucketPolicy
|
||||
Runtime: nodejs12.x
|
||||
Role: !GetAtt 'S3PutBucketPolicyFunctionRole.Arn'
|
||||
Timeout: 30
|
||||
DependsOn:
|
||||
- S3PutBucketPolicyFunctionRole
|
||||
SnsSubscribeFunctionRole:
|
||||
Type: 'AWS::IAM::Role'
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service:
|
||||
- lambda.amazonaws.com
|
||||
Action:
|
||||
- 'sts:AssumeRole'
|
||||
ManagedPolicyArns:
|
||||
- !Ref 'RoleBasePolicy'
|
||||
Policies:
|
||||
- PolicyName: SNSSubscriber
|
||||
PolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- 'sns:subscribe'
|
||||
- 'sns:unsubscribe'
|
||||
Resource: '*'
|
||||
SnsSubscribeFunction:
|
||||
Type: 'AWS::Serverless::Function'
|
||||
Properties:
|
||||
CodeUri: out/cloudformation-helpers
|
||||
Description: Used to subscribe to existing SNS topics
|
||||
Handler: aws/sns.subscribe
|
||||
Runtime: nodejs12.x
|
||||
Role: !GetAtt 'SnsSubscribeFunctionRole.Arn'
|
||||
Timeout: 30
|
||||
DependsOn:
|
||||
- SnsSubscribeFunctionRole
|
||||
SesCreateReceiptRuleFunctionRole:
|
||||
Type: 'AWS::IAM::Role'
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service:
|
||||
- lambda.amazonaws.com
|
||||
Action:
|
||||
- 'sts:AssumeRole'
|
||||
ManagedPolicyArns:
|
||||
- !Ref 'RoleBasePolicy'
|
||||
Policies:
|
||||
- PolicyName: SESReceiptRuleModifier
|
||||
PolicyDocument:
|
||||
Version: 2012-10-17
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- 'ses:CreateReceiptRule'
|
||||
- 'ses:DeleteReceiptRule'
|
||||
Resource: '*'
|
||||
SesCreateReceiptRuleFunction:
|
||||
Type: 'AWS::Serverless::Function'
|
||||
Properties:
|
||||
CodeUri: out/cloudformation-helpers
|
||||
Description: Used to create SES receipt rules
|
||||
Handler: aws/ses.createReceiptRule
|
||||
Runtime: nodejs12.x
|
||||
Role: !GetAtt 'SesCreateReceiptRuleFunctionRole.Arn'
|
||||
Timeout: 30
|
||||
DependsOn:
|
||||
- SesCreateReceiptRuleFunctionRole
|
||||
|
||||
Outputs:
|
||||
ApiGatewayCreateApiFunctionArn:
|
||||
Description: The ARN of the ApiGatewayCreateApiFunction, for use in other CloudFormation templates
|
||||
Value: !GetAtt 'ApiGatewayCreateApiFunction.Arn'
|
||||
CloudWatchLogsPutMetricFilterFunctionArn:
|
||||
Description: The ARN of the CloudWatchLogsPutMetricFilterFunction, for use in other CloudFormation templates
|
||||
Value: !GetAtt 'CloudWatchLogsPutMetricFilterFunction.Arn'
|
||||
DynamoDBPutItemsFunctionArn:
|
||||
Description: The ARN of the DynamoDBPutItemsFunction, for use in other CloudFormation templates.
|
||||
Value: !GetAtt 'DynamoDBPutItemsFunction.Arn'
|
||||
KinesisCreateStreamFunctionArn:
|
||||
Description: The ARN of the KinesisCreateStreamFunction, for use in other CloudFormation templates
|
||||
Value: !GetAtt 'KinesisCreateStreamFunction.Arn'
|
||||
SnsSubscribeFunctionArn:
|
||||
Description: The ARN of the SnsSubscribeFunction, for use in other CloudFormation templates.
|
||||
Value: !GetAtt 'SnsSubscribeFunction.Arn'
|
||||
S3PutObjectFunctionArn:
|
||||
Description: The ARN of the S3PutObjectFunction, for use in other CloudFormation templates.
|
||||
Value: !GetAtt 'S3PutObjectFunction.Arn'
|
||||
S3PutBucketPolicyFunctionArn:
|
||||
Description: The ARN of the S3PutBucketPolicyFunction, for use in other CloudFormation templates.
|
||||
Value: !GetAtt 'S3PutBucketPolicyFunction.Arn'
|
||||
SesCreateReceiptRuleFunctionArn:
|
||||
Description: The ARN of the SesCreateReceiptRuleFunction, for use in other CloudFormation templates.
|
||||
Value: !GetAtt 'SesCreateReceiptRuleFunction.Arn'
|
20
package.json
20
package.json
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"name": "cloudformation-helpers",
|
||||
"version": "0.0.0",
|
||||
"description": "A set of helper methods to fill in the gaps in existing CloudFormation support.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/gilt/cloudformation-helpers"
|
||||
},
|
||||
"keywords": [
|
||||
"cloudformation"
|
||||
],
|
||||
"author": "Ryan Martin",
|
||||
"license": "Apache 2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/gilt/cloudformation-helpers/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"bluebird": "3.x"
|
||||
}
|
||||
}
|
20
src/.eslintrc.js
Normal file
20
src/.eslintrc.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
module.exports =
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true,
|
||||
},
|
||||
"globals": {
|
||||
"handler": "readonly",
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2017,
|
||||
"sourceType": "module",
|
||||
},
|
||||
"rules": {
|
||||
"no-undef": "error",
|
||||
"no-unused-vars": "error",
|
||||
"no-var": "error",
|
||||
"semi": ["error", "always" ],
|
||||
}
|
||||
};
|
114
src/aws/apiGateway.js
Normal file
114
src/aws/apiGateway.js
Normal file
|
@ -0,0 +1,114 @@
|
|||
const
|
||||
utilPromisifyAll = require('util-promisifyall'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
apiGateway = utilPromisifyAll(new AWS.APIGateway());
|
||||
|
||||
// Exposes the SNS.subscribe API method
|
||||
function CreateApi(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
CreateApi.prototype = Object.create(base.Handler.prototype);
|
||||
CreateApi.prototype.handleCreate = async function() {
|
||||
const p = this.event.ResourceProperties;
|
||||
const apiData = await apiGateway.createRestApiAsync({
|
||||
name: p.name,
|
||||
description: p.description
|
||||
});
|
||||
await this.setReferenceData({ restApiId: apiData.id }); // Set this immediately, in case later calls fail
|
||||
const resourceData = await apiGateway.getResourcesAsync({
|
||||
restApiId: apiData.id
|
||||
});
|
||||
await setupEndpoints(p.endpoints, resourceData.items[0].id, apiData.id);
|
||||
await apiGateway.createDeploymentAsync({
|
||||
restApiId: apiData.id,
|
||||
stageName: p.version
|
||||
});
|
||||
// Total hack: there are limits to the number of API Gateway API calls you can make. So this function
|
||||
// will fail if a CloudFormation template includes two or more APIs. Attempting to avoid this by blocking.
|
||||
const until = new Date();
|
||||
until.setSeconds(until.getSeconds() + 60);
|
||||
while (new Date() < until) {
|
||||
// Wait...
|
||||
}
|
||||
// AWS.config.region is a bit of a hack, but I can't figure out how else to dynamically
|
||||
// detect the region of the API - seems to be nothing in API Gateway or AWS Lambda context.
|
||||
// Could possibly get it from the CloudFormation stack, but that seems wrong.
|
||||
return {
|
||||
baseUrl: "https://" + apiData.id + ".execute-api." + AWS.config.region + ".amazonaws.com/" + p.version,
|
||||
restApiId: apiData.id
|
||||
};
|
||||
};
|
||||
CreateApi.prototype.handleDelete = async function(referenceData) {
|
||||
if (referenceData && referenceData.restApiId) {
|
||||
// Can simply delete the entire API - don't need to delete each individual component
|
||||
return await apiGateway.deleteRestApiAsync({
|
||||
restApiId: referenceData.restApiId
|
||||
});
|
||||
}
|
||||
};
|
||||
async function setupEndpoints(config, parentResourceId, restApiId) {
|
||||
return await Promise.all(
|
||||
Object.keys(config).map(async key => {
|
||||
switch (key.toUpperCase()) {
|
||||
case 'GET':
|
||||
case 'HEAD':
|
||||
case 'DELETE':
|
||||
case 'OPTIONS':
|
||||
case 'PATCH':
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
const params = config[key];
|
||||
params["httpMethod"] = key.toUpperCase();
|
||||
params["resourceId"] = parentResourceId;
|
||||
params["restApiId"] = restApiId;
|
||||
params["apiKeyRequired"] = params["apiKeyRequired"] == "true"; // Passing through CloudFormation, booleans become strings :/
|
||||
const integration = params["integration"];
|
||||
delete params.integration;
|
||||
await apiGateway.putMethodAsync(params);
|
||||
if (integration) {
|
||||
const contentType = integration["contentType"];
|
||||
if (!contentType) {
|
||||
throw "Integration config must include response contentType.";
|
||||
}
|
||||
delete integration.contentType;
|
||||
integration["httpMethod"] = key.toUpperCase();
|
||||
integration["resourceId"] = parentResourceId;
|
||||
integration["restApiId"] = restApiId;
|
||||
apiGateway.putIntegrationAsync(integration);
|
||||
const responseContentTypes = {};
|
||||
responseContentTypes[contentType] = "Empty";
|
||||
await apiGateway.putMethodResponseAsync({
|
||||
httpMethod: key.toUpperCase(),
|
||||
resourceId: parentResourceId,
|
||||
restApiId: restApiId,
|
||||
statusCode: '200',
|
||||
responseModels: responseContentTypes
|
||||
});
|
||||
responseContentTypes[contentType] = "";
|
||||
return await apiGateway.putIntegrationResponseAsync({
|
||||
httpMethod: key.toUpperCase(),
|
||||
resourceId: parentResourceId,
|
||||
restApiId: restApiId,
|
||||
statusCode: '200',
|
||||
responseTemplates: responseContentTypes
|
||||
});
|
||||
}
|
||||
else {
|
||||
return;
|
||||
}
|
||||
default:
|
||||
const resourceData = await apiGateway.createResourceAsync({
|
||||
parentId: parentResourceId,
|
||||
pathPart: key,
|
||||
restApiId: restApiId,
|
||||
});
|
||||
return await setupEndpoints(config[key], resourceData.id, restApiId);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
exports.createApi = function(event, context) {
|
||||
handler = new CreateApi(event, context);
|
||||
handler.handle();
|
||||
};
|
64
src/aws/dynamo.js
Normal file
64
src/aws/dynamo.js
Normal file
|
@ -0,0 +1,64 @@
|
|||
const
|
||||
utilPromisifyAll = require('util-promisifyall'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
helpers = require('lib/helpers'),
|
||||
dynamoDB = utilPromisifyAll(new AWS.DynamoDB());
|
||||
|
||||
// Exposes the SNS.subscribe API method
|
||||
function PutItems(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
PutItems.prototype = Object.create(base.Handler.prototype);
|
||||
PutItems.prototype.handleCreate = async function() {
|
||||
const p = this.event.ResourceProperties;
|
||||
|
||||
const tableData = await dynamoDB.describeTableAsync({
|
||||
TableName: p.TableName
|
||||
});
|
||||
|
||||
const itemsInserted = await Promise.all(
|
||||
p.Items.map(async item => {
|
||||
await dynamoDB.putItemAsync({
|
||||
TableName: p.TableName,
|
||||
Item: helpers.formatForDynamo(item, true)
|
||||
});
|
||||
|
||||
const key = {};
|
||||
tableData.Table.KeySchema.forEach(function(keyMember) {
|
||||
key[keyMember.AttributeName] = item[keyMember.AttributeName];
|
||||
});
|
||||
|
||||
return {
|
||||
TableName: p.TableName,
|
||||
Key: key
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
ItemsInserted: itemsInserted
|
||||
};
|
||||
};
|
||||
PutItems.prototype.handleDelete = async function(referenceData) {
|
||||
if (referenceData) {
|
||||
const itemsDeleted = await Promise.all(
|
||||
referenceData.itemsInserted.map(async item => {
|
||||
await dynamoDB.deleteItemAsync({
|
||||
TableName: item.TableName,
|
||||
Key: helpers.formatForDynamo(item.Key, true)
|
||||
});
|
||||
|
||||
return item.Key;
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
ItemsDeleted: itemsDeleted
|
||||
};
|
||||
}
|
||||
};
|
||||
exports.putItems = function(event, context) {
|
||||
handler = new PutItems(event, context);
|
||||
handler.handle();
|
||||
};
|
54
src/aws/kinesis.js
Normal file
54
src/aws/kinesis.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
const
|
||||
utilPromisifyAll = require('util-promisifyall'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
kinesis = utilPromisifyAll(new AWS.Kinesis());
|
||||
|
||||
// Exposes the SNS.subscribe API method
|
||||
function CreateStream(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
CreateStream.prototype = Object.create(base.Handler.prototype);
|
||||
CreateStream.prototype.handleCreate = async function() {
|
||||
const p = this.event.ResourceProperties;
|
||||
delete p.ServiceToken;
|
||||
p.ShardCount = parseInt(p.ShardCount);
|
||||
await kinesis.createStreamAsync(p);
|
||||
const arn = await waitWhileStatus(p.StreamName, "CREATING");
|
||||
return {
|
||||
StreamName: p.StreamName,
|
||||
Arn: arn
|
||||
};
|
||||
};
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
CreateStream.prototype.handleDelete = async function(referenceData) {
|
||||
try {
|
||||
const p = this.event.ResourceProperties;
|
||||
await kinesis.deleteStreamAsync({StreamName: p.StreamName});
|
||||
return await waitWhileStatus(p.StreamName, "DELETING");
|
||||
}
|
||||
catch(err) {
|
||||
return err;
|
||||
}
|
||||
};
|
||||
exports.createStream = function(event, context) {
|
||||
handler = new CreateStream(event, context);
|
||||
handler.handle();
|
||||
};
|
||||
// Watch until the given status is no longer the status of the stream.
|
||||
async function waitWhileStatus(streamName, status) {
|
||||
const validStatuses = ["CREATING", "DELETING", "ACTIVE", "UPDATING"];
|
||||
if (validStatuses.indexOf(status) >= 0) {
|
||||
const data = await kinesis.describeStreamAsync({StreamName: streamName});
|
||||
|
||||
console.log("Current status for [" + streamName +"]: " + data.StreamDescription.StreamStatus);
|
||||
if (data.StreamDescription.StreamStatus == status) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
return await waitWhileStatus(streamName, status);
|
||||
} else {
|
||||
return data.StreamDescription.StreamARN;
|
||||
}
|
||||
} else {
|
||||
throw "status [" + status + "] not one of [" + validStatuses.join(", ") + "]";
|
||||
}
|
||||
}
|
72
src/aws/s3.js
Normal file
72
src/aws/s3.js
Normal file
|
@ -0,0 +1,72 @@
|
|||
const
|
||||
utilPromisifyAll = require('util-promisifyall'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
s3 = utilPromisifyAll(new AWS.S3());
|
||||
|
||||
// Exposes the SNS.subscribe API method
|
||||
function PutObject(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
PutObject.prototype = Object.create(base.Handler.prototype);
|
||||
PutObject.prototype.handleCreate = async function() {
|
||||
const p = this.event.ResourceProperties;
|
||||
delete p.ServiceToken;
|
||||
return await s3.putObjectAsync(p);
|
||||
};
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
PutObject.prototype.handleDelete = async function(referenceData) {
|
||||
const p = this.event.ResourceProperties;
|
||||
if (p.Key.endsWith("/")) {
|
||||
const subObjects = await s3.listObjectsAsync({
|
||||
Bucket: p.Bucket,
|
||||
Prefix: p.Key
|
||||
});
|
||||
|
||||
await Promise.all(
|
||||
subObjects.Contents.map(async item => {
|
||||
return await s3.deleteObjectAsync({
|
||||
Bucket: p.Bucket,
|
||||
Key: item.Key
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return s3.deleteObjectAsync({
|
||||
Bucket: p.Bucket,
|
||||
Key: p.Key
|
||||
});
|
||||
};
|
||||
exports.putObject = function(event, context) {
|
||||
handler = new PutObject(event, context);
|
||||
handler.handle();
|
||||
};
|
||||
|
||||
// Exposes the S3.putBucketPolicy API method
|
||||
function PutBucketPolicy(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
PutBucketPolicy.prototype = Object.create(base.Handler.prototype);
|
||||
PutBucketPolicy.prototype.handleCreate = async function() {
|
||||
const p = this.event.ResourceProperties;
|
||||
delete p.ServiceToken;
|
||||
await s3.putBucketPolicyAsync(p);
|
||||
|
||||
return {
|
||||
BucketName : p.Bucket
|
||||
};
|
||||
};
|
||||
PutBucketPolicy.prototype.handleDelete = async function(referencedData) {
|
||||
if(referencedData) {
|
||||
return await s3.deleteBucketPolicyAsync({
|
||||
Bucket : referencedData.BucketName
|
||||
});
|
||||
}
|
||||
};
|
||||
exports.putBucketPolicy = function(event, context) {
|
||||
console.log(JSON.stringify(event));
|
||||
handler = new PutBucketPolicy(event, context);
|
||||
handler.handle();
|
||||
};
|
||||
|
36
src/aws/ses.js
Normal file
36
src/aws/ses.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
const
|
||||
utilPromisifyAll = require('util-promisifyall'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
ses = utilPromisifyAll(new AWS.SES());
|
||||
|
||||
// Exposes the SES.createReceiptRule API method
|
||||
function CreateReceiptRule(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
CreateReceiptRule.prototype = Object.create(base.Handler.prototype);
|
||||
CreateReceiptRule.prototype.handleCreate = async function() {
|
||||
const p = this.event.ResourceProperties;
|
||||
delete p.ServiceToken;
|
||||
p.Rule.Enabled = ("true" === p.Rule.Enabled );
|
||||
p.Rule.ScanEnabled = ("true" === p.Rule.ScanEnabled );
|
||||
await ses.createReceiptRuleAsync(p);
|
||||
|
||||
return {
|
||||
RuleSetName : p.RuleSetName,
|
||||
RuleName : p.Rule.Name
|
||||
};
|
||||
};
|
||||
CreateReceiptRule.prototype.handleDelete = async function(referenceData) {
|
||||
if (referenceData) {
|
||||
return await ses.deleteReceiptRuleAsync({
|
||||
RuleSetName : referenceData.RuleSetName,
|
||||
RuleName : referenceData.RuleName
|
||||
});
|
||||
}
|
||||
};
|
||||
exports.createReceiptRule = function(event, context) {
|
||||
console.log(JSON.stringify(event));
|
||||
handler = new CreateReceiptRule(event, context);
|
||||
handler.handle();
|
||||
};
|
30
src/aws/sns.js
Normal file
30
src/aws/sns.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
const
|
||||
utilPromisifyAll = require('util-promisifyall'),
|
||||
AWS = require('aws-sdk'),
|
||||
base = require('lib/base'),
|
||||
sns = utilPromisifyAll(new AWS.SNS());
|
||||
|
||||
// Exposes the SNS.subscribe API method
|
||||
function Subscribe(event, context) {
|
||||
base.Handler.call(this, event, context);
|
||||
}
|
||||
Subscribe.prototype = Object.create(base.Handler.prototype);
|
||||
Subscribe.prototype.handleCreate = async function() {
|
||||
const p = this.event.ResourceProperties;
|
||||
return await sns.subscribeAsync({
|
||||
Endpoint: p.Endpoint,
|
||||
Protocol: p.Protocol,
|
||||
TopicArn: p.TopicArn
|
||||
});
|
||||
};
|
||||
Subscribe.prototype.handleDelete = async function(referenceData) {
|
||||
if (referenceData && referenceData.SubscriptionArn) {
|
||||
return await sns.unsubscribeAsync({
|
||||
SubscriptionArn: referenceData.SubscriptionArn
|
||||
});
|
||||
}
|
||||
};
|
||||
exports.subscribe = function(event, context) {
|
||||
handler = new Subscribe(event, context);
|
||||
handler.handle();
|
||||
};
|
|
@ -1,49 +1,45 @@
|
|||
// Implement this class for every new handler.
|
||||
|
||||
var Promise = require('bluebird'),
|
||||
helpers = require('lib/helpers'),
|
||||
response = require('lib/cfn-response'),
|
||||
AWS = require('aws-sdk'),
|
||||
dynamoDB = Promise.promisifyAll(new AWS.DynamoDB());
|
||||
const
|
||||
utilPromisifyAll = require('util-promisifyall'),
|
||||
helpers = require('lib/helpers'),
|
||||
response = require('lib/cfn-response'),
|
||||
AWS = require('aws-sdk'),
|
||||
dynamoDB = utilPromisifyAll(new AWS.DynamoDB());
|
||||
|
||||
exports.Handler = function(event, context) {
|
||||
this.event = event;
|
||||
this.context = context;
|
||||
}
|
||||
};
|
||||
|
||||
exports.Handler.prototype.handle = function() {
|
||||
var outer = this;
|
||||
Promise.try(function() {
|
||||
switch (outer.event.RequestType) {
|
||||
exports.Handler.prototype.handle = async function() {
|
||||
try {
|
||||
let data;
|
||||
let referenceData;
|
||||
switch (this.event.RequestType) {
|
||||
case 'Create':
|
||||
return outer.handleCreate()
|
||||
.then(function(data) {
|
||||
return outer.setReferenceData(data)
|
||||
.then(function() {
|
||||
return data;
|
||||
});
|
||||
});
|
||||
const created = await this.handleCreate();
|
||||
await this.setReferenceData(created);
|
||||
data = created;
|
||||
break;
|
||||
case 'Delete':
|
||||
return outer.getReferenceData()
|
||||
.then(function(data) {
|
||||
return outer.handleDelete(data);
|
||||
});
|
||||
referenceData = await this.getReferenceData();
|
||||
data = await this.handleDelete(referenceData);
|
||||
break;
|
||||
case 'Update':
|
||||
return outer.getReferenceData()
|
||||
.then(function(data) {
|
||||
return outer.handleUpdate();
|
||||
});
|
||||
await this.getReferenceData();
|
||||
data = await this.handleUpdate();
|
||||
break;
|
||||
default:
|
||||
throw "Unrecognized RequestType [" + outer.event.RequestType + "]";
|
||||
throw "Unrecognized RequestType [" + this.event.RequestType + "]";
|
||||
}
|
||||
})
|
||||
.then(function(data) {
|
||||
response.send(outer.event, outer.context, response.SUCCESS, data);
|
||||
})
|
||||
.catch(function(err) {
|
||||
outer.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
response.send(this.event, this.context, response.SUCCESS, data);
|
||||
}
|
||||
catch(err) {
|
||||
this.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
When implemented, these should all return a Promise, which will then be completed by the handle()
|
||||
|
@ -54,59 +50,56 @@ exports.Handler.prototype.handle = function() {
|
|||
*/
|
||||
exports.Handler.prototype.handleCreate = function() {
|
||||
throw "create method not implemented";
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
exports.Handler.prototype.handleDelete = function(referenceData) {
|
||||
throw "delete method not implemented";
|
||||
}
|
||||
};
|
||||
|
||||
exports.Handler.prototype.handleUpdate = function(referenceData) {
|
||||
var self = this;
|
||||
return this.handleDelete(referenceData)
|
||||
.then(function() {
|
||||
return self.handleCreate();
|
||||
});
|
||||
}
|
||||
exports.Handler.prototype.handleUpdate = async function(referenceData) {
|
||||
await this.handleDelete(referenceData);
|
||||
return await this.handleCreate();
|
||||
};
|
||||
|
||||
exports.Handler.prototype.error = function(message) {
|
||||
console.error(message);
|
||||
response.send(this.event, this.context, response.FAILED, { Error: message });
|
||||
throw message;
|
||||
}
|
||||
};
|
||||
|
||||
exports.Handler.prototype.getStackName = function() {
|
||||
var functionName = this.context.functionName;
|
||||
const functionName = this.context.functionName;
|
||||
// Assume functionName is: stackName-resourceLogicalId-randomString.
|
||||
// Per http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/resources-section-structure.html,
|
||||
// resourceLogicalId cannot include a '-'; randomString seems to also be alphanumeric.
|
||||
// Thus it seems safe to search for the two dashes in order to find the stackName.
|
||||
var i = functionName.lastIndexOf("-", functionName.lastIndexOf("-") - 1);
|
||||
const i = functionName.lastIndexOf("-", functionName.lastIndexOf("-") - 1);
|
||||
if (i >= 0)
|
||||
return functionName.substr(0, i);
|
||||
else
|
||||
return functionName;
|
||||
}
|
||||
};
|
||||
|
||||
exports.Handler.prototype.getReferenceData = function() {
|
||||
return dynamoDB.getItemAsync(
|
||||
exports.Handler.prototype.getReferenceData = async function() {
|
||||
const data = await dynamoDB.getItemAsync(
|
||||
{
|
||||
TableName: this.getStackName() + "-reference",
|
||||
Key: helpers.formatForDynamo({
|
||||
key: this.event.StackId + this.event.LogicalResourceId
|
||||
}, true)
|
||||
}
|
||||
)
|
||||
.then(function(data) {
|
||||
data = helpers.formatFromDynamo(data);
|
||||
if (data && data.Item && data.Item.value)
|
||||
return data.Item.value;
|
||||
else
|
||||
return null;
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
exports.Handler.prototype.setReferenceData = function(data) {
|
||||
return dynamoDB.putItemAsync(
|
||||
const formattedData = helpers.formatFromDynamo(data);
|
||||
if (formattedData && formattedData.Item && formattedData.Item.value)
|
||||
return formattedData.Item.value;
|
||||
else
|
||||
return null;
|
||||
};
|
||||
|
||||
exports.Handler.prototype.setReferenceData = async function(data) {
|
||||
return await dynamoDB.putItemAsync(
|
||||
{
|
||||
TableName: this.getStackName() + "-reference",
|
||||
Item: helpers.formatForDynamo({
|
||||
|
@ -115,4 +108,4 @@ exports.Handler.prototype.setReferenceData = function(data) {
|
|||
}, true)
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
|
@ -10,7 +10,7 @@ exports.FAILED = "FAILED";
|
|||
|
||||
exports.send = function(event, context, responseStatus, responseData, physicalResourceId) {
|
||||
|
||||
var responseBody = JSON.stringify({
|
||||
const responseBody = JSON.stringify({
|
||||
Status: responseStatus,
|
||||
Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
|
||||
PhysicalResourceId: physicalResourceId || context.logStreamName,
|
||||
|
@ -22,15 +22,15 @@ exports.send = function(event, context, responseStatus, responseData, physicalRe
|
|||
|
||||
console.log("Response body:\n", responseBody);
|
||||
|
||||
var https = require("https");
|
||||
var url = require("url");
|
||||
const https = require("https");
|
||||
const url = require("url");
|
||||
|
||||
// This script comes from http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-lambda-function-code.html#cfn-lambda-function-code-cfnresponsemodule
|
||||
// The only change is this 'if' statement around this block of code, so it doesn't
|
||||
// fail when the ResonseURL is missing (i.e. during manual testing).
|
||||
if (event.ResponseURL) {
|
||||
var parsedUrl = url.parse(event.ResponseURL);
|
||||
var options = {
|
||||
const parsedUrl = url.parse(event.ResponseURL);
|
||||
const options = {
|
||||
hostname: parsedUrl.hostname,
|
||||
port: 443,
|
||||
path: parsedUrl.path,
|
||||
|
@ -41,7 +41,7 @@ exports.send = function(event, context, responseStatus, responseData, physicalRe
|
|||
}
|
||||
};
|
||||
|
||||
var request = https.request(options, function(response) {
|
||||
const request = https.request(options, function(response) {
|
||||
console.log("Status code: " + response.statusCode);
|
||||
console.log("Status message: " + response.statusMessage);
|
||||
context.done();
|
||||
|
@ -55,4 +55,4 @@ exports.send = function(event, context, responseStatus, responseData, physicalRe
|
|||
request.write(responseBody);
|
||||
request.end();
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,5 +1,3 @@
|
|||
var Promise = require('bluebird');
|
||||
|
||||
// Translates from raw JSON into DynamoDB-formatted JSON. This is more than a
|
||||
// convenience thing: the original iteration of this accepted DyanamoDB-JSON Items,
|
||||
// to avoid complications in translation. But when CloudFormation passes the parameters
|
||||
|
@ -8,44 +6,44 @@ var Promise = require('bluebird');
|
|||
// it is 'true' or 'false') as a 'BOOL' value. So some translation was needed, and
|
||||
// it seemed best to then simplify things for the client by accepting raw JSON.
|
||||
exports.formatForDynamo = function(value, topLevel) {
|
||||
var result = undefined;
|
||||
let result = undefined;
|
||||
if (value == 'true' || value == 'false') {
|
||||
result = {'BOOL': value == 'true'}
|
||||
result = {'BOOL': value == 'true'};
|
||||
} else if (!isNaN(value) && value.trim() != '') {
|
||||
result = {'N': value}
|
||||
result = {'N': value};
|
||||
} else if (Array.isArray(value)) {
|
||||
var arr = [];
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
const arr = [];
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
arr.push(exports.formatForDynamo(value[i], false));
|
||||
}
|
||||
result = {'L': arr};
|
||||
} else if (typeof value === "object") {
|
||||
var map = {};
|
||||
const map = {};
|
||||
Object.keys(value).forEach(function(key) {
|
||||
map[key] = exports.formatForDynamo(value[key], false)
|
||||
map[key] = exports.formatForDynamo(value[key], false);
|
||||
});
|
||||
if (topLevel) result = map;
|
||||
else result = {'M': map}
|
||||
else result = {'M': map};
|
||||
} else {
|
||||
result = {'S': value}
|
||||
result = {'S': value};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
exports.formatFromDynamo = function(value) {
|
||||
var result = undefined;
|
||||
let result = undefined;
|
||||
if (typeof value === "string" || typeof value === 'boolean' || typeof value === 'number') {
|
||||
result = value;
|
||||
} else if (Array.isArray(value)) {
|
||||
var arr = [];
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
const arr = [];
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
arr.push(exports.formatFromDynamo(value[i]));
|
||||
}
|
||||
result = arr;
|
||||
} else if (typeof value === "object") {
|
||||
var map = {};
|
||||
const map = {};
|
||||
Object.keys(value).forEach(function(key) {
|
||||
var v = exports.formatFromDynamo(value[key]);
|
||||
const v = exports.formatFromDynamo(value[key]);
|
||||
switch (key) {
|
||||
case 'B':
|
||||
throw "Unsupported Mongo type [B]";
|
||||
|
@ -65,7 +63,7 @@ exports.formatFromDynamo = function(value) {
|
|||
break;
|
||||
case 'NS':
|
||||
result = [];
|
||||
for (var i = 0; i < v.length; i++) {
|
||||
for (let i = 0; i < v.length; i++) {
|
||||
result.push(Number(value[i]));
|
||||
};
|
||||
break;
|
||||
|
@ -85,16 +83,16 @@ exports.formatFromDynamo = function(value) {
|
|||
throw "Unrecognized type [" + (typeof value) + "]";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
if (!String.prototype.endsWith) {
|
||||
String.prototype.endsWith = function(searchString, position) {
|
||||
var subjectString = this.toString();
|
||||
const subjectString = this.toString();
|
||||
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
|
||||
position = subjectString.length;
|
||||
}
|
||||
position -= searchString.length;
|
||||
var lastIndex = subjectString.indexOf(searchString, position);
|
||||
const lastIndex = subjectString.indexOf(searchString, position);
|
||||
return lastIndex !== -1 && lastIndex === position;
|
||||
};
|
||||
}
|
33
src/package.json
Normal file
33
src/package.json
Normal file
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"name": "cloudformation-helpers",
|
||||
"version": "0.0.0",
|
||||
"description": "A set of helper methods to fill in the gaps in existing CloudFormation support.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/empathicqubit/cloudformation-helpers"
|
||||
},
|
||||
"keywords": [
|
||||
"cloudformation"
|
||||
],
|
||||
"author": "Ryan Martin, EmpathicQubit",
|
||||
"license": "Apache 2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/empathicquibt/cloudformation-helpers/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"package": "shx mkdir -p ../out && shx rm -rf ../out/cloudformation-helpers && shx cp -r ../src ../out/cloudformation-helpers && cd ../out/cloudformation-helpers && yarn install --production",
|
||||
"lint": "eslint .",
|
||||
"deploy": "cd .. && cross-env-shell \"aws cloudformation package --s3-bucket $CF_S3_BUCKET --template-file create_cloudformation_helper_functions.template.yml --output-template-file out/create_cloudformation_helper_functions.template.yml\" && cross-env-shell \"aws cloudformation deploy --template-file out/create_cloudformation_helper_functions.template.yml --capabilities CAPABILITY_NAMED_IAM --stack-name $CF_STACK_NAME\"",
|
||||
"publish": "npm-run-all lint package deploy"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"util-promisifyall": "^1.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^7.2.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"shx": "^0.3.2"
|
||||
}
|
||||
}
|
1231
src/yarn.lock
Normal file
1231
src/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue