5. Set Up CI/CD Pipeline for ECS with AWS CodePipeline
Goal
Create an automated CI/CD pipeline that builds Docker images and deploys them to ECS whenever you push code to GitHub.
What you’ll learn:
- How to set up GitHub integration with AWS CodeStar Connections
- How to configure CodeBuild for multi-architecture Docker builds
- How to create CodePipeline with Source, Build, and Deploy stages
- Best practices for IAM roles and automated ECS deployments
Table of Contents
Prerequisites
Before starting, ensure you have:
- ✓ AWS account with administrative access
- ✓ GitHub repository with Dockerfile and buildspec.yml
- ✓ ECS cluster and service already deployed
- ✓ Basic understanding of CI/CD concepts
Exercise Steps
Overview
- Create ECR Repository
- Create GitHub Connection
- Create S3 Bucket for Artifacts
- Create IAM Roles
- Create CodeBuild Project
- Create CodePipeline
- Verify Pipeline
Step 1: Create ECR Repository
Create a private Docker registry to store your application images.
Navigate to ECR → Repositories
Click Create repository
Select Private
Enter repository name:
my-ecs-php-appEnable “Scan on push”
Enable “Tag immutability” (optional, for production)
Click Create repository
Note down the repository URI (e.g.,
123456789.dkr.ecr.us-east-1.amazonaws.com/my-ecs-php-app)
ℹ Concept Deep Dive
ECR (Elastic Container Registry) is AWS’s private Docker registry. It integrates seamlessly with ECS and CodeBuild, providing secure image storage with vulnerability scanning.
Scan on push automatically scans images for CVEs (security vulnerabilities) when pushed.
✓ Quick check: ECR repository created with scan-on-push enabled
Step 2: Create GitHub Connection
Set up secure GitHub integration using CodeStar Connections.
Navigate to Developer Tools → Connections
Click Create connection
Select GitHub
Enter connection name:
github-connectionClick Connect to GitHub
Click “Install a new app” (or select existing GitHub App)
Authorize AWS Connector for GitHub on your GitHub account
Select repositories to grant access (select your PHP app repo)
Click Install
Click Connect
Note down the connection ARN (e.g.,
arn:aws:codestar-connections:...)
ℹ Concept Deep Dive
CodeStar Connections provide secure, token-based authentication with GitHub without storing credentials. The connection uses a GitHub App that AWS manages.
Connection status must be “Available” before use. If pending, complete the GitHub authorization.
âš Common Mistakes
- Not completing GitHub authorization - connection stays in “Pending” state
- Not granting repository access - pipeline can’t access source code
✓ Quick check: Connection status shows “Available”
Step 3: Create S3 Bucket for Artifacts
Create an S3 bucket to store pipeline artifacts (source code and build outputs).
Navigate to S3 → Buckets
Click Create bucket
Enter bucket name:
dev-pipeline-artifacts-<your-account-id>(must be globally unique)Select your AWS region
Leave “Block all public access” enabled
Enable “Bucket Versioning”
Click Create bucket
ℹ Concept Deep Dive
Artifact bucket stores zip files containing source code between pipeline stages. CodePipeline automatically manages uploads/downloads.
Versioning allows rollback to previous pipeline executions if needed.
✓ Quick check: S3 bucket created with versioning enabled
Step 4: Create IAM Roles
Create IAM roles for CodePipeline and CodeBuild with appropriate permissions.
Create CodePipeline Service Role
Navigate to IAM → Roles → Create role
Select “AWS service” → “CodePipeline”
Click Next (policy is auto-attached)
Enter role name:
dev-codepipeline-service-roleClick Create role
Click on the created role
Click Add permissions → Attach policies
Search and attach
AmazonECS_FullAccessClick Add permissions → Create inline policy
Select JSON tab and paste:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:PutObject",
"s3:GetBucketLocation",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::dev-pipeline-artifacts-*",
"arn:aws:s3:::dev-pipeline-artifacts-*/*"
]
},
{
"Effect": "Allow",
"Action": [
"codebuild:BatchGetBuilds",
"codebuild:StartBuild"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"codestar-connections:UseConnection"
],
"Resource": "*"
}
]
}
Enter policy name:
CodePipelineCustomPolicyClick Create policy
Create CodeBuild Service Role
Click Create role again
Select “AWS service” → “CodeBuild”
Click Next
Enter role name:
dev-codebuild-service-roleClick Create role
Click on the created role
Click Add permissions → Create inline policy
Select JSON tab and paste:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"ecr:PutImage",
"ecr:InitiateLayerUpload",
"ecr:UploadLayerPart",
"ecr:CompleteLayerUpload"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::dev-pipeline-artifacts-*/*"
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
Enter policy name:
CodeBuildCustomPolicyClick Create policy
ℹ Concept Deep Dive
CodePipeline role needs permissions to:
- Access S3 artifacts
- Trigger CodeBuild builds
- Use CodeStar connections
- Deploy to ECS (via AmazonECS_FullAccess)
CodeBuild role needs permissions to:
- Push/pull images from ECR
- Access S3 artifacts
- Write CloudWatch logs
✓ Quick check: Two roles created with inline policies attached
Step 5: Create CodeBuild Project
Create a CodeBuild project that builds multi-architecture Docker images.
Navigate to CodeBuild → Build projects
Click Create build project
Enter project name:
dev-php-app-buildUnder Source:
- Source provider:
AWS CodePipeline
- Source provider:
Under Environment:
- Environment image:
Managed image - Operating system:
Ubuntu - Runtime:
Standard - Image:
aws/codebuild/standard:7.0 - Image version:
Always use the latest - Enable “Privileged” (required for Docker builds)
- Service role:
Existing service role - Role name:
dev-codebuild-service-role
- Environment image:
Under Environment variables, add:
- Name:
AWS_ACCOUNT_ID, Value:<your-account-id>, Type:Plaintext - Name:
AWS_DEFAULT_REGION, Value:<your-region>, Type:Plaintext - Name:
IMAGE_REPO_NAME, Value:my-ecs-php-app, Type:Plaintext - Name:
CONTAINER_NAME, Value:php-app-container, Type:Plaintext
- Name:
Under Buildspec:
- Build specifications:
Use a buildspec file - Buildspec name:
buildspec.yml
- Build specifications:
Under Artifacts:
- Type:
AWS CodePipeline
- Type:
Under Logs:
- Check “CloudWatch logs”
- Group name:
/aws/codebuild/dev-php-app-build
Click Create build project
ℹ Concept Deep Dive
Privileged mode is required for Docker-in-Docker builds (building Docker images inside a Docker container).
Environment variables are injected into the build environment and used by buildspec.yml to configure the build.
buildspec.yml must exist in your repository root with build instructions.
âš Common Mistakes
- Forgetting to enable Privileged mode - Docker builds will fail
- Wrong environment variable names - buildspec.yml expects specific names
- Using wrong CodeBuild image - must support Docker and buildx
✓ Quick check: Build project created with privileged mode enabled
Step 6: Create CodePipeline
Create the pipeline that orchestrates Source → Build → Deploy stages.
Navigate to CodePipeline → Pipelines
Click Create pipeline
Enter pipeline name:
dev-php-app-pipelineUnder Service role:
- Role name:
Existing service role - Select
dev-codepipeline-service-role
- Role name:
Under Artifact store:
- Select
Custom location - Bucket:
dev-pipeline-artifacts-<your-account-id>
- Select
Click Next
Configure Source Stage
Under Source provider:
GitHub (Version 2)Select connection:
github-connectionEnter repository name:
<owner>/<repo>(e.g.,larsappel/my-ecs-php-app)Select branch:
mainSelect output artifact format:
CodePipeline defaultClick Next
Configure Build Stage
Select build provider:
AWS CodeBuildSelect region: (your region)
Select project name:
dev-php-app-buildLeave build type as
Single buildClick Next
Configure Deploy Stage
Select deploy provider:
Amazon ECSSelect cluster name:
dev-cluster(your existing cluster)Enter service name:
dev-php-app-alb-service(your existing service)Enter image definitions file:
imagedefinitions.jsonClick Next
Review all settings
Click Create pipeline
Pipeline will automatically start - let it run
ℹ Concept Deep Dive
Pipeline stages:
- Source: Pulls code from GitHub when commits are pushed
- Build: Runs CodeBuild to create Docker image and push to ECR
- Deploy: Updates ECS service with new image from imagedefinitions.json
imagedefinitions.json is generated by buildspec.yml and tells ECS which container to update with which image.
Automatic triggers: Pipeline runs automatically on every push to the configured branch.
âš Common Mistakes
- Wrong cluster or service name - deploy will fail
- Image definitions file name typo - must be exactly
imagedefinitions.json- Forgetting to push buildspec.yml to repo - build will fail
✓ Quick check: Pipeline created and first execution started
Step 7: Verify Pipeline
Verify that the pipeline successfully builds and deploys your application.
Check Pipeline Execution
Navigate to CodePipeline → Pipelines →
dev-php-app-pipelineMonitor the execution progress:
- Source stage should succeed quickly (pulling from GitHub)
- Build stage takes 3-5 minutes (building Docker images)
- Deploy stage takes 1-2 minutes (updating ECS service)
If any stage fails, click Details to see error messages
Check Build Logs
Click Details on Build stage
Click Link to execution details
Click Build logs
Verify Docker build completed and images pushed to ECR
Check ECR Images
Navigate to ECR → Repositories →
my-ecs-php-appVerify images with tags: commit hash, latest, and build number
Check ECS Deployment
Navigate to ECS → Clusters →
dev-cluster→ Services →dev-php-app-alb-serviceClick Deployments tab
Verify new deployment is running
Click Tasks tab
Verify new tasks are using the new image
Test Application
Navigate to EC2 → Load Balancers →
dev-php-app-albCopy DNS name
Open browser and navigate to
http://<alb-dns>Verify application is running with latest code
ℹ Concept Deep Dive
Pipeline execution ID uniquely identifies each run. You can view history of all executions.
Zero-downtime deployment: ECS automatically starts new tasks, waits for health checks, then stops old tasks.
Rollback: If deployment fails, ECS keeps old tasks running and circuit breaker can automatically roll back.
âš Common Mistakes
- Not waiting for health checks - deployment appears to fail but is actually pending
- Checking old tasks - new tasks take 2-3 minutes to become healthy
- Testing before deployment completes - ALB might route to old tasks
✓ Success indicators:
- ✓ All pipeline stages show “Succeeded”
- ✓ New images in ECR with multiple tags
- ✓ ECS service deployment completed
- ✓ New tasks running with new image
- ✓ Application accessible via ALB
Common Issues
“Source stage fails with connection error”:
- Verify CodeStar connection status is “Available”
- Check GitHub App has access to the repository
- Ensure repository name format is correct (owner/repo)
“Build stage fails with Docker permission denied”:
- Verify CodeBuild project has Privileged mode enabled
- Check CodeBuild role has ECR permissions
- Ensure buildspec.yml exists in repository root
“Build fails with ‘Commands[X] subkeys error’”:
- Check buildspec.yml for YAML syntax errors
- Avoid colons (
:) in echo statements - use hyphens (-) instead- Ensure proper indentation in buildspec.yml
“Deploy stage fails with permission error”:
- Verify CodePipeline role has ECS permissions (AmazonECS_FullAccess)
- Check cluster and service names match exactly
- Ensure ECS service exists and is active
“Deployment succeeds but old image still running”:
- Check imagedefinitions.json container name matches task definition
- Verify ECS service is actually updating (check Deployments tab)
- Wait for health checks to pass (can take 2-3 minutes)
Summary
You’ve successfully created an automated CI/CD pipeline that:
- ✓ Monitors GitHub for code changes
- ✓ Builds multi-architecture Docker images automatically
- ✓ Pushes images to ECR with proper tagging
- ✓ Deploys to ECS with zero downtime
- ✓ Provides full build/deploy visibility in CloudWatch
Key takeaway:
AWS-native CI/CD with CodePipeline, CodeBuild, and ECR provides fully managed continuous deployment with automatic triggers, artifact management, and ECS integration. This is production-ready infrastructure for containerized applications.
Done! 🎉
You’ve learned how to set up a complete CI/CD pipeline for ECS applications. Now every push to your GitHub repository automatically builds and deploys to your running ECS service!
To trigger a deployment:
- Make a code change in your repository
- Commit and push to the main branch
- Watch the pipeline automatically execute
- Verify deployment in ECS
To clean up:
- Delete CodePipeline
- Delete CodeBuild project
- Delete ECR repository (and images)
- Delete S3 bucket
- Delete CodeStar connection
- Delete IAM roles