Deploying applications to ElasticBeanstalk with Terraform

Jack Mahoney
3 min readFeb 18, 2019

--

AWS offers a range of container hosting solutions such as ECS, EKS, and ElasticBeanstalk. For a small application, ElasticBeanstalk is a fast and simple way to get up and running.

Recently I started rewriting a project called MailSlurp. It’s a SAAS API that let’s you send and receive emails from random email addresses. It’s built in Kotlin and SpringBoot and deployed as a JAR to ElasticBeanstalk.

I wanted to automate my deployments and infrastructure so I decided to build a workflow with Terraform.

Here’s a guide (that assumes some knowledge of Terraform and AWS).

1) Building your app

In order to deploy an app to ElasticBeanstalk you need to build a deployable image. A number of formats are supported, including JARs and Docker images. I chose to build a fat JAR with gradle. This part was not handled by Terraform.

2) Publish your deployment image to S3

The next step is publish the deployment file to S3. This can be done with Terraform’s data.archive_file directive. It takes a file path and zips it for you. Point this to your image. If you are using Docker images, look into ECR instead.

# create a zip of your deployment with terraform
data "archive_file" "api_dist_zip" {
type = "zip"
source_file = "${path.root}/${var.api_dist}"
output_path = "${path.root}/${var.api_dist}.zip"
}

Next create an S3 bucket and a bucket object for the deployment in terraform. This is where your zip will be published when you run `terraform apply`. For the object key I use the terraform uuid() function. This returns a random unique string, meaning each deployment will be unique. This has a downside (each terraform apply will redeploy the app) but suited my needs.

resource "aws_s3_bucket" "dist_bucket" {
bucket = "${var.namespace}-elb-dist"
acl = "private"
}
resource "aws_s3_bucket_object" "dist_item" {
key = "${var.environment}/dist-${uuid()}"
bucket = "${aws_s3_bucket.dist_bucket.id}"
source = "${var.dist_zip}"
}

3) Create an ElasticBeanstalk application, environment and application version

Next we need to create an ElasticBeanstalk environment, application, and application version. I use a 3rd party CloudPosse module to make this easier. The application and the environment describe how and where our deployment will live. The application version is a version of the application that can run in a given environment. Notice the version also uses the `uuid()` function.

module "elastic_beanstalk_application" {
source = "git::https://github.com/cloudposse/terraform-aws-elastic-beanstalk-application.git?ref=master"
namespace = "${var.namespace}"
stage = "${var.environment}"
name = "${var.app}"
}
module "elastic_beanstalk_environment" {
source = "git::https://github.com/cloudposse/terraform-aws-elastic-beanstalk-environment.git?ref=master"
}
resource "aws_elastic_beanstalk_application_version" "default" {
name = "${var.namespace}-${var.environment}-${uuid()}"
application = "${module.elastic_beanstalk_application.app_name}"
description = "application version created by terraform"
bucket = "${aws_s3_bucket.dist_bucket.id}"
key = "${aws_s3_bucket_object.dist_item.id}"
}

Let’s output the environment name and application version name for later.

output "app_version" {
value = "${aws_elastic_beanstalk_application_version.default.name}"
}
output "env_name" {
value = "${module.elastic_beanstalk_environment.name}"
}

4) Deploy the version we created

Running `terraform apply`will create the ElasticBeanstalk environment, application and application version for our deployment file but it will not actually deploy the application. For this we need to use the aws cli to update the environment’s application version.

aws --region $REGION elasticbeanstalk update-environment --environment-name $(terraform output env_name) --version-label $(terraform output app_version)

Voila.

--

--