Stacks on Stacks

The Serverless Ecosystem Blog by Stackery.

Posts on Cloud Infrastructure

Creating Your First AWS RDS Database with Stackery
Toby Fee

Toby Fee | September 10, 2018

Creating Your First AWS RDS Database with Stackery

Once you’ve built your first Lambda, you’ll need a datastore.

AWS does have an official instruction guide to help with this, but the official AWS instruction guide is extensive. You need to set up at least four resources before you can deploy your first RDS table, and by the time their 2,000-word guide is done you still haven’t even selected your DB format.

Thankfully there’s a much easier way: The deployment service Stackery, can get your RDS up and running and hooked up to Lambdas in just a few minutes.

Getting Started with RDS

AWS’s Relational Database Service (RDS) is a good choice if you’re familiar with SQL databases already or just prefer the reliable classic of tables over NoSQL.

At first glance, RDS looks like it’s basically just database hosting from Amazon, but in reality, it’s very different from their traditional server options hosted on EC2. When looking the tooling closely, it’s obvious RDS is as different from a traditional DB as a Lambda is different from a web server.

A few key differences:

  1. No OS management — While you can select the exact DB type and version (e.g., PostgreSQL, MySQL, MariaDB, Oracle, Microsoft SQL Server, Aurora) the operating system level is fully virtualized
  2. AWS’s usual restrictive access policies — Centrally, this is a positive thing: Amazon forces you to be in tight control of what services can access each other. As a result most beginners often end up stumped when, after trying to stand up their RDS, they want to connect to it from their laptop.
  3. Indescribably smooth uptime management — With the concept of an Availability Zone (AZ) you can create multiple instances that Amazon can automatically use for failover

(Seen here: two availability zones in action)

  1. And finally, backups and scaling are simpler — Because if virtualization doesn’t make scaling and backups easier, what the heck is it even for, right?

Deploy an RDS Table

Start by creating a new Stack in the Stackery UI and deleting the default nodes. We’ll add one node for our database and a second for a lambda to access DB data. You’ll need to set a DB name. Note that this is not the database name for DB requests, just the name it’ll be referred to in the Stackery UI. Set a root password here as well. Be sure to record the password somewhere for later use in this tutorial.

For the moment, our Lambda function will be fine with the default settings, but we’ll edit it later to retrieve records from the database.

This tutorial doesn’t cover populating the database in detail, since the most likely scenario is that you’ll have test data you’d like to populate. Let’s set up our Lambda to load records from the account DB.

To get access to edit your Lambda’s code: commit this stack in the Stackery UI (no need to deploy yet), then click the commit ID to go to the stack code in Github.

Next, clone the repository. Then you can edit it in whatever editor you prefer.

Let’s populate the Lambda with some code to access records from the DB:

const knex = require('knex');
cont dbInfo = {
  development: {
    client: 'mysql',
    connection: {
      host: 'localhost',
      user: 'root',
      password: 'mySecretPW',
      database: 'accounts'
    }
  },

  production: {
    client: 'mysql',
    connection: {
      host,
      user: 'root',
      password: process.env.DB_PASSWORD,
      database: 'accounts'
    }
  }
};

const connectionName = process.env.CONNECTION || 'development';
const connection = dbInfo[connectionName];

/**
 * Fetch list of accounts from database and respond with an array of account
 * names.
 */
module.exports = async message => {
  const client = knex(connection);

  try {
    const records = await client('accounts').select('name');
    return records.map(record => record.name);
  } finally {
    client.destroy();
  }
};

A few things to note about this code:

  • As is the default for new Lambda Resources in Stackery, this is written in Node 8, with support for finally{}.
  • This code relies on inline connection info, but you’ll more likely want to share that info between multiple functions — See how to share code modules between Javascript functions in a separate guide.
  • If you’ve written code to access a database from a web app, you might be surprised to discover we’re creating and destroying a client for every request. Since we’re deploying this code to a serverless environment, it’s critical that we don’t max out our connections on smaller RDS instances. It’s quite easy for multiple requests to our Lambda to result in a point of 65 simultaneous connections, which is the limit on a db.t2.micro instance — We’ve previously discussed database connection usage in a serverless context on our blog.

Push the new Lambda code up to the GitHub repository, and refresh the Stackery UI to show your updates.

The last step is to give the Lambda function the database password. It expects to find it as a config variable for this Lambda — which you can set from the Stackery UI — but this isn’t an ideal place to put the password, since it will be visible in both the Stackery UI and the GitHub repo for this stack.

Instead, set two environment variables CONNECTION to production and DB_PASSWORD to ${config.dbPassword}, so the password will be populated from the environment config.

You can access ‘environments’ from the menu at the top right. Finally, set any environment variables as standard Javascript Object Notation (JSON).

Now your stack should be ready to commit, prepare, and deploy!

AWS Serverless Application Model YAML Templates: An Introduction
Toby Fee

Toby Fee | August 21, 2018

AWS Serverless Application Model YAML Templates: An Introduction

