Pages

Saturday, May 28, 2011

Controlling Application Pool Idle Timeouts in Windows Azure

A question I hear from time to time is whether it’s possible to change the idle timeout time for IIS application pools in Windows Azure. It is indeed possible, and fairly easy to do with a startup task.
The default idle timeout value is twenty minutes, which means your app pool is shut down after twenty minutes if it’s not being used. Some people want to change this, because it means their apps are a bit slow after twenty minutes of inactivity.
To change the timeout to zero (which means “never”), add the following startup task declaration to ServiceDefinition.csdef:
<Startup>
  <Task commandLine="startup\disableTimeout.cmd" executionContext="elevated" />
</Startup>
And add disableTimeout.cmd in a folder called startup, with the following line of code (should be one line, split just for formatting):
%windir%\system32\inetsrv\appcmd set config -section:applicationPools
    -applicationPoolDefaults.processModel.idleTimeout:00:00:00
Be sure to mark disableTimeout.cmd’s “Copy to Output Directory” setting to “Copy always”.

ASP.NET MVC 3 in Windows Azure

The recently-released ASP.NET MVC 3 is awesome. I’ve been a fan of ASP.NET MVC since the first version, and it keeps getting better. The Razor syntax (which comes from the new ASP.NET Web Pages) makes me very productive, and I love the unobtrusive JavaScript. Scott Guthrie’s blog post announcing MVC 3 has a longer list of the new features. In this post, I’ll give you two options for deploying ASP.NET MVC 3 application to Windows Azure.

A bit of background

Windows Azure virtual machines don’t have ASP.NET MVC installed already, so to deploy an MVC app, you need to also deploy the necessary binaries. (This is true if you’re deploying an MVC app to any server that doesn’t have MVC already installed.) For ASP.NET MVC 2, the Windows Azure tools have a nice template (“ASP.NET MVC 2 Web Role”) that automatically sets the right assemblies to “Copy Local,” meaning they get deployed with your application. ASP.NET MVC 3 just shipped, so there isn’t yet a template in the Windows Azure tools. That means it’s up to you to include the right assemblies.

Technique #1: Include the assemblies in your application

Scott Guthrie has a blog post called “Running an ASP.NET MVC 3 app on a web server that doesn’t have ASP.NET MVC 3 installed,” which covers this technique. Specifically, there’s a link in there to a post by Drew Prog about “\bin deployment.” It lists the assemblies you should reference in your project and mark as “Copy Local.” For convenience, here’s the list from his post:
  • Microsoft.Web.Infrastructure
  • System.Web.Helpers
  • System.Web.Mvc
  • System.Web.Razor
  • System.Web.WebPages
  • System.Web.WebPages.Deployment
  • System.Web.WebPages.Razor
Not all of these assemblies are directly referenced by your project when you create a new ASP.NET MVC 3 application. Some are indirectly used, so you’ll have to add them. Scott Hanselman also has a post about \bin deploying ASP.NET MVC 3 in which he argues that adding these as references isn’t the right thing to do:
NOTE: It's possible to just reference these assemblies directly from your application, then click properties on each one and set copyLocal=true. Then they'd get automatically copied to the bin folder. However, I have a problem with that philosophically. Your app doesn't need a reference to these assemblies. It's assemblies your app depends on. A depends on B that depends on C. Why should A manually set a dependency on C just to get better deployment? More on this, well, now.
Check out his post for his recommended approach. Both have a similar result, causing the required assemblies to be deployed in the \bin folder with your application.

Technique #2: Installing ASP.NET MVC 3 with a startup task

In my last two blog posts (“Introduction to Windows Azure Startup Tasks” and “Windows Azure Startup Tasks: Tips, Tricks, and Gotchas”), then you already know one way to do this. You can use the new WebPI Command Line tool to install MVC 3. See “Using WebPICmdline to run 32-bit installers” in my tips and tricks post to see my recommended syntax for doing this. Just change the product to “MVC3.”
The WebPI method is pretty easy, but I wanted to show an alternative in this post, which is to directly bundle the ASP.NET MVC 3 installer with your application and execute it in a startup task. This turns out to also be quite easy to do.

