Skip to content
AWS aws deployment 6 min read

AWS CodePipeline (CI/CD)

AWS CodePipeline is a fully managed service that automates the steps your code goes through from a commit to a live release. CI/CD stands for Continuous Integration and Continuous Delivery — the practice of automatically building, testing, and shipping every change so you never deploy by hand. CodePipeline is the orchestrator: it does not build or deploy anything itself, it just wires together other services (like CodeBuild and CodeDeploy) into a repeatable, visual workflow. If you have ever copied a build artifact to a server by hand and forgotten a step, this is the service that removes that risk.

How CodePipeline is structured

A pipeline is a sequence of stages, and each stage contains one or more actions. An action does a single unit of work — pull source code, run a build, deploy to production. Stages run in order, top to bottom. Within one stage, actions can run in parallel (great for fanning out tests) or in sequence (using “run order”).

The most common shape is three stages:

StageWhat it doesTypical action provider
SourceDetects a new commit and grabs the codeGitHub, CodeCommit, S3, ECR
BuildCompiles, runs unit tests, packages an artifactCodeBuild
DeployPushes the artifact to your environmentCodeDeploy, CloudFormation, ECS, Elastic Beanstalk

You can add as many stages as you like — for example a separate Test, Approval, and DeployProd stage.

Why this matters: Each stage hands its results to the next as an artifact (a zipped bundle of files stored in S3). The Source stage produces an output artifact; the Build stage takes that as its input and produces a new one; the Deploy stage consumes that. If those names don’t line up, the pipeline breaks — more on that gotcha below.

When to use CodePipeline (and when not)

Use it when you want a managed, AWS-native CI/CD flow that deploys to AWS targets (ECS, Lambda, EC2, CloudFormation) and you value tight IAM and CloudWatch integration. Do not reach for it if your team already lives in GitHub Actions or GitLab CI and only occasionally touches AWS — running two CI systems adds confusion. CodePipeline shines when AWS is your platform.

Manual approvals

A manual approval action pauses the pipeline until a human clicks “Approve” in the console (or via the CLI). Use it before a production deploy so a person signs off after staging looks healthy. It is a free, zero-config gate. When NOT to use it: in a fully automated continuous deployment flow where speed matters more than a human checkpoint.

A simple pipeline (CloudFormation)

This template defines a three-stage pipeline that pulls from GitHub (via a CodeStar connection), builds with CodeBuild, and deploys a CloudFormation stack — with an approval gate before deploy.

Resources:
  Pipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      Name: my-app-pipeline
      RoleArn: arn:aws:iam::123456789012:role/CodePipelineServiceRole
      ArtifactStore:
        Type: S3
        Location: my-pipeline-artifacts-123456789012
      Stages:
        - Name: Source
          Actions:
            - Name: GitHubSource
              ActionTypeId: { Category: Source, Owner: AWS, Provider: CodeStarSourceConnection, Version: "1" }
              Configuration:
                ConnectionArn: arn:aws:codestar-connections:us-east-1:123456789012:connection/abcd1234
                FullRepositoryId: my-org/my-app
                BranchName: main
              OutputArtifacts: [{ Name: SourceOut }]
        - Name: Build
          Actions:
            - Name: BuildAndTest
              ActionTypeId: { Category: Build, Owner: AWS, Provider: CodeBuild, Version: "1" }
              Configuration: { ProjectName: my-app-build }
              InputArtifacts: [{ Name: SourceOut }]
              OutputArtifacts: [{ Name: BuildOut }]
        - Name: Deploy
          Actions:
            - Name: ApproveDeploy
              ActionTypeId: { Category: Approval, Owner: AWS, Provider: Manual, Version: "1" }
              RunOrder: 1
            - Name: DeployStack
              ActionTypeId: { Category: Deploy, Owner: AWS, Provider: CloudFormation, Version: "1" }
              RunOrder: 2
              InputArtifacts: [{ Name: BuildOut }]
              Configuration:
                ActionMode: CREATE_UPDATE
                StackName: my-app-stack
                TemplatePath: BuildOut::template.yaml
                Capabilities: CAPABILITY_IAM
                RoleArn: arn:aws:iam::123456789012:role/CfnDeployRole

