Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -347,3 +347,4 @@ x86/
dashboard/.azure/
dashboard/dist/
dashboard/**/dist/

115 changes: 115 additions & 0 deletions plugin/skills/deploy-to-aks/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
name: deploy-to-aks
license: MIT
metadata:
author: Microsoft
version: "1.0.0"
description: "Use when deploying a web application or API to an existing Azure Kubernetes Service cluster. Detects framework, generates Dockerfile and Kubernetes manifests, validates against AKS Deployment Safeguards, and deploys with verification. WHEN: deploy to AKS, deploy app to Kubernetes, containerize for AKS, deploy to existing AKS cluster, generate K8s manifests for Azure, set up CI/CD for AKS, migrate app to AKS, deploy container to Azure, I have a Django/Express/Spring Boot app and want to run it on AKS, my AKS deployment is failing safeguard checks."
---

# Deploy to AKS

Deploy applications to an existing AKS cluster with production-grade artifacts. Detects the framework, generates Dockerfile + K8s manifests, validates against Deployment Safeguards, and deploys — with minimal questions.

## When to Use This Skill

**Use this skill when:**
- You want to deploy an existing web application or API to an AKS cluster
- You need to containerize an app for Kubernetes and generate deployment manifests
- Your AKS deployment is failing Deployment Safeguard checks (DS001–DS013) and you need guidance
- You want to set up or improve CI/CD pipelines for AKS deployments
- You're migrating an application from another platform to AKS

**Do NOT use this skill for:**
- Provisioning or creating a new AKS cluster (use a separate provisioning skill)
- Deploying to non-AKS compute targets (Web Apps, Container Apps, etc.)
- Managing cluster infrastructure, scaling policies, or node pools
- Performing Kubernetes cluster administration tasks (RBAC, networking policies, etc.)

## MCP Tools

| Tool | Purpose | Required |
|------|---------|----------|
| `azure-documentation` | Fetch Azure documentation and configuration references | Yes |
| Terminal commands | Execute `kubectl`, `az`, `docker`, `gh` CLI commands | Yes |

Comment on lines +29 to +35

Copilot AI Apr 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Markdown tables in this skill are written with leading double pipes (|| ... |), which renders as an extra empty column and is inconsistent with the rest of the repo’s markdown table formatting (single leading |). Update these tables to standard markdown table syntax (single | column separators) for correct rendering and readability (this pattern appears multiple times throughout the skill docs).

Copilot uses AI. Check for mistakes.
## Error Handling

| Error | Likely Cause | Recovery |
|-------|--------------|----------|
| Safeguard validation failure (DS001–DS013) | Manifest violates deployment best practices (missing resource limits, security policies, etc.) | Review the safeguard checklist in `references/safeguards.md`, apply recommended fixes, re-validate |
| Image push fails to ACR | ACR not attached to cluster or authentication token expired | Run `az acr login --name <registry>`, verify ACR attachment with `az aks check-acr`, retry push |
| `kubectl apply` fails | Manifest syntax error or unsupported API version | Check manifest YAML syntax, verify API version compatibility with cluster Kubernetes version using `kubectl api-resources` |
| Pod CrashLoopBackOff | Application fails to start (missing env vars, config, port mismatch) | Check logs with `kubectl logs <pod>`, verify health endpoint config, ensure all required environment variables are set in ConfigMap/Secrets |
| Workload Identity auth failure | OIDC not configured or service account not mapped | Follow `references/workload-identity.md` to set up federated identity credentials and service account annotations |
| Deployment rollout stuck | Resource quota exceeded or image pull failure | Check `kubectl describe deployment`, verify resource requests fit quota, ensure image pull secrets are configured, check node readiness |

## Prerequisites

- An existing AKS cluster
- Azure CLI authenticated (`az login`)
- `kubectl` configured for the target cluster

## Workflow

Comment thread
gambtho marked this conversation as resolved.
Follow the quick deploy workflow in `phases/quick-deploy.md`. The workflow has 5 sections:

1. **Detection** — scan project for framework/port/health endpoints; detect AKS cluster, ACR, routing mode
2. **File Generation** — generate Dockerfile + K8s manifests from templates
3. **Safeguards Validation** — validate manifests against AKS Deployment Safeguards DS001-DS013
4. **Deploy** — build image, push to ACR, apply manifests
5. **Verify** — confirm pods running, external IP available, health check passing

## Quick Reference

| Property | Value |
|----------|-------|
| Best for | Deploying apps to an existing AKS cluster |
| MCP Tools | `azure-documentation` |
| CLI | `az acr build`, `kubectl apply`, `kubectl rollout status` |
| Related skills | azure-kubernetes (cluster provisioning), azure-diagnostics (troubleshooting) |

## Workflow Quick Reference

| Step | Read | Also load |
|------|------|-----------|
| Quick Deploy | `phases/quick-deploy.md` | `references/detection.md`, `knowledge-packs/frameworks/<detected>.md` (if exists), `references/safeguards.md`, `references/workload-identity.md`, `references/rollback.md` (on failure) |