Step 1: Create a new Windows Azure solution and add an ASP.NET MVC 3 project to it

Use “File/New/Project…” to create a new Windows Azure application, but don’t add any roles to it right away (since there’s no template for ASP.NET MVC 3). In other words, just hit “OK” when you see this dialog:
image
Next, add an ASP.NET MVC 3 project to your solution by right-clicking on the solution and choosing “Add/New Project…” Choose an ASP.NET MVC 3 Web Application, just as you would outside of Windows Azure.

Step 2: Add the ASP.NET MVC 3 application as a web role

Right-click the “Roles” node under your Windows Azure project and choose “Add/Web Role Project in solution…”
image
Then just pick your ASP.NET MVC 3 project. Now it’s associated as a role in your Windows Azure application.

Step 3: Create a startup task

Just for aesthetics, I like to create a separate folder for any startup task that’s more than a single file. In this case, we’re going to need the ASP.NET MVC 3 installer itself as well as a batch file that will run it. (This is because we can’t pass command-line arguments in a startup task.)
Create a folder called “startup,” and put AspNetMVC3Setup.exe in it. Also add a batch file called “installmvc.cmd” (being careful to avoid the byte order mark Visual Studio will want to add, see my tips and tricks post). Put the following two lines in “installmvc.cmd”:
%~dp0AspNetMVC3Setup.exe /q /log %~dp0mvc3_install.htm
exit /b 0
Note the %~dp0, which gives you the directory of the batch file. This is important if your batch file is in a subdirectory, as the current working directory won’t be the same as the location of your startup task files. Also note the exit /b 0, which ensures this batch file always returns a success code. (For extra credit, detect whether ASP.NET MVC 3 is successfully installed and return the proper error code. What you don’t want to do is just rely on the error code of the installer, which probably returns non-success if ASP.NET MVC 3 is already installed.)
Set the batch file and the executable to both have a “build action” of “none,” and a “copy to output directory” setting of “copy always” (or “copy if newer” if you prefer). See my introduction to startup tasks post for a screenshot of what those property settings look like.
Finally, edit ServiceDefinition.csdef to call the batch file:
<WebRole name="WebRole">
  <Startup>
    <Task commandLine="startup\installmvc.cmd" executionContext="elevated" />
  </Startup>
That’s it! Now you can add whatever ASP.NET MVC 3 content you want to the web role, without having to worry about whether the right assemblies will be installed.

Download the code

You can download a full Visual Studio solution that implements the above steps here: http://cdn.blog.smarx.com/files/MVC3WebRole_source.zip

Windows Azure Startup Tasks: Tips, Tricks, and Gotchas

In my last post, I gave a brief introduction to Windows Azure startup tasks and how to build one. The reason I’ve been thinking lately about startup tasks is that I’ve been writing them and helping other people write them. In fact, this blog is currently running on Ruby in Windows Azure, and this is only possible because of an elevated startup task that installs Application Request Routing (to use as a reverse proxy) and Ruby.
Through the experience of building some non-trivial startup tasks, I’ve learned a number of tips, tricks, and gotchas that I’d like to share, in the hopes that they can save you time. Here they are, in no particular order.

Batch files and Visual Studio

I mentioned this in my last post, but it appears that text files created in Visual Studio include a byte order mark that makes it impossible to run them. To work around this, I create my batch files in notepad first. (Subsequently editing in Visual Studio seems to be fine.) This isn’t really specific to startup tasks, but it’s a likely place to run into this gotcha.
[UPDATE 1/26/2011] If you do File/Save As…, you can click the little down arrow and choose a different encoding. “Unicode (UTF8 without signature) codepage 65001” works fine for saving batch files. Thanks, Travis Pettijohn, for emailing me this tip!

Debug locally with “start /w cmd”

This is a tip Ryan Dunn shared with me, and I’ve found it helpful. If you put start /w cmd in a batch file, it will pop up a new command window and wait for it to exit. This is useful when you’re testing your startup tasks locally, as it gives you a way to try out commands in exactly the same context as the startup task itself. It’s a bit like setting a breakpoint and using the “immediate” panel in Visual Studio.
Remember to take this out before you deploy to the cloud, as it will cause the startup task to never complete.

