Bean & Bark: your first deploy

View .md

Bean & Bark's wholesale orders arrive by DM and spreadsheet, and the roastery's first tasting for retail buyers is Thursday. Nadia wants orders coming through an API before then. You have a fresh laptop, an AWS dev account, and one command surface. This is where you learn the thing the CLI is built around: a deploy isn't done when the code is up — it's done when the deployment ledger has recorded exactly what shipped.

You'll learn
  • Scaffold a deploy-ready .NET service — solution, manifest, a dev stage, tests — with one kanject new
  • Prove your machine is ready with kanject doctor, entirely offline
  • Deploy to the dev stage and follow the phase ladder to record ledger
  • Predict every AWS resource name from two words: the service name and the stage

One command, a whole service

kanject new scaffolds from a template that ships inside the CLI — no feed, no license, nothing to authenticate. webapi gives you an ASP.NET Core REST API on AWS Lambda, split into Api, Business, Common and Data projects with an xUnit test project, and --yes accepts the prompts so it runs clean in one go.

bash
# One command: a deploy-ready servicekanject new webapi --name BeanAndBark.Orders --yes​# Every kanject command in this series runs from the API projectcd BeanAndBark.Orders/src/BeanAndBark.Orders.Api

new runs dotnet new, then runs kanject init inside the API project, so the service is already wired — you never run init on a fresh scaffold:

text
BeanAndBark.Orders/├── BeanAndBark.Orders.slnx├── src/│   ├── BeanAndBark.Orders.Api/          ← the Lambda entry: run kanject here│   │   ├── template.json                ← the SAM template deploy builds on│   │   └── kanject-cli/│   │       ├── manifest.json│   │       ├── manifest.lock.json│   │       └── stages/dev.json│   ├── BeanAndBark.Orders.Business/│   ├── BeanAndBark.Orders.Common/│   └── BeanAndBark.Orders.Data/└── tests/    └── BeanAndBark.Orders.Api.Tests/

kanject-cli/ holds the service's identity (manifest.json), its pinned dependencies (manifest.lock.json, empty for now), and one stage, dev. It lives in the API project, which is why the cd matters: run a kanject command anywhere else and it can't find the manifest (doctor reports KANCLI002). Every command in this series runs from src/BeanAndBark.Orders.Api.

Prove the machine is ready

Before anything touches AWS — a guess. doctor is about to run its checks: how many of them need AWS credentials? Run it and count.

bash
kanject doctor
text
── Kanject Doctor ──────────────────────────────────────────────────────────────​╭────┬───────────────────────────────────────────────────┬────────┬──────┬─────╮│    │ Check                                             │ Status │ Code │ Fix │├────┼───────────────────────────────────────────────────┼────────┼──────┼─────┤│ ✓  │ dotnet 10.0.401                                   │ ok     │ -    │ -   ││ ✓  │ global tools on PATH (/Users/you/.dotnet/tools)   │ ok     │ -    │ -   ││ ✓  │ kanject-cli/manifest.json (schema v2)             │ ok     │ -    │ -   ││ ✓  │ Amazon.Lambda.Tools 6.0.5                         │ ok     │ -    │ -   ││ ✓  │ kanject-cli/manifest.lock.json in sync (no deps)  │ ok     │ -    │ -   ││ ✓  │ service.entryProjects (1 entry/entries; api)      │ ok     │ -    │ -   │╰────┴───────────────────────────────────────────────────┴────────┴──────┴─────╯​ok     6warn   0error  0​── Next Steps ──────────────────────────────────────────────────────────────────  kanject doctor --env <stage>  Run stage-specific checks  kanject doctor --features     Review optional feature readiness

The answer is none. Every row is local: the .NET SDK, the global-tools PATH, the manifest, the Lambda tooling, the lockfile, the entry project. Even kanject doctor --env dev stays local for a stage like this one; doctor only calls AWS to check a stage that declares a VPC (network) or a custom API domain. So it won't tell you your credentials have expired or your bucket is missing — the deploy will. The exit code is honest too: warnings keep it 0; only a red ✗ row makes it 1.

Point dev at your AWS

Open kanject-cli/stages/dev.json. A stage is the whole answer to "where does this deploy?" — region, profile, stack, artifact bucket, and the Parameter Store path for its config:

json
{  "$schema": "../stage.schema.json",  "schemaVersion": 1,  "region": "eu-west-1",  "profile": "default",  "stack": "dev-beanandbark-orders",  "artifactBucket": "dev-beanandbark-orders-artifacts",  "parameterStore": {    "path": "/beanandbark-orders/dev/"  },  "env": {},  "entryEnv": {}}

