Running Asp.Net Core with IIS on Nano Server

Following on from my earlier blog post introducing IIS on Nano Server, this post outlines the steps required to get an ASP.Net Core application running with IIS on Nano.

Installing IIS

First you need to install IIS on Nano. This is already covered in the official Nano documentation so I’ll skip those steps here for brevity

Refer to:

Installing Reverse Forwarders

Currently, both HttpPlatformHandler and Asp.Net Core Kestrel host requires Reverse Forwarders package to be installed on the Nano machine.

Reverse Fowarders can be installed offline (machine turned off) or online (machine running).

Offline, Reverse Forwarders can be installed by passing –ReverseForwarders parameters when calling New-NanoServerImage cmdlet. e.g.

New-NanoServerImage -MediaPath f:\ -BasePath .\Base -TargetPath .\Nano1.vhd -GuestDrivers -Packages Microsoft-NanoServer-IIS-Package -ReverseForwarders

Online, Reverse Forwarders can be installed by calling dism on the relevant package:

  • dism /online /add-package /packagepath:c:\packages\Microsoft-OneCore-ReverseForwarders-Package.cab
  • dism /online /add-package /packagepath:c:\packages\en-us\Microsoft-OneCore-ReverseForwarders-Package.cab
  • <reboot>

The 1st command installs the Reverse Forwarders feature package.

The 2nd command installs the associated language pack for the package.

In this case, C:\packages is a local directory that I created where I copied the Packages folder from the Nano Server media installation location.

After installing Reverse Forwarders package (whether offline or online), verify that IIS and Reverse Forwarders are correctly installed by running dism /online /get-packages.

You should see the Feature and Language packs for Microsoft-NanoServer-IIS-Package and Microsoft-OneCore-ReverseForwarders-Package.

Installing HttpPlatformHandler

At this time, the install for HttpPlatformHandler on Nano is manual.

Understandably this is a pain…. so it’s something that we’re working on improving.

