Access Elastic Beanstalk environment properties in NGINX configs running on AWS Linux 2 Access Elastic Beanstalk environment properties in NGINX configs running on AWS Linux 2 nginx nginx

Access Elastic Beanstalk environment properties in NGINX configs running on AWS Linux 2


Summary

One way to do it is to create a shell script in .platform/hooks/postdeploy.

Here is a simplified example, assuming you have an Elastic Beanstalk environment property called MASTER_DOMAIN:

#!/bin/bash# write nginx config filecat > /etc/nginx/conf.d/elasticbeanstalk/test.conf << LIMIT_STRINGlocation /test/ {  default_type text/html;  return 200 "nginx variable: \$host, and EB env property: $MASTER_DOMAIN";}LIMIT_STRING# restart nginx service so the config takes effectsystemctl restart nginx.service

The location block from this example can be replaced by the nginx content from .ebextensions/10_proxy.config in the original post. No need for the Fn::GetOptionSetting stuff though.

I think you also need a duplicate script in .platform/confighooks/postdeploy.

Details below.

(sorry for the wall of text)

Environment variables in nginx

Actually, as discussed in here and here, it is not possible (out-of-the-box) to use os environment variables inside the http, server, or location blocks in nginx config files. There are some workarounds, such as using lua, perl, or templates, but let's not get into those. This part has nothing to do with AWS.

In the OP's original configuration for Amazon Linux AMI (AL1), using the files section in .ebextensions/10_proxy.config, they were actually using a shell script to write the nginx config file during deployment. The shell script expanded the environment variables, but the resulting proxy.conf for nginx did not actually access any environment variables.

That's why it worked on AL1.

Platform hooks

Now, for Amazon Linux 2 (AL2), we can do something similar using shell scripts in the .platform/hooks and .platform/confighooks folders.

These .platform hook scripts are executed as the root user, and they have access to the Elastic Beanstalk (EB) environment properties. The EB environment properties can be accessed just like normal OS environment variables, so there is no need to use the Fn::GetOptionSetting stuff.

Basically, we need to create a shell script that writes a file with the content from your original .ebextensions/10_proxy.config. However, there are two questions we need to consider:

  1. Should we use a prebuild, predeploy, or postdeploy hook?

  2. What is the proper destination directory for our nginx proxy.conf file?

File locations

To answer these questions, we have to refer to the AWS documentation for Extending Elastic Beanstalk Linux platforms, and specifically the Instance deployment workflow section.

... The current working directory (cwd) for platform hooks is the application's root directory. For prebuild and predeploy files it's the application staging directory, and for postdeploy files it's the current application directory. If one of the files fails (exits with a non-zero exit code), the deployment aborts and fails.

This is interesting, but leaves some questions, e.g. where is the "application staging directory" located? We can fill in the blanks by inspecting one of our deployment log files. Based on our eb-engine.log, here's what happens with the platform hooks and nginx config files during app deployment (skipping a lot of details):

  1. the source bundle is downloaded from S3 and extracted to /var/app/staging/
  2. platform hooks in .platform/hooks/prebuild/ are executed
  3. proxy server configuration is copied from /var/app/staging/.platform/nginx/ to /var/proxy/staging/nginx
  4. platform hooks in .platform/hooks/predeploy/ are executed
  5. proxy server is started, configuration is copied from /var/proxy/staging/nginx/ to /etc/nginx
  6. platform hooks in .platform/hooks/postdeploy/ are executed

Note, after deployment the app is located in /var/app/current.

Based on the above, there are several options:

  1. Create a shell script in .platform/hooks/postdeploy that writes to /etc/nginx/conf.d/proxy.conf.

    The nginx service is already running, at this stage, so we need to restart for the configuration to take effect.

    Below is a minimal test example. In this example we write to the elasticbeanstalk subdirectory, because we just want to add a location inside the default server block. We can then visit the /test/ page in a browser, to check that the configuration works.

    We use some bash io redirection (<<, >) to write the nginx config file.

    Note that we need to escape any nginx variables, e.g. $host becomes \$host, otherwise the shell will interpret them as environment variables.

    Also note that the shell scripts need to have execution permission, as explained under More about platform hooks in the docs.

    #!/bin/bashcat > /etc/nginx/conf.d/elasticbeanstalk/test.conf << LIMIT_STRINGlocation /test/ {  default_type text/html;  return 200 "nginx variable: \$host, and EB env property: $MASTER_DOMAIN";}LIMIT_STRINGsystemctl restart nginx.service
  2. Alternatively, we could create a shell script in .platform/hooks/predeploy that writes to /var/proxy/staging/nginx/conf.d/proxy.conf.

    There is no need to restart the nginx service in this case, because this hook is executed before the server configuration is applied.

BEWARE:

Not sure if this is a bug or a design feature, but our newly created proxy.conf disappears after a configuration deployment (as opposed to an application deployment), unless we put a duplicate script in the .platform/confighooks/postdeploy directory. Not very DRY...

EDIT: AWS support confirmed that we need duplicate scripts in hooks and confighooks in this case. The application example in the docs also shows some duplicates (at least duplicate filenames) in hooks and confighooks.

Disclaimer: This was tested on a freshly created single instance EB environment with "Python 3.7 running on 64bit Amazon Linux 2/3.1.5" using all default configuration and the default AWS Python sample application (only extended with our custom hooks).