Skip to content

AWS CodePipeline Container Delivery to ECS

CodeBuild with security gates, immutable images and approval-gated ECS releases

Working portfolio implementationImplemented & validated locally

Basis: Portfolio implementation with a synthetic application. Not presented as client work.

Overview

An AWS-native delivery pipeline: CodePipeline V2 pulls from GitHub, CodeBuild tests, builds and scans the image, pushes an immutable SHA tag to ECR, deploys to ECS Fargate staging, and promotes to production after manual approval — with automatic rollback.

Problem

Teams on AWS often have pipelines that rebuild images per environment, push mutable `latest` tags, inject secrets as plaintext build variables and rely on a person to notice a bad deploy. Each of those breaks traceability or safety.

Solution

Build once, promote the same image: every image is tagged with the commit SHA in an immutable ECR repository, scanned before it is pushed, deployed to staging, and only then promoted to production behind an approval. ECS deployment circuit breakers roll a failing release back automatically.

Architecture

  1. Source

    • GitHub

      via CodeConnections

    • CodePipeline V2

  2. Build

    • CodeBuild: test

      unit tests + JUnit reports

    • CodeBuild: build

      docker build, Trivy gate

  3. Artifacts

    • ECR

      immutable SHA tags, KMS

    • imagedefinitions.json

  4. Deploy

    • ECS staging

    • Manual approval

      SNS notification

    • ECS prod

      circuit breaker + rollback

One image per commit flows through both environments; prod requires approval.

Failure and recovery path

  1. A CRITICAL vulnerability fails the Trivy gate; the image is never pushed.
  2. A task that fails its health check trips the ECS deployment circuit breaker, which rolls the service back to the last good task definition.
  3. A missing or malformed secret makes the container refuse to start, so the same circuit breaker rolls back instead of serving with bad config.
  4. Manual rollback redeploys a previous commit SHA — the image still exists because tags are immutable.

Technology stack

  • CodePipeline V2
  • CodeBuild
  • CodeConnections
  • Amazon ECR
  • ECS Fargate
  • Secrets Manager
  • Trivy
  • KMS
  • Terraform
  • Docker

Implementation

  • Two buildspecs (test and build); image tag comes from CODEBUILD_RESOLVED_SOURCE_VERSION, never `latest`.
  • Secrets reach CodeBuild through `env: secrets-manager` with the `secret-id:json-key` form, and reach ECS tasks through the task definition `secrets` block.
  • A stdlib buildspec policy lint enforces the rules in CI: no secret-looking plaintext variables, SHA tags, the scan step before any push, test reports declared, and the `:json-key` suffix on JSON secrets.
  • Small stdlib HTTP service with a multi-stage, non-root Dockerfile and a container health check; verified locally with Docker, read-only root filesystem included.
  • Terraform module and example: ECR, CodeBuild projects, the pipeline with staging, approval and prod stages, an encrypted artifact bucket, and ECS cluster, task definitions and services.
  • 39 unit tests covering the app (health, version, secret never logged), the image-definitions writer and the buildspec lint with eight failing fixtures.

Security

  • Separate least-privilege roles per CodeBuild project; ECR push scoped to one repository, secret reads scoped to specific secret ARNs.
  • One secret and one task execution role per environment, so staging cannot read production credentials.
  • Artifact bucket with SSE-KMS, public access blocked and a TLS-only bucket policy; images scanned on push as well as in the build.
  • Access logs drop query strings so tokens in URLs are never written to logs.

Reliability

  • ECS deployment circuit breaker with automatic rollback in both environments.
  • Graceful shutdown on SIGTERM so rolling deployments drain cleanly.
  • Staging receives every build first; production only ever runs an image that has already passed staging.

Cost

  • CodePipeline V2 bills per action execution minute and CodeBuild per build minute — short, cached builds keep both small (figures in the project's cost doc are illustrative).
  • ECR lifecycle policy expires untagged and old images so storage does not grow without bound.

Results

No quantified results are published for this project. Figures from client work are confidential, and this portfolio only shows numbers that can be verified or reproduced.

Code & documentation

Lessons learned

  • Build once and promote: rebuilding per environment means production runs something staging never tested.
  • Referencing a JSON secret without `:json-key` silently injects the whole JSON blob — worth a lint rule rather than a code review comment.
  • Known gaps, stated in the README: no load balancer or image signing yet, HIGH findings don't fail the build, and nothing has run in a real AWS account.