Orchestrating AWS Infrastructure and Application Rollouts with Terraform and CodeDeploy

The convergence of Infrastructure as Code (IaC) and automated deployment pipelines has become the standard for modern cloud engineering. While Terraform excels at provisioning and managing the underlying infrastructure layers, AWS CodeDeploy provides the mechanism to distribute application artifacts across that infrastructure. Integrating these two distinct but complementary systems allows engineers to achieve a fully automated lifecycle: from the creation of a Virtual Private Cloud (VPC) and Elastic Compute Cloud (EC2) instances to the seamless installation of application code on running servers. This article explores the technical architecture, configuration requirements, and operational workflows necessary to automate application deployments using Terraform and AWS CodeDeploy, covering both EC2-based web services and containerized ECS workloads.

Architectural Foundations and Resource Provisioning

The foundation of this integration lies in Terraform's ability to define the state of the infrastructure. When using AWS CodeDeploy, the infrastructure must be prepared to accept deployment agents and handle traffic routing. A robust Terraform script for this purpose typically provisions a comprehensive set of resources that ensure high availability and scalability. The creation of these resources is not merely a step in a tutorial but a critical requirement for the deployment mechanism to function correctly.

The following table outlines the essential resources typically provisioned by a Terraform script designed to support AWS CodeDeploy operations on EC2 instances.

Resource Type Description Role in CodeDeploy Workflow
VPC Virtual Private Cloud Isolated network environment for the infrastructure.
Subnets Network segments across Availability Zones Distributes instances across zones for fault tolerance.
Route Table Traffic routing rules Directs network traffic between subnets and internet gateways.
Launch Configuration Instance template Defines the AMI and instance type for the Auto Scaling Group.
Autoscaling Group Collection of EC2 instances Ensures a fixed or variable number of instances are available.
EC2 Instances Application servers The target hosts where the CodeDeploy agent installs the application.
Application Load Balancer (ALB) Layer 7 load balancer Distributes incoming traffic to healthy EC2 instances.
CodeDeploy Deployment Group Target configuration Maps the application to specific EC2 instances or auto scaling groups.
IAM Roles Security credentials Grants permissions to EC2 instances and CodeDeploy services.
SNS Topic Simple Notification Service Handles notifications for deployment success or failure.

In a standard implementation, the Terraform script creates a VPC in a selected region, such as ap-southeast-1, along with three subnets spread across three different availability zones. This distribution is critical for redundancy. The script also provisions an Auto Scaling Group (ASG) and an Application Load Balancer (ALB). The ALB sits in front of the application servers, exposing the application to the internet while shielding the individual EC2 instances from direct public exposure. The CodeDeploy agent must be installed on every EC2 instance managed by the Auto Scaling Group. This agent is responsible for pulling the application bundle from Amazon S3 or another source and executing the installation scripts.

The configuration of the Auto Scaling Group is particularly important. When CodeDeploy integrates with an ASG, it uses lifecycle hooks to ensure that new instances are not marked as "In Service" by the ALB until the deployment is successful. This prevents traffic from being routed to instances that have not yet updated their application code. Conversely, if a deployment fails on an instance, the lifecycle hook can terminate the instance, allowing the ASG to replace it, thereby maintaining the desired capacity and ensuring only healthy nodes serve traffic.

Manual Deployment Verification and Workflow

Before automating the entire pipeline, it is essential to understand the manual deployment process to validate the infrastructure. This step serves as a diagnostic tool to confirm that the Terraform-created resources are correctly configured and that the CodeDeploy agent is operational.

Once the terraform apply command completes, the AWS CodeDeploy console will reflect the new application. For example, if the Terraform script defines an application named demo_app, it will appear in the CodeDeploy "Applications" list. Within this application, there will be a deployment group, often named cd_dg1, which has been created by the Terraform script. This deployment group defines the targets (the EC2 instances or ASG) and the deployment configuration (such as OneAtATime or AllAtOnce).

To perform a manual deployment, an engineer navigates to the deployment group and clicks "Create Deployment." The user is presented with options for the source of the application. In many test scenarios, the application bundle is stored in an S3 bucket. For instance, a sample application might be hosted at s3://jaydenstaticwebsite/download/demo_cd.zip. The user selects this S3 object and initiates the deployment.

The CodeDeploy service then processes the deployment. The status page will display the progress, indicating that the application is being installed on the instances. The deployment configuration dictates the speed and risk profile. A OneAtATime configuration deploys the application to servers sequentially, which takes more time but allows for quick rollback if a failure occurs on the first instance. An AllAtOnce configuration deploys to all instances simultaneously, reducing total deployment time but increasing the risk if the application code is faulty.

Upon successful completion, the status will indicate that a specific number of instances have been updated, such as "3 of 3 instances updated - Succeeded." At this point, the Application Load Balancer's DNS name can be accessed in a browser to verify that the sample web application is live. This manual verification confirms that the network path, the IAM roles, the S3 permissions, and the CodeDeploy agent are all functioning as expected.

Advanced Configuration: ECS and Blue/Green Deployments

While the EC2-based deployment is straightforward, modern applications often run on Amazon Elastic Container Service (ECS). Configuring CodeDeploy for ECS requires additional Terraform resources and specific parameters to enable advanced deployment strategies like Blue/Green deployments.

To manage ECS services with CodeDeploy, the Terraform configuration must include an aws_codedeploy_app resource with the compute_platform parameter set to ECS. This tells CodeDeploy that the target is a container service rather than an EC2 instance.

hcl resource "aws_codedeploy_app" "example" { compute_platform = "ECS" name = "example_codedeploy_ecs_app" }

The next critical component is the aws_codedeploy_deployment_group. This resource links the CodeDeploy application to the specific ECS service. It also allows the definition of deployment configurations, such as Blue/Green with Lambda or Blue/Green with ECS Service. For a Blue/Green deployment, two target groups are required: one for the "Blue" (current) service and one for the "Green" (new) service. Additionally, a test traffic listener may be configured to route a small percentage of traffic to the new service for verification before cutover.

A crucial aspect of managing ECS services with Terraform and CodeDeploy involves the lifecycle block in the aws_ecs_service resource. Because CodeDeploy updates the task definition (and thus the container image) outside of Terraform's control during the deployment process, Terraform will detect a drift if it expects the task definition to remain unchanged. To prevent Terraform from attempting to "fix" this drift and potentially disrupting the deployment, the lifecycle block must include an ignore_changes parameter for the task_definition.

```hcl
resource "awsecsservice" "example" {
# ... other parameters ...

deploymentcontroller {
type = "CODE
DEPLOY"
}

lifecycle {
# Ignore changes in image ID outside Terraform, i.e., in GitHub actions
ignorechanges = [taskdefinition]
}
}
```

Furthermore, the task definition used in the ECS service must be passed to CodeDeploy. This includes ensuring that the task execution role and task role are correctly defined and accessible by the CodeDeploy service. These roles grant the necessary permissions for the tasks to access AWS resources, such as pulling images from Amazon Elastic Container Registry (ECR) or writing to S3.

Automating the Pipeline with CodeBuild and CodePipeline

Manual execution of terraform plan and terraform apply is not sustainable for production environments. To achieve true DevOps automation, the Terraform code itself must be deployed via a CI/CD pipeline. AWS CodePipeline and CodeBuild provide the orchestration and build capabilities to automate this process.

The goal is to create a pipeline that triggers whenever code is committed to a GitHub repository. The pipeline consists of two main stages: a Planning stage and an Apply stage.

  1. Planning Stage:

    • Source: Downloads the source code from the API Gateway or infrastructure repository.
    • Build (CodeBuild):
      • Downloads and installs Terraform.
      • Initializes the Terraform environment with an S3 backend. Using an S3 backend ensures that the Terraform state is stored remotely, allowing for team collaboration and centralized state management.
      • Runs terraform plan to generate a proposed change set.
      • Saves the plan output to a pipeline artifact.
    • Approval: Sends an email via Amazon Simple Notification Service (SNS) to notify stakeholders that the pipeline is awaiting approval. This manual approval step prevents blind deployment of untested infrastructure changes.
    • Manual Approval: An engineer reviews the plan and approves the pipeline.
  2. Apply Stage:

    • Build (CodeBuild):
      • Downloads and installs Terraform.
      • Initializes the Terraform environment with the same S3 backend.
      • Runs terraform apply using the artifact from the Planning stage. This ensures that the infrastructure changes applied are exactly those that were reviewed and approved.

This flow ensures that the infrastructure is only updated after a rigorous review process. The pipeline itself can be defined in code, often using the AWS Serverless Application Repository or Terraform to create the CodePipeline and CodeBuild resources. Initially, this pipeline might be deployed manually or via a local machine, but once established, it manages the entire lifecycle of the infrastructure.

The prerequisites for this automation include setting up the S3 backend for Terraform. The Terraform configuration file must specify the backend details:

```hcl
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}

# S3 backend configuration
backend "s3" {
bucket = "terraform-state-bucket"
key = "env/dev/terraform.tfstate"
region = "us-east-1"
}
}
```

This separation of concerns—where one pipeline manages the infrastructure (Terraform) and another or the same pipeline manages the application code (CodeDeploy)—allows for independent scaling of changes. The infrastructure pipeline may rarely need updates, while the application deployment pipeline may run multiple times a day.

Conclusion

The integration of Terraform and AWS CodeDeploy represents a mature approach to cloud operations, bridging the gap between infrastructure provisioning and application delivery. By leveraging Terraform to define the VPC, subnets, Auto Scaling Groups, and IAM roles, engineers create a reproducible and versioned foundation. AWS CodeDeploy then utilizes this foundation to manage the deployment of application artifacts, whether on EC2 instances or ECS containers.

The operational workflow, from manual verification to fully automated CI/CD pipelines, demonstrates the evolution from ad-hoc operations to industrial-grade DevOps practices. The use of S3 backends for Terraform state and CodePipeline for orchestration ensures that changes are reviewed, tracked, and reversible. For ECS services, the specific configuration of the deployment_controller and the lifecycle block in Terraform highlights the need for precise coordination between the IaC tool and the deployment service.

Ultimately, this combination allows organizations to scale their AWS environments with confidence. The automation of infrastructure changes reduces the risk of human error, while the granular control provided by CodeDeploy ensures that application rollouts are safe, monitored, and efficient. As cloud architectures become more complex, the synergy between these tools will only become more critical, providing the backbone for resilient and scalable cloud-native applications.

Sources

  1. Jayden Aung
  2. AWS Builders
  3. ScaleFactory

Related Posts