Notice how SourceOut flows into Build, and BuildOut flows into Deploy. Those names are the contract between stages.

Creating a pipeline

Console steps

  1. Open the CodePipeline console and choose Create pipeline.
  2. Name it (e.g. my-app-pipeline), let AWS create a new service role, and accept the default S3 artifact store. Click Next.
  3. Source provider: pick GitHub (via Connect to GitHub), CodeCommit, or S3. Select the repo and branch. Click Next.
  4. Build provider: choose AWS CodeBuild and select (or create) a build project. Click Next.
  5. Deploy provider: choose CodeDeploy, CloudFormation, ECS, or Elastic Beanstalk and fill in the target. Click Next.
  6. Review and click Create pipeline. It runs immediately on the latest commit.

CLI equivalent

Define the pipeline structure in a JSON file and create it:

aws codepipeline create-pipeline --cli-input-json file://pipeline.json

Output:

{
    "pipeline": {
        "name": "my-app-pipeline",
        "roleArn": "arn:aws:iam::123456789012:role/CodePipelineServiceRole",
        "artifactStore": { "type": "S3", "location": "my-pipeline-artifacts-123456789012" },
        "stages": [ ... ],
        "version": 1
    }
}

Manually trigger a run, then check its state:

aws codepipeline start-pipeline-execution --name my-app-pipeline
aws codepipeline get-pipeline-state --name my-app-pipeline

Output:

{
    "stageStates": [
        { "stageName": "Source", "latestExecution": { "status": "Succeeded" } },
        { "stageName": "Build",  "latestExecution": { "status": "InProgress" } },
        { "stageName": "Deploy", "latestExecution": { "status": "Failed" } }
    ]
}

Approve a waiting manual-approval action from the CLI:

aws codepipeline put-approval-result \
  --pipeline-name my-app-pipeline \
  --stage-name Deploy --action-name ApproveDeploy \
  --result summary="staging looks good",status=Approved \
  --token <token-from-get-pipeline-state>

The gotcha: roles, buckets, and artifact names

Two failure patterns cause the overwhelming majority of “it worked locally but the pipeline is red” tickets, and neither is a code problem.

1. The service role is missing a permission. CodePipeline assumes a single IAM role to orchestrate every action. That role needs permission for each thing it touches — codebuild:StartBuild, cloudformation:CreateStack, codedeploy:CreateDeployment, plus s3:GetObject/PutObject on the artifact bucket and kms:Decrypt if the bucket is encrypted with a customer-managed key. Add a deploy provider and forget the IAM line, and the stage fails with an access-denied error that has nothing to do with your application.

2. The artifact handoff doesn’t line up. When a stage reports Cannot find input artifact (or a stage simply has no files), it is almost always because the input artifact name does not exactly match an earlier stage’s output name, or the role can’t read the S3 artifact bucket / KMS key. Check three things in order: the OutputArtifacts/InputArtifacts names match, the role has S3 access to the artifact bucket, and the role has KMS decrypt rights. The code is fine.

Cost note: Your first V1 pipeline per account is free; additional active V1 pipelines are about $1/month each. The real cost is the work CodePipeline triggers — CodeBuild compute minutes and the resources you deploy. The S3 artifact bucket is pennies, but turn on a lifecycle rule to expire old artifacts so it doesn’t grow forever.

Best practices

  • Give the pipeline service role least-privilege permissions — scope S3 and KMS actions to the specific artifact bucket and key, not *.
  • Put a manual approval before any production stage, and notify approvers via an SNS topic.
  • Enable encryption on the artifact bucket and add an S3 lifecycle rule to delete artifacts older than 30 days.
  • Keep build logic in a buildspec.yml inside your repo, not in the pipeline — so the build is versioned with the code.
  • Use CloudWatch Events / EventBridge triggers for source changes instead of polling; it’s faster and avoids API throttling.
  • Define the pipeline as infrastructure as code (CloudFormation or CDK) so it is reviewable and reproducible across accounts.
Last updated June 15, 2026
Was this helpful?