Getting the full local path of a file in a site node in a webrole within a startup cmd file? - azure

It's been several hours lost now, i just don't get it.
Situation :
- Migrating an ASP.net app to Azure
- IMPORTANT : This is a webrole with several websites, all websites are compiled in a folder "azure.builds" so my sites bindings are like
physicalDirectory="..\azure.builds\www.mywebsite.com"
physicalDirectory="..\azure.builds\cb.mywebsite.com"
and so on
I have a cmd script that works great on my dev machine, which set permission on IIS to execute on an exe file (it has to be called by an html form).
Here is my local code startup.cmd which works against my local IIS :
%windir%\system32\inetsrv\appcmd set config /section:system.webServer/handlers /+"[name='MY_GCI', path='.exe',verb='',modules='CgiModule', scriptProcessor='c:\websites\myproject\azure.builds\cb.mywebsite.com\cgi-bin\mymodule.exe',resourceType='Either']"
On my html form the file is called via action "http://cb.mywebsite.com/cgi-bin/mymodule.exe" and guess what it works.
But in my migration to Azure it doesn't, i'm unable to set permission to this particular file. The problem is i can't figure the full complete path to the EXE files as the example above show.
I tried this (%ROLEROOT%) :
%windir%\system32\inetsrv\appcmd set config /section:system.webServer/handlers /+"[name='MY_GCI', path='.exe',verb='',modules='CgiModule', scriptProcessor='%ROLEROOT%\azure.build\cb.mywebsite.com\cgi-bin\mymodule.exe',resourceType='Either']"
Does not work.
I studied several example involving similar CGI setup (such running PHP) but the difficulty is that the EXE module have to be exposed on http://cb.mywebsite.com/cgi-bin/mymodule.exe wich is not even the main root webiste on my azure webrole, so this is not exactly the same.
Sorry if it is not very clear to understand, let say in short : how to find the full local path of a particular file in a particular site node in a webrole within a startup cmd file ?

It appears you have this value outside of Azure:
scriptProcessor='c:\websites\myproject\azure.builds\cb.mywebsite.com\cgi-bin\mymodule.exe
Now you want the equivalent within Azure. The key for doing this is to use the ROLEROOT environment variable.
It appears you have considered ROLEROOT already, but perhaps there is a little bit off in mapping into your directory structure. I suggest you RDP into the Azure instance and get oriented (look at e:\approot for starters, but you are not guaranteed to always be there, thus the need for using ROLEROOT). RDP: http://msdn.microsoft.com/en-us/library/windowsazure/gg443832.aspx
If your real problem has to do with "...the difficulty is that the EXE module have to be exposed on http://cb.mywebsite.com/cgi-bin/mymodule.exe wich is not even the main root webiste on my azure webrole, so this is not exactly the same." then I am not sure I can help as I am unsure of issue you are describing. But one idea: RDP in (as mentioned above) and try to execute the CGI script by hand in Azure and see how that goes. If that doesn't work, you may have a path or permission problem. If it does work, you still may have a problem related to permissions (since the RDP account will have more power than your default IIS user).
A couple of other things to try: Ensure your Azure Web Role is running with elevated permissions by using the following setting in ServiceDefinition.csdef:
<WebRole name="WebHost">
<Runtime executionContext="elevated"/>
...
And also consider running your startup task elevated, also via ServiceDefinition.csdef:
<Startup>
<Task commandLine="startup.cmd" executionContext="elevated" />
</Startup>
And, finally, some StartUp Task debugging tips:
http://blog.smarx.com/posts/windows-azure-startup-tasks-tips-tricks-and-gotchas

Related

IIS - AddDataProtection PersistKeysToFileSystem not creating