If you’ve never worked with YAML before, you’ll probably find the basic template format daunting. YAML — which somehow doesn’t stand for Yet Another Markdown Language, but instead YAML Ain’t Markdown Language — is a simplified way of storing data structures. YAML is popular with Rails users and sees some popularity with all higher order language programmers (JS, Python, etc…).

Clean, Simple Formatting

YAML is popular with languages that tend to see only three types of data:

  • Scalars: numbers and strings, the ‘text’ of your data
  • Heaps: e.g. objects, dictionaries, or any other term for a mapping of key-value pairs
  • Lists: i.e. arrays or an ordered sequence of the other two data types

YAML primarily tries to produce configuration documents that are readable without any special interpretation and formatting. If you look at even a short snippet of YAML from the middle of a SAM configuration file the key-value pairs are pretty readable, and you can even guess how lists might be used:

   DynamoDBTable:
     Type: AWS::DynamoDB::Table
     Properties:
       AttributeDefinitions:
         - AttributeName: id
           AttributeType: S

Indentation is used for structure, colons separate key-value pairs, and dashes are used to create “bullet” lists.

This simple, clean formatting also brings us to the most common complaint about YAML, it is whitespace-dependent, and missing a single tab can mess up the interpretation of the whole document.

YAML Basics

Always remember that YAML Lint is your friend. I still use yamllint.com every time I write YAML, turning on a display for invisible characters and purpose built linters for your IDE are all well and good, but that final check of YAML is still crucial.

YAML vs. XML

If you’re familiar with XML you may well have already interacted with systems that use either YAML or XML for configuration, or noticed that they are used by competing data storage systems. YAML is instantly readable, XML is not. On the other hand, the X in XML stands for “extensible” and while YAML really only has three ways to store data, the markup available in XML is limitless.

YAML vs. JSON

JavaScript Object Notation (JSON) and YAML look more similar and share design goals. While JSON is designed to be a bit easier to compile programmatically, the biggest difference for everyday use is that YAML has a method for referencing other items in the same JSON file. This might seem pretty minor but when standing up multiple API pathways in a SAM file, the ability to say TABLE_NAME: !Ref Table is a huge convenience.

The official specifications for YAML are well written and have some general guidance on best practices, implementation differences, and the design goals of YAML.

AWS Serverless Application Model

AWS CloudFormation templates are a standardized specification for describing, documenting, and deploying components of a serverless application Let’s look at one of the shortest possible SAM files:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Resources:
    MyFirstFunction:
        Type: AWS::Serverless::Function
        Properties:
           Handler: index.handler
           Runtime: nodejs4.3
           CodeUri: s3://bucketName/codepackage.zip

With the Handler property set to index.handler, the code package at the CodeUri will be opened, CloudFormation will look for a file (“index”) and a function or module (“handler”). Both the AWSTemplateFormatVersion and Transform should be the same for all your Sam files, and the Type and Runtime properties are self-explanatory.

Events

While the template above will create a Lambda, it’ll be fundamentally incomplete since it needs something to trigger a Lambda in order to run. The Events property defines these triggers.

In general you’d use SAM files when you want to define multiple interlinked pieces of your application. This example (leaving off the top three lines that will be the same in every SAM file) grabs events from a DynamoDB table:

  TableAlerter:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs6.10
      Events:
        Stream:
          Type: DynamoDB
          Properties:
            Stream: !GetAtt DynamoDBTable.StreamArn
            BatchSize: 100
            StartingPosition: TRIM_HORIZON  

Bringing it All Together

Most of the time SAM is the best choice because SAM can stand up an interlinked set of resources. So our SAM file will have more than one key under Resources.

Let’s stand up a table for the lambda above to read from:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  TableAlerter:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs6.10
      Events:
        Stream:
          Type: DynamoDB
          Properties:
            Stream: !GetAtt DynamoDBTable.StreamArn
            BatchSize: 100
            StartingPosition: TRIM_HORIZON   

  Alerts:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits: 5
        WriteCapacityUnits: 5

At this point it might be helpful to use anchors from the YAML specification to share config information or try the AWS SAM system for creating and sharing environment variables.

The official AWS documentation on SAM isn’t particularly instructive, with just a few examples and some tutorial references. However the full specification is laid out in the AWSLabs GitHub project documentation.

Once you’ve mastered the basics, or if you’re feeling overwhelmed by the tool, you may want to use a service to create and deploy your stack via CloudFormation. Stackery does just that, and recently announced that Stackery now builds SAM templates natively.

Serverless for Total Beginners
Anna Spysz

Anna Spysz | August 16, 2018

Serverless for Total Beginners

As the newest member of the Stackery Engineering team and Stackery’s Resident N00b™, I have been wanting to explain what serverless is in the most beginner-friendly terms possible. This is my attempt to do so.

I recently graduated a full-stack coding bootcamp, where I learned several ways to build and deploy a traditional (i.e. monolithic) web application, how to use containers to deploy an app, but nothing about serverless architecture. It wasn’t until I started my internship at Stackery that I even began to grasp what serverless is, and I’m still learning ten new things about it every day. While the concept of serverless functions and FaaS may seem daunting to new developers, I’ve found that it’s actually a great thing for beginners to learn; if done right, it can make the process of deployment a lot easier.

