Skip to content

Instantly share code, notes, and snippets.

@PatNeedham
Last active July 10, 2020 16:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save PatNeedham/5b3c4b68997b0375f27d402a056a6e1b to your computer and use it in GitHub Desktop.
Save PatNeedham/5b3c4b68997b0375f27d402a056a6e1b to your computer and use it in GitHub Desktop.
CloudFormation Stack
AWSTemplateFormatVersion: 2010-09-09
Parameters:
stackID:
Type: String
AllowedPattern: '^[a-zA-Z0-9-]+$'
DomainName:
Type: String
AllowedPattern: '.*'
route53HostedZoneId:
Type: String
AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9.]+$'
CertificateArn:
Type: String
AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9./:-]+$'
Resources:
apiGateway:
Type: 'AWS::ApiGateway::RestApi'
Properties:
Name: my-api
Description: My API
BinaryMediaTypes:
- '*/*'
apiGatewayDeployment:
Type: 'AWS::ApiGateway::Deployment'
DependsOn:
- apiGatewayGETMethod
Properties:
RestApiId: !Ref apiGateway
StageName: prod
lambdaFunction:
Type: 'AWS::Lambda::Function'
Properties:
Code:
ZipFile: |
def handler(event,context):
return {
'body': 'Hello there {0}'.format(event['requestContext']['identity']['sourceIp']),
'headers': {
'Content-Type': 'text/plain'
},
'statusCode': 200
}
Description: My function
FunctionName: !Sub '${stackID}'
Handler: prodserver.handler
MemorySize: 128
Role: !GetAtt lambdaIAMRole.Arn
Runtime: nodejs12.x
Timeout: 10
Layers:
- >-
arn:aws:lambda:us-east-1:378688096774:layer:kerrpopovich2020-deps-layer:15
lambdaApiGatewayInvoke:
Type: 'AWS::Lambda::Permission'
DependsOn:
- apiGatewayLambdaResource
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !GetAtt lambdaFunction.Arn
Principal: apigateway.amazonaws.com
SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*/*/*'
lambdaIAMRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Policies:
- PolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- 'logs:CreateLogGroup'
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Effect: Allow
Resource:
- !Sub >-
arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${stackID}:*
PolicyName: lambda
s3AccessIAMRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- s3.amazonaws.com
- apigateway.amazonaws.com
Policies:
- PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: '*'
Resource: '*'
PolicyName: s3
lambdaLogGroup:
Type: 'AWS::Logs::LogGroup'
Properties:
LogGroupName: !Sub '/aws/lambda/${stackID}'
RetentionInDays: 90
apiGatewayGETMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
AuthorizationType: NONE
HttpMethod: GET
Integration:
IntegrationHttpMethod: POST
Type: AWS_PROXY
Uri: !Sub
- >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations
- lambdaArn: !GetAtt lambdaFunction.Arn
ResourceId: !GetAtt apiGateway.RootResourceId
RestApiId: !Ref apiGateway
s3Bucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: !Ref stackID
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
PolicyDocument:
Id: MyS3BucketPolicy
Version: 2012-10-17
Statement:
- Sid: PublicReadForGetBucketObjects
Effect: Allow
Principal: '*'
Action: 's3:GetObject'
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref s3Bucket
- /*
Bucket: !Ref s3Bucket
apiGatewayAssetsResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref apiGateway
ParentId: !GetAtt
- apiGateway
- RootResourceId
PathPart: assets
apiGatewayAssetsItemsResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref apiGateway
PathPart: '{item}'
ParentId: !Ref apiGatewayAssetsResource
OptionsMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
ResourceId: !Ref apiGatewayAssetsItemsResource
RestApiId: !Ref apiGateway
HttpMethod: OPTIONS
Integration:
IntegrationResponses:
- StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Methods: "'GET,OPTIONS'"
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
application/json: ''
PassthroughBehavior: WHEN_NO_MATCH
RequestTemplates:
application/json: '{"statusCode": 200}'
Type: MOCK
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: 'Empty'
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: false
method.response.header.Access-Control-Allow-Methods: false
method.response.header.Access-Control-Allow-Origin: false
apiGatewayAssetsItemsResourceMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
ResourceId: !Ref apiGatewayAssetsItemsResource
RestApiId: !Ref apiGateway
AuthorizationType: NONE
HttpMethod: GET
RequestParameters:
method.request.path.item: true
Integration:
Type: AWS
Credentials: !GetAtt s3AccessIAMRole.Arn
IntegrationHttpMethod: GET
PassthroughBehavior: WHEN_NO_MATCH
RequestParameters:
integration.request.path.item: 'method.request.path.item'
Uri: !Sub >-
arn:aws:apigateway:${AWS::Region}:s3:path/${stackID}/{item}
IntegrationResponses:
- StatusCode: 200
ResponseTemplates: {"application/json": "", "image/jpg": ""}
MethodResponses:
- StatusCode: 200
ResponseModels: { "application/json": "Empty", "image/jpg": "Empty" }
apiGatewayLambdaResource:
Type: 'AWS::ApiGateway::Resource'
Properties:
RestApiId: !Ref apiGateway
PathPart: '{proxy+}'
ParentId: !GetAtt
- apiGateway
- RootResourceId
apiGatewayLambdaResourceMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
AuthorizationType: NONE
RestApiId: !Ref apiGateway
ResourceId: !Ref apiGatewayLambdaResource
HttpMethod: ANY
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub
- >-
arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations
- lambdaArn: !GetAtt lambdaFunction.Arn
MethodResponses:
- StatusCode: 200
ResponseModels: { "application/json": "Empty" }
domainNameResource:
Type: 'AWS::ApiGateway::DomainName'
Properties:
CertificateArn: !Ref CertificateArn
DomainName: !Ref DomainName
basePathMapping:
Type: 'AWS::ApiGateway::BasePathMapping'
DependsOn:
- apiGatewayDeployment
Properties:
DomainName: !Ref DomainName
RestApiId: !Ref apiGateway
Stage: prod
domainRecordSet:
Type: 'AWS::Route53::RecordSet'
Properties:
AliasTarget:
DNSName: !GetAtt domainNameResource.DistributionDomainName
HostedZoneId: !GetAtt domainNameResource.DistributionHostedZoneId
Type: A
HostedZoneId: !Ref route53HostedZoneId
Name: !Ref DomainName
Outputs:
apiGatewayInvokeURL:
Value: !Sub >-
https://${DomainName}
lambdaArn:
Value: !GetAtt lambdaFunction.Arn
@PatNeedham
Copy link
Author

image
image

Making it more flexible so this template can be used for multiple projects. Those previously hard-coded values are now parameters

@PatNeedham
Copy link
Author

image

The tld and subdomain parameters were only used in combination with each other. Replacing them with DomainName makes it possible to now have a url without a subdomain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment