Update [Jan 12, 2022]: I came up with a simpler script and wrote about it.

I recently deployed a Rails app onto the latest AWS Elastic Beanstalk platform version running Ruby. I had done this a couple years ago, but a newer platform version (running Amazon Linux 2) is out now, with some significant changes.

One such change: the Elastic Beanstalk application’s environment properties are no longer automatically available in a login shell as environment variables. In the previous platform version, I could start a Rails console like this:

# switch to root, then start a Rails
# console session as the webapp user
sudo su -
cd /var/app/current
su -s /bin/bash -c 'bin/rails c' webapp

To my surprise, when I tried doing this for the first time on an instance launched via the newer Elastic Beanstalk platform version, I was greeted with this error message after trying to query the database:

Mysql2::Error::ConnectionError (Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2))

Odd that it was trying to connect via a local socket rather than my RDS instance… So I tried Rails.env and got back "development". Definitely not correct. It seemed that the environment variables were not being set.

I exited the console and tried echo $MY_VAR to test for a variable defined as an Elastic Beanstalk environment property. Empty. Same for $RACK_ENV, a (Ruby) platform built-in, which I expected to be set to production.

While I could just add RACK_ENV=production MY_VAR=my-val before rails c, this would be a lot to remember and to type each time, and a copy/paste note could easily get out of sync with the list of properties the application needs.

What I needed was a way to start a Rails console (or run other scripts) with my application’s environment variables available. I looked around a bit but didn’t find a way to do this. On a help page, AWS notes:

Environment properties aren’t automatically exported to the shell, even though they are present in the instance. Instead, environment properties are made available to the application through the stack that it runs in, based on which platform you’re using.

Just under that note is a link to Accessing environment properties, which shows how to access individual properties from within an application, but not from a shell. At the bottom, though, there’s a link to the built-in get-config program, which looks promising.

This page shows how to get a single environment property using get-config:

/opt/elasticbeanstalk/bin/get-config environment -k PORT

While not entirely clear from the documentation, all environment properties can be obtained by leaving off the -k option. The results are returned as JSON. Now we just need to convert the JSON to environment variables.

Amazon Linux 2 instances on Elastic Beanstalk (on the Ruby platform, at least) come with jq preinstalled. We can use this to parse the JSON from the get-config program and turn the key/value pairs into export commands.

Assuming get-config returns this JSON:

{"RACK_ENV":"production","MY_VAR":"my-val"}

we want to get these export commands:

export RACK_ENV="production"; export MY_VAR="my-val";

Here’s a command that does just that1:

/opt/elasticbeanstalk/bin/get-config environment \
  | jq -j 'to_entries | .[] | "export \(.key)=\"\(.value)\"; "'

Rather than having to copy and paste that command each time I ssh to a server, I put it into a script. I named it ebenv because it loads the EB properties into the shell as environment variables.

Note: If you plan on using this code, be sure to first check out the update.

#!/bin/bash

# This script runs the command (and args)
# passed to it as the EB app user, after
# making Elastic Beanstalk environment
# variables available to the command.
#
# Usage:
#
#   ebenv <command> [arguments]
#
# Example:
#
#   ebenv bin/rails c

# exit if there's no command to run
if [[ "$#" == "0" ]]; then
  echo 'Usage: ebenv <command> [arguments]'
  exit 1
fi

# convert env properties to export commands
EXPORTS="$(
  /opt/elasticbeanstalk/bin/get-config environment \
  | jq -j 'to_entries | .[] | "export \(.key)=\"\(.value)\"; "'
)"

# get the user to run the command as
EB_APP_USER="$(
  /opt/elasticbeanstalk/bin/get-config platformconfig -k AppUser
)"

eval "$EXPORTS su -s /bin/bash -c \"$*\" $EB_APP_USER"

After making sure there’s a command to run, this script collects the Beanstalk environment’s properties, converts them to export commands, and then runs the command (preceeded by the exports) as the app user (webapp).

This little script lives happily in my Rails app’s bin directory, and I also have a prebuild platform hook script that copies it to /usr/local/bin/ so that it’s available everywhere.


Notes
  1. Note that this is a simple implementation that assumes the property values do not contain quotes, escape characters, or dollar-sign characters. It just slaps a " at the beginning and end of the value. ↩︎