I've developing an asp.net core application to tun on a web far, and I'm using "AddDataProtection" to protect for key encryption at rest like, the documentation recommends, but when I deploy my application and run directly from IIS with AppPool identity, the key is never created and I get errors on the DpapiNG windows logs.
My code is the following:
services.AddDataProtection(opt => opt.ApplicationDiscriminator = ApplicationConfig.dataProtectionApplicationDiscriminator)
.PersistKeysToFileSystem(new DirectoryInfo(encKeyPath))
.ProtectKeysWithDpapiNG(string.Format("CERTIFICATE=HashId:{0}", ApplicationConfig.dataProtectionCertThumbprint),
flags: DpapiNGProtectionDescriptorFlags.None);
Debugging from visual studio, everything runs fine, but I'm running VS under administrator rights, so permission is not an issue here.
I've tried adding permissions to the AppPool App user to the private key it self directly from MMC, but it did not worked, and even gave permission on the full path to the location were the keys should be created like stated here https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview (check first comment) but also it did not worked.
I was only able to make it work by setting the AppPool to run with the identity of an Administrator, but clearly this is a no go, I just wanted to make sure this was a permission issue somewhere.
Is anybody facing the same issue that is able to help?
Regards,
André
Most likely your issue is you are trying to store your keys somewhere in a folder path that you are cobbling together (or even by using the default path that AddDataProtection provides) that uses an environment path such as %LOCALAPPDATA%. Example: "%LOCALAPPDATA%\ASP.NET\DataProtection-Keys".
Usually, by default IIS DOES NOT set up your app pool accounts with environment path variables such as %LOCALAPPDATA%. The value ends up being blank and your app then tries to write keys to the wrong folder (such as \ASP.NET\DataProtection-Keys instead of %LOCALAPPDATA%\ASP.NET\DataProtection-Keys).
Fix: Within %WINDIR%\System32\inetsrv\config\applicationHost.config set setProfileEnvironment=true. I think you have to restart IIS as well.

How to setup IIS Express from a script the way Visual Studio does it?

When we configure a web application to run in IIS Express there are certain things VS does, like:
Creating the application host configuration file in the IISExpress subfolder of the user documents folder.
Creating a dedicated site section for each web application in the solution, including ours.
Maybe more things are done, which I am unaware of.
I would like to replicate the same process from a script, so that running the web application from the script would be equivalent to running it from VS. Including for the very first time.
Right now I start IISExpress with the /port and /path flags, because this is how I used to run Cassini. However, Cassini supported an additional flag - /vpath. They removed it from IISExpress, meaning I have to use another set of flags - /config, /site, /siteid. But I suspect it must be done in conjunction with the Appcmd.exe utility.
This second approach is still something I haven't managed to master. So, my question is this - suppose I am given the port, path and vpath of a web application (i.e. no need to read them from the web application's csproj file, like VS does). What command sets up the right application host configuration file and how do I run IISExpress to take advantage of it?

Azure: How to execute startup tasks for each site entry at webrole?

