10.4. Walkthrough: Windows Server & IIS Starter App Deployment

Now that we have learned about remote access mechanisms and the IIS Manager, it’s time to get our hands dirty! In this walkthrough we will:

  • Provision a Windows Server VM
  • RDP into the machine to configure the Web Server Role
  • Explore the IIS Manager and connect to the default site
  • Configure IIS to serve a .NET web app
  • Deploy and connect to a .NET starter web app served by IIS

Note

Some of the az CLI steps are shown in both Windows/PowerShell and Linux/Bash to illustrate the cross-platform nature of the tool with minor syntactical changes. However, to complete this walkthrough will require a local Windows machine in order to use RDP.

10.4.1. Provision the VM

10.4.1.1. Create a Resource Group

As always, we will begin by creating a resource group. This time we will combine creating and configuring it as the default group into one step.

Notice how we use the --query argument to have the output of the create command be just the name of the new resource group. We perform all of this within an in-line execution so the output (the resource group name) can be assigned as the default group value:

Windows/PowerShell
> az configure -d group=$(az group create -n <name>-ws-wt --query "name")
Linux/Bash
# remember in Bash we have to output in tsv format to remove the default JSON quote characters
$ az configure -d group=$(az group create -n <name>-ws-wt -o tsv --query "name")

10.4.1.2. Create a Windows Server VM

Next let’s create our Windows Server VM within the new resource group. Windows Server has been around for many years and there are many versions. For our purposes, we will use the latest, 2019, edition. Just as we did with the Ubuntu VM, let’s search the available VM images for the urn of the 2019 Windows Server image. In this case, we need to provide a compound filter that will look for a urn that contains both Windows and 2019:

Windows/PowerShell
> az vm image list --query "[? contains(urn, 'Windows') && contains(urn, '2019')] | [0].urn"
# "MicrosoftWindowsServer:WindowsServer:2019-Datacenter:latest"

Let’s assign this value to a variable:

Windows/PowerShell
> $WsImageUrn=$(az vm image list --query "[? contains(urn, 'Windows') && contains(urn, '2019')] | [0].urn")
Linux/Bash
# don't forget to output in tsv format
$ ws_image_urn=$(az vm image list -o tsv --query "[? contains(urn, 'Windows') && contains(urn, '2019')] | [0].urn")

To create our VM we will use most of the same Arguments as we did when creating the Linux machine. Whereas Linux VMs will enable SSH access by default, new Windows Server VMs will have RDP enabled instead. However, recall that RDP uses a basic username and password credential set instead of the RSA keys used in SSH. We will need to provide one additional flag --admin-password when creating the WS VM:

Warning

It is important that you do not change the admin username (student) or password (LaunchCode-@zure1). Although it is a poor practice to use the same password for multiple users, we do so for consistency in order to make it easy to help you debug if somethings goes wrong.

Windows/PowerShell
> az vm create -n ws-vm --size "Standard_B2s" --image "$WsImageUrn" --admin-username "student" --admin-password "LaunchCode-@zure1" --assign-identity
Linux/Bash
$ az vm create -n ws-vm --size "Standard_B2s" --image "$ws_image_urn" --admin-username "student" --admin-password "LaunchCode-@zure1" --assign-identity

Once the VM is created, let’s set is as the default VM:

either shell
> az configure -d vm=ws-vm

10.4.2. Set Up & Explore IIS

Now that we have our Windows Server VM, we can get our first taste of using RDP. We will use RDP to enter the desktop of the VM and configure it to deploy our sample application.

Note

You must use a local Windows machine in order to RDP into the VM using the pre-installed mstsc utility.

10.4.2.1. RDP Into the VM

In order to RDP into a machine you need (at minimum):

  • The IP address
  • Username: student
  • Password: LaunchCode-@zure1

Since we have set the VM as our default we can use the list-ip-addresses command and a query filter to get its value. We will capture the public IP address in a variable so we can use it to RDP into the machine:

Windows/PowerShell
> $VmPublicIp=$(az vm list-ip-addresses --query "[0].virtualMachine.network.publicIpAddresses[0].ipAddress")
Linux/Bash
# output in tsv format
$ vm_public_ip=$(az vm list-ip-addresses -o tsv --query "[0].virtualMachine.network.publicIpAddresses[0].ipAddress")

Now we can use the built-in mstsc command-line utility to open an RDP session with the machine:

Windows/PowerShell
> mstsc /v:"$VmPublicIp"

This will begin the RDP authentication process and prompt you to enter your credentials:

RDP credentials prompt

The first time you connect to a remote machine (using default RDP settings) you will need to confirm that you trust it. This is due to the default usage of a self-signed server certificate in the VM. The discussion of Public Key Infrastructure (PKI) and certificates is outside of the scope of this course, but in this context the warning is nothing to be concerned about.

Tip

In a production setting, you would likely configure a Group Policy Object (GPO) for enforcing trusted connections. If you are curious feel free to look over the linked article, but do not be concerned if it goes over your head!

For now you can select Don’t ask me again and confirm to continue:

RDP trust remote server prompt

If everything goes well, a new window will appear that gives you access to the full desktop of the remote machine.

10.4.2.1.1. Explore the Server Manager

The Server Manager application will then open to the dashboard overview:

Windows Server Manager dashboard view

The SM can be used to monitor and manage fleets of servers, but for our purposes we will focus on a single server. You can select the Local Server tab on the left to switch to a view specific to the current VM:

Windows Server Manager local server view

Take a moment to explore this section of the SM. You can find details about how the server is configured as well as live performance statistics like CPU and memory usage.

Windows Server Manager local server usage statistics

10.4.2.2. Configure Web Server Role

Before we can host our application, we need to configure our VM to operate as a Web Server Role. In the top right corner of the SM you will see a Manage dropdown containing an option to Add Roles and Features. This will open the Roles and Features wizard:

Windows Server Manager add Roles & Features

Because we are configuring this single server we can select the first option:

Roles & Features wizard Role based installation

We want to select our server by its name. We should only have a single server in our pool:

Roles & Features wizard select server by name

We want to configure our server to assume the Web Server Role to use the IIS Web Server. You can find this role at the end of the Server Roles list:

Roles & Features wizard add Web Server (IIS) Role

Because IIS requires the IIS Management Console to configure it, we are prompted to install the required feature. Although it can be installed and used remotely we will install it locally on this server. Select Add Features to install it:

Roles & Features wizard install IIS Management Console Feature

For our purposes we will not require any other role services beyond the defaults. Feel free to read over what each role service does by selecting it and reading its description on the right side panel. Be careful not to check any boxes beyond those that are already selected by default:

Roles & Features wizard select IIS Role Service defaults

Finally you can continue to the Confirmation tab. Double check that your selections match the list below. The installation process may take a minute or two but will not require a restart:

Roles & Features wizard confirm Web Server (IIS) Role

10.4.2.3. Explore the IIS Manager

Once the installation is complete, you can open the IIS Manager. In your taskbar search for IIS:

Search for IIS Manager

The IIS Manager dashboard shows all of the servers that are linked to it. In our case, we will see just our single VM listed. Within each Server are sections for configuring the application pools and sites that will be served by IIS from that machine.

IIS includes a pre-configured default site and application pool to get you started. Let’s take a look at the Default site:

IIS Manager Sites view

From the Sites tab you can see all of the sites that are being served by IIS. Notice how each site has a name, a binding (what port it listens on) and a path to its content directory.

Selecting the Default Site will display the Site dashboard. From here you can configure all of the content-related aspects of the site.

IIS Manager Default Site dashboard

At the bottom of the view is a tab to switch from Features to Content. Selecting the Content tab shows the contents of the site’s directory. For the default site there are just two files, an HTML file and an image:

IIS Manager Default Site content view

