Skip to content

Instantly share code, notes, and snippets.

Revisions

  1. @PatNeedham PatNeedham revised this gist Jun 21, 2020. 1 changed file with 6 additions and 9 deletions.
    15 changes: 6 additions & 9 deletions dynamicServerlessEnvironmentsStack.yaml
    Original file line number Diff line number Diff line change
    @@ -3,12 +3,9 @@ Parameters:
    stackID:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9-]+$'
    tld:
    DomainName:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9.]+$'
    subdomain:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+$'
    AllowedPattern: '.*'
    route53HostedZoneId:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9.]+$'
    @@ -243,13 +240,13 @@ Resources:
    Type: 'AWS::ApiGateway::DomainName'
    Properties:
    CertificateArn: !Ref CertificateArn
    DomainName: !Sub '${subdomain}.${tld}'
    DomainName: !Ref DomainName
    basePathMapping:
    Type: 'AWS::ApiGateway::BasePathMapping'
    DependsOn:
    - apiGatewayDeployment
    Properties:
    DomainName: !Sub '${subdomain}.${tld}'
    DomainName: !Ref DomainName
    RestApiId: !Ref apiGateway
    Stage: prod
    domainRecordSet:
    @@ -260,10 +257,10 @@ Resources:
    HostedZoneId: !GetAtt domainNameResource.DistributionHostedZoneId
    Type: A
    HostedZoneId: !Ref route53HostedZoneId
    Name: !Sub '${subdomain}.${tld}'
    Name: !Ref DomainName
    Outputs:
    apiGatewayInvokeURL:
    Value: !Sub >-
    https://${subdomain}.${tld}
    https://${DomainName}
    lambdaArn:
    Value: !GetAtt lambdaFunction.Arn
  2. @PatNeedham PatNeedham revised this gist May 30, 2020. 1 changed file with 22 additions and 24 deletions.
    46 changes: 22 additions & 24 deletions dynamicServerlessEnvironmentsStack.yaml
    Original file line number Diff line number Diff line change
    @@ -1,22 +1,20 @@
    AWSTemplateFormatVersion: 2010-09-09
    Parameters:
    apiGatewayStageName:
    stackID:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
    Default: call
    lambdaFunctionName:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
    Default: my-function
    s3BucketName:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
    domainName:
    AllowedPattern: '^[a-zA-Z0-9-]+$'
    tld:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9.]+$'
    subdomain:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+$'
    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'
    @@ -31,7 +29,7 @@ Resources:
    - apiGatewayGETMethod
    Properties:
    RestApiId: !Ref apiGateway
    StageName: !Ref apiGatewayStageName
    StageName: prod
    lambdaFunction:
    Type: 'AWS::Lambda::Function'
    Properties:
    @@ -46,7 +44,7 @@ Resources:
    'statusCode': 200
    }
    Description: My function
    FunctionName: !Ref lambdaFunctionName
    FunctionName: !Sub '${stackID}'
    Handler: prodserver.handler
    MemorySize: 128
    Role: !GetAtt lambdaIAMRole.Arn
    @@ -87,7 +85,7 @@ Resources:
    Effect: Allow
    Resource:
    - !Sub >-
    arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${lambdaFunctionName}:*
    arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${stackID}:*
    PolicyName: lambda
    s3AccessIAMRole:
    Type: 'AWS::IAM::Role'
    @@ -113,7 +111,7 @@ Resources:
    lambdaLogGroup:
    Type: 'AWS::Logs::LogGroup'
    Properties:
    LogGroupName: !Sub '/aws/lambda/${lambdaFunctionName}'
    LogGroupName: !Sub '/aws/lambda/${stackID}'
    RetentionInDays: 90
    apiGatewayGETMethod:
    Type: 'AWS::ApiGateway::Method'
    @@ -132,7 +130,7 @@ Resources:
    s3Bucket:
    Type: 'AWS::S3::Bucket'
    Properties:
    BucketName: !Ref s3BucketName
    BucketName: !Ref stackID
    BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
    @@ -209,7 +207,7 @@ Resources:
    RequestParameters:
    integration.request.path.item: 'method.request.path.item'
    Uri: !Sub >-
    arn:aws:apigateway:${AWS::Region}:s3:path/${s3BucketName}/{item}
    arn:aws:apigateway:${AWS::Region}:s3:path/${stackID}/{item}
    IntegrationResponses:
    - StatusCode: 200
    ResponseTemplates: {"application/json": "", "image/jpg": ""}
    @@ -244,28 +242,28 @@ Resources:
    domainNameResource:
    Type: 'AWS::ApiGateway::DomainName'
    Properties:
    CertificateArn: arn:aws:acm:us-east-1:378688096774:certificate/b06f34c4-151c-4909-b098-709f35708792
    DomainName: !Ref domainName
    CertificateArn: !Ref CertificateArn
    DomainName: !Sub '${subdomain}.${tld}'
    basePathMapping:
    Type: 'AWS::ApiGateway::BasePathMapping'
    DependsOn:
    - apiGatewayDeployment
    Properties:
    DomainName: !Ref domainName
    DomainName: !Sub '${subdomain}.${tld}'
    RestApiId: !Ref apiGateway
    Stage: !Ref apiGatewayStageName
    Stage: prod
    domainRecordSet:
    Type: 'AWS::Route53::RecordSet'
    Properties:
    AliasTarget:
    DNSName: !GetAtt domainNameResource.DistributionDomainName
    HostedZoneId: !GetAtt domainNameResource.DistributionHostedZoneId
    Type: A
    HostedZoneId: Z02040821EGFVM8K3KXFD
    Name: !Sub '${subdomain}.guacchain.com'
    HostedZoneId: !Ref route53HostedZoneId
    Name: !Sub '${subdomain}.${tld}'
    Outputs:
    apiGatewayInvokeURL:
    Value: !Sub >-
    https://${subdomain}.guacchain.com
    https://${subdomain}.${tld}
    lambdaArn:
    Value: !GetAtt lambdaFunction.Arn
  3. @PatNeedham PatNeedham revised this gist May 26, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion dynamicServerlessEnvironmentsStack.yaml
    Original file line number Diff line number Diff line change
    @@ -233,7 +233,7 @@ Resources:
    HttpMethod: ANY
    Integration:
    Type: AWS_PROXY
    IntegrationHttpMethod: GET
    IntegrationHttpMethod: POST
    Uri: !Sub
    - >-
    arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations
  4. @PatNeedham PatNeedham revised this gist May 25, 2020. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion dynamicServerlessEnvironmentsStack.yaml
    Original file line number Diff line number Diff line change
    @@ -23,6 +23,8 @@ Resources:
    Properties:
    Name: my-api
    Description: My API
    BinaryMediaTypes:
    - '*/*'
    apiGatewayDeployment:
    Type: 'AWS::ApiGateway::Deployment'
    DependsOn:
    @@ -259,7 +261,7 @@ Resources:
    DNSName: !GetAtt domainNameResource.DistributionDomainName
    HostedZoneId: !GetAtt domainNameResource.DistributionHostedZoneId
    Type: A
    HostedZoneId: !GetAtt domainNameResource.DistributionHostedZoneId
    HostedZoneId: Z02040821EGFVM8K3KXFD
    Name: !Sub '${subdomain}.guacchain.com'
    Outputs:
    apiGatewayInvokeURL:
  5. @PatNeedham PatNeedham revised this gist May 25, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion dynamicServerlessEnvironmentsStack.yaml
    Original file line number Diff line number Diff line change
    @@ -260,7 +260,7 @@ Resources:
    HostedZoneId: !GetAtt domainNameResource.DistributionHostedZoneId
    Type: A
    HostedZoneId: !GetAtt domainNameResource.DistributionHostedZoneId
    Name: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*/*/*'
    Name: !Sub '${subdomain}.guacchain.com'
    Outputs:
    apiGatewayInvokeURL:
    Value: !Sub >-
  6. @PatNeedham PatNeedham revised this gist May 25, 2020. 1 changed file with 18 additions and 3 deletions.
    21 changes: 18 additions & 3 deletions dynamicServerlessEnvironmentsStack.yaml
    Original file line number Diff line number Diff line change
    @@ -14,6 +14,9 @@ Parameters:
    domainName:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9.]+$'
    subdomain:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9.]+$'
    Resources:
    apiGateway:
    Type: 'AWS::ApiGateway::RestApi'
    @@ -207,10 +210,10 @@ Resources:
    arn:aws:apigateway:${AWS::Region}:s3:path/${s3BucketName}/{item}
    IntegrationResponses:
    - StatusCode: 200
    ResponseTemplates: {"application/json": ""}
    ResponseTemplates: {"application/json": "", "image/jpg": ""}
    MethodResponses:
    - StatusCode: 200
    ResponseModels: { "application/json": "Empty" }
    ResponseModels: { "application/json": "Empty", "image/jpg": "Empty" }
    apiGatewayLambdaResource:
    Type: 'AWS::ApiGateway::Resource'
    Properties:
    @@ -243,12 +246,24 @@ Resources:
    DomainName: !Ref domainName
    basePathMapping:
    Type: 'AWS::ApiGateway::BasePathMapping'
    DependsOn:
    - apiGatewayDeployment
    Properties:
    DomainName: !Ref domainName
    RestApiId: !Ref apiGateway
    Stage: !Ref apiGatewayStageName
    domainRecordSet:
    Type: 'AWS::Route53::RecordSet'
    Properties:
    AliasTarget:
    DNSName: !GetAtt domainNameResource.DistributionDomainName
    HostedZoneId: !GetAtt domainNameResource.DistributionHostedZoneId
    Type: A
    HostedZoneId: !GetAtt domainNameResource.DistributionHostedZoneId
    Name: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*/*/*'
    Outputs:
    apiGatewayInvokeURL:
    Value: !Sub >-
    https://${apiGateway}.execute-api.${AWS::Region}.amazonaws.com/${apiGatewayStageName}
    https://${subdomain}.guacchain.com
    lambdaArn:
    Value: !GetAtt lambdaFunction.Arn
  7. @PatNeedham PatNeedham revised this gist May 25, 2020. 1 changed file with 7 additions and 1 deletion.
    8 changes: 7 additions & 1 deletion dynamicServerlessEnvironmentsStack.yaml
    Original file line number Diff line number Diff line change
    @@ -49,9 +49,11 @@ Resources:
    Timeout: 10
    Layers:
    - >-
    arn:aws:lambda:us-east-1:378688096774:layer:kerrpopovich2020-deps-layer:14
    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
    @@ -94,6 +96,7 @@ Resources:
    Principal:
    Service:
    - s3.amazonaws.com
    - apigateway.amazonaws.com
    Policies:
    - PolicyDocument:
    Version: 2012-10-17
    @@ -230,6 +233,9 @@ Resources:
    - >-
    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:
  8. @PatNeedham PatNeedham revised this gist May 23, 2020. 1 changed file with 15 additions and 2 deletions.
    17 changes: 15 additions & 2 deletions dynamicServerlessEnvironmentsStack.yaml
    Original file line number Diff line number Diff line change
    @@ -11,6 +11,9 @@ Parameters:
    s3BucketName:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
    domainName:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9.]+$'
    Resources:
    apiGateway:
    Type: 'AWS::ApiGateway::RestApi'
    @@ -39,7 +42,7 @@ Resources:
    }
    Description: My function
    FunctionName: !Ref lambdaFunctionName
    Handler: prod.server.handler
    Handler: prodserver.handler
    MemorySize: 128
    Role: !GetAtt lambdaIAMRole.Arn
    Runtime: nodejs12.x
    @@ -192,7 +195,7 @@ Resources:
    method.request.path.item: true
    Integration:
    Type: AWS
    Credentials: !Ref s3AccessIAMRole
    Credentials: !GetAtt s3AccessIAMRole.Arn
    IntegrationHttpMethod: GET
    PassthroughBehavior: WHEN_NO_MATCH
    RequestParameters:
    @@ -227,6 +230,16 @@ Resources:
    - >-
    arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations
    - lambdaArn: !GetAtt lambdaFunction.Arn
    domainNameResource:
    Type: 'AWS::ApiGateway::DomainName'
    Properties:
    CertificateArn: arn:aws:acm:us-east-1:378688096774:certificate/b06f34c4-151c-4909-b098-709f35708792
    DomainName: !Ref domainName
    basePathMapping:
    Type: 'AWS::ApiGateway::BasePathMapping'
    Properties:
    DomainName: !Ref domainName
    RestApiId: !Ref apiGateway
    Outputs:
    apiGatewayInvokeURL:
    Value: !Sub >-
  9. @PatNeedham PatNeedham created this gist May 19, 2020.
    235 changes: 235 additions & 0 deletions dynamicServerlessEnvironmentsStack.yaml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,235 @@
    AWSTemplateFormatVersion: 2010-09-09
    Parameters:
    apiGatewayStageName:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
    Default: call
    lambdaFunctionName:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
    Default: my-function
    s3BucketName:
    Type: String
    AllowedPattern: '^[a-zA-Z0-9]+[a-zA-Z0-9-]+[a-zA-Z0-9]+$'
    Resources:
    apiGateway:
    Type: 'AWS::ApiGateway::RestApi'
    Properties:
    Name: my-api
    Description: My API
    apiGatewayDeployment:
    Type: 'AWS::ApiGateway::Deployment'
    DependsOn:
    - apiGatewayGETMethod
    Properties:
    RestApiId: !Ref apiGateway
    StageName: !Ref apiGatewayStageName
    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: !Ref lambdaFunctionName
    Handler: prod.server.handler
    MemorySize: 128
    Role: !GetAtt lambdaIAMRole.Arn
    Runtime: nodejs12.x
    Timeout: 10
    Layers:
    - >-
    arn:aws:lambda:us-east-1:378688096774:layer:kerrpopovich2020-deps-layer:14
    lambdaApiGatewayInvoke:
    Type: 'AWS::Lambda::Permission'
    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/${lambdaFunctionName}:*
    PolicyName: lambda
    s3AccessIAMRole:
    Type: 'AWS::IAM::Role'
    Properties:
    AssumeRolePolicyDocument:
    Version: 2012-10-17
    Statement:
    - Action:
    - 'sts:AssumeRole'
    Effect: Allow
    Principal:
    Service:
    - s3.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/${lambdaFunctionName}'
    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 s3BucketName
    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: !Ref s3AccessIAMRole
    IntegrationHttpMethod: GET
    PassthroughBehavior: WHEN_NO_MATCH
    RequestParameters:
    integration.request.path.item: 'method.request.path.item'
    Uri: !Sub >-
    arn:aws:apigateway:${AWS::Region}:s3:path/${s3BucketName}/{item}
    IntegrationResponses:
    - StatusCode: 200
    ResponseTemplates: {"application/json": ""}
    MethodResponses:
    - StatusCode: 200
    ResponseModels: { "application/json": "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: GET
    Uri: !Sub
    - >-
    arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations
    - lambdaArn: !GetAtt lambdaFunction.Arn
    Outputs:
    apiGatewayInvokeURL:
    Value: !Sub >-
    https://${apiGateway}.execute-api.${AWS::Region}.amazonaws.com/${apiGatewayStageName}
    lambdaArn:
    Value: !GetAtt lambdaFunction.Arn