Backing up and Restoring Domain-Based DFS Namespaces

I had a question for a customer recently which needed some investigation, as the seemingly “easy steps” to export and import DFSN configurations didn’t do what either of us expected.

KB969382 lists the actions to take in the event of your DFS Namespace going west. Option 2 was the one we were looking at as we wanted to create regular DFS-N backups to be used in any DFS-N related emergency.

It seemed simple enough, run this command to backup your configuration:

dfsutil root export \domain.nameDFSN DFSN-root.txt

And when disaster strikes, just run this command to put it all back again:

dfsutil root import set DFSN-Root.txt \domain.nameDFSN

However, no matter what the DFS-N emergency we created in the lab, the import would always fail citing “element not found”.

The problem was that we were breaking the DFS-N root (on purpose), but the export/import scenario requires you to have a working DFS-N root. And to get that, you’d need good system-state backups of both a DC and a DFS Namespace server. Which isn’t going to provide for a fast, efficient restore scenario in a large organisation.

So I started experimenting, and it seems that the objects in AD are easily copied and imported again using ldifde – there is no attachment to the object GUIDs (like there is say in a failover cluster). And once all the objects are back in AD, all the links and targets start working again as expected.

The same applies to the share and DFSN root information in the registry – a simple ‘reg save’ followed by a ‘reg restore’ will get that information back with the registry ACLs in tact.

So, I wrote 2 scripts (each fires of a second script to run directly on the DFS Namespace servers):

  1. The scheduled backup script deployed as a scheduled task to a central management server
  2. A restore script to be used in the case of any DFS-N emergency

Now, while the restore could be more targeted to allow you to chose the scenario to recover from (e.g. restore ONLY the objects in AD or DFS-N registry information on just 1 DFS-N server, or only one DFS-N root), I’ll leave it to you good reader to add that intelligence. This restore script restore the entire DFS-N configuration for all roots and to all DFS-N servers.

This will backup and restore both Windows 2000/2003 roots and Windows Server 2008 roots. It uses psexec from Sysinternals, available here. The reason it does this is to use reg save/reg restore, which capture ACLs on the registry keys and restores exactly the configuration which was backed up, rather than merging the configuration. While my testing shows that these reg keys do not have explicit permissions defined, you’re better safe than sorry.

Make sure and change any instances of “dc=domain,DC=name” and “\domain.name” to the domain name in your environment.

Backup

Main Job

rem Setup input file

if not exist .backupfiles mkdir .backup-files if exist root-servers.txt del root-servers.txt

setlocal

if exist servers.txt del servers.txt dsquery * "CN=DFS-Configuration,CN=System,DC=domain,dc=name" -filter "(|(objectClass=fTDfs)(objectClass=msDFS-Namespacev2))" -attr name > allRoots.txt for /F "tokens=1-3 skip=1 delims= " %%i IN (allRoots.txt) DO ( dfsutil root \domain.name%%i | find /i "target" | find /i "%%i" >> %%i-serversRAW.txt for /F "tokens=2 delims=" %%u IN (%%i-serversRAW.txt) do echo %%i;%%u >> root-servers.txt del %%i-serversRAW.txt )

for /F "tokens=1,2 delims=; " %%i IN (root-servers.txt) DO echo %%j>> serversRAW.txt sort serversRAW.txt /O serversSORTED.txt for /F "Tokens=*" %%s in ('type serversSORTED.txt') do set record=%%s&call :output

del serversRAW.txt del serversSORTED.txt

endlocal  

rem Backup

for /F %%i IN (servers.txt) DO ( if not exist \%%ic$temp mkdir \c$temp copy .NSserverBackup.bat \%%ic$temp /Y psexec \%%i C:tempNSserverBackup.bat copy \%%ic$TEMP%%i-dfsroots.hiv .backup-files%%i-dfsroots.hiv /Y copy \%%ic$TEMP%%i-CCS-shares.hiv .backup-files%%i-CCS-shares.hiv /Y copy \%%ic$TEMP%%i-CS1-shares.hiv .backup-files%%i-CS1-shares.hiv /Y )

