serverless-rest-api/javascript-http-sam/template.yaml [199:823]:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Principal: 
              Service: 
                - apigateway.amazonaws.com
            Action: 
              - sts:AssumeRole
      Path: "/"
      Tags:
        - Key: "Stack" 
          Value: !Sub "${AWS::StackName}"

  AuthorizerFunctionExecutionRolePolicy: 
    Type: AWS::IAM::Policy
    Properties: 
      PolicyName: !Sub ${AWS::StackName}-Authorizer-Policy
      PolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: Allow
            Action: lambda:InvokeFunction
            Resource: !GetAtt AuthorizerFunction.Arn
      Roles: 
        - Ref: AuthorizerFunctionExecutionRole

  HttpApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      FailOnWarnings: True
      CorsConfiguration:
        AllowMethods:
          - PUT
          - GET
          - DELETE
          - OPTIONS
        AllowHeaders: 
          - Content-Type
          - Authorization
          - X-Forwarded-For
          - X-Api-Key
          - X-Amz-Date
          - X-Amz-Security-Token
        AllowOrigins: 
          - "*"
      Auth: 
        Authorizers:
          LambdaAuthorizer:
            AuthorizerPayloadFormatVersion: 1.0
            FunctionArn: !GetAtt AuthorizerFunction.Arn
            FunctionInvokeRole: !GetAtt AuthorizerFunctionExecutionRole.Arn
            Identity:
                Headers:
                  - Authorization
        DefaultAuthorizer: LambdaAuthorizer
      AccessLogSettings:
        DestinationArn: !GetAtt AccessLogs.Arn
        Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "status":"$context.status","protocol":"$context.protocol", "integrationStatus": $context.integrationStatus, "integrationLatency": $context.integrationLatency, "responseLength":"$context.responseLength" }'
      Tags:
        Name: !Sub "${AWS::StackName}-API"
        Stack: !Sub "${AWS::StackName}"

  AccessLogs:
    Type: AWS::Logs::LogGroup
    Properties:
      RetentionInDays: 30
      LogGroupName: !Sub "/${AWS::StackName}/APIAccessLogs"


  LocationsTable:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
        Name: locationid
        Type: String
      ProvisionedThroughput:
        ReadCapacityUnits: 2
        WriteCapacityUnits: 2
      Tags:
        Stack: !Sub "${AWS::StackName}"

  ResourcesTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: resourceid
          AttributeType: S
        - AttributeName: locationid
          AttributeType: S
      KeySchema:
        - AttributeName: resourceid
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 2
        WriteCapacityUnits: 2
      GlobalSecondaryIndexes:
      - IndexName: locationidGSI
        KeySchema:
        - AttributeName: locationid
          KeyType: HASH
        Projection:
          ProjectionType: ALL
        ProvisionedThroughput:
          ReadCapacityUnits: 2
          WriteCapacityUnits: 2
      Tags:
        - Key: "Stack" 
          Value: !Sub "${AWS::StackName}"

  BookingsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: bookingid
          AttributeType: S
        - AttributeName: userid
          AttributeType: S
        - AttributeName: resourceid
          AttributeType: S
        - AttributeName: starttimeepochtime
          AttributeType: N
      KeySchema:
        - AttributeName: bookingid
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 2
        WriteCapacityUnits: 2
      GlobalSecondaryIndexes:
      - IndexName: useridGSI
        KeySchema:
        - AttributeName: userid
          KeyType: HASH
        Projection:
          ProjectionType: ALL
        ProvisionedThroughput:
          ReadCapacityUnits: 2
          WriteCapacityUnits: 2
      - IndexName: bookingsByUserByTimeGSI
        KeySchema:
        - AttributeName: userid
          KeyType: HASH
        - AttributeName: starttimeepochtime
          KeyType: RANGE
        Projection:
          ProjectionType: ALL
        ProvisionedThroughput:
          ReadCapacityUnits: 2
          WriteCapacityUnits: 2
      - IndexName: bookingsByResourceByTimeGSI
        KeySchema:
        - AttributeName: resourceid
          KeyType: HASH
        - AttributeName: starttimeepochtime
          KeyType: RANGE
        Projection:
          ProjectionType: ALL
        ProvisionedThroughput:
          ReadCapacityUnits: 2
          WriteCapacityUnits: 2
      Tags:
        - Key: "Stack" 
          Value: !Sub "${AWS::StackName}"

  AlarmsKMSKey:
    Type: AWS::KMS::Key
    Properties: 
      Description: CMK for SNS alarms topic
      Enabled: true
      EnableKeyRotation: True
      KeyPolicy: 
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal: 
              Service: 
                - "cloudwatch.amazonaws.com"
                - "sns.amazonaws.com"
            Action:
              - "kms:GenerateDataKey*"
              - "kms:Decrypt"
            Resource: "*"
          - Effect: Allow
            Principal: 
              AWS: 
                - !Sub "arn:aws:iam::${AWS::AccountId}:root"
            Action:
              - "kms:*"
            Resource: "*"
      KeySpec: SYMMETRIC_DEFAULT
      KeyUsage: ENCRYPT_DECRYPT
      PendingWindowInDays: 30
      Tags: 
        - Key: "Stack" 
          Value: !Sub "${AWS::StackName}"

  AlarmsTopic:
    Type: AWS::SNS::Topic
    Properties:
      KmsMasterKeyId: !Ref AlarmsKMSKey
      Tags:
        - Key: "Stack" 
          Value: !Sub "${AWS::StackName}"

  HttpApiErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: ApiName
          Value: !Ref HttpApi
      EvaluationPeriods: 1
      MetricName: 5XXError
      Namespace: AWS/ApiGateway
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  AuthorizerFunctionErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref AuthorizerFunction
      EvaluationPeriods: 1
      MetricName: Errors
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  LocationsFunctionErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref LocationsFunction
      EvaluationPeriods: 1
      MetricName: Errors
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  ResourcesFunctionErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref ResourcesFunction
      EvaluationPeriods: 1
      MetricName: Errors
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  BookingsFunctionErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref BookingsFunction
      EvaluationPeriods: 1
      MetricName: Errors
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  LocationsFunctionThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref LocationsFunction
      EvaluationPeriods: 1
      MetricName: Throttles
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  ResourcesFunctionThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref ResourcesFunction
      EvaluationPeriods: 1
      MetricName: Throttles
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  BookingsFunctionThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref BookingsFunction
      EvaluationPeriods: 1
      MetricName: Throttles
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  AuthorizerFunctionThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref AuthorizerFunction
      EvaluationPeriods: 1
      MetricName: Throttles
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  LocationsDynamoDBThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: TableName
          Value: !Ref LocationsTable
      EvaluationPeriods: 1
      MetricName: ThrottledRequests
      Namespace: AWS/DynamoDB
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  ResourcesDynamoDBThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: TableName
          Value: !Ref ResourcesTable
      EvaluationPeriods: 1
      MetricName: ThrottledRequests
      Namespace: AWS/DynamoDB
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  BookingsDynamoDBThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: TableName
          Value: !Ref BookingsTable
      EvaluationPeriods: 1
      MetricName: ThrottledRequests
      Namespace: AWS/DynamoDB
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  ApplicationDashboard:
    Type: AWS::CloudWatch::Dashboard
    Properties:
      DashboardName: !Sub "${AWS::StackName}-dashboard"
      DashboardBody: 
        Fn::Sub: >
          {
            "widgets": [
                {
                    "height": 6,
                    "width": 6,
                    "y": 12,
                    "x": 0,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/Lambda", "Invocations", "FunctionName", "${LocationsFunction}" ],
                            [ ".", "Errors", ".", "." ],
                            [ ".", "Throttles", ".", "." ],
                            [ ".", "Duration", ".", ".", { "stat": "Average" } ],
                            [ ".", "ConcurrentExecutions", ".", ".", { "stat": "Maximum" } ]
                        ],
                        "view": "timeSeries",
                        "region": "${AWS::Region}",
                        "stacked": false,
                        "title": "Locations Lambda",
                        "period": 60,
                        "stat": "Sum"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 18,
                    "x": 0,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/Lambda", "Invocations", "FunctionName", "${ResourcesFunction}" ],
                            [ ".", "Errors", ".", "." ],
                            [ ".", "Throttles", ".", "." ],
                            [ ".", "Duration", ".", ".", { "stat": "Average" } ],
                            [ ".", "ConcurrentExecutions", ".", ".", { "stat": "Maximum" } ]
                        ],
                        "view": "timeSeries",
                        "region": "${AWS::Region}",
                        "stacked": false,
                        "title": "Resources Lambda",
                        "period": 60,
                        "stat": "Sum"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 24,
                    "x": 0,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/Lambda", "Invocations", "FunctionName", "${BookingsFunction}" ],
                            [ ".", "Errors", ".", "." ],
                            [ ".", "Throttles", ".", "." ],
                            [ ".", "Duration", ".", ".", { "stat": "Average" } ],
                            [ ".", "ConcurrentExecutions", ".", ".", { "stat": "Maximum" } ]
                        ],
                        "view": "timeSeries",
                        "region": "${AWS::Region}",
                        "stacked": false,
                        "title": "Bookings Lambda",
                        "period": 60,
                        "stat": "Sum"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 6,
                    "x": 6,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/Lambda", "Invocations", "FunctionName", "${AuthorizerFunction}" ],
                            [ ".", "Errors", ".", "." ],
                            [ ".", "Throttles", ".", "." ],
                            [ ".", "Duration", ".", ".", { "stat": "Average" } ],
                            [ ".", "ConcurrentExecutions", ".", ".", { "stat": "Maximum" } ]
                        ],
                        "view": "timeSeries",
                        "region": "${AWS::Region}",
                        "stacked": false,
                        "title": "Authorizer Lambda",
                        "period": 60,
                        "stat": "Sum"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 12,
                    "x": 6,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/DynamoDB", "ConsumedReadCapacityUnits", "TableName", "${LocationsTable}", { "stat": "Maximum" } ],
                            [ ".", "ConsumedWriteCapacityUnits", ".", ".", { "stat": "Maximum" } ],
                            [ ".", "ProvisionedReadCapacityUnits", ".", ".", { "period": 300 } ],
                            [ ".", "ProvisionedWriteCapacityUnits", ".", ".", { "period": 300 } ]
                        ],
                        "view": "timeSeries",
                        "stacked": false,
                        "region": "${AWS::Region}",
                        "title": "DynamoDB - Locations",
                        "period": 60,
                        "stat": "Average"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 18,
                    "x": 6,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/DynamoDB", "ConsumedReadCapacityUnits", "TableName", "${ResourcesTable}", { "stat": "Maximum" } ],
                            [ ".", "ConsumedWriteCapacityUnits", ".", ".", { "stat": "Maximum" } ],
                            [ ".", "ProvisionedReadCapacityUnits", ".", ".", { "period": 300 } ],
                            [ ".", "ProvisionedWriteCapacityUnits", ".", ".", { "period": 300 } ]
                        ],
                        "view": "timeSeries",
                        "stacked": false,
                        "region": "${AWS::Region}",
                        "title": "DynamoDB - Resources",
                        "period": 60,
                        "stat": "Average"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 24,
                    "x": 6,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/DynamoDB", "ConsumedReadCapacityUnits", "TableName", "${BookingsTable}", { "period": 60, "stat": "Maximum" } ],
                            [ ".", "ConsumedWriteCapacityUnits", ".", ".", { "period": 60, "stat": "Maximum" } ],
                            [ ".", "ProvisionedReadCapacityUnits", ".", "." ],
                            [ ".", "ProvisionedWriteCapacityUnits", ".", "." ]
                        ],
                        "view": "timeSeries",
                        "stacked": false,
                        "region": "${AWS::Region}",
                        "title": "DynamoDB - Bookings",
                        "period": 300,
                        "stat": "Average"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 6,
                    "x": 0,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/ApiGateway", "4xx", "ApiId", "${HttpApi}", { "yAxis": "right" } ],
                            [ ".", "5xx", ".", ".", { "yAxis": "right" } ],
                            [ ".", "DataProcessed", ".", ".", { "yAxis": "left" } ],
                            [ ".", "Count", ".", ".", { "label": "Count", "yAxis": "right" } ],
                            [ ".", "IntegrationLatency", ".", ".", { "stat": "Average" } ],
                            [ ".", "Latency", ".", ".", { "stat": "Average" } ]
                        ],
                        "view": "timeSeries",
                        "stacked": false,
                        "region": "${AWS::Region}",
                        "period": 60,
                        "stat": "Sum",
                        "title": "API Gateway"
                    }
                },
                {
                    "height": 6,
                    "width": 12,
                    "y": 0,
                    "x": 0,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "${AWS::StackName}", "ProcessedBookings", "ServiceName", "${BookingsFunction}", "LogGroup", "${BookingsFunction}", "ServiceType", "AWS::Lambda::Function", "Service", "Bookings", { "label": "Processed Bookings"} ],
                            [ ".", "ProcessedLocations", ".", "${LocationsFunction}", ".", "${LocationsFunction}", ".", ".", ".", "Locations", { "label": "Processed Locations"} ],
                            [ ".", "ProcessedResources", ".", "${ResourcesFunction}", ".", "${ResourcesFunction}", ".", ".", ".", "Resources", { "label": "Processed Resources"} ]
                        ],
                        "view": "timeSeries",
                        "stacked": false,
                        "title": "Business Metrics",
                        "region": "${AWS::Region}",
                        "period": 60,
                        "stat": "Sum"
                    }
                }
            ]
          }