Within the Content view mode you can select the Explore option on the right-side panel.

IIS default Site Explore action

This will open the file explorer to the content directory path to see and manage the files directly. Notice how this directory path matches the default site path we saw in the Sites overview earlier:

IIS default Site contents in file explorer

10.4.2.4. Connect to the Default Site Within the VM

Once IIS has been installed, through the Web Server Role, it immediately begins serving the default site on port 80. You can open the IE browser within the server to http://localhost to view it. Notice how we do not need to include the port because the browser sets 80 implicitly as the standard HTTP port.

Warning

As part of the Windows Server security default settings, IE is locked down to restrict its usage. Unless you have good reason to stray from these defaults, you should accept them and proceed to viewing the default site.

IIS default Site in the Server browser

10.4.2.5. Connect to the Default Site From Your Local Machine

So far, we have been able to connect to the default site within the server itself. But what about connecting to it publicly over the Internet? By now you should understand that navigating to http://localhost on your local machine will not request the default site.

Instead, we will need to use the public IP address of our VM in place of localhost. This should make sense because it is not locally hosted anymore, it is publicly hosted! Or is it?

On your local machine, open your browser and navigate to http://<your VM public IP>:

IIS default site local browser timeout

Before continuing, take a moment to consider why the connection timed out. Use what you have learned to apply critical thinking to this common issue when hosting on the web.

Tip

Connection timeouts are an indication of a network related issue. If you receive a status code 5XX it means a connection was formed but something went wrong with the web or application server. Receiving no response at all means that some sort of machine or network-level firewall has blocked the connection from ever being formed.

When we provisioned our VM, we assumed default network security group (NSG) rules. The default NSG configuration for a new VM does not allow traffic to reach the machine through any port, including the common HTTP ports (80 for http and 443 for https).

However, when you create a Windows Server VM, a new rule that exposes port 3389 is opened automatically to allow for RDP traffic. This behavior is described in the az vm create -h listing.

10.4.2.6. Adding a New NSG Rule

In order to connect to our VM, and therefore the site, we need to add an additional NSG rule that will allow traffic on port 80. Fortunately this is a quick fix using our trusty az CLI and the VM open-port Command.

assumes a default RG, location and VM have been configured
> az vm open-port --port 80

You will receive a lengthy output showing the current state of the NSG associated with the VM. Most of the output is related to the first property, defaultSecurityRules. Towards the bottom you will see the securityRules list which includes both the RDP and the new port 80 rules.

trimmed securityRules list showing rules allowing RDP and HTTP public traffic
...
"securityRules": [
  {
    "access": "Allow",
    "destinationPortRange": "3389",
    "direction": "Inbound",
    "name": "rdp",
    ...
  },
  {
    "access": "Allow",
    "destinationPortRange": "80",
    "direction": "Inbound",
    "name": "open-port-80",
    ...
  }
],
...

Note

This command opens a port for all public traffic. In other words, requests from any IP address and any protocol will be allowed access to our VM on port 80. This is a quick solution for our purposes. But in a production setting, you will likely use more rigorous NSG rules with source IP and protocol restrictions for greater security.

10.4.3. Configure the Host VM

The final steps of our walkthrough will create, publish, and deploy a .NET starter API to IIS.

10.4.3.1. Install Dependencies

In order to create and host the starter project we will need to install the following dependencies:

  • chocolatey: the package manager for Windows to install other dependencies
  • dotnet: the .NET SDK and CLI tool for creating and publishing the starter MVC web app
  • dotnet hosting bundle: IIS dependencies needed to serve a .NET web app

In your VM open up the PowerShell console by searching for it like you did for the IIS Manager.

Tip

You can right-click PowerShell and pin it to the task bar for easy access.

Now open PowerShell and enter the following command to install choco:

