Use PowerShell to Find Active Directory Forest Conflict Objects

Quite often there's conflict amongst the PoSh progeny. Usually, the most belligerent sibling gets sent to their bedroom.

In Active Directory, when there's a sibling name conflict the Relative Distinguished Name of the losing sibling is 'mangled', i.e. you'll see 'CNF:<guid>' inserted into the name.

Now, the last time there was a rumpus in the PoSh houshold, I tried inserting '*CNF:bf8149dd-3e1f-41f5-ad6b-bc11403bc579*' into the name of the chief offender... I petrified the PoSh chickens and severely strained my vocal chords!


Anyway, here's a sweet one-liner to get all of the conflict objects in a forest.

(Get-ADForest).Domains | ForEach-Object {Get-ADObject -LDAPFilter "(|(cn=*\0ACNF:*)(ou=*CNF:*))" -Server $_}


It is possible that the 'CNF:<guid>' sibling is the one you want to keep. Say what?

I refer you to the wisdom of an esteemed colleague, Mr Ali Sajjad:

"...There are more things in heaven and earth, Horatio,
Than are dreamt of in your philosophy..."

Hang on, that wasn't Ali... here he is:

"...By definition if there are CNF objects then they are not in use in favour of the object that maintained the original name (live object) when replication conflict took place. Hence, the question is - should you keep CNF objects or remove them all?

The best way to approach this is by looking at the date when the CNF object(s) were created. There are two possibilities;

  1. Old CNF objects: If the date modified is sufficiently in the past (a week, 2 weeks, a month, etc. – this period depends on the object and how volatile it is in a given environment) then that means the live object has been in use for this period of time and may have received latest updates. In other words, if you see old CNF objects then most definitely you do not want to bring them back as they have stale data as compared to the live ones. If a CNF object is “old”, and nothing broke, no one complained, then that means the live object is indeed “good” and you do not want to risk replacing a live object with a stale CNF one.
  2. Recent CNF Objects: If on the other hand there are some CNF objects that have recently been created, then they need careful analysis to see which ones have the correct data.

Splitting the CNFs in old and recent would greatly simply the remediation process..."


In response to Ali's sage utterances, I wrote this:

#Create a datetime object for our time calculations

$DaysAgo = (Get-Date).AddDays(-10)



#Pipe each domain in our forest into a loop to test for 'old' conflict objects

(Get-ADForest).Domains | ForEach-Object {

#Get conflict objects older than our $DaysAgo value

Get-ADObject -LDAPFilter "(|(cn=*\0ACNF:*)(ou=*CNF:*))" -Server $_ -Properties WhenChanged |

Where-Object {$_.WhenChanged -lt $DaysAgo}

} #End of ForEach-Object



#Pipe each domain in our forest into a loop to test for 'recent' conflict objects

(Get-ADForest).Domains | ForEach-Object {

#Get conflict objects newer than our $DaysAgo value

Get-ADObject -LDAPFilter "(|(cn=*\0ACNF:*)(ou=*CNF:*))" -Server $_ -Properties WhenChanged |

Where-Object {$_.WhenChanged -ge $DaysAgo}

} #End of ForEach-Object

This lets you define the conflict objects as either 'old' or 'recent' based on a threshold defined by the $DaysAgo variable.


Then Ali suggested that it would be very useful to also dump out details of the corresponding 'live' objects. In Ali's words, the benefits of this would be:

  • If the Live object has been deleted / moved then the decision is easy to delete the CNF object (irrespective if it is old or new)
  • For new objects the DN and WhenChanged values would be available for comparison in the output rather than having to go query them separately 


Back to the proverbial drawing board! I wrote a function that produces a CSV report of conflict objects, their whenChanged date, the corresponding live object (if found) and its whenChanged date. The output of which looks like this:



The function can be found in the TechNet script repository:

 Get-ADForestConflictObjects Function



Comments (7)

  1. Marc Denman says:

    Great script. Is there any chance you can help with an error that I get? I wonder if the environment targeted is too large. I am getting a "timeout limit exceeded" on the get-adobject.

    Thanks in advance.

  2. anonymouscommenter says:

    A host of reference material for AD and Group Policy

  3. Marc Denman says:

    The script failed due to call depth overflow.
    + CategoryInfo : InvalidOperation: (0:Int32) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : CallDepthOverflow

  4. Hey, Marc,

    What size environment we talking about here? Are you running the function as a PS job? What vn of PS?

    How many conflict objects does the 1 liner produce or does that fail, too?



  5. Hi,

    Nice oneliner. It ignores the non-domain partitions, though.

    1. Well spotted! A colleague made the same observation a few weeks ago. I have another ‘to-do’ item now 🙂

  6. Ben van Zanten says:

    Wonderful script examples Ian, thanks for that.
    However, I found that your examples only show the CNF records for the Default naming contexts, and we also have some replication errors in the Configuration context (especially PKI OID records).
    I’ve updated your search to a little script, that first fills a hash table with all the naming contexts in the current forest and the domain where the naming context is found, then loops through each NC to find CNF records.

    (Get-ADForest).Domains | ForEach-Object { $Domain=$_ ; (Get-ADRootDSE -Server $Domain ).namingContexts } | ForEach-Object { $NCHash[$_]=$Domain }
    $NCHash.Keys | ForEach-Object {
    Get-ADObject -LDAPFilter “(|(cn=*\0ACNF:*)(ou=*CNF:*))” -SearchBase $_ -Server $NCHash[$_]

Skip to main content