Make it a background task so you can use Remote Desktop

“Simple” startup tasks (the default task type) are generally what you want, because they run to completion before any of your other code runs. However, they also run to completion before Remote Desktop is set up (also via a startup task). That means that if your startup task never finishes, you don’t have a chance to use RDP to connect and debug.
A tip that will save you lots of debugging frustration is to set your startup type to “background” (just during development/debugging), which means RDP will still get configured even if your startup task fails to complete.

Log to a file

Sometimes (particularly for timing issues), it’s hard to reproduce an error in a startup task. You’ll be much happier if you log everything to a local file, by doing something like this in your startup task batch file:
command1 >> log.txt 2>> err.txt
command2 >> log.txt 2>> err.txt
...
Then you can RDP into the role later and see what happened. (Bonus points if you configure Windows Azure Diagnostics to copy these log files off to blob storage!)

Executing PowerShell scripts

To execute an unsigned PowerShell script (the sort you’re likely to include as a startup task), you need to configure PowerShell first to allow this. In PowerShell 2.0, you can simply launch PowerShell from a batch file with powershell -ExecutionPolicy Unrestricted ./myscript.ps1. This will work fine in Windows Azure if you’re running with osFamily=”2”, which gives you an operating system image based on Windows Server 2008 R2. If you’re using osFamily=”1”, though, you’ll have PowerShell 1.0, which doesn’t include this handy commandline argument.
For PowerShell 1.0, the following one-liner should tell PowerShell to allow any scripts, so run this in your batch file before calling PowerShell:
reg add HKLM\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell /v ExecutionPolicy /d Unrestricted /f
(I haven’t actually tested that code yet… but I found it on the internet, so it must be right.)

Using the Service Runtime from PowerShell

In Windows Azure, you’ll find a PowerShell snap-in that lets you interact with the service runtime APIs. There’s a gotcha with using it, though, which is that the snap-in is installed asynchronously, so it’s possible for your startup task to run before the snap-in is available. To work around that, I suggest the following code (from one of my startup scripts), which simply loops until the snap-in is available:
Add-PSSnapin Microsoft.WindowsAzure.ServiceRuntime
while (!$?) {
    sleep 5
    Add-PSSnapin Microsoft.WindowsAzure.ServiceRuntime
}
(That while loop condition amuses me greatly. It means “Did the previous command fail?”)

Using WebPICmdline to run 32-bit installers

[UPDATE 1/25/2011 11:47pm] Interesting timing! WebPI Command Line just shipped. If you scroll to the bottom of the announcement, you’ll see instructions for running it in Windows Azure. There’s an AnyCPU version of the binary that you should use. However, it doesn’t address the problem I’m describing here.
Using WebPICmdline (in CTP now, but about to ship) is a nice way to install things (particularly PHP), but running in the context of an elevated Windows Azure startup task, it has a surprising gotcha that’s difficult to debug. Elevated startup tasks run as NT AUTHORITY\SYSTEM, which is a special user in a number of ways. One way it’s special is that its user profile is under the system32 directory. This is special, because on 64-bit machines (like all VMs in Windows Azure), 64-bit processes see the system32 directory, but 32-bit processes see the SysWOW64 directory instead.
When the Web Platform Installer (via WebPICmdline or otherwise) downloads something to execute locally, it stores it in the current user’s “Local AppData,” which is typically under the user profile. This will end up under system32 in our elevated startup task, because WebPICmdline is a 64-bit process. The gotcha comes when WebPI executes that code. If that code is a self-extracting executable 32-bit process, it will try to read itself (to unzip) and search in the SysWOW64 directory instead. This doesn’t work, and will typically pop up a dialog box (that you can’t see) and hang until you dismiss it (which you can’t do).
This is a weird combination of things, but the point is that many applications or components that you install via WebPICmdline will run into this issue. The good news is that there’s a couple simple fixes. One fix is to use a normal user (instead of system) to run WebPICmdline. David Aiken has a blog post called “Running Azure startup tasks as a real user,” which describes exactly how to do this.
I have a different solution, which I find simpler if all you need to do is work around this issue. I simply change the location of “Local AppData.” Here’s how to do that. (I inserted line breaks for readability. This should be exactly four lines long.)
md "%~dp0appdata"
reg add "hku\.default\software\microsoft\windows\currentversion\explorer\user shell folders"
    /v "Local AppData" /t REG_EXPAND_SZ /d "%~dp0appdata" /f
