Continuous Delivery to (on premise) VM inaccessible from internet

Did you know that automated deployment to environments hidden behind firewall, e.g. intranet web application in a VNET is surprisingly easy with Azure DevOps?

Azure DevOps is the most productive CI/CD platform by large margin and it won't stop to surprise me how incredibly helpful and user friendly it is.

This time I tried to automate repetitive tasks of deploying new versions of my app to test environment on VM that is not accessible from internet.

How I deployed new versions manually

  1. Make backup copy of web.config and appsettings.json files because it contains local configuration and secrets like connection strings
  2. Stop the IIS website
  3. Copy build artifacts
  4. Merge the config and settings files
  5. Start IIS website
  6. Open browser and perform some smoke tests

Deploying website automatically using Azure DevOps

The magic here is that Azure DevOps will give you a nice PowerShell script that you run on your VM. It will install an agent and register it with the Azure DevOps. Done. The communication between Azure DevOps and the VM behind firewall is taken care of. All you need is access from the VM to the internet.

I have followed this tutorial which describes creating Deployment Group and registering you VM in the group: Automating deployments to on premise servers with Azure DevOps | by Sam Wright | Level Up Coding (

Release pipeline

Consists of only two tasks:

  1. IIS Web App Deploy - automates tasks 1-5  from above.
    The secrets are now securely stored in Release Variables in Azure DevOps and the task takes care of replacing values in web.config and appsettings.json.
  2. Invoke a REST api (health check)

    I have created my own web api controller that perform some smoke tests, e.g database connectivity. If I'm able to call it, I consider my app deployed.

    It's important to note here, that the Web App is not accessible from internet, so we can't use build in Invoke Rest Api task, see Stack Overflow question.

    Instead I invoke the REST api using Power Shell on the VM. Azure DevOps again is very helpful by reading the script output.

Moral of the story

Spinning up the entire environment in deployment pipeline might be difficult and time consuming. However automating the repetitive tasks that needs to be done each time you deploy new version is usually pretty straightforward and is ALWAYS worth it, even in a simple hobby project.

Not only it saves your time, but ultimately causes more frequent releases and is also very welcomed documentation of what is happening during a release.

Nobody said you have to automate everything in order to be productive. For example, if you don't feel like automating database deployment yet, automate the rest.