Introduction:
In a traditional approach to application deployment, you typically fix a failed deployment by redeploying an earlier, stable version of the application. Redeployment in traditional data centers is typically done on the same set of resources due to the cost and effort of provisioning additional resources. Although this approach works, it has many shortcomings. Rollback isn’t easy because it’s implemented by redeployment of an earlier version from scratch. This process takes time, making the application potentially unavailable for long periods. Even in situations where the application is only impaired, a rollback is required, which overwrites the faulty version. As a result, you have no opportunity to debug the faulty application in place.
Applying the principles of agility, scalability, utility consumption, as well as the automation capabilities of Amazon Web Services can shift the paradigm of application deployment. This enables a better deployment technique called blue/green deployment.
Blue/Green Deployment Methodology:
Blue/green deployments provide releases with near zero-downtime and rollback capabilities. The fundamental idea behind blue/green deployment is to shift traffic between two identical environments that are running different versions of your application. The blue environment represents the current application version serving production traffic. In parallel, the green environment is staged running a different version of your application. After the green environment is ready and tested, production traffic is redirected from blue to green. If any problems are identified, you can roll back by reverting traffic back to the blue environment.
Architecture diagram
Benefits of Blue/Green Deployment:
- Zero Downtime: Seamless traffic transition ensures uninterrupted service.
- Safe Rollbacks: Easy reversion to the Blue environment if issues arise.
- Efficient Testing: Allows comprehensive testing of the new environment without affecting live users.
- Scalability: Works well for both monolithic and microservices architectures.
AWS Elastic Beanstalk:
AWS Elastic Beanstalk performs an in-place update when you update your application versions, your application might become unavailable to users for a short period of time. To avoid this, perform a blue/green deployment. To do this, deploy the new version to a separate environment, and then swap the CNAMEs of the two environments to redirect traffic to the new version instantly.
To validate this approach, I tested and deployed Blue-Green Deployment with javascript application, This hands-on implementation ensured real-world feasibility and reliability.
Project Highlights:
- Tech Stack: Terraform, Elastic Beanstalk, EC2, S3.
- Goal: Achieve zero downtime while updating my application.
- Key Challenges: Ensuring that visitors never experience an outage during deployments.
- Solution: Implementing Blue-Green Deployment using Elastic Beanstalk and EC2.
Project Structure
My project is structured in the following way:
Day-17/
├── Readme.md
├── Assets/
└── terraform/
├── .terraform/
├── .terraform.lock.hcl
├── app-v1/
│ ├── app.js
│ ├── package.json
│ └── app-v1.zip
├── app-v2/
│ ├── app.js
│ ├── package.json
│ └── app-v2.zip
├── blue-environments.tf
├── green-environments.tf
├── main.tf
├── outputs.tf
├── package-apps.ps1
├── package-apps.sh
├── swap-environments.ps1
├── swap-environments.sh
├── terraform.tfstate
├── terraform.tfstate.backup
├── terraform.tfvars.example
└── variables.tf
Checkout my Github Repository
Step-by-step Implementation:
Step 1: Package Your Applications
Before deploying, you need to package your application code into ZIP files. The repository includes packaging scripts for different operating systems.
For Linux/Mac:
chmod +x package-apps.sh
./package-apps.sh
For Windows:
.\package-apps.ps1
These scripts create app-v1.zip and app-v2.zip files that Terraform will upload to S3.
Step 2: Core Infrastructure Setup (main.tf)
The main.tf file establishes the foundational infrastructure:
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
required_version = ">= 1.0"
}
provider "aws" {
region = var.aws_region
}
# IAM Role for Elastic Beanstalk EC2 instances
resource "aws_iam_role" "eb_ec2_role" {
name = "${var.app_name}-eb-ec2-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
tags = var.tags
}
# Attach the AWS managed policy for Web Tier
resource "aws_iam_role_policy_attachment" "eb_web_tier" {
role = aws_iam_role.eb_ec2_role.name
policy_arn = "arn:aws:iam::aws:policy/AWSElasticBeanstalkWebTier"
}
# Attach the AWS managed policy for Worker Tier
resource "aws_iam_role_policy_attachment" "eb_worker_tier" {
role = aws_iam_role.eb_ec2_role.name
policy_arn = "arn:aws:iam::aws:policy/AWSElasticBeanstalkWorkerTier"
}
# Attach the AWS managed policy for Multicontainer Docker
resource "aws_iam_role_policy_attachment" "eb_multicontainer_docker" {
role = aws_iam_role.eb_ec2_role.name
policy_arn = "arn:aws:iam::aws:policy/AWSElasticBeanstalkMulticontainerDocker"
}
# Instance Profile
resource "aws_iam_instance_profile" "eb_ec2_profile" {
name = "${var.app_name}-eb-ec2-profile"
role = aws_iam_role.eb_ec2_role.name
tags = var.tags
}
# IAM Role for Elastic Beanstalk Service
resource "aws_iam_role" "eb_service_role" {
name = "${var.app_name}-eb-service-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "elasticbeanstalk.amazonaws.com"
}
}
]
})
tags = var.tags
}
# Attach Enhanced Health Reporting policy
resource "aws_iam_role_policy_attachment" "eb_service_health" {
role = aws_iam_role.eb_service_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSElasticBeanstalkEnhancedHealth"
}
# Attach Managed Updates policy
resource "aws_iam_role_policy_attachment" "eb_service_managed_updates" {
role = aws_iam_role.eb_service_role.name
policy_arn = "arn:aws:iam::aws:policy/AWSElasticBeanstalkManagedUpdatesCustomerRolePolicy"
}
# Elastic Beanstalk Application
resource "aws_elastic_beanstalk_application" "app" {
name = var.app_name
description = "Blue-Green Deployment Demo Application"
tags = var.tags
}
# S3 Bucket for application versions
resource "aws_s3_bucket" "app_versions" {
bucket = "${var.app_name}-versions-${data.aws_caller_identity.current.account_id}"
tags = var.tags
}
# Block public access to S3 bucket
resource "aws_s3_bucket_public_access_block" "app_versions" {
bucket = aws_s3_bucket.app_versions.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# Data source for current AWS account
data "aws_caller_identity" "current" {}
Step 3: Blue Environment Configuration
The blue-environment.tf file defines the production environment running version 1.0:
# Application Version 1.0 (Blue Environment - Production)
resource "aws_s3_object" "app_v1" {
bucket = aws_s3_bucket.app_versions.id
key = "app-v1.zip"
source = "${path.module}/app-v1/app-v1.zip"
etag = filemd5("${path.module}/app-v1/app-v1.zip")
tags = var.tags
}
resource "aws_elastic_beanstalk_application_version" "v1" {
name = "${var.app_name}-v1"
application = aws_elastic_beanstalk_application.app.name
description = "Application Version 1.0 - Initial Release"
bucket = aws_s3_bucket.app_versions.id
key = aws_s3_object.app_v1.id
tags = var.tags
}
# Blue Environment (Production)
resource "aws_elastic_beanstalk_environment" "blue" {
name = "${var.app_name}-blue"
application = aws_elastic_beanstalk_application.app.name
solution_stack_name = var.solution_stack_name
tier = "WebServer"
version_label = aws_elastic_beanstalk_application_version.v1.name
# IAM Settings
setting {
namespace = "aws:autoscaling:launchconfiguration"
name = "IamInstanceProfile"
value = aws_iam_instance_profile.eb_ec2_profile.name
}
setting {
namespace = "aws:elasticbeanstalk:environment"
name = "ServiceRole"
value = aws_iam_role.eb_service_role.name
}
# Instance Settings
setting {
namespace = "aws:autoscaling:launchconfiguration"
name = "InstanceType"
value = var.instance_type
}
# Environment Type (Load Balanced)
setting {
namespace = "aws:elasticbeanstalk:environment"
name = "EnvironmentType"
value = "LoadBalanced"
}
setting {
namespace = "aws:elasticbeanstalk:environment"
name = "LoadBalancerType"
value = "application"
}
# Auto Scaling Settings
setting {
namespace = "aws:autoscaling:asg"
name = "MinSize"
value = "1"
}
setting {
namespace = "aws:autoscaling:asg"
name = "MaxSize"
value = "2"
}
# Health Reporting
setting {
namespace = "aws:elasticbeanstalk:healthreporting:system"
name = "SystemType"
value = "enhanced"
}
# Platform Settings
setting {
namespace = "aws:elasticbeanstalk:environment:process:default"
name = "HealthCheckPath"
value = "/"
}
setting {
namespace = "aws:elasticbeanstalk:environment:process:default"
name = "Port"
value = "8080"
}
setting {
namespace = "aws:elasticbeanstalk:environment:process:default"
name = "Protocol"
value = "HTTP"
}
# Environment Variables
setting {
namespace = "aws:elasticbeanstalk:application:environment"
name = "ENVIRONMENT"
value = "blue"
}
setting {
namespace = "aws:elasticbeanstalk:application:environment"
name = "VERSION"
value = "1.0"
}
# Deployment Policy
setting {
namespace = "aws:elasticbeanstalk:command"
name = "DeploymentPolicy"
value = "Rolling"
}
setting {
namespace = "aws:elasticbeanstalk:command"
name = "BatchSizeType"
value = "Percentage"
}
setting {
namespace = "aws:elasticbeanstalk:command"
name = "BatchSize"
value = "50"
}
# Managed Updates
setting {
namespace = "aws:elasticbeanstalk:managedactions"
name = "ManagedActionsEnabled"
value = "false"
}
tags = merge(
var.tags,
{
Environment = "blue"
Role = "production"
}
)
}
Step 4: Green Environment Configuration
The green-environment.tf file creates an identical staging environment with version 2.0:
# Application Version 2.0 (Green Environment - Staging)
resource "aws_s3_object" "app_v2" {
bucket = aws_s3_bucket.app_versions.id
key = "app-v2.zip"
source = "${path.module}/app-v2/app-v2.zip"
etag = filemd5("${path.module}/app-v2/app-v2.zip")
tags = var.tags
}
resource "aws_elastic_beanstalk_application_version" "v2" {
name = "${var.app_name}-v2"
application = aws_elastic_beanstalk_application.app.name
description = "Application Version 2.0 - New Feature Release"
bucket = aws_s3_bucket.app_versions.id
key = aws_s3_object.app_v2.id
tags = var.tags
}
# Green Environment (Staging/Pre-production)
resource "aws_elastic_beanstalk_environment" "green" {
name = "${var.app_name}-green"
application = aws_elastic_beanstalk_application.app.name
solution_stack_name = var.solution_stack_name
tier = "WebServer"
version_label = aws_elastic_beanstalk_application_version.v2.name
# IAM Settings
setting {
namespace = "aws:autoscaling:launchconfiguration"
name = "IamInstanceProfile"
value = aws_iam_instance_profile.eb_ec2_profile.name
}
setting {
namespace = "aws:elasticbeanstalk:environment"
name = "ServiceRole"
value = aws_iam_role.eb_service_role.name
}
# Instance Settings
setting {
namespace = "aws:autoscaling:launchconfiguration"
name = "InstanceType"
value = var.instance_type
}
# Environment Type (Load Balanced)
setting {
namespace = "aws:elasticbeanstalk:environment"
name = "EnvironmentType"
value = "LoadBalanced"
}
setting {
namespace = "aws:elasticbeanstalk:environment"
name = "LoadBalancerType"
value = "application"
}
# Auto Scaling Settings
setting {
namespace = "aws:autoscaling:asg"
name = "MinSize"
value = "1"
}
setting {
namespace = "aws:autoscaling:asg"
name = "MaxSize"
value = "2"
}
# Health Reporting
setting {
namespace = "aws:elasticbeanstalk:healthreporting:system"
name = "SystemType"
value = "enhanced"
}
# Platform Settings
setting {
namespace = "aws:elasticbeanstalk:environment:process:default"
name = "HealthCheckPath"
value = "/"
}
setting {
namespace = "aws:elasticbeanstalk:environment:process:default"
name = "Port"
value = "8080"
}
setting {
namespace = "aws:elasticbeanstalk:environment:process:default"
name = "Protocol"
value = "HTTP"
}
# Environment Variables
setting {
namespace = "aws:elasticbeanstalk:application:environment"
name = "ENVIRONMENT"
value = "green"
}
setting {
namespace = "aws:elasticbeanstalk:application:environment"
name = "VERSION"
value = "2.0"
}
# Deployment Policy
setting {
namespace = "aws:elasticbeanstalk:command"
name = "DeploymentPolicy"
value = "Rolling"
}
setting {
namespace = "aws:elasticbeanstalk:command"
name = "BatchSizeType"
value = "Percentage"
}
setting {
namespace = "aws:elasticbeanstalk:command"
name = "BatchSize"
value = "50"
}
# Managed Updates
setting {
namespace = "aws:elasticbeanstalk:managedactions"
name = "ManagedActionsEnabled"
value = "false"
}
tags = merge(
var.tags,
{
Environment = "green"
Role = "staging"
}
)
}
Step 5: Deploy the Infrastructure
Execute the Terraform workflow:
# Initialize Terraform
terraform init
# Review the planned changes
terraform plan
# Apply the configuration
terraform apply
This creates both environments and uploads your application versions to S3. The output will display the URLs for both environments.
Step 6: Verify Deployment
After deployment, access both environments using the provided URLs:
Blue Environment: http://blue-environment.xxx.elasticbeanstalk.com
Displays: "This is version 2.0"
Status: Current production environment
Green Environment: http://green-environment.xxx.elasticbeanstalk.com
Displays: "This is version 2.0 with new features"
Status: Staging environment
Step 7: Perform the Blue-Green Swap
Method: AWS Console
- Navigate to Elastic Beanstalk → Environments
- Select the blue-environment
- Click Actions → Swap environment URLs
- Choose green-environment as the target
- Click Swap
After swapping, verify that:
- The blue URL now serves version 2.0
- The green URL now serves version 1.0
Cleanup
After completing your testing, destroy all resources to avoid charges:
terraform destroy
Conclusion
Blue-green deployments with Terraform and AWS Elastic Beanstalk provide a robust framework for releasing applications with confidence. This approach eliminates downtime, enables instant rollbacks, and maintains high availability during deployments.
Reference:
>> Connect With Me
If you enjoyed this post or want to follow my #30DaysOfAWSTerraformChallenge journey, feel free to connect with me here:
💼 LinkedIn: Amit Kushwaha
🐙 GitHub: Amit Kushwaha
📝 Hashnode / Amit Kushwaha
🐦 Twitter/X: Amit Kushwaha




Top comments (0)