CI/CD Pipeline Setup
Ship on git push. kanject aws pipeline up provisions an AWS CodePipeline that builds and tests your service, then deploys each stage in order, with a manual approval before the last one. Already on GitHub Actions, GitLab or Buildkite? Run kanject aws deploy from your own runner instead.
- Provision an AWS-native pipeline for a service with
kanject aws pipeline up - Share pipeline settings between the services in one repository
- Run the same deploy from your own CI runner
One command: pipeline up
You need a service with its stages declared, the code on GitHub, and AWS credentials for the account the pipeline lives in. On a terminal, pipeline up works out or creates the rest:
- Repository and branch — read from your git remote and current branch. It warns when the branch isn't
main, because that branch becomes the one the pipeline tracks. - GitHub connection — uses your available AWS CodeConnections connection to GitHub (asking which, if you have several), resumes a pending one, or creates one. Authorizing a new connection is a one-time step in the AWS console; the CLI prints the link and waits.
- Artifact bucket and GitHub token secret — offered for creation when missing, only after you confirm. The token secret is what lets CodeBuild clone private repositories.
- CodeArtifact coordinates — asked for only when the manifest declares cross-repo dependencies.
# On a terminal: derives what it can from git and AWS, asks for the rest,# and offers to create anything missing before it deploys the stackkanject aws pipeline up Every value is saved to kanject-cli/pipeline.json, so you supply it once; commit the file. Then up writes the deploy/ folder if it is missing and creates or updates the <service>-pipeline CloudFormation stack. Re-running it with nothing changed is a no-op. Without a terminal, missing values fail together in one KANCLI315 error that lists them all:
# In CI or scripts: pass everything, once — it is saved to kanject-cli/pipeline.jsonkanject aws pipeline up --non-interactive \ --connection arn:aws:codeconnections:eu-west-1:111122223333:connection/abcd \ --repo-owner acme --repo-name acme-analytics \ --artifact-bucket acme-pipeline-artifacts \ --github-pat-secret arn:aws:secretsmanager:eu-west-1:111122223333:secret:kanject/github-pat# Later runs need no flags; add a second region to prod like thiskanject aws pipeline up --stage prod --add-region us-east-1 What gets provisioned
- A pipeline triggered by pushes to your branch: Source → Build → one deploy stage per entry in
manifest.aws.stages, in order. - A manual approval before the final stage, skipped when the service has a single stage.
- A shared Build project (
kanject sync,dotnet build,dotnet test) and a shared Deploy project (kanject aws deploy), with the stage passed to each run. - The IAM roles CodeBuild needs and the webhook that starts the pipeline.
The Build project runs kanject sync first, so cross-repo dependencies resolve on a fresh runner; see Dependencies. A service with Native AOT entries gets a privileged deploy project that can run Docker, on ARM for arm64 functions. A repository with git submodules gets a full clone and a submodule checkout in the build.
Before touching CloudFormation, up checks that your credentials work, the connection is authorized, and the token secret and artifact bucket are readable, so wrong settings fail in seconds with a KANCLI310–KANCLI313 code. A CI role that may deploy the stack but not run those checks can pass --no-preflight.
Several services in one repository
Pipelines are one per service. Each service directory owns its manifest, its stack and its <service>-pipeline, so a broken deploy in one never blocks the others. A service with several Lambdas still needs only one pipeline, because its entries deploy together into one stack. --shared writes the values every service has in common (connection, artifact bucket, CodeArtifact, token secret, pipeline region) to kanject-cli/pipeline.shared.json at the git root, and each service keeps only its own:
# Save the repo-wide values once, at the git root…kanject aws pipeline up --shared --cwd services/api# …and every other service inherits themkanject aws pipeline up --cwd services/worker Bring your own runner
On GitHub Actions, GitLab, Buildkite or Jenkins the recipe is the same: install the .NET SDK, install Amazon.Lambda.Tools and Kanject.Cli as global tools, sign in to AWS, then run kanject aws deploy --env <stage> --non-interactive. That flag turns prompts off without approving anything on your behalf.
name: Deployon: push: branches: [main]jobs: deploy: runs-on: ubuntu-latest permissions: id-token: write # AWS OIDC contents: read steps: - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v4 with: { dotnet-version: '10.0.x' } - run: dotnet tool install -g Amazon.Lambda.Tools - run: dotnet tool install -g Kanject.Cli - uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789012:role/github-deploy aws-region: eu-west-1 - run: kanject aws deploy --env stage --non-interactive Gate production with your provider's own approval, such as a GitHub environment with required reviewers. For several services in one repository, use a matrix job with a path filter so only changed services redeploy.
kanject aws pipeline upderives the repository and branch from git, finds or creates the GitHub connection, and offers to create the bucket and token secret, then provisions the pipeline.- Settings persist to
kanject-cli/pipeline.json;--sharedputs repository-wide ones inpipeline.shared.jsonat the git root. - The pipeline builds, tests, then deploys each stage in order, with an approval before the last.
- On your own runner, the recipe ends in
kanject aws deploy --env <stage> --non-interactive.