Home / Blog / How to Set Up CI/CD for Legacy Applications Migrated to .NET Core
0%

How to Set Up CI/CD for Legacy Applications Migrated to .NET Core

Migrating a legacy .NET application to modern .NET is often treated as the finish line. In practice, it is usually only one stage of modernization.

An application can move from an older .NET Framework architecture to modern .NET while the surrounding development and deployment process remains unchanged. Teams may still rely on manually configured build servers, custom deployment scripts, shared environments, undocumented release procedures, and production changes that depend on individual engineers.

This creates a familiar problem: the application is modernized, but the way it is delivered is still legacy.

Setting up CI/CD for legacy .NET Core applications helps close this gap. A properly designed pipeline can automate builds, testing, security checks, artifact creation, deployments, and rollback procedures while providing a consistent path from source code to production.

For organizations that depend on business-critical legacy applications, this is particularly important. The objective is not simply to deploy faster. It is to make releases repeatable, auditable, testable, and safer without disrupting existing operations.

Our guide explains how to build a CI/CD process for a modernized legacy .NET application and how to adapt the approach when the application still contains legacy components, on-premises dependencies, or hybrid infrastructure.

Why Legacy Applications Often Need CI/CD After .NET Migration

A typical modernization project may update the application itself but leave its delivery process largely untouched.

For example, an organization may have:

  • an older .NET Framework application migrated to modern .NET;
  • SQL Server or another legacy database;
  • IIS-based hosting;
  • PowerShell deployment scripts;
  • manually configured Windows servers;
  • shared build machines;
  • production configuration stored outside source control;
  • limited automated testing;
  • separate development and production configurations;
  • no standardized rollback process.

The result is a modern application sitting inside a legacy delivery model.

This creates several risks.

Manual deployments

Deployments may depend on engineers copying files, modifying configuration, restarting services, or executing scripts manually.

Every additional manual step introduces an opportunity for error.

Shared build environments

If builds depend on a specific developer workstation or server configuration, reproducing the build can become difficult. A new engineer may install a slightly different SDK version or package dependency and receive different results.

Build configuration outside source control

When pipeline configuration exists only on a build server, it becomes difficult to understand how an artifact was produced or reproduce a previous release.

No reliable rollback

If a deployment fails, teams may have to manually restore the previous version.

For business-critical applications, this can turn a deployment issue into prolonged downtime.

Configuration drift

Development, staging, and production environments may contain different versions of dependencies, configuration files, operating-system components, or infrastructure.

CI/CD cannot eliminate all environment differences, but it can make them explicit and automate deployment consistently.

What Changes When a Legacy Application Moves to Modern .NET?

Modern .NET provides a much stronger foundation for automated builds and deployments than many older .NET Framework applications.

Modern .NET supports cross-platform execution, SDK-style projects, command-line tooling, containers, automated testing, and cloud deployment models.

A typical .NET Core CI/CD pipeline can therefore use standard commands such as:

dotnet restore
dotnet build
dotnet test
dotnet publish

The application can then be packaged as a deployment artifact or container image.

This makes the build process independent from an individual developer’s machine.

Modern .NET also supports deployments to:

  • Windows and Linux servers;
  • IIS;
  • Azure App Service;
  • containers;
  • Kubernetes;
  • AWS services;
  • on-premises infrastructure.

This flexibility is particularly useful during gradual modernization.

The application does not necessarily have to move directly from an old server to a fully cloud-native architecture. CI/CD can support intermediate stages such as:

Legacy application → modern .NET → automated deployment → hybrid hosting → cloud migration → further architectural modernization

Choosing a CI/CD Toolchain

The right CI/CD platform depends on the organization’s existing infrastructure, cloud strategy, security requirements, and development workflow.

Azure DevOps

Azure DevOps for legacy systems can be a natural choice for organizations already invested in Microsoft technologies.

Azure Pipelines supports:

  • Windows and Linux build agents;
  • .NET builds;
  • automated testing;
  • artifact management;
  • Azure deployments;
  • approval gates;
  • environment management;
  • integration with Azure services.

It can also work with on-premises infrastructure through self-hosted agents.

This makes it useful for organizations transitioning gradually from on-premises infrastructure to Azure.

GitHub Actions

GitHub Actions for .NET Core provides pipeline automation directly within GitHub repositories.

A workflow can:

  1. check out the code;
  2. install the required .NET SDK;
  3. restore NuGet packages;
  4. build the application;
  5. execute tests;
  6. publish an artifact;
  7. deploy it to the target environment.