ldifde -f .backup-filesdfs-export.ldf -v -d "CN=Dfs-Configuration,CN=System,DC=domain,dc=name" -l objectClass,remoteServerName,pKTGuid,pKT,msDFS-SchemaMajorVersion,msDFS-SchemaMinorVersion,msDFS-GenerationGUIDv2,msDFS-NamespaceIdentityGUIDv2,msDFS-LastModifiedv2,msDFS-Propertiesv2,msDFS-TargetListv2,msDFS-Ttlv2,msDFS-LinkPathv2,msDFS-LinkSecurityDescriptorv2,msDFS-Ttlv2,msDFS-Commentv2,msDFS-ShortNameLinkPathv2,msDFS-LinkIdentityGUIDv2 > .backup-filesldf-export.log

goto :EOF

:output if not defined previous_record goto write if "%record%" EQU "%previous_record%" goto :EOF

:write @echo %record%>>servers.txt set previous_record=%record%

NSserverBackup.bat

C: cd cd temp

reg save HKLMSoftwareMicrosoftWindowsDFSRoots C:TEMP%COMPUTERNAME%-dfsroots.hiv /y reg save HKLMSystemCurrentControlSetServiceslanmanservershares C:temp%COMPUTERNAME%-CCS-shares.hiv /y reg save HKLMSystemControlSet001Serviceslanmanservershares C:temp%COMPUTERNAME%-CS1-shares.hiv /y

The main backup job copies NSserverBackup.bat to each Namespace server and runs it from there.

 

Restore

Main Job

rem Check input files

if not exist allRoots.txt goto :EOF if not exist servers.txt goto :EOF

rem clean up before restore

dsquery * "CN=DFS-Configuration,CN=System,DC=DC=domain,dc=name" -filter "(|(objectClass=fTDfs)(objectClass=msDFS-NamespaceAnchor))" | dsrm -q -subtree -noprompt for /F %%i IN (servers.txt) DO ( reg delete \%%iHKLMSoftwareMicrosoftWindowsDFSRoots /f reg delete \%%iHKLMSystemCurrentControlSetServiceslanmanservershares /f reg delete \%%iHKLMSystemControlSet001Serviceslanmanservershares /f reg add \%%iHKLMSoftwareMicrosoftWindowsDFSRoots /f reg add \%%iHKLMSystemCurrentControlSetServiceslanmanservershares /f reg add \%%iHKLMSystemControlSet001Serviceslanmanservershares /f )

rem restore

ldifde -I -f .backup-filesdfs-export.ldf -k -v > .backup-filesdfs-import.log for /F %%i IN (servers.txt) DO ( copy .backup-files%%i-dfsroots.hiv \%%ic$temp%%i-dfsroots.hiv /Y copy .backup-files%%i-CCS-shares.hiv \%%ic$temp%%i-CCS-shares.hiv /Y copy .backup-files%%i-CS1-shares.hiv \%%ic$temp%%i-CS1-shares.hiv /Y copy .NSserverRestore.bat \%%ic$tempNSserverRestore.bat /Y copy .allRoots.txt \%%ic$tempallRoots.txt /Y ) psexec @servers.txt C:tempNSserverRestore.bat

NSserverRestore.bat

reg restore HKLMSoftwareMicrosoftDFSRoots C:temp%COMPUTERNAME%-dfsroots.hiv reg restore HLLMSystemCurrentControlSetServiceslanmanservershares C:temp%COMPUTERNAME-CSS-shares.hiv reg restore HLLMSystemControlSet001Serviceslanmanservershares C:temp%COMPUTERNAME-CS1-shares.hiv for /F "tokens=1-3 skip=1 delims= " %%i IN (allRoots.txt) DO dfsutil root forcesync \domain.name%%i net stop dfs && net start dfs

The main backup job copies NSserverRestore.bat to each Namespace server and runs it from there.