IIS6 doesn’t like NewSID

Our PowerShell-based VM building framework is still in use. This is because some of the components for the new system were sent First Class instead of Next Day, and got lost in the mail. In future I’ll be sure to demand Next Day delivery on everything.

So our test server still operates (or not) at the whim of a mess of scripts. This seems to be working out OK so far, although human intervention is required when the VM registration process fails to restart properly, ie. roughly once a week. At least the terminal server I set up lets me fix it from home before I leave for work in the morning, which means the server might actually be built and working by the time I arrive.

The original test server runs Windows XP, which is pretty much the minimum requirement for our product. Recently we ran into a bug that only appeared on Windows 2003, requiring a second test server running that OS. As I mentioned back in May, we use NewSID plus yet more Powershell to deploy images since it suits our system better than Sysprep.

NewSID is a great tool. It generates a new Security ID for the machine, updates the Registry and other system files accordingly, fixes the filesystem’s Access Control Lists to use the new SID, and reboots the machine. Unfortunately, it doesn’t update the IIS Metabase.

On Windows XP this is not an issue for us. IIS versions prior to 6 use only a single application pool and our product’s installer sets up all permissions appropriately anyway. On II6, however, our app pool identity is a member of IIS_WPG, the IIS Worker Process Group, and relies on that group’s permissions. NewSID leaves the old SID in the Metabase and doesn’t add the new one, effectively nuking IIS_WPG’s permissions on all IIS directories.

IIS7 doesn’t have this problem. It uses built-in NT accounts not dependent upon the machine or domain SID, making a clean IIS7 Metabase effectively immune to this sort of corruption. Once you start adding permission sets it’s a different story, but in most cases only a clean system would be the subject of NewSID.

The conventional workaround for the issue is to remove IIS prior to running NewSID and reinstall it afterwards. This is fine for manual setup but falls short in our situation. It is by all means possible to automate the addition and removal of Windows components with the ‘sysocmgr’ command, but setting up the necessary answer files is tedious. I wanted a better way.

So I poked around in Powershell, made heavy use of Google, and wrote some exploratory C#. Through Directory Services the Metabase can be edited just like Active Directory. I wrote some code to look through all the Access Control Lists for entries with trustees in a given domain and replace the trustee with its equivalent in another domain. This was all done at the SID level; ‘equivalent’ trustees are considered to be those with the same RID, since NewSID does not change that. Trustees that mapped to known accounts are ignored, since they aren’t broken.

It took me a while to figure out why the ‘patched’ entries were being removed by the original version of the tool. It seems that the AccessControlEntry interface doesn’t like raw SIDs being put into its Trustee property. Resolving the SID to a human-readable account beforehand solved this problem.

The result is a tool called MetabaseACL, which takes a directory path as its first argument:

MetabaseACL "IIS://Localhost"

This will display all ACLs in the Metabase within the given path. To fix SIDs, it can be given the ‘fixup’ command:

MetabaseACL "IIS://Localhost" fixup <old SID> <new SID>

It doesn’t matter if the SIDs are specific NT accounts or domain/machine SIDs; only the domain info will be used.

This integrates nicely with the ‘newsid’ package our VM builder uses. The script which does the work now looks something like this:

if($env:COMPUTERNAME -eq $compName)
{
if(Test-Path "C:Machine.sid")
{
$id = New-Object System.Security.Principal.NTAccount("Administrator");
$newSid = $id.Translate([System.Security.Principal.SecurityIdentifier]).AccountDomainSid.Value;
$oldSid = Get-Content "C:Machine.sid";
MetabaseACL "IIS://Localhost" fixup $oldSid $newSid;
Remove-Item "C:Machine.sid";
}
[We're done here. Call next script in the chain.]
}
else
{
net stop iisadmin /y ;
if(-not (Test-Path HKCU:/SOFTWARE/Sysinternals))
{
New-Item HKCU:/SOFTWARE/Sysinternals
}
# Record old Machine SID.
$id = New-Object System.Security.Principal.NTAccount("Administrator");
$sid = $id.Translate([System.Security.Principal.SecurityIdentifier]).AccountDomainSid.Value;
Set-Content "C:Machine.sid" $sid;
# Prepare for NewSID.
$key = New-Item HKCU:/SOFTWARE/Sysinternals/NewSID;
$key.SetValue("EulaAccepted", 1);
$p = [diagnostics.process]::Start("newsid.exe", "/a ${compName}");
$p.WaitForExit();
}

This script is run when the VM image boots. First time through it will record its SID and run NewSID. On the second boot it will retrieve the old SID, get the new SID, run MetabaseACL to do the fixup, and call the next script in the image building procedure.

And so ends another adventure with VM image deployment. Judging by the number of forum topics I came across while googling for a solution to the IIS/NewSID problem, this new tool will prove useful to people besides me.

Download MetabaseACL

Zip file includes source code and binaries. Runs on WinXP and Win2003. I haven’t tried it on Vista but it should be fine.