Cleanup broken links between AD objects

I use the seeAlso and Secretary attributes to establish a relationship between a users personal employee account and any service accounts they “own”.  What I subsequently discovered is that if the service account is deleted then the “link” is not deleted and worse the SD tell me that when a user with one of these broken “link” tries to delegate access to their mailbox they get an error and are unable to delegate. delegateerror

A deleted link will show up in the CN=Deleted Objects container and be renamed as follows:

CN=<ObjectOriginalName>ADEL:<GUID>,CN=Deleted Objects,DC=ad,DC=com

Note the ADEL in the name.  I use this as an easy way to search for the deleted “linked” object in a clean up script.  I use this LDAP filter to locate all of the accounts that have the links configured,

$ownerldapFilter = “(&(employeeID=*)(sAMAccountType=805306368)(seeAlso=*))”

I get all of the user objects with this line of code where $searchroot has been initialised to the OU in AD storing the user objects I’m interested in

$Users = get-qaduser -SearchRoot $searchroot -LdapFilter $ownerldapFilter -IncludedProperties seeAlso -sizelimit 0 -Proxy

For Each user object I loop through the values in the seeAlso attribute and check if the string OADEL

if ( $serviceAccountDN.indexOf(“0ADEL:”) -gt 0 ) {

If I find it I set a flag indicating to the script that I need to update the attribute value.  If the link does not contain the string I store it in a temporary array.  Once I’ve been through all of the links I check if the update flag was set and if it was I update the attribute value with the stored values.  Anyway here’s the full code you can use as a template for your own clean up script.

#===========================================================================================================================================
$ScriptName    = “User-ManageSeeAlsoAndSecretaryAttributes”
$scriptVersion = “1.0”
$taskDN = “CN=Clear Deleted Account Links,CN=Data Integrity,CN=Scheduled Tasks,CN=Server Configuration,CN=Configuration”
#===========================================================================================================================================
#
# This scheduled task checks for broken account links – where the secretary or seeAlso attributes contain deleted objects
#
# Version 1.0 20/05/2015 Initial version
#
#region Helper Functions
function Send-Alert {
$arrstrAlertMessage = Get-Content $logfilepath
$strAlertMessage = “”
$arrstrAlertMessage | % { $strAlertMessage += “$_   `n” }
Send-MailMessage -Body $strAlertMessage -SmtpServer $smtpServer -Subject $subject -To $toMail -From $fromMail
throw “ERROR in last run – please check email for error report”
}
function GetTaskParameters {
try {
$Task.DirObj.GetInfo()
$Task.DirObj.GetInfoEx(@(“edsaParameters”),0)
$strParameters = $Task.DirObj.Get(“edsaParameters”)
}
catch {
try {
$Task = Get-QADObject -Identity $TaskDN  -Proxy -IncludedProperties edsaParameters
$strParameters = $Task.edsaParameters
}
catch {
$stream.WriteLine(“FATAL ERROR: Failed to get script parameters”)
$stream.Close()
throw “FATAL ERROR: Failed to get script parameters”
}
}
$strParameters = ‘<parameters>’ + $strParameters + ‘</parameters>’
if( $Task ){ if ( $Task -is [IDisposable] ){ try { $Task.Dispose() } catch{} } }; Remove-Variable -Name Task -ErrorAction SilentlyContinue
return $strParameters
}
#endregion Helper Functions
#region Initialise script variables
$ownerldapFilter = “(&(employeeID=*)(sAMAccountType=805306368)(seeAlso=*))”
$resourceldapFilter = “(&(sAMAccountType=805306368)(secretary=*))”
# Set up a log file for debug and reporting
$logfilename = “Account RelationShip Checks”
$logDirectory = “c:\scripts\Identity Management\Logs\AccountRelationshipChecks”
$logfilepath = $logDirectory+”\”+$logfilename+”_”+$(Get-Date -Format yyyy-MM-dd)+”.log”
# Create a UTF8Encoding object so we can force the BOM – so that Umlauts display correctly
$utf8 = New-Object System.Text.UTF8Encoding($true)
$stream = New-Object system.IO.StreamWriter($logfilepath,$true,$utf8) # True appends to the file
#endregion Initialise script variables
#region Get Task Parameters
$strParameters          = GetTaskParameters
$xmlParameters          = [xml]$strParameters
$smtpServer = [string]$xmlParameters.parameters.smtpServer
if ( ($smtpServer -eq $null) -or ($smtpServer.length -le 10) ) { $smtpServer = “smtp.myAD.com”}
$subject = [string]$xmlParameters.parameters.subject
if ( ($subject -eq $null ) -or ($subject.length -le 0 )) { $subject = “ERROR CLEARING ACCOUNT RELATIONSHIP – PLEASE INVESTIGATE URGENTLY” }
$toMail = [string]$xmlParameters.parameters.toMail
if (( $toMail -eq $null ) -or ($toMail.length -le 0 ) ) { $toMail = “lee.andrews@MyDomain.com” }
$tomail = $tomail.split(“,”)
$fromMail = [string]$xmlParameters.parameters.fromMail
if ( ( $fromMail -eq $null ) -or ($fromMail.length -le 0 )) { $fromMail = “lee.andrews@MyDomain.com” }
$loglevel = [int]$xmlParameters.parameters.loglevel
if ( ( $loglevel -eq $null ) -or ( $loglevel.gettype().Name -ne “int32” ) -or ( $loglevel -eq 0 ) ) { $loglevel = [int]9 }
$commitChanges = [string]$xmlParameters.parameters.CommitChanges
switch ($commitChanges) {
“Yes” {
$whatIf = $false
break
}
“No” {
$whatIf = $true
break
}
default {
$whatIf = $false
break
}
}
$searchroot = [string]$xmlParameters.parameters.searchroot
if ( ($searchroot -eq $null) -or ($searchroot.length -le 10) ) { $searchroot = “OU=AD Management,DC=MyAD,DC=com” }
#endregion Get Task Parameters
#region Write log header
$stream.writeline(“===========================================================================” )
$stream.writeline(“ScriptName     : $ScriptName” )
$stream.writeline(“Script Version : $scriptVersion” )
$stream.writeline(“Date           : $(Get-Date)” )
$stream.writeline(“—————————————————————————” )
$stream.writeline(“Parameters:” )
$stream.writeline(“`tOwner LDAP Filter    : $ownerldapFilter” )
$stream.writeline(“`tResource LDAP Filter : $resourceldapFilter” )
$stream.writeline(“`tSMTP Server          : $smtpServer” )
$stream.writeline(“`tSubject              : $subject” )
$stream.writeline(“`ttoMail               : $toMail” )
$stream.writeline(“`tfromMail             : $fromMail” )
$stream.writeline(“`tcommitChanges        : $commitChanges” )
$stream.writeline(“`tsearchroot           : $searchroot” )
$stream.writeline(“`tloglevel             : $loglevel” )
$stream.writeline(“===========================================================================” )
#endregion Write log header
#region Look for broken seeAlso links
$stream.writeline(“get-qaduser -SearchRoot  $searchroot -LdapFilter $ownerldapFilter -IncludedProperties seeAlso -sizelimit 0 -Proxy”)
$Users = get-qaduser -SearchRoot  $searchroot -LdapFilter $ownerldapFilter -IncludedProperties seeAlso -sizelimit 0 -Proxy
$seeAlsoUpdated = 0
$stream.writeline(“Found: $($users.Count) users with seeAlso attribute set”)
$stream.writeline(“===========================================================================” )
forEach ( $user in $Users ) {
$seealso = @()
$userNameWrittenToLog = $false
$seeAlsoRequiresUpdate = $false
ForEach ( $serviceAccountDN in $user.seeAlso ) {
if ( $serviceAccountDN.indexOf(“0ADEL:”) -gt  0 ) {
$seeAlsoRequiresUpdate = $true
$seeAlsoUpdated++
if ( $loglevel -ge 8 ) {
if ( $userNameWrittenToLog -eq $false ) {
$stream.writeline(“Processing $($user.NTAccountName)”)
$userNameWrittenToLog = $true
}
$stream.writeline(“Removing : $serviceAccountDN”)
}
}
else {
$seealso += $serviceAccountDN
if ( $loglevel -ge 9 ) {
if ( $userNameWrittenToLog -eq $false ) {
$stream.writeline(“Processing $($user.NTAccountName)”)
$userNameWrittenToLog = $true
}
$stream.writeline(“Keeping  :$serviceAccountDN”)
}
}
}
if ( $seeAlsoRequiresUpdate -eq $true ) {
try { Set-QADUser -Identity $user.DN -ObjectAttributes @{seealso=$seealso} -Proxy -WhatIf:$whatIf -Control @{OperationReason=”$($ScriptName)_v$($scriptVersion)”} }
catch { $stream.writeline(“ERROR REMOVING seeAlso from: $($user.NTAccountName)” ) }
}
if ( ( $loglevel -ge 8 ) -and ( $userNameWrittenToLog -eq $true ) ) { $stream.writeline(“—————————————————————————” ) }
}
$stream.writeline(“Updated seeAlso attribute on $seeAlsoUpdated users”)
#endregion Look for broken seeAlso links
#region Look for broken secretary links
$stream.writeline(“===========================================================================” )
$Users = get-qaduser -SearchRoot  $searchroot -LdapFilter $resourceldapFilter -IncludedProperties secretary -sizelimit 0 -Proxy
$stream.writeline(“Found: $($users.Count) users with secretary attribute set”)
$stream.writeline(“===========================================================================” )
$secretaryUpdated = 0
forEach ( $user in $Users ) {
$secretary = @()
$userNameWrittenToLog = $false
$secretaryRequiresUpdate = $false
ForEach ( $ownerDN in $user.secretary ) {
if ( $ownerDN.indexOf(“0ADEL:”) -gt  0 ) {
$secretaryRequiresUpdate = $true
$secretaryUpdated++
if ( $loglevel -ge 8 ) {
if ( $userNameWrittenToLog -eq $false ) {
$stream.writeline(“Processing $($user.NTAccountName)”)
$userNameWrittenToLog = $true
}
$stream.writeline(“Removing : $ownerDN”)
}
}
else {
$secretary += $ownerDN
if ( $loglevel -ge 9 ) {
if ( $userNameWrittenToLog -eq $false ) {
$stream.writeline(“Processing $($user.NTAccountName)”)
$userNameWrittenToLog = $true
}
$stream.writeline(“Keeping  :$ownerDN”)
}
}
}
if ( $secretaryRequiresUpdate -eq $true ) {
try { Set-QADUser -Identity $user.DN -ObjectAttributes @{secretary=$secretary} -Control @{OperationReason=”$($ScriptName)_v$($scriptVersion)”} -Proxy -WhatIf:$whatIf }
catch { $stream.writeline(“ERROR REMOVING secretary from: $($user.NTAccountName)” ) }
}
if ( ( $loglevel -ge 8 ) -and ( $userNameWrittenToLog -eq $true ) ) { $stream.writeline(“—————————————————————————” ) }
}
$stream.writeline(“Updated ‘secretary’ attribute on $secretaryUpdated users”)
#endregion Look for broken secretary links
#region Close log file
$stream.writeline(“===========================================================================” )
$stream.writeline(“Script Run Finish : $(Get-Date)” )
$stream.writeline(“===========================================================================” )
$stream.close()
#endregion Close log file

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.