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:\.rootcimv2")
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.