Operational Serverless

Vicenç García Altés

@vgaltes

info@vgaltes.com

https://vgaltes.com

What's serverless?

Title Text

BIASES

MORE BIASES

The three ways

  • Principles of flow
  • Principles of feedback
  • Principles of continuous learning and investigation

TECHNICAL PRACTICES OF FLOW

FAST AND RELIABLE TESTING

ENABLE AND PRACTICE CONTINUOUS INTEGRATION

CREATE THE FOUNDATIONS OF OUR DEPLOYMENT PIPELINE

FOUNDATIONS OF THE DEPLOYMENT PIPELINE

  • Enable on-demand creation of Dev, Test and Prod environments.
  • Create our single repository of truth for the entire system.
  • Make our infrastructure easier to rebuild than to repair.
  • Modify our definition of development done to include running in production-like environments.

CLOUDFORMATION

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Description": "The AWS CloudFormation template for this Serverless application",
  "Resources": {
    "ServerlessDeploymentBucket": {
      "Type": "AWS::S3::Bucket"
    },
    "ServiceDashaLogGroup": {
      "Type": "AWS::Logs::LogGroup",
      "Properties": {
        "LogGroupName": "/aws/lambda/serverless-observability-dev-service-a"
      }
    },
    "ServiceDashbLogGroup": {
      "Type": "AWS::Logs::LogGroup",
      "Properties": {
        "LogGroupName": "/aws/lambda/serverless-observability-dev-service-b"
      }
    },
    "ServiceDashcLogGroup": {
      "Type": "AWS::Logs::LogGroup",
      "Properties": {
        "LogGroupName": "/aws/lambda/serverless-observability-dev-service-c"
      }
    },
    "ServiceDashreadDashsnsLogGroup": {
      "Type": "AWS::Logs::LogGroup",
      "Properties": {
        "LogGroupName": "/aws/lambda/serverless-observability-dev-service-read-sns"
      }
    },
    "ServiceDashreadDashdynamoLogGroup": {
      "Type": "AWS::Logs::LogGroup",
      "Properties": {
        "LogGroupName": "/aws/lambda/serverless-observability-dev-service-read-dynamo"
      }
    },
    "ServiceDashreadDashs3LogGroup": {
      "Type": "AWS::Logs::LogGroup",
      "Properties": {
        "LogGroupName": "/aws/lambda/serverless-observability-dev-service-read-s3"
      }
    },
    "TimeoutLogGroup": {
      "Type": "AWS::Logs::LogGroup",
      "Properties": {
        "LogGroupName": "/aws/lambda/serverless-observability-dev-timeout"
      }
    },
    "ErrorLogGroup": {
      "Type": "AWS::Logs::LogGroup",
      "Properties": {
        "LogGroupName": "/aws/lambda/serverless-observability-dev-error"
      }
    },
    "SnsLogGroup": {
      "Type": "AWS::Logs::LogGroup",
      "Properties": {
        "LogGroupName": "/aws/lambda/serverless-observability-dev-sns"
      }
    },
    "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",
                  "serverless-observability",
                  "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/serverless-observability-dev-service-a:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-service-b:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-service-c:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-service-read-sns:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-service-read-dynamo:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-service-read-s3:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-timeout:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-error:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-sns:*"
                    }
                  ]
                },
                {
                  "Effect": "Allow",
                  "Action": [
                    "logs:PutLogEvents"
                  ],
                  "Resource": [
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-service-a:*:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-service-b:*:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-service-c:*:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-service-read-sns:*:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-service-read-dynamo:*:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-service-read-s3:*:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-timeout:*:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-error:*:*"
                    },
                    {
                      "Fn::Sub": "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/serverless-observability-dev-sns:*:*"
                    }
                  ]
                },
                {
                  "Effect": "Allow",
                  "Action": [
                    "dynamodb:PutItem",
                    "dynamodb:GetItem",
                    "dynamodb:DescribeStream",
                    "dynamodb:GetRecords",
                    "dynamodb:GetShardIterator",
                    "dynamodb:ListStreams"
                  ],
                  "Resource": [
                    {
                      "Fn::Sub": "arn:aws:dynamodb:${AWS::Region}:*:table/serverless-observability-dev"
                    }
                  ]
                },
                {
                  "Effect": "Allow",
                  "Action": [
                    "s3:GetObject*",
                    "s3:PutObject*",
                    "s3:ListBucket*",
                    "s3:PutBucketNotification"
                  ],
                  "Resource": [
                    {
                      "Fn::Sub": "arn:aws:s3:::serverless-observability-dev-${AWS::Region}-${AWS::AccountId}"
                    },
                    {
                      "Fn::Sub": "arn:aws:s3:::serverless-observability-dev-${AWS::Region}-${AWS::AccountId}/*"
                    }
                  ]
                },
                {
                  "Effect": "Allow",
                  "Action": [
                    "sns:Publish",
                    "sns:Subscribe"
                  ],
                  "Resource": [
                    {
                      "Fn::Sub": "arn:aws:sns:${AWS::Region}:*:serverless-observability-dev"
                    }
                  ]
                },
                {
                  "Effect": "Allow",
                  "Action": [
                    "lambda:InvokeFunction"
                  ],
                  "Resource": [
                    {
                      "Fn::Sub": "arn:aws:lambda:${AWS::Region}:*:function:serverless-observability-dev-*"
                    }
                  ]
                },
                {
                  "Effect": "Allow",
                  "Action": [
                    "dynamodb:GetRecords",
                    "dynamodb:GetShardIterator",
                    "dynamodb:DescribeStream",
                    "dynamodb:ListStreams"
                  ],
                  "Resource": [
                    {
                      "Fn::GetAtt": [
                        "DynamoDB",
                        "StreamArn"
                      ]
                    }
                  ]
                }
              ]
            }
          }
        ],
        "Path": "/",
        "RoleName": {
          "Fn::Join": [
            "-",
            [
              "serverless-observability",
              "dev",
              {
                "Fn::Sub": "${AWS::Region}"
              },
              "lambdaRole"
            ]
          ]
        }
      }
    },
    "ServiceDashaLambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": {
            "Ref": "ServerlessDeploymentBucket"
          },
          "S3Key": "serverless/serverless-observability/dev/1536766420565-2018-09-12T15:33:40.565Z/serverless-observability.zip"
        },
        "FunctionName": "serverless-observability-dev-service-a",
        "Handler": "functions/service-a.handler",
        "MemorySize": 1024,
        "Role": {
          "Fn::GetAtt": [
            "IamRoleLambdaExecution",
            "Arn"
          ]
        },
        "Runtime": "nodejs8.10",
        "Timeout": 10,
        "Environment": {
          "Variables": {
            "stage": "dev",
            "service": "serverless-observability",
            "accountId": {
              "Ref": "AWS::AccountId"
            },
            "thundra_api_key": "33acec61-a6c1-49ad-96ad-2ad513a64dab",
            "BUCKET_NAME": {
              "Fn::Sub": "serverless-observability-dev-${AWS::Region}-${AWS::AccountId}"
            }
          }
        }
      },
      "DependsOn": [
        "ServiceDashaLogGroup",
        "IamRoleLambdaExecution"
      ]
    },
    "ServiceDashbLambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": {
            "Ref": "ServerlessDeploymentBucket"
          },
          "S3Key": "serverless/serverless-observability/dev/1536766420565-2018-09-12T15:33:40.565Z/serverless-observability.zip"
        },
        "FunctionName": "serverless-observability-dev-service-b",
        "Handler": "functions/service-b.handler",
        "MemorySize": 1024,
        "Role": {
          "Fn::GetAtt": [
            "IamRoleLambdaExecution",
            "Arn"
          ]
        },
        "Runtime": "nodejs8.10",
        "Timeout": 6,
        "Environment": {
          "Variables": {
            "stage": "dev",
            "service": "serverless-observability",
            "accountId": {
              "Ref": "AWS::AccountId"
            },
            "thundra_api_key": "33acec61-a6c1-49ad-96ad-2ad513a64dab"
          }
        }
      },
      "DependsOn": [
        "ServiceDashbLogGroup",
        "IamRoleLambdaExecution"
      ]
    },
    "ServiceDashcLambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": {
            "Ref": "ServerlessDeploymentBucket"
          },
          "S3Key": "serverless/serverless-observability/dev/1536766420565-2018-09-12T15:33:40.565Z/serverless-observability.zip"
        },
        "FunctionName": "serverless-observability-dev-service-c",
        "Handler": "functions/service-c.handler",
        "MemorySize": 1024,
        "Role": {
          "Fn::GetAtt": [
            "IamRoleLambdaExecution",
            "Arn"
          ]
        },
        "Runtime": "nodejs8.10",
        "Timeout": 6,
        "Environment": {
          "Variables": {
            "stage": "dev",
            "service": "serverless-observability",
            "accountId": {
              "Ref": "AWS::AccountId"
            },
            "thundra_api_key": "33acec61-a6c1-49ad-96ad-2ad513a64dab"
          }
        }
      },
      "DependsOn": [
        "ServiceDashcLogGroup",
        "IamRoleLambdaExecution"
      ]
    },
    "ServiceDashreadDashsnsLambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": {
            "Ref": "ServerlessDeploymentBucket"
          },
          "S3Key": "serverless/serverless-observability/dev/1536766420565-2018-09-12T15:33:40.565Z/serverless-observability.zip"
        },
        "FunctionName": "serverless-observability-dev-service-read-sns",
        "Handler": "functions/service-read-sns.handler",
        "MemorySize": 1024,
        "Role": {
          "Fn::GetAtt": [
            "IamRoleLambdaExecution",
            "Arn"
          ]
        },
        "Runtime": "nodejs8.10",
        "Timeout": 6,
        "Environment": {
          "Variables": {
            "stage": "dev",
            "service": "serverless-observability",
            "accountId": {
              "Ref": "AWS::AccountId"
            },
            "thundra_api_key": "33acec61-a6c1-49ad-96ad-2ad513a64dab"
          }
        }
      },
      "DependsOn": [
        "ServiceDashreadDashsnsLogGroup",
        "IamRoleLambdaExecution"
      ]
    },
    "ServiceDashreadDashdynamoLambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": {
            "Ref": "ServerlessDeploymentBucket"
          },
          "S3Key": "serverless/serverless-observability/dev/1536766420565-2018-09-12T15:33:40.565Z/serverless-observability.zip"
        },
        "FunctionName": "serverless-observability-dev-service-read-dynamo",
        "Handler": "functions/service-read-dynamo.handler",
        "MemorySize": 1024,
        "Role": {
          "Fn::GetAtt": [
            "IamRoleLambdaExecution",
            "Arn"
          ]
        },
        "Runtime": "nodejs8.10",
        "Timeout": 6,
        "Environment": {
          "Variables": {
            "stage": "dev",
            "service": "serverless-observability",
            "accountId": {
              "Ref": "AWS::AccountId"
            },
            "thundra_api_key": "33acec61-a6c1-49ad-96ad-2ad513a64dab"
          }
        }
      },
      "DependsOn": [
        "ServiceDashreadDashdynamoLogGroup",
        "IamRoleLambdaExecution"
      ]
    },
    "ServiceDashreadDashs3LambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": {
            "Ref": "ServerlessDeploymentBucket"
          },
          "S3Key": "serverless/serverless-observability/dev/1536766420565-2018-09-12T15:33:40.565Z/serverless-observability.zip"
        },
        "FunctionName": "serverless-observability-dev-service-read-s3",
        "Handler": "functions/service-read-s3.handler",
        "MemorySize": 1024,
        "Role": {
          "Fn::GetAtt": [
            "IamRoleLambdaExecution",
            "Arn"
          ]
        },
        "Runtime": "nodejs8.10",
        "Timeout": 6,
        "Environment": {
          "Variables": {
            "stage": "dev",
            "service": "serverless-observability",
            "accountId": {
              "Ref": "AWS::AccountId"
            },
            "thundra_api_key": "33acec61-a6c1-49ad-96ad-2ad513a64dab"
          }
        }
      },
      "DependsOn": [
        "ServiceDashreadDashs3LogGroup",
        "IamRoleLambdaExecution"
      ]
    },
    "TimeoutLambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": {
            "Ref": "ServerlessDeploymentBucket"
          },
          "S3Key": "serverless/serverless-observability/dev/1536766420565-2018-09-12T15:33:40.565Z/serverless-observability.zip"
        },
        "FunctionName": "serverless-observability-dev-timeout",
        "Handler": "functions/timeout.handler",
        "MemorySize": 1024,
        "Role": {
          "Fn::GetAtt": [
            "IamRoleLambdaExecution",
            "Arn"
          ]
        },
        "Runtime": "nodejs8.10",
        "Timeout": 1,
        "Environment": {
          "Variables": {
            "stage": "dev",
            "service": "serverless-observability",
            "accountId": {
              "Ref": "AWS::AccountId"
            },
            "thundra_api_key": "33acec61-a6c1-49ad-96ad-2ad513a64dab"
          }
        }
      },
      "DependsOn": [
        "TimeoutLogGroup",
        "IamRoleLambdaExecution"
      ]
    },
    "ErrorLambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": {
            "Ref": "ServerlessDeploymentBucket"
          },
          "S3Key": "serverless/serverless-observability/dev/1536766420565-2018-09-12T15:33:40.565Z/serverless-observability.zip"
        },
        "FunctionName": "serverless-observability-dev-error",
        "Handler": "functions/error.handler",
        "MemorySize": 1024,
        "Role": {
          "Fn::GetAtt": [
            "IamRoleLambdaExecution",
            "Arn"
          ]
        },
        "Runtime": "nodejs8.10",
        "Timeout": 6,
        "Environment": {
          "Variables": {
            "stage": "dev",
            "service": "serverless-observability",
            "accountId": {
              "Ref": "AWS::AccountId"
            },
            "thundra_api_key": "33acec61-a6c1-49ad-96ad-2ad513a64dab"
          }
        }
      },
      "DependsOn": [
        "ErrorLogGroup",
        "IamRoleLambdaExecution"
      ]
    },
    "SnsLambdaFunction": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": {
            "Ref": "ServerlessDeploymentBucket"
          },
          "S3Key": "serverless/serverless-observability/dev/1536766420565-2018-09-12T15:33:40.565Z/serverless-observability.zip"
        },
        "FunctionName": "serverless-observability-dev-sns",
        "Handler": "functions/sns.handler",
        "MemorySize": 1024,
        "Role": {
          "Fn::GetAtt": [
            "IamRoleLambdaExecution",
            "Arn"
          ]
        },
        "Runtime": "nodejs8.10",
        "Timeout": 6,
        "Environment": {
          "Variables": {
            "stage": "dev",
            "service": "serverless-observability",
            "accountId": {
              "Ref": "AWS::AccountId"
            },
            "thundra_api_key": "33acec61-a6c1-49ad-96ad-2ad513a64dab"
          }
        }
      },
      "DependsOn": [
        "SnsLogGroup",
        "IamRoleLambdaExecution"
      ]
    },
    "S3BucketServerlessobservabilitydevAWSRegionAWSAccountId": {
      "Type": "AWS::S3::Bucket",
      "Properties": {
        "BucketName": {
          "Fn::Sub": "serverless-observability-dev-${AWS::Region}-${AWS::AccountId}"
        },
        "NotificationConfiguration": {
          "LambdaConfigurations": [
            {
              "Event": "s3:ObjectCreated:*",
              "Function": {
                "Fn::GetAtt": [
                  "ServiceDashreadDashs3LambdaFunction",
                  "Arn"
                ]
              }
            }
          ]
        }
      },
      "DependsOn": [
        "ServiceDashreadDashs3LambdaPermissionServerlessobservabilitydevAWSRegionAWSAccountIdS3"
      ]
    },
    "ServiceDashreadDashs3LambdaPermissionServerlessobservabilitydevAWSRegionAWSAccountIdS3": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": [
            "ServiceDashreadDashs3LambdaFunction",
            "Arn"
          ]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": {
          "Fn::Join": [
            "",
            [
              "s3.",
              {
                "Ref": "AWS::URLSuffix"
              }
            ]
          ]
        },
        "SourceArn": {
          "Fn::Join": [
            "",
            [
              "arn:",
              {
                "Ref": "AWS::Partition"
              },
              {
                "Fn::Sub": ":s3:::serverless-observability-dev-${AWS::Region}-${AWS::AccountId}"
              }
            ]
          ]
        }
      }
    },
    "ApiGatewayRestApi": {
      "Type": "AWS::ApiGateway::RestApi",
      "Properties": {
        "Name": "dev-serverless-observability",
        "EndpointConfiguration": {
          "Types": [
            "EDGE"
          ]
        }
      }
    },
    "ApiGatewayResourceDemo": {
      "Type": "AWS::ApiGateway::Resource",
      "Properties": {
        "ParentId": {
          "Fn::GetAtt": [
            "ApiGatewayRestApi",
            "RootResourceId"
          ]
        },
        "PathPart": "demo",
        "RestApiId": {
          "Ref": "ApiGatewayRestApi"
        }
      }
    },
    "ApiGatewayResourceDemoServiceDasha": {
      "Type": "AWS::ApiGateway::Resource",
      "Properties": {
        "ParentId": {
          "Ref": "ApiGatewayResourceDemo"
        },
        "PathPart": "service-a",
        "RestApiId": {
          "Ref": "ApiGatewayRestApi"
        }
      }
    },
    "ApiGatewayResourceDemoServiceDashb": {
      "Type": "AWS::ApiGateway::Resource",
      "Properties": {
        "ParentId": {
          "Ref": "ApiGatewayResourceDemo"
        },
        "PathPart": "service-b",
        "RestApiId": {
          "Ref": "ApiGatewayRestApi"
        }
      }
    },
    "ApiGatewayResourceDemoTimeout": {
      "Type": "AWS::ApiGateway::Resource",
      "Properties": {
        "ParentId": {
          "Ref": "ApiGatewayResourceDemo"
        },
        "PathPart": "timeout",
        "RestApiId": {
          "Ref": "ApiGatewayRestApi"
        }
      }
    },
    "ApiGatewayResourceDemoError": {
      "Type": "AWS::ApiGateway::Resource",
      "Properties": {
        "ParentId": {
          "Ref": "ApiGatewayResourceDemo"
        },
        "PathPart": "error",
        "RestApiId": {
          "Ref": "ApiGatewayRestApi"
        }
      }
    },
    "ApiGatewayMethodDemoServiceDashaGet": {
      "Type": "AWS::ApiGateway::Method",
      "Properties": {
        "HttpMethod": "GET",
        "RequestParameters": {
          "method.request.querystring.n": true
        },
        "ResourceId": {
          "Ref": "ApiGatewayResourceDemoServiceDasha"
        },
        "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": [
                    "ServiceDashaLambdaFunction",
                    "Arn"
                  ]
                },
                "/invocations"
              ]
            ]
          }
        },
        "MethodResponses": []
      }
    },
    "ApiGatewayMethodDemoServiceDashbGet": {
      "Type": "AWS::ApiGateway::Method",
      "Properties": {
        "HttpMethod": "GET",
        "RequestParameters": {},
        "ResourceId": {
          "Ref": "ApiGatewayResourceDemoServiceDashb"
        },
        "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": [
                    "ServiceDashbLambdaFunction",
                    "Arn"
                  ]
                },
                "/invocations"
              ]
            ]
          }
        },
        "MethodResponses": []
      }
    },
    "ApiGatewayMethodDemoTimeoutGet": {
      "Type": "AWS::ApiGateway::Method",
      "Properties": {
        "HttpMethod": "GET",
        "RequestParameters": {},
        "ResourceId": {
          "Ref": "ApiGatewayResourceDemoTimeout"
        },
        "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": [
                    "TimeoutLambdaFunction",
                    "Arn"
                  ]
                },
                "/invocations"
              ]
            ]
          }
        },
        "MethodResponses": []
      }
    },
    "ApiGatewayMethodDemoErrorGet": {
      "Type": "AWS::ApiGateway::Method",
      "Properties": {
        "HttpMethod": "GET",
        "RequestParameters": {},
        "ResourceId": {
          "Ref": "ApiGatewayResourceDemoError"
        },
        "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": [
                    "ErrorLambdaFunction",
                    "Arn"
                  ]
                },
                "/invocations"
              ]
            ]
          }
        },
        "MethodResponses": []
      }
    },
    "ApiGatewayDeployment1536766421374": {
      "Type": "AWS::ApiGateway::Deployment",
      "Properties": {
        "RestApiId": {
          "Ref": "ApiGatewayRestApi"
        },
        "StageName": "dev"
      },
      "DependsOn": [
        "ApiGatewayMethodDemoServiceDashaGet",
        "ApiGatewayMethodDemoServiceDashbGet",
        "ApiGatewayMethodDemoTimeoutGet",
        "ApiGatewayMethodDemoErrorGet"
      ]
    },
    "ServiceDashaLambdaPermissionApiGateway": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": [
            "ServiceDashaLambdaFunction",
            "Arn"
          ]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": {
          "Fn::Join": [
            "",
            [
              "apigateway.",
              {
                "Ref": "AWS::URLSuffix"
              }
            ]
          ]
        },
        "SourceArn": {
          "Fn::Join": [
            "",
            [
              "arn:",
              {
                "Ref": "AWS::Partition"
              },
              ":execute-api:",
              {
                "Ref": "AWS::Region"
              },
              ":",
              {
                "Ref": "AWS::AccountId"
              },
              ":",
              {
                "Ref": "ApiGatewayRestApi"
              },
              "/*/*"
            ]
          ]
        }
      }
    },
    "ServiceDashbLambdaPermissionApiGateway": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": [
            "ServiceDashbLambdaFunction",
            "Arn"
          ]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": {
          "Fn::Join": [
            "",
            [
              "apigateway.",
              {
                "Ref": "AWS::URLSuffix"
              }
            ]
          ]
        },
        "SourceArn": {
          "Fn::Join": [
            "",
            [
              "arn:",
              {
                "Ref": "AWS::Partition"
              },
              ":execute-api:",
              {
                "Ref": "AWS::Region"
              },
              ":",
              {
                "Ref": "AWS::AccountId"
              },
              ":",
              {
                "Ref": "ApiGatewayRestApi"
              },
              "/*/*"
            ]
          ]
        }
      }
    },
    "TimeoutLambdaPermissionApiGateway": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": [
            "TimeoutLambdaFunction",
            "Arn"
          ]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": {
          "Fn::Join": [
            "",
            [
              "apigateway.",
              {
                "Ref": "AWS::URLSuffix"
              }
            ]
          ]
        },
        "SourceArn": {
          "Fn::Join": [
            "",
            [
              "arn:",
              {
                "Ref": "AWS::Partition"
              },
              ":execute-api:",
              {
                "Ref": "AWS::Region"
              },
              ":",
              {
                "Ref": "AWS::AccountId"
              },
              ":",
              {
                "Ref": "ApiGatewayRestApi"
              },
              "/*/*"
            ]
          ]
        }
      }
    },
    "ErrorLambdaPermissionApiGateway": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": [
            "ErrorLambdaFunction",
            "Arn"
          ]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": {
          "Fn::Join": [
            "",
            [
              "apigateway.",
              {
                "Ref": "AWS::URLSuffix"
              }
            ]
          ]
        },
        "SourceArn": {
          "Fn::Join": [
            "",
            [
              "arn:",
              {
                "Ref": "AWS::Partition"
              },
              ":execute-api:",
              {
                "Ref": "AWS::Region"
              },
              ":",
              {
                "Ref": "AWS::AccountId"
              },
              ":",
              {
                "Ref": "ApiGatewayRestApi"
              },
              "/*/*"
            ]
          ]
        }
      }
    },
    "SNSTopicServerlessobservabilitydev": {
      "Type": "AWS::SNS::Topic",
      "Properties": {
        "TopicName": "serverless-observability-dev",
        "DisplayName": "serverless-observability-dev",
        "Subscription": [
          {
            "Endpoint": {
              "Fn::GetAtt": [
                "ServiceDashreadDashsnsLambdaFunction",
                "Arn"
              ]
            },
            "Protocol": "lambda"
          },
          {
            "Endpoint": {
              "Fn::GetAtt": [
                "SnsLambdaFunction",
                "Arn"
              ]
            },
            "Protocol": "lambda"
          }
        ]
      }
    },
    "ServiceDashreadDashsnsLambdaPermissionServerlessobservabilitydevSNS": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": [
            "ServiceDashreadDashsnsLambdaFunction",
            "Arn"
          ]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": {
          "Fn::Join": [
            "",
            [
              "sns.",
              {
                "Ref": "AWS::URLSuffix"
              }
            ]
          ]
        },
        "SourceArn": {
          "Fn::Join": [
            "",
            [
              "arn:",
              {
                "Ref": "AWS::Partition"
              },
              ":sns:",
              {
                "Ref": "AWS::Region"
              },
              ":",
              {
                "Ref": "AWS::AccountId"
              },
              ":",
              "serverless-observability-dev"
            ]
          ]
        }
      }
    },
    "SnsLambdaPermissionServerlessobservabilitydevSNS": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "FunctionName": {
          "Fn::GetAtt": [
            "SnsLambdaFunction",
            "Arn"
          ]
        },
        "Action": "lambda:InvokeFunction",
        "Principal": {
          "Fn::Join": [
            "",
            [
              "sns.",
              {
                "Ref": "AWS::URLSuffix"
              }
            ]
          ]
        },
        "SourceArn": {
          "Fn::Join": [
            "",
            [
              "arn:",
              {
                "Ref": "AWS::Partition"
              },
              ":sns:",
              {
                "Ref": "AWS::Region"
              },
              ":",
              {
                "Ref": "AWS::AccountId"
              },
              ":",
              "serverless-observability-dev"
            ]
          ]
        }
      }
    },
    "ServiceDashreadDashdynamoEventSourceMappingDynamodbDynamoDB": {
      "Type": "AWS::Lambda::EventSourceMapping",
      "DependsOn": "IamRoleLambdaExecution",
      "Properties": {
        "BatchSize": 10,
        "EventSourceArn": {
          "Fn::GetAtt": [
            "DynamoDB",
            "StreamArn"
          ]
        },
        "FunctionName": {
          "Fn::GetAtt": [
            "ServiceDashreadDashdynamoLambdaFunction",
            "Arn"
          ]
        },
        "StartingPosition": "TRIM_HORIZON",
        "Enabled": "True"
      }
    },
    "DynamoDB": {
      "Type": "AWS::DynamoDB::Table",
      "Properties": {
        "TableName": "serverless-observability-dev",
        "AttributeDefinitions": [
          {
            "AttributeName": "id",
            "AttributeType": "S"
          }
        ],
        "KeySchema": [
          {
            "AttributeName": "id",
            "KeyType": "HASH"
          }
        ],
        "ProvisionedThroughput": {
          "ReadCapacityUnits": 1,
          "WriteCapacityUnits": 1
        },
        "StreamSpecification": {
          "StreamViewType": "NEW_AND_OLD_IMAGES"
        }
      }
    }
  },
  "Outputs": {
    "ServerlessDeploymentBucketName": {
      "Value": {
        "Ref": "ServerlessDeploymentBucket"
      }
    },
    "ServiceEndpoint": {
      "Description": "URL of the service endpoint",
      "Value": {
        "Fn::Join": [
          "",
          [
            "https://",
            {
              "Ref": "ApiGatewayRestApi"
            },
            {
              "Fn::Sub": ".execute-api.${AWS::Region}."
            },
            {
              "Ref": "AWS::URLSuffix"
            },
            "/dev"
          ]
        ]
      }
    }
  }
}

Terraform

data "aws_iam_role" "role" {
  name = "example-role"
}

resource "aws_api_gateway_rest_api" "api" {
  name = "example-api"
}

resource "aws_api_gateway_resource" "resource" {
  rest_api_id = "${aws_api_gateway_rest_api.api.id}"
  parent_id = "${aws_api_gateway_rest_api.api.root_resource_id}"
  path_part = "example-resource"
}

resource "aws_lambda_function" "function" {
  filename         = "example-zip-file-name.zip"
  function_name    = "example-function-name"
  role             = "${aws_iam_role.role.arn}"
  handler          = "src/example-file-name.handler"
  source_code_hash = "${base64sha256(file("example-zip-file-name.zip"))}"
  runtime          = "nodejs6.10"

  environment {
    variables {
      some_variable = "some_value"
    }
  }
}

module "serverless" {
  source = "salte-io/serverless/aws"
  version = "1.0.0"

  api_id = "${aws_api_gateway_rest_api.api.id}"
  resource_id = "${aws_api_gateway_resource.resource.id}"
  http_method = "GET"
  function_name = "${aws_lambda_function.function.function_name}"
  invoke_arn = "${aws_lambda_function.function.invoke_arn}"
}

AWS SERVERless application model

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Description: 'SAM template for Serverless framework service: '
Resources:
  DynamoDB:
    Type: 'AWS::DynamoDB::Table'
    Properties:
      TableName: serverless-observability-dev
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 1
        WriteCapacityUnits: 1
      StreamSpecification:
        StreamViewType: NEW_AND_OLD_IMAGES
  ServiceA:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: functions/service-a.handler
      Runtime: nodejs8.10
      CodeUri: >-
        /home/vgaltes/Study/Serverless/serverless-observabiilty/.serverless/serverless-observability.zip
      MemorySize: 128
      Timeout: 10
      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'dynamodb:PutItem'
                - 'dynamodb:GetItem'
                - 'dynamodb:DescribeStream'
                - 'dynamodb:GetRecords'
                - 'dynamodb:GetShardIterator'
                - 'dynamodb:ListStreams'
              Resource:
                - >-
                  arn:aws:dynamodb:us-east-1:*:table/serverless-observability-dev
            - Effect: Allow
              Action:
                - 's3:GetObject*'
                - 's3:PutObject*'
                - 's3:ListBucket*'
                - 's3:PutBucketNotification'
              Resource:
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}/*
            - Effect: Allow
              Action:
                - 'sns:Publish'
                - 'sns:Subscribe'
              Resource:
                - 'arn:aws:sns:us-east-1:*:serverless-observability-dev'
            - Effect: Allow
              Action:
                - 'lambda:InvokeFunction'
              Resource:
                - >-
                  arn:aws:lambda:us-east-1:*:function:serverless-observability-dev-*
      Environment:
        Variables:
          stage: dev
          service: serverless-observability
          accountId:
            Ref: 'AWS::AccountId'
          BUCKET_NAME: 'serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}'
      Events:
        Event1:
          Type: Api
          Properties:
            Path: /demo/service-a
            Method: get
            RestApiId:
              Ref: ServerlessObservability
  ServerlessObservability:
    Type: 'AWS::Serverless::Api'
    Properties:
      StageName: dev
      DefinitionBody:
        swagger: '2.0'
        info:
          title:
            Ref: 'AWS::StackName'
        paths:
          /demo/service-a:
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  'Fn::Sub': >-
                    arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ServiceA.Arn}/invocations
              responses: {}
          /demo/service-b:
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  'Fn::Sub': >-
                    arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ServiceB.Arn}/invocations
              responses: {}
          /demo/timeout:
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  'Fn::Sub': >-
                    arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Timeout.Arn}/invocations
              responses: {}
          /demo/error:
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  'Fn::Sub': >-
                    arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Error.Arn}/invocations
              responses: {}
  ServiceALambdaPermission:
    Type: 'AWS::Lambda::Permission'
    DependsOn:
      - ServiceA
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName:
        Ref: ServiceA
      Principal: apigateway.amazonaws.com
  ServiceB:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: functions/service-b.handler
      Runtime: nodejs8.10
      CodeUri: >-
        /home/vgaltes/Study/Serverless/serverless-observabiilty/.serverless/serverless-observability.zip
      MemorySize: 128
      Timeout: 3
      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'dynamodb:PutItem'
                - 'dynamodb:GetItem'
                - 'dynamodb:DescribeStream'
                - 'dynamodb:GetRecords'
                - 'dynamodb:GetShardIterator'
                - 'dynamodb:ListStreams'
              Resource:
                - >-
                  arn:aws:dynamodb:us-east-1:*:table/serverless-observability-dev
            - Effect: Allow
              Action:
                - 's3:GetObject*'
                - 's3:PutObject*'
                - 's3:ListBucket*'
                - 's3:PutBucketNotification'
              Resource:
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}/*
            - Effect: Allow
              Action:
                - 'sns:Publish'
                - 'sns:Subscribe'
              Resource:
                - 'arn:aws:sns:us-east-1:*:serverless-observability-dev'
            - Effect: Allow
              Action:
                - 'lambda:InvokeFunction'
              Resource:
                - >-
                  arn:aws:lambda:us-east-1:*:function:serverless-observability-dev-*
      Environment:
        Variables:
          stage: dev
          service: serverless-observability
          accountId:
            Ref: 'AWS::AccountId'
      Events:
        Event1:
          Type: Api
          Properties:
            Path: /demo/service-b
            Method: get
            RestApiId:
              Ref: ServerlessObservability
  ServiceBLambdaPermission:
    Type: 'AWS::Lambda::Permission'
    DependsOn:
      - ServiceB
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName:
        Ref: ServiceB
      Principal: apigateway.amazonaws.com
  ServiceC:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: functions/service-c.handler
      Runtime: nodejs8.10
      CodeUri: >-
        /home/vgaltes/Study/Serverless/serverless-observabiilty/.serverless/serverless-observability.zip
      MemorySize: 128
      Timeout: 3
      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'dynamodb:PutItem'
                - 'dynamodb:GetItem'
                - 'dynamodb:DescribeStream'
                - 'dynamodb:GetRecords'
                - 'dynamodb:GetShardIterator'
                - 'dynamodb:ListStreams'
              Resource:
                - >-
                  arn:aws:dynamodb:us-east-1:*:table/serverless-observability-dev
            - Effect: Allow
              Action:
                - 's3:GetObject*'
                - 's3:PutObject*'
                - 's3:ListBucket*'
                - 's3:PutBucketNotification'
              Resource:
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}/*
            - Effect: Allow
              Action:
                - 'sns:Publish'
                - 'sns:Subscribe'
              Resource:
                - 'arn:aws:sns:us-east-1:*:serverless-observability-dev'
            - Effect: Allow
              Action:
                - 'lambda:InvokeFunction'
              Resource:
                - >-
                  arn:aws:lambda:us-east-1:*:function:serverless-observability-dev-*
      Environment:
        Variables:
          stage: dev
          service: serverless-observability
          accountId:
            Ref: 'AWS::AccountId'
  ServiceReadSns:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: functions/service-read-sns.handler
      Runtime: nodejs8.10
      CodeUri: >-
        /home/vgaltes/Study/Serverless/serverless-observabiilty/.serverless/serverless-observability.zip
      MemorySize: 128
      Timeout: 3
      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'dynamodb:PutItem'
                - 'dynamodb:GetItem'
                - 'dynamodb:DescribeStream'
                - 'dynamodb:GetRecords'
                - 'dynamodb:GetShardIterator'
                - 'dynamodb:ListStreams'
              Resource:
                - >-
                  arn:aws:dynamodb:us-east-1:*:table/serverless-observability-dev
            - Effect: Allow
              Action:
                - 's3:GetObject*'
                - 's3:PutObject*'
                - 's3:ListBucket*'
                - 's3:PutBucketNotification'
              Resource:
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}/*
            - Effect: Allow
              Action:
                - 'sns:Publish'
                - 'sns:Subscribe'
              Resource:
                - 'arn:aws:sns:us-east-1:*:serverless-observability-dev'
            - Effect: Allow
              Action:
                - 'lambda:InvokeFunction'
              Resource:
                - >-
                  arn:aws:lambda:us-east-1:*:function:serverless-observability-dev-*
      Environment:
        Variables:
          stage: dev
          service: serverless-observability
          accountId:
            Ref: 'AWS::AccountId'
      Events:
        Event1:
          Type: SNS
          Properties:
            Topic: ''
  ServerlessObservabilitySnsTopicQVJUw:
    Type: 'AWS::SNS::Topic'
    Properties:
      TopicName:
        topicName: serverless-observability-dev
        displayName: serverless-observability-dev
      DisplayName: serverless-observability-dev
  ServiceReadDynamo:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: functions/service-read-dynamo.handler
      Runtime: nodejs8.10
      CodeUri: >-
        /home/vgaltes/Study/Serverless/serverless-observabiilty/.serverless/serverless-observability.zip
      MemorySize: 128
      Timeout: 3
      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'dynamodb:PutItem'
                - 'dynamodb:GetItem'
                - 'dynamodb:DescribeStream'
                - 'dynamodb:GetRecords'
                - 'dynamodb:GetShardIterator'
                - 'dynamodb:ListStreams'
              Resource:
                - >-
                  arn:aws:dynamodb:us-east-1:*:table/serverless-observability-dev
            - Effect: Allow
              Action:
                - 's3:GetObject*'
                - 's3:PutObject*'
                - 's3:ListBucket*'
                - 's3:PutBucketNotification'
              Resource:
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}/*
            - Effect: Allow
              Action:
                - 'sns:Publish'
                - 'sns:Subscribe'
              Resource:
                - 'arn:aws:sns:us-east-1:*:serverless-observability-dev'
            - Effect: Allow
              Action:
                - 'lambda:InvokeFunction'
              Resource:
                - >-
                  arn:aws:lambda:us-east-1:*:function:serverless-observability-dev-*
      Environment:
        Variables:
          stage: dev
          service: serverless-observability
          accountId:
            Ref: 'AWS::AccountId'
      Events:
        Event1:
          Type: Dynamodb
          Properties:
            Stream:
              'Fn::GetAtt':
                - DynamoDB
                - StreamArn
  ServiceReadS3:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: functions/service-read-s3.handler
      Runtime: nodejs8.10
      CodeUri: >-
        /home/vgaltes/Study/Serverless/serverless-observabiilty/.serverless/serverless-observability.zip
      MemorySize: 128
      Timeout: 3
      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'dynamodb:PutItem'
                - 'dynamodb:GetItem'
                - 'dynamodb:DescribeStream'
                - 'dynamodb:GetRecords'
                - 'dynamodb:GetShardIterator'
                - 'dynamodb:ListStreams'
              Resource:
                - >-
                  arn:aws:dynamodb:us-east-1:*:table/serverless-observability-dev
            - Effect: Allow
              Action:
                - 's3:GetObject*'
                - 's3:PutObject*'
                - 's3:ListBucket*'
                - 's3:PutBucketNotification'
              Resource:
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}/*
            - Effect: Allow
              Action:
                - 'sns:Publish'
                - 'sns:Subscribe'
              Resource:
                - 'arn:aws:sns:us-east-1:*:serverless-observability-dev'
            - Effect: Allow
              Action:
                - 'lambda:InvokeFunction'
              Resource:
                - >-
                  arn:aws:lambda:us-east-1:*:function:serverless-observability-dev-*
      Environment:
        Variables:
          stage: dev
          service: serverless-observability
          accountId:
            Ref: 'AWS::AccountId'
      Events:
        Event1:
          Type: S3
          Properties:
            Bucket: 'serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}'
            Events: '*'
  Timeout:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: functions/timeout.handler
      Runtime: nodejs8.10
      CodeUri: >-
        /home/vgaltes/Study/Serverless/serverless-observabiilty/.serverless/serverless-observability.zip
      MemorySize: 128
      Timeout: 1
      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'dynamodb:PutItem'
                - 'dynamodb:GetItem'
                - 'dynamodb:DescribeStream'
                - 'dynamodb:GetRecords'
                - 'dynamodb:GetShardIterator'
                - 'dynamodb:ListStreams'
              Resource:
                - >-
                  arn:aws:dynamodb:us-east-1:*:table/serverless-observability-dev
            - Effect: Allow
              Action:
                - 's3:GetObject*'
                - 's3:PutObject*'
                - 's3:ListBucket*'
                - 's3:PutBucketNotification'
              Resource:
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}/*
            - Effect: Allow
              Action:
                - 'sns:Publish'
                - 'sns:Subscribe'
              Resource:
                - 'arn:aws:sns:us-east-1:*:serverless-observability-dev'
            - Effect: Allow
              Action:
                - 'lambda:InvokeFunction'
              Resource:
                - >-
                  arn:aws:lambda:us-east-1:*:function:serverless-observability-dev-*
      Environment:
        Variables:
          stage: dev
          service: serverless-observability
          accountId:
            Ref: 'AWS::AccountId'
      Events:
        Event1:
          Type: Api
          Properties:
            Path: /demo/timeout
            Method: get
            RestApiId:
              Ref: ServerlessObservability
  TimeoutLambdaPermission:
    Type: 'AWS::Lambda::Permission'
    DependsOn:
      - Timeout
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName:
        Ref: Timeout
      Principal: apigateway.amazonaws.com
  Error:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: functions/error.handler
      Runtime: nodejs8.10
      CodeUri: >-
        /home/vgaltes/Study/Serverless/serverless-observabiilty/.serverless/serverless-observability.zip
      MemorySize: 128
      Timeout: 3
      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'dynamodb:PutItem'
                - 'dynamodb:GetItem'
                - 'dynamodb:DescribeStream'
                - 'dynamodb:GetRecords'
                - 'dynamodb:GetShardIterator'
                - 'dynamodb:ListStreams'
              Resource:
                - >-
                  arn:aws:dynamodb:us-east-1:*:table/serverless-observability-dev
            - Effect: Allow
              Action:
                - 's3:GetObject*'
                - 's3:PutObject*'
                - 's3:ListBucket*'
                - 's3:PutBucketNotification'
              Resource:
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}/*
            - Effect: Allow
              Action:
                - 'sns:Publish'
                - 'sns:Subscribe'
              Resource:
                - 'arn:aws:sns:us-east-1:*:serverless-observability-dev'
            - Effect: Allow
              Action:
                - 'lambda:InvokeFunction'
              Resource:
                - >-
                  arn:aws:lambda:us-east-1:*:function:serverless-observability-dev-*
      Environment:
        Variables:
          stage: dev
          service: serverless-observability
          accountId:
            Ref: 'AWS::AccountId'
      Events:
        Event1:
          Type: Api
          Properties:
            Path: /demo/error
            Method: get
            RestApiId:
              Ref: ServerlessObservability
  ErrorLambdaPermission:
    Type: 'AWS::Lambda::Permission'
    DependsOn:
      - Error
    Properties:
      Action: 'lambda:InvokeFunction'
      FunctionName:
        Ref: Error
      Principal: apigateway.amazonaws.com
  Sns:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: functions/sns.handler
      Runtime: nodejs8.10
      CodeUri: >-
        /home/vgaltes/Study/Serverless/serverless-observabiilty/.serverless/serverless-observability.zip
      MemorySize: 128
      Timeout: 3
      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'dynamodb:PutItem'
                - 'dynamodb:GetItem'
                - 'dynamodb:DescribeStream'
                - 'dynamodb:GetRecords'
                - 'dynamodb:GetShardIterator'
                - 'dynamodb:ListStreams'
              Resource:
                - >-
                  arn:aws:dynamodb:us-east-1:*:table/serverless-observability-dev
            - Effect: Allow
              Action:
                - 's3:GetObject*'
                - 's3:PutObject*'
                - 's3:ListBucket*'
                - 's3:PutBucketNotification'
              Resource:
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}
                - >-
                  arn:aws:s3:::serverless-observability-dev-#{AWS::Region}-#{AWS::AccountId}/*
            - Effect: Allow
              Action:
                - 'sns:Publish'
                - 'sns:Subscribe'
              Resource:
                - 'arn:aws:sns:us-east-1:*:serverless-observability-dev'
            - Effect: Allow
              Action:
                - 'lambda:InvokeFunction'
              Resource:
                - >-
                  arn:aws:lambda:us-east-1:*:function:serverless-observability-dev-*
      Environment:
        Variables:
          stage: dev
          service: serverless-observability
          accountId:
            Ref: 'AWS::AccountId'
      Events:
        Event1:
          Type: SNS
          Properties:
            Topic: ''
  ServerlessObservabilitySnsTopicOJtJm:
    Type: 'AWS::SNS::Topic'
    Properties:
      TopicName:
        topicName: serverless-observability-dev
        displayName: serverless-observability-dev
      DisplayName: serverless-observability-dev

SERVERLESS FRAMEWORK

service: serverless-observability

plugins:
  - serverless-pseudo-parameters
  - serverless-plugin-existing-s3

custom:
  bucketName: serverless-observability-${self:provider.stage}-#{AWS::Region}-#{AWS::AccountId}

provider:
  name: aws
  runtime: nodejs8.10
  stage: dev
  region: us-east-1
  versionFunctions: false
  environment:
    stage: ${self:provider.stage}
    service: ${self:service}
    accountId: 
      Ref: AWS::AccountId

  iamRoleStatements:
    - Effect: "Allow"
      Action:
        - "dynamodb:PutItem"
        - "dynamodb:GetItem"
        - "dynamodb:DescribeStream"
        - "dynamodb:GetRecords"
        - "dynamodb:GetShardIterator"
        - "dynamodb:ListStreams"
      Resource:
        - "arn:aws:dynamodb:${self:provider.region}:*:table/serverless-observability-${self:provider.stage}"
    - Effect: "Allow"
      Action:
        - "s3:GetObject*"
        - "s3:PutObject*"
        - "s3:ListBucket*"
        - "s3:PutBucketNotification"
      Resource:
        - "arn:aws:s3:::${self:custom.bucketName}"
        - "arn:aws:s3:::${self:custom.bucketName}/*"
    - Effect: "Allow"
      Action:
        - "sns:Publish"
        - "sns:Subscribe"
      Resource:
        - "arn:aws:sns:${self:provider.region}:*:serverless-observability-${self:provider.stage}"
    - Effect: "Allow"
      Action:
        - "lambda:InvokeFunction"
      Resource:
        - "arn:aws:lambda:${self:provider.region}:*:function:serverless-observability-${self:provider.stage}-*"

functions:
  service-a:
    handler: functions/service-a.handler
    timeout: 10
    events:
      - http:
          path: demo/service-a
          method: get
          request:
            parameters:
              querystrings:
                n: true
    environment:
      BUCKET_NAME: ${self:custom.bucketName}

  service-b:
    handler: functions/service-b.handler
    events:
      - http:
          path: demo/service-b
          method: get

  service-c:
    handler: functions/service-c.handler

  service-read-sns:
    handler: functions/service-read-sns.handler
    events:
      - sns:
          topicName: "serverless-observability-${self:provider.stage}"
          displayName: "serverless-observability-${self:provider.stage}"

  service-read-dynamo:
    handler: functions/service-read-dynamo.handler
    events:
      - stream:
          type: dynamodb
          arn:
            Fn::GetAtt:
              - DynamoDB
              - StreamArn

  service-read-s3:
    handler: functions/service-read-s3.handler
    events:
      - s3: 
          bucket: ${self:custom.bucketName}

  timeout:
    handler: functions/timeout.handler
    timeout: 1
    events:
      - http:
          path: demo/timeout
          method: get

  error:
    handler: functions/error.handler
    events:
      - http:
          path: demo/error
          method: get

  sns:
    handler: functions/sns.handler
    events:
      - sns:
          topicName: ${self:service}-${self:provider.stage}
          displayName: ${self:service}-${self:provider.stage}

# you can add CloudFormation resource templates here
resources:
  Resources:
    # S3Bucket:
    #   Type: AWS::S3::Bucket
    #   Properties:
    #     BucketName: ${self:custom.bucketName}

    DynamoDB:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: serverless-observability-${self:provider.stage}
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        StreamSpecification:
          StreamViewType: NEW_AND_OLD_IMAGES

PULUMI

// Copyright 2016-2018, Pulumi Corporation.  All rights reserved.

const cloud = require("@pulumi/cloud-aws");

// A bucket to store videos and thumbnails.
const bucket = new cloud.Bucket("bucket");
const bucketName = bucket.bucket.id;

// A task which runs a containerized FFMPEG job to extract a thumbnail image.
const ffmpegThumbnailTask = new cloud.Task("ffmpegThumbTask", {
    build: "./docker-ffmpeg-thumb",
    memoryReservation: 512,
});

// When a new video is uploaded, run the FFMPEG task on the video file.
// Use the time index specified in the filename (e.g. cat_00-01.mp4 uses timestamp 00:01)
bucket.onPut("onNewVideo", bucketArgs => {
    console.log(`*** New video: file ${bucketArgs.key} was uploaded at ${bucketArgs.eventTime}.`);
    const file = bucketArgs.key;
    
    const thumbnailFile = file.substring(0, file.indexOf('_')) + '.jpg';
    const framePos = file.substring(file.indexOf('_')+1, file.indexOf('.')).replace('-',':');

    ffmpegThumbnailTask.run({
        environment: {
            "S3_BUCKET":   bucketName.get(),
            "INPUT_VIDEO": file,
            "TIME_OFFSET": framePos,
            "OUTPUT_FILE": thumbnailFile,
        },
    }).then(() => {
        console.log(`Running thumbnailer task.`);
    });
}, { keySuffix: ".mp4" });

// When a new thumbnail is created, log a message.
bucket.onPut("onNewThumbnail", bucketArgs => {
    console.log(`*** New thumbnail: file ${bucketArgs.key} was saved at ${bucketArgs.eventTime}.`);
    return Promise.resolve();
}, { keySuffix: ".jpg" });

// Export the bucket name.
exports.bucketName = bucketName;

STACKERY

OTHER TOOLS

  • Claudia JS
  • Apex
  • Sparta
  • A million more...

AWS CLOUD DEVELOPMENT KIT

APP SYNC

SHARING RESOURCES

automate and enable low risk releases

TECHNICAL PRACTICES OF FEEDBACK

OBSERVABILITY

  • Monitoring
  • Alerting and visualization
  • Distributed systems tracing infrastructure
  • Log aggregation

CHALLENGES

  • No access to the underlying infrastructure
  • Timing
  • Concurrency
  • Platform limitations

WHAT AWS OFFERS

GENERAL PURPOSE SOLUTIONS

SERVERLESS SPECIFIC SOLUTIONS

TECHNICAL PRACTICES OF CONTINUAL LEARNING AND EXPERIMENTATION

chaos engineering

"Limited scope, continuous, disaster recovery"

CHAOS MONKEY & SERVERLESS

actionable failure modes

  • Improperly tuned timeouts
  • Missing error handling
  • Missing fallbacks for when a downstream service is unavailable

SECURITY

SUMMARY

TESTING
CONTINUOUS INTEGRATION
DEPLOYMENT PIPELINE
LOW-RISK RELEASES
OBSERVABILITY
CHAOS ENGINEERING

MORE INFO

Donostia 9 y 10 de Mayo

 

theserverelesscourse.com

 

info@vgaltes.com

THANK YOU!

Vicenç García Altés

@vgaltes

theserverlesscourse.com

info@vgaltes.com

Operational Serverles

By Vicenç Garcia

Operational Serverles

  • 719