So you’ve decided to port your ASP.NET 4.x Azure Web Apps to the Google Cloud Platform (GCP). The only option available today for hosting non-Core ASP.NET in GCP is with Windows Server VMs in Compute Engine. Let’s start by acknowledging that this is a move from a Platform as a Service (PaaS) to Infrastructure as a Service (IaaS), with all the typical tradeoffs: in short, you sacrifice rich features in exchange for increased granularity of control. With that in mind, let’s take a look at some of the most fundamental tasks in deploying and managing a cloud-based app: publishing to a cloud testing environment and then deploying a production version to a load-balanced, auto-scaling cluster.

Publishing an ASP.NET Web App to a Windows Server VM in GCP

In another topic, I talked through the steps to take to create a new Windows Server VM ready to run ASP.NET applications in GCP. By the end of those steps, you should have created a Windows user account and gotten back a random password. You now have everything you need to publish an ASP.NET application to the Windows Server VM, straight from Visual Studio. Select your web app, then select Build | Publish… to open the Publish dialog. Under “Select a publish target”, choose Custom. Fill in the Server and Destination URL using the Site address in the Deployment Manager.

And fill in the User name and Password with the Windows user you created and the password generated by GCP. Fresh out of the box, IIS is configured with a single web site named “Default Web Site”. Use that as the Site name.

And that’s it! Click Publish, and Visual Studio will build and publish the web app to IIS on the VM. After publishing, your default browser will automatically open the Destination URL.

Common additional steps

Enable SSL

Most web sites and applications these days support SSL or even insist upon it. You will have to take a few extra steps to enable this in your Windows Server VM. If you already know your way around IIS, then you know how to do this. However, if you’re coming from Azure, you might not. Here’s a quick visual guide.

  1. In the Site Actions menu, click Bindings…
  2. In the Site Bindings dialog, click Add…
  3. In the Add Site Binding dialog, set the Type to “https”
  4. Select an SSL certificate. GCP Windows Server instances come with a self-signed certificate that you can use for development, but for production you’ll want to purchase a certificate signed by a widely recognized Certificate Authority (CA).

Enable URL Rewriting

Another common scenario that goes hand-in-hand with enabling SSL is forcing SSL by using URL Rewriting. This is enabled by default in Azure, but not in IIS. In fact, if you have an app that includes URL Rewriting in web.config, your app will simply not work until you enable this feature in IIS. The error you’ll see will simply state “HTTP Error 500.19 — Internal Server Error. The requested page cannot be accessed because the related configuration data for the page is invalid.” The easiest way to enable it is to use the Web Platform Installer to search for “URL Rewrite” and install it.

Of course, lacking the URL Rewrite module is not the only cause of a 500.19 error. Other configuration-related errors including malformed or invalid XML may also result in this error.

Preparing for Horizontal Scaling

To me, deploying to a single instance in the cloud for testing fills a pretty similar role to having a staging slot in Azure. It’s probably OK that the staging environment is a single instance, but in production you’re probably going to want so scale on demand or even automatically. Let’s take a look at the steps needed to create an auto-scaling, load-balanced cluster from that staged web app. There’s already a great walkthrough in the GCP dotnet docs. Don’t be intimidated; in spite of the length, it is not a complicated process–merely a multi-step one.

I stepped through the walkthrough myself, but rather than using the bookshelf sample app, I wanted to deploy a super-simple app that just reported the IP address of the host that handled the request so I could see for myself that requests were being distributed across instance group members. Whenever the walkthrough said to enable API access, I skipped those, because my app didn’t use any GCP APIs. I also chose “None” for Session affinity to get a round-robin distribution, to more easily observe that traffic was going to the 2 instances I requested for the group I created. Once it was all set up, I could see my VM instances created by the instance group.

And navigating to the IP address of the load balancer resulted in a page that reported back one of the two internal IP addresses.

According to the documentation, traffic will continue to be routed to the same host for the duration of the session. I was able to confirm this by refreshing the page and observing that the address did not change. Then I navigated to the load balancer’s IP address in an incognito window and got back the other instance’s IP address.

Scaling an ASP.NET web app–or any other app in VMs–is currently a manual multi-step process that can’t be fully automated due to the need to RDP into the target VM to run GCESysPrep. There is also currently not a really good story for rolling out updates to instance groups yet; the Instance Group Updater service is currently in alpha. I hope to see it become ready for production use soon!


As you may have noticed, while it is possible to migrate an ASP.NET 4.x web app from Azure to GCP VMs, it comes at the cost of some conveniences that you may have taken for granted in Azure. This is obviously a bit of an unfair comparison since the same could be said about Azure Web Apps vs Azure VMs. A closer comparison would be moving an ASP.NET Core Web App from Azure to GCP App Engine Flex, but that’s better left as the topic of another post.

The following two tabs change content below.