serverless-blue-green-deployments
Version:
A Serverless plugin to implement blue/green deployment of Lambda functions
1,095 lines (1,094 loc) • 28 kB
JSON
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "The AWS CloudFormation template for this Serverless application",
"Resources": {
"ServerlessDeploymentBucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"BucketEncryption": {
"ServerSideEncryptionConfiguration": [
{
"ServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}
]
}
}
},
"ServerlessDeploymentBucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"PolicyDocument": {
"Statement": [
{
"Action": "s3:*",
"Effect": "Deny",
"Principal": "*",
"Resource": [
{
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":s3:::",
{
"Ref": "ServerlessDeploymentBucket"
},
"/*"
]
]
}
],
"Condition": {
"Bool": {
"aws:SecureTransport": false
}
}
}
]
}
}
},
"AuthorizeLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/canary-deployments-test-dev-authorize"
}
},
"HelloLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/canary-deployments-test-dev-hello"
}
},
"PreHookLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/canary-deployments-test-dev-preHook"
}
},
"PostHookLogGroup": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": "/aws/lambda/canary-deployments-test-dev-postHook"
}
},
"IamRoleLambdaExecution": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Policies": [
{
"PolicyName": {
"Fn::Join": [
"-",
[
"dev",
"canary-deployments-test",
"lambda"
]
]
},
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream"
],
"Resource": [
{
"Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/canary-deployments-test-dev*:*"
}
]
},
{
"Effect": "Allow",
"Action": [
"logs:PutLogEvents"
],
"Resource": [
{
"Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/canary-deployments-test-dev*:*:*"
}
]
},
{
"Effect": "Allow",
"Action": [
"codedeploy:*"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"execute-api:ManageConnections"
],
"Resource": [
"arn:aws:execute-api:*:*:*/@connections/*"
]
},
{
"Effect": "Allow",
"Action": [
"dynamodb:GetRecords",
"dynamodb:GetShardIterator",
"dynamodb:DescribeStream",
"dynamodb:ListStreams"
],
"Resource": [
{
"Fn::GetAtt": [
"StreamsTestTable",
"StreamArn"
]
}
]
},
{
"Effect": "Allow",
"Action": [
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
"sqs:GetQueueAttributes"
],
"Resource": [
{
"Fn::GetAtt": [
"MyQueue",
"Arn"
]
}
]
}
]
}
}
],
"Path": "/",
"RoleName": {
"Fn::Join": [
"-",
[
"canary-deployments-test",
"dev",
{
"Ref": "AWS::Region"
},
"lambdaRole"
]
]
}
}
},
"AuthorizeLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/canary-deployments-test/dev/1575930299000-2019-12-09T22:24:59.000Z/canary-deployments-test.zip"
},
"FunctionName": "canary-deployments-test-dev-authorize",
"Handler": "handler.authorize",
"MemorySize": 1024,
"Role": {
"Fn::GetAtt": [
"IamRoleLambdaExecution",
"Arn"
]
},
"Runtime": "nodejs10.x",
"Timeout": 6
},
"DependsOn": [
"AuthorizeLogGroup",
"IamRoleLambdaExecution"
]
},
"AuthorizeLambdaVersionbTfsT57iU4ZfOGZTf3Zj4S1BeSuy3parpc3gsEQB14": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "AuthorizeLambdaFunction"
},
"CodeSha256": "Ofxyq/kgN80mbxe4T0albpCu+wmd3J6qqY/ur4VFDMQ="
}
},
"HelloLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/canary-deployments-test/dev/1575930299000-2019-12-09T22:24:59.000Z/canary-deployments-test.zip"
},
"FunctionName": "canary-deployments-test-dev-hello",
"Handler": "handler.hello",
"MemorySize": 1024,
"Role": {
"Fn::GetAtt": [
"IamRoleLambdaExecution",
"Arn"
]
},
"Runtime": "nodejs10.x",
"Timeout": 6
},
"DependsOn": [
"HelloLogGroup",
"IamRoleLambdaExecution"
]
},
"HelloLambdaVersionc1BYUZExb51lc5z0vanRRxW4Wn9wVPF1MZ7NcARvig": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "HelloLambdaFunction"
},
"CodeSha256": "Ofxyq/kgN80mbxe4T0albpCu+wmd3J6qqY/ur4VFDMQ="
}
},
"PreHookLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/canary-deployments-test/dev/1575930299000-2019-12-09T22:24:59.000Z/canary-deployments-test.zip"
},
"FunctionName": "canary-deployments-test-dev-preHook",
"Handler": "hooks.pre",
"MemorySize": 1024,
"Role": {
"Fn::GetAtt": [
"IamRoleLambdaExecution",
"Arn"
]
},
"Runtime": "nodejs10.x",
"Timeout": 6
},
"DependsOn": [
"PreHookLogGroup",
"IamRoleLambdaExecution"
]
},
"PreHookLambdaVersiondpbHvoFBPQYrU4rmE0qY9NDi4L98YHpjapLePRTv8Q": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "PreHookLambdaFunction"
},
"CodeSha256": "Ofxyq/kgN80mbxe4T0albpCu+wmd3J6qqY/ur4VFDMQ="
}
},
"PostHookLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "ServerlessDeploymentBucket"
},
"S3Key": "serverless/canary-deployments-test/dev/1575930299000-2019-12-09T22:24:59.000Z/canary-deployments-test.zip"
},
"FunctionName": "canary-deployments-test-dev-postHook",
"Handler": "hooks.post",
"MemorySize": 1024,
"Role": {
"Fn::GetAtt": [
"IamRoleLambdaExecution",
"Arn"
]
},
"Runtime": "nodejs10.x",
"Timeout": 6
},
"DependsOn": [
"PostHookLogGroup",
"IamRoleLambdaExecution"
]
},
"PostHookLambdaVersionoEafgDXpRIiDAmp1U2sE8MReDiTTacw8XlwC1prVjH8": {
"Type": "AWS::Lambda::Version",
"DeletionPolicy": "Retain",
"Properties": {
"FunctionName": {
"Ref": "PostHookLambdaFunction"
},
"CodeSha256": "Ofxyq/kgN80mbxe4T0albpCu+wmd3J6qqY/ur4VFDMQ="
}
},
"HelloEventsRuleSchedule1": {
"Type": "AWS::Events::Rule",
"Properties": {
"ScheduleExpression": "rate(1 minute)",
"State": "DISABLED",
"Targets": [
{
"Arn": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"Id": "helloSchedule"
}
]
}
},
"HelloLambdaPermissionEventsRuleSchedule1": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "events.amazonaws.com",
"SourceArn": {
"Fn::GetAtt": [
"HelloEventsRuleSchedule1",
"Arn"
]
}
}
},
"ApiGatewayRestApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "dev-canary-deployments-test",
"EndpointConfiguration": {
"Types": [
"EDGE"
]
}
}
},
"ApiGatewayResourceHello": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"ParentId": {
"Fn::GetAtt": [
"ApiGatewayRestApi",
"RootResourceId"
]
},
"PathPart": "hello",
"RestApiId": {
"Ref": "ApiGatewayRestApi"
}
}
},
"ApiGatewayMethodHelloGet": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"HttpMethod": "GET",
"RequestParameters": {},
"ResourceId": {
"Ref": "ApiGatewayResourceHello"
},
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"Integration": {
"IntegrationHttpMethod": "POST",
"Type": "AWS_PROXY",
"Uri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
},
"MethodResponses": []
}
},
"ApiGatewayDeployment1575930276029": {
"Type": "AWS::ApiGateway::Deployment",
"Properties": {
"RestApiId": {
"Ref": "ApiGatewayRestApi"
},
"StageName": "dev"
},
"DependsOn": [
"ApiGatewayMethodHelloGet"
]
},
"HelloLambdaPermissionApiGateway": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":execute-api:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
{
"Ref": "ApiGatewayRestApi"
},
"/*/*"
]
]
}
}
},
"WebsocketsApi": {
"Type": "AWS::ApiGatewayV2::Api",
"Properties": {
"Name": "dev-canary-deployments-test-websockets",
"RouteSelectionExpression": "$request.body.action",
"Description": "Serverless Websockets",
"ProtocolType": "WEBSOCKET"
}
},
"HelloWebsocketsIntegration": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
"Ref": "WebsocketsApi"
},
"IntegrationType": "AWS_PROXY",
"IntegrationUri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
}
}
},
"AuthorizeWebsocketsAuthorizer": {
"Type": "AWS::ApiGatewayV2::Authorizer",
"Properties": {
"ApiId": {
"Ref": "WebsocketsApi"
},
"Name": "authorize",
"AuthorizerType": "REQUEST",
"AuthorizerUri": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":apigateway:",
{
"Ref": "AWS::Region"
},
":lambda:path/2015-03-31/functions/",
{
"Fn::GetAtt": [
"AuthorizeLambdaFunction",
"Arn"
]
},
"/invocations"
]
]
},
"IdentitySource": [
"route.request.header.Auth"
]
}
},
"HelloLambdaPermissionWebsockets": {
"Type": "AWS::Lambda::Permission",
"DependsOn": [
"WebsocketsApi",
"HelloLambdaFunction"
],
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com"
}
},
"AuthorizeLambdaPermissionWebsockets": {
"Type": "AWS::Lambda::Permission",
"DependsOn": [
"WebsocketsApi",
"AuthorizeLambdaFunction"
],
"Properties": {
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"FunctionName": {
"Fn::GetAtt": [
"AuthorizeLambdaFunction",
"Arn"
]
}
}
},
"SdefaultWebsocketsRoute": {
"Type": "AWS::ApiGatewayV2::Route",
"Properties": {
"ApiId": {
"Ref": "WebsocketsApi"
},
"RouteKey": "$default",
"AuthorizationType": "CUSTOM",
"Target": {
"Fn::Join": [
"/",
[
"integrations",
{
"Ref": "HelloWebsocketsIntegration"
}
]
]
},
"AuthorizerId": {
"Ref": "AuthorizeWebsocketsAuthorizer"
}
}
},
"WebsocketsDeployment1575930276029": {
"Type": "AWS::ApiGatewayV2::Deployment",
"DependsOn": [
"SdefaultWebsocketsRoute"
],
"Properties": {
"ApiId": {
"Ref": "WebsocketsApi"
},
"Description": "Serverless Websockets"
}
},
"WebsocketsDeploymentStage": {
"Type": "AWS::ApiGatewayV2::Stage",
"Properties": {
"ApiId": {
"Ref": "WebsocketsApi"
},
"DeploymentId": {
"Ref": "WebsocketsDeployment1575930276029"
},
"StageName": "dev",
"Description": "Serverless Websockets"
}
},
"SNSTopicSnsTopic": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "snsTopic",
"DisplayName": "",
"Subscription": [
{
"Endpoint": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"Protocol": "lambda"
}
]
}
},
"HelloLambdaPermissionSnsTopicSNS": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "sns.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":sns:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
"snsTopic"
]
]
}
}
},
"SNSTopicFooTopic": {
"Type": "AWS::SNS::Topic",
"Properties": {
"TopicName": "fooTopic",
"DisplayName": "fooTopic"
}
},
"HelloSnsSubscriptionFooTopic": {
"Type": "AWS::SNS::Subscription",
"Properties": {
"Endpoint": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"Protocol": "lambda",
"TopicArn": {
"Ref": "SNSTopicFooTopic"
},
"FilterPolicy": {
"eventType": [
"FooEvent"
]
}
}
},
"HelloLambdaPermissionFooTopicSNS": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "sns.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":sns:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":",
"fooTopic"
]
]
}
}
},
"HelloEventSourceMappingDynamodbStreamsTestTable": {
"Type": "AWS::Lambda::EventSourceMapping",
"DependsOn": "IamRoleLambdaExecution",
"Properties": {
"BatchSize": 10,
"ParallelizationFactor": 1,
"EventSourceArn": {
"Fn::GetAtt": [
"StreamsTestTable",
"StreamArn"
]
},
"FunctionName": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"StartingPosition": "TRIM_HORIZON",
"Enabled": "True"
}
},
"HelloIotTopicRule1": {
"Type": "AWS::IoT::TopicRule",
"Properties": {
"TopicRulePayload": {
"RuleDisabled": "false",
"Sql": "SELECT * FROM 'some_topic'",
"Actions": [
{
"Lambda": {
"FunctionArn": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
}
}
}
]
}
}
},
"HelloLambdaPermissionIotTopicRule1": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": "iot.amazonaws.com",
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":iot:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":rule/",
{
"Ref": "HelloIotTopicRule1"
}
]
]
}
}
},
"HelloLogsSubscriptionFilterCloudWatchLog1": {
"Type": "AWS::Logs::SubscriptionFilter",
"DependsOn": "HelloLambdaPermissionLogsSubscriptionFilterCloudWatchLog",
"Properties": {
"LogGroupName": "/aws/lambda/sls-wshop-dev-hello",
"FilterPattern": "",
"DestinationArn": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
}
}
},
"HelloLambdaPermissionLogsSubscriptionFilterCloudWatchLog": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"Action": "lambda:InvokeFunction",
"Principal": {
"Fn::Join": [
"",
[
"logs.",
{
"Ref": "AWS::Region"
},
".amazonaws.com"
]
]
},
"SourceArn": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":logs:",
{
"Ref": "AWS::Region"
},
":",
{
"Ref": "AWS::AccountId"
},
":log-group:",
"/aws/lambda/sls-wshop-dev-hello",
":*"
]
]
}
}
},
"HelloEventSourceMappingSQSMyQueue": {
"Type": "AWS::Lambda::EventSourceMapping",
"DependsOn": "IamRoleLambdaExecution",
"Properties": {
"BatchSize": 10,
"EventSourceArn": {
"Fn::GetAtt": [
"MyQueue",
"Arn"
]
},
"FunctionName": {
"Fn::GetAtt": [
"HelloLambdaFunction",
"Arn"
]
},
"Enabled": "True"
}
},
"HelloFooAlarm": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"Namespace": "AWS/Lambda",
"MetricName": "Errors",
"Threshold": 1,
"Period": 60,
"EvaluationPeriods": 1,
"ComparisonOperator": "GreaterThanOrEqualToThreshold",
"OKActions": [],
"AlarmActions": [],
"InsufficientDataActions": [],
"Dimensions": [
{
"Name": "FunctionName",
"Value": {
"Ref": "HelloLambdaFunction"
}
}
],
"TreatMissingData": "missing",
"Statistic": "Minimum"
}
},
"StreamsTestTable": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"TableName": "StreamsTestTable",
"AttributeDefinitions": [
{
"AttributeName": "id",
"AttributeType": "S"
}
],
"KeySchema": [
{
"AttributeName": "id",
"KeyType": "HASH"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 1,
"WriteCapacityUnits": 1
},
"StreamSpecification": {
"StreamViewType": "NEW_AND_OLD_IMAGES"
}
}
},
"MyQueue": {
"Type": "AWS::SQS::Queue",
"Properties": {
"DelaySeconds": 0,
"VisibilityTimeout": 120
}
}
},
"Outputs": {
"ServerlessDeploymentBucketName": {
"Value": {
"Ref": "ServerlessDeploymentBucket"
}
},
"AuthorizeLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "AuthorizeLambdaVersionbTfsT57iU4ZfOGZTf3Zj4S1BeSuy3parpc3gsEQB14"
}
},
"HelloLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "HelloLambdaVersionc1BYUZExb51lc5z0vanRRxW4Wn9wVPF1MZ7NcARvig"
}
},
"PreHookLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "PreHookLambdaVersiondpbHvoFBPQYrU4rmE0qY9NDi4L98YHpjapLePRTv8Q"
}
},
"PostHookLambdaFunctionQualifiedArn": {
"Description": "Current Lambda function version",
"Value": {
"Ref": "PostHookLambdaVersionoEafgDXpRIiDAmp1U2sE8MReDiTTacw8XlwC1prVjH8"
}
},
"ServiceEndpoint": {
"Description": "URL of the service endpoint",
"Value": {
"Fn::Join": [
"",
[
"https://",
{
"Ref": "ApiGatewayRestApi"
},
".execute-api.",
{
"Ref": "AWS::Region"
},
".",
{
"Ref": "AWS::URLSuffix"
},
"/dev"
]
]
}
},
"ServiceEndpointWebsocket": {
"Description": "URL of the service endpoint",
"Value": {
"Fn::Join": [
"",
[
"wss://",
{
"Ref": "WebsocketsApi"
},
".execute-api.",
{
"Ref": "AWS::Region"
},
".",
{
"Ref": "AWS::URLSuffix"
},
"/dev"
]
]
}
},
"AuthorizerJWT": {
"Type": "AWS::ApiGatewayV2::Authorizer",
"Properties": {
"ApiId": {
"Ref": "HttpApi"
},
"AuthorizerType": "JWT",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": [
"example"
],
"Issuer": "https://cognito-idp.region.amazonaws.com/us-east"
},
"Name": "userJwtAuthorizer"
}
}
}
}