# ECS Fargate

ECS Fargate is the deploy target for a long-running `webapi` that wants to run as a container rather than a Lambda. Set `deployTarget: "ecs-fargate"` on the entry and `kanject aws deploy` builds the image, registers a task definition, and promotes it through CloudFormation. Deploy, rollback, and previews work the same way they do for Lambda — a different substrate, the same workflow.

**You'll learn**

- Turn an entry into an ECS Fargate service (`deployTarget`, `webapi`, `image`)
- Configure the service — cpu/memory, autoscaling, ALB, IAM — in the stage file
- Understand how a deploy promotes a task definition, and how rollback works
- Know what's managed for you vs referenced, and the MVP constraints

## How it works: two planes

CloudFormation owns the **durable infrastructure** — the cluster, service, load balancer, IAM roles, and log groups. Each deploy produces an **immutable artifact** — a digest-pinned image and a registered task-definition revision — and promotes it by swapping a single stack parameter (`TaskDefinitionArn`). So a deploy is a CloudFormation change set, and a rollback is the same change set with the previous revision. The stack stays the source of truth; the task definition is the thing that changes.

## Make an entry ECS Fargate

An ECS service is exactly one entry, running one primary application container. `doctor` enforces the shape: `runtimeMode` must be `webapi` and `packageType` must be `image`.

```json
// manifest.json → service.entryProjects[]
{
  "id": "web",
  "projectPath": "src/Acme.Web/Acme.Web.csproj",
  "runtimeMode": "webapi",
  "packageType": "image",
  "deployTarget": "ecs-fargate",
  "build": { "kind": "container", "mode": "sdk-publish", "containerPort": 8080 }
}
```

- `deployTarget` — `"ecs-fargate"` (omit, or `"lambda"`, for the Lambda path).
- `packageType` — `"image"` is required; the service runs a container.
- `build` — how the image is produced: `mode: "sdk-publish"` runs `dotnet publish -t:PublishContainer` (no Docker daemon), or `"dockerfile"` builds a `Dockerfile`. `containerPort` defaults to `8080`.

## Configure the service

The region-agnostic service shape lives in `stages/<stage>.json → ecs`:

```json
// stages/<stage>.json
"ecs": {
  "cpu": 512,
  "memory": 1024,
  "desiredCount": 2,
  "autoscaling": { "min": 2, "max": 10, "targetCpuPercent": 60 },
  "circuitBreaker": { "enable": true, "rollback": true },
  "alb": { "scheme": "public", "healthCheckPath": "/healthz", "port": 443 },
  "assignPublicIp": false,
  "taskRolePolicies": [],
  "execRolePolicies": []
}
```

- `cpu` / `memory` / `desiredCount` *(required)* — Fargate task size and the baseline task count.
- `autoscaling` — `min`, `max`, `targetCpuPercent` for target-tracking; omit to hold `desiredCount`.
- `circuitBreaker` — `enable` + `rollback`; ECS rolls a failed deployment back to the last good task definition.
- `alb` — `scheme` (`public` / `internal`), `healthCheckPath`, `port`.
- `assignPublicIp`, `taskRolePolicies` (your app's AWS permissions), `execRolePolicies` (image pull, secret read, logs).

## Per-region references

Region-scoped resources — VPC, subnets, certificate, hosted zone, and an optional existing cluster — attach per target under `targets[].ecs`. They are **referenced** (Kanject diagnoses them with `KANCLI400–404` if missing, but never creates them).

```json
// stages/<stage>.json — per-region references for an ECS target
"targets": [
  {
    "region": "eu-west-2",
    "ecs": {
      "cluster": { "ref": "arn:aws:ecs:eu-west-2:111122223333:cluster/shared" },
      "vpc": "vpc-0a1b2c3d",
      "subnets": ["subnet-1111", "subnet-2222"],
      "certificate": "arn:aws:acm:eu-west-2:111122223333:certificate/abc",
      "hostedZone": "Z0123456789"
    }
  }
]
```

## Deploy, roll back, preview

A deploy builds the image → pushes to ECR (pinned by **digest**, never a mutable tag) → registers a task definition → creates and executes a CloudFormation change set that points the service at the new revision → waits for **steady state**. The first deploy runs a two-phase bootstrap (foundation resources, then the service); later deploys just swap the parameter.

```bash
# Deploy: build the image, register a task definition, promote via CloudFormation
kanject aws deploy --env prod

# Roll back to the previous task definition (a stack update — minutes, not seconds)
kanject aws rollback --env prod
```

> **Rollback is minutes, not seconds:** A Lambda rollback is a sub-second alias flip. An ECS rollback re-promotes the previous task definition through CloudFormation and waits for steady state — minutes, not seconds. It is preflighted first: the task-def revision and its ECR image digest must still exist (KANCLI415 / KANCLI416), so nothing is attempted that can't complete.

## Managed vs referenced

- **Managed (CloudFormation creates):** the ECS service, ALB + target group + listener, task & execution IAM roles, log groups, security groups, autoscaling. The cluster is managed by default, or you reference an existing one.
- **Referenced (must pre-exist):** VPC + subnets, the ACM certificate, and the Route 53 hosted zone. Missing references are diagnosed before any change set runs.

## Constraints (MVP)

- One entry — one primary application container — per ECS service. User-declared sidecars are a follow-up.
- Rolling deploys with the ECS circuit breaker. Blue/green and canary traffic shifting are a separate proposal.
- Migrating an existing Lambda to ECS creates a new, target-qualified stack and leaves the Lambda stack standing — opt in with `--replace-target` (refused when the stage carries an `api.domain`, to protect DNS).

## Guidelines

- **Set a real `healthCheckPath`.** The ALB and the steady-state wait both depend on it — a service that never reports healthy times out (`KANCLI408`).
- **Set `autoscaling` for anything production.** `desiredCount` alone is a fixed floor; target-tracking handles load.
- **Keep secrets as references.** Kanject resolves them describe-only and emits `valueFrom` — secret material never enters the CLI process or the CloudFormation template.
- **Expect a Fargate preview to cost more than a Lambda preview.** It's an always-on task, not a scale-to-zero function.
- **Use `--replace-target` deliberately.** It acknowledges the old Lambda stack is now orphaned; the diagnostic names it for you to remove.

**Recap**

- Set `deployTarget: "ecs-fargate"` (with `webapi` + `image`) to run an entry as a container service.
- CloudFormation owns the durable infra; each deploy registers a digest-pinned task definition and promotes it by a change-set parameter swap.
- Configure the service in `stages/<stage>.json → ecs`; reference VPC / cert / zone per region under `targets[].ecs`.
- Rollback re-promotes the previous task definition (minutes, preflighted). One entry per service, rolling deploys, in MVP.

---
_Source: https://www.kanject.com/docs/ecs-fargate/ · Kanject Docs_
