AWS Developer Tools (CodePipeline, CodeBuild, CodeDeploy)
These are three separate, narrowly-scoped AWS services — CodePipeline orchestrates, CodeBuild builds and tests, CodeDeploy ships — and this page covers them together for the same reason most real AWS accounts run them together: almost nobody uses exactly one of the three. A pipeline without a build stage does nothing; a build without somewhere to deploy the result is just a container image sitting in a registry. This page is the practitioner's view — the actual JSON and YAML you write, the IAM roles that stitch the three services together, the CLI commands you run at 2 a.m., and the failure modes that eat an afternoon the first time you hit them. It is deliberately paired with SDLC Automation, which covers the exact same three services from the DOP-C02 exam's Domain 1 angle — read that page for what the exam asks, read this one for what you'd actually type.
Picture a small factory with three specialists who each do exactly one job and refuse to do anyone else's. The dispatcher (CodePipeline) doesn't build or ship anything — she just watches for a new order to come in, and tells the next specialist in line that it's their turn, passing along the box each one leaves behind. The assembler (CodeBuild) reads one recipe card, builds exactly what it says, and tears down their whole workbench the second they're done — a fresh bench, every single time. The delivery driver (CodeDeploy) doesn't build anything either — they just know eleven different careful ways to swap the old box for the new one on the shelf without ever making a customer notice the swap happening. None of the three would be useful alone. Together, dispatched in order, they're the whole factory line.
What the suite is, and what's actually still called what
☺ Like you're 10: Three separate tools that happen to share a naming prefix — and one of the family members that used to live here moved out in 2024, so don't go looking for it.
"AWS Developer Tools" is AWS's umbrella label for the family of services with a Code-prefixed name — CodePipeline, CodeBuild, CodeDeploy, CodeArtifact, and (formerly) CodeCommit and CodeStar. Each one does exactly one job in the delivery pipeline and nothing else: CodePipeline is a release orchestrator with no compute of its own, CodeBuild is a managed container that runs your build and disappears, and CodeDeploy is a deployment engine that knows nothing about how the thing it's deploying got built. That narrowness is the whole design philosophy — compose three single-purpose services instead of shipping one do-everything platform — and it maps directly onto the generic pipeline stages this course already taught in CI/CD pipelines, build & artifact management, and deployment strategies. This page assumes you've read those; it's the AWS-specific "and here's the config" that follows.
CodeCommit, AWS's own Git hosting service, stopped onboarding new customers in 2024; existing repositories keep working, but no new pipeline should be designed around it, and most now point at GitHub, GitLab, or Bitbucket through a connection resource instead. CodeStar, the older project-scaffolding service that used to sit alongside this suite, has likewise been deprecated. AWS's newer answer to "one unified place to build software" is CodeCatalyst, launched in 2023 — but as of this writing, CodePipeline, CodeBuild, and CodeDeploy remain independently supported, separately billed, actively developed services in their own right; they are not folded into CodeCatalyst, and CodeCatalyst has not stayed still since launch either. Treat every specific service name and every claim of "current" positioning on this page as true at time of writing, and check AWS's own Developer Tools documentation before a real design — or an exam answer — leans on any of it.
Architecture: how the three services actually relate
☺ Like you're 10: One box tells the other two when it's their turn, a shelf in the middle holds whatever gets passed between them, and the last box can hand the finished thing to three different kinds of shelf depending on what you're running.
CodePipeline sits in the middle of the flow but does the least actual work of the three. It has no build capability and no deployment capability of its own — every action inside a stage names a provider (CodeBuild, CodeDeploy, CloudFormation, S3, a manual approval, or a third party like Jenkins) and CodePipeline's entire job is starting that provider at the right moment and handing it the right artifact. That artifact — literally a zip of files — moves between stages through one S3 bucket CodePipeline manages per pipeline, the artifact store, which should be KMS-encrypted with a customer-managed key the moment more than one AWS account is involved.
Notice the diagram has three distinct service roles, not one shared role. That's deliberate, and it's the single most important architectural fact on this page: the pipeline's own role only needs enough permission to start each action's provider and read/write the artifact bucket — it does not need CodeBuild's permissions to pull from ECR, and it does not need CodeDeploy's permissions to touch an Auto Scaling Group. Collapsing all three into one over-privileged role is the most common security mistake teams make setting this up, and it's covered further in Gotchas and failure modes below.
CodePipeline: stages, actions, and the artifact store
☺ Like you're 10: A pipeline is a numbered list of stops, each stop has one or more jobs to do, and the jobs inside one stop can happen at the same time if you tell them to.
A pipeline is a list of stages that run strictly in order — a stage cannot start until every action in the previous stage has succeeded. Inside a stage, one or more actions run, and actions that share the same numeric runOrder run in parallel; actions with different run orders run sequentially within that one stage. This is the actual object CodePipeline stores — the shape you get back from aws codepipeline get-pipeline and the shape Terraform's aws_codepipeline resource and CloudFormation's AWS::CodePipeline::Pipeline both mirror closely:
{
"pipeline": {
"name": "checkout-pipeline",
"pipelineType": "V2",
"roleArn": "arn:aws:iam::111122223333:role/checkout-pipeline-role",
"artifactStore": {
"type": "S3",
"location": "codepipeline-checkout-artifacts-111122223333",
"encryptionKey": { "id": "arn:aws:kms:us-east-1:111122223333:key/checkout-artifacts", "type": "KMS" }
},
"stages": [
{
"name": "Source",
"actions": [{
"name": "GitHub_Source",
"actionTypeId": { "category": "Source", "owner": "AWS", "provider": "CodeStarSourceConnection", "version": "1" },
"configuration": {
"ConnectionArn": "arn:aws:codeconnections:us-east-1:111122223333:connection/abc-123",
"FullRepositoryId": "acme/checkout-service",
"BranchName": "main"
},
"outputArtifacts": [{ "name": "SourceOutput" }],
"runOrder": 1
}]
},
{
"name": "Build",
"actions": [{
"name": "CodeBuild_Build",
"actionTypeId": { "category": "Build", "owner": "AWS", "provider": "CodeBuild", "version": "1" },
"configuration": { "ProjectName": "checkout-build" },
"inputArtifacts": [{ "name": "SourceOutput" }],
"outputArtifacts": [{ "name": "BuildOutput" }],
"runOrder": 1
}]
},
{
"name": "Deploy",
"actions": [
{
"name": "ManualApproval",
"actionTypeId": { "category": "Approval", "owner": "AWS", "provider": "Manual", "version": "1" },
"configuration": { "NotificationArn": "arn:aws:sns:us-east-1:111122223333:pipeline-approvals" },
"runOrder": 1
},
{
"name": "CodeDeploy_Deploy",
"actionTypeId": { "category": "Deploy", "owner": "AWS", "provider": "CodeDeploy", "version": "1" },
"configuration": { "ApplicationName": "checkout", "DeploymentGroupName": "checkout-prod" },
"inputArtifacts": [{ "name": "BuildOutput" }],
"runOrder": 2
}
]
}
]
}
}A few things worth reading twice in that shape: the manual approval action and the CodeDeploy action share the Deploy stage but have different runOrder values, so approval genuinely blocks deployment rather than merely running alongside it — that's the AWS-native implementation of the human gate CI/CD pipelines draws between continuous delivery and continuous deployment. The ConnectionArn lives under the newer codeconnections namespace even though the action provider is still literally named CodeStarSourceConnection for backward compatibility — a naming fossil from before the CodeStar deprecation, and exactly the kind of detail worth double-checking rather than pattern-matching from an old example. And every action's inputArtifacts/outputArtifacts names must chain correctly stage to stage — SourceOutput feeds Build, BuildOutput feeds Deploy — because that chaining, not anything implicit, is what guarantees the exact bits that passed the Build stage are the ones that reach Deploy, the "build once, promote everywhere" rule from build & artifact management.
For pipelines that span accounts or regions — a shared "tooling" account running the pipeline against separate dev/staging/prod accounts, the pattern most platform teams converge on — each cross-account action needs its own action role in the target account, distinct from the pipeline's own role, and the artifact bucket's KMS key policy has to explicitly trust every account involved. This is the single fiddliest piece of CodePipeline to set up correctly the first time; see Scaling CI/CD Across Teams for the organizational pattern this supports.
CodeBuild: buildspec.yml, compute, and caching
☺ Like you're 10: A fresh, empty computer wakes up, reads a four-step recipe card, does exactly what it says, and then disappears completely — nothing about it carries over to the next build unless you explicitly ask for that.
A CodeBuild project runs one buildspec.yml — checked into the repo root, supplied inline in the project config, or pulled from S3 — through four ordered phases, each of which can declare an on-failure policy (ABORT, the default, or CONTINUE) and a finally block that runs regardless of whether the phase's main commands succeeded, the shell equivalent of a try/finally:
# buildspec.yml
version: 0.2
env:
parameter-store:
DB_PASSWORD: /checkout/prod/db-password # SSM Parameter Store — good for config, not high-security secrets
secrets-manager:
API_KEY: checkout/prod/api-key:key # Secrets Manager — versioned, rotatable, the stronger default
variables:
NODE_ENV: production
phases:
install:
runtime-versions: { nodejs: 20 }
pre_build:
commands:
- npm ci
- npm run lint
build:
on-failure: ABORT
commands:
- npm test -- --reporters=jest-junit
- docker build -t $REPO_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION .
finally:
- echo "build phase finished at $(date)" >> /tmp/build.log # always runs, pass or fail
post_build:
commands:
- docker push $REPO_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION
- printf '[{"name":"checkout","imageUri":"%s"}]' "$REPO_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION" > imagedefinitions.json
reports:
jest-reports:
files: [ junit.xml ]
file-format: JUNITXML
cache:
paths:
- 'node_modules/**/*'
- '/root/.cache/**/*'
artifacts:
files: [ imagedefinitions.json ]Four settings on the CodeBuild project (not the buildspec) come up constantly in practice: compute type ranges from small general-purpose instances up through large ones, plus a Lambda-based compute option that starts faster and bills cheaper for short builds, and ARM/Graviton-based compute for native arm64 image builds; privileged mode, required the moment the build itself runs docker build — CodeBuild's own container needs Docker-in-Docker access, and forgetting this flag is the most common reason a perfectly correct Dockerfile fails only inside CodeBuild; VPC configuration, required when a build needs to reach something private — an RDS instance, an internal package mirror — at the direct cost of needing VPC endpoints (S3, STS, and whichever service the build calls) or a NAT gateway, since a build container in a private subnet with neither simply times out reaching the public internet; and batch builds, which fan one trigger out into a graph of related builds with explicit dependencies between them:
# buildspec.yml — batch build graph: two builds run in parallel, a third waits on both
version: 0.2
batch:
fast-fail: true
build-graph:
- identifier: build_linux
buildspec: buildspec-linux.yml
- identifier: build_windows
buildspec: buildspec-windows.yml
- identifier: integration_test
depend-on: [build_linux, build_windows]
buildspec: buildspec-test.ymlCodeBuild ships a local agent: a Docker image plus a shell script (codebuild_build.sh, from the aws/aws-codebuild-docker-images repository on GitHub) that runs a real buildspec against a real CodeBuild build image, entirely on your laptop. ./codebuild_build.sh -i codebuild/local:latest -a /tmp/artifacts -s ~/checkout gives you the exact same phase-by-phase behavior you'd get in the cloud, minutes faster than push-wait-check-console-repeat, and it's the fastest way to find out whether a buildspec bug is your YAML or your actual test suite.
CodeDeploy: deployment groups, appspec.yml, and deployment configs
☺ Like you're 10: The same swap-the-old-box-for-the-new-one job, just with a different rulebook depending on whether you're swapping boxes on physical shelves, inside a function, or inside a container.
Every CodeDeploy deployment targets a deployment group, and a deployment group's target type — EC2/on-premises instances (by tag or by Auto Scaling Group), a Lambda function, or an ECS service — decides which appspec shape and which deployment configs are even legal. For EC2/on-premises, every target instance must be running the CodeDeploy agent (a Ruby-based background process, installed and started separately from anything CodePipeline does) with an instance profile that can read the deployment revision from S3; a deployment against an instance whose agent is stopped or too old simply fails, and the only useful signal is on the instance itself, in /var/log/aws/codedeploy-agent/codedeploy-agent.log — the CodePipeline and CodeDeploy consoles show "failed," not why.
The revision bundle's root appspec.yml for EC2/on-premises maps files into place and defines hooks — named lifecycle points, each running one or more scripts, each with its own timeout and the OS user it runs as:
# appspec.yml — EC2/on-premises
version: 0.0
os: linux
files:
- source: /
destination: /var/www/checkout
permissions:
- object: /var/www/checkout
pattern: "**"
owner: www-data
group: www-data
mode: '0755'
hooks:
ApplicationStop:
- location: scripts/stop_server.sh
timeout: 30
runas: root
BeforeInstall:
- location: scripts/install_dependencies.sh
timeout: 180
AfterInstall:
- location: scripts/configure_app.sh
timeout: 60
ApplicationStart:
- location: scripts/start_server.sh
timeout: 60
runas: root
ValidateService:
- location: scripts/health_check.sh
timeout: 60Two hooks that are conspicuously absent from that list — DownloadBundle and Install — are reserved: CodeDeploy runs them itself, and a custom script can't be attached to either. When a target group sits behind a load balancer, a blue/green deployment adds a second pair of hooks around traffic itself — BeforeAllowTraffic and AfterAllowTraffic on the new instances, BeforeBlockTraffic and AfterBlockTraffic on the ones being replaced — so a validation script can run before a single real request reaches the new code. The exact ordering differs between in-place and blue/green deployments; treat the sequence above as broadly right and check AWS's own AppSpec hooks reference for the authoritative order before you build a script that depends on hook N running strictly before hook N+1.
Deployment behavior for EC2/on-premises is governed by a deployment config: CodeDeployDefault.AllAtOnce (fastest, zero fault tolerance), HalfAtATime, OneAtATime (slowest, most conservative), or a custom minimum-healthy-host percentage. Lambda and ECS don't get that in-place/blue-green choice — blue/green is effectively the only real option — and instead pick a traffic-shifting shape: AllAtOnce, linear (a fixed percentage every fixed interval, e.g. CodeDeployDefault.LambdaLinear10PercentEvery1Minute), or canary (one jump, a hold, then the rest, e.g. CodeDeployDefault.LambdaCanary10Percent5Minutes). Automatic rollback on either platform only fires when a CloudWatch alarm is explicitly attached to that specific deployment group's rollback configuration — an alarm that exists elsewhere in the account does nothing for a deployment it was never wired to.
| Compute platform | Deployment model | What you must provide |
|---|---|---|
| EC2 / on-premises | In-place (config-governed) or blue/green (new instance set) | CodeDeploy agent running on every target; an instance profile that can pull the revision from S3; appspec.yml with file mappings and hooks |
| Lambda | Traffic shifting between two function versions behind one weighted alias — never against raw ARNs or $LATEST | An alias already pointing at the current version; an appspec.yml naming the function, alias, and target version; optional BeforeAllowTraffic/AfterAllowTraffic validation functions |
| ECS | Blue/green via a second task set behind a second target group, optionally proven against a test listener first | Two target groups on the load balancer; an appspec.yml naming the task definition and container/port; the CodeDeploy service role granted ECS and ELB permissions |
(Amazon ECS has since added a native blue/green deployment capability that doesn't route through CodeDeploy at all — one more naming and ownership detail worth confirming against current documentation rather than assuming CodeDeploy is the only path for an ECS service today.)
Day-to-day commands
☺ Like you're 10: Nine commands cover almost everything you'll type by hand: start it, check on it, approve it, and — if it goes wrong — stop it.
# CodePipeline
$ aws codepipeline start-pipeline-execution --name checkout-pipeline
$ aws codepipeline get-pipeline-state --name checkout-pipeline
$ aws codepipeline put-approval-result --pipeline-name checkout-pipeline \
--stage-name Deploy --action-name ManualApproval \
--result summary="Looks good, ship it",status=Approved --token <approval-token>
# CodeBuild
$ aws codebuild start-build --project-name checkout-build \
--environment-variables-override name=IMAGE_TAG,value=abc1234,type=PLAINTEXT
$ aws codebuild batch-get-builds --ids checkout-build:1a2b3c4d-5678-90ab-cdef
$ aws codebuild list-builds-for-project --project-name checkout-build
# CodeDeploy
$ aws deploy create-deployment --application-name checkout --deployment-group-name checkout-prod \
--s3-location bucket=checkout-artifacts,key=checkout/build.zip,bundleType=zip
$ aws deploy get-deployment --deployment-id d-ABCDEF123
$ aws deploy list-deployment-instances --deployment-id d-ABCDEF123
$ aws deploy stop-deployment --deployment-id d-ABCDEF123 --auto-rollback-enabled # the rollback buttonGotchas and failure modes
☺ Like you're 10: Almost every "why did this fail" moment traces back to one of three things: a permission nobody granted, a network path nobody opened, or a role that has more power than it needs.
One role per service, on purpose. The pipeline role, the CodeBuild service role, and the CodeDeploy service role are three separate IAM identities with three separate, narrow permission sets. The most common real-world mistake is collapsing them — or worse, attaching a broad managed policy to all three "just to make the errors stop" — because a genuine permissions error from CodePipeline is often an opaque "action execution failed" with the real IAM denial buried in CloudTrail, not the console. Diagnose the actual denied action before widening anything, and scope each role down again once you find it.
CodeBuild in a VPC needs a way out, or it just hangs. A build container placed in a private subnet with no NAT gateway and no VPC endpoints for S3, STS, and whatever service the build calls will time out reaching them — this is the single most common "my build just sits there" report, and the fix is either a NAT gateway or the specific VPC endpoints the build actually needs, not a wider security group.
The buildspec source is exclusive, not additive. A CodeBuild project resolves exactly one buildspec — the file in the repo, an inline override, or an S3 location — and an override completely replaces the file version rather than merging with it. Expecting an inline override to layer on top of a checked-in buildspec.yml is a fast way to lose phases you thought were still running.
Local build caching is opportunistic, not reliable. CodeBuild's LOCAL_DOCKER_LAYER_CACHE and LOCAL_SOURCE_CACHE modes only help when a build happens to land on the same underlying host as a previous one, which CodeBuild does not guarantee. For consistent caching across genuinely ephemeral hosts, use the S3-backed cache.paths shown above instead — and remember an S3 cache that's never invalidated on a dependency-file change can just as easily serve a stale node_modules silently, so key it off a lockfile hash if staleness would actually hurt you.
Cross-account pipelines need trust in three places at once. The pipeline's artifact-bucket KMS key policy, the target account's action role, and that role's trust relationship back to the pipeline account all have to agree — miss any one and the failure surfaces as a generic access-denied on the deploy action, several steps removed from the actual misconfigured resource.
In a sandbox AWS account: create a CodeBuild project pointed at a throwaway repo with the buildspec above, and run it once with aws codebuild start-build. Watch it succeed, then deliberately break it two ways — first remove privileged: true from a project that runs docker build and watch the exact failure message, then point the project at a VPC subnet with no NAT and no endpoints and watch it hang instead of failing fast. Two very different failure shapes for two very different missing pieces, and knowing which shape means which cause is most of the debugging skill this page is teaching.
Alternatives and when to choose the AWS-native suite
☺ Like you're 10: If you're already deep in AWS, staying inside one company's tools for the whole trip is simpler — the moment you need to leave AWS, or want one product instead of three, that trade flips.
CodePipeline and CodeBuild compete for the same job as Jenkins, GitHub Actions, GitLab CI/CD, and CircleCI — orchestrating and running the build. CodeDeploy competes more narrowly with dedicated deployment orchestrators like Argo CD and Spinnaker for the deploy step specifically. The honest comparison usually isn't feature-for-feature; it's about how much of your stack is already AWS.
| Option | Model | Best when | Costs you |
|---|---|---|---|
| AWS Developer Tools (this page) | Three narrow managed services, deep native IAM and CloudWatch integration, no server to run | Already all-in on AWS; want IAM roles instead of a separate OIDC federation story; a compliance boundary that must stay inside one AWS account or org | Three services and three roles to wire together instead of one product; smaller third-party integration ecosystem than GitHub Actions' marketplace; three separate YAML/JSON dialects (buildspec, appspec, pipeline definition) that only exist here |
| Jenkins | Self-hosted controller + agents, ~1,800+ plugins | Multi-cloud or on-prem builds; hardware or network access no managed runner reaches | You run and patch Jenkins itself |
| GitHub Actions | SaaS, hosted runners, YAML workflows in the repo | Already on GitHub; want zero infrastructure and a huge marketplace of reusable actions | Tied to GitHub as the SCM; AWS access needs OIDC federation configured explicitly |
| GitLab CI/CD | SaaS or self-managed, tightly coupled to GitLab's SCM and built-in scanners | Already on GitLab; want SAST/DAST in the same product as the pipeline | Self-managed GitLab is its own operational commitment |
| CircleCI | SaaS-first, reusable orbs | Fast setup, polished UI, no interest in running or owning cloud-specific config | Usage-based pricing at scale; less native AWS IAM depth than the AWS-native path |
A useful rule of thumb: reach for CodePipeline/CodeBuild/CodeDeploy when the deciding factor is staying inside one IAM boundary — no cross-cloud OIDC trust to configure, no separate vendor to grant AWS credentials to, deployment permissions that are just another IAM policy instead of a federated identity. Reach for a SaaS CI product when the deciding factor is a single unified product, a bigger ecosystem, or a genuinely multi-cloud target. Most large AWS-centric organizations that already run one of the SaaS products still keep CodeDeploy specifically in the mix for its deep native EC2/Lambda/ECS traffic-shifting behavior, even when CodePipeline and CodeBuild have been replaced by something else — the three services don't have to be adopted or abandoned as a set.
Benny the Beaver: Build's been sitting at "PROVISIONING" for six minutes. That's not a code problem — that's a network problem.
Nutty the Squirrel: Check the project's VPC config. If it's got subnets but no NAT gateway and no S3 endpoint, it's trying to reach the internet from a room with no door.
Benny: ...that's exactly it. Added the endpoint, reran it, thirty seconds. Should've checked that before I re-read the buildspec three times.
Gizmo: Or just slap AdministratorAccess on the CodeBuild role. No more permission errors, ever. 🤑
Timmy the Turtle: And no more knowing what that role can actually do, either. Find the one denied action in CloudTrail and grant exactly that. It takes longer today and saves you an incident review later.
Timmy: Speaking of which — the Lambda deploy is queued behind mine. Canary10Percent5Minutes, alarm watched, then the rest. Nobody's skipping the hold.
Foxy: Three services, three roles, three YAML dialects. Why not just one big tool that does all of it?
Benny: Because the day one of them needs replacing — say we outgrow CodeBuild's compute options — I only rip out one narrow piece. Not the whole pipeline.
1. Why does this page cover CodePipeline, CodeBuild, and CodeDeploy together instead of as three separate pages? 2. In the pipeline JSON shown, what makes the manual approval action genuinely block the CodeDeploy action instead of running alongside it? 3. Name the four CodeBuild buildspec phases in order, and what a phase's finally block is for. 4. A CodeBuild project needs to run docker build inside its own build container — which project setting does that require, and what's the other common reason a VPC-attached build simply hangs? 5. Name the three CodeDeploy compute platforms and, for each, what "deployment model" actually means for it. 6. Why shouldn't the pipeline role, the CodeBuild role, and the CodeDeploy role be collapsed into one broad role? 7. Name one thing that's already changed in this suite's lineup since it launched, and what AWS service that change points toward.
Check your answers
- Because almost no real pipeline uses only one of the three — CodePipeline needs something to orchestrate, and that's normally CodeBuild for the build and CodeDeploy for the ship, so the practical unit of understanding is the trio, not any one service in isolation.
- The two actions sit in the same stage but with different
runOrdervalues (1 for approval, 2 for deploy) — CodePipeline runs actions with the same run order in parallel and different run orders sequentially, so the deploy action genuinely waits for the approval action to resolve first. - install (runtime setup), pre_build (dependency install, lint), build (compile, test, image build), post_build (push, notify) — always in that order. A
finallyblock runs regardless of whether that phase's main commands succeeded, the shell equivalent of a try/finally, useful for cleanup or always-log-this steps. - Privileged mode — required for Docker-in-Docker inside the build container. The other common hang is a VPC-attached build with no NAT gateway and no VPC endpoints for the services it needs to reach (S3, STS, etc.), which times out instead of failing with a clear error.
- EC2/on-premises — in-place (config-governed rolling update) or blue/green (a whole new instance set); Lambda — traffic shifting between two function versions behind one weighted alias; ECS — blue/green via a second task set behind a second target group, optionally validated through a test listener first.
- Each role needs only the narrow permissions its own service actually uses — the pipeline role only needs to start actions and touch the artifact bucket, not build or deploy anything itself. Collapsing them (or over-granting all three) removes the blast-radius protection that separation buys you, and a genuine permissions error becomes much harder to diagnose because it's no longer obvious which service's narrow permission set was actually missing.
- CodeCommit stopped onboarding new customers in 2024 (existing repos still work); most new pipelines now source from GitHub, GitLab, or Bitbucket via a connection instead. That change points toward AWS's newer, broader CodeCatalyst offering — though as of this writing CodePipeline, CodeBuild, and CodeDeploy remain independent, separately maintained services rather than being absorbed into it.