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