Skip to content

Commit

Permalink
AWS Elastic Beanstalk Blue/Green deployment with Ansible
Browse files Browse the repository at this point in the history
  • Loading branch information
yohanbeschi committed Feb 18, 2021
1 parent 7884add commit 2c482c5
Show file tree
Hide file tree
Showing 36 changed files with 2,234 additions and 0 deletions.
46 changes: 46 additions & 0 deletions aws-eb-bluegreen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Testing the code

## Requirements

- [An AWS Account](https://aws.amazon.com/account/)
- An IAM user with [programmatic access](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html)
- The AWS CLI with a [profile](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) configured
- [Ansible](https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html) (on Windows you will need [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10))

## Configuration

cd eb-bluegreen-infra
# Update the file `vars/common-vars.asb.yml` with appropriate values
ansible-playbook init/main.asb.yml --check -v
ansible-playbook init/main.asb.yml

ansible-playbook infra/main.asb.yml --check -v
ansible-playbook infra/main.asb.yml

# Insert secrets into SSM Parameter Store
# Create a Python script from .vault/ssm-parameters/parameters.py.template (change the profile and the values) named ssm-labs.py
cd .vault/ssm-parameters
python ssm-labs.py
python ssm-labs.py --nodryrun

cd ../../../eb-bluegreen-app
git init
git remote add origin codecommit::us-east-1://spikeseed-labs@eb-bluegreen-app
git add -A
git commit -m "Demo application"
git push origin master

cd ../eb-bluegreen-infra
ansible-playbook cicd/main.asb.yml --check -v
ansible-playbook cicd/main.asb.yml

# Wait for the Elastic BeanStalk to be created, then:

curl -i -H "x-com-token: 0123456789" https://<aws_accounts.labs.backend_dns>/api/v1/hello # FQDN configured in common-vars.asb.yml

### Cleanup

1. Remove the S3 Buckets
1. Remove the stacks
1. Remove the CodeCommit repositories
1. Remove SSM Parameters
7 changes: 7 additions & 0 deletions aws-eb-bluegreen/eb-bluegreen-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
__pycache__
.classpath
.idea/
*.iml
.project
.settings/
target/
190 changes: 190 additions & 0 deletions aws-eb-bluegreen/eb-bluegreen-app/aws/main.asb.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
---
- hosts: localhost
connection: local
gather_facts: false

vars:
pbk_account: "{{ aws_accounts.labs }}"
pbk_account_name: "{{ pbk_account.account_name }}"
pbk_account_code: "{{ pbk_account.account_code }}"
pbk_account_env: "{{ pbk_account.environment }}"

vars_files:
- vars/common-vars.asb.yml

tasks:

# -----------------
# Common stack - Always present
# -----------------
- name: "Deploy CloudFormation stack {{ local_stack_name }}"
amazon.aws.cloudformation:
stack_name: "{{ local_stack_name }}"
state: present
region: "{{ local_region_name }}"
profile: "{{ pbk_account_name }}"
template: "templates/backend-common.cfn.yml"
tags: "{{ local_aws_tags}}"
template_parameters:
AccountName: "{{ pbk_account_name }}"
AccountCode: "{{ pbk_account_code }}"
RegionCode: "{{ local_region_code }}"
Environment: "{{ pbk_account_env }}"
Application: "{{ application }}"
ServerType: "{{ local_aws_tags.ServerType }}"
SsmVpcIdKey: "{{ ssm_vpc_id_key }}"
SsmBackendAlbSecurityGroupIdKey: "{{ ssm_backend_sg_alb_id_key }}"
SsmElasticBeanstalkServiceRoleNameKey: "{{ ssm_backend_eb_role_name_key }}"
SsmElasticBeanstalkServiceRoleArnKey: "{{ ssm_backend_eb_role_arn_key }}"
SsmBackendInstanceProfileNameKey: "{{ ssm_backend_instanceprofile_name_key }}"
vars:
local_region_name: "{{ default_aws_region }}"
local_region_code: "{{ default_aws_region_code }}"
local_stack_name: "{{ pbk_account_code }}-{{ local_region_code }}-{{ application }}-backend-common"
local_aws_tags:
Application: "{{ application }}"
ServerType: backend
Environment: "{{ pbk_account_env }}"
Name: "{{ local_stack_name }}"
tags:
- backend
- backend-common

# -----------------
# Computing the Next deployment color
# -----------------

- name: Get values from SSM Parameter Store
ansible.builtin.set_fact:
cicd_deploy_color: "{{ lookup( 'aws_ssm', ssm_cicd_deploy_color_key, region=default_aws_region, aws_profile=pbk_account_name ) }}"
tags:
- backend
- backend-eb
- smoke

- name: Blue/Green Deployment variables
ansible.builtin.set_fact:
new_deploy_color: "{{ 'blue' if cicd_deploy_color == 'green' else 'green' }}"
new_ssm_backend_eb_endpoint_url_key: "{{ ssm_backend_eb_endpoint_url_blue_key if cicd_deploy_color == 'green' else ssm_backend_eb_endpoint_url_green_key }}"
tags:
- backend
- backend-eb
- smoke

# -----------------
# Starting new EB env
# -----------------

- name: "Deploy CloudFormation stack {{ local_stack_name }}"
amazon.aws.cloudformation:
stack_name: "{{ local_stack_name }}"
state: present
region: "{{ local_region_name }}"
profile: "{{ pbk_account_name }}"
template: "templates/backend-eb.cfn.yml"
tags: "{{ local_aws_tags }}"
template_parameters:
AccountCode: "{{ pbk_account_code }}"
RegionCode: "{{ local_region_code }}"
Environment: "{{ pbk_account_env }}"
Application: "{{ application }}"
ServerType: "{{ local_aws_tags.ServerType }}"
SsmVpcIdKey: "{{ ssm_vpc_id_key }}"
SsmPublicSubnetsKey: "{{ ssm_web_public_subnets_id_key }}"
SsmPrivateSubnetsKey: "{{ ssm_app_private_subnets_id_key }}"
BackendCertificateArn: "{{ pbk_account.backend_certificate_arn }}"
BackendSolutionStackName: "{{ pbk_account.backend_eb_stack_name }}"
SsmElasticBeanstalkServiceRoleNameKey: "{{ ssm_backend_eb_role_name_key }}"
SsmElasticBeanstalkServiceRoleArnKey: "{{ ssm_backend_eb_role_arn_key }}"
SsmBackendInstanceProfileNameKey: "{{ ssm_backend_instanceprofile_name_key }}"
SsmBackendAlbSecurityGroupIdKey: "{{ ssm_backend_sg_alb_id_key }}"
SsmBackendS3BucketNameKey: "{{ ssm_assets_bucket_name_key }}"
SsmBackendArchiveS3KeyKey: "{{ ssm_backend_eb_app_archive_s3_key_key }}"
BackendInstanceType: "{{ pbk_account.backend_instance_type }}"
AppMinSize: "{{ pbk_account.backend_asg_min_size }}"
AppMaxSize: "{{ pbk_account.backend_asg_max_size}}"
SsmLogsBucketNameKey: "{{ ssm_logs_bucket_name_key }}"
SecurityToken: "{{ pbk_account.backend_secured_header }}"
SsmBeanstalkEndpointUrlKey: "{{ new_ssm_backend_eb_endpoint_url_key }}"
Color: "{{ new_deploy_color }}"
PublicHostedZoneId: "{{ public_hosted_zone_id }}"
BackendDns: "{{ pbk_account.backend_dns }}"
AlbHostedZoneId: "{{ aws_config[local_region_name].alb_hz_id }}"
vars:
local_region_name: "{{ default_aws_region }}"
local_region_code: "{{ default_aws_region_code }}"
local_stack_name: "{{ pbk_account_code }}-{{ local_region_code }}-{{ application }}-backend-{{ new_deploy_color }}"
local_aws_tags:
Application: "{{ application }}"
ServerType: backend
Environment: "{{ pbk_account_env }}"
Name: "{{ local_stack_name }}"
tags:
- backend
- backend-eb

# -----------------
# Smoke Tests
# -----------------

- name: "Run Smoke Tests with https://{{new_deploy_color}}.{{pbk_account.backend_dns}} {{ pbk_account.backend_secured_header }}"
ansible.builtin.script: "scripts/smoke_test.py https://{{new_deploy_color}}.{{pbk_account.backend_dns}} {{ pbk_account.backend_secured_header }}"
tags:
- smoke

# -----------------
# Switch DNS
# -----------------
- name: "Deploy CloudFormation stack {{ local_stack_name }}"
amazon.aws.cloudformation:
stack_name: "{{ local_stack_name }}"
state: present
region: "{{ local_region_name }}"
profile: "{{ pbk_account_name }}"
template: "templates/backend-dns.cfn.yml"
tags: "{{ local_aws_tags }}"
template_parameters:
PublicHostedZoneId: "{{ public_hosted_zone_id }}"
BackendDns: "{{ pbk_account.backend_dns }}"
SsmBeanstalkEndpointUrlKey: "{{ new_ssm_backend_eb_endpoint_url_key }}"
AlbHostedZoneId: "{{ aws_config[local_region_name].alb_hz_id }}"
vars:
local_region_name: "{{ default_aws_region }}"
local_region_code: "{{ default_aws_region_code }}"
local_stack_name: "{{ pbk_account_code }}-{{ local_region_code }}-{{ application }}-backend-dns"
local_aws_tags:
Application: "{{ application }}"
ServerType: backend
Environment: "{{ pbk_account_env }}"
Name: "{{ local_stack_name }}"
tags:
- backend

# -----------------
# Put new color in SSM Parameter Store
# -----------------
- name: "Put new color: <{{ new_deploy_color }}> in SSM Parameter Store"
aws_ssm_parameter_store:
name: "{{ ssm_cicd_deploy_color_key }}"
value: "{{ new_deploy_color }}"
region: "{{ default_aws_region }}"
profile: "{{ pbk_account_name }}"
tags:
- backend

# -----------------
# Remove old EB environment
# -----------------
- name: "Delete CloudFormation stack {{ local_stack_name }}"
amazon.aws.cloudformation:
stack_name: "{{ local_stack_name }}"
state: absent
region: "{{ local_region_name }}"
profile: "{{ pbk_account_name }}"
template: "templates/backend-eb.cfn.yml"
vars:
local_region_name: "{{ default_aws_region }}"
local_region_code: "{{ default_aws_region_code }}"
local_stack_name: "{{ pbk_account_code }}-{{ local_region_code }}-{{ application }}-backend-{{ cicd_deploy_color }}"
tags:
- backend
35 changes: 35 additions & 0 deletions aws-eb-bluegreen/eb-bluegreen-app/aws/scripts/smoke_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env python3

import json
import sys
import time
import urllib
import boto3

def main(retries, sleep):
endpoint = sys.argv[1]
secured_header = sys.argv[2]

current_retry = 0
ok = False
while current_retry < retries:
req = urllib.request.Request(f'{endpoint}/api/v1/hello', method='GET')
req.add_header('x-com-token', secured_header)

try:
with urllib.request.urlopen(req) as response:
if response.getcode() == 200:
ok = True

break
except:
pass # do nothing

current_retry += 1
time.sleep(sleep)

if not ok:
exit(1)

if __name__ == "__main__":
main(10, 30) # 10 retries, sleeps 30s between retries
Loading

0 comments on commit 2c482c5

Please sign in to comment.