AWS DevOps Tools: CodePipeline, CodeBuild, and ECS
· Pavan Y N · aws, devops, codepipeline, ecs, automation
Published: October 30, 2025
Reading Time: 23 minutes
Tags: AWS, DevOps, CodePipeline, CodeBuild, ECS, Automation
# Introduction
AWS provides a comprehensive suite of DevOps tools that integrate seamlessly with the AWS ecosystem. This guide covers AWS CodePipeline, CodeBuild, CodeDeploy, ECS, ECR, and how to build complete CI/CD workflows on AWS.
# AWS CodePipeline
## What is CodePipeline?
AWS CodePipeline is a fully managed continuous delivery service that automates release pipelines for fast and reliable application updates.
## Basic Pipeline Structure
# pipeline.yml (CloudFormation)
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CI/CD Pipeline for Application'
Resources:
ArtifactBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub '${AWS::StackName}-artifacts'
VersioningConfiguration:
Status: Enabled
LifecycleConfiguration:
Rules:
- Id: DeleteOldArtifacts
Status: Enabled
ExpirationInDays: 30
Pipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: !Sub '${AWS::StackName}-pipeline'
RoleArn: !GetAtt PipelineRole.Arn
ArtifactStore:
Type: S3
Location: !Ref ArtifactBucket
Stages:
- Name: Source
Actions:
- Name: SourceAction
ActionTypeId:
Category: Source
Owner: ThirdParty
Provider: GitHub
Version: '1'
Configuration:
Owner: !Ref GitHubOwner
Repo: !Ref GitHubRepo
Branch: !Ref GitHubBranch
OAuthToken: !Ref GitHubToken
OutputArtifacts:
- Name: SourceOutput
- Name: Build
Actions:
- Name: BuildAction
ActionTypeId:
Category: Build
Owner: AWS
Provider: CodeBuild
Version: '1'
Configuration:
ProjectName: !Ref CodeBuildProject
InputArtifacts:
- Name: SourceOutput
OutputArtifacts:
- Name: BuildOutput
- Name: Deploy
Actions:
- Name: DeployAction
ActionTypeId:
Category: Deploy
Owner: AWS
Provider: ECS
Version: '1'
Configuration:
ClusterName: !Ref ECSCluster
ServiceName: !Ref ECSService
FileName: imagedefinitions.json
InputArtifacts:
- Name: BuildOutput
PipelineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Action: 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/AWSCodePipelineFullAccess'
Policies:
- PolicyName: PipelinePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 's3:*'
- 'codebuild:*'
- 'ecs:*'
Resource: '*'
# AWS CodeBuild
## BuildSpec Configuration
# buildspec.yml
version: 0.2
env:
variables:
NODE_ENV: production
parameter-store:
DATABASE_URL: /myapp/database-url
API_KEY: /myapp/api-key
secrets-manager:
DOCKER_PASSWORD: prod/docker:password
phases:
install:
runtime-versions:
nodejs: 20
commands:
- echo "Installing dependencies..."
- npm ci
pre_build:
commands:
- echo "Running pre-build tasks..."
- npm run lint
- npm audit --audit-level=high
- echo "Logging in to Amazon ECR..."
- aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
build:
commands:
- echo "Building application..."
- npm run build
- echo "Building Docker image..."
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
post_build:
commands:
- echo "Running tests..."
- npm test
- echo "Pushing Docker image..."
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- echo "Creating imagedefinitions.json..."
- printf '[{"name":"%s","imageUri":"%s"}]' $CONTAINER_NAME $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG > imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
- appspec.yml
- taskdef.json
cache:
paths:
- 'node_modules/**/*'
- '.npm/**/*'
reports:
test-results:
files:
- 'test-results/**/*'
file-format: JunitXml
coverage-report:
files:
- 'coverage/clover.xml'
file-format: CoberturaXml
## CodeBuild Project (CloudFormation)
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub '${AWS::StackName}-build'
ServiceRole: !GetAtt CodeBuildRole.Arn
Artifacts:
Type: CODEPIPELINE
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_MEDIUM
Image: aws/codebuild/standard:7.0
PrivilegedMode: true
EnvironmentVariables:
- Name: AWS_ACCOUNT_ID
Value: !Ref AWS::AccountId
- Name: IMAGE_REPO_NAME
Value: !Ref ECRRepository
- Name: IMAGE_TAG
Value: latest
- Name: CONTAINER_NAME
Value: myapp
Source:
Type: CODEPIPELINE
BuildSpec: buildspec.yml
Cache:
Type: S3
Location: !Sub '${ArtifactBucket}/build-cache'
LogsConfig:
CloudWatchLogs:
Status: ENABLED
GroupName: !Sub '/aws/codebuild/${AWS::StackName}'
# Amazon ECS (Elastic Container Service)
## Task Definition
{
"family": "myapp-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::123456789012:role/ecsTaskRole",
"containerDefinitions": [
{
"name": "myapp",
"image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest",
"essential": true,
"portMappings": [
{
"containerPort": 3000,
"protocol": "tcp"
}
],
"environment": [
{
"name": "NODE_ENV",
"value": "production"
},
{
"name": "PORT",
"value": "3000"
}
],
"secrets": [
{
"name": "DATABASE_URL",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/database-url"
},
{
"name": "API_KEY",
"valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/api-key"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/myapp",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
},
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
}
}
]
}
## ECS Service (CloudFormation)
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Sub '${AWS::StackName}-cluster'
CapacityProviders:
- FARGATE
- FARGATE_SPOT
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 1
- CapacityProvider: FARGATE_SPOT
Weight: 4
ECSService:
Type: AWS::ECS::Service
DependsOn: LoadBalancerListener
Properties:
ServiceName: !Sub '${AWS::StackName}-service'
Cluster: !Ref ECSCluster
TaskDefinition: !Ref TaskDefinition
DesiredCount: 3
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED
SecurityGroups:
- !Ref ECSSecurityGroup
Subnets:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
LoadBalancers:
- ContainerName: myapp
ContainerPort: 3000
TargetGroupArn: !Ref TargetGroup
HealthCheckGracePeriodSeconds: 60
DeploymentConfiguration:
MaximumPercent: 200
MinimumHealthyPercent: 100
DeploymentCircuitBreaker:
Enable: true
Rollback: true
ServiceAutoScaling:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MaxCapacity: 10
MinCapacity: 2
ResourceId: !Sub 'service/${ECSCluster}/${ECSService.Name}'
RoleARN: !GetAtt AutoScalingRole.Arn
ScalableDimension: ecs:service:DesiredCount
ServiceNamespace: ecs
ServiceScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: !Sub '${AWS::StackName}-scaling-policy'
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref ServiceAutoScaling
TargetTrackingScalingPolicyConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ECSServiceAverageCPUUtilization
TargetValue: 70.0
ScaleInCooldown: 300
ScaleOutCooldown: 60
# Complete CI/CD Pipeline Example
## Terraform Configuration
# main.tf
provider "aws" {
region = var.aws_region
}
# ECR Repository
resource "aws_ecr_repository" "app" {
name = var.app_name
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
encryption_configuration {
encryption_type = "KMS"
kms_key = aws_kms_key.ecr.arn
}
}
# CodeBuild Project
resource "aws_codebuild_project" "app" {
name = "${var.app_name}-build"
service_role = aws_iam_role.codebuild.arn
build_timeout = 30
artifacts {
type = "CODEPIPELINE"
}
environment {
compute_type = "BUILD_GENERAL1_MEDIUM"
image = "aws/codebuild/standard:7.0"
type = "LINUX_CONTAINER"
privileged_mode = true
image_pull_credentials_type = "CODEBUILD"
environment_variable {
name = "AWS_ACCOUNT_ID"
value = data.aws_caller_identity.current.account_id
}
environment_variable {
name = "IMAGE_REPO_NAME"
value = aws_ecr_repository.app.name
}
environment_variable {
name = "IMAGE_TAG"
value = "latest"
}
}
source {
type = "CODEPIPELINE"
buildspec = file("${path.module}/buildspec.yml")
}
cache {
type = "S3"
location = "${aws_s3_bucket.artifacts.bucket}/build-cache"
}
logs_config {
cloudwatch_logs {
group_name = "/aws/codebuild/${var.app_name}"
}
}
}
# CodePipeline
resource "aws_codepipeline" "app" {
name = "${var.app_name}-pipeline"
role_arn = aws_iam_role.codepipeline.arn
artifact_store {
location = aws_s3_bucket.artifacts.bucket
type = "S3"
}
stage {
name = "Source"
action {
name = "Source"
category = "Source"
owner = "ThirdParty"
provider = "GitHub"
version = "1"
output_artifacts = ["source_output"]
configuration = {
Owner = var.github_owner
Repo = var.github_repo
Branch = var.github_branch
OAuthToken = var.github_token
}
}
}
stage {
name = "Build"
action {
name = "Build"
category = "Build"
owner = "AWS"
provider = "CodeBuild"
version = "1"
input_artifacts = ["source_output"]
output_artifacts = ["build_output"]
configuration = {
ProjectName = aws_codebuild_project.app.name
}
}
}
stage {
name = "Deploy"
action {
name = "Deploy"
category = "Deploy"
owner = "AWS"
provider = "ECS"
version = "1"
input_artifacts = ["build_output"]
configuration = {
ClusterName = aws_ecs_cluster.main.name
ServiceName = aws_ecs_service.app.name
FileName = "imagedefinitions.json"
}
}
}
}
# ECS Cluster
resource "aws_ecs_cluster" "main" {
name = "${var.app_name}-cluster"
setting {
name = "containerInsights"
value = "enabled"
}
}
# ECS Task Definition
resource "aws_ecs_task_definition" "app" {
family = "${var.app_name}-task"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "512"
memory = "1024"
execution_role_arn = aws_iam_role.ecs_execution.arn
task_role_arn = aws_iam_role.ecs_task.arn
container_definitions = jsonencode([
{
name = var.app_name
image = "${aws_ecr_repository.app.repository_url}:latest"
essential = true
portMappings = [
{
containerPort = 3000
protocol = "tcp"
}
]
environment = [
{
name = "NODE_ENV"
value = "production"
}
]
secrets = [
{
name = "DATABASE_URL"
valueFrom = aws_secretsmanager_secret.database_url.arn
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = aws_cloudwatch_log_group.app.name
"awslogs-region" = var.aws_region
"awslogs-stream-prefix" = "ecs"
}
}
healthCheck = {
command = ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]
interval = 30
timeout = 5
retries = 3
startPeriod = 60
}
}
])
}
# ECS Service
resource "aws_ecs_service" "app" {
name = "${var.app_name}-service"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = 3
launch_type = "FARGATE"
network_configuration {
security_groups = [aws_security_group.ecs_tasks.id]
subnets = aws_subnet.private[*].id
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.app.arn
container_name = var.app_name
container_port = 3000
}
deployment_configuration {
maximum_percent = 200
minimum_healthy_percent = 100
deployment_circuit_breaker {
enable = true
rollback = true
}
}
depends_on = [aws_lb_listener.app]
}
# STAR Approach: AWS DevOps Implementation
Situation: Company using on-premises Jenkins with manual deployments to EC2, experiencing slow releases and frequent failures.
Task: Migrate to AWS-native CI/CD using CodePipeline, CodeBuild, and ECS for automated, reliable deployments.
Action:
- •
Infrastructure Setup:
- →Created VPC with public/private subnets
- →Set up ECS Fargate cluster
- →Configured Application Load Balancer
- →Implemented auto-scaling policies
- •
CI/CD Pipeline:
- →Built CodePipeline with GitHub integration
- →Configured CodeBuild for Docker builds
- →Set up ECR for container registry
- →Implemented blue-green deployments
- •
Security Implementation:
- →Used Secrets Manager for credentials
- →Implemented IAM roles with least privilege
- →Enabled ECR image scanning
- →Configured VPC endpoints for private communication
- •
Monitoring & Logging:
- →Enabled Container Insights
- →Set up CloudWatch dashboards
- →Configured alarms for critical metrics
- →Implemented X-Ray for distributed tracing
- •
Cost Optimization:
- →Used Fargate Spot for non-production
- →Implemented auto-scaling
- →Set up lifecycle policies for ECR
- →Optimized task resource allocation
Result:
- →Deployment time reduced from 2 hours to 10 minutes
- →Deployment success rate improved from 70% to 98%
- →Infrastructure costs reduced by 35%
- →Zero-downtime deployments achieved
- →Mean time to recovery decreased by 80%
- →Developer productivity increased by 50%
# Best Practices
## 1. Pipeline Design
- →Use separate pipelines for different environments
- →Implement approval gates for production
- →Enable pipeline notifications
- →Version control all configurations
## 2. Security
- →Use Secrets Manager for sensitive data
- →Enable ECR image scanning
- →Implement least privilege IAM roles
- →Use VPC endpoints for private communication
## 3. Performance
- →Enable build caching
- →Use appropriate compute types
- →Implement parallel stages
- →Optimize Docker images
## 4. Monitoring
- →Enable Container Insights
- →Set up CloudWatch alarms
- →Implement distributed tracing
- →Monitor pipeline metrics
## 5. Cost Optimization
- →Use Fargate Spot when possible
- →Implement auto-scaling
- →Set up lifecycle policies
- →Right-size task resources
# Conclusion
AWS DevOps tools provide a comprehensive, fully managed solution for CI/CD workflows. By leveraging CodePipeline, CodeBuild, and ECS, teams can build scalable, secure, and cost-effective deployment pipelines that integrate seamlessly with the AWS ecosystem.
# Additional Resources
- →AWS CodePipeline Documentation
- →AWS CodeBuild Documentation
- →Amazon ECS Documentation
- →AWS DevOps Blog
Ready to build on AWS? Start with a simple pipeline and gradually add more sophisticated features!