Comment thread
gambtho marked this conversation as resolved.
## References

Load these on-demand based on workflow phase:

- [detection.md](./references/detection.md) — framework, port, and health endpoint detection tables
- [safeguards.md](./references/safeguards.md) — AKS Deployment Safeguards DS001-DS013 checklist
- [workload-identity.md](./references/workload-identity.md) — Azure Workload Identity setup for AKS pods
- [rollback.md](./references/rollback.md) — recovery procedures for deployment failures

## Knowledge Packs

After detecting the framework, load the matching pack from `knowledge-packs/frameworks/` if available. Packs provide framework-specific Dockerfile patterns, health endpoints, database config, and writable path requirements.

Available: `spring-boot`, `express`, `nextjs`, `fastapi`, `django`, `nestjs`, `aspnet-core`, `go`, `flask`

## Templates

Templates are starting points — replace `<angle-bracket>` placeholders with detected values.

| Category | Directory | Files |
|----------|-----------|-------|
| Dockerfiles | `templates/dockerfiles/` | node, python, java, go, dotnet, rust (+ matching `.dockerignore` per language) |
| K8s manifests | `templates/k8s/` | namespace, deployment, service, ingress, gateway, httproute, hpa, pdb, serviceaccount, configmap, networkpolicy |
| CI/CD | `templates/github-actions/` | deploy.yml |
| Diagrams | `templates/mermaid/` | architecture-diagram, summary-dashboard |
Comment thread
gambtho marked this conversation as resolved.

Comment thread
gambtho marked this conversation as resolved.
## Execution Model

- **Generate artifacts automatically** — Dockerfiles, manifests, workflows
- **Execute CLI commands only with confirmation** — `az`, `docker`, `kubectl`, `gh`
- **Detect before create** — check for existing Dockerfiles, manifests, CI/CD
- **Validate before replace** — improve what exists rather than overwriting

## Key Principles

- ONE concept per turn — never overload the developer
- Sensible defaults — Ingress (Web App Routing), Workload Identity, 2 replicas
- Teach while fixing — when auto-fixing Safeguard violations, explain why
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
# ASP.NET Core Knowledge Pack

> **Applies to:** Projects detected with `*.csproj` containing `Microsoft.NET.Sdk.Web` or referencing `Microsoft.AspNetCore.*` packages

---

## Dockerfile Patterns

### Multi-stage build with project-file-first NuGet restore

Copying only `*.csproj` and restoring before copying source ensures NuGet restore is cached unless dependencies change:

```dockerfile
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /app
COPY *.csproj ./
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app/publish

# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS runtime
WORKDIR /app
ENV DOTNET_EnableDiagnostics=0 \
DOTNET_RUNNING_IN_CONTAINER=true
COPY --from=build /app/publish .
USER app
EXPOSE 8080
ENTRYPOINT ["dotnet", "<app-name>.dll"]
```

### Key points

- **Base image:** `mcr.microsoft.com/dotnet/aspnet` is the official Microsoft runtime image — minimal and supported
- **Alpine variant** reduces image size by ~60% compared to the Debian-based tag
- **Project-file-first copy** (`COPY *.csproj`) means `dotnet restore` layer is cached until dependencies change
- **Non-root user** (`app`, uid 1654) is built into .NET 8+ images — no need to create one manually; satisfies DS004
- **`DOTNET_EnableDiagnostics=0`** disables diagnostic pipes that require writable paths not available in read-only filesystems
- **`DOTNET_RUNNING_IN_CONTAINER=true`** signals the runtime to optimize for container environments (GC, thread pool)



---

## Health Endpoints

ASP.NET Core has built-in health check middleware via `Microsoft.Extensions.Diagnostics.HealthChecks`:

| Endpoint | Purpose | Probe Type |
|----------|---------|-----------|
| `/healthz` | Overall health | `livenessProbe` |
| `/ready` | Dependency readiness | `readinessProbe` |

### Required configuration

In `Program.cs`:

```csharp
var builder = WebApplication.CreateBuilder(args);

// Register health checks
builder.Services.AddHealthChecks()
.AddNpgSql(builder.Configuration.GetConnectionString("DefaultConnection")!,
name: "postgresql",
tags: new[] { "ready" });

var app = builder.Build();

// Map health endpoints
app.MapHealthChecks("/healthz", new HealthCheckOptions
{
Predicate = _ => false // No dependency checks for liveness
});

app.MapHealthChecks("/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
```

The `AspNetCore.HealthChecks.NpgSql` NuGet package provides the PostgreSQL health check. Install with:

```bash
dotnet add package AspNetCore.HealthChecks.NpgSql
```

### Probe configuration in Deployment manifest

```yaml
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 5
periodSeconds: 15
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
```

**Note:** ASP.NET Core apps start significantly faster than JVM-based frameworks — `initialDelaySeconds: 5` is typically sufficient.

---

## Database Profiles

ASP.NET Core uses configuration providers and Entity Framework Core for database access:

