4.1.18.2 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.

 ULONG
 IDL_DRSRemoveDsServer(
     [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
   return ERROR_INVALID_PARAMETER
 endif
  
 if serverDn = null or serverDn = "" then
   return ERROR_INVALID_PARAMETER
 endif
  
 /* Note that DomainDN can be null, but it cannot be empty. */
 if domainDn = "" then
   return ERROR_INVALID_PARAMETER
 endif
  
 /* Compute fLastDcInDomain if domainDn is non-null. */
 if domainDn ≠ null then
   otherNtdsdsa := select one o from subtree ConfigNC() where
     (o!objectCategory = nTDSDSA)
     and
     (domainDn in o!hasMasterNCs or domainDn in o!msDS-hasMasterNCs)
     and
     (o ≠ ntdsdsa)
   if otherNtdsdsa = null then
     pmsgOut^.V1.fLastDcInDomain = true
   else
     pmsgOut^.V1.fLastDcInDomain = false
   endif
 endif
  
 /* If nothing to commit, processing is complete. */
 if not pmsgIn^.V1.fCommit then
   return 0
 endif
  
 ntdsdsa := DescendantObject([dn: serverDn], "CN=NTDS Settings,")
 if ntdsdsa = null then
   return ERROR_DS_CANT_FIND_DSA_OBJ
 endif
  
 /* 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)
 endif
  
 /* Remove the subtree of objects rooted at the DC's ntdsDsa object.*/
  
 if not AccessCheckObject(ntdsdsa, RIGHT_DS_DELETE_TREE) then
   return ERROR_ACCESS_DENIED
 endif
  
 rt := RemoveObj(ntdsdsa,true)
 if rt ≠ 0 then
   return rt
 endif
  
 /* 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
       return ERROR_ACCESS_DENIED
     endif
  
     RemoveObj(r, false)
   endfor
  
   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}
     endif
   endfor
  /* 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
  endfor
  
   if not AccessCheckAttr(computer, servicePrincipalName, 
       RIGHT_DS_WRITE_PROPERTY) then
     return ERROR_ACCESS_DENIED
   endif
  
   computer!servicePrincipalName := 
     computer!servicePrincipalName - spnsToRemove
 endif
  
 return 0
Show: