Migrating FPSE Roles Between Servers

I ran into one of those strange situations the other day where you feel like you've been doing the same thing on your computer at some time in the distant past - kind of like déjà vu for geeks. In this specific case, I was moving some web sites that I am hosting for other people that still use FPSE from an older physical server to a new virtual server that is hosted through Hyper-V. (I'm also trying to convert them all to WebDAV, but that's another story.)

Anyway, I had dozens of custom FPSE roles set up for each of those sites that I didn't want to manually replicate on the new server. Unfortunately, FPSE doesn't have a way to migrate the roles from one server to another. All of those FPSE-related roles are kept in local groups with cryptic names like OWS_nnnnn_xxxxx, so I started thinking, "If only I could write a script that could migrate the OWS_nnnnn_xxxxx groups between the two servers... "

Then it dawned on me - I had written such a script several years ago! (Now if only I could find it...) Like many people that write code, I'm something of a code packrat - I tend to keep all of my old code around somewhere, just in case. Sparing you the details of my long search, I eventually found the script that I was looking for, and I thought that it would make a nice blog because I'm sure that someone else may need to migrate their FPSE roles.

Here's the script and a brief description of what script it will do:

  • Create ADSI objects for the source and destination servers.
  • Loop through the ADSI objects and only looks for groups.
    • Note: You could also use an object.Filter statement for this.
    • You will obviously need to update the server names for your source and destination servers.
  • Compare each group name with the group stub and only process those groups that match the stub.
    • By way of explanation, FPSE role groups have names like OWS_nnnnn_xxxxx, where nnnnn is a simple numeric hash that identifies the site, and the xxxxx denotes the individual FPSE role like admin, browser, author, etc.
    • The script uses the OWS_nnnnn stub and will copy all of the role groups that it finds. For example: OWS_12345_admin, OWS_12345_author, OWS_12345_browser, etc.
    • In the code I used OWS_nnnnn for the stub, so you would have to replace nnnnn with the numbers that you see in the list of groups using Computer Management on the source computer.
  • Determine if the group exists on the destination server, and creates it if it doesn't already exist.
  • Loop through the list of users in the group on the source server and adds those same users to the group on the destination server.
    • Note: This will fail for any local users that were used on the source server that do not exist on the destination server. I only used domain accounts, so I didn't run into that problem.

Some additional notes:

  • The person that runs this code must be an administrator on each server.
  • The two computers must be on the same network or the ADSI calls will fail.

With that in mind, here's the script code:


 Option Explicit
On Error Resume Next

Dim objComputer1, objGroup1
Dim objComputer2, objGroup2

Dim objOBJECT
Dim objUSER
Dim strGroupName
Dim strGroupDesc

Const ERROR_SUCCESS = 0

' Note: update the following server names.
Const strComputer1 = "SERVER1"
Const strComputer2 = "SERVER2"

' Note: update the following group stub.
Const strGroupStub = "OWS_nnnnn"

' ----------------------------------------------------------------------

Set objComputer1 = GetObject("WinNT://" & strComputer1 & ",computer")
If CLng(Err.Number) <> ERROR_SUCCESS Then HandleError
Set objComputer2 = GetObject("WinNT://" & strComputer2 & ",computer")
If CLng(Err.Number) <> ERROR_SUCCESS Then HandleError

For Each objOBJECT in objComputer1
  If UCase(objOBJECT.Class) = "GROUP" Then
    strGroupName = objOBJECT.Name

    If UCase(Left(strGroupName,Len(strGroupStub))) = UCase(strGroupStub) Then

      Err.Clear : Set objGroup1 = GetObject("WinNT://" & strComputer1 & "/" & strGroupName & ",group")
      If CLng(Err.Number) <> ERROR_SUCCESS Then HandleError

      strGroupDesc = objGroup1.Description
      WScript.Echo "Copying " & strGroupName & "..."

      Err.Clear : Set objGroup2 = GetObject("WinNT://" & strComputer2 & "/" & strGroupName & ",group")
      
      If CLng(Err.Number) <> ERROR_SUCCESS Then
        If CLng(Err.Number) <> -2147022676 Then
          HandleError
        Else
          Err.Clear : Set objGroup2 = objComputer2.Create("group",strGroupName)
          If CLng(Err.Number) <> ERROR_SUCCESS Then HandleError

          Err.Clear : objGroup2.SetInfo
          If CLng(Err.Number) <> ERROR_SUCCESS Then HandleError

          Err.Clear : objGroup2.Description = strGroupDesc
          If CLng(Err.Number) <> ERROR_SUCCESS Then HandleError

          Err.Clear : objGroup2.SetInfo
          If CLng(Err.Number) <> ERROR_SUCCESS Then HandleError
        End If
      End If

      For Each objUSER in objGroup1.Members
        WScript.Echo vbTab & "Adding " & objUSER.Name
        Err.Clear : objGroup2.Add objUSER.ADsPath 
        If CLng(Err.Number) <> ERROR_SUCCESS And CLng(Err.Number) <> -2147023518 Then HandleError
        Err.Clear : objGroup2.SetInfo
        If CLng(Err.Number) <> ERROR_SUCCESS Then HandleError
      Next

      Set objGroup1 = Nothing
      Set objGroup2 = Nothing
      
    End If
  End If
Next

' ----------------------------------------------------------------------

Sub HandleError()
  WScript.Echo vbCrLf & "FATAL ERROR:"
  WScript.Echo vbTab & "Number: " & CLng(Err.Number) & " (0x" & Hex(CLng(Err.Number)) & ")"
  WScript.Echo vbTab & "Description: " & Err.Description
  WScript.Quit
End Sub

As usual, all of the normal caveats like "this code is totally unsupported" will apply, but I've used this code with great success on several severs over the years. The great thing about this code is that it's non-destructive because it doesn't delete anything on the source server - it only creates groups on the destination server.

You can also use Const strGroupStub = "OWS_" to migrate all FPSE role groups from one server to another, but that's a major operation. With that in mind, I'd try it out on a single FPSE site using "OWS_nnnnn" as a stub before trying out a full server by using "OWS_ " as a stub; the latter is very time-consuming and CPU-intensive.