Windows/PowerShell
> [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

Next we will use the choco package manager to install the .NET hosting bundle:

Windows/PowerShell
# the -y option skips prompting for confirmation
> choco install dotnetcore-windowshosting -y

In order for the hosting bundle to be recognized by IIS we need to restart the underlying processes used by IIS. The Windows Process Activation Service (WAS) and its dependent World Wide Publishing Service (W3SVC) can be restarted by entering the following commands:

Windows/PowerShell
# /y is like -y and is used to skip a confirmation prompt

# when WAS is stopped it automatically stops all dependent processes including W3SVC
> net stop WAS /y

# when W3SVC is started it starts its WAS process dependency automatically
> net start W3SVC

Finally, let’s install the dotnet SDK and CLI tool using choco:

Windows/PowerShell
> choco install dotnetcore-sdk -y

After installing you need to close PowerShell and reopen it before the dotnet CLI can be used. Then enter the following command to confirm it is installed and usable:

Windows/PowerShell
# expect a single line with the version number as output
> dotnet --version

If you get an error, it means you did not close and reopen PowerShell. Sometimes this can happen if multiple PowerShell windows are open. Make sure you close all of them before reopening.

10.4.4. Deploy a .NET Web App

10.4.4.1. Create the Starter Web App

Let’s start by creating and switching to a new directory to keep our home directory clean:

Windows/PowerShell
# issue this in the home directory, C:\Users\student
> New-Item -ItemType directory -Path WebApps
> Set-Location WebApps

# or using the simpler mkdir and cd aliases
> mkdir WebApps
> cd WebApps

Inside this directory we can create the starter MVC project:

Windows/PowerShell
> dotnet new webapp -n StarterApp

10.4.4.2. Publish the Web App

Before we publish the web app we need to create a content directory for IIS to serve. The C:\inetpub directory is traditionally used by IIS for site content. We will create a StarterApp directory in here to hold our published content:

Windows/PowerShell
> New-Item -ItemType directory -Path C:\inetpub\StarterApp

# or using the simpler mkdir alias
> mkdir C:\inetpub\StarterApp

Now we can publish our web app into this directory so IIS can serve it. If you are not already in the StarterApp directory then switch to it first. We will publish for the Windows x64 architecture and output to the new C:\inetpub\StarterApp directory we just made:

Windows/PowerShell
> cd C:\Users\student\WebApps\StarterApp
> dotnet publish -c Release -r win-x64 -o C:\inetpub\StarterApp

10.4.4.3. Configure IIS to Serve the Web App Site

We now have a published web app and its contents in a directory. The final step is to configure a new site for IIS to serve it. Let’s begin this process by removing the default site. This will free up port 80 for our .NET web app site.

In the IIS Manager right click on the default site and select Remove:

IIS Manager remove default site

Next, right-click the Sites icon and select Add Website:

IIS Manager add new site

This will present the New Site dialog. We need to fill in the following details:

  • Site name: StarterApp
  • Application pool: StarterApp, by default it will create a new pool with the same name as the site
  • Physical path: C:\inetpub\StarterApp, this is the path to the directory we published the web app to
  • Binding: 80, we want to serve on the standard http port
  • Host name: leave blank, we do not have a domain name to add a host name to

After hitting OK, IIS will create the application pool and immediately begin serving the site.

10.4.4.4. Test Your Work

Try connecting locally on the server to confirm everything worked. You can open IE to http://localhost and should see the starter web app content:

IIS StarterApp in Server browser

Finally, confirm that you are able to connect over the Internet from your local machine:

IIS StarterApp in local browser

10.4.5. Next Step

Congratulations on completing your first Windows Server & IIS deployment! How did this process feel relative to using the Azure browser console and a Linux VM? Did you like using RDP and having a full desktop to work with?

Before continuing to your studio, consider the following aspects needed for the CodingEvents API deployment:

  • What other dependencies will we need (tools, backing services)?
  • How will we get our API source code onto the server to publish?
  • How can we serve the API site on port 443 (https) to support the secure connection requirement of Azure ADB2C?