It is particularly convenient when the development team already uses GitHub for source control and pull requests.

Jenkins

A Jenkins .NET Core pipeline can be appropriate when an organization already has Jenkins infrastructure or needs extensive customization.

Jenkins can integrate with:

  • .NET SDKs;
  • Git repositories;
  • NuGet;
  • Docker;
  • Kubernetes;
  • cloud platforms;
  • static analysis tools;
  • security scanners.

However, Jenkins introduces additional infrastructure and maintenance requirements, so teams should consider whether they need its flexibility or would benefit from a managed CI/CD platform.

Other platforms

GitLab CI/CD, TeamCity, and Octopus Deploy can also be appropriate depending on the organization’s existing toolchain.

The important principle is not which CI/CD product is selected.

The important principle is that the build and deployment process becomes automated, version-controlled, repeatable, and observable.

Step-by-Step Guide: Setting Up CI/CD for a Modernized Legacy .NET Application

Step 1: Put the Application and Pipeline Configuration in Source Control

Start by moving application code into Git if it is not already there.

For larger applications, decide whether a monorepo or multiple repositories better reflects the architecture.

The pipeline configuration should live alongside the code whenever practical.

For example:

/src
/tests
/infrastructure
/deployment
.github/workflows/

or, for Azure DevOps:

azure-pipelines.yml

This makes pipeline changes reviewable through pull requests.

It also creates an audit trail of how the application was built and deployed.

Branching and pull requests

A modernization project does not necessarily need an elaborate Git branching model.

A simpler approach is often easier to maintain:

  • feature branch;
  • pull request;
  • automated validation;
  • merge;
  • build artifact;
  • deployment to a controlled environment.

PR validation should ideally run at least:

  • compilation;
  • unit tests;
  • static analysis;
  • dependency checks.

Step 2: Build the Application Automatically

The next step is to create a reproducible build.

A basic pipeline can execute:

dotnet restore
dotnet build --configuration Release --no-restore
dotnet test --configuration Release --no-build
dotnet publish --configuration Release --no-build

The exact commands depend on the solution structure, but the principle remains the same.

The build agent should use a defined .NET SDK version rather than relying on whatever happens to be installed on the machine.

A global.json file can be used where appropriate to control the SDK version used by the project.

This is particularly useful for migrated applications where different components may have different framework or SDK requirements.

Step 3: Manage NuGet Packages Consistently

Package management becomes especially important during .NET Core migration CI/CD.

Legacy applications may contain years of dependencies, including packages that are:

  • outdated;
  • unsupported;
  • duplicated;
  • incompatible with modern .NET;
  • vulnerable.

The pipeline should restore dependencies from controlled package sources and produce reproducible builds.

NuGet package management should therefore be treated as part of the CI/CD process rather than as a developer-only concern.

Organizations should also consider:

  • dependency vulnerability scanning;
  • approved package sources;
  • package version policies;
  • lock files where appropriate;
  • internal NuGet feeds for proprietary packages.

Step 4: Add Automated Testing

CI/CD is only useful if the pipeline can identify problems before production.

At minimum, the pipeline should include unit and integration testing.

Unit tests

Unit tests validate individual components without requiring the complete application environment.

They are generally fast enough to run on every pull request.

Integration tests

Integration tests validate interactions between components such as:

  • application and database;
  • APIs;
  • authentication;
  • message queues;
  • external services.

Legacy applications often have limited automated test coverage, so modernization is an opportunity to build tests around the most business-critical workflows.

For example, if the application processes orders, prioritize automated tests for:

  • order creation;
  • pricing;
  • payment;
  • inventory updates;
  • order status;
  • reporting.

The objective is not to achieve an arbitrary test coverage percentage.

The objective is to create enough automated validation to make incremental modernization safer.

Step 5: Add Static Analysis and Security Checks

The CI pipeline should also validate code quality and dependencies.

Depending on the technology stack, this can include:

  • static code analysis;
  • code formatting;
  • dependency vulnerability scanning;
  • secret detection;
  • container image scanning;
  • infrastructure configuration scanning.

For legacy applications, these checks are especially useful because modernization often exposes technical debt that was previously hidden.

The pipeline becomes a mechanism for preventing new technical debt from accumulating while the existing debt is addressed.

Step 6: Create a Build and Release Pipeline

A mature pipeline should separate building from deploying.

A simplified model is:

Commit
   ↓
Build
   ↓
Unit Tests
   ↓
Integration Tests
   ↓