"%~dp0webpicmdline" /AcceptEula /Products:PHP53 >>log.txt 2>>err.txt
reg add "hku\.default\software\microsoft\windows\currentversion\explorer\user shell folders"
    /v "Local AppData" /t REG_EXPAND_SZ /d %%USERPROFILE%%\AppData\Local /f
[UPDATE 1/31/2011] Prepended the %~dp0 to the webpicmdline call. Sometimes I just put a “cd %~dp0” at the top of the script to avoid gotchas around locations like this.

Use PsExec to try out the SYSTEM user

PsExec is a handy tool from Sysinternals for starting processes in a variety of ways. (And I’m not just saying that because Mark Russinovich now works down the hall from me.) One thing you can do with PsExec is launch a process as the system user. Here’s how to launch a command prompt in an interactive session as system:
psexec –s -i cmd
You can only do this if you’re an administrator (which you are when you use remote desktop to connect to a VM in Windows Azure), so be sure to elevate first if you’re trying this on your local computer.

Introduction to Windows Azure Startup Tasks

Startup tasks are a simple way to run some code during your Windows Azure role’s initialization. Ryan and I gave a basic introduction to startup tasks in Cloud Cover Episode 31. The basic idea is that you include some sort of executable code in your role (usually a batch file), and then you define a startup task to execute that code.
The purpose of a startup task is generally to set something up before your role actually starts. The example Ryan and I used was running Classic ASP in a web role. That’s quite a simple trick with a startup task. Add a batch file called startup.cmd to your role. Be sure to create it with notepad or another ASCII text editor… batch files created in Visual Studio seem to have a byte order mark at the top that makes them fail when executed. Believe it or not, this is the entirety of the batch file:
start /w pkgmgr /iu:IIS-ASP
Mark the batch file as “Copy to Output Directory” in Visual Studio. This will make sure the batch file ends up in the bin folder of your role, which is where Windows Azure will look for it:image
Create the startup task in by adding this code to ServiceDefinition.csdef in the web role:
<Startup>
  <Task commandLine="startup.cmd" executionContext="elevated" />
</Startup>
Note that this startup task is set to run elevated. Had I chosen the other option (“limited”), the startup task would run as a standard (non-admin) user. Choosing an execution context of “elevated” means the startup task will run as NT AUTHORITY\SYSTEM instead, which gives you plenty of permission to do whatever you need.
There’s another attribute on the <Task /> element, which I haven’t defined here. That’s the taskType attribute, which specifies how the startup task runs with respect to the rest of the role lifecycle. The default is a “simple” task, which runs synchronously, in that nothing else runs until the startup task completes. The other two task types, “background” and “foreground,” run concurrently with other startup tasks and with the rest of your role’s execution. The difference between the two is analogous to the difference between background and foreground threads in .NET. In practice, I have yet to see the “foreground” task type used.
The above code actually works. Add a default.asp, and you’ll see Classic ASP running in Windows Azure.
NOTE: In testing this code today, it seems that this only works if you’ve specified osFamily=”2” in your ServiceDefinition.csdef. (With osFamily=”1”, the behavior I was seeing was that pkgmgr never exited. I’m not sure why this is, and it could be something unrelated that I changed, but I thought I’d point it out.)

Download the Code

If you want to try Classic ASP in Windows Azure, you can download a full working Visual Studio solution here: http://cdn.blog.smarx.com/files/ClassicASP_source.zip

More Information

Wade Wegner (Windows Azure evangelist and new cohost of Cloud Cover) has also been working with startup tasks recently, so he and I decided to do a show dedicated to sharing what we’ve learned. We’ll record that show tomorrow and publish it on Channel 9 this Friday. Be sure to check out that episode.

How to Resolve “SetConfigurationSettingPublisher needs to be called before FromConfigurationSetting can be used” After Moving to Windows Azure SDK 1.3

