A hands-on guide to modernizing legacy .NET applications using the Spec2Cloud framework and AI-powered agents
From ASP.NET MVC 5 / .NET Framework 4.8 → ASP.NET Core 9 → Containerized → Deployed to Azure
This repository is a guided, step-by-step learning experience for modernizing legacy .NET applications using Spec-Driven Development (SDD) and the Spec2Cloud agent framework.
Instead of jumping straight into code rewrites, this approach follows a disciplined methodology:
- Understand first — Reverse-engineer and document what exists
- Plan before coding — Create comprehensive modernization specifications
- Execute with confidence — Implement changes backed by detailed plans
- Deploy with best practices — Containerize and deploy to Azure with IaC
Each step of the process has been pre-implemented in a dedicated branch, so you can follow along, compare results, or jump to any stage.
Spec2Cloud is a multi-agent framework that orchestrates the full lifecycle of application modernization through specialized AI agents in GitHub Copilot, each responsible for a distinct phase:
┌──────────────────────────────────────────────────────────────────────────┐
│ SPEC2CLOUD FRAMEWORK │
│ │
│ 📋 Reverse 📐 Modernization 🔨 Implementation ☁️ Deploy │
│ Engineering Planning & Build to Azure │
│ │
│ /rev-eng → /modernize → /plan → /deploy │
│ │
│ tech-analyst modernizer dev azure │
│ agent agent agent agent │
└──────────────────────────────────────────────────────────────────────────┘
Each agent is invoked through a prompt file (in .github/prompts/) that defines its mission, rules, and workflow. The agents use the specs/ folder as their shared knowledge base — each one reads what the previous agents produced and builds upon it.
Spec-Driven Development is an approach where every change begins with a specification — a structured document that captures intent, requirements, analysis, and decisions before any code is written.
| Traditional Approach | Spec-Driven Approach |
|---|---|
| "Let's just rewrite it in .NET 9" | Analyze → Document → Plan → Execute |
| Knowledge lives in developers' heads | Knowledge captured in versioned specs |
| Ad-hoc decisions during coding | Decisions documented in ADRs upfront |
| Hard to review or validate approach | Specs are reviewable artifacts |
| Risk discovered during implementation | Risk identified during planning |
The modernization follows a clear progression. Each step builds on the previous one, and each has a corresponding branch you can checkout and explore:
main ──► code-import ──► brown-analyze ──► brown-modernize-plan ──► brown-modernize-dev ──► brown-modernize-deploy
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
Template Legacy app Reverse eng. Modernization New .NET 9 app Containerized
repo imported specs created plan & tasks built & running + Azure IaC
Tip: To explore any step, run
git checkout <branch-name>and compare with the previous branch to see exactly what changed.
📂 Branch:
main
Start by creating a new repository based on the Spec2Cloud template. This gives you the agent framework, prompt files, and project structure.
- Go to the Spec2Cloud repo and click "Use this template"
- Clone your new repository locally
- Open it in VS Code with GitHub Copilot enabled (agent mode)
- Familiarize yourself with the prompt files in
.github/prompts/
| Folder / File | Purpose |
|---|---|
.github/prompts/rev-eng.prompt.md |
📋 Reverse engineering agent — analyzes existing code |
.github/prompts/modernize.prompt.md |
📐 Modernization agent — creates upgrade strategy |
.github/prompts/plan.prompt.md |
🔨 Dev agent — implements the modernization tasks |
.github/prompts/deploy.prompt.md |
☁️ Azure agent — containerizes and deploys to Azure |
docs/ |
Framework documentation and guides |
specs/ |
Target folder for all generated specifications |
📂 Branch:
code-import— View changes from main →
Copy the legacy Contoso University application into the src/ folder. This is a classic ASP.NET MVC 5 application running on .NET Framework 4.8 — representative of the thousands of enterprise .NET apps still running in production.
- Go to the Azure-Samples/dotnet-migration-copilot-samples repository
- Download the Contoso University sample application
- Copy it into
src/ContosoUniversity/in your repository - Commit to a new branch (
code-import)
| Aspect | Details |
|---|---|
| Framework | ASP.NET MVC 5 on .NET Framework 4.8 |
| ORM | Entity Framework Core 3.1.32 |
| Frontend | Razor views, jQuery, Bootstrap 3 |
| Database | SQL Server LocalDB |
| Packages | 45 NuGet packages via packages.config |
| Auth | ❌ None |
| Messaging | MSMQ for notifications |
| File Storage | Local file system |
This is a university enrollment management system with Students, Courses, Instructors, Departments, and Enrollments — a standard line-of-business application.
📂 Branch:
brown-analyze— View changes from code-import →
This is where the magic begins. Using the /rev-eng prompt, the Tech Analyst agent performs a comprehensive analysis of the entire codebase and produces structured documentation.
- Open GitHub Copilot Chat in VS Code (agent mode)
- Type
/rev-eng(or invoke therev-eng.prompt.mdprompt) - The agent will analyze every file, pattern, and dependency
- Review the generated specifications in
specs/
specs/
├── prd.md ← Extracted Product Requirements Document
├── features/ ← Business feature documentation
│ ├── FRD-001-student-management.md
│ ├── FRD-002-course-management.md
│ ├── FRD-003-instructor-management.md
│ ├── FRD-004-department-management.md
│ ├── FRD-005-enrollment-tracking.md
│ ├── FRD-006-notification-system.md
│ └── FRD-007-teaching-materials.md
└── docs/
├── architecture/
│ ├── overview.md ← System architecture analysis
│ ├── components.md ← Component breakdown
│ └── security.md ← Security assessment
├── technology/
│ ├── stack.md ← Technology inventory
│ └── dependencies.md ← Dependency analysis (all 45 packages)
└── infrastructure/
└── deployment.md ← Current deployment model
- Feature Requirements Documents (FRDs) — Each business capability documented with data models, API endpoints, and UI components
- Security Assessment — Vulnerabilities identified: no authentication, no HTTPS, MSMQ misconfigurations
- Technology Stack — Every dependency catalogued with version and purpose
- Architecture Overview — How the layers connect, what patterns are used (or missing)
💡 Key insight: The agent documents what actually exists, not what should exist. It's honest about gaps, missing tests, and technical debt. This truthful foundation is what makes the subsequent planning phase effective.
📂 Branch:
brown-modernize-plan— View changes from brown-analyze →
Using the /modernize prompt, the Modernizer agent reads all the specs from Step 2 and creates a comprehensive modernization strategy with detailed implementation tasks.
- Run the
/modernizeprompt in Copilot Chat - The agent reads all
specs/features/,specs/docs/, andspecs/prd.md - It produces a complete modernization strategy with 57+ specification documents
- Review the plan, strategy, risk analysis, and task breakdown
specs/modernize/
├── assessment/ ← "Where are we now?"
│ ├── architecture-review.md Current vs target architecture
│ ├── compliance-gaps.md OWASP / SOC 2 / GDPR gaps
│ ├── performance-analysis.md Bottleneck identification
│ ├── security-audit.md Vulnerability assessment
│ └── technical-debt.md 20+ debt items catalogued
│
├── strategy/ ← "Where are we going?"
│ ├── architecture-evolution.md Target Clean Architecture
│ ├── devops-transformation.md CI/CD and IaC strategy
│ ├── roadmap.md 6-phase migration roadmap
│ ├── security-enhancement.md Security improvement plan
│ └── technology-upgrade.md Stack upgrade decisions
│
├── plans/ ← "How do we get there?"
│ ├── migration-plan.md Step-by-step execution plan
│ ├── rollback-procedures.md Rollback strategy per phase
│ ├── testing-strategy.md Test pyramid and validation
│ └── validation-criteria.md Phase gate checklists
│
├── risk-management/ ← "What could go wrong?"
│ ├── risk-analysis.md 12 risks with ratings
│ ├── mitigation-strategies.md Concrete mitigations
│ └── contingency-plans.md Emergency procedures
│
└── tasks/ ← "What exactly do we build?"
├── modernization/
│ ├── 001-dependency-upgrade-dotnet9.md
│ ├── 002-dependency-upgrade-efcore9.md
│ ├── 003-implement-service-layer.md
│ ├── 004-migrate-controllers.md
│ ├── 005-migrate-views-tag-helpers.md
│ ├── 006-implement-authentication.md
│ ├── 007-implement-authorization.md
│ ├── 008-security-hardening.md
│ ├── 009-replace-msmq-service-bus.md
│ ├── 010-replace-local-storage-blob.md
│ ├── 011-migrate-azure-sql.md
│ ├── 012-setup-cicd-pipeline.md
│ ├── 013-infrastructure-as-code.md
│ ├── 014-monitoring-observability.md
│ └── 015-performance-optimization.md
└── testing/
├── 001-unit-tests.md
├── 002-integration-tests.md
├── 003-security-tests.md
└── 004-performance-tests.md
| Phase | Focus | Tasks |
|---|---|---|
| 1 — Foundation | .NET 9 project, models, EF Core 9 | MOD-001, MOD-002 |
| 2 — Architecture | Service layer, controllers, views | MOD-003, MOD-004, MOD-005 |
| 3 — Security | Authentication, authorization, hardening | MOD-006, MOD-007, MOD-008 |
| 4 — Cloud Services | Service Bus, Blob Storage, Azure SQL | MOD-009, MOD-010, MOD-011 |
| 5 — DevOps | CI/CD, Infrastructure as Code | MOD-012, MOD-013 |
| 6 — Operations | Monitoring, performance optimization | MOD-014, MOD-015 |
💡 Key insight: At this point, zero code has been changed. You have a complete, reviewable plan that captures every decision, risk, and trade-off. This is the power of Spec-Driven Development — you can review, discuss, approve (or adjust) the approach before writing a single line of code.
📂 Branch:
brown-modernize-dev— View changes from brown-modernize-plan →
Using the /plan prompt, the Dev agent reads the task specifications from Step 3 and systematically implements the modernization — building the new .NET 9 application.
- Run the
/planprompt in Copilot Chat - The agent reads all task files in
specs/modernize/tasks/ - It implements tasks MOD-001 through MOD-006 (core migration)
- Build and run the application to verify:
dotnet build→dotnet run
A complete new ASP.NET Core 9.0 MVC application at src/ContosoUniversity.Web/:
src/ContosoUniversity.Web/
├── ContosoUniversity.Web.csproj ← SDK-style project targeting net9.0
├── Program.cs ← Minimal hosting with DI, health checks
├── appsettings.json ← Configuration
│
├── Models/ ← 10 entity models + 3 view models
│ ├── Person.cs (abstract base)
│ ├── Student.cs, Instructor.cs
│ ├── Course.cs, Department.cs
│ ├── Enrollment.cs, Notification.cs
│ └── SchoolViewModels/
│
├── Data/
│ ├── SchoolContext.cs ← EF Core 9 DbContext (9 DbSets)
│ └── DbInitializer.cs ← Seed data
│
├── Services/ ← Clean separation of concerns
│ ├── IStudentService.cs + StudentService.cs
│ ├── ICourseService.cs + CourseService.cs
│ ├── IInstructorService.cs + InstructorService.cs
│ ├── IDepartmentService.cs + DepartmentService.cs
│ └── INotificationService.cs + DatabaseNotificationService.cs
│
├── Controllers/ ← 6 fully async controllers
├── Views/ ← 28 Razor views with Tag Helpers
├── PaginatedList.cs ← Async pagination utility
│
└── wwwroot/
├── css/ ← Custom styling
├── js/ ← Notification system
└── lib/ ← Bootstrap 5.3, jQuery 3.7 (via libman)
| Before (.NET Framework 4.8) | After (.NET 9) |
|---|---|
Global.asax.cs startup |
Program.cs minimal hosting |
Web.config configuration |
appsettings.json |
packages.config (45 packages) |
SDK-style csproj (2 PackageReferences) |
Fat controllers with SchoolContextFactory |
Service layer + constructor injection |
| Synchronous database calls | Fully async/await with Task<IActionResult> |
@Html.ActionLink() helpers |
<a asp-action=""> Tag Helpers |
BundleConfig.cs |
libman.json (Bootstrap 5.3, jQuery 3.7) |
| MSMQ notifications | Database-backed notification service |
Server.MapPath() file uploads |
IWebHostEnvironment.WebRootPath + IFormFile |
| No dependency injection | Full DI with 5 scoped services |
cd src/ContosoUniversity.Web
dotnet build # ✅ 0 errors, 0 warnings
dotnet run # ✅ Application starts and serves requests💡 Key insight: The agent didn't blindly convert files — it re-architected the application following the patterns and decisions defined in the specification tasks. Controllers got service layers, views were rewritten with Tag Helpers, the full async pipeline was implemented, and MSMQ was replaced with a cross-platform solution. Every decision traces back to a spec.
📂 Branch:
brown-modernize-deploy— View changes from brown-modernize-dev →
Using the /deploy prompt, the Azure agent analyzes the modernized application and creates everything needed for production deployment to Azure.
- Run the
/deployprompt in Copilot Chat - The agent analyzes the app, retrieves Azure best practices, and generates all deployment artifacts
- Review the Dockerfile, Bicep templates, and CI/CD pipelines
A multi-stage Dockerfile with security best practices:
- Build stage: .NET 9 SDK for compilation
- Runtime stage: ASP.NET 9 minimal image
- Non-root user, port 8080, health check endpoint
infra/
├── main.bicep ← Orchestrator (subscription scope)
├── main.parameters.bicepparam ← AZD environment parameters
└── modules/
├── identity.bicep ← User-assigned managed identity
├── log-analytics.bicep ← Centralized logging
├── app-insights.bicep ← Application performance monitoring
├── key-vault.bicep ← Secrets management with RBAC
├── sql.bicep ← Azure SQL Server + Database
├── storage.bicep ← Blob Storage (teaching materials)
├── container-registry.bicep ← ACR with AcrPull role assignment
├── container-apps-env.bicep ← Container Apps Environment
└── container-app.bicep ← Container App with health probes
| Workflow | Trigger | What it does |
|---|---|---|
| ci.yml | PR / push to main |
Build → Test → Docker image verify |
| deploy.yml | Push to main / manual |
Staging auto-deploy → Health check → Production (manual approval) |
┌────────────────────────────────────────────────────────────┐
│ Resource Group │
│ │
│ User ──HTTPS──► Container App ──► Azure SQL Database │
│ │ │
│ ├──► Blob Storage (file uploads) │
│ ├──► Key Vault (secrets, RBAC) │
│ ├──► Application Insights (telemetry) │
│ │ │ │
│ │ Log Analytics (centralized logs) │
│ │ │
│ Container Registry ◄──── Managed Identity │
└────────────────────────────────────────────────────────────┘
| Practice | Implementation |
|---|---|
| Managed Identity | All service-to-service auth — no passwords in code |
| Key Vault | RBAC authorization, purge protection enabled |
| Storage | Local auth disabled, no public blob access |
| TLS | 1.2 minimum on all services |
| Container | Non-root user |
| Health probes | Liveness + Readiness on /healthz |
| Registry | Anonymous pull disabled, AcrPull via managed identity |
# One-command deployment with Azure Developer CLI
azd auth login
azd env new contoso-dev
azd env set AZURE_LOCATION eastus
azd env set SQL_ADMIN_PASSWORD '<YourStr0ngP@ssw0rd!>'
azd up # Provisions infrastructure + builds + deploys💡 Key insight: The
/deployagent doesn't just create files — it follows Azure best practices retrieved in real-time, validates all Bicep templates for errors, ensures AZD rule compliance, and produces infrastructure that's production-ready from day one.
Here's what the Spec-Driven approach produced across all 5 steps:
| Metric | Count |
|---|---|
| Specification documents created | 57+ |
| Business features documented | 7 FRDs |
| Risks identified & mitigated | 12 |
| Modernization tasks defined | 19 (15 mod + 4 test) |
| Entity models migrated | 10 + 3 view models |
| Controllers migrated | 6 (fully async) |
| Views migrated | 28 (Tag Helpers) |
| Services created | 5 interfaces + 5 implementations |
| Azure resources defined | 10 Bicep modules |
| CI/CD workflows | 2 (CI + CD with staging/prod) |
| Build warnings | 0 |
| Branch | Step | What Happens | Key Artifacts |
|---|---|---|---|
main |
0 | Template repository | .github/prompts/, docs/ |
code-import |
1 | Legacy app imported | src/ContosoUniversity/ |
brown-analyze |
2 | Reverse engineering | specs/features/, specs/docs/ |
brown-modernize-plan |
3 | Modernization plan | specs/modernize/ (57 files) |
brown-modernize-dev |
4 | New app built & running | src/ContosoUniversity.Web/ |
brown-modernize-deploy |
5 | Containerized + Azure IaC | Dockerfile, infra/, azure.yaml |
| Tool | Purpose | Install |
|---|---|---|
| VS Code | IDE with Copilot integration | Download |
| GitHub Copilot | AI agent mode for running prompts | Setup |
| .NET 9 SDK | Build the modernized application | Download |
| Docker | Container build and local testing | Download |
| Azure CLI | Azure resource management | Install |
| Azure Developer CLI (azd) | One-command Azure deployment | Install |
| Resource | Description |
|---|---|
| Spec2Cloud Framework | The template repository and full framework documentation |
| SPEC2CLOUD.md | Framework architecture and agent details |
| Deployment Guide | Azure deployment instructions and runbook |
| Workflow Documentation | Detailed workflow descriptions |
| Specs Structure Guide | How the specs folder is organized |
| INTEGRATION.md | Installing Spec2Cloud into existing projects |
Contributions are welcome! See docs/contributing.md for guidelines.
This project is licensed under the MIT License — see LICENSE.md for details.