Security / Code Analysis
   ↓
Publish Artifact
   ↓
Deploy to Development
   ↓
Deploy to Staging
   ↓
Approval / Validation
   ↓
Production

The build should produce a versioned artifact.

The same artifact should then move through environments rather than rebuilding the application separately for each environment.

This is an important principle for legacy .NET automation.

If the production build is different from the staging build, testing staging does not provide the same confidence.

Step 7: Parameterize Environment Configuration

Application configuration should not be hard-coded into the application package.

Use environment-specific configuration for values such as:

  • database connection information;
  • API endpoints;
  • feature flags;
  • service URLs;
  • logging configuration.

Sensitive information should never be stored directly in source control.

Instead, use secure secrets management through services such as:

  • Azure Key Vault;
  • AWS Secrets Manager;
  • GitHub Secrets;
  • dedicated enterprise secret-management platforms.

Environment variables or managed identities can then provide applications with access to required resources without embedding credentials in code.

Step 8: Automate Deployment

The deployment method depends on where the application runs.

IIS / on-premises

For an application that still runs on Windows and IIS, deployment can be automated using tools such as Web Deploy, PowerShell, or an agent-based deployment platform.

The important change is that the deployment should no longer depend on an engineer manually copying files and changing settings.

Azure App Service

For an application moving to Azure, CI/CD can deploy directly to Azure App Service.

Deployment slots can provide a safer release mechanism:

Production
     ↑
Staging slot
     ↑
Automated deployment

The new version can be deployed to the staging slot, tested, and then swapped into production.

Containerized deployment

A migrated application can also be packaged as a Docker container.

The pipeline becomes:

Source code
   ↓
.NET build
   ↓
Tests
   ↓
Docker image
   ↓
Container registry
   ↓
Deployment

Containers can then be deployed to environments such as Azure Container Apps, Azure Kubernetes Service, or AWS ECS.

Containerisation is not mandatory for every legacy modernization project. It should be introduced when it provides a practical operational or architectural benefit.

Step 9: Support Hybrid and On-Premises Environments

Many organizations modernizing legacy applications are not moving everything to the cloud immediately.

They may have:

  • an on-premises SQL Server;
  • cloud-hosted application services;
  • legacy Windows servers;
  • SaaS integrations;
  • internal APIs;
  • VPN or private networking.

CI/CD therefore needs to support on-prem to cloud CI/CD rather than assuming a fully cloud-native environment from day one.

Self-hosted build or deployment agents can be used where applications or infrastructure cannot be accessed directly from a public CI/CD environment.

The pipeline can then deploy different components to different environments while keeping the overall release process consistent.

Step 10: Design a Rollback Strategy

A deployment is not complete until the team knows what happens when it fails.

A rollback strategy should define:

  • what constitutes a failed deployment;
  • who can initiate rollback;
  • how the previous version is identified;
  • how application binaries are restored;
  • how database changes are handled;
  • how configuration is reverted;
  • how traffic is redirected;
  • how stakeholders are notified.

Versioned artifacts make application rollback relatively straightforward.

Database rollback requires additional planning because schema changes can be destructive.

For database migrations, teams should generally favor backward-compatible changes where possible:

  1. introduce new schema elements;
  2. deploy code that can work with both old and new structures;
  3. migrate data;
  4. remove obsolete structures later.

This reduces the risk of coupling a database migration to a single irreversible application deployment.

Having trouble implementing these steps yourself? Contact ModLogix for professional guidance and hands-on support.

Observability After Deployment

A CI/CD pipeline should not stop at “deployment successful.”

The team also needs to know whether the new version is actually working.

Depending on the environment, monitoring can include:

  • application logs;
  • request latency;
  • error rates;
  • database performance;
  • infrastructure metrics;
  • dependency failures;
  • CPU and memory;
  • availability.

For Azure deployments, Application Insights can provide application monitoring and telemetry.

For containerized environments, Prometheus and other observability tools can be integrated into the platform.

A deployment should ideally trigger automated health checks.

For example:

Deploy
  ↓
Health check
  ↓
Smoke tests
  ↓
Monitor errors
  ↓
Continue / Roll back

This moves CI/CD closer to continuous delivery rather than simply automated file transfer.

Best Practices for CI/CD During Legacy Modernization

Keep pipelines with the code

Use YAML pipeline configuration wherever practical.

This makes infrastructure and delivery logic version-controlled and reviewable.

Use infrastructure as code

Infrastructure as Code can define:

  • networks;
  • compute;
  • databases;
  • security configuration;
  • monitoring;
  • deployment environments.