Above all, serverless is a new way of thinking about building applications. What’s exciting to me as a frontend-leaning developer is that it allows for most of the heavy lifting of your app to take place in the frontend, while cloud services handle typically backend aspects such as logging in users or writing values to a database. That means writing less code up front, and allows beginners to build powerful apps faster than the traditional monolith route.

So let’s dive in with some definitions.

What is a stack, and why are we stacking things?

A stack is essentially a collection of separate computing resources that work together as a unit to accomplish a specific task. In some applications, they can make up the entire backend of an app.

Stackery dashboard

The above example is about as simple as you can get with a stack. It consists of a function and an object store. When triggered, the function manipulates the data stored in the object store (in this case, an S3 bucket on AWS).

A simple use case would be a function that returns a specific image from the bucket when triggered - say, when a user logs into an app, their profile picture could be retrieved from the object store.

Here’s a somewhat more complex stack:

Stackery dashboard

This stack consists of a function (SignupHandler) that is triggered when someone submits an email address on a website’s newsletter signup form (Newsletter Signup API). The function takes the contents of that signup form, in this case a name and email address, and stores it in a table called Signup. It also has an error logger (another function called LogErrors), which records what happened should anything go wrong. If this stack were to be expanded, another function could email the contents of the Signup table to a user when requested, for example.

Under the hood, this stack is using several AWS services: Lambda for the functions, API Gateway for the API, and DynamoDB for the table.

Finally, here is a stack handling CRUD operations in a web application:

Stackery dashboard

While this looks like a complex operation, it’s actually just the GET, PUT, POST, and DELETE methods connected to a table of users. Each of the functions is handling just one operation, depending on which API endpoint is triggered, and then the results of that function are stored in a table.

This kind of CRUD stack would be very useful in a web application that requires users to sign up and sign in to use. When a user signs up, the POST API triggers the createUser function, which simply pulls up the correct DynamoDB table and writes the values sent (typically username and password) to the table. The next time the user comes back to the app and wants to log in, the getUser function is called by the GET API. Should the user change their mind and want to delete their account, the deleteUser function handles that through the DELETE API.

Are microservices == or != serverless?

There is a lot of overlap between the concepts of microservices and serverless: both consist of small applications that do very specific things, usually as a part of a larger application. The main difference is how they are managed.

A complex web application - a storefront, for example - may consist of several microservices doing individual tasks, such as logging in users, handling a virtual shopping cart, and processing payments. In a microservice architecture, those individual apps still operate within a larger, managed application with operational overhead - usually a devOps team making it all work smoothly together.

With serverless, the operational overhead is largely taken care of by the serverless platform where your code lives. In the case of a function on AWS Lambda, just about everything but the actual code writing is handled by the platform, from launching an instance of an operating system to run the code in your function when it is triggered by an event, to then killing that OS or container when it is no longer needed.

Depending on the demand of your application, serverless can make it cheaper and easier to deploy and run, and is generally faster to get up and running than a group of microservices.

Are monoliths bad?

To understand serverless, it’s helpful to understand what came before: the so-called “monolith” application. A monolith application has a complex backend that lives on a server (or more likely, many servers), either at the company running the application or in the cloud, and is always running, regardless of demand - which can make it expensive to maintain.

The monolith is still the dominant form of application, and certainly has its strengths. But as I learned when trying to deploy my first monolith app in school, it can be quite difficult for beginners to deploy successfully, and is often overkill if you’re trying to deploy and test a simple application.

So serverless uses servers?

Stackery dashboard

Yes, there are still servers behind serverless functions, just as “the cloud” consists of a lot of individual servers.

After all, as the mug says, “There is no cloud, it’s just someone else’s computer”.

That’s true for serverless as well. We could just as well say, “There is no serverless, it’s just someone else’s problem.”

What I find great about serverless is that it gives developers, and especially beginning developers, the ability to build and deploy applications with less code, which means less of an overall learning curve. And for this (often frustrated) beginner, that’s quite the selling point.

Simple Authentication with AWS Cognito
Matthew Bradburn

Matthew Bradburn | August 07, 2018

Simple Authentication with AWS Cognito

I was recently doing some work related to AWS Cognito, which I wasn’t previously familiar with, and it turns out to be pretty interesting. Stackery has a cloud-based app for building and deploying serverless applications, and we use Cognito for our own authentication.

The thing I was trying to do was hard to figure out but easy once I figured it out, so I’ll include some code snippets related to my specific use case. I’m assuming this is only interesting for people who are doing something similar, so it’s partly a description of what we do and partly a HOW-TO guide for those who want to do similar things.

Cognito is Amazon’s cloud solution for authentication – if you’re building an app that has users with passwords, you can depend on AWS to handle the tricky high-risk security stuff related to storing login credentials instead of doing it yourself. Pricing is based on your number of monthly active users, and the first 50k users are free. For apps I’ve worked on, we would have been very pleased to grow out of the free tier. It can also do social login, such as “log in with Facebook” and so forth.

