Recently we had a problem with an internal application that is hosted on Azure PaaS. Windows Authentication would be disabled periodically for our Cloud Service. This would cause our application to return a 403 error to all visitors because we required that users be authenticated before accessing the application. Originally we didn’t have this issue with our application on Azure, but at some point the default IIS configuration changed and we started seeing this issue when a new role was started.
This issue underlines the main difference between hosting applications on-site or in an Azure Virtual Machine vs hosting applications as Azure Cloud Services. With a physical or virtual server, the system administrator has complete control over the software installed, and its configuration. In Azure, this persists even if the physical server hosting the Virtual Machine has a hardware failure. However, in a Cloud Service, the web role is deployed to a fresh virtual machine that has the default configuration.
Our solution to this issue was to use a Startup Task to enable Windows Authentication in IIS when the web role was started. Startup Tasks will run whenever a role is recycled, whether it is on the same server or when a new instance of the role is started on a new server.
Why Startup Tasks
Startup Tasks are the easiest way to configure the environment your Cloud Service runs in because they run every time a Web or Worker Role is started or recycled. This means the tasks will run when the VM hosting your application is restarted due to updates being installed, the instance is moved to a new VM due to hardware failure or a new instance spinning up in Azure. Startup Tasks can be used to automate most if not all configuration changes needed on the server and can be very helpful when transitioning from on-site hosting to Azure Cloud Services.
Startup Tasks can be used to run arbitrary batch or PowerShell scripts that can set up the server environment for your running application. This includes enabling IIS features and configuration options, installing software your application uses, and logging messages about the environment. Any number of different Startup Tasks can be run, so even applications that rely on a highly-customized environment and several external software packages can be run in Cloud Services by using Startup Tasks.
How to Use Startup Tasks
Startup Task entries are defined in the ServiceDefinition.csdef file in your Cloud Service project. Under the Web Role definition that you want to add the Startup Task to, put the following XML:
<Task commandLine="startup.bat" executionContext="elevated" taskType="simple"></Task>
This will run the script “startup.bat” in your Web Role project’s root. You can, of course, put the script wherever you want in the project and reference that path in the commandLine attribute. You can also put a simple command here, but for anything but the simplest tasks it’s better to reference a script file.
The executionContext attribute determines what permissions the Startup Task is run with. The first option is limited. This gives the task the same permissions as the role that is being started. The second option is elevated. This gives the task administrative permissions. This is necessary to do things like make changes to IIS configurations.
The taskType determines how the task is run. The first type is simple. This task will run synchronously, blocking further initialization of the role and any other startup tasks until it completes. The other taskTypes are background and foreground. Both will run asynchronously, so the other tasks and the web role will run at the same time. The difference is that a foreground task will keep the role from being recycled, while a background task will not.
If you want to run a PowerShell script in a Startup Task, you should use your “startup.bat” file to run the PowerShell script. You can do this with the following command:
PowerShell -ExecutionPolicy Unrestricted .\startup.ps1 >> "%TEMP%\StartupLog.txt" 2>&1
The name of the PowerShell script is “startup.ps1” in this case. It will take any output from the script and log it to a “StartupLog.txt” file in the system’s temporary folder. This is a good practice to follow because it can make diagnosing issues with Startup Tasks easier.
In your PowerShell script, you can now do things like make IIS configuration changes. Keep in mind that the Startup Task will run before IIS is fully initialized and the site is created, so you might not have access to more advanced settings. In our case, we were able to enable Windows Authentication globally and disable anonymous authentication with the following two lines:
# Disable Anonymous Authentication
Set-WebConfigurationProperty -filter /system.WebServer/security/authentication/AnonymousAuthentication -name enabled -value false
# Enable Windows Authentication
Set-WebConfigurationProperty -filter /system.WebServer/security/authentication/windowsAuthentication -name enabled -value true
For more IIS commands you can run through PowerShell, see this article on Microsoft’s TechNet. Make sure to write Startup Tasks so that they can be run successfully multiple times because sometimes the role will recycle without a machine restart.
The final step is to modify the properties of the two startup files so that they are copied to the output directory. To do this, select the files in Solution Explorer and look for the “Copy to Output Directory” field in the Properties window. Set this to “Copy always.”