Sorted By: Tag (iis)

Script Out Everything - Initialize Your Windows Azure VM for Your Web Server with IIS, Web Deploy and Other Stuff

Script Out Everything - Initialize Your Windows Azure VM for Your Web Server with IIS, Web Deploy and Other Stuff
2012-09-17 20:17
Tugberk Ugurlu


Today, I am officially sick and tired of initializing a new Web Server every time so I decided to script it all out as much as I can. I created a new Windows Azure VM running Windows Server 2012 and installed Web Platform Installer v4 Command Line tool (aka WebPICMD.exe).

Next thing I need to do is to install the IIS Web Server Role inside my VM. To do that, I opened up the Server Manager and closed it instantly because I didn't wanna do that through a GUI either. It turned out that it is fairly easy to manage your server roles and features through PowerShell thanks to ServerManager PowerShell module. This module has couple of handy Cmdlets which enable you to manage your server’s roles and features.

image

To install the IIS Web Server, I run the following PowerShell command. It installs IIS Web Server (along with the necessary dependencies and management tools) and logs the output under the TEMP folder.

$logLabel = $((get-date).ToString("yyyyMMddHHmmss"))
Import-Module -Name ServerManager
Install-WindowsFeature -Name Web-Server -IncludeManagementTools -LogPath "$env:TEMP\init-webservervm_webserver_install_log_$logLabel.txt"

image

When we look at the installed features, we should see that IIS Web Server is now listed there:

image

At this stage, we have a few more necessary features to install such as ASP.NET 4.5 and Management Service. Optionally, I always want to install Dynamic Content Compression and IIS Management Scripts and Tools features. To install those features, we will run the following script:

#add additional windows features
$additionalFeatures = @('Web-Mgmt-Service', 'Web-Asp-Net45', 'Web-Dyn-Compression', 'Web-Scripting-Tools')
foreach($feature in $additionalFeatures) { 
    
    if(!(Get-WindowsFeature | where { $_.Name -eq $feature }).Installed) { 

        Install-WindowsFeature -Name $feature -LogPath "$env:TEMP\init-webservervm_feature_$($feature)_install_log_$((get-date).ToString("yyyyMMddHHmmss")).txt"   
    }
}
#Set WMSvc to Automatic Startup
Set-Service -Name WMSvc -StartupType Automatic

#Check if WMSvc (Web Management Service) is running
if((Get-Service WMSvc).Status -ne 'Running') { 
    Start-Service WMSvc
}

As you can see at the end of the script, we are also setting the Management Service's startup type to automatic and finally, we are starting the service.

Now the IIS Web Server is ready, we need to get a few more bits through the Web Platform Installer v4 Command Line tool. With all of my web servers, there are two concrete tools I would like to have: Web Deploy and URL Rewrite Module. We can certainly install those manually but we can also script out their installation. WebPICMD.exe allows us to install products through command line and the following command will work for us:

$webPiProducts = @('WDeployPS', 'UrlRewrite2')
.\WebPICMD.exe /Install /Products:"$($webPiProducts -join ',')" /AcceptEULA /Log:"$env:TEMP\webpi_products_install_log_$((get-date).ToString("yyyyMMddHHmmss")).txt"

I assume that WebPICMD.exe is under your path here (if you have installed the 64x version of the product, you can find the executable file under C:\Program Files\Microsoft\Web Platform Installer). When the installation is complete, we should see the success message:

image

Well, we have completed most of the work but there are still couple of things to do. First of all, we need to allow incoming connections through TCP port 8172 because this is the port that Web Deploy will talk through. To enable that, we can go to Windows Firewall with Advanced Security window but that would be lame. Are we gonna use netsh? Certainly not :) With PowerShell 3.0, we can now control the Windows Firewall with Advanced Security Administration. This functionality is provided through NetSecurity PowerShell module but with the new dynamic module loading feature of PowerShell 3.0, we don’t need to separately import this. The following command will add the proper firewall rule to our server.

New-NetFirewallRule -DisplayName "Allow IIS Management Service In" -Direction Inbound -LocalPort 8172 -Protocol TCP -Action Allow

Lastly, we need to inform windows azure about this firewall rule as well because all the requests, which come from outside, will go through the load balancer and it doesn’t open up any ports by default (except for Remote Desktop ports). You can the endpoints through Windows Azure Portal but Windows Azure PowerShell Cmdlets to add this as well. The following command will add the proper rule. Just change the $serviceName and $vmName according to your credentials:

Get-AzureVM -ServiceName $serviceName -Name $vmName | Add-AzureEndpoint -Name "WebDeploy" -Protocol TCP -LocalPort 8172 -PublicPort 8172 | Update-AzureVM

When we look at the portal, we should see that our endpoint was created.

AzureEndpoint

Now, everything should be working perfectly. Of course you also need to add TCP port 80 to your Endpoint lists for your VM in order for your web sites to be reachable through HTTP (assuming you will only use PORT 80 for your web applications). To test everything out, I created a web application under IIS. Then, I right clicked on it and navigate to Deploy > Configure Web Deploy Publishing.

image

This will bring up another dialog. This is the place where we can configure web deploy settings. There are other ways to do this as well.

image

Just change the VM name with your VIP address and this will generate a publish profile. We can now use this publish profile file to push our web application to the server.

WebDeployPublishVS

When the publish is completed, we will see the complete result inside the Output window.

WebDeployPublishVS_2

We jumped through lots of hoops to get this done but how is this better than doing it manually? Imagine that you bring all these scripts together and run them through multiple VMs. It’s going to save a lot of time for you. I am not an IT pro. So, this is enough to make me happy because I just proved that nearly everything that a web developer needs can be automated through PowerShell or various command line tools.

Update:

I put these together inside one script file. Follow the instructions inside the script and you will be good to go.

https://gist.github.com/3742921#file_init_web_server_vm.ps1

This doesn't add the endpoints for your VM. For this, you have another script as well:

https://gist.github.com/3742921#file_init_web_server_azure_vm_endpoints.ps1

Remove Trailing Slash From the URLs of Your ASP.NET Web Site With IIS 7 URL Rewrite Module

One of the aspect of SEO (Search Engine Optimization) is canonicalization. In this blog post, we will see how easy to work with IIS Rewrite Module in order to remove evil trailing slash from our URLs
2011-09-11 07:08
Tugberk Ugurlu


redirect-me-baby

One of the aspect of SEO (Search Engine Optimization) is canonicalization. Canonicalization is the process of picking the best URL when there are several choices according to Matt Cutts, the head of Google’s Webspam team.

Here is how Matt Cutts explains what canonical URL is :

“Sorry that it’s a strange word; that’s what we call it around Google. Canonicalization is the process of picking the best URL when there are several choices, and it usually refers to home pages. For example, most people would consider these the same URLs:

  • www.example.com
  • example.com/
  • www.example.com/index.html
  • example.com/home.asp

But technically all of these URLs are different. A web server could return completely different content for all the URLs above.”

If you have multiple ways of reaching your web page (as above), then you need to sit down because it is time to make some decisions my friends.

Trailing Slash is Evil

Let’s assume that we have created a web application, an ASP.NET MVC app because we are so cool. We have our pretty URLs as well.

Let’s go to a page on our web site :

http://localhost:55050/Home/About

image

And another page :

http://localhost:55050/Home/About/

image

We have got the same page content. As we have mentioned before, these two will be treated as two different web page and it will confuse the search engine a bit (even if they are so smart today).

The solution is pretty simple : when a page is requested with trailing slash, then make a 301 (permanent) redirect to the non-trailing-slash version.

IIS URL Rewrite Module

There are several ways of doing that with ASP.NET architecture :

  • You could write your own HttpModule to handle this.
  • You could do a poor man’s redirection on your controller (on your page load if the application is a web forms application).
  • You could use IIS URL Rewrite Module to easily handle this.
  • And so on…

In this quick blog post, I will show how we can implement this feature for our whole web site with IIS Rewrite Module.

URL Rewrite Module is an awesome extension to IIS. Installing it to your web server is also pretty easy if you haven’t got it yet. Just run the Web Platform Installer on your server, and make a search for “url rewrite”. Then the filtered result will appear and you will see if it is installed or not :

image

After you have it, you will see the management section inside your IIS Manager under IIS section :

image

Cut the crap and show me the code

Now, we are all set up and ready to implement this feature. As it is usual nearly for all Microsoft products, there are thousands (ok, not thousand but still) of way to approach this feature but the easiest way of implementing it is to write the logic inside your web.config file.

As you already know, there is a node called system.webServer under the root configuration node. IIS Rewrite Module reserves a node under system.webServer section and allow us to configure the settings there pretty easily. What we will do is to only write the following code under system.webServer node :

<rewrite>
  <rules>
  
    <!--To always remove trailing slash from the URL-->
    <rule name="Remove trailing slash" stopProcessing="true">
      <match url="(.*)/$" />
      <conditions>
        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
      </conditions>
      <action type="Redirect" redirectType="Permanent" url="{R:1}" />
    </rule>
    
  </rules>
</rewrite>

What this code does is to tell the module to remove the trailing slash from the url if there is one and make 301 permanent redirect to the new URL.

Don’t allow the code to freak you out. It might look complicated but there are good recourses out there to make you feel better. Here is one of them :

Using the URL Rewrite Module by Ruslan Yakushev

When you run your site after this implementation and navigate to /Home/About/, watch what is going to happen :

image

image

Isn’t that awesome? A little effort and perfectly clean way of implementing the 1 of a thousand parts of canonicalization.

