<# This script will enumerate all contact objects in a Domain, providing both a summary overview and a full report based on the values of the following attributes: - name - distinguishedname - givenName - sn - initials - mail - telephoneNumber - mobile - displayName - description - title - company - physicalDeliveryOfficeName - manager - employeeID - employeeNumber - employeeType - msexchextensioncustomattribute1 - msexchextensioncustomattribute2 - msexchextensioncustomattribute3 - msexchextensioncustomattribute4 - msexchextensioncustomattribute5 - isCriticalSystemObject - expirationtime - info - whencreated - whenchanged - memberOf - targetAddress - proxyAddresses - legacyExchangeDN - mailNickName - msRTCSIP-Line - otherIpPhone We further derive: - The Parent OU from the distinguishedname attribute. - MailEnabled from targetAddress, proxyAddresses, legacyExchangeDN, and mailNickName. - http://pmoreland.blogspot.com.au/2013/03/creating-mail-contacts-and-distribution.html - UMIntegrationObject from msRTCSIP-Line and otherIpPhone We check to see if the Surname (sn) contains a non-alpha character. This is to ensure that a consistent standard/pattern has been used for hyphenated namnes, some Asian names, and tussenvoegsels. Syntax examples: - To execute the script in the current Domain: Get-ContactReport.ps1 - To execute the script against a trusted Domain: Get-ContactReport.ps1 -TrustedDomain mydemosthatrock.com Script Name: Get-ContactReport.ps1 Release 2.3 Written by Jeremy@jhouseconsulting.com 27/12/2013 Modified by Jeremy@jhouseconsulting.com 14/12/2015 #> #------------------------------------------------------------- param([String]$TrustedDomain,[switch]$verbose) if ($verbose.IsPresent) { $VerbosePreference = 'Continue' Write-Verbose "Verbose Mode Enabled" } Else { $VerbosePreference = 'SilentlyContinue' } #------------------------------------------------------------- # Set this to the OU structure where the you want to search to # start from. Do not add the Domain DN. If you leave it blank, # the script will start from the root of the domain. $OUStructureToProcess = "" # Set this to true to include the employeeID, employeeNumber and # employeeType attributes. $IncludeEmployeeAttributes = $True # Set this to true to include the first 3 msexchextensioncustomattribute # attributes. Really handy for Office365 license management with DirSync. $IncludeExchExtensionCustomAttributes = $False # Set this to true to include extra contact attributes such as # displayName, description, telephoneNumber, mobile, title, # company, physicalDeliveryOfficeName $IncludeExtendedDetails = $True # Set this to true to include the user's direct group membership $IncludeMemberOf = $True # Set the name of the attribute you want to populate for objects # to be evaluated as a stale or non-stale object. $ExcludeAttribute = "comment" # Set the text within the $ExcludeAttribute that you want to use # to evaluate if the object should be excluded from the stale # object collection. $ExcludeText = "Decommission=False" # Set this to the delimiter for the CSV output $Delimiter = "," # Set this to remove the double quotes from each value within the # CSV. $RemoveQuotesFromCSV = $False # Set this value to true if you want to see the progress bar. $ProgressBar = $True #------------------------------------------------------------- $invalidChars = [io.path]::GetInvalidFileNamechars() $datestampforfilename = ((Get-Date -format s).ToString() -replace "[$invalidChars]","-") # Get the script path $ScriptPath = {Split-Path $MyInvocation.ScriptName} $ReferenceFileFull = $(&$ScriptPath) + "\ContactReport-$($datestampforfilename).csv" $ReferenceFileSummaryTotals = $(&$ScriptPath) + "\ContactReport-Summary-Totals-$($datestampforfilename).csv" if (Test-Path -path $ReferenceFileFull) { remove-item $ReferenceFileFull -force -confirm:$false } if (Test-Path -path $ReferenceFileSummaryTotals) { remove-item $ReferenceFileSummaryTotals -force -confirm:$false } if ([String]::IsNullOrEmpty($TrustedDomain)) { # Get the Current Domain Information $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain() } else { $context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("domain",$TrustedDomain) Try { $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($context) } Catch [exception] { write-host -ForegroundColor red $_.Exception.Message Exit } } # Get AD Distinguished Name $DomainDistinguishedName = $Domain.GetDirectoryEntry() | select -ExpandProperty DistinguishedName If ($OUStructureToProcess -eq "") { $ADSearchBase = $DomainDistinguishedName } else { $ADSearchBase = $OUStructureToProcess + "," + $DomainDistinguishedName } $TotalcontactsProcessed = 0 $contactCount = 0 $TotalMailEnabledObjects = 0 $TotalMailDisabledObjects = 0 $TotalDefaultUsersContainer = 0 $TotalUMIntegrationObjects = 0 $TotalADFSFarmObjects = 0 $TotalConflictingObjects = 0 $TotalManagerNotSet = 0 $TotalExpirationTimeNotSet = 0 $TotalExcludedObjects = 0 # Create an LDAP search for all contacts $ADFilter = "(&(objectClass=contact)(objectcategory=person))" # There is a known bug in PowerShell requiring the DirectorySearcher # properties to be in lower case for reliability. $ADPropertyList = @("name","distinguishedname","givenname","sn","initials","mail", ` "telephonenumber","mobile","displayname","description","title", ` "company","physicaldeliveryofficename","manager","employeeid", ` "employeenumber","employeetype","info","iscriticalsystemobject", ` "whencreated","whenchanged","expirationtime","memberof", ` $ExcludeAttribute,"msexchextensioncustomattribute1", ` "msexchextensioncustomattribute2","msexchextensioncustomattribute3", ` "msexchextensioncustomattribute4","msexchextensioncustomattribute5", ` "targetaddress","proxyaddresses","legacyexchangedn","mailnickname", ` "msrtcsip-line","otheripphone") $ADScope = "SUBTREE" $ADPageSize = 1000 $ADSearchRoot = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$($ADSearchBase)") $ADSearcher = New-Object System.DirectoryServices.DirectorySearcher $ADSearcher.SearchRoot = $ADSearchRoot $ADSearcher.PageSize = $ADPageSize $ADSearcher.Filter = $ADFilter $ADSearcher.SearchScope = $ADScope if ($ADPropertyList) { foreach ($ADProperty in $ADPropertyList) { [Void]$ADSearcher.PropertiesToLoad.Add($ADProperty) } } Try { write-host -ForegroundColor Green "`nPlease be patient whilst the script retrieves all contact objects and specified attributes..." $colResults = $ADSearcher.Findall() # Dispose of the search and results properly to avoid a memory leak $ADSearcher.Dispose() $contactCount = $colResults.Count } Catch { $contactCount = 0 Write-Host -ForegroundColor red "The $ADSearchBase structure cannot be found!" } if ($contactCount -ne 0) { write-host -ForegroundColor Green "`nProcessing $contactCount contact objects in the $domain Domain..." $colResults | ForEach-Object { $contactDN = $_.Properties.distinguishedname[0] $ParentOU = $contactDN -split '(?