Convenient way to run app commands in Elastic Beanstalk

Dec 29, 2019

When the need arise to SSH into your EB instance and execute a command in your app, you might run into issues. I describe in an earlier post how to trigger a restart of DelayedJob after each deployment. In that particular case, one issue was that the command was not running under the correct ruby installation. Turns out that we can make use of EB's own support scripts to help us out.

EB_SCRIPT_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k script_dir)
. $EB_SCRIPT_DIR/use-app-ruby.sh

Ever since then, I have repeatedly tweaked that solution to fit a bunch of other needs as well. For example, one of my applications included a file-system index solution. So when I ran the rails console (bundle exec rails c from /var/app/current) which usually is enough, I encountered raised errors because it did not have permissions to write to the file system. Not surprising since I was running under the ec2-user credentials, whereas the app uses the webapp credentials.

At this point, you might be tempted to just sudo your way into the console, but even though that will not work in itself, also consider the potential consequences if you start causing writes to the file system under the root credentials. That might cause your application to crash, when it tries and fails to make changes to those writes.

Looking at my previous post about DelayedJob, one solution is pretty obvious, just change the restart command to rails c command and run that script as root. An easy solution which I have made use of at times.

Eventually though, if you have several commands that you might use (rails, rake, etc.) and you want to have them available across deployments and provisions, you might end up with a bunch of different scripts created by multiple ebextensions.

To make things easier and better suited to handle any new command that I might want to run, I modified the script so that I can simple pass the command to execute as an argument.

Here is the ebextension I use

files:
  "/var/my-app/scripts/run_app_cmd.sh":
    mode: "000755"
    owner: root
    group: root
    content: |
      #!/usr/bin/env bash

      # Loading environment data
      EB_SCRIPT_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k script_dir)
      EB_SUPPORT_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k support_dir)
      EB_APP_USER=$(/opt/elasticbeanstalk/bin/get-config container -k app_user)
      EB_APP_CURRENT_DIR=$(/opt/elasticbeanstalk/bin/get-config container -k app_deploy_dir)

      # Setting up correct environment and ruby version so that bundle can load all gems
      . $EB_SUPPORT_DIR/envvars
      . $EB_SCRIPT_DIR/use-app-ruby.sh

      cd $EB_APP_CURRENT_DIR
      su -s /bin/bash -c "$1" $EB_APP_USER

This script can be invoked and tested like this.

$ sudo /var/my-app/scripts/run_app_cmd "bundle exec rails c"

> system("whoami")
webapp
=> true

One last note, this is obviously risky in a production environment and not something I recommend. However, in a staging environment or a dev/debug copy of your instance, it is a convenient tool to have.

Code responsibly!