Deploying to AWS with runway & SSM
Tags: aws, cloud, terraform, serverless
Published: 2019-10-09 17:11:06

Deploying to AWS with the Serverless Framework is a snap. Deploying to AWS with Terraform is not quite as easy as with Serverless, but it's pretty straightforward. Deploying infrastructure that's composed of both Terraform & Serverless configurations? That's crazy talk. Bash scripts, branches, and CLI output parsing will be needed to manage that mess. Right? Not quite...

## Enter runway

[Runway](https://github.com/onicagroup/runway) is...

> A lightweight wrapper around linting (e.g. yamllint) & infrastructure deployment tools (e.g. CloudFormation, Terraform, Serverless) to ease management of per-environment configs & deployment.

In short, runway is good at making deployments consistent between the various Infrastructure-as-Code tools.

Runway is a layer of abstraction between your pipeline and configurations therein. Those configurations can be implemented in [Terraform](https://www.terraform.io), [Serverless](https://serverless.com), [Stacker](https://github.com/cloudtools/stacker) ([CloudFormation](https://aws.amazon.com/cloudformation/)), or [cdk](https://aws.amazon.com/cdk/). Runway knows how to inject variables into each framework, configure backends (in the case of terraform), specify stages/workspaces/environments, etc.

For the purposes of this post, I'm just going to focus on Terraform and Serverless.

## A simple example

This simple runway.yml file defines a deployment of a Serverless app and a Terraform configuration (in that order). The deployment will occur in us-east-1, and the dev and prod environments are enabled (but have no specific configuration associated with either).

```yaml
---
deployments:
  - modules:
      - myapp.sls                # directory w/ Serverless stack
      - infrastructure.terraform # directory w/ terraform configuration
    regions:
      - us-east-1
    environments:
      dev: true
      prod: true
```

To deploy this to prod, you could run DEPLOY_ENVIRONMENT=prod && runway deploy. You can also use Git branch names or folder names to specify the environment.

## A little more real-world

This slightly more complicated example shows how you can specify unique variables & values into each module.

```yaml
---
deployments:
  - modules:
      - path: myapp.sls
        environments:
          dev:
            api_gateway_custom_domain_name: dev.my-api.com
          prod:
            api_gateway_custom_domain_name: my-api.com
      - path: infrastructure.terraform
        environments:
          dev: 
            golden_image_ami: ami-zyx123456
          prod:
            golden_image_ami: ami-zyx654321
    regions:
      - us-east-1
```

Here I'm providing new values in the environments property for each module. These variables will be passed into the module deployments via syntax native to each deployment tool. This makes it very easy to build the multi-stack configuration in one place and maintaining native variable reference syntax within each configuration.

## SSM: cross-configuration glue

For values that needs to cross configurations, my team has made heavy use of [AWS Systems Manager Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html) parameters. We've found that this is the easiest construct (to use in AWS) that all of the infrastructure-as-code tools that we use can natively understand.

### Terraform

You can use an `aws_ssm_parameter` data source to import an SSM parameter set somewhere else.

```hcl
data "aws_ssm_parameter" "foo" {
  name = "/foo/${terraform.workspace}/value"
}
```

To create an SSM parameter, add an `aws_ssm_parameter` resource to the configuration.

```hcl
resource "aws_ssm_parameter" "foo" {
  name  = "/foo/${terraform.workspace}/lb-access-key-secret"
  type  = "SecureString"
  value = aws_iam_access_key.lb.secret
}
```

### Serverless

Referencing SSM parameters from a Serverless stack is a little more straightforward. You can use their native syntax to reference variables stored in SSM like this...

```
${ssm:/foo/${self:provider.stage}/value}
```

To create an SSM parameter, add a Cloudformation resource to the `Resources` section of the template.

```yaml
resources:
  Resources:
    foo:
      Type: AWS::SSM::Parameter
      Properties: 
        Name: "/foo/${self:provider.stage}/lb-access-key-secret"
        Type: SecureString
        Value: "${self:custom.secret_value}"
```

## Summary

Runway is a great option to use if:

- You want a common CLI to use for multiple Infrastructure-as-Code tools
- You have multiple configurations and want to deploy them in one go
- You'd like to standardize the deploy jobs of your CI pipeline

It has certainly helped the productivity of my team in many ways. I could talk about it more than I have, but I'd love to hear your feedback! What would you like to hear more about? What could use some more explanation?