We have WebRole, that hosting multiple sites. We made startup tasks for each site with some site specific actions. The problem is that only startup task of first site (first site listed in section), could be run during deployment.
Looks like this happens cause only first site (or that one from web-role defifnition) will be copied to [drive]:\approot folder at WebRole Instance. All other sites will be copied to [drive]:\sites\ (early it was another location so it could be a subject of future changes).
Is there any simple way to run batch files for each site in [drive]:\sites\ ?
Is there any tricks with RoleEnvironment object or xPath values (like here http://msdn.microsoft.com/en-us/library/windowsazure/hh404006.aspx) that can provide exact path where additional sites will be placed ?
ATM we have 2 options, both are hacky and unstable, imo:
1.Read data from IIS configuration and use it as main startup task input
2.Scan /sites/ for explicitly named scripts and execute it if found.
Startup Task is meant to be for a Role and not for a site. You have to find your way out using the RoleEntryPoint descendant and utilize its OnStart method to manually run your startup tasks per site.
As for finding out the sites directories check out this question, as the answer completly covers the task. I use it, it is 100% reliable for production.

Azure: How to execute a startup task delayed?

I have two Sites within my WebRole and have defined a Startup task.
The first line works fine, it creates a new App pool for me:
%WINDIR%\system32\inetsrv\appcmd.exe add apppool /name:"VCPool" /managedRuntimeVersion:"v4.0" /managedPipelineMode:"Integrated"
Now I would like to change my second to this new created AppPool, but adding another line right after, doesn't help.
%WINDIR%\system32\inetsrv\appcmd.exe set app "WebRole_IN_0_VC/" /applicationPool:"VCPool"
It seems the second site is somehow not yet ready.
How can I delay my task by 30 seconds or delay appcmd.exe slightly?
Unless there is a way to create dependencies for this startup task that it shall only be executed when that second site is up and running?
Any help would be highly appreciated,
Many Thanks,
Is there a way to delay this execution by 30 seconds to make sure the second site is up and can be changed?
Update:
Thanks for the hints. I have made further investigation into this matter. I have found OnStart() event.
1) But since I am using silverlight and simply wrap the existing Web project in the Cloud Roles project, I wouldn't have a WebRole.cs as such. Can I just add it to my Silverlight Web project and use it there? Or is it recommended creating a WebRole project from scratch and make it to replace the Silverlight Web project alltogether?
2) Regarding the <Runtime/> tag in the service definition, do I simply add it like this? Would it have any security implications when the webrole runs elevated?
<WebRole name="WebRole" enableNativeCodeExecution="true" vmsize="ExtraSmall">
<Runtime executionContext="elevated"/>
<Sites>
<Site name="WebRole" physicalDirectory="./WebRole">
...
</Sites>
</WebRole>
3) Last but not least how do I run a cmd file or in fact this line
%WINDIR%\system32\inetsrv\appcmd.exe set site /site.name:"WebRole_IN_0_VC" /[Path='/'].applicationPool:"ASP.NET v4.0" >>Log.txt
in the OnStart() method?
Can you explore using PowerShell scripts for performing these tasks. PowerShell has a way to sleep thread. Something like
Batch file
%WINDIR%\system32\inetsrv\appcmd.exe add apppool /name:"VCPool" /managedRuntimeVersion:"v4.0" /managedPipelineMode:"Integrated"
powershell -command "Set-ExecutionPolicy Unrestricted" 2>> err.out
powershell .\sleep.ps1 2>> err.out
%WINDIR%\system32\inetsrv\appcmd.exe set app "WebRole_IN_0_VC/" /applicationPool:"VCPool"
I have not tried but it should work. See this post to know about Powershell integration.
The site, as you have discovered, is created after the 'Task' section runs. Instead, run your code from the 'OnStart' event. The site will have been created by that point. You may need to update your Service Definition file to run your role 'elevated' This can be done by adding this tag:
<Runtime executionContext="elevated"/>
Edited
To answer your further questions:
1) Whatever project you have, you should just be able to add the RoleEntryPoint class. You may have to do this manually.
2) Adding the runtime tag won't add any significant risk to your deployment.
3) Create a cmd file to put your command in (i.e. OnStart.cmd) and use some code like this:
System.Diagnostics.Process.Start("OnStart.cmd")
More information on starting a process here: http://msdn.microsoft.com/en-us/library/53ezey2s.aspx
For those interested ... I just blogged about how to execute AppCmd startup tasks on Windows Azure configure IIS websites - including finding the site by web role name, and executing it delayed so that the site config is already created by Azure's IISConfigurator.exe.
More here: http://mvolo.com/configure-iis-websites-windows-azure-startup-tasks-appcmd/.
Thanks,
Mike

Unable to commit WebAdministration changes in Azure Web Role

I have an Azure Web Role running in the new 1.3 SDK and I am having permissions issues when trying to make changes to IIS using the Microsoft.Web.Administration.ServerManager. Whenever I execute CommitChanges() it throws this error:
an UnauthorizedAccessException "Cannot write configuration file due to insufficient permissions".
My ServerManager code is executing in the OnStart method of the RoleEntryPoint.
My understanding was that the purpose of moving to full IIS support in 1.3 was so that we could have greater control over the configuration of our application, including creating new IIS sites on the fly if desired.
Make sure your role is running with elevated privileges.
I think there are two questions here. Firstly the use of IIS in Azure. Yes using the 1.3 SDK means that we now have access to more features than we did previously. This means that we can setup more than one site and virtual directories for our sites in the configs as shown in the training kit.
Secondly there is the privileges issue that you're getting while trying to make changes programatically. I'm going to presume that you're not trying to do one of the things that you can simply do through the config above. The most likely reason your code is erroring is because web roles are not run with admin privileges. Fortunately in the 1.3 SDK we also have a way to run code with elevated privileges. As shown elsewhere in the training kit you can create a separate .exe that you specify to be run at startup with elevated privileges in the config.
Providing a clear example for reference to #smarx answer.
Here is the configuration to run RoleEntryPoint.OnStart (WebRole.Onstart) with Admin-level privileges.
<ServiceDefinition name="MyProject.Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2015-04.2.6">
<WebRole name="MyProject.WebRole" vmsize="Small">
<Runtime executionContext="elevated"/> <!-- Required for certain WebRoleOnStart tasks (avoid insufficient permission errors) -->
<Sites>
<!-- ... -->
</Sites>
</WebRole>
</ServiceDefinition>

Resources