If you’re receiving this exception after migrating a web role from Windows Azure SDK 1.2 to SDK 1.3, this may have to do with changes associated with the new full IIS model introduced in SDK 1.3. This post will help you to understand the change and how to use the FromConfigurationSetting method correctly under the full IIS model. If you want to simply go back to using the Hosted Web Core model (as in previous SDK releases), you can do that too.

The Full IIS Model

Full IIS in Windows Azure introduces new capabilities, such as hosting multiple web sites within a single web role and advanced IIS configuration. For a discussion of the new full IIS hosting model in Windows Azure SDK 1.3 and how it differs from the Hosted Web Core model used previously, see “New Full IIS Capabilities: Differences from Hosted Web Core” on the Windows Azure blog.
The following diagram from that post illustrates one difference that impacts how FromConfigurationSetting is used:
full iis app domain model
With the Hosted Web Core model, RoleEntryPoint code (e.g., WebRole.cs) and web application code (e.g., Default.aspx.cs for ASP.NET) were executed in the same app domain. Under full IIS, these are two distinct app domains. This means that static objects are no longer shared between these two parts of your application, and that’s the key to understanding how to use FromConfigurationSetting in the new full IIS hosting model.

Before: FromConfigurationSetting with Hosted Web Core

The Hosted Web Core model, where everything ran in the same app domain, lead to a common pattern for initializing CloudStorageAccount objects. This is how SDK 1.2 code was commonly written:
In the OnStart method of WebRole.cs:
CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSettingPublisher) =>
    {
        var connectionString = RoleEnvironment.GetConfigurationSettingValue(configName);
        configSettingPublisher(connectionString);
    }
);
(Your function may look more complicated than this, but the information in this post still applies.) Then, in Default.aspx.cs (or an ASP.NET MVC controller):
var account = CloudStorageAccount.FromConfigurationSetting("MyConnectionString");
This code worked, because the call to SetConfigurationSettingPublisher was made in the same app domain as the call to FromConfigurationSetting. However, under the full IIS hosting model, these are two different app domains.

After: FromConfigurationSetting with full IIS

A convenient place to put the call to SetConfigurationSettingPublisher is in the Application_Start method, which runs as part of the app domain where all your web application code runs. This method is located in Global.asax.cs. (If you don’t have a Global.asax.cs already, you can add one by right-clicking on the web application project, choosing “Add,” “New Item,” and then “Global Application Class.”)
void Application_Start(object sender, EventArgs e)
{
    CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSettingPublisher) =>
        {
            var connectionString = RoleEnvironment.GetConfigurationSettingValue(configName);
            configSettingPublisher(connectionString);
        }
    );
}
This is exactly the same code that would have been in WebRole.cs, just moved to a location that’s part of the web application’s app domain.

An Alternative

The combination of SetConfigurationSettingPublisher and FromConfigurationSetting abstracts the location of the configuration setting. Some people use this to write code that works both inside and outside of Windows Azure, reading configuration settings from Windows Azure when available and falling back to web.config or some other location otherwise.
If you don’t need this abstraction layer, you can use another method of initializing CloudStorageAccount objects from configuration settings:
var account = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("MyConnectionString"));

Source From My PDC10 Session: Building Windows Phone 7 Applications with the Windows Azure Platform

Last week at PDC 2010, I presented a session called “Building Windows Phone 7 Applications with the Windows Azure Platform.” Thanks to all who attended in person and virtually! As promised, I’m going to share all the source code from my session here on my blog.
To start with, I’ve just published the source code for my “DoodleJoy” application here: http://cdn.blog.smarx.com/files/DoodleJoy_source.zip. The source includes both a Windows Azure application that hosts an OData service and a Windows Phone 7 application that consumes it.
To get the application running, you’ll need the just-released OData client for Windows Phone 7, which you can find at http://odata.codeplex.com/. (Click “View all downloads” to find the binaries and code gen tool.) Then open the two projects (phone and cloud) in separate Visual Studio instances. Run the cloud project first, and note the port. If it’s running at anything other than port 81, you’ll need to edit the URL in MainViewModel.cs in the phone project to point to the right location. Then launch the phone project, and things should just work.