AWS Terraform Blue-Green Deployment Using Elastic Beanstalk
Jenkins
Source: Dev.to | Jan 11, 2026 (UTC)
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.
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
Enter fullscreen mode
Exit fullscreen mode
For Windows:
.\package-apps.ps1
Enter fullscreen mode
Exit fullscreen mode
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" {}
Enter fullscreen mode
Exit fullscreen mode
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" } ) }
Enter fullscreen mode
Exit fullscreen mode
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" } ) }
Enter fullscreen mode
Exit fullscreen mode
Step 5: Deploy the Infrastructure
Execute the Terraform workflow:
# Initialize Terraform terraform init # Review the planned changes terraform plan # Apply the configuration terraform apply
Enter fullscreen mode
Exit fullscreen mode
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:
After completing your testing, destroy all resources to avoid charges:
terraform destroy
Enter fullscreen mode
Exit fullscreen mode
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:
Залишити відповідь