Outputs:
  APIEndpoint:
    Description: "API Gateway endpoint URL"
    Value: !Sub "https://${HttpApi}.execute-api.${AWS::Region}.amazonaws.com/"

  DashboardURL:
    Description: "Dashboard URL"
    Value: !Sub "https://console.aws.amazon.com/cloudwatch/home?region=${AWS::Region}#dashboards:name=${ApplicationDashboard}"
  
  AlarmsTopic:
    Description: "SNS Topic to be used for the alarms subscriptions"
    Value: !Ref AlarmsTopic

  AccessLogs:
    Description: "CloudWatch Logs group for API Gateway access logs"
    Value: !Ref AccessLogs

  LocationsTable:
    Description: "DynamoDB Locations table"
    Value: !Ref LocationsTable
    
  ResourcesTable:
    Description: "DynamoDB Resources table"
    Value: !Ref ResourcesTable

  BookingsTable:
    Description: "DynamoDB Bookings table"
    Value: !Ref BookingsTable
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



serverless-rest-api/python-http-sam/template.yaml [206:829]:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
            Principal:
              Service:
                - apigateway.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: "/"
      Tags:
        - Key: "Stack"
          Value: !Sub "${AWS::StackName}"

  AuthorizerFunctionExecutionRolePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: !Sub ${AWS::StackName}-Authorizer-Policy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: lambda:InvokeFunction
            Resource: !GetAtt AuthorizerFunction.Arn
      Roles:
        - Ref: AuthorizerFunctionExecutionRole

  HttpApi:
    Type: AWS::Serverless::HttpApi
    Properties:
      FailOnWarnings: True
      CorsConfiguration:
        AllowMethods:
          - PUT
          - GET
          - DELETE
          - OPTIONS
        AllowHeaders:
          - Content-Type
          - Authorization
          - X-Forwarded-For
          - X-Api-Key
          - X-Amz-Date
          - X-Amz-Security-Token
        AllowOrigins:
          - "*"
      Auth:
        Authorizers:
          LambdaAuthorizer:
            AuthorizerPayloadFormatVersion: 1.0
            FunctionArn: !GetAtt AuthorizerFunction.Arn
            FunctionInvokeRole: !GetAtt AuthorizerFunctionExecutionRole.Arn
            Identity:
              Headers:
                - Authorization
        DefaultAuthorizer: LambdaAuthorizer
      AccessLogSettings:
        DestinationArn: !GetAtt AccessLogs.Arn
        Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "status":"$context.status","protocol":"$context.protocol", "integrationStatus": $context.integrationStatus, "integrationLatency": $context.integrationLatency, "responseLength":"$context.responseLength" }'
      Tags:
        Name: !Sub "${AWS::StackName}-API"
        Stack: !Sub "${AWS::StackName}"

  AccessLogs:
    Type: AWS::Logs::LogGroup
    Properties:
      RetentionInDays: 30
      LogGroupName: !Sub "/${AWS::StackName}/APIAccessLogs"

  LocationsTable:
    Type: AWS::Serverless::SimpleTable
    Properties:
      PrimaryKey:
        Name: locationid
        Type: String
      ProvisionedThroughput:
        ReadCapacityUnits: 2
        WriteCapacityUnits: 2
      Tags:
        Stack: !Sub "${AWS::StackName}"

  ResourcesTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: resourceid
          AttributeType: S
        - AttributeName: locationid
          AttributeType: S
      KeySchema:
        - AttributeName: resourceid
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 2
        WriteCapacityUnits: 2
      GlobalSecondaryIndexes:
        - IndexName: locationidGSI
          KeySchema:
            - AttributeName: locationid
              KeyType: HASH
          Projection:
            ProjectionType: ALL
          ProvisionedThroughput:
            ReadCapacityUnits: 2
            WriteCapacityUnits: 2
      Tags:
        - Key: "Stack"
          Value: !Sub "${AWS::StackName}"

  BookingsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: bookingid
          AttributeType: S
        - AttributeName: userid
          AttributeType: S
        - AttributeName: resourceid
          AttributeType: S
        - AttributeName: starttimeepochtime
          AttributeType: N
      KeySchema:
        - AttributeName: bookingid
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 2
        WriteCapacityUnits: 2
      GlobalSecondaryIndexes:
        - IndexName: useridGSI
          KeySchema:
            - AttributeName: userid
              KeyType: HASH
          Projection:
            ProjectionType: ALL
          ProvisionedThroughput:
            ReadCapacityUnits: 2
            WriteCapacityUnits: 2
        - IndexName: bookingsByUserByTimeGSI
          KeySchema:
            - AttributeName: userid
              KeyType: HASH
            - AttributeName: starttimeepochtime
              KeyType: RANGE
          Projection:
            ProjectionType: ALL
          ProvisionedThroughput:
            ReadCapacityUnits: 2
            WriteCapacityUnits: 2
        - IndexName: bookingsByResourceByTimeGSI
          KeySchema:
            - AttributeName: resourceid
              KeyType: HASH
            - AttributeName: starttimeepochtime
              KeyType: RANGE
          Projection:
            ProjectionType: ALL
          ProvisionedThroughput:
            ReadCapacityUnits: 2
            WriteCapacityUnits: 2
      Tags:
        - Key: "Stack"
          Value: !Sub "${AWS::StackName}"

  AlarmsKMSKey:
    Type: AWS::KMS::Key
    Properties: 
      Description: CMK for SNS alarms topic
      Enabled: true
      EnableKeyRotation: True
      KeyPolicy: 
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal: 
              Service: 
                - "cloudwatch.amazonaws.com"
                - "sns.amazonaws.com"
            Action:
              - "kms:GenerateDataKey*"
              - "kms:Decrypt"
            Resource: "*"
          - Effect: Allow
            Principal: 
              AWS: 
                - !Sub "arn:aws:iam::${AWS::AccountId}:root"
            Action:
              - "kms:*"
            Resource: "*"
      KeySpec: SYMMETRIC_DEFAULT
      KeyUsage: ENCRYPT_DECRYPT
      PendingWindowInDays: 30
      Tags: 
        - Key: "Stack" 
          Value: !Sub "${AWS::StackName}"

  AlarmsTopic:
    Type: AWS::SNS::Topic
    Properties:
      KmsMasterKeyId: !Ref AlarmsKMSKey
      Tags:
        - Key: "Stack" 
          Value: !Sub "${AWS::StackName}"

  HttpApiErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: ApiName
          Value: !Ref HttpApi
      EvaluationPeriods: 1
      MetricName: 5XXError
      Namespace: AWS/ApiGateway
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  AuthorizerFunctionErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref AuthorizerFunction
      EvaluationPeriods: 1
      MetricName: Errors
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  LocationsFunctionErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref LocationsFunction
      EvaluationPeriods: 1
      MetricName: Errors
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  ResourcesFunctionErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref ResourcesFunction
      EvaluationPeriods: 1
      MetricName: Errors
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  BookingsFunctionErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref BookingsFunction
      EvaluationPeriods: 1
      MetricName: Errors
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  LocationsFunctionThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref LocationsFunction
      EvaluationPeriods: 1
      MetricName: Throttles
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  ResourcesFunctionThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref ResourcesFunction
      EvaluationPeriods: 1
      MetricName: Throttles
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  BookingsFunctionThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref BookingsFunction
      EvaluationPeriods: 1
      MetricName: Throttles
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  AuthorizerFunctionThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: FunctionName
          Value: !Ref AuthorizerFunction
      EvaluationPeriods: 1
      MetricName: Throttles
      Namespace: AWS/Lambda
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  LocationsDynamoDBThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: TableName
          Value: !Ref LocationsTable
      EvaluationPeriods: 1
      MetricName: ThrottledRequests
      Namespace: AWS/DynamoDB
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  ResourcesDynamoDBThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: TableName
          Value: !Ref ResourcesTable
      EvaluationPeriods: 1
      MetricName: ThrottledRequests
      Namespace: AWS/DynamoDB
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  BookingsDynamoDBThrottlingAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmActions:
        - !Ref AlarmsTopic
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Dimensions:
        - Name: TableName
          Value: !Ref BookingsTable
      EvaluationPeriods: 1
      MetricName: ThrottledRequests
      Namespace: AWS/DynamoDB
      Period: 60
      Statistic: Sum
      Threshold: 1.0

  ApplicationDashboard:
    Type: AWS::CloudWatch::Dashboard
    Properties:
      DashboardName: !Sub "${AWS::StackName}-dashboard"
      DashboardBody:
        Fn::Sub: >
          {
            "widgets": [
                {
                    "height": 6,
                    "width": 6,
                    "y": 12,
                    "x": 0,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/Lambda", "Invocations", "FunctionName", "${LocationsFunction}" ],
                            [ ".", "Errors", ".", "." ],
                            [ ".", "Throttles", ".", "." ],
                            [ ".", "Duration", ".", ".", { "stat": "Average" } ],
                            [ ".", "ConcurrentExecutions", ".", ".", { "stat": "Maximum" } ]
                        ],
                        "view": "timeSeries",
                        "region": "${AWS::Region}",
                        "stacked": false,
                        "title": "Locations Lambda",
                        "period": 60,
                        "stat": "Sum"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 18,
                    "x": 0,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/Lambda", "Invocations", "FunctionName", "${ResourcesFunction}" ],
                            [ ".", "Errors", ".", "." ],
                            [ ".", "Throttles", ".", "." ],
                            [ ".", "Duration", ".", ".", { "stat": "Average" } ],
                            [ ".", "ConcurrentExecutions", ".", ".", { "stat": "Maximum" } ]
                        ],
                        "view": "timeSeries",
                        "region": "${AWS::Region}",
                        "stacked": false,
                        "title": "Resources Lambda",
                        "period": 60,
                        "stat": "Sum"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 24,
                    "x": 0,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/Lambda", "Invocations", "FunctionName", "${BookingsFunction}" ],
                            [ ".", "Errors", ".", "." ],
                            [ ".", "Throttles", ".", "." ],
                            [ ".", "Duration", ".", ".", { "stat": "Average" } ],
                            [ ".", "ConcurrentExecutions", ".", ".", { "stat": "Maximum" } ]
                        ],
                        "view": "timeSeries",
                        "region": "${AWS::Region}",
                        "stacked": false,
                        "title": "Bookings Lambda",
                        "period": 60,
                        "stat": "Sum"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 6,
                    "x": 6,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/Lambda", "Invocations", "FunctionName", "${AuthorizerFunction}" ],
                            [ ".", "Errors", ".", "." ],
                            [ ".", "Throttles", ".", "." ],
                            [ ".", "Duration", ".", ".", { "stat": "Average" } ],
                            [ ".", "ConcurrentExecutions", ".", ".", { "stat": "Maximum" } ]
                        ],
                        "view": "timeSeries",
                        "region": "${AWS::Region}",
                        "stacked": false,
                        "title": "Authorizer Lambda",
                        "period": 60,
                        "stat": "Sum"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 12,
                    "x": 6,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/DynamoDB", "ConsumedReadCapacityUnits", "TableName", "${LocationsTable}", { "stat": "Maximum" } ],
                            [ ".", "ConsumedWriteCapacityUnits", ".", ".", { "stat": "Maximum" } ],
                            [ ".", "ProvisionedReadCapacityUnits", ".", ".", { "period": 300 } ],
                            [ ".", "ProvisionedWriteCapacityUnits", ".", ".", { "period": 300 } ]
                        ],
                        "view": "timeSeries",
                        "stacked": false,
                        "region": "${AWS::Region}",
                        "title": "DynamoDB - Locations",
                        "period": 60,
                        "stat": "Average"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 18,
                    "x": 6,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/DynamoDB", "ConsumedReadCapacityUnits", "TableName", "${ResourcesTable}", { "stat": "Maximum" } ],
                            [ ".", "ConsumedWriteCapacityUnits", ".", ".", { "stat": "Maximum" } ],
                            [ ".", "ProvisionedReadCapacityUnits", ".", ".", { "period": 300 } ],
                            [ ".", "ProvisionedWriteCapacityUnits", ".", ".", { "period": 300 } ]
                        ],
                        "view": "timeSeries",
                        "stacked": false,
                        "region": "${AWS::Region}",
                        "title": "DynamoDB - Resources",
                        "period": 60,
                        "stat": "Average"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 24,
                    "x": 6,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/DynamoDB", "ConsumedReadCapacityUnits", "TableName", "${BookingsTable}", { "period": 60, "stat": "Maximum" } ],
                            [ ".", "ConsumedWriteCapacityUnits", ".", ".", { "period": 60, "stat": "Maximum" } ],
                            [ ".", "ProvisionedReadCapacityUnits", ".", "." ],
                            [ ".", "ProvisionedWriteCapacityUnits", ".", "." ]
                        ],
                        "view": "timeSeries",
                        "stacked": false,
                        "region": "${AWS::Region}",
                        "title": "DynamoDB - Bookings",
                        "period": 300,
                        "stat": "Average"
                    }
                },
                {
                    "height": 6,
                    "width": 6,
                    "y": 6,
                    "x": 0,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "AWS/ApiGateway", "4xx", "ApiId", "${HttpApi}", { "yAxis": "right" } ],
                            [ ".", "5xx", ".", ".", { "yAxis": "right" } ],
                            [ ".", "DataProcessed", ".", ".", { "yAxis": "left" } ],
                            [ ".", "Count", ".", ".", { "label": "Count", "yAxis": "right" } ],
                            [ ".", "IntegrationLatency", ".", ".", { "stat": "Average" } ],
                            [ ".", "Latency", ".", ".", { "stat": "Average" } ]
                        ],
                        "view": "timeSeries",
                        "stacked": false,
                        "region": "${AWS::Region}",
                        "period": 60,
                        "stat": "Sum",
                        "title": "API Gateway"
                    }
                },
                {
                    "height": 6,
                    "width": 12,
                    "y": 0,
                    "x": 0,
                    "type": "metric",
                    "properties": {
                        "metrics": [
                            [ "${AWS::StackName}", "ProcessedBookings", "ServiceName", "${BookingsFunction}", "LogGroup", "${BookingsFunction}", "ServiceType", "AWS::Lambda::Function", "Service", "Bookings", { "label": "Processed Bookings"} ],
                            [ ".", "ProcessedLocations", ".", "${LocationsFunction}", ".", "${LocationsFunction}", ".", ".", ".", "Locations", { "label": "Processed Locations"} ],
                            [ ".", "ProcessedResources", ".", "${ResourcesFunction}", ".", "${ResourcesFunction}", ".", ".", ".", "Resources", { "label": "Processed Resources"} ]
                        ],
                        "view": "timeSeries",
                        "stacked": false,
                        "title": "Business Metrics",
                        "region": "${AWS::Region}",
                        "period": 60,
                        "stat": "Sum"
                    }
                }
            ]
          }

Outputs:
  APIEndpoint:
    Description: "API Gateway endpoint URL"
    Value: !Sub "https://${HttpApi}.execute-api.${AWS::Region}.amazonaws.com/"

  DashboardURL:
    Description: "Dashboard URL"
    Value: !Sub "https://console.aws.amazon.com/cloudwatch/home?region=${AWS::Region}#dashboards:name=${ApplicationDashboard}"

  AlarmsTopic:
    Description: "SNS Topic to be used for the alarms subscriptions"
    Value: !Ref AlarmsTopic

  AccessLogs:
    Description: "CloudWatch Logs group for API Gateway access logs"
    Value: !Ref AccessLogs

  LocationsTable:
    Description: "DynamoDB Locations table"
    Value: !Ref LocationsTable

  ResourcesTable:
    Description: "DynamoDB Resources table"
    Value: !Ref ResourcesTable

  BookingsTable:
    Description: "DynamoDB Bookings table"
    Value: !Ref BookingsTable
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



