Date posted: 24 Nov 2020, 2 minutes to read

Use GitHub Actions with a private runner to deploy to IIS

Recently I got asked if you could use GitHub Actions to deploy to an IIS web application which of course I had to test :grin:.

TL;DR It runs the same as you would with a PowerShell script

GitHub Actions Logo


For testing this I used an example application in this repo (you can find the actions there as well). It’s based on the following dotnet command:

dotnet new webapp


Since this is a .NET Core application, the workflow for GitHub Actions has these steps:

  1. Checkout the repo
  2. Set the correct .NET Core version
  3. dotnet build
  4. dotnet publish
  5. deploy to IIS
  6. Run a smoketest
  7. Run the webtests (added as an extra example, keep on reading for more info)

You can find that workflow here. 💡 If you want to see the workflow for pushing the application to an Azure App Service, check the dotnetcore.yml file next to it.


For running the IIS commands I’ve used the most simple example, other command line options will work as well:

  1. Stop the website (or the entire webserver in this case)
  2. Overwrite all files
  3. Start the website again Using WebDeploy or a remote PowerShell session will work as well. Find more explanation on remoting in this blogpost as well.


The actual actions that ‘deploy’ the application are as follows.

- name: Deploy to IIS
      run: |
        iisreset /stop
        Copy-Item ./dotnetcorewebapp/* C:/inetpub/wwwroot/dotnetcore-webapp -Recurse -Force
        iisreset /start
Note: running these steps requires Admin level access rights, so you’ll need to run the self-hosted runner with that access level. This stems from the AppExec commands that it fires that require that level of access (still an unfortunate thing).

Private GitHub Action Runner

To enable the deployment of the application on a Windows box, you’ll have to use a private GitHub action runner since the cloud hosted runners will not have access to that machine (they shouldn’t!). You can install them like a normal runner like for example Azure DevOps. Luckily the list of URL’s you need to add to your proxy/allow list is a lot shorter than the Azure DevOps list.

The runner runs on demand or as a Windows Service and will periodically open a long polling connection to GitHub, asking if there is work to do. The connection is always outgoing and on port 443.

Installing a runner can be done from a repository, team or organization level from the website. Go to “Settings” –> Actions and scroll down to Self-hosted runners: Screenshot of the self-hosted runners view

Adding a runner is made very easy, all the steps are listed right in the screen, even including the temporary token it uses for a one time authentication process: Screenshot of the steps to add a self-hosted runner


The next question that came up was if you could run a Selenium WebTest (as I call that type of end-to-end test) with such a runner and if that would also work with a hosted runner. Long story short: it just works.

In both workflows I’ve added the last step ‘Run Web Test’ that runs the unit tests in the WebTest project that use a Selenium Driver to talk to the installed Chrome instance on the runner. You can find all the preinstalled software on the hosted runner here.

Screenshot of webtest output