Some Gotchas

  • In your development environment, if you run your web site under Visual Studio Development Sever, you won’t be able to see this feature working. You need to configure your application to run under at least IIS Express to see this feature working.
  • When you deploy your web site and see this feature not working on your server, it is highly possible that you misconfigured something on your server. One of the misconfiguration you might have done could be setting the overrideModeDefault attribute to Deny for rules under <sectionGroup name="rewrite"> inside your applicationHost.config file.
  • If you are on a shared hosting environment and you see this feature not working, then ask your provider if they have given you the permission of configuring this part.

Local IIS 7.0 - CS0016: Could not write to output file / Microsoft.Net > Framework > v4.0.30319 > Temporary ASP.NET Files

Solution to an annoying error message! You are getting 'Could not write to output file 'c:\Windows\Microsoft.NET\Framework\....' message? You are at the right place.
2011-03-05 08:18
Tugberk Ugurlu


This week I went nuts over my local IIS. I have never swore to a machine that much in my whole life. I am sure of that! The problem is not that big and probably not worth to be written on a blog post I am going to write it anyway because the solution was hard to find on the internet. Maybe this post will help you to fix the problem as I did and you will stop swearing to you machine as I did Smile

Let’s get to the point. I am no IIS guy! Seriously!  The so called Cassini (the tiny web server which pops up when you run a web application on Visual Studio) was so enough for me for over 2 years. But no more enough. I figured that IIS can not be ignored by me anymore. How can I get that point? That’s not the issue here. The issue is that I tried to run an ASP.NET MVC 3.0 application under my local IIS 7.0 and got a very annoying error. Which is;

Compiler Error Message: CS0016: Could not write to output file 'c:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files\root\62d43c41\27d749ca\App_Code.7lodcznm.dll' – 'Access denied.'

imageTemporary ASP.NET Files folder (c:\Windows\Microsoft.NET\Framework\v4.0.30319\Temporary ASP.NET Files) was the the one with the problem here. First, I thought that the problem is related to security permissions on the folder and I was right.

I right clicked on the Temporary ASP.NET Files folder and go to the security tab. I noticed that there is user called IIS_IUSRS and that guy has the full control permission. But apparently that was not enough.

The Temporary ASP.NET Files  and C:\Windows\temp folders should have IIS_WPG and NETWORK SERVICE users with the full control permission. I have no idea why C:\Windows\temp folder needs that but I have no effort left to try to find that. Instead, I am writing a blog post about the problem. Maybe latter I will get to that and figure it out, too Smile

Once you applied those setting, restart your IIS and try to run your application again. The error should be gone by now.

I suffered a lot by trying to find the right method for the problem and I hope you didn’t have to go through hell over this.

Hope this helps Smile

Running ASP.NET MVC Under IIS 6.0 and IIS 7.0 Classic Mode : Solution to Routing Problem

In this blog post, we will see how to run ASP.NET MVC application under IIS 6.0 and IIS 7.0 classic mode with some configurations on IIS and Global.asax file...
2011-02-26 07:31
Tugberk Ugurlu


Highway sign

I wrote a blog post on Deployment of ASP.Net MVC 3 RC 2 Application on a Shared Hosting Environment Without Begging The Hosting Company couple of months ago. The solution was working for most case scenarios if the server is configured properly for ASP.NET Routing. Other working case I have seen was the applications which are running under IIS 7.0 integrated mode. Under IIS 7.0 integrated mode, no special configuration necessary to use ASP.NET Routing.

As we know, one of the most beautiful parts of ASP.NET MVC framework is Routing. We have nice, clean, extensionless URLs thanks to routing and this is becoming an issue under IIS 6.0 and IIS 7.0 classic mode.

When we typing the path of a web site page inside the address bar of our web browser, we are making a request against server. If our web application is running under any version of IIS, the request hits ASP.NET framework on certain conditions. Especially, Older versions of IIS only map certain requests to the ASP.NET framework. If the extension of the web request is aspx, ashx, axd or any other extensions which is specific for ASP.NET framework are being mapped to ASP.NET framework. So, in a MVC application the requests are not being mapped to ASP.NET framework. 

 image
(You will be getting this 404 exception when you hit the extensionless URL of your application)

No, do not throw away your precious, new born ASP.NET MVC application which you have created working along with Nuget PMC (which is perfect), EFCodeFirst and any other cool newbie stuff.  There are optional solutions for this problem and they are not like hard things to implement.

Although, if you have a Windows Server 2003 and thinking about going up to IIS 7.0 integrated or classic mode, stop right there my friend ! Because, IIS 7.0 is not compatible with Windows Server 2003. So you are stuck with IIS 6.0 for now and keep reading for the solution Smile

The solutions are optional as indicated. They depend on what kind of powers you have over your server. Here is the list of solution you might be interested;

