I know the title of this post is quite big
and hard to understand in first go. But I wanted to highlight the important
aspect I am going to touch in the article and hence such a long name.
Ok, so here in this post I am going to
demo how you can host multiple web applications in single cloud service. I know
you may say that there are numerous posts on the same topic what is different
in here? Yes the difference is there. You may find that most of the posts talks
about hosting multiple web sites in single cloud service web role but they do
not focus on running them on HTTPS and with custom domain name. Exactly this is
what I am explaining in this post.
So in essence, we will see that, how can
we host multiple web applications in single cloud service web role. Then we
will see how the same applications can be hosted on HTTPS using SSL
certificates in single cloud service web role. After that we will extend the
talk to hosting same apps with custom domains using new feature of IIS 8.0
called as Server Name Indication (SNI).
Applicable
software stack – VS 2013 and above, Azure SDK 2.3
and above, IIS 8.0 and above. [IIS 7 not supported].
Create
required project in Visual Studio
This part is pretty straightforward
implementation. To start with I created a simple cloud service project in
VS2013 and added one web role in it. I renamed the web role to
ManegementAppWebRole as shown below –
I changed the default.aspx UI text to just
make it identify as Management App. Now I added another ASP.NET web project in
the same solution. This will be my survey app. In this also I changed the
default.aspx text to just make it identify as Survey app. So in all my projects
structure is as follows –
Hosting
multiple web applications in the same cloud service web role
Okay, so here I am representing a way by
which you can have two web sites hosted in the same cloud service web role. In
my case one of the applications is Management app web role itself and another is
Survey web application which is not the part of cloud service. So I will run
the survey app as part of Management app web role. To configure this full IIS
capability can be leveraged. We have a special file in cloud service project
know ServiceDefinition.csdef file. My current structure of .csdef file is as shown
below –
The <Sites> tag
is the key to make this possible. So I just added another site in .csdef file
and provided the path of Survey web application. And provided the actual
physical path of my survey app. Most importantly if you observe I have provided
the host header for my survey app as “surveyapp.kunal.com”. This is the main
differentiator of the survey app running in the Management app web role. IIS do
not allow to have more than one site listening on the same port without adding
host header. We need to provide host header because both of our applications
will be running on the same Endpoint port which is 80. Therefore there has to
be some way by which IIS can understand the request of user need to be served
by which application. Therefore we need host header. The complete structure of
my .csdef file is as shown below –
Now in reality my host header site
“surveyapp.kunal.com” do not exists. Therefore to make survey app resolve to
this URL I need to update “hosts” file of my machine. Hosts file is located at C:\Windows\System32\drivers\etc\hosts.
I copied the file to desktop and opened in notepad and added an entry as
highlighted below –
Then from desktop I replaced / copy-pasted
the hosts file back to its location.
Now let’s run the project in debug mode by
pressing F5 and it open the browser with management app with url similar to
below – http://127.0.0.1:60123/
Here 60123 is port opened by my emulator, this will different in your case. Open
another browser and cope the same url of management app and just replace
127.0.0.1 by our host header name i.e. surveyapp.kunal.com. So final url in new
browser window will be - http://surveyapp.kunal.com:60123/and bingo!! I see my management app and survey app running on the same port as
shown below –
Let’s run the same implementation in Full
IIS and Full emulator mode. This can be configured from the cloud service
project properties. To open the properties of cloud service project, right
click on Cloud service project and select Properties.
Note
– With Azure SDK 2.4 FULL Emulator has been
deprecated; however I guess you should be able to run the Full IIS with
Emulator Express. This is what I think!!
Ok, now let’s run the project by pressing Ctrl+F5
[DO NOT RUN in DEBUG MOE!!] and here is what I see in browser and IIS –
As you can see, host header has been
applied for my Survey Application. This is how we can run multiple web
application in single cloud service web role.
Advantage
– You are running the tow different web apps
in the same IIS. In other words you are running two different web applications
in same instance of Azure role and hence you save money. In case of 2 different
roles for 2 different web app, you will be charged for role VM instances of 2
applications. In this case you are charged for only one role VM instances.
Now in next part we will see how we can
run these web applications over HTTPS and custom domain. This means, I have
custom domain mapped for survey as “surveyapp.kunal.com” and now we will have
custom domain name updated in hosts file for Management app as
“managementapp.kunal.com” plus to run them on https and certificates.
Disclaimer
– To make multiple web applications run in
web role over different custom domains; of course neither I purchased the
custom domains nor certificates from certificate authority. I will demo
everything locally; however same actions will be required on production
environment while running the apps in cloud service on Azure. I will describe
details about running this on azure cloud service in later part of this post.
IMPORTANT
[Action for you for next steps]– to host multiple
web apps in single cloud service web role over https and custom domain, we are
going to use Server Name Indication (SNI) capability of IIS and it is available
only with IIS 8.0 and above. Therefore you can use Windows 8/ windows 8.1
or windows Server 2012 server machine to make this possible. I am using
Windows Server 2012 server machine for this. I am not sure if windows 7 support
IIS 8.0. Also when you publish your cloud service on Azure today; by default
you get Server 2012 OS and IIS 8.0. Therefore not to worry about cloud
environment J…now
moving back to the topic!!
Create
Self Signed Certificates
To run web roles over https/ SSL of course
you need certificates. For the demo purpose, I am here going to use self-signed
certificates created in IIS. So in all we need 2 certificates to be created IIS.
Open IIS and then open feature Server Certificates. Click on “Create Self
Signed certificate and specify name as “managementapp.kunal.com”. This will
create the certificate and now we need to export it as .pfx file. Therefore
right click on the generated certificate and select “Export”, provide the file
name as “managementapp.kunal.com” and password as Xyz1234# and save to desktop
as shown below –
Follow the same procedure for
“surveyapp.kunal.com” certificate generation and provide the path of desktop
for .pfx file, file name as “surveyapp.kunal.com” and password as Xyz1234#. I
added these generated certificates in my web role, under Certificates folder
and opened the properties of these .pfx files and changed the property of both
– “Copy to output directory” as “Copy Always” option. These certificates are
present in the download source code location.
Installing
Certificates in Local Machine – Personal (My) store
To install certificates in Local Machine –
Personal (My) store refer to the post written by me earlier –
After installation, certificates can be
viewed along with names we provided as part Friendly name column.
Making
Management app web role SSL enabled
Open web role properties, select
“Certificates” tab and click on “Add Certificate”. Choose the Store Location as
“LocalMachine” and Store Name as “My”. Click on the browse button and in the
pop up select certificate managementapp.kunal.com and its thumbprint will
automatically get listed.
Similary I added another certificate of surveyapp also in the same web role properties.
Let’s add the endpoint for 443 https and
remove the original endpoint of 80 from the web role properties.
Now here, important point is, we did not
select the Survey app certificate for endpoint. The survey app certificate is
added in web role properties only for referencing it at runtime.
Making
Survey app SSL enabled
Open the properties of survey web
application and mark “SSL Enabled” as true.
Map
custom domain to web role and web application in .csdef file
This is the most important step. In .csdef
file of cloud service project we will map the custom domains and update the
hosts file of local machine for both of domains. Now Management app custom
domain we want is “managementapp.kunal.com” and survey app to
“surveyapp.kunal.com”. Therefore open hosts file and add the entries as below –
Configure
Server Name Indication (SNI) in cloud service web role
Server name indication is introduced to
overcome the problem of SSL scalability.
In real world most of the sites work on https. In IIS prior to 8.0 we could not
bind more than one certificate for a single port. Hence we could not achieve
same url mapped to many https custom domains. For example, https://myservice.cloudapp.net
having endpoint as 80 could not get bind to https://site1.com
and https://site2.com.
Because both of these custom domains site1 and site2 will require 2 different
certificates to get them work on https.
To overcome on this problem primarily SNI
was introduced and it allows you to map same port, cloud service url to TWO
different custom domains on https. More details about SNI are here.
Ok, so configure the SNI in web role, we
need to create host header for the two sites we are planning to host as a part
of single web role and mark sslFlag
as 1 to enable SNI using C# code on web role OnStart method.
First let’s modify the .csdef file to have
host headers for both of the application as shown below.
If you observe, I have marked execution
context as Elevated. To make the code of web role OnStart method work we have
to run the context in elevated mode. The name of Management App site tag in
.csdef file is “Web”. This is special case name treated where in you don’t have
to specify actual physical path; however the survey app site tag name is not
the special case and therefore we have to specify the physical path like
(c:\something). If you change the name of Management app from “Web” to “web1”
you will get an error at runtime stating that, “Cannot find physical directory for virtual path Web1/”. I will keep
this default which is “Web”.
Now add reference of
Microsoft.WebAdministration dll to management web role using nuget as shown
below –
Install-Package Microsoft.Web.Administration
To modify the binding of web role and web
app at runtime, add following code in web role Onstart method. -
public override bool OnStart()
{
try
{
// For information on handling
configuration changes
// see the MSDN topic at
http://go.microsoft.com/fwlink/?LinkId=166357.
string certificateSurveyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\Certificates\\surveyapp.kunal.com.pfx";
string surveySiteName = string.Empty, npsAppSiteName = string.Empty;
////install certificate
var newCert = new X509Certificate2(certificateSurveyPath, "Xyz1234#", X509KeyStorageFlags.MachineKeySet
| X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
var readWriteMyStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
readWriteMyStore.Open(OpenFlags.ReadWrite);
readWriteMyStore.Add(newCert);
readWriteMyStore.Close();
//create the biding
var serverManager = new ServerManager();
foreach (Site site in serverManager.Sites)
{
if (site.Name.Contains("SurveyApplication"))
{
surveySiteName = site.Name;
break;
}
}
var surveySite = serverManager.Sites[surveySiteName];
Binding surveyBinding = surveySite.Bindings.Where(x =>
x.BindingInformation.Contains("surveyapp.kunal.com")).FirstOrDefault();
surveySite.Bindings.Remove(surveyBinding);
var binding = surveySite.Bindings.Add(":443:surveyapp.kunal.com", newCert.GetCertHash(), "My");
binding.SetAttributeValue("sslFlags", 1); //enables the SNI
serverManager.CommitChanges();
string certificateNPSPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "\\Certificates\\managementapp.kunal.com.pfx";
//install certificate
var npsCert = new X509Certificate2(certificateNPSPath, "Xyz1234#", X509KeyStorageFlags.MachineKeySet
| X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
var readWriteMyStoreNPS = new X509Store(StoreName.My, StoreLocation.LocalMachine);
readWriteMyStoreNPS.Open(OpenFlags.ReadWrite);
readWriteMyStoreNPS.Add(npsCert);
readWriteMyStoreNPS.Close();
//create the biding
var serverManager1 = new ServerManager();
foreach (Site site in serverManager1.Sites)
{
if (site.Name.Contains("Web"))
{
npsAppSiteName = site.Name;
break;
}
}
var npsSite = serverManager1.Sites[npsAppSiteName];
//while publish to cloud service
uncomment this
Binding npsBinding = npsSite.Bindings.Where(x =>
x.BindingInformation.Contains("managementapp.kunal.com")).FirstOrDefault();
if (npsBinding != null)
{
npsSite.Bindings.Remove(npsBinding);
}
var binding1 = npsSite.Bindings.Add(":443:managementapp.kunal.com", npsCert.GetCertHash(), "My");
binding1.SetAttributeValue("sslFlags", 1); //enables the SNI
serverManager1.CommitChanges();
}
catch (Exception ex)
{
throw ex;
}
return base.OnStart();
}
If you observe, first I am removing the
existing binding that gets created automatically and creating the same binding
again with 443 port, associated certificate and custom domain and marking SNI
as 1 through the property “sslFlag”.
Warning
– The code of management app in web role
OnStart method searched for sites containing name as “Web”. Your local IIS 8.0
may have “Default Web Site” running on port 80. The above code of binding
change may get applied to “Default Web Site” instead of your actual management
app “Web” named site in IIS at runtime and things may not work. Therefore open
IIS 8.0 and remove the “Default Web Site” from IIS. Of course you can add it
back any time you want. For the demo of SSL scalability in Azure cloud
services, remove the “Default Web Site” running on port 80 if you have one.
Running
the SSL scalability in IIS 8.0 implementation in Azure cloud service web role locally
We have 2 options in which we can run the
implementation of SSL scalability in IIS locally. First is to run the
application in Full IIS mode and other is to run in IIS Express and Emulator
express mode. Let’s have a look in Full IIS mode. Out of this Emulator express
and IIS express mode will not work as our code of web role Onstart method tries
to find the sites in local machine IIS and not IIs express. Hence in case of
IIS express we end up in exceptions. Therefore let’s run this application multiple
web app in cloud service over https in full IIS and full emulator mode.
Open the properties of cloud service
project SNIDemo from solution explorer and select Full IIS mode, Full emulator
mode. Now in Full IIS debug will not work therefore run the application by
pressing “Ctrl +F5” or by selecting the option “Run without Debug” from Debug
menu. Now the explorer will open with some certificate error. Let it be as it
is. Now open IIS 8.0 and you will see that, two web sites have been created in
local IIS 8.0 but they are in STOPPED state. If you open the binding details of
every site, you will observe that, SNI is set for both of them.
Now if you click on Start for management
or survey app in IIS 8.0; you will receive an error as “The Process cannot
access the file because it is being used by another process.” This is because
the Compute emulator is using port 443 and hence the web sites cannot be
started and ultimately you cannot see them running. To overcome on this problem
WE HAVE TO REMOVE THE CURRENT DEPLOYMENT FROM COMPUTE EMULATOR AND NOT TO
REFRESH THE IIS. Open compute emulator and right click on current deployment
and select REMOVE. This frees the port 443 being used by emulator. Then
minimize the compute emulator and without refreshing IIS select start option
for both of the web sites and open the urls in browser https://managementapp.kunal.com/default.aspx
and similarly open new browser and type https://surveyapp.kunal.com/default.aspx.
You will see the certificate error here; click to continue with web site option
and Bingo!!!! Both apps are running over HTTPS with different domain names on
the same port 443 in IIS 8.0.
Full IIS gives you an idea how the sites will
get deployed in Azure cloud service role instance after deployment.
Deploying
to Azure Cloud Service
The as is application if hosted on Azure
cloud service then it will not work. This application with multiple web sites
over https in single web role will run over azure cloud service only if you
have valid certificates issued by certificate authority and custom domains
provided by an Internet Service Provider are mapped to your cloud service URL.
So once you have valid certificate and custom domain mapped to
YourCloudService.cloudapp.net url; SSL scalability in IIS in Azure cloud
service will work.
Hope this article has given you detailed
insight on running multiple web application in azure cloud service web role,
then running same on HTTPS over SSL using Server Name Indication feature of IIS
8.0.
Important
– Please suggest your Feedback/ Changes / Comments to the article to improve
it.
Download entire source code with test
certificates - https://gallery.technet.microsoft.com/Multiple-web-sites-in-942e153e
Cheers!!
The hosting information of the post was really good, interesting, Keep updating.
ReplyDeleteHit on our tags to know more.
Dedicated Servers in India | Cheap Dedicated Hosting India | Dedicated Hosting companies India
This is a very nice article. thank you for publishing this. i can understand this easily. AWS Online Training
ReplyDeleteThanks for sharing this useful information, I really appriciate your work, keep sharing such an amazing content in future. Azure Cloud Services Australia
ReplyDeleteAmazing solution, definitely worth all the efforts.
ReplyDelete