AWS Elastic Beanstalk Blue-Green Deployment using Terraform

As part of my Terraform learning journey, today I worked on a real-world deployment strategy that many production teams rely on — Blue-Green Deployment using AWS Elastic Beanstalk.
The goal of this mini project was to replicate Azure App Service deployment slots on AWS and achieve zero-downtime deployments, safe testing, and instant rollback — all managed through Terraform.
What This Demo Achieves
This Terraform project creates:
Blue Environment (Production) running Application v1.0
Green Environment (Staging) running Application v2.0
Fully managed infrastructure:
Application Load Balancers
Auto Scaling Groups
Health checks & monitoring
Instant traffic switching using CNAME swap
Rollback in seconds without redeploying
Architecture Overview
Elastic Beanstalk Application
│
├── Blue Environment (Production)
│ ├── App Version 1.0
│ ├── Load Balancer
│ ├── Auto Scaling
│ └── Health Checks
│
├── Green Environment (Staging)
│ ├── App Version 2.0
│ ├── Load Balancer
│ ├── Auto Scaling
│ └── Health Checks
│
└── CNAME Swap (Instant Traffic Switch)
Both environments are identical in infrastructure, ensuring production parity and reducing deployment risk.
Prerequisites
To run this project, you need:
AWS Account with Elastic Beanstalk permissions
Terraform
>= 1.0AWS CLI configured
PowerShell (for automation scripts)
Node.js (optional, for local testing)
Deployment Flow (High Level)
Package application versions (v1 and v2)
Create IAM roles & instance profiles
Create Elastic Beanstalk application
Upload application versions to S3
Deploy Blue & Green environments
Swap traffic instantly when ready
IAM Roles & Permissions (Critical for Elastic Beanstalk)
Before launching any environment, Elastic Beanstalk requires proper IAM roles for EC2 instances and the EB service itself.
Why this matters
EC2 instances need permission to pull app versions, write logs, and talk to AWS services
Elastic Beanstalk needs permissions for health monitoring and managed updates
Terraform – IAM Roles & Instance Profile
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
}
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"
}
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"
}
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"
}
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
}
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
}
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"
}
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 & S3 Storage
Elastic Beanstalk application versions are stored in S3, and public access is fully blocked for security.
resource "aws_elastic_beanstalk_application" "app" {
name = var.app_name
description = "Blue-Green Deployment Demo Application"
tags = var.tags
}
resource "aws_s3_bucket" "app_versions" {
bucket = "${var.app_name}-versions-${data.aws_caller_identity.current.account_id}"
tags = var.tags
}
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 "aws_caller_identity" "current" {}
Blue Environment (Production – v1.0)
The Blue environment represents the currently live production version.
Production traffic always remains stable here while new versions are tested separately.
Terraform – Blue Environment Setup
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.key
tags = var.tags
}
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 = "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 = "blue"
Role = "production"
})
}
Green Environment (Staging – v2.0)
The Green environment runs the new version and mirrors production infrastructure exactly.
Once validated, traffic is switched instantly.
Blue-Green Swap (Zero Downtime)
aws elasticbeanstalk swap-environment-cnames \
--source-environment-name my-app-bluegreen-blue \
--destination-environment-name my-app-bluegreen-green
⏱️ Swap completes in 1–2 minutes
💥 Users experience zero downtime
Rollback? Just swap again.
Key Learnings
Blue-Green deployment is safer than in-place updates
DNS-level traffic switching is extremely powerful
Terraform makes deployments repeatable and auditable
Elastic Beanstalk simplifies complex infra without losing control
📺 Video That Helped Me Understand this concept:
Final Thoughts
This project gave me hands-on experience with production-grade deployment strategies used by real engineering teams.