Look at the names. You never chose them — they're derived: service beanandbark-orders + stage dev gives stack dev-beanandbark-orders, bucket dev-beanandbark-orders-artifacts, and the SSM path /beanandbark-orders/dev/. Two words predict every resource name, on every stage. Change the region or profile if yours differ.

One value before the first deploy. The scaffold's SAM template sets ASPNETCORE_ENVIRONMENT to Production on the function, so dev would load the production settings. Stage values win over the template's at deploy, so tell dev what it is:

bash
kanject add env ASPNETCORE_ENVIRONMENT Development --env dev
text
  +  set ASPNETCORE_ENVIRONMENT in kanject-cli/stages/dev.json
bash
aws s3 mb s3://dev-beanandbark-orders-artifacts --region eu-west-1 --profile default

Ship it

bash
kanject aws deploy --env dev
text
── Deploy  stage dev ───────────────────────────────────────────────────────────​  sync deps  (no deps)               00:00:00  resolve env  1 keys                00:00:01  prepare artifacts  done            00:00:00  write deploy config  done          00:00:00  render provider artifacts  done    00:00:01  deploy Lambda  ok                  00:01:42  promote revision  1 → live         00:01:45  record ledger  s3 snapshot written 00:01:46​​╭────────────────────────┬────────────┬─────────────╮│ .NET key               │ Source     │ Value       │├────────────────────────┼────────────┼─────────────┤│ ASPNETCORE_ENVIRONMENT │ StagePlain │ Development │╰────────────────────────┴────────────┴─────────────╯​  ✓  Deployed dev-beanandbark-orders (Lambda) @ revision 1

resolve env found the one value you added, and the table shows what was baked into the function. Then read the last two phases, because they're the point. promote revision published Lambda version 1 — code and environment frozen together — and flipped the live alias to it; that number is about to matter. record ledger wrote an entry to _ledger/deployments/ in the artifact bucket and pointed _ledger/current.json at it: the revision, your git commit, who deployed, and a SHA-256 of the lockfile and of every resolved env value. And the ordering is a guarantee: if any phase fails, no ledger entry is written. The ledger never claims a deploy that didn't finish.

Notice what it didn't print: a URL. The template publishes the API's address as the stack output ApiURL, so that's what you send Tunde:

bash
aws cloudformation describe-stacks --stack-name dev-beanandbark-orders \  --query "Stacks[0].Outputs[?OutputKey=='ApiURL'].OutputValue" --output text --region eu-west-1 --profile default

Read it back

Don't take the terminal's word for it — ask the ledger:

bash
kanject aws deployments list --env dev
text
── Deployments  stage dev ────────────────────────────────────────────────────────────╭──────────┬────────┬──────────────────────────────┬─────────┬──────────┬────────────╮│ Revision │ Target │ Deployed                     │ Commit  │ Status   │ By         │├──────────┼────────┼──────────────────────────────┼─────────┼──────────┼────────────┤│ 1        │ Lambda │ 2026-07-06T09:26:14.5821930Z │ 9f3c1ab │ ✓ stable │ you@laptop │╰──────────┴────────┴──────────────────────────────┴─────────┴──────────┴────────────╯

One revision, target Lambda, the UTC timestamp as recorded, your commit, status stable, and your user and machine name. Every deploy from now on — including Thursday's panicked ones — adds a row here. When something goes wrong, this table is where the recovery starts.

Recap
  • kanject new webapi --name <Name> --yes scaffolds a deploy-ready service with one dev stage and runs init inside the API project — run every kanject command from there.
  • kanject doctor without --env is fully offline; only errors (not warnings) flip its exit code to 1.
  • Stage + service name derive every resource: dev-beanandbark-orders, …-artifacts, /beanandbark-orders/dev/. The artifact bucket is the one thing you create yourself.
  • A deploy ends by publishing a Lambda version, flipping the live alias, and writing a ledger entry — and a failed deploy writes no ledger entry.
Before Thursday — check yourself2
Your deploy fails at the deploy Lambda phase. What does the deployment ledger show afterwards?
Which of these belong in git?
Try it yourself 1
1 Rehearse the deploy
Run the dev deploy as a dry run and find the line telling you where it stopped. What did it write, and which step never ran?
Add one flag to the deploy command. The output ends with a dim "Stopped before …" line.
Show solution
After the resolved-env table it prints "Dry run — rendered the deploy inputs; no AWS changes were made.", lists aws-lambda-tools.dev.json and template.dev.json — the two per-stage files the real deploy uses — and ends "Stopped before dotnet lambda deploy-serverless." No build, no upload, no stack change, no alias flip, no ledger entry.
bash
kanject aws deploy --env dev --dry-run
Was this page helpful?