| Configuration Source | Activation | Typical Usage |
|---------------------|------------|---------------|
| `appsettings.json` | Default | Local dev with SQLite or LocalDB |
| `appsettings.Production.json` | `ASPNETCORE_ENVIRONMENT=Production` | Production connection strings |
| Environment variables | Always override file config | AKS deployments |

### Environment variables for PostgreSQL on AKS

```yaml
env:
- name: ASPNETCORE_ENVIRONMENT
value: Production
- name: ConnectionStrings__DefaultConnection
value: "Host={{PG_SERVER_NAME}}.postgres.database.azure.com;Database={{DB_NAME}};Username={{IDENTITY_NAME}};Ssl Mode=Require"
```

The double-underscore (`__`) in `ConnectionStrings__DefaultConnection` maps to the `:` separator in .NET configuration — `ConnectionStrings:DefaultConnection`.

### Workload Identity with Azure.Identity

See `references/workload-identity.md` for connection patterns. Requires `Azure.Identity` and `Npgsql.EntityFrameworkCore.PostgreSQL` packages.

### ConfigMap pattern

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: {{APP_NAME}}-config
data:
ASPNETCORE_ENVIRONMENT: "Production"
ConnectionStrings__DefaultConnection: "Host={{PG_SERVER_NAME}}.postgres.database.azure.com;Database={{DB_NAME}};Ssl Mode=Require"
DOTNET_EnableDiagnostics: "0"
DOTNET_RUNNING_IN_CONTAINER: "true"
```

---

## Writable Paths (DS012 Compliance)

When `readOnlyRootFilesystem: true` is set, ASP.NET Core needs `/tmp` writable:

- **Data Protection keys** are written to a local directory by default for key persistence
- **Temporary files** from multipart uploads and response buffering use `/tmp`
- **Entity Framework** compiled models may write to temp directories

### Required volume mount

```yaml
volumes:
- name: tmp
emptyDir: {}
containers:
- name: app
volumeMounts:
- name: tmp
mountPath: /tmp
```

### Data Protection key persistence

By default, ASP.NET Core Data Protection stores encryption keys in-memory when no persistent path is available, meaning keys are lost on pod restart. This breaks authentication cookies and anti-forgery tokens across pod restarts or in multi-replica deployments.

For production, persist keys to Azure Blob Storage:

```csharp
builder.Services.AddDataProtection()
.PersistKeysToAzureBlobStorage("<connection-string>", "<container>", "<blob-name>")
.ProtectKeysWithAzureKeyVault(new Uri("<key-vault-uri>"), new DefaultAzureCredential());
```

Alternatively, mount a PVC at a known path and configure:

```csharp
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("/keys"));
```

---

## Resource Sizing

ASP.NET Core on the .NET runtime is efficient but needs moderate memory for the CLR.

| Resource | Request | Limit |
|----------|---------|-------|
| CPU | 200m | 500m |
| Memory | 256Mi | 512Mi |

---

## Port Configuration

- **Default port:** 8080 (since .NET 8; previously 80 in .NET 7 and earlier)
- **Env var override:** `ASPNETCORE_URLS=http://+:8080` or `ASPNETCORE_HTTP_PORTS=8080`
- **Code override:** `builder.WebHost.UseUrls("http://+:8080")` in `Program.cs`

The port change from 80 to 8080 in .NET 8 aligns with non-root container best practices — port 80 requires elevated privileges.

---

## Build Commands

| Variant | Build Command | Output |
|---------|---------------|--------|
| Framework-dependent | `dotnet publish -c Release -o ./publish` | `./publish/<app-name>.dll` — requires .NET runtime on target |
| Self-contained | `dotnet publish -c Release --self-contained -o ./publish` | `./publish/<app-name>` — includes .NET runtime |
| Single-file | `dotnet publish -c Release --self-contained -p:PublishSingleFile=true -o ./publish` | Single executable binary |

The `-c Release` flag enables compiler optimizations and disables debug symbols — always use it for production builds.

---

## Common Issues on AKS

| Issue | Symptom | Fix |
|-------|---------|-----|
| Kestrel bound to port 80 | `CrashLoopBackOff` — permission denied binding to port 80 as non-root | Set `ASPNETCORE_HTTP_PORTS=8080` or upgrade to .NET 8+ which defaults to 8080 |
| Data Protection keys lost on restart | Users logged out after pod restart, anti-forgery token validation failures | Persist keys to Azure Blob Storage or a PVC — do not rely on in-memory default |
| EF Core migrations not applied | `NpgsqlException: relation "..." does not exist` | Run `dotnet ef database update` as an init container or at startup with `Database.Migrate()` |
| Image too large (>500MB) | Slow pulls, high ACR storage | Use self-contained + trimmed publish with `runtime-deps` Alpine base image |
| HTTPS redirect loop behind gateway | Infinite 307/308 redirects, `ERR_TOO_MANY_REDIRECTS` | Disable HTTPS redirection in `Program.cs` when behind a TLS-terminating gateway — configure `ForwardedHeaders` middleware instead |
Loading
Loading