Part of the problem I had getting started with Cognito is the number of different architectures and authentication flows that can be implemented. You can use it from a smartphone app or a web app, and you may want to talk to Cognito from the front end as well as the back end. And then security-related APIs tend to be complicated in general.

In our case, we wanted to create user accounts from a back-end NodeJS server and we needed to do sign-in from a mostly-static website. Ordinarily you’d do sign-in from some more structured javascript environment like React. It turns out not to be tricky, but the problem with not using React is that a lot of examples aren’t applicable.

Account Creation

We create user accounts programmatically from our API server, which talks to Cognito as an administrator. We also create a user record in our own database for the user at that time, so we want to control that process. As I implied above, we don’t store user credentials ourselves. Our Cognito user pool is configured such that only admins can create users – the users do not sign themselves up directly.

Setting up the Cognito User Pool is easy once you know what to do. The Cognito defaults are good for what we’re doing; although we disable user sign-ups and set “Only allow administrators to create users”. We have a single app client, although you could have more. When we create the app client, We do not ask Cognito to generate a client secret – since we do login from a web page, there isn’t a good way to keep secrets of this type. We set “Enable sign-in API for server-based authentication”, named ADMIN_NO_SRP_AUTH. (“SRP” here stands for “Secure Remote Password”, which is a protocol in which a user can be authenticated by a remote server without sending their password over the network. It would be vital for doing authentication over an insecure network, but we don’t need it.)

Assuming you’re creating your own similar setup, you’ll need to note your User Pool ID and App Client ID, which are used for every kind of subsequent operation.

Cognito also makes a public key available that is used later to verify that the client has successfully authenticated. Cognito uses RSA, which involves a public/private key pair. The private key is used to sign a content payload, which is given to the client (it’s a JWT, JSON Web Token), and the client gives that JWT to the server in the header of its authenticated requests. Our API server uses the public key to verify that the JWT was signed with the private key.

There are actually multiple public keys involved for whatever reason, but they’re available from Cognito as a JWKS (“JSON Web Key Set”). To retrieve them you have to substitute your region and user pool ID and send a GET to this endpoint:

(https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json)

To get a user account created from the website, we send an unauthenticated POST to our API server’s /accounts endpoint, where the request includes the user’s particulars (name and email address) and plaintext password – so this connection to the API server must obviously be over HTTPS. Our API server creates a user record in our database and uses the key as our own user ID. Then we use the Cognito admin API to create the user.

const AWS = require('aws-sdk');
const cognito = new AWS.CognitoIdentityServiceProvider();

// userId - our user record index key
// email - the new user's email address
// password - the new user's password
function createCognitoUser(userId, email, password) {
  let params = {
    UserPoolId: USER_POOL_ID, // From Cognito dashboard "Pool Id"
    Username: userId,
    MessageAction: 'SUPPRESS', // Do not send welcome email
    TemporaryPassword: password,
    UserAttributes: [
      {
        Name: 'email',
        Value: email
      },
      {
        // Don't verify email addresses
        Name: 'email_verified',
        Value: 'true'
      }
    ]
  };

  return cognito.adminCreateUser(params).promise()
    .then((data) => {
      // We created the user above, but the password is marked as temporary.
      // We need to set the password again. Initiate an auth challenge to get
      // started.
      let params = {
        AuthFlow: 'ADMIN_NO_SRP_AUTH',
        ClientId: USER_POOL_CLIENT_ID, // From Cognito dashboard, generated app client id
        UserPoolId: USER_POOL_ID,
        AuthParameters: {
          USERNAME: userId,
          PASSWORD: password
        }
      };
      return cognito.adminInitiateAuth(params).promise();
    })
    .then((data) => {
      // We now have a proper challenge, set the password permanently.
      let challengeResponseData = {
        USERNAME: userId,
        NEW_PASSWORD: password,
      };

      let params = {
        ChallengeName: 'NEW_PASSWORD_REQUIRED',
        ClientId: USER_POOL_CLIENT_ID,
        UserPoolId: USER_POOL_ID,
        ChallengeResponses: challengeResponseData,
        Session: data.Session
      };
      return cognito.adminRespondToAuthChallenge(params).promise();
    })
    .catch(console.error);
}

Of course the server needs admin access to the user pool, which can be arranged by putting AWS credentials in environment variables or in a profile accessible to the server.

Cognito wants users to have an initial password that they must change when they first log in. We didn’t want to do it that way, so during the server-side account creation process, while we have the user’s plaintext password, we do an authentication and set the user’s desired password as a permanent password at that time. Once that authentication completes, the user password is saved only in encrypted form in Cognito. The authentication process gives us a set of access and refresh tokens as a result, but we don’t need them for anything on the server side.

Client Authentication

When the users later want to authenticate themselves, they do that directly with Cognito from a login web form, which requires no interaction with our API server. Our web page includes the Cognito client SDK bundle. You can read about it on NPM, where there’s a download link:

