@mapbox/cloudfriend
Version:
Helper functions for assembling CloudFormation templates in JavaScript
411 lines • 11.6 kB
JSON
{
"AWSTemplateFormatVersion": "2010-09-09",
"Metadata": {},
"Parameters": {},
"Rules": {},
"Mappings": {},
"Conditions": {},
"Resources": {
"PassApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": {
"Fn::Sub": "${AWS::StackName}-webhook"
},
"FailOnWarnings": true,
"EndpointConfiguration": {
"Types": [
"REGIONAL"
]
}
}
},
"PassStage": {
"Type": "AWS::ApiGateway::Stage",
"Properties": {
"DeploymentId": {
"Ref": "PassDeploymentbdbc0f16"
},
"StageName": "hookshot",
"RestApiId": {
"Ref": "PassApi"
},
"MethodSettings": [
{
"HttpMethod": "*",
"ResourcePath": "/*",
"ThrottlingBurstLimit": 20,
"ThrottlingRateLimit": 5,
"LoggingLevel": "OFF",
"DataTraceEnabled": false,
"MetricsEnabled": false
}
]
}
},
"PassDeploymentbdbc0f16": {
"Type": "AWS::ApiGateway::Deployment",
"DependsOn": "PassMethod",
"Properties": {
"RestApiId": {
"Ref": "PassApi"
},
"StageName": "unused"
}
},
"PassResource": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"ParentId": {
"Fn::GetAtt": [
"PassApi",
"RootResourceId"
]
},
"RestApiId": {
"Ref": "PassApi"
},
"PathPart": "webhook"
}
},
"PassOptionsMethod": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"RestApiId": {
"Ref": "PassApi"
},
"ResourceId": {
"Ref": "PassResource"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"HttpMethod": "OPTIONS",
"Integration": {
"Type": "AWS_PROXY",
"IntegrationHttpMethod": "POST",
"Uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PassFunction.Arn}/invocations"
}
}
}
},
"PassMethod": {
"Type": "AWS::ApiGateway::Method",
"Properties": {
"RestApiId": {
"Ref": "PassApi"
},
"ResourceId": {
"Ref": "PassResource"
},
"ApiKeyRequired": false,
"AuthorizationType": "NONE",
"HttpMethod": "POST",
"Integration": {
"Type": "AWS_PROXY",
"IntegrationHttpMethod": "POST",
"Uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${PassFunction.Arn}/invocations"
}
}
}
},
"PassPermission": {
"Type": "AWS::Lambda::Permission",
"Properties": {
"FunctionName": {
"Ref": "PassFunction"
},
"Action": "lambda:InvokeFunction",
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${PassApi}/*"
}
}
},
"PassSecret": {
"Type": "AWS::ApiGateway::ApiKey",
"Properties": {
"Enabled": false
}
},
"PassFunctionLogs": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": {
"Fn::Sub": [
"/aws/lambda/${name}",
{
"name": {
"Fn::Sub": "${AWS::StackName}-Pass"
}
}
]
},
"RetentionInDays": 14
}
},
"PassFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"ZipFile": {
"Fn::Sub": "'use strict';\n\nconst { InvokeCommand, LambdaClient } = require('@aws-sdk/client-lambda');\nconst client = new LambdaClient();\n\nmodule.exports.lambda = (event, context, callback) => {\n if (event.httpMethod === 'OPTIONS') {\n const requestHeaders = event.headers['Access-Control-Request-Headers'] || event.headers['access-control-request-headers'];\n const response = {\n statusCode: 200,\n body: '',\n headers: {\n 'Access-Control-Allow-Headers': requestHeaders,\n 'Access-Control-Allow-Methods': 'POST, OPTIONS',\n 'Access-Control-Allow-Origin': '*'\n }\n };\n return callback(null, response);\n }\n\n const command = new InvokeCommand({\n FunctionName: '${Destination}',\n Payload: JSON.stringify(event)\n });\n\n client.send(command)\n .then((response) => {\n if (!response || !response.Payload)\n return callback(new Error('Your Lambda function Destination did not provide a payload'));\n\n var payload = JSON.parse(Buffer.from(response.Payload).toString());\n payload.headers = payload.headers || {};\n payload.headers['Access-Control-Allow-Origin'] = '*';\n callback(null, payload);\n })\n .catch((err) => callback(err));\n};"
}
},
"Description": {
"Fn::Sub": "Passthrough function for ${AWS::StackName}"
},
"FunctionName": {
"Fn::Sub": "${AWS::StackName}-Pass"
},
"Handler": "index.lambda",
"MemorySize": 128,
"Runtime": "nodejs22.x",
"Timeout": 30,
"Role": {
"Fn::GetAtt": [
"PassFunctionRole",
"Arn"
]
}
}
},
"PassFunctionErrorAlarm": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"AlarmName": {
"Fn::Sub": "${AWS::StackName}-PassFunction-Errors-${AWS::Region}"
},
"AlarmDescription": {
"Fn::Sub": [
"Error alarm for ${name} lambda function in ${AWS::StackName} stack",
{
"name": {
"Fn::Sub": "${AWS::StackName}-Pass"
}
}
]
},
"AlarmActions": [],
"Period": 60,
"EvaluationPeriods": 1,
"DatapointsToAlarm": 1,
"Statistic": "Sum",
"Threshold": 0,
"ComparisonOperator": "GreaterThanThreshold",
"TreatMissingData": "notBreaching",
"Namespace": "AWS/Lambda",
"Dimensions": [
{
"Name": "FunctionName",
"Value": {
"Ref": "PassFunction"
}
}
],
"MetricName": "Errors"
}
},
"PassFunctionLogPolicy": {
"Type": "AWS::IAM::Policy",
"DependsOn": "PassFunctionRole",
"Properties": {
"PolicyName": "PassFunction-lambda-log-access",
"Roles": [
{
"Ref": "PassFunctionRole"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "logs:*",
"Resource": {
"Fn::GetAtt": [
"PassFunctionLogs",
"Arn"
]
}
}
]
}
}
},
"PassFunctionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": {
"Service": {
"Fn::Sub": "lambda.amazonaws.com"
}
}
}
]
},
"Policies": [
{
"PolicyName": "main",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "lambda:InvokeFunction",
"Resource": {
"Fn::GetAtt": [
"Destination",
"Arn"
]
}
}
]
}
}
]
}
},
"DestinationLogs": {
"Type": "AWS::Logs::LogGroup",
"Properties": {
"LogGroupName": {
"Fn::Sub": [
"/aws/lambda/${name}",
{
"name": {
"Fn::Sub": "${AWS::StackName}-Destination"
}
}
]
},
"RetentionInDays": 14
}
},
"Destination": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"ZipFile": "module.exports.handler = (e, c, cb) => cb();"
},
"Description": {
"Fn::Sub": "Destination in the ${AWS::StackName} stack"
},
"FunctionName": {
"Fn::Sub": "${AWS::StackName}-Destination"
},
"Handler": "index.handler",
"MemorySize": 128,
"Runtime": "nodejs22.x",
"Timeout": 300,
"Role": {
"Fn::GetAtt": [
"DestinationRole",
"Arn"
]
}
}
},
"DestinationErrorAlarm": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"AlarmName": {
"Fn::Sub": "${AWS::StackName}-Destination-Errors-${AWS::Region}"
},
"AlarmDescription": {
"Fn::Sub": [
"Error alarm for ${name} lambda function in ${AWS::StackName} stack",
{
"name": {
"Fn::Sub": "${AWS::StackName}-Destination"
}
}
]
},
"AlarmActions": [],
"Period": 60,
"EvaluationPeriods": 5,
"DatapointsToAlarm": 1,
"Statistic": "Sum",
"Threshold": 0,
"ComparisonOperator": "GreaterThanThreshold",
"TreatMissingData": "notBreaching",
"Namespace": "AWS/Lambda",
"Dimensions": [
{
"Name": "FunctionName",
"Value": {
"Ref": "Destination"
}
}
],
"MetricName": "Errors"
}
},
"DestinationLogPolicy": {
"Type": "AWS::IAM::Policy",
"DependsOn": "DestinationRole",
"Properties": {
"PolicyName": "Destination-lambda-log-access",
"Roles": [
{
"Ref": "DestinationRole"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "logs:*",
"Resource": {
"Fn::GetAtt": [
"DestinationLogs",
"Arn"
]
}
}
]
}
}
},
"DestinationRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Principal": {
"Service": {
"Fn::Sub": "lambda.amazonaws.com"
}
}
}
]
}
}
}
},
"Outputs": {
"PassEndpointOutput": {
"Description": "The HTTPS endpoint used to send github webhooks",
"Value": {
"Fn::Sub": "https://${PassApi}.execute-api.${AWS::Region}.amazonaws.com/hookshot/webhook"
}
},
"PassSecretOutput": {
"Description": "A secret key to give Github to use when signing webhook requests",
"Value": {
"Ref": "PassSecret"
}
}
}
}