Server Behavior of the IDL_DRSRemoveDsServer Method

Informative summary of behavior: Removes the metadata defining a DC, which consists of the tree of objects rooted at the DC's nTDSDSA object as well as the rIDSet objects and DRS SPNs associated with the DC's computer object. This method is typically used when a DC is demoted. As part of the demotion process, the DC being demoted calls this method on another DC (either in the same domain, if such a DC exists, or in the parent domain, if there are no other DCs in the same domain but there is a parent domain) to remove the metadata of the DC being demoted from the forest. Alternatively, if a DC is removed from the domain without being properly demoted (for example, if the DC suffers a fatal hardware failure), a client can make this call to remove the metadata of the now-nonexistent DC. When pmsgIn^.V1.DomainDN is specified, this method also computes whether the DC is the last replica of its default domain NC.

The behavior of this method has two variants. If pmsgIn^.V1.fCommit is false, the method is read-only with regard to abstract state; that is, it does not make any changes to the directory contents. In this mode, the main purpose of the method is to compute pmsgOut^.V1.fLastDcInDomain (and so there is little point to calling the method in this mode without setting pmsgIn^.V1.DomainDN). For example, prior to removing the DC's metadata, a client application might try to determine whether any DCs would be left in the domain, so that it can warn the user if the user is removing the last DC in the domain.

When pmsgIn^.V1.fCommit is true, the second variant of the behavior is performed. In this mode, the method actually removes the DC metadata. The pmsgOut^.V1.fLastDcInDomain value is also computed in this mode (provided that pmsgIn^.V1.DomainDN was passed in). This method undoes the effects of the IDL_DRSAddEntry method when IDL_DRSAddEntry is used to create an nTDSDSA object. The removal of the DC's metadata signals other DCs in the forest that this particular DC no longer exists.

     [in, ref] DRS_HANDLE hDrs,
     [in] DWORD dwInVersion,
     [in, ref, switch_is(dwInVersion)]
         DRS_MSG_RMSVRREQ *pmsgIn,
     [out, ref] DWORD *pdwOutVersion,
     [out, ref, switch_is(*pdwOutVersion)]
         DRS_MSG_RMSVRREPLY *pmsgOut);
 serverDn: unicodestring
 domainDn: unicodestring
 server: DSName
 ntdsdsa: DSName
 otherNtdsdsa: DSName
 spnsToRemove: set of unicodestring
 computerDn: unicodestring
 computer: DSName
 objectsToDelete: set of DSName
 rt: ULONG 
 RODCKrbtgtAcct: DSName
 accountList: set of DSName
 ValidateDRSInput(hDrs, 14)
 serverDn := pmsgIn^.V1.ServerDN
 domainDn := pmsgIn^.V1.DomainDN
 pdwOutVersion^ := 1
 pmsgOut^.V1.fLastDcInDomain = false
 /* Basic parameter validation */
 if dwInVersion ≠ 1 then
 if serverDn = null or serverDn = "" then
 /* Note that DomainDN can be null, but it cannot be empty. */
 if domainDn = "" then
 /* Compute fLastDcInDomain if domainDn is non-null. */
 if domainDn ≠ null then
   otherNtdsdsa := select one o from subtree ConfigNC() where
     (o!objectCategory = nTDSDSA)
     (domainDn in o!hasMasterNCs or domainDn in o!msDS-hasMasterNCs)
     (o ≠ ntdsdsa)
   if otherNtdsdsa = null then
     pmsgOut^.V1.fLastDcInDomain = true
     pmsgOut^.V1.fLastDcInDomain = false
 /* If nothing to commit, processing is complete. */
 if not pmsgIn^.V1.fCommit then
   return 0
 ntdsdsa := DescendantObject([dn: serverDn], "CN=NTDS Settings,")
 if ntdsdsa = null then
 /* Perform the actual DC metadata removal. */
 /* Locate the computer object for the DC's account. */
 server := ntdsdsa!parent
 computerDn := server!serverReference
 computer := null
 if computerDn ≠ null then
   computer := GetDSNameFromDN(computerDn)
 /* Remove the subtree of objects rooted at the DC's ntdsDsa object.*/
 if not AccessCheckObject(ntdsdsa, RIGHT_DS_DELETE_TREE) then
 rt := RemoveObj(ntdsdsa,true)
 if rt ≠ 0 then
   return rt
 /* If the DC's computer account exists, remove rIDSet objects and 
  * remove the DRS SPNs from the computer object. */
 if computer ≠ null then
   foreach r in computer!rIDSetReferences
     if (not AccessCheckObject(r, RIGHT_DELETE)) and
        (not AccessCheckObject(r.parent, RIGHT_DS_DELETE_CHILD)) then
     RemoveObj(r, false)
   foreach spn in computer!servicePrincipalName  
     if StartsWith(spn, "ldap/") or
        StartsWith(spn, "GC/") or
        StartsWith(spn, "E3514235-4B06-11D1-AB04-00C04FC2DCD2/") or
        StartsWith(spn, "RPC/") then
       spnsToRemove := spnsToRemove + {spn}
  /* Cleanup for read-only domain controllers */
  /* Clear the KrbTgtLink from computer and delete its object */
  /* Get the msDS-KrbTgtLink attribute from the object */
  RODCKrbtgtAcct := computer!msDS-KrbTgtLink
  /* Delet the attribute from the object */
  Computer!msDS-KrbTgtLink := null
  /* Remove the KrbTgtLink */
  RemoveObj(RODCKrbTgtLink, false)
  /* Delete RODC policies */
  computer!msDS-NeverRevealGroup := null
  computer!msDS-RevealOnDemandGroup := null
  computer!msDS-RevealedUsers := null
  /* Delete msDS-AuthenticatedToAccountList links */
  accountList := { computer!msDS-AuthenticatedToAccountList }
  foreach entry in accountList
    entry!msDS-AuthenticatedAtDC := entry!msDS-AuthenticatedAtDC – computer
   if not AccessCheckAttr(computer, servicePrincipalName, 
   computer!servicePrincipalName := 
     computer!servicePrincipalName - spnsToRemove
 return 0