amazon-cognito-identity

Our web page uses “Use Case 4” described on that page, in which we call Cognito’s authenticateUser() API to get a JWT access token. That JWT is sent to our API server with subsequent requests in the HTTP Authorization header.

Server Verification

The API server needs to verify that the client is actually authenticated, and it does this by decoding the JWT. It has the public key set that we downloaded as above, and we follow the verification process described here:

decode-verify-jwt

The link has a good explanation, so I won’t repeat that.

One of the items in the JWT payload is the username, which allows us to look up our own user record for the authenticated user. And that’s all there is to it. I hope this saves someone some time!

Custom CloudFormation Resources: Real Ultimate Power
Chase Douglas

Chase Douglas | May 24, 2018

Custom CloudFormation Resources: Real Ultimate Power

my ninja friend mark

Lately, I’ve found CloudFormation custom resources to be supremely helpful for many use cases. I actually wanted to write a post mimicing Real Ultimate Power:

Hi, this post is all about CloudFormation custom resources, REAL CUSTOM RESOURCES. This post is awesome. My name is Chase and I can’t stop thinking about custom resources. These things are cool; and by cool, I mean totally sweet.

Trust me, it would have been hilarious, but rather than spend a whole post on a meme that’s past its prime let’s take a look at the real reasons why custom resources are so powerful!

an awesome ninja

What Are Custom Resources?

Custom resources are virtual CloudFormation resources that can invoke AWS Lambda functions. Inside the Lambda function you have access to the properties of the custom resource (which can include information about other resources in the same CloudFormation stack by way of Ref and Fn::GetAtt functions). The function can then do anything in the world as long as it (or another resource it invokes) reports success or failure back to CloudFormation within one hour. In the response to CloudFormation, the custom resource can provide data that can be referenced from other resources within the same stack.

another awesome ninja

What Can I Do With Custom Resources?

Custom resources are such a fundamental resource that it isn’t obvious at first glance all the use cases it enables. Because it can be invoked once or on every deployment, it’s a powerful mechanism for lifecycle management of many resources. Here are a few examples:

You could even use custom resources to enable post-provisioning smoke/verification testing:

  1. A custom resource is “updated” as the last resource of a deployment (this is achieved by adding every other resource in the stack to its DependsOn property)
  2. The Lambda function backing the custom resource triggers smoke tests to run, then returns success or failure to CloudFormation
  3. If a failure occurs, CloudFormation automatically rolls back the deployment

Honestly, while I have begun using custom resources for many use cases, I discover new use cases all the time. I feel like I have hardly scratched the surface of what’s possible through custom resources.

And that’s what I call REAL Ultimate Power!!!!!!!!!!!!!!!!!!

more awesome ninja

Alexa, tell Stackery to deploy
Apurva Jantrania

Apurva Jantrania | May 01, 2018

Alexa, tell Stackery to deploy

We have a couple of Amazon Dot’s around the office and one day, Nate was wondering if we could use Alexa to deploy a stack. That sounded like a fun side project, although I’d never created an Alexa skill before. So this week, I’m going to write a bit about the proof-of-concept I made, and some of the learnings I came across.

To learn about Alexa skills, I used two guides:

  1. Steps to Build a Custom Skill to guide me through building the custom Alexa Skill
  2. Developing Your First Skill to understand how custom Skill Handlers are written in NodeJS

Creating the Alexa Skill

Designing and building the Alexa Skill following the first guide was surprisingly straight-forward. I decided I wanted to build my skill to enable deploying a stack into a specific environment. For the purpose of this POC, I decided that adding which branch to use for the deployment to start getting to be too long of an utterance/dialog. My ideal phrasing was to be able to say “Alexa, tell stackery to deploy $STACK_NAME into $ENVIRONMENT_NAME”.

The first issue I came across is the skill invocation name. I wanted to just use stackery but there is a very large dialog box that lists requirements, and at the top of that list is that the invocation name should be two or more words. That seemed increadibly unwieldy and I wasn’t sure what I’d go with. This requirement also seemed to go against some of the examples I’d seen in some of Amazon’s own guides: I decided that I really did want stackery as my invocation and I got lucky when I tried it - turns out that Amazon’s definition of requirement here is synonomous with guideline.

I then created a new intent that I called deployIntent and populated the sample utterences with a couple of phrases:

deploy
deploy {stackName}
deploy {stackName} to {env}

Where {stackName} and {env} are slots that I was able to dive into their Edit Dialog settings to tell Alexa that both slots are required and how to prompt for them if the user doesn’t provided it.

I got to say, the Alexa Skills UI/UX was really making this easy for me as a first time developer. It felt slick.

With this, I was pretty much done creating the skill, and now I needed to create the handler that would actually do the deployment.


Creating the Alexa Skill Handler

I created a new stack in Stackery called alexaDeployments. As an Alexa skill can directly invoke an AWS Lambda function, I deleted all of the existing resources and started with a fresh function which I called alexaHandler. I updated the timeout to be 300 seconds. Note that deployments can easily take more than 5 minutes. To really be robust, the stack deployment should be handled by Docker Task resource instead, but since this was just a POC, I was willing to accept this limitation to speed things up.

