WebDeploy Tutorial - Debugging Delegation Rules for Web Deploy

I recently had a shared hosting provider customer contact me about a problem with non-administrators deploying using Web Deploy. The specific issue he was running into was that the deployment would work if the user deployed to a sub-application (e.g. mywebsite/myapp) but not if he deployed to the root of the site (e.g. mywebsite). I want to walk through how to debug issues with Web Deploy delegation rules.

First things first โ€“ let's take some steps to make debugging easier. Read here about how to enable Web Management Service (wmsvc) tracing โ€“ you can read about IIS tracing in general here. Don't forget to restart wmsvc once you enable tracing! This will start recording all sorts of useful information every time a request comes to wmsvc.

Once the customer did that, I looked at his tracing logs files. I generally search for the names of common Web Deploy providers, such as createApp, setAcl and recycleApp, that require non-standard permissions. Sure enough, I found the culprit:

<EventData>
 
<Data Name="ContextId">{00000000-0000-0000-517F-0080020000F6}</Data>
 
<Data Name="Uri">/msdeploy.axd</Data>
 
<Data Name="eventData">Tracing deployment agent exception. Request ID &apos;&apos;. Request Timestamp: &apos;09/23/2010 18:47:04&apos;. Error Details:
System.UnauthorizedAccessException: Attempted to perform an unauthorized operation.
at System.Security.AccessControl.Win32.SetSecurityInfo(ResourceType type, String name, SafeHandle handle, SecurityInfos securityInformation, SecurityIdentifier owner, SecurityIdentifier group, GenericAcl sacl, GenericAcl dacl)
   at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, SafeHandle handle, AccessControlSections includeSections, Object exceptionContext)
   at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, AccessControlSections includeSections, Object exceptionContext)
   at Microsoft.Web.Deployment.FileSystemSecurityEx.Persist(String path)
   at Microsoft.Web.Deployment.SetAclProvider.Add(DeploymentObject source, Boolean whatIf)
   at Microsoft.Web.Deployment.DeploymentObject.Update(DeploymentObject source, DeploymentSyncContext syncContext)
   at Microsoft.Web.Deployment.DeploymentSyncContext.HandleUpdate(DeploymentObject destObject, DeploymentObject sourceObject)
   at Microsoft.Web.Deployment.DeploymentSyncContext.SyncChildrenOrder(DeploymentObject dest, DeploymentObject source)
   at Microsoft.Web.Deployment.DeploymentSyncContext.ProcessSync(DeploymentObject destinationObject, DeploymentObject sourceObject)
   at System.Security.AccessControl.Win32.SetSecurityInfo(ResourceType type, String name, SafeHandle handle, SecurityInfos securityInformation, SecurityIdentifier owner, SecurityIdentifier group, GenericAcl sacl, GenericAcl dacl)
   at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, SafeHandle handle, AccessControlSections includeSections, Object exceptionContext)
   at System.Security.AccessControl.NativeObjectSecurity.Persist(String name, AccessControlSections includeSections, Object exceptionContext)
   at Microsoft.Web.Deployment.FileSystemSecurityEx.Persist(String path)
   at Microsoft.Web.Deployment.SetAclProvider.Add(DeploymentObject source, Boolean whatIf)
   at Microsoft.Web.Deployment.DeploymentObject.Update(DeploymentObject source, DeploymentSyncContext syncContext)
   at Microsoft.Web.Deployment.DeploymentSyncContext.HandleUpdate(DeploymentObject destObject, DeploymentObject sourceObject)
   at Microsoft.Web.Deployment.DeploymentSyncContext.SyncChildrenOrder(DeploymentObject dest, DeploymentObject source)
   at Microsoft.Web.Deployment.DeploymentSyncContext.ProcessSync(DeploymentObject destinationObject, DeploymentObject sourceObject)
   at Microsoft.Web.Deployment.DeploymentObject.SyncToInternal(DeploymentObject destObject, DeploymentSyncOptions syncOptions, PayloadTable payloadTable, ContentRootTable contentRootTable)
   at Microsoft.Web.Deployment.DeploymentAgent.HandleSync(DeploymentAgentWorkerRequest workerRequest)
</Data>
 
</EventData>   at Microsoft.Web.Deployment.DeploymentObject.SyncToInternal(DeploymentObject destObject, DeploymentSyncOptions syncOptions, PayloadTable payloadTable, ContentRootTable contentRootTable)
   at Microsoft.Web.Deployment.DeploymentAgent.HandleSync(DeploymentAgentWorkerRequest workerRequest)
</Data>
 
</EventData>

Notice that one of the lines mentions the SetAclProvider. This told me that Web Deploy was failing to set file system permissions. I then asked him for the output of icacls.exe on the folder where the website's files are stored. I saw something like this:

C:UsersAdministrator>icacls C:hostingspacesfoobarfoobar.comwwwroot

C:hostingspacesfoobarfoobar.comwwwroot NT AUTHORITYSYSTEM:(OI)(CI)
BUILTINAdministrators:(OI)
machinesomeuser:(OI)(CI)(R)
machinesomeuser:(OI)(CI)(M)
machinesomeuser:(OI)(CI)(RX)

Successfully processed 1 files; Failed processing 0 files

From this, I can tell that the customer hasn't given the local user account (someuser) Full Control over the wwwroot directory. SetAcl is failing because Modify permissions are not enough to change the permissions on a directory, they're just enough to set permissions on child objects (such as sub-folders and files underneath the directory). Giving this user Full Control fixed the problem!

I hope this blog post helps you debug Web Deploy delegated deployments.

Next Recommended Reading Deploy WebMatrix in Shared Hosting