Elastic Beanstalk on Amazon Linux 2
Run command or script after deployment

Aug 5, 2021

I have written about this or similar topics before in both Post-Deployment script on Elastic Beanstalk: Restart Delayed Job as well as Convenient way to run app commands in Elastic Beanstalk.

But at the time it applied to the first generation of Amazon Linux image. Nowadays I believe that image has been deprecated and new applications will use the Amazon Linux 2 image when deploying on Elastic Beanstalk and there are some breaking differences when it comes to configuring deployments.

New folder structure for hooks

The first thing you'll notice if you try any of the previous guides is that your hooks are not running. The reason is that /opt/elasticbeanstalk/hooks is no longer the correct place for hooks to live. In fact, that folder does not even exist anymore (unless created by your own ebextensions).

The good news is that hooks have now become a configurable platform extension (with proper documentation from AWS).

To summarise, you can commit shell scripts in your application soruce code under .platform/hooks/[hook-folder]/[shell-script] and they will execute during the deploy lifecycle.

To quote the documentation, the currently available hook folders are

  • prebuild – Files here run after the Elastic Beanstalk platform engine downloads and extracts the application source bundle, and before it sets up and configures the application and web server. The prebuild files run after running commands found in the commands section of any configuration file and before running Buildfile commands.

  • predeploy – Files here run after the Elastic Beanstalk platform engine sets up and configures the application and web server, and before it deploys them to their final runtime location. The predeploy files run after running commands found in the container_commands section of any configuration file and before running Procfile commands.

  • postdeploy – Files here run after the Elastic Beanstalk platform engine deploys the application and proxy server. This is the last deployment workflow step.

In my opinion, this is a big improvement and I feel confident that even if future platform upgrades will change folder locations in the filesystem, any properly configured platform extension should be adapted and work fine.

Environment Variables

The next thing you will likely run into is that even when your shell scripts are running, they don't have access to any environment variables you might have configured in your application.

This is not something new to Amazon Linux 2, the same was true for the previous Amazon Linux AMI. However, the previous one did have a pre-generated shell script in the support folder called envvars which basically was a prepared script with export statements for all the environment variables. In the new version, this file no longer exists.

Unlike the hooks scenario, I have not been able to find any official article from https://docs.aws.amazon.com/. I did however find an article from their Premium Support knowledge center.

How do I use environment variables from an Elastic Beanstalk instance shell?

So while not an actual platform feature, I still consider this to be an officially endorsed solution.

This solution includes adding an .ebextension to your application that looks like this.

commands:
    setvars:
        command: /opt/elasticbeanstalk/bin/get-config environment | jq -r 'to_entries | .[] | "export \(.key)=\"\(.value)\""' > /etc/profile.d/sh.local
packages:
    yum:
        jq: []

What happens here is that a command is run on deploy that first uses the get-config utlity to get a list of all the environment variables in JSON format.

It then pipes that output to the jq executable. This is a command-line package that can parse JSON. This particular command converts the JSON format into an export syntax for the shell.

Lastly the output from jq is written to a file in the folder /etc/profile.d/. If you are unfamiliar with this folder, just know that any script placed here is run when a new shell is started.

The end result is that our hooks can now have access to any environment variables configured in the application.

Example Script

To put this in use with a scenario I have used before, here is an example of a postdeploy hook that will restart DelayedJob in a Rails application. Make sure the above .ebextension is implemented, then add the following file in your application bundle under .platform/hooks/postdeploy/01_restart_delayed_job.sh

!/usr/bin/env bash

 # Loading environment data
EB_APP_USER=$(/opt/elasticbeanstalk/bin/get-config platformconfig -k AppUser)
EB_APP_CURRENT_DIR=$(/opt/elasticbeanstalk/bin/get-config platformconfig -k AppDeployDir)
EB_APP_PIDS_DIR=/var/pids

 # Now we can execute the command. The -l flag sources makes sure to source everything
 # in /etc/profile.d/
su -l ${EB_APP_USER} -c "cd $EB_APP_CURRENT_DIR; bundle exec bin/delayed_job --pid-dir=$EB_APP_PIDS_DIR restart"

That's it for now

Thank you for reading and happy coding!