I then saved the stack in the UI and cloned the repo locally to start developing the handler. Following the second guide quickly gave me the skeleton of my alexaHandler lambda function. It’s a lot of relatively repetative code, thats well outlined in the guide, so I’m not going to add it here. What I needed to do now was code my DeployIntentHandler and add the stackery CLI to the function.

When Stackery packages a function to lambda, it includes everything in the function directory, so taking advantage of that, I downloaded the Linux varient of the Stackery CLI into the /Stackery/functions/alexaHanlder folder in my repo. The Stackery CLI requires a few steps to be able to deploy:

  • A .stackery.toml file that is created by running through the stackery login command
  • AWS credentials provided either via the command line (--access-key-id and --secret-access-key) or via a profile in the ~/.aws/credentials file

To make things easier, I took my .stackery.toml file and added that to the function folder so I could skip the stackery login step on each invocation. As for my AWS Credentials, I will get them from environment variables set via Stackery’s Environment Configurations.

With that, my DeployIntentHandler looked like this

const DeployIntentHandler = {
  canHandle (handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'DeployIntent';
  },
  handle (handlerInput) {
    console.log('DeployIntent Invoked');
    console.dir(handlerInput);

    const request = handlerInput.requestEnvelope.request;

    if (request.dialogState !== 'COMPLETED') {
      return {
        directives: [{"type": "Dialog.Delegate"}]
      };
    }

    const stackName = request.intent.slots.stackName.value;
    const env = request.intent.slots.env.value;

    let args = ['deploy', stackName, env, 'master',
                '--config', './.stackery.toml',
                '--access-key-id', process.env.accessKeyId,
                '--secret-access-key', process.env.secretAccessKey];

    return childProcess.execFile('./stackery', args)
      .then(result => {
        console.log(`stackery returned: stdout: ${result.stdout}`);
        console.log(`stackery returned: stderr: ${result.stderr}`);
      })
      .catch(error => {
        console.log(`ChildProcess errored with ${JSON.stringify(error)}`);
        if (error.stdout) {
          console.log(error.stdout);
          console.log(error.stderr);
        }
      })
      .then(() => {
        const speechText = `Starting deployment of ${stackName} into ${env}`;

        return handlerInput.responseBuilder
          .speak(speechText)
          .getResponse();
      })
  }
};

I commited my changes and deployed my alexaDeployments stack. Once deployed, I was able to go into the Deployed Stack Dashboard and click on the alexaHandler resource to get the Lambda ARN, which let me finish the last step in setting up my Alexa Skill - connecting the Alexa Skill to the Lambda function.

Function Permission Errors

However, when I tried to add the ARN of the Lambda function to the Alexa skill, I got an error The trigger setting for the Lambda arn:aws:lambda:us-west-2:<account>:function:<functionName> is invalid. Error code: SkillManifestError - Friday, Apr 27, 2018, 1:43 PM. Whoops, I forgot to give Alexa permission to access the lambda function. Stackery usually takes care of all the permissions needed, but since it didn’t know about the Alexa Skill, I was going to have to manually add the needed permission. Luckily, Stackery makes this easy with Custom CloudFormation Resources. I added a custom resource to my stack with the following CloudFormation:

{
  "Resources": {
    "alexaSkillPolicy": {
      "Type": "AWS::Lambda::Permission",
      "Properties": {
        "Action": "lambda:InvokeFunction",
        "FunctionName": "stackery-85928785027043-development-33181332-alexaHandler",
        "Principal": "alexa-appkit.amazon.com"
      }
    }
  }
}

This let’s alexa-appkit.amazon.com invoke my function. After re-deploying my stack with this change, I was able to finish linking my Alexa Skill to my handler, and it was time to test!

Timeouts and Retry Errors

Initial testing looked good - Alexa was able to run my skill, my handler was getting invoked, and I could see my test stack (a simple hello world stack) was getting re-deployed. However, when I looked into the CloudWatch logs for my alexaHandler function, I noticed that I was getting the errors printed from the Stackery CLI Failed to prepare deployment: \nStackery API responded with status code: 409\nYou probably already have a deployment in progress\n. With some inspection, I realized that since the handler took the time to actually deploy before responding to Alexa, Alexa was seemingly timing out and retrying in about 30 seconds. So this error was from the re-invocation of the Stackery CLI.

Ideally, I’d be able to provide intermittent status updates via Alexa, but unfortunately you are only allowed to respond once. To handle this issue, I refactored my alexaHandler function to asynchronously invoke another lambda function stackeryWrapper.

So now, my DeployIntentHandler looked like this:

const DeployIntentHandler = {
  canHandle (handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'DeployIntent';
  },
  handle (handlerInput) {
    console.log('DeployIntent Invoked');
    console.dir(handlerInput);

    const request = handlerInput.requestEnvelope.request;

    if (request.dialogState !== 'COMPLETED') {
      return {
        directives: [{ "type": "Dialog.Delegate" }]
      };
    }

    const stackName = request.intent.slots.stackName.value.replace(' ', '');
    const env = request.intent.slots.env.value.replace(' ', '');
    let message = { stackName, env };
    const Payload = JSON.stringify(message, null, 2);

    return lambda.invoke({
      FunctionName: stackeryWrapper.functionName,
      InvocationType: 'Event',
      Payload
    }).promise()
      .then(() => {
        const speechText = `Starting deployment of ${stackName} into ${env}!`;

        return handlerInput.responseBuilder
          .speak(speechText)
          .getResponse();
      })
  }
};

And my new stackeryWrapper function looks like this:

const childProcess = require('child-process-promise');

module.exports = async message => {
  console.dir(message);

  const stackName = message.stackName;
  const env = message.env;

  return childProcess.execFile('./stackery', ['deploy', stackName, env, 'master', '--config', './.stackery.toml', '--access-key-id', process.env.accessKeyId, '--secret-access-key', process.env.secretAccessKey])
    .then(result => {
      console.log(`stackery returned: stdout: ${result.stdout}`);
      console.log(`stackery returned: stderr: ${result.stderr}`);
    })
    .catch(error => {
      console.log(`ChildProcess errored with ${error}`);
      if (error.stdout) {
        console.log(error.stdout);
        console.log(error.stderr);
      }
    });
}

And my stack looks like this:


Final Thoughts

While this project is far from being useable by anyone else as it stands, I found it interesting and honestly exciting to be able to get Stackery deployment to work via Alexa. Ramping on Alexa was relatively painless, although Amazon does have some contradictory documentation that can confuse the waters. And with Stackery, it was painless to handle adding the CLI and the refactoring that I needed. There’s a lot that could still be done to this project such as authorization, authentication, status updates, etc, but that will have to wait for another day.

Fargate and Cucumber-js: A Review
Stephanie Baum

Stephanie Baum | April 16, 2018

Fargate and Cucumber-js: A Review

Lately, here at Stackery, as we’ve begun shipping features more rapidly into the product, we’ve also been shifting some of our focus towards reliability and integration testing in preparation. I decided to try out AWS Fargate for UI integration testing using BDD and Cucumber-js in a day long experimental POC. Cucumber is a behavior driven development testing framework with test cases written in a language called gherkin that focuses specifically on user features. AWS Fargate is a recently released abstraction on top of ECS services that gets rid of managing EC2 instances. These are my conclusions:

1. Fargate is awesome. Why would you not use Fargate?

If you’re configuring a Fargate task via the AWS UI it’s somewhat confusing and clumsy. With Stackery, you can configure Fargate while avoiding the pain of the AWS UI entirely. The communication between AWS Lambda to a Fargate task is the same as it would be for a normal ECS service, so moving existing ECS clusters/services to Fargate is straightforward application logic-wise. Here’s a simplified code snippet, dockerTaskPort refers to the conveniently provided Stackery Port environment variable. See our docs for the Docker Task node for more information.

  const repoName = `cross-region-us-east`;
  const browserCiRepo = `https://${token}@github.com/sbaum1994/${repoName}.git`;

  const dockerCommands = [
    `echo 'Running node index.js'`,
    `node index.js`
  ];

  const env = {
    ENV_VAR: 'value'
  };

  let dockerCommand = ['/bin/bash', '-c', dockerCommands.join('; ')];

  const params = {
    taskDefinition: dockerTaskPort.taskDefinitionId,
    overrides: {
      containerOverrides: [
        {
          name: '0'
        }
      ]
    },
    launchType: 'FARGATE'
  };

  params.networkConfiguration = {
    awsvpcConfiguration: {
      subnets: dockerTaskPort.vpcSubnets.split(','),
      assignPublicIp: (dockerTaskPort.assignPublicIPAddress ? 'ENABLED' : 'DISABLED')
    }
  };

  params.overrides.containerOverrides[0].command = dockerCommand;

  params.overrides.containerOverrides[0].environment = Object.keys(env).map((name) => {
    return {name, value: env[name]};
  });

  const ecs = new AWS.ECS({ region: process.env.AWS_REGION });
  return ecs.runTask(params)...

It’s a nice plus that there are no EC2 configurations to worry about, and it also simplified scaling. In the past we’ve had to use an ECS cluster and service for CI when the integration testing has been too long running for AWS lambda. Here, my Fargate service just scales up and down nicely without having to worry about configuration, bottlenecks or cost.

Here’s my UI integration testing set up, triggered by an endpoint that specifies the environment to test.

With Fargate there is still technically an ECS cluster that needs configuring on set up, and when using a load balancer and target group. You are still creating a task definition, containers, and a service. Stackery’s UI makes it easy to understand and configure, but if I were doing this on my own I’d still find it a PIA. Furthermore, I could see Fargate not being ideal in some use cases, since you can’t select the EC2 instance type.

Stackery UI setting up Fargate:

2. Cucumber is pretty cool too. BDD creates clear tests and transparent reporting.

