Here is the sample VB code to migrate FRS configuration to DFSRadmin commands (from ReadFRS.vbs mentioned in the webcast). The script migrates settings from FRS to DFSR - a one-to-one mapping between FRS replica sets and DFS Replication replication groups. All memberships are disabled. After verifying migration, you will need to stop FRS, enable DFS Replication memberships, and set one member for each replicated folder as the primary using DFSRadmin.  After you complete migration and determine that DFS Replication has completed initial sync and is in healthy condition, you will need to delete the FRS objects using ADSI edit. A diagram of FRS objects in Active Directory is located in the FRS Technical Reference. You'll need to scroll to the section titled "Hierarchy of FRS Objects in Active Directory for DFS Replica Sets."

We also recommend that you review our recent blog post on staging because DFS Replication does not allow you to set a common staging path. If you were doing this for multiple replica sets in FRS, you will need to do something like what is described in the staging blog.

Many thanks to Robert Powalka and Ram Natarajan for creating this sample code!

Update 3-6
The original sample code we posted contained several differences than the code shown in the migration webcast. We've updated the sample code below to more closely match the one in the webcast. You might also need to do some manual reformatting after copying/pasting this sample code.

 

 

option explicit

const VER="v1.0 16/12/2005"
const APP="readFRS"

const DFSRADMIN=" "     'change to empty string if you want to use DFSRADMIN BULK

dim oDomain
dim colForestDomains
dim colForestDomainDns
dim szDomainNC
dim szDomainNETBIOSname       'finally not needed - set just in case
dim szDomainRootDns
dim szDfsRoot
dim szDfsLink
dim szRGname
dim szRFname
dim szDfsPublishedPath


'do the work!
main

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

sub main

    Init
    ReadContentSetConfig
end sub 'main


sub Init
On Error Resume Next

dim objArgs
dim szConfigurationNC
dim oPartitionsContainer
dim oCrossRef

    Set objArgs = WScript.Arguments
    If objArgs.Count < 2 Then            'must get the name of the content set - RG to be
        wscript.echo "usage: " & APP & " DfsRoot DfsLink"
        WScript.Quit   'quit program execution
    end if
    szDfsRoot = objArgs(0)  'DfsRoot to become RG
    szDfsLink = objArgs(1)  'DfsLink to become RF
   
    Set oDomain = GetObject("LDAP://RootDSE")
    If Err <> 0 Then
        wscript.echo "cannot connect to the domain - error", Err.Number, Err.Description
        WScript.Quit   'quit program execution
    end if
    szDomainNC = oDomain.Get("defaultNamingContext")

    set colForestDomains = CreateObject("Scripting.Dictionary")
    set colForestDomainDns = CreateObject("Scripting.Dictionary")
    'find and store all NetBIOS domain names within the forest
    szConfigurationNC = oDomain.Get("configurationNamingContext")
    Set oPartitionsContainer = GetObject("LDAP://CN=Partitions," & szConfigurationNC)
    ' Set the filter so that only crossRef objects are returned
    oPartitionsContainer.Filter = Array("crossRef")
    'find the right crossRef object and extract NetBIOS domain name - will be quick - we've got one domain
    ' we also extract the domain dns name
    For Each oCrossRef in oPartitionsContainer
        If oCrossRef.SystemFlags = 3 Then
            colForestDomains.add oCrossRef.nCName, oCrossRef.nETBIOSName
            colForestDomainDns.add oCrossRef.nCName, oCrossRef.dnsRoot
            if oCrossRef.nCName = szDomainNC then
                szDomainNETBIOSname = oCrossRef.nETBIOSName
                szDomainRootDns     = oCrossRef.dnsRoot
            end if
        End If
    Next

end sub 'Init


sub ReadContentSetConfig
On Error Resume Next

