Skip to content

A config-driven pipeline (Autopilot)

What you'll build

  • A working CodePipeline for a stock CDK app, provisioned from a single cicd.config.ts.
  • An understanding of every defineCICD field and when to reach for it.
  • A customized CI phase — your own named build steps and, optionally, a custom build image.

The whole Autopilot opt-in is two things: a cicd.config.ts, and pointing cdk.json at cdk-cicd exec.

1. Your app stays plain CDK

bin/app.ts is exactly what cdk init gave you — a plain App with your stacks. No PipelineBlueprint, no builder, no wrapper import:

// bin/app.ts
import * as cdk from 'aws-cdk-lib';
import { MyStack } from '../lib/my-stack';

const app = new cdk.App();
new MyStack(app, 'my-app', {
  env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});

2. Describe the pipeline in cicd.config.ts

// cicd.config.ts
import { defineCICD, Repository } from '@cdklabs/cdk-cicd-wrapper';

export default defineCICD({
  application: 'my-app',
  repository: Repository.codecommit('my-app'),   // or .github('org/my-app', 'main') / .s3('bucket/app.zip')
  stages: ['dev', 'prod'],
});

3. Point cdk.json at the wrapper's exec hook

{
  "app": "npx cdk-cicd exec bin/app.ts"
}

cdk-cicd exec runs your app under a preload that injects the resolved config (and tags, synthesizer, and compliance Aspects) around it — which is how your untouched bin/ becomes wrapper-aware without importing anything.

4. Provision the pipeline — once

npx cdk-cicd deploy-ci

This deploys one pipeline into your hub account from cicd.config.ts alone. On every run it:

Source → Build (checks + synth) → UpdatePipeline (re-deploys itself from config) → deploy dev → deploy prod

The flat Autopilot pipeline in the CodePipeline console: Source → Build → UpdatePipeline → deploy

The flat Autopilot pipeline in the CodePipeline console — one linear pipeline, not Blueprint's 100+ CodeBuild projects.

The UpdatePipeline stage means you never run deploy-ci again by hand: change cicd.config.ts, push, and the pipeline re-synthesizes its own definition on the next run and applies the change before the stages it affects.

Every field of defineCICD

The two-stage example above uses three fields. Here is the full set, and why each matters — you'll only ever write the few you need, because the wrapper resolves sensible defaults for the rest.

Field What it does Why it matters
application Logical name for the app and its resources. Defaults from package.json#name. The prefix on pipeline and support-stack names — set it once so resources are recognizable.
qualifier Short (≤10 char) sanitized id used to disambiguate shared resources. Derived from application. Only set it if two apps would otherwise collide on shared names.
repository The pipeline's source: Repository.github('org/repo', branch?), Repository.codecommit('name', branch?), or Repository.s3('bucket/key', branch?). This is where the pipeline reads code and what triggers it — the one field you almost always set explicitly.
stages Ordered list of deployment stages — bare names or objects with env, manualApproval, deployment. Your promotion path (dev → prod). Config-as-data, not pipeline code. Covered in the next chapter.
ci Customizes the CI phase: steps, synthStages, image. Add your own build/test steps or a custom image. See Customizing CI below.
codeArtifact Authenticates builds to a private CodeArtifact repo (domain, repository, account?, region?, npmScope?). Needed when your deps (or the wrapper itself, pre-release) live in a private registry. See chapter 4.
deployModel DeployModel.ASSEMBLY_PROMOTION (default) or DeployModel.DEPLOY_TIME_SYNTH. Controls when synth happens — one synth per run vs per-stage at deploy time. See chapter 3.
asyncDeploy boolean (default false). Hands the CloudFormation wait to a Lambda instead of holding a build. Saves build compute when the CloudFormation wait dominates. See chapter 3.
synthesizer { type?: SynthesizerType.DEFAULT \| SynthesizerType.APP_STAGING }. DEFAULT (DefaultStackSynthesizer) suits most apps; opt into APP_STAGING for per-app staging + roles-only bootstrap.
engine Selects the CD engine (EngineType). EngineType.CODEPIPELINE is the default and covers most cases — you rarely set it. Two alternates exist: CDK_PIPELINES (plain CDK Pipelines, no CodePipeline-specific extras) and GITHUB_ACTIONS (renders a .github/workflows/deploy.yml instead of an AWS-hosted pipeline — see GitHub as source & CD engine). Tuning for the default engine lives on the stages and ci (chapter 3), not here.
githubActions GitHub Actions engine config (roleName, subjectClaims, workflowTriggers, etc.). Only read when engine is EngineType.GITHUB_ACTIONS.
deployerImage Turns the pipeline into a config-agnostic image builder (BuildImage.docker({...})). The container-mode entry point. See chapter 5.

Start small

The minimum config is repository + stages. application defaults from package.json#name, the engine defaults to CodePipeline, and the synthesizer defaults to DefaultStackSynthesizer. Add fields only when a default doesn't fit.

Customizing CI

The ci block shapes the Build phase. All three sub-fields are optional; with none set, the build runs your project's own npm scripts (npm run audit/build/test, each run-if-present, warn-if-absent) plus the synth step.

Named build steps (ci.steps) — a map of { name: shell-command }. Each entry becomes a named step in the CI build, so you add project-specific gates without editing pipeline code:

import { defineCICD, Repository } from '@cdklabs/cdk-cicd-wrapper';

export default defineCICD({
  application: 'my-app',
  repository: Repository.github('my-org/my-app'),
  stages: ['dev', 'prod'],
  ci: {
    steps: {
      lint: 'npm run lint',
      test: 'npm test',
      audit: 'npx cdk-cicd check-dependencies',
    },
  },
});

A custom build image (ci.image) — point CI at your own CodeBuild image (a registry reference) when you need tools the default image doesn't ship:

  ci: {
    image: 'public.ecr.aws/my-org/ci-node:20',   // custom CodeBuild image for CI steps
    steps: { test: 'npm test' },
  },

Synth scope (ci.synthStages)'all' synthesizes every stage as a validation gate; a list narrows it to specific stages when synth cost matters. This interacts with the deploy model, so it's covered in chapter 3.

The build image needs the AWS CLI

Steps like the CodeArtifact login and asset publishing shell out to the AWS CLI, which the default CodeBuild image ships. If you set a custom ci.image, make sure the AWS CLI is on its PATH.

Verify

Verify

Confirm the pipeline exists and runs end to end:

  • In the CodePipeline console, your pipeline shows the flat Source → Build → UpdatePipeline → deploy dev → deploy prod shape.
  • The most recent execution reaches Succeeded on every stage.
  • Compare the CodeBuild project count to a Blueprint pipeline: Autopilot provisions a small, constant set — not one per asset or per stage.

You can also validate the config locally before pushing:

npx cdk-cicd synth --all       # synthesizes every stage from your config

Recap

You turned a stock CDK app into one flat CodePipeline with a single config file and a one-line cdk.json change — no wrapper code in bin/. You know every defineCICD field, and you can add your own CI steps and image. The next chapters go deep on the fields you'll reach for most: stages and approvals, deploy models and tuning, private registries, and container mode.