dfsrmig is the SYSVOL migration tool. It ships with the DFS Replication service and it does exactly one job: it moves replication of the SYSVOL share from the old File Replication Service (FRS) to DFS Replication (DFSR), and it reports how far that move has got.
Most people who type dfsrmig are not migrating anything. They inherited a domain, something is wrong with Group Policy or logon scripts, and they need to know which service is actually replicating SYSVOL right now. That is a read operation, it is safe, and it takes one command. Treating dfsrmig as a check command first and a migration tool second is the useful way round.
The reference page lists the parameters and stops. What it does not put together for you is that the two read commands answer different questions and will disagree with each other by design, that the tool reports ten numbered states while accepting only four of them as input, and that the output of /getmigrationstate breaks every obvious way of parsing it. This page covers all three, and the parsing is tested rather than asserted.
Applies to: Windows Server 2008 R2, 2012 R2, 2016, 2019, 2022 and 2025 domain controllers.
Quick answer
Run these from an elevated prompt on a domain controller. Together they tell you which service is replicating SYSVOL today and whether every domain controller agrees. The first one is only trustworthy on the PDC emulator, for reasons covered below.
rem the global state the domain is supposed to be in
rem run this one on the PDC emulator, see below for why it matters
dfsrmig /getglobalstate
rem what every domain controller has actually reached
rem safe to run anywhere, and this is the one that finds stragglers
dfsrmig /getmigrationstate
A domain that finished its migration years ago answers like this, and nothing else needs doing:
Current DFSR global state: Eliminated
Succeeded.
/setglobalstate, and it is covered further down with the warning it deserves.
Before the first example
Four things have to be true before any of this behaves the way the examples show. Each one is a separate check, and the fourth is the one people skip.
1. You are on a domain controller. dfsrmig.exe is installed with the DFS Replication service, which means it exists on domain controllers and on servers with the DFS Replication role service. If where comes back empty, you are on the wrong machine, not missing a download.
rem an empty result here means the DFS Replication binaries are not installed
rem it is not a PATH problem and there is nothing to add to PATH
where dfsrmig
2. The prompt is elevated. Microsoft’s own migration walkthrough says to run each dfsrmig step as an elevated domain admin. The cheapest way to confirm elevation from a batch prompt is the mandatory integrity level SID, which only appears in an elevated token.
rem S-1-16-12288 is the High Mandatory Level SID
rem it is present only when the prompt is running elevated
whoami /groups | findstr /c:"S-1-16-12288"
3. You know which domain controller holds the PDC emulator role. This matters for both read commands and it is mandatory for the write command. netdom query lists the FSMO role owners, and Microsoft documents this exact form as the way to list the PDC emulator for a domain. netdom also has to be run from an elevated prompt.
rem FSMO lists every role owner, including the PDC emulator
rem /d: targets a specific domain instead of the one you are joined to
netdom query /d:contoso.com FSMO
4. The domain functional level is Windows Server 2008 or higher. Microsoft states that dfsrmig is supported only on domain controllers running at the Windows Server domain functional level, because the FRS to DFSR migration is only possible there. On a domain still at an older level the command has nothing to do, which is a different situation from the command being missing.
# DomainMode is the functional level, not the OS version of the DCs
# this needs the RSAT Active Directory module
Get-ADDomain | Select-Object Name, DomainMode, PDCEmulator
contoso.com and the domain controller names CONTOSO-DC2 and CONTOSO-DC3 because those are the names in Microsoft’s published output captures. Every output block on this page is Microsoft’s, reproduced so the parsing claims can be checked against something real rather than something invented.
Two read commands, two different questions
/getglobalstate and /getmigrationstate look like a pair of synonyms. They are not, and the difference is the single most useful thing to understand about this tool.
| Command | What it reads | Where to run it | Answers |
|---|---|---|---|
/getglobalstate | The local copy of the AD DS database | PDC emulator only | What state the domain has been told to be in |
/getmigrationstate | The local state reported by every domain controller | Anywhere | What state every domain controller has actually reached |
Microsoft is explicit about the restriction. /getglobalstate reads the local copy of the directory, so on any domain controller other than the PDC emulator the answer is only as fresh as the last inbound replication. The documentation says you can get inconsistencies if you run it elsewhere, and marks the PDC emulator restriction as an Important note rather than a suggestion.
That is why /getmigrationstate is the command to reach for on a random domain controller at 2am. It polls, it names the machines that are behind, and it tells you at the end that the picture might be stale.
Redirected with two domain controllers still reporting Redirecting means the directive replicated and the work is in progress. Wait, then read again.
Reading the /getmigrationstate output
When every domain controller agrees, the output is three lines and there is nothing to read carefully:
All Domain Controllers have migrated successfully to Global state (Prepared).
Migration has reached a consistent state on all Domain Controllers.
Succeeded.
When they do not agree, you get a table. This is Microsoft’s published capture of that case, reproduced exactly:
The following Domain Controllers are not in sync with Global state (Prepared):
Domain Controller (Local Migration State) DC type
=========
CONTOSO-DC2 (start) ReadOnly DC
CONTOSO-DC3 (Preparing) Writable DC
Migration has not yet reached a consistent state on all domain controllers
State information might be stale due to AD latency.
Four things in those seven lines are worth pointing at, because each one breaks something a script would reasonably assume.
| What it looks like | What is actually true |
|---|---|
| A fixed-width table with a separator rule | The header is 49 characters and the separator is 9. The columns are space delimited and not aligned: the opening bracket sits at column 18 in the header and column 12 in the first data row |
| The state is a plain word | It arrives wrapped in brackets with spaces either side, so a default tokenizer hands you (start) with the brackets still attached |
| DC type is one column | It is two space separated words, ReadOnly DC and Writable DC, so it is two tokens |
| State names are capitalised | (start) is lowercase and (Preparing) is not, on adjacent rows of the same table |
There is one more difference between the two forms that is easy to use and easy to miss. In both of Microsoft’s captures the consistent output ends with Succeeded. and the inconsistent output does not. If you need a single line test for “is the domain settled”, that is the line to look for, not the presence of the word Prepared.
The migration states, and which ones you can set
There are ten numbered local migration states. /setglobalstate accepts four of them. The other six are set by the DFS Replication service while it works, and you will only ever see them in a /getmigrationstate listing.
| Value | State | Kind | When you see it |
|---|---|---|---|
| 0 | Start | Stable, settable | FRS is replicating SYSVOL and nothing has been migrated |
| 1 | Prepared | Stable, settable | DFSR replicates its own copy, FRS still serves the share |
| 2 | Redirected | Stable, settable | The DFSR copy now serves the share, FRS still replicates the old folder |
| 3 | Eliminated | Stable, settable | FRS is gone. There is no way back from here |
| 4 | Preparing | Transition | Moving from Start toward Prepared |
| 5 | Waiting for initial sync | Transition | DFSR is doing its first synchronisation of the copy |
| 6 | Redirecting | Transition | Moving from Prepared toward Redirected |
| 7 | Eliminating | Transition | Moving from Redirected toward Eliminated |
| 8 | Undo redirecting | Transition, rollback only | Rolling back from Redirected toward Prepared |
| 9 | Undo preparing | Transition, rollback only | Rolling back from Prepared toward Start |
Now look at the order those numbers actually occur in. A domain going all the way forward passes through eight of the ten values, in this sequence:
0 -> 4 -> 5 -> 1 -> 6 -> 2 -> 7 -> 3
Start Preparing Waiting Prepared Redirecting Redirected Eliminating Eliminated
Seven steps, of which four increase the number and three decrease it. The numbering is not a progress bar and sorting it gives you nothing: sorted numerically those eight values read 0, 1, 2, 3, 4, 5, 6, 7, which is a completely different journey from the real one. The two rollback states, 8 and 9, do not appear in the forward sequence at all.
Practical examples
1. Confirm which service replicates SYSVOL on a domain you inherited
The problem: You have taken over a domain with no documentation. Group Policy changes are reaching some domain controllers and not others, and before you debug replication you need to know which replication engine is even involved.
The solution: Read the global state on the PDC emulator, then confirm every domain controller has actually reached it.
rem step 1, on the PDC emulator you identified with netdom query FSMO
rem this reads the directive, not the reality
dfsrmig /getglobalstate
rem step 2, anywhere in the domain
rem this reads the reality and names any machine that disagrees
dfsrmig /getmigrationstate
A domain that completed its migration reads back as Eliminated, and that is the answer you want to see. Anything else means FRS is still involved somewhere and the domain has work outstanding.
Eliminated plus a consistent /getmigrationstate means DFSR owns SYSVOL, FRS is out of the picture, and any Group Policy distribution problem you are chasing is somewhere else. That is a useful thing to rule out in two commands.
2. Check the prerequisites before a migration, not after
The problem: The global state is Start, so the domain is still on FRS and has to move. Starting the migration into a domain that is not healthy is how a short job becomes a long one.
The solution: Microsoft’s streamlined migration guidance names four conditions to confirm before you start. All four are read operations, and these are the commands that check them. Disk space first, because the migration makes a full second copy of SYSVOL on the same volume.
rem 1. free space on the SYSVOL volume
rem the migration copies SYSVOL to a parallel SYSVOL_DFSR folder on the same volume
rem so you need at least the current SYSVOL size again, plus roughly 10 percent
dir %SystemRoot%\SYSVOL
rem 2. SYSVOL is shared and the DC is advertising
rem migration never completes on a DC that is not advertising, at any stage
dcdiag /e /test:sysvolcheck /test:advertising
rem 3. AD replication is healthy, because the whole migration rides on it
rem the * targets every domain controller in the forest
repadmin /replsummary *
The fourth condition is a Group Policy one. The built-in Administrators group needs the Manage auditing and security log user right on every domain controller. It is on by default, so a missing one means somebody removed it, and Microsoft does not support removing it. Microsoft’s guidance points at gpresult as the way in, which tells you which policies are landing on the domain controller before you go looking at the setting itself.
rem /scope computer drops the user half of the report, since user rights are computer policy
rem /r prints the summary, and /s targets a remote DC instead of this one
gpresult /scope computer /r
/e in the dcdiag line runs the tests against every domain controller in the enterprise rather than the local one, which is what makes it a single command instead of a loop. The DCDIAG article covers what each of those tests actually asserts.
3. Move one state forward and watch it converge
The problem: The checks pass and the domain needs to move from Start to Prepared. You want to be able to tell the difference between “it is still working” and “it is stuck”.
The solution: Set the state on the PDC emulator, then poll with /getmigrationstate until it stops naming machines.
rem on the PDC emulator only, elevated
rem 1 is Prepared, the first stable state after Start and fully reversible
dfsrmig /setglobalstate 1
rem confirm the directive landed in the directory
dfsrmig /getglobalstate
rem then poll this until it reports a consistent state
rem each DC has to receive the directive, do the work, and report back
dfsrmig /getmigrationstate
Convergence is not instant and it is not supposed to be. The directive has to replicate from the PDC emulator to every domain controller, each one has to do the work, and the result has to replicate back. Two things pace that: Active Directory replication, and the DFS Replication service’s polling interval. Microsoft documents that interval as five minutes, and both commands below shorten the wait. Substitute your own PDC emulator name for CONTOSO-DC1.
rem optional, to push AD replication out now instead of waiting for the schedule
rem /A all naming contexts, /P push outward from this DC,
rem /e across every site, /d name the partners by distinguished name
repadmin /syncall CONTOSO-DC1 /APed
rem optional, to make DFSR poll AD now instead of on its five minute cycle
rem Microsoft's SYSVOL guidance runs this from an elevated prompt
dfsrdiag pollad
# Get-ADDomainController -Filter * enumerates every DC in the named domain
# then each one is told to re-read its DFSR configuration from AD immediately
Get-ADDomainController -Server corp.contoso.com -Filter * |
ForEach-Object { Update-DfsrConfigurationFromAD -ComputerName $_.Name -Verbose }
4. Roll back a state you are not happy with
The problem: The domain reached Prepared or Redirected and something looks wrong. You want to put it back.
The solution: Rollback uses the same command. You set a lower stable state and the service runs the same steps in reverse, passing through the two rollback transition states on the way.
rem back to Start from Prepared, on the PDC emulator
rem the DCs pass through state 9, Undo preparing, and the SYSVOL_DFSR folder is deleted
dfsrmig /setglobalstate 0
rem watch it unwind the same way you watched it go forward
dfsrmig /getmigrationstate
Rolling back from Redirected straight to Start is allowed and passes through state 8 and then state 9. There is one thing to know about a long stay in Redirected: while the domain sits there, Group Policy edits land only in the DFSR copy, so the rollback has to resynchronise the old FRS folder from it before FRS can carry on.
Parsing the output without getting it wrong
Sooner or later somebody wants the straggler list in a monitoring check. The obvious pipeline is to filter for lines with a bracket and take the first two tokens. Every part of that is wrong, and it is wrong quietly.
Start with the filter. Running findstr /c:"(" against Microsoft’s published capture matches four lines, not two, because the banner line and the column header both contain brackets. The findstr article covers what /c: is doing here.
The following Domain Controllers are not in sync with Global state (Prepared):
Domain Controller (Local Migration State) DC type
CONTOSO-DC2 (start) ReadOnly DC
CONTOSO-DC3 (Preparing) Writable DC
Two of those four are not domain controllers. A check built on that filter reports a domain controller called The in state following, and another called Domain in state Controller, and it does it without erroring.
Now the tokenizer. These are the candidates, each run against the two real data rows:
for /f options | Result for CONTOSO-DC2 (start) ReadOnly DC | Verdict |
|---|---|---|
"tokens=1,2" | CONTOSO-DC2 and (start) | Brackets come along for the ride |
"tokens=1,2,3" | CONTOSO-DC2, (start), ReadOnly | Truncates the DC type, which is two words |
"tokens=2 delims=()" | start | Clean state, but the DC name is gone |
"tokens=1,2*" | CONTOSO-DC2 and (start) | Same bracket problem as the first row |
"tokens=1,2 delims=() " | CONTOSO-DC2 and start | Correct |
The one that works puts the brackets and the space in the delimiter set together. Pair it with a filter that keys on the end of the line instead of on brackets, and both problems go away at once:
rem capture once so the parse is not re-running the command per line
dfsrmig /getmigrationstate > "%TEMP%\dfsrmig.txt"
rem /e anchors the match to the end of the line, and every data row ends in " DC"
rem the banner, the header and the two trailing prose lines do not
rem delims=() and a space strips the brackets and splits the columns in one pass
for /f "tokens=1,2 delims=() " %a in ('findstr /e /c:" DC" "%TEMP%\dfsrmig.txt"') do @echo %a is in state %b
Against the published capture that returns exactly two lines, the two real domain controllers, with the state already unwrapped:
CONTOSO-DC2 is in state start
CONTOSO-DC3 is in state Preparing
.bat file every %a and %b above becomes %%a and %%b. The line as written is for typing at an interactive prompt, which is where most people run it the first time.
Hidden gems
The same check written in CMD and in PowerShell gives opposite answers. Look again at the capture: one domain controller is reported as (start) in lowercase and the other as (Preparing) with a capital. Now write the obvious “is anything still in the Start state” check in both shells. findstr /c: is a case sensitive literal match. PowerShell’s -match is case insensitive by default.
rem finds nothing, because the tool wrote "start" and you asked for "Start"
findstr /c:"(Start)" "%TEMP%\dfsrmig.txt"
rem finds it, because /i drops case sensitivity
findstr /i /c:"(Start)" "%TEMP%\dfsrmig.txt"
# measured in PowerShell 7.4.6 against the same capture
$cap = Get-Content "$env:TEMP\dfsrmig.txt"
[bool]($cap -match '\(Start\)') # True, -match ignores case
[bool]($cap -cmatch '\(Start\)') # False, -cmatch is the case sensitive form
Two checks, same intent, opposite verdicts, on Microsoft’s own published output. If a monitoring rule is written in one shell and its replacement in the other, that is a silent change in behaviour with nothing in the diff to show for it.
-match against an array does not populate $Matches. This one costs people an afternoon. When the left operand of -match is a collection, the operator switches from “test and capture” to “filter”, returning the matching elements. The if still fires, because a non-empty array is truthy, and then the capture group is empty.
# $cap is an array of lines, so -match FILTERS it and returns the matching lines
# $Matches is never populated, and this prints nothing at all
if ($cap -match '\((?<s>\w+)\)') { $Matches.s }
# pull one data row out first, the same way the CMD filter did
$row = $cap | Where-Object { $_ -like '* DC' } | Select-Object -First 1
# one string on the left, and -match behaves the way everyone expects
if ($row -match '\((?<s>\w+)\)') { $Matches.s } # start
For parsing the whole block at once, skip -match and use Select-String, which gives you a match object per line and keeps the named groups. Anchor the last group on the DC that ends every data row, for the same reason the batch version filtered on it:
# the last group ends in " DC" on purpose, and that is not cosmetic:
# without it the tool's own copyright banner matches and you get a
# domain controller called Copyright sitting in state c
$cap | Select-String '^(?<dc>\S+)\s+\((?<state>[^)]+)\)\s+(?<type>.+ DC)$' |
ForEach-Object {
$g = $_.Matches[0].Groups
[pscustomobject]@{ DC = $g['dc'].Value; State = $g['state'].Value; Type = $g['type'].Value }
}
DFSR copies SYSVOL with robocopy, and it keeps the log. During the move to Prepared, the DFS Replication service uses robocopy to copy the contents of the SYSVOL domain folder into the new SYSVOL_DFSR folder, and writes the output to %WINDIR%\Debug\DFSR CopyLog\*.txt. If a domain controller sticks in state 4, that log is the first place to look, and it reads like any other robocopy log because it is one.
The migration is a one way trip in a second sense. Everyone knows state 3 cannot be rolled back. The wider version is that Microsoft states the whole migration is one way: once SYSVOL is on DFS Replication, you cannot revert to FRS. The reversible part is only the journey between states 0 and 2, and only until 3 is set.
Where this matters
- You cannot add a modern domain controller to an FRS domain. Microsoft states that Windows Server 2016 is the release that ends support for NTFRS, and promoting a newer server into a domain still using FRS fails outright with a message naming DFSRMIG as the fix.
- Inheriting a domain. Two read commands tell you whether SYSVOL is on the modern engine, which is information no amount of poking at Group Policy will give you.
- Group Policy that applies inconsistently. GPO content lives in SYSVOL. If the replication engine underneath SYSVOL is mid-migration, inconsistent policy is a symptom rather than the problem.
- Audit and handover evidence.
dfsrmig /getmigrationstateproduces a per domain controller statement in one command, which is exactly the shape a handover document or an audit finding wants. - Planning a domain controller refresh. If the migration has not happened, it has to happen before the new hardware arrives, not after, because the promotion is what gets blocked.
Tips and limitations
/setglobalstatehas to run on the PDC emulator. Microsoft states plainly that if the PDC emulator is not available, the command fails.- You can run
dfsrmigon any domain controller, but the parameters that create or manipulate directory objects are refused on read-only domain controllers. An RODC can report, it cannot write. /createglobalobjects,/deleterontfrsmemberand/deleterodfsrmemberare repair parameters for read-only domain controllers, not steps in a normal migration. Microsoft’s wording is that you should not need them during a normal migration, and they exist for when the automatic cleanup stalls.- Promoting a new read-only domain controller during a migration, after
Preparedbut beforeEliminated, leaves its objects uncreated and fails the migration. That is the documented case for/createglobalobjects. - The volume that hosts SYSVOL needs room for a second copy of it. Microsoft’s streamlined guidance suggests the current SYSVOL size plus roughly ten percent.
- There is a known defect worth checking before you start on Windows Server 2019. An in-place upgrade from 2012 R2 or 2016 to 2019 does not enforce the FRS promotion block, and the upgraded domain controllers then stick in the Start phase when the migration is run, leaving SYSVOL and NETLOGON unshared. Microsoft documents this as KB4493934 together with its workarounds.
Official documentation
- dfsrmig: Windows Commands, Microsoft Learn
- Migrate SYSVOL replication from FRS to DFS Replication
- NTFRS deprecation blocks promotion of replica DCs
Related tools
- nltest Command Builder: builds the query that names the PDC emulator, which is the one machine every write in this article has to run on.
- Event Log Analyzer: the DFS Replication log is where a domain controller stuck in a transition state explains itself, and this parses the exported entries.
Related guides
- DCDIAG: Active Directory troubleshooting: the sysvolcheck and advertising tests are the prerequisite check this article calls for before any migration.
- repadmin: Active Directory replication: the migration moves only as fast as AD replication, so replsummary and syncall belong in the same session.
- nltest: domain controller and trust diagnostics: finds the PDC emulator, and tests the secure channel that all of this depends on.
- gpresult: Group Policy results: Microsoft’s named starting point for confirming the Manage auditing and security log user right the migration prerequisites require.
- findstr and find: text search in CMD: explains the /c: and /e switches the parsing section depends on, and why case sensitivity bites.
- ROBOCOPY: sync folders: the same tool DFSR uses internally to seed the SYSVOL_DFSR copy during the Prepared phase.
- Active Directory troubleshooting cheat sheet: the wider command set for domain health, with dfsrmig alongside its neighbours.