Steve Spencer's Blog

Blogging on Azure Stuff

Custom ASP.NET MVC app running in a Windows Container

With the introduction of Windows Containers on  Window Server 2016 and the ability to run containers in Service Fabric I thought it was time to investigate Windows Containers and I wanted to know how to build one that will run a web site using IIS.

As I’m new to containers, although I’ve done a very similar exercise with Docker on Linux, I decided to follow the Windows Quick Start Guide. I hit a few problems early on so I’ve put the steps I followed here:

After opening a PowerShell window as administrator I ran the following commands:

Install-Module -Name DockerMsftProvider -Repository PSGallery –Force – Ran OK
Install-Package -Name docker -ProviderName DockerMsftProvider – Had an error
WARNING: Cannot verify the file SHA256. Deleting the file.
WARNING: C:\Users\ADMINI~1.DEV\AppData\Local\Temp\DockerMsftProvider\Docker-1-12-2-cs2-ws-beta.zip does not exist
Install-Package : Cannot find path 'C:\Users\ADMINI~1.DEV\AppData\Local\Temp\DockerMsftProvider\Docker-1-12-2-cs2-ws-beta.zip' because it does not exist.

 

Not sure what was causing this to fail but I followed the instructions to manually install (from https://github.com/OneGet/MicrosoftDockerProvider/issues/15)

Start-BitsTransfer -Source https://dockermsft.blob.core.windows.net/dockercontainer/docker-1-12-2-cs2-ws-beta.zip -Destination /docker.zip
Get-FileHash -Path /docker.zip -Algorithm SHA256
mkdir C:\Users\Administrator\AppData\Local\Temp\DockerMsftProvider\
cp .\docker.zip C:\Users\Administrator\AppData\Local\Temp\DockerMsftProvider\
cd C:\Users\Administrator\AppData\Local\Temp\DockerMsftProvider\
cp .\docker.zip Docker-1-12-2-cs2-ws-beta.zip
Install-Package -Name docker -ProviderName DockerMsftProvider -Verbose Restart-Computer –Force

After Rebooting I tried to download and run a sample container

docker run microsoft/dotnet-samples:dotnetapp-nanoserver

but I got the following error

docker : C:\Program Files\Docker\docker.exe: error during connect: Post http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.25/containers/create: open //./pipe/docker_engine: The

system cannot find the file specified..

At line:1 char:1

+ docker run microsoft/dotnet-samples:dotnetapp-nanoserver

+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+ CategoryInfo : NotSpecified: (C:\Program File...ile specified..:String) [], RemoteException

+ FullyQualifiedErrorId : NativeCommandError

It turns out that the docker service wasn’t running after the reboot, so open services.msc and find the docker service to start it.

Running the same command again will download the image from docker hub, create a container from it and then run it. As this is a visual container it runs once and then stops.

clip_image001

Every time I do docker run microsoft/dotnet-samples:dotnetapp-nanoserver it creates a new container. What I want to do is to run one that is stopped and view the output on the screen.

For this I needed to start the container I had already created. If you run

docker –ls –a

This will list all the containers that are both running and stopped and you can see from the image below that I had run docker run a number of time. Each time it tried to download the image (which was already downloaded) and then create a new container from it.

clip_image001[6]

docker container start -a 12d382ae0bd6 (-a attached STDOUT so you can see the output)

clip_image001[8]

Now I know how to create and start containers I wanted to build one of my own. This is easier than I first though as there a lots of base templates stored on docker hub and git hub.

https://docs.microsoft.com/en-us/virtualization/windowscontainers/samples#Application-Frameworks

https://hub.docker.com/r/microsoft/

I picked one on docker hub that has IIS and ASP.Net installed already, so all I needed to do after was to add my own website and configure IIS correctly. Using docker pull,

docker pull microsoft/aspnet (see https://hub.docker.com/r/microsoft/aspnet/)

This retrieves the template from Docker Hub and I want to use that template to install my ASP.Net MVC site and configure IIS to serve the pages on port 8000. Following the instructions here (https://docs.microsoft.com/en-us/dotnet/articles/framework/docker/aspnetmvc). I published my MVC site and copied the publish folder to my docker machine. Then I needed to create a Dockerfile recipe to instruct docker what to install in my image. So I created a folder that contained the Dockerfile and also the published website as below

clip_image001[10]

The contents of the Dockerfile are:

# The FROM instruction specifies the base image. You are
# extending the microsoft/aspnet image.
FROM microsoft/aspnet
# Next, this Dockerfile creates a directory for your application
RUN mkdir C:\sdsweb
# configure the new site in IIS.
RUN powershell -NoProfile -Command \
Import-module IISAdministration; \
New-IISSite -Name "sdsweb" -PhysicalPath C:\sdsweb -BindingInformation "*:8000:"
# This instruction tells the container to listen on port 8000.
EXPOSE 8000
# The final instruction copies the site you published earlier into the container.
ADD sdswebsource/ /sdsweb

Now I need to run this to create the image

In PowerShell, I changed directory to the folder containing my Dockerfile, then ran

docker build -t sdsweb .

This has created an image and we need to now get this running as a container

clip_image001[12]

using docker run again

docker run -d -p 8000:8000 --name sdsweb sdsweb

clip_image001[14]

My container is now running and I should be able to view the web pages in my browser on port 8000, but I need to know the IP address first

docker inspect -f "{{ .NetworkSettings.Networks.nat.IPAddress }}" sdsweb

clip_image001[16]

Now I can browser to http://172.17.97.235:8000

clip_image001[18]

I changed the default web page to show the machine name serving the pages under Getting Started. Listing the containers will show the container ID and this matches the machine name displayed on the web page

image

That’s it running in a container. There are a couple more things I’d like to do before I’ve finished. The first is to make sure that when my Windows Server restarts, then my sdsweb container also starts. At the moment it will not start as I didn’t add a restart parameter when I called docker run. Adding –restart always will cause the container to restart when windows restarts.

docker run -d -p 8000:8000 --name sdsweb --restart always sdsweb

The final thing I want to do is to be able to share this image so I’d like to push it up to docker hub

docker login - enter username and password

docker push recneps/sdsweb

clip_image001[20]

Then to use it on another machine

docker pull recneps/sdsweb

clip_image002

In my next post I am going to look at how I can create a container that can be hosted in Service Fabric

System.Web.Mvc not found after deploying to Azure Web Apps using Release Manager

I’m currently evaluating Release Manager in Visual Studio Team Services and I am using it to deploy website to Azure Web Apps. I recently tried to deploy an Asp.Net MVC 4 application and ran into some issues.

I’ve created a build that packages and zips up my web application which runs successfully.I’ve linked a Release pipeline to this build and I can deploy to my test Azure site without any errors, but when I try and run the web application I get the following error:

Could not load file or assembly 'System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified.

image

I’m using Visual Studio 2013 with MVC as a nuget package.Looking at the properties of System.Web.Mvc I can see that it is set to Copy Local = True

image

I tried a few different things to try to get the assembly to be copied like redoing the nuget install and eventually I toggled the Copy Local to False, saved the project file and then set it back to true. When I looked at the diff of the project file I found an additional property

image

This seems to fix the build. When I checked this in and rebuild, System.Web.Mvc now appears in the zip file. The build was then release to Azure and the web app worked correctly.

Changing Website Settings through the Azure Portal

When using configuration in Microsoft Azure websites, ensure that you put configuration that you are likely to change often in AppSettings. This allows you to make configuration changes in the Management portal of Azure rather than having to edit the web.config file directly. An example of where you might like to do this include settings that allow you to disable site features temporarily such as during an upgrade or routine maintenance.

App settings in the web config file are names/value pairs and are accessed as follows:

System.Configuration.ConfigurationManager.AppSettings["StevesSetting"]

Which can be seen in the web.config as follows:

<configuration>
  .
  <appSettings>
      <add key="StevesSetting" value="Webconfig setting" />
  </appSettings>
  .
</configuration>

In order to manage this configuration in the portal you need to navigate to your website and click the configure tab (in the old portal)

image

and scroll down to app settings, then add in the setting you wish to change

image

Or in the old portal navigate to the website and click settings then applications settings and scroll down to the app settings section

image

Azure Websites: Blocking access to the azurewebsites.net url

I’ve been setting up one of our services as the backend service for Azure API management. Part of this process we have mapped DNS to point to the service. As the service is hosted in Azure Websites there are now two urls that exist which can be used to access the service. I wanted to stop a user from accessing the site using the azurewebsites.net url and only access it via the mapped domain. This is easy to achieve and can be configured in the web.config file of the service.

In the <system.webServer> section add the following configuration

<rewrite>
    <rules>
        <rule name="Block traffic to the raw azurewebsites url"  patternSyntax="Wildcard" stopProcessing="true">
          <match url="*" />
          <conditions>
            <add input="{HTTP_HOST}" pattern="*azurewebsites.net*" />
          </conditions>
          <action type="CustomResponse" statusCode="403" statusReason="Forbidden"
          statusDescription="Site is not accessible" />
        </rule>
    </rules>
</rewrite>

Now if I try and access my site through the azurewebsites.net url, I get a 403 error, but accessing through the mapped domain is fine.

Windows Azure Websites, Web API and SignalR

One of our projects involves a web service that implements both SignalR and Web API and we were looking at the quickest and most cost effective way to get it deployed so that one of our customers could run a Windows 8 application as a demo away from the office. The application works well internally as we have the service deployed on one of our servers on IIS. The options we were considering were:

  1. Package the application up in an install package, ship this to our customer and then provide them with instructions and support to allow them to deploy and configure their application
  2. Deploy it on one of our servers and then publish the service through our firewall
  3. Deploy as a Cloud service in Windows Azure
  4. Deploy as a website in Windows Azure

We considered the fact that the first option would probably take us a fair amount of time to make a deployment package, test it and provide enough documentation and support to allow our customer to deploy it on their servers. The other 3 options involved us doing a smaller amount of work, but at least we could get everything working well before shipping the demo out. Option 2 would mean using our internal resources for something that would not be used that often but we would not necessarily know whether and when it was being used so the resources would need to be kept running limiting our capacity internally. Windows Azure was a good fit for this application and the choice was really between setting up a cloud service or use a web site, I guess we could have set up a virtual machine hosted in Windows Azure, but this was a bit excessive just for a simple web service. The two remaining options were to set up a cloud service by creating a web role in deploying to Windows Azure or to use Websites. The cloud service would involve more work for us as we would need to change the project to add in the cloud service project and web role and then do a full PaaS deploy to Windows Azure. This would then utilise a whole virtual machine (although we would have used an Extra Small instance), but the web sites seem a sensible option especially as we already have a number of them available for free. How easy was this going to be and will both Web API and SignalR work with Windows Azure Websites, especially as we were using preview software. I was surprised about how easy this was to deploy and I’ll walk through the process we went through.

Step 1: Make sure that the service runs locally,

Step 2: Our service uses Code First Entity Framework using a local SQL server. Create a database using Windows Azure SQL Server via the Windows Azure Management portal (https://manage.windowsazure.com), the copy the ADO.NET connection string.

image

Paste this into your web config file of the web api service. You will need to make sure that the Windows Azure SQL Server firewall has your public IP address configured and you will need to make sure that your firewall will allow connections through port 1433. Now run your application and make sure that you can connect to the Windows Azure SQL database. As we are using Code First Entity Framework, the database tables were created for me so I didn’t need to do any database deployment. The only issue I had with this approach was that I had to create the database first in Windows Azure.

Step 3: With our service running locally but with the database in Windows Azure we are now ready to deploy to the cloud. In the Windows Azure Management portal, click the “New” button

image

The “Quick Create”, enter the url you want to use and click “Create Web Site”

image

Step 4: We now need to deploy our service. In the Azure management portal, navigate to the web site you just created and click “Download Publishing Profile”. Save this to your computer.

image

In Visual Studio 2012, open your web api project, right click on the project in Solution Explorer and click publish.

image

This will display the publish dialog.

image

Click the import button and navigate to the folder where the publish profile was saved. This should then allow you to complete the wizard

image

Click Next and check to make sure the correct connection string is displayed, click Next then Publish. This should then start to upload your web api project to the Windows Azure Website. The deploy should be relatively quick and no where near the time it takes to deploy a cloud service. When completed, your deployed website should start in the browser and you can carry out whatever tests you need.

Step 5: With your website deployed you should just need to change the url of your service in the Window 8 application.

This whole process took less than 10 minutes to setup and deploy. One of the nice features of using websites is that changes are quick to deploy.

We had a number of issues to get this all working fully:

  1. As I mentioned earlier we had to ensure that the database was created before the EF code would create the correct tables
  2. When we first ran the Windows 8 application we were getting an error each time we tried to use SignalR. We received an “Incompatible protocol version”. This was because I installed the latest SignalR libraries on the server side code but the client was using an older version. You need to make sure that both the client and server are using the same version of SignalR
  3. We also had an issue when deployed to Windows Azure where it looked like the SignalR hubs were not being created correctly. It looked like the hub creation was hanging and not returning. This is a known issue that has been fixed but not yet deployed to Azure. There is a work around which is to configure SignalR to use long polling (https://github.com/SignalR/SignalR/issues/510). We did that with the following code:
   1: hubConnection = new HubConnection(App.SignalRUrl);            
   2: proxy = App.hubConnection.CreateHubProxy("statushub");
   3: App.hubConnection.Start(new LongPollingTransport()).Wait();

Windows Azure Web Sites is not just for web sites, using it also for services can make a lot of sense as the scaling model will allow a lot of flexibility and can provide a cost effective way to host your services, especially if they are not heavily loaded at the start. They are also easy and fast to deploy which is always a bonus Smile

Windows Azure Training Kit–June 2012 Release

The Windows Azure Training Kit June 2012 release is out now with the following features:

  • 12 new hands-on labs for Windows Azure Virtual Machines
  • 11 new hands-on labs for Windows Azure Web Sites
  • 2 new hands-on labs demonstrating Windows Azure with Windows 8 Metro-style applications
  • Several new hands-on labs for Node.js and PHP using Mac OS X
  • Updated content for the latest Windows Azure SDKs, tools, and new Windows Azure Management Portal
  • New and updated presentations designed to support individual sessions to a full 3 day training workshops

Publishing Windows Azure Websites with TFS

This is a follow on post from my introduction to Windows Azure Websites and shows you how you can synchronise your website in TFS with Windows Azure.

One of the biggest problems with the way you deploy applications to  Windows Azure is that minor changes (e.g .markup, content and styling) require a redeploy to publish the changes. Windows Azure Websites solves this problem by allowing you to synchronise your website with Team Foundation Server or GIT.

In this post I will show you how easy it is to manage your websites in version controlled environment using Team Foundation Service. Team Foundation Service is a cloud hosted version of Team Foundation Server.

This works by creating a continuous integration build with your source code that will automatically deploy your website after successful build each time code is checked in.

This is configured as follows:

Click the “+” button at the bottom of your portal screen and select Website –> Quick Create

image

Enter the url details and click Create Web Site

image

An Empty site has now been created.

This site now needs to be link to your Team Foundation Service. Click on the website in the dash board and then select “Setup TFS Publishing”. you will also note that you can use a GIT repository as well as TFS.

image

Enter your TFS url (or create a new one), then click Authorize Now.

image

this connects through to your TFS service and setup the CI build that will deploy your application to the cloud.

The TFS site will now be displayed asking you to authorize the connection

image

You now need to pick the website you want to deploy. If you haven’t create a site yet then you need to go to ~Visual Studio, create your site and check it in to TFS.

image

You have now linked your web site in TFS to the Azure Website. This will take a few moments to synchronise.

image

Your website has not been deployed yet. You need to make a change and then check the changes in

image

upon check-in the build is started

image

image

When the build is complete the new website is deployed

image

image

You can also revert back to older versions of the web site by clicking the desired version and then clicking redeploy:

image

This will start the redeploy of the older version:

image

A new build is kicked off using the same changeset details as the original deployment. Once the build is complete the  web site is reverted back. this whole cycle only took a few minutes so it is a lot faster than the redeploy mechanism you had previously.

image

image

TFS and Windows Azure provide a good mechanism for version controlling your website. Adding application life cycle management to any software development activity is a good thing.

Session State in Windows Azure

We recently moved a web application into Windows Azure that was using session state. As it was running on a single webserver the session state was set to InProc but this is not useful when in a multi-server environment as the session is stored on the specific machine and is therefore not accessible to other machines. There were a number of options:

  1. Use the Windows AppFabric Caching service (http://msdn.microsoft.com/en-us/library/windowsazure/gg278339.aspx)
  2. Use SQL Azure (http://blogs.msdn.com/b/sqlazure/archive/2010/08/04/10046103.aspx)
  3. Use Windows Azure Storage

Windows Azure Storage seemed to be the more cost effective version as the site does not currently use SQL Azure and they have purchased a subscription for Azure which includes both transaction and storage costs.

There is a sample asp.net session provider that uses Windows Azure Table Storage as its backing store. The sample can be downloaded from MSDN at

http://code.msdn.microsoft.com/windowsazure/Windows-Azure-ASPNET-03d5dc14

How to use the Azure Storage Session State Provider

Add the following Session State provider config to the web.config file of the project

   1: <!-- SessionState Provider Configuration -->
   2: <sessionState mode="Custom"
   3:               customProvider="TableStorageSessionStateProvider">
   4:   <providers>
   5:     <clear/>
   6:     <add name="TableStorageSessionStateProvider" type="Microsoft.Samples.ServiceHosting.AspProviders.TableStorageSessionStateProvider"/>
   7:   </providers>
   8: </sessionState>

Add your windows azure storage connection string (DataConnectionString) to each web role that requires session state (Not setting this will result in an object reference not set to an instance of an object exception)

Add a reference to the ASPProviders.dll taken from the sample project and make sure that the Copy Local property is set to true (Not setting this will cause an unable to load exception)

image

We also added a reference to System.DataServices.Client and set copy local to true on this too.(Not sure if this is needed)

Once this is setup and running, add multiple instances to your role configuration and run in the debugger. Make sure you can navigate to the page that has the session data in. I put a break point onto the action of the page and added a watch for Microsoft.WindowsAzure.ServiceRuntime.RoleInstance.CurrentRoleInstance.Id and checked to see if it changed and if it did change checked to see if the session data was visible.

You may well get the following error when you are using session as all the objects that are put into the Azure Table Storage session object need to be serializable.

Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET will serialize the session state objects, and as a result non-serializable objects or MarshalByRef objects are not permitted. The same restriction applies if similar serialization is done by the custom session state store in 'Custom' mode.

You can check to see the session data in the Azure Storage Server Explorer.

image

We are going to run this for a while to se how well it works and also see what debris is left behind in the table and blob storage due to ended sessions. We might have to have a job running that tidies up the expired sessions later.

Creating your own identity provider for Windows Azure AppFabric Access Control

Whilst doing an access control service demo I was asked whether you could wire in your own existing authentication mechanisms as customers did not want to have to redo their authentication/registration mechanisms to use Live ID, Google, Yahoo! etc. The answer to this was yes but I had never done it so this was a good time to investigate how.

I started off with the Windows Azure Platform Training Kit(VS2010) and worked through the “Introduction to the AppFabric Access Control Service V2” lab to setup a web site that allows login via Live ID, Google and Yahoo!. Once this was running I needed to create my own provider and wire it into the lab solution that I just created. There is an additional lab ""Federated Authentication in a Windows Azure Web Role Application" which gives the basics of creating your own identity provider. Unfortunately this does not link to ACS so I needed to work out how to wire the provider in. The following instructions are how I created the site and wired it in:

Taking the ACS lab solution as the basis, create an ASP.Net website that will carry out the login process. For this I added a “ASP.NET Security Token Service Web Site”. Right click on your solution and select new website. Make sure that the URL you enter for the site includes https at the start. (e.g. https://localhost/MyIDProvider).

When the project is created, you need to change some of the code in the template as it does not handle the return address correctly when redirecting from your identity provider after logging in.

The template for an STS web site needs the following code changing in App_Code\CustomSecurityTokenService.cs

Go to GetScope and change the line

scope.ReplyToAddress = scope.AppliesToAddress;

to

scope.ReplyToAddress = String.IsNullOrEmpty(request.ReplyTo) ? scope.AppliesToAddress : request.ReplyTo; 

This takes the replyto address from the query string and uses this to redirect back to ACS once the login process has been completed. There are 2 other changes required to the basic STS template in order for it to work correctly.

Open web.config and search for IssuerName in the application settings section and change it to be the url of your STS website (e.g. https://localhost/MyIDProvider)

Also change the SigningCertificateName to point to a certificate that exists in your local machine certificate store. This website will now provide a simple mechanism for logging in. Without any changes you can enter any username and it will authenticate. At this point you will need to wire in your own authentication mechanism, but for testing purposes the default site will allow you to set it up correctly and test it out.

We now need to wire this into ACS. I am using the labs version of the access control service at https://portal.appfabriclabs.com/.

Navigate to your Access Control Service at appfabriclabs.

Click “Identity Providers”, “Add Identity Provider” and add a new “Microsoft Active Directory Federation Service 2.0” provider. The two bits that are important are “WS-Federation metatdata” and the relying party application. Browse to the FederationMetadata.xml file of your STS project you have just created. (e.g. C:\inetpub\wwwroot\MyIDProvider\FederationMetadata\2007-06\FederationMetadata.xml). Also ensure that the ACS website created as part of the labs is checked and press Save.

The final piece of configuration that is required is to add in the rules for your provider. still in the Access Control Service portal, click “Rule Groups”, select the rule group that you setup for your ACS lab and select “Generate Rules”. Ensure that your new identity provider is in the list and that it has been checked and press the “Generate” button. Two new rules should have been added for your provider (Pass through for name and role). You are now ready to test this.

To make it easier to see what is happening I added the following to the Default.aspx of my ACS lab

In default.aspx add the following:

    <asp:LoginView ID="LoginView1" runat="server">
        <AnonymousTemplate>
            <asp:Panel Visible="true" CssClass="secretContent" runat="server" ID="unauthorisedContent">
            You are unauthorised to view this page
            </asp:Panel>
        </AnonymousTemplate>
    
        <LoggedInTemplate>
                You are logged in
        </LoggedInTemplate>
        <RoleGroups>
            <asp:RoleGroup Roles="Administrator">
                <ContentTemplate>
                    <asp:Panel ID="SecretContent" runat="server" CssClass="secretContent" 
                        Visible="true">
                        Secret Content (Only administrators can access this section)
                    </asp:Panel>
                </ContentTemplate>
            </asp:RoleGroup>
        </RoleGroups>
    </asp:LoginView>

This will display the login status so you can see whether the login works or not.

Also add the following style to the site.css file in the ACS lab site:

.secretContent
{
  border-style: solid; 
  background-color: Red; 
  padding: 5px;
  color: White;
}

Run the ACS lab application and check to see if your provider appears in the list of providers and also that when you click on the button it redirects to you page. Login and you should be redirected to the Default.aspx page of the ACS lab site with the text “you are logged in”.

You may want to change the claims that are allowed for specific users. This is done in App_Data\CustomSecurityTokenService.cs in your identity provider web site.

Modify GetOutputClaimsIdentity to change depending upon who is logged in.

Change the code that adds a Manager Role to the following code to allow a user called Steve to be an administrator and everyone else as a user.

if (principal.Identity.Name.Equals("Steve") == true)
{
    outputIdentity.Claims.Add(new Claim(ClaimTypes.Role, "Administrator"));
}
else
{
    outputIdentity.Claims.Add(new Claim(ClaimTypes.Role, "User"));
}

Run your ACS website again and login with “Steve” and you should now see the secret content that only administrator should see. Login as anyone else and you will not see the secret content.

All that you need to do now is to wire in your own authentication mechanism and deal with the claims for each user.