Nano is 64 bit only (no WOW), so you’ll need to install the latest x64 version of HttpPlatformHandler (http://www.iis.net/downloads/microsoft/httpplatformhandler) on a regular (not Nano) machine.

Once you have x64 bit version of HttpPlatformHandler installed on your regular (not Nano) machine, there are 2 files which we’ll need to copy to the Nano machine:

  • %windir%\System32\inetsrv\httpPlatformHandler.dll
  • %windir%\System32\inetsrv\config\schema\httpplatform_schema.xml

On the Nano machine you’ll need to copy those 2 files to the respective locations (dll->inetsrv, schema file->inetsrv\config\schema)

  • copy .\httpPlatformHandler.dll c:\Windows\System32\inetsrv
  • copy .\httpplatform_schema.xml c:\Windows\System32\inetsrv\config\schema

Enabling HttpPlatformHandler (PowerShell)

You can execute the steps below in a remote PowerShell session to the Nano machine.

Note that the below steps works on a clean system, but is not meant to be idempotent. If you run this multiple times it will add multiple entries and you will run into problems! If you end up in a bad state, you can find backups of the applicationHost.config file at %systemdrive%\inetpub\history.

Import-Module IISAdministration
$sm = Get-IISServerManager

# Add AppSettings section (for Asp.Net Core)
$sm.GetApplicationHostConfiguration().RootSectionGroup.Sections.Add("appSettings")

# Unlock handlers section
$appHostconfig = $sm.GetApplicationHostConfiguration()
$section = $appHostconfig.GetSection("system.webServer/handlers")
$section.OverrideMode="Allow"

# Add httpPlatform section to system.webServer
$sectionHttpPlatform = $appHostConfig.RootSectionGroup.SectionGroups["system.webServer"].Sections.Add("httpPlatform")
$sectionHttpPlatform.OverrideModeDefault = "Allow"

# Add to globalModules
$globalModules = Get-IISConfigSection "system.webServer/globalModules" | Get-IISConfigCollection
New-IISConfigCollectionElement $globalModules -ConfigAttribute @{"name"="httpPlatformHandler";"image"="%SystemRoot%\system32\inetsrv\httpPlatformHandler.dll"}

# Add to modules
$modules = Get-IISConfigSection "system.webServer/modules" | Get-IISConfigCollection
New-IISConfigCollectionElement $modules -ConfigAttribute @{"name"="httpPlatformHandler"}
$sm.CommitChanges()

Enabling HttpPlatformHandler (manually editing applicationHost.config)

You can skip this section if you already did the PowerShell steps above.

I recommend following the PowerShell steps, although if you absolutely must edit the IIS applicationHost.config file to enable HttpPlatformHandler then these are the steps.

Open up c:\windows\system32\inetsrv\applicationHost.config

(if you are using Powershell ISE v5 you can do this using psedit c:\windows\system32\inetsrv\applicationHost.config)

Under <configSections> add

<configSections>
	<section name="appSettings" />

In system.webServer section, unlock handlers from Deny to Allow

<section name="handlers" overrideModeDefault="Allow" />

In system.webServer section, add new httpPlatform section

	<section name="httpPlatform" overrideModeDefault="Allow" />
</sectionGroup>

Add the following to globalModules

	<add name="httpPlatformHandler" image="%SystemRoot%\system32\inetsrv\httpPlatformHandler.dll" />
</globalModules>

Add the following to Modules

	<add name="httpPlatformHandler" />
</modules>

Installing Asp.Net Core application

First, make sure that your Asp.Net Core application is built targeting coreclr and x64.

This is very important as any other combination will not work on Nano.

After building for coreclr/x64 you will need to copy the whole application to the Nano machine – in this example I’m using c:\HelloAspNetCore.

Next I will setup a new Site pointing to c:\HelloAspNetCore\wwwroot using port 8000 (for simplicity we will go with Default App Pool)

There are no problems running the Asp.Net Core site on default port 80, though in a testing environment I like to separate the Asp.Net Core app from the default website, so that it’s easier to troubleshoot when something goes wrong (e.g. so you can verify that the default IIS site still works fine).

Using IIS PowerShell:

Import-module IISAdministration
New-IISSite -Name "AspNetCoreSite" -PhysicalPath c:\HelloAspNetcore\wwwroot -BindingInformation "*:8000:"

Creating the site manually (omit if you already created using PowerShell above) by editing c:\windows\system32\inetsrv\applicationHost.config:

<sites>
	<site name="Default Web Site" id="1">
		<application path="/">
			<virtualDirectory path="/" physicalPath="%SystemDrive%\inetpub\wwwroot" />
		</application>
		<bindings>
			<binding protocol="http" bindingInformation="*:80:" />
		</bindings>
	</site>
	<site name="AspNetCoreSite" id="2">
		<application path="/">
			<virtualDirectory path="/" physicalPath="C:\HelloAspNetCore\wwwroot" />
		</application>
		<bindings>
			<binding protocol="http" bindingInformation="*:8000:" />
		</bindings>
	</site>
	… rest of xml…
</sites>

The next step is to open up port 8000 in the firewall.

New-NetFirewallRule -Name "AspNetCore" -DisplayName "HTTP on TCP/8000" -Protocol tcp -LocalPort 8000 -Action Allow -Enabled True

Troubleshooting

First make sure that IIS itself is correctly installed. http://<ipaddress> should get you the default page (Blue IIS page) otherwise something very basic is not set up correctly.

Secondly, make sure that your Application can run standalone. e.g. c:\HelloAspNetCore\approot\web.cmd

Thirdly, check to make sure that you are seeing logs created by HttpPlatformHandler – e.g. c:\HelloAspNetCore\logs

3 Comments

  • I have followed the steps, but it doesn't work :-(
    It gets a PERMISSION DENIED; I found it in the app /logs directory (included below).
    Does it need an additional ICACL somewhere? I don't know under what security context it runs...
    I think I saw somewhere it could need permission for the port it runs on. If so, how do we grant permissions on that port?

    Microsoft.AspNet.Server.Kestrel.Networking.UvException: Error -4092 EACCES permission denied
    at Microsoft.AspNet.Server.Kestrel.Networking.Libuv.Check(Int32 statusCode)
    at Microsoft.AspNet.Server.Kestrel.Networking.UvTcpHandle.Bind(ServerAddress address)
    at Microsoft.AspNet.Server.Kestrel.Http.TcpListener.CreateListenSocket()
    at Microsoft.AspNet.Server.Kestrel.Http.Listener.<>c__DisplayClass5_0.<StartAsync>b__0(Object _)

  • Can you stop (kill) all instances of dnx.exe and start Kestrel selfhost? e.g. approot\web.cmd
    Does that have the same problem with the port?
    If so the port it's trying to use could be in use.
    Can you check also check project.json (e.g. "web": "Microsoft.AspNet.Server.Kestrel --server.urls http://*:5000")

  • would be nice to provide it as a container!

Comments have been disabled for this content.