This helps prevent infrastructure drift between environments.

Separate configuration from application code

Environment-specific values should be injected during deployment rather than hard-coded into the application.

Build once, deploy many times

Create one tested artifact and promote it across environments.

Do not rebuild separately for staging and production.

Automate the highest-risk manual processes first

You do not have to automate every operational task immediately.

Start with the processes most likely to cause deployment failures:

  • builds;
  • database deployment;
  • configuration;
  • application packaging;
  • server configuration;
  • production deployment.

Introduce containers selectively

Containers can improve portability and consistency, but containerizing a legacy application should not become a modernization goal in itself.

If the application still depends on a specific Windows environment or legacy integration, a VM-based deployment may be the appropriate intermediate architecture.

Treat the pipeline as production software

CI/CD pipelines can become business-critical infrastructure.

They should therefore have:

  • version control;
  • code review;
  • access control;
  • documentation;
  • monitoring;
  • backup and recovery procedures.

Example GitHub Actions Pipeline for a Modern .NET Application

A basic GitHub Actions for .NET Core workflow might look like this:

name: .NET CI/CD

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.x'

      - name: Restore
        run: dotnet restore

      - name: Build
        run: dotnet build --configuration Release --no-restore

      - name: Test
        run: dotnet test --configuration Release --no-build

      - name: Publish
        run: dotnet publish --configuration Release --output ./publish

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: application
          path: ./publish

This example intentionally focuses on the CI portion.

A production pipeline would normally add:

  • dependency scanning;
  • code analysis;
  • integration testing;
  • artifact versioning;
  • environment-specific deployment;
  • secure secrets management;
  • approval gates;
  • health checks;
  • rollback mechanisms.

The same principles can be implemented using Azure Pipelines, GitLab CI/CD, Jenkins, or another platform.

How CI/CD Fits Into a Larger Legacy Modernization Strategy

CI/CD should not be considered an isolated DevOps project.

For organizations modernizing legacy software, it is one layer of a broader transformation:

Legacy application
       ↓
Application assessment
       ↓
.NET modernization
       ↓
Automated build & testing
       ↓
CI/CD
       ↓
Infrastructure automation
       ↓
Cloud / hybrid deployment
       ↓
Incremental application modernization

This allows the organization to improve the delivery process while continuing to modernize the application itself.

For example, a monolithic application can initially be migrated to modern .NET and deployed as a single unit.

Later, selected functionality can be extracted into APIs or independent services.

Because CI/CD is already established, each new component can use automated testing and deployment rather than introducing another manual release process.

Conclusion

Migrating a legacy application to modern .NET does not automatically solve the operational problems surrounding it.

If builds are still manual, deployments still depend on individual engineers, configuration still drifts between environments, and production releases cannot be rolled back safely, the organization still carries significant delivery risk.

CI/CD for legacy .NET Core applications provides the foundation for addressing those problems.

By introducing source-controlled pipeline configuration, automated builds, NuGet package management, unit and integration testing, security validation, artifact versioning, automated deployment, secrets management, observability, and rollback procedures, organizations can make modernized applications significantly easier to operate.

The right implementation depends on the application’s current architecture.

A legacy system running on IIS may benefit from automated Windows deployments first. A system moving toward Azure may use Azure DevOps or GitHub Actions with Azure App Service. A suitable application may eventually move into containers and be deployed through a cloud-native platform.

The important point is to modernize delivery incrementally alongside the application.

For businesses that depend on legacy systems, the goal is not simply to introduce a new pipeline. It is to create a repeatable engineering process that allows teams to release changes safely, test modernization work continuously, and gradually move from legacy infrastructure toward a maintainable modern application platform.

CI/CD is therefore not the final step after .NET modernization. It is what makes continued modernization safer and more sustainable.

Latest Blog Posts

Ask Us Any Question

* Please enter your name
* Please leave a message









    elipse
    Whitepaper.doc
    close
    How to protect your business from getting obsolete?

    Download the white paper to discover hands-on approaches to aging software and mainframes.

    DOWNLOAD
    Get Your FREE Template!

    Please, provide the email address so that we can send the whitepaper to you.

    * Please enter your name








      Whitepaper.doc
      close
      Get Your FREE Template!

      Please, provide the email address so that we can send the whitepaper to you.

      * Please enter your name








        CHECKLIST.DOC
        close
        Get Your FREE Template!

        Please, provide the email address so that we can send the checklist to you.

        * Please enter your name