views:

1465

answers:

4

How can I stop and then restart an IIS 7 application pool from an MSBuild script running inside TeamCity. I want to deploy our nightly builds to an IIS server for out testers to view.

I have tried using appcmd like so:

appcmd stop apppool /apppool.name:MYAPP-POOL

... but I have run into elevation issues in Windows 2008 that so far have stopped me from being able to run that command from my TeamCity build process because Windows 2008 requires elevation in order to run appcmd.

If I do not stop the application pool before I copy my files to the web server my MSBuild script is unable to copy the files to the server.

Has anybody else seen and solved this issue when deploying web sites to IIS from TeamCity?

A: 

You can try changing the Build Agent Service settings to log-on as a normal user account instead of SYSTEM (the default), this can be done from the services control panel (Start | Run | services.msc).

If it doesn't help, you can also try configuring the appcmd to always run elevated, refer to this document for details.

In case such option is not available for appcmd or it still doesn't work, you can disable UAC completely for this user.

CrazyCoder
Thanks for the suggestions. appCmd is a system file so it looks like it can't be configured to always run elevated. I am running under a dedicated user account but it still fails to contact the WAS service when trying to run appcmd from my build server
Andrew Hanson
Try to disable the User Account Control for the user running the build agent. Try running the agent using .bat file instead of the service.
CrazyCoder
A: 

this is the fairly hackey workaround I ended up using:

1) Set up a limited-access account for your service to run as. Since I'm running a CruiseControl.NET service, I'll call my user 'ccnet'. He does NOT have admin rights.

2) Make a new local user account, and assign to the Administrators group (I'll call him 'iis_helper' for this example). Give him some password, and set it to never expire.

3) Change iis_helper's access permissions to NOT allow local login or remote desktop login, and anything else you might want to do to lock down this account.

4) Log in (either locally or through remote desktop) as your non-admin user, 'ccnet' in this example.

5) Open a command terminal, and use the 'runas' command to execute whatever it is that needs to be run escalated. Use the /savecred option. Specify your new administrative user.

runas /savecred /user:MYMACHINE\iis_helper "C:\Windows\System32\inetsrv\appcmd.exe"

The first time it will prompt you for 'iis_helper's password. After that, it will be stored thanks to the /savecred option (this is why we're running it once from a real command prompt, so we can enter the password once).

6) Assuming that command executed OK, you can now log out. I then logged back in as a local admin and turned off the 'ccnet' user for local interactive login, and remote desktop. The account is only used to run a service, but no real logins. This isnt a mandatory step.

7) Set up your service to run as your user account ('ccnet').

8) Configure whatever service is running (CruiseControl.NET in my case) to execute the 'runas' command instead of 'appcmd.exe' directly, the same as before:

replace:

"C:\Windows\System32\inetsrv\appcmd.exe" start site "My Super Site"

with:

runas /savecred /user:MYMACHINE\iis_helper "\"C:\Windows\System32\inetsrv\appcmd.exe\" start site \"My Super Site\""

The thing to note there is that the command should be in one set of quotes, with all the inner quotes escaped (slash-quote).

9) Test, call it a day, hit the local pub.


Edit: I apparently did #9 in the wrong order and had a few too many before testing...

This method also doesn't completely work. It does attempt to run as the administrative account, however it still runs as a non-escalated process under the administrative user, so still no admin permissions. I didn't initially catch the failure because the 'runas' command spawns a separate cmd window then closes right away, so I wasn't seeing the failure output.

Its starting to seem like the only real possibility might be writing a windows service that will run as admin, and its only purpose is to run appcmd.exe, then somehow call that service to start/stop IIS.

Isn't it great how UAC is there to secure things, but in actuality just unsecures more servers, because anything you want to do you have to do as admin, so its easier to just always run everything as admin and forget it?

rally25rs
just to elaborate one step further, the 'nice' think about using 2 accounts here is that my CruiseControl.net service is not running as admin, the build process is not running as admin, the unit tests are not running as admin, etc... only the start / stop of IIS is being run as admin, and you can't log in to the box locally or via remote desktop from either account. I find this fairly secure... and way better than disabling UAC or running the entire process as admin.
rally25rs
A: 

Here you go. You can use this from CC.NET with NAnt or just with NAnt:

http://nantcontrib.sourceforge.net/release/latest/help/tasks/iisapppool.html

+2  A: 

The msbuild community tasks includes an AppPoolController that appears to do what you want (though as noted it is dated and at present only supports IIS6.) An example:

<AppPoolController ApplicationPoolName="MyAppPool" Action="Restart" />

Note that you can also provide a username and password if necessary.

Edit: Just noticed that the MSBuild Extension Pack has an Iis7AppPool task that is probably more appropriate.

Pedro
Only suppored for IIS6 NOT for IIS7
Adam
@Adam - Ah... Hadn't seen that in the documentation. Looks like the IISVersion enum has the supported versions, which only includes 4-6 currently.
Pedro
+1 for MSBuild Extension Pack
ongle