Skip to main content

Command Palette

Search for a command to run...

AWS Elastic Beanstalk Blue-Green Deployment using Terraform

Published
5 min read
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.0

  • AWS CLI configured

  • PowerShell (for automation scripts)

  • Node.js (optional, for local testing)


Deployment Flow (High Level)

  1. Package application versions (v1 and v2)

  2. Create IAM roles & instance profiles

  3. Create Elastic Beanstalk application

  4. Upload application versions to S3

  5. Deploy Blue & Green environments

  6. 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.