Option 1 : I am the guy with the full control power over my server and I want to keep extensionless URLs

If you have full access over your server, you could create so called Wildcard Script Map so that you can use the default ASP.NET MVC route table with IIS 7.0 (in classic mode) or IIS 6.0. This Wildcard Script Map will map all requests to the web server to the ASP.NET framework.

I have no experience with this option, though. I had a problem like this within this week, I have solved it with the following option and didn’t want to use this one even if I have full control over my server. I am not a server pro, so I won’t be making any comments on how this will effect the requests flow made against your server. I just thought that “man, every single request which made against my server will be mapped to ASP.NET framework. This could effect the speed of the delivery process.” and that how I skipped this option.

And, bad news guys Sarcastic smile Microsoft, also, indicated the following line of sentence one of their web site;

“Be aware that this option causes IIS to intercept every request made against the web server. This includes requests for images, classic ASP pages, and HTML pages. Therefore, enabling a wildcard script map to ASP.NET does have performance implications.”

To implement this feature, you need to follow some steps and here is the text I grabbed from http://asp.net/ ;

 

image_thumb[5]

Here's how you enable a wildcard script map for IIS 7.0 (classic mode):

  1. Select your application in the Connections window
  2. Make sure that the Features view is selected
  3. Double-click the Handler Mappings button
  4. Click the Add Wildcard Script Map link
  5. Enter the path to the aspnet_isapi.dll file (You can copy this path from the PageHandlerFactory script map)
  6. Enter the name MVC Click the OK button

 

 

 

image_thumb[4]

Follow these steps to create a wildcard script map with IIS 6.0:

  1. Right-click a website and select Properties
  2. Select the Home Directory tab
  3. Click the Configuration button
  4. Select the Mappings tab
  5. Click the Insert button
  6. Paste the path to the aspnet_isapi.dll into the Executable field (you can copy this path from the script map for .aspx files)
  7. Uncheck the checkbox labeled Verify that file exists
  8. Click the OK button

 

Option 2 : I am the guy who has the full control power over my server, cares about performance and not care about URLs

This option is the one of the other option I do not like very much but maybe you will Smile So here is the deal;

We will simply add Extensions to the Route Table so that older versions of IIS can pass requests to the ASP.NET framework. This option requires changes inside Global.asax file of you application and some addition work on IIS for modifying the Default route so that it includes a file extension that is mapped to the ASP.NET framework.

Let’s see the RegisterRoutes method inside your Global.asax file;

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
            );

        }

As you see here on line 7, we are aiming to get URLs without extension. Let’s see what it needs to be look like after we change it in order to implement this option;

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default",
                "{controller}.mvc/{action}/{id}",
                new { action = "Index", id = "" }
              );

            routes.MapRoute(
              "Root",
              "",
              new { controller = "Home", action = "Index", id = "" }
            );


        }

Here, we are assigning .mvc extension for every URL for controller name.

I want to warn you about your links inside your views. If you created them by hard coding, now you are officially screwed my friend. Because, you either have to change all of them by hand or need to implement option 1 in order to keep them unbreakable. If you used ActionLink or RouteLink kind of way, then your are good to go. Changes will be handled by MVC Framework for you.

Therefore, to get ASP.NET Routing to work, we must modify the Default route so that it includes a file extension that is mapped to the ASP.NET framework.

This is done using a script named registermvc.wsf. It was included with the ASP.NET MVC 1 release in C:\Program Files\Microsoft ASP.NET\ASP.NET MVC 1.0\Scripts, but as of ASP.NET 2 this script has been moved to the ASP.NET Futures, available at http://aspnet.codeplex.com/releases/view/39978.

Executing this script registers a new .mvc extension with IIS. After you register the .mvc extension, you can modify your routes in the Global.asax file so that the routes use the .mvc extension.

After this implementation, your URLs will look like this;

/Home.mvc/Index/

/Product.mvc/Details/3

/Product.mvc/

Option 3 : I am the guy who has no control power over my server, not care about URLs (If you care, it does not matter. You have no choice)

This option is the easiest way of make your application up and running within minutes depending on your application structure. Only you need to do here is; making some changes inside your Global.asax file, recompiling your application and publishing it into your server. That’s all. Let’s see how our new RegisterRoutes method inside Global.asax file needs to be look like;

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default",
                "{controller}.ashx/{action}/{id}",
                new { action = "Index", id = "" }
              );

            routes.MapRoute(
              "Root",
              "",
              new { controller = "Home", action = "Index", id = "" }
            );


        }

Look at line 7. Isn’t it familiar? It is one of the ASP.NET framework extensions. You could add .aspx or whatever you want from ASP.NET framework extensions. It is already registered into the Default route so the requests will be mapped to the ASP.NET framework.

I hope that this is the solution you are looking for your problem.

Tags