const DFSVolumes="CN=DFS Volumes,CN=File Replication Service,CN=System"
dim oRG
dim oRF

    Set oRG = GetObject("LDAP://cn=" & szDfsRoot & "," & DFSVolumes & "," & szDomainNC)
    if Err <> 0 then
        wscript.echo "cannot read information from AD on the DFS Root object", szDfsRoot
        wscript.Quit
    end if

    set oRF = GetObject("LDAP://cn=" & szDfsRoot & "|" & szDfsLink & ",cn=" & szDfsRoot & "," & DFSVolumes & "," & szDomainNC)
    if Err <> 0 then
        wscript.echo "cannot read information from AD on the DFS Link object", szDfsLink
        wscript.Quit
    end if

    szRFname = mid(oRF.cn, instr(oRF.cn,"|")+1)     'convention, plus get the original spelling
    szRGname = oRG.cn & "_" & szRFname              'convention, also - get the original spelling

    wscript.echo DFSRADMIN & "RG New /RgName:""" & szRGname & """"
    wscript.echo DFSRADMIN & "RF New /RgName:""" & szRGname & """ /RfName:""" & szRFname & """"
    szDfsPublishedPath = "\\" & szDomainRootDns & "\" & oRG.cn & "\" & szRFname
    wscript.echo DFSRADMIN & "RF Set /RgName:""" & szRGname & """ /RfName:""" & szRFname & _
        """ /RfDfsPath:""" & szDfsPublishedPath & """ /Force"

On Error Goto 0
    ProcessReplicatedFolder(oRF)

end sub 'ReadContentSetConfig

' Get the DFS Target Share for the given link on
' the given server using WMI
Function GetShareNameForLink(szLinkName, szServer)
   
    Dim objWmiService
    Dim szQueryString
    Dim objResultSet
    Dim objTarget

    Set objWmiService = GetObject("winmgmts:\\.\root\cimv2")
    szQueryString = "Select * From Win32_DfsTarget Where LinkName Like '%\\" & _
                    szDfsRoot & "\\" & szLinkName & "'" & _
                    " And ServerName Like'" & szServer & "%'"

    Set objResultSet = objWmiService.ExecQuery(szQueryString)

    If ( IsNull(objResultSet) Or objResultSet.Count = 0 ) Then
        WScript.StdErr.WriteLine "Error getting target share path for " & szLinkName & " on " & szServer
        GetShareNameForLink = ""
        Exit Function
    End If
   
    For Each objTarget In objResultSet
        GetShareNameForLink = objTarget.ShareName
        Exit Function
    Next
   
End Function


sub ProcessReplicatedFolder(obj)

dim colRgMembers
dim colRfMembers
dim oMbr
dim szMbr
dim oConn
dim oComputer
dim strConn
dim szFileFilter, szDirFilter
dim oSubscriber
dim szComputerDomainFQDN
dim szDfsFolderPath
dim szShareName
dim blPrimarySet

    blPrimarySet = False
    if not isempty(obj.fRSFileFilter) then
        szFileFilter = obj.fRSFileFilter
        wscript.echo DFSRADMIN & "RF Set /RgName:""" & szRGname & """ /RfName:""" & szRFname & _
            """ /RfFileFilter:""" & szFileFilter & """"
    end if
    if not isempty(obj.fRSDirectoryFilter) then
        szDirFilter = obj.fRSDirectoryFilter
        wscript.echo DFSRADMIN & "RF Set /RgName:""" & szRGname & """ /RfName:""" & szRFname & _
            """ /RfDirFilter""" & szDirFilter & """"
    end if

    set colRgMembers = CreateObject("Scripting.Dictionary")
    set colRfMembers = CreateObject("Scripting.Dictionary")

    obj.Filter = Array("nTFRSMember")
    for each oMbr in obj
        colRfMembers.Add oMbr.distinguishedName, oMbr.fRSComputerReference

        szComputerDomainFQDN = mid(oMbr.fRSComputerReference,instr(oMbr.fRSComputerReference,"DC="))
        set oComputer = getObject("LDAP://" & oMbr.fRSComputerReference)
        colRgMembers.Add oMbr.fRSComputerReference, colForestDomains.Item(szComputerDomainFQDN) & "\" & oComputer.cn
        wscript.echo DFSRADMIN & "Mem New /RgName:""" & szRGname & """ /MemName:""" & colRgMembers.Item(oMbr.fRSComputerReference) & """ /Force"

        set oSubscriber = getObject("LDAP://" & oMbr.fRSmemberReferenceBL)
        szShareName = GetShareNameForLink(szRFname, oComputer.cn)
        szDfsFolderPath = "\\" & oComputer.cn & "." & colForestDomainDns.Item(szComputerDomainFQDN) & "\" & szShareName
  
            wscript.echo DFSRADMIN & "Membership Set /RgName:""" & szRGname & """ /RfName:""" & szRFname & _
                """ /MemName:""" & colRgMembers.Item(oMbr.fRSComputerReference) & """" & _
                " /LocalPath:""" & oSubscriber.fRSRootPath & """"  & _
                " /MembershipDFSFolder:""" & szDfsFolderPath & _
                """ /MembershipEnabled:False /DisableDirectoryVerification  /Force"
          
    next

    for each szMbr in colRfMembers.Keys
        set oMbr = GetObject("LDAP://" & szMbr)
        oMbr.Filter = Array("nTDSConnection")
        for each oConn in oMbr
            strConn = " /SendMem:" & colRgMembers.Item(colRfMembers.Item(oConn.fromServer)) & _
                      " /RecvMem:" & colRgMembers.Item(colRfMembers.Item(oMbr.distinguishedName))
            wscript.echo DFSRADMIN & "Conn New /RgName:""" & szRGname & """" & strConn
            wscript.echo DFSRADMIN & "Conn Set /RgName:""" & szRGname & """" & strConn & " /ConnEnabled:true"

            if not isempty(oConn.schedule) then
                DumpSchedule oConn.schedule, strConn
            end if
        next
    next

    'need to set the primary replica as well - obj.fRSPrimarymember points out to it, it has to be devolved using LUTs

end sub 'ProcessReplicatedFolder


sub DumpSchedule(binSchedule,strConnection)

const STARTIDX = 21   'blob start + 1, since midb counts string position from 1
dim aDaysOfWeek
dim d, h, n
dim strSched
dim aSched(187)

    aDaysOfWeek = array("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
    'schedule format determined by analysing the blob - might be wrong
    'we only run if the schedule blob is 188 bytes - otherwise no clue how to intepret it
    if (ubound(binSchedule) = 187) then

        'remember - the schedule is in UTC!

        for n = STARTIDX to 188      'copy to true array - for convenience - vbs is not very good with octet string data
            aSched(n-STARTIDX) = cint(ascb(midb(binSchedule,n,1)))
        next

        for d = 0 to 6
            if aSched(d * 24) = 0 then
                strSched = "0000"
            else
                strSched = "ffff"
            end if
            for h = 1 to 23
                if aSched(d * 24 + h) = 0 then
                    strSched = strSched & ",0000"
                else
                    strSched = strSched & ",ffff"
                end if
            next
            wscript.echo DFSRADMIN & " Conn Set Schedule Custom /RgName:""" & szRGname & """ " & strConnection & _
                " /Day:" & aDaysOfWeek(d) & " /Schedule:" & strSched
        next
        wscript.echo DFSRADMIN & " Conn Set /RgName:""" & szRGname & """ " & strConnection & _
            " /IsScheduleInLocalTime:False"
    end if
end sub 'DumpSchedule