AWS Elastic Beanstalk makes it easy to get a simple application from development into a public web server. If you use a linked RDS instance for storage, the service even handles the population of the database credentials on your application instance by loading them in the environment. If you want to include additional credentials as environment variables, you can do it, but they are unfortunately readable as plain text in the Elastic Beanstalk console. Instead, I've taken to importing a credentials.py file which gets loaded on my instances via encrypted S3 as part of the deploy script.

credentials.py

Create your credentials.py file for the environments you'll be deploying to. Upload them to S3 making sure to enable server-side encryption. Protect the bucket these are stored in. Treat it just like part of a settings.py file.

MY_SECRET_PASSWORD = 'five golDen chickens'

(Jeez, that's not really my password. Whatever.)

Import credentials

In the top of my base.py, which is loaded in each of my tier-sepcific config files, I include:

from config.settings.credentials import *  #noqa

Including #noqa ensures that flake8 won't register an error, since we really do want to import everything in this case. I include a default config/settings/credentials.py that sets dummy variables to enable my tests to pass, or reads those from the environment. That file is overwritten during deployment and copied into place via an .ebextensions file.

.ebextensions

The following code is an .ebextensions file with the settings needed to get credentials.py installed into your Django application on each deploy. Depending where your credentials files are stored, you'll want to change config/settings/ to lineup with your directory structure. container_commands run from the root of your app:

commands:
  01_mkdirs:
    test: '[ ! -d /var/makeconf ]'
    command: 'mkdir -p /var/makeconf'
  02_set_perms:
    command: 'chown -R wsgi:wsgi /var/makeconf'

container_commands:
  01_move_credentials:
    command: 'mv /var/makeconf/credentials.py config/settings/'

files:
  '/var/makeconf/credentials.py':
    owner: wsgi
    group: wsgi
    mode: '000400'
    source: https://s3.amazonaws.com/BUCKET_NAME/PATH/TO/CREDENTIALS
    authentication: S3Access

Resources:
  AWSEBAutoScalingGroup:
    Metadata:
      AWS::CloudFormation::Authentication:
        S3Access:
          type: S3
          roleName: my.elastic.beanstalk.iam.role
          buckets: BUCKET_NAME

django-makeconf

You're likely going to want a different credentials.py file for each tier, and so I recommend using a tool like django-makeconf to build your .ebextensions for each tier. It could be as simple as specifying the following variables in your tier-based Django config files:

  • CREDENTIALS_BUCKET
  • CREDENTIALS_KEY
  • EB_IAM_ROLE

Alter the above file to replace

  • BUCKET_NAME with {{ settings.CREDENTIALS_BUCKET }}
  • PATH/TO/CREDENTIALS with {{ settings.CREDENTIALS_KEY }}
  • my.elasticbeanstalk.iam.role to {{ settings.EB_IAM_ROLE }}

Once those pieces are in place, put that file at YOUR_APP_NAME/templates/credentials.config.tmpl and set MAKECONF_MAP to something like:

{'.ebextensions/01_credentials.config': 'credentials.config.tmpl'}

Then run python manage.py makeconf with the correct config file for each tier during each deploy build. e.g. for QA, you'd run the following command to build your .ebextensions file:

DJANGO_SETTINGS_MODULE=config.settings.qa python manage.py makeconf

IAM role

You're going to need an IAM role with permissions to the buckets you're storing your credentials.py files in. You'll need the following properties on the buckets you're using:

  • s3:ListBucketVersions
  • s3:GetObjectVersion
  • s3:ListBucket
  • s3:GetObject

And you'll want to allow s3:ListAllMyBucket for all resources.

Deploy

Make sure you include the .ebextensions file you just created in the zipfile you're senging to Elastic Beanstalk, and then it's rock & roll.