I really like the abstraction Cucumber provides between the test definitions and underlying assertions/implementations. For this POC I created a simple “login.feature” file as follows:

Feature: Login
  In order to use Stackery
  As a single user
  I want to login to my Stackery account

  Background:
    Given I've navigated to the Stackery app in the browser
    And it has loaded successfully

  Scenario: Logging in as a user with a provider set up
    Given a test user account exists with a provider
    When I login with my username and password
    Then I'm taken to the "Stacks" page and see the text "Select a stack"
    And I see the "Stackery Stacks" section populated in the page
    And I see the "CloudFormation Stacks" section populated in the page

Each step maps to a function that uses Selenium Webdriver on headless chrome under the hood to run the tests. I also pass in configuration that lets the test know what the test account username and password is, what Stackery environment is being tested, and other definitions like the timeout settings. In my pipeline, I also added an S3 bucket to hold the latest Cucumber reporting results for visibility after a test finishes.

Report generated:

Overall, I think this can potentially be a great way to keep track of adding new features while maintaining existing ones / making sure everything is regressively tested on each merge. Furthermore it’s clear, organized and user flow oriented, which can work well for a dashboard style app like ours with multiple, repeatable, extensible steps (Create Environment, Deploy a Stack To Environment) etc.

Quickly Iterating on Developing and Debugging AWS Lambda Functions
Apurva Jantrania

Apurva Jantrania | March 15, 2018

Quickly Iterating on Developing and Debugging AWS Lambda Functions

Recently, I found myself having to develop a complex lambda function that required a lot of iteration and the need for interactive debugging. Iterating on lambda functions can be painful due to the amount of time it takes to re-deploy an update to lambda and trying to attach a debugger to lambda just isn’t an option. If you find yourself re-deploying more than a handful of times, the delay introduced by the redeployment processes can feel like watching paint dry. I thought I’d take this opportunity to share some of my strategies to alleviate the issues I’ve encountered developing and debugging both simple and complex lambda functions.

I find that it is always useful to log the event or input (depending on your language of choice) for any deployed lambda function - while you can mock this out (and should for unit tests!), I’ve found that having the full event has been critical for some debug cases. Even with AWS X-Ray enabled on your function, there isn’t enough information to usually recreate the full event structure. Depending on your codebase you may want to also log the context object, but in my experience, this is isn’t usually necessary.

Method 1: A quick and dirty method

With the event logged, it is straightforward to build a quick harness to run the failure instance locally in a way that is usually good enough.

Let’s look at an example in Python - if for example, our handler is handler() in my_lambda.py:

def handler(message):
    print('My Handler')
    print(message)
    # Do stuff

    # Error happens here
    raise Exception('Beep boop bop')

    return None

First, open your cloud watch logs for this lambda function (If you are using Stackery to manage your stack, you can find a direct link to your logs in the deployment panel) and capture the message that the function printed Cloud Watch Log

Then, we can create a simple wrapper file tester.py and import the handler inside. For expediency, I also just dump the event into a variable in this file.

import my_lambda

message = {
  'headers': {
    'accept': '...',
    'accept-language': '...',
    # ...
  }
}


my_lambda.handler(message)

With this, you can quickly iterate on the code in your handler with the message that caused your failure. Just run python tester.py.

There are a handful of caveat’s to keep in mind with this implementation:

  • ENV vars: If your function requires any ENV vars to be set, you’ll want to add those to the testing harness.
  • AWS SDK: If your lambda function invokes any AWS SDK’s, they will run with the credentials defined for the default user in ~/.aws/credentials which may cause permission issues
  • Dependencies: You’ll need to install any dependencies your function requires

But, with those caveats in mind, I find this usually is good enough and is the fastest way to replicate an error/iterate on lambda development.

Method 2: Using Docker

For the times you need to run in a sandboxed environment that is identical (or as close to as possible) as Lambda, I turn to using Docker with the images provided by LambCI.

When debugging/iterating, I find that my cycle time is sped up by using the build images versions of the LambCI images and running bash interactively. Eg, if my function is running on Python 2.7, I’ll use the lambci/lambda:build-python2.7 image. I prefer launching into bash rather than having Docker run my lambda function directly, because otherwise, any dependencies will need to be downloaded & installed each run, which can add significant latency to the run.

So in the above example, my command would be docker run -v /path/to/code:/test -it lambci/lambda:build-python2.7 bash. Then, once bash is loaded in the Docker Container, I first do the following:

  1. CD to the test directory: cd /test
  2. Install your dependencies
  3. Run test tester: python /test/tester.py

With this, since we are running docker run with the -v flag to mount the handler directory inside the container as a volume, any changes you make to your code will immediately affect your next run, enabling the same iteration speed as Method 1 above. You can also attach a debugger of your choice if needed.

While this method requires some setup of Docker and thus is a little more cumbersome to start up than Method 1, it will enable you to run locally in an environment identical to Lambda.

Get the Serverless Development Toolkit for Teams

Sign up now for a 60-day free trial. Contact one of our product experts to get started building amazing serverless applications today.

To Top