Aws
14 Nov 2019

Update URL for Nginx on Beanstalk Deployments

Note: Steps were valid as of 2019; Beanstalk and console options may have changed since then.

Beanstalk Immutable Deployment

I faced an issue during my CI-CD process, in this process an artifact is created in develop environment with a default configuration that includes a URL to test the development environment; this artifact is immutable and is promoted to the different environments (Staging, QA, Demo, Prod).

The artifact structure will be something similar to this:

MY_APP/
    MY_APP.jar
    .ebextensions
        certificates_deploy.config
        additional_commands.config
        nginx/
            conf.d/
                nginx_ssl.conf

The content of those files will be something similar to this:

certificates_deploy.config

Resources:
  AWSEBAutoScalingGroup:
    Metadata:
      AWS::CloudFormation::Authentication:
        S3Auth:
          type: "s3"
          buckets: ["BUCKET-NAME"]
          roleName:
            "Fn::GetOptionSetting":
              Namespace: "aws:autoscaling:launchconfiguration"
              OptionName: "IamInstanceProfile"
              DefaultValue: "AWS-ACCESS-ROLE"

files:
  "/etc/nginx/certs/bundle.crt" :
    mode: "000400"
    owner: root
    group: root
    authentication: "S3Auth"
    source: https://s3.amazonaws.com/BUCKET-NAME/<PATH_TO_YOUR_BUNDLE>/bundle.crt

  "/etc/nginx/certs/my_domain.key" :
    mode: "000400"
    owner: root
    group: root
    authentication: "S3Auth"
    source: https://s3.amazonaws.com/BUCKET-NAME/<PATH_TO_YOUR_KEY>/my_domain.key

additional_commands.config

container_commands:
  01update_ssl_url:
    command: /etc/nginx/update_ssl_url.sh "$ENVIRONMENT_NAME" "$APPLICATION_NAME"
  06restart_nginx:
    command: "service nginx restart"

files:
  "/etc/nginx/update_ssl_url.sh":
    content: |
      #!/usr/bin/env bash
      environment_name=$1
      application_name=$2
      sed -i "s/server_name*/server_name $application_name-$environment_name.my_domain.com;#/" /etc/nginx/conf.d/nginx_ssl.conf
      cat /etc/nginx/conf.d/nginx_ssl.conf
    mode: "000755"
    owner: root
    group: root

nginx_ssl.conf

server {
  listen        443 http2 ssl default_server;
  access_log    /var/log/nginx/access.log main;
  server_name my-service-dev.my_domain.com;
  ssl_certificate /etc/nginx/certs/bundle.crt;
  ssl_certificate_key /etc/nginx/certs/my_domain.key;
  client_header_timeout 60;
  client_body_timeout   60;
  keepalive_timeout     60;
  ...
  ...

During the Execution will be pass an option file in JSON format with the Environment variables.

env-dev.json

[
  {
    "Namespace": "aws:elasticbeanstalk:application:environment",
    "OptionName": "APPLICATION_NAME",
    "Value": "my-service"
  },
  {
    "Namespace": "aws:elasticbeanstalk:application:environment",
    "OptionName": "ENVIRONMENT_NAME",
    "Value": "dev"
  },
  {
    "Namespace": "aws:elasticbeanstalk:application:environment",
    "OptionName": "SERVER_PORT",
    "Value": "9999"
  }
]

this file will be readed during the insstance update

aws elasticbeanstalk update-environment --environment-name "my-service-dev" --version-label "1.0.0" --option-settings file://env-dev.json

Now according to AWS Documentation the script update_ssl_url.sh should be executed after the artifact is unpacked in the instance, but this does not happen; the script is executed before the artifact is unpacked and the file nginx_ssl.conf is created in the instance giving you the same URL for every environment.

After some investigation I found that I have to use the elasticbeanstalk hooks that allow you to execute scripts after, during or before the deployment.

To be able to reach our goal the file additional-commands.config will be updated adding new lines and commands.

container_commands:
  01Install_jq:
    command: "yum install -y jq"
  02update_ssl_url:
    command: /opt/elasticbeanstalk/hooks/appdeploy/post/update_ssl_url.sh
  03restart_nginx:
    command: "service nginx restart"

files:
  "/opt/elasticbeanstalk/hooks/appdeploy/post/update_ssl_url.sh":
    content: |
      #!/usr/bin/env bash
      application_name=$(sudo bash -c '/opt/elasticbeanstalk/bin/get-config environment |jq -r ".APPLICATION_NAME"')
      environment_name=$(sudo bash -c '/opt/elasticbeanstalk/bin/get-config environment |jq -r ".ENVIRONMENT_NAME"')
      if test -f "/etc/nginx/conf.d/nginx_ssl.conf"; then
        sed -i "s/server_name*/server_name $application_name-$environment_name.my_domain.com;#/" /etc/nginx/conf.d/nginx_ssl.conf
        cat /etc/nginx/conf.d/nginx_ssl.conf
      fi
    mode: "000755"
    owner: root
    group: root

As you can see, we have installed jq this will be used to read the environment variables that are created using the env-dev.json file.

Also we change the file location to /opt/elasticbeanstalk/hooks/appdeploy/post/ that is the PATH that is readed by AWS during the deployment to execute scripts after the deployment.

Using the get-config environment command we will be able to read the environment variables created with the env-dev.josn file, then, the the URL will be updated in the nginx_ssl.conf file and nginx will be restarted.


Tags: