How do I identify invalid/inactive primary and secondary site collection owners through PowerShell?

The primary and secondary site collection owners play an important role in a SharePoint 2010 environment. They are usually the primary contacts for IT with the business users. In addition, they receive important notifications such as quota expiration and automatic site deletion notifications during the lifecycle of a Team Site. Hence, it is important to ensure that the primary and secondary site collection owners are always by e-mail reachable collaborators. This works fine as long as the collaborators have valid user accounts. Unfortunately, we encounter invalid user accounts when a user leaves a company and the IT was not informed about this fact. Usually, this is not a big deal for small companies with a limited amount of collaborator. On the other side, it becomes an issue for bigger organizations with a lot of site collections and collaborators.

This blog post shows how a PowerShell script might help identifying invalid/inactive primary and secondary site collection owners by querying Active directory. It might be a help for IT staff to identify such accounts and to interact pro-actively when such an account is identified.

This script is not intended for productive environments and shows only how it may look like. You have to ensure that the script is performing well in your environment.

The script can be split in two parts:

  • querying Active directory for invalid/inactive users
  • looping through all site collections and extracting the primary and secondary site collection owner

Querying invalid/inactive users

The next script shows how Active Directory might be queried to identify invalid accounts. I defined an invalid account to be an account that is either deleted, expired, or disabled. The function “isValidAccount” receives the account in form of “DOMAINusername”, queries active directory by using the “sAMAccountName” property and makes the required checks.

# retrieves from active directory the user and verifies if the user exists, the account is expired, or the account was disabled
function isValidAccount ($userName) {
  $userName = $userName.Substring($userName.IndexOf("") + 1)
  
  $searcher = New-Object System.DirectoryServices.DirectorySearcher
  $searcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry
  $searcher.PageSize = 100
  $searcher.Filter = "(&(objectCategory=User)(sAMAccountName=" + $username + "))"
  $results = $searcher.FindAll()
  
  foreach ($result in $results){
    $user = $result.GetDirectoryEntry()
    $expirationDate = $user.psbase.InvokeGet("AccountExpirationDate")
    
    $date = [DateTime]::Parse($expirationDate)
    if (($date -lt [DateTime]::Now) -and ($date -gt $noExpirationDate)) {
      return $false
    }
    return !$user.psbase.invokeget('accountdisabled')
  }
  
  return $false;
}

In the first line I am going to trim the account domain from the variable “$userName” because the “sAMAccountName” property in AD can be queried by using the username (see line 8).

This script was tested in a single domain only. Please consider to do the necessary tests when working with multiple domains. The “$searcher” object queries the domain of the executing user.

Line 11 loops through the results (should be only one record). If nothing was found, then the account was probably deleted by an AD administrator. On the other hand, if we are entering the loop statement, we are going to verify the property “AccountExpirationDate”.

The next lines are important. The “AccountExpirationDate” always returns a date. It can either be set with a recent date or with the “default” value “1/1/1970″. Latter is interpreted in Active Directory as the “not expired” date. We simply compare if the account is not already expired in the if statement. If so, the function immediately returns that it is not valid.

Afterwards, we verify the “AccountDisabled” property and return the corresponding value.

The last return statement after the loop returns always false. The reason to do so is that the account was probably deleted because no results were returned from our query.

Looping through all site collections

With the previous script we went through the Active Directory query and verifications. The next script is simply looping though all the site collections of a given web application, returns the primary and secondary site collection owners and passes them to the previously explained method.

# specify the web application to browse through
$webAppUrl = "http://lrd01sha"
$webApp = Get-SPWebApplication $webAppUrl
# active directory "no expiration" date
$noExpirationDate = [DateTime]::Parse("1/1/1970")

$allSites = $webApp.Sites

# browse all site collections and check the primary and secondary owner
foreach ($site in $allSites) {
  $primaryLogin = $site.Owner.UserLogin
  $secondaryLogin = $site.SecondaryContact.UserLogin
 
  if (-not [system.string]::IsNullOrEmpty($primaryLogin)) {
     $valid = isValidAccount $primaryLogin
     if (-not $valid) {
       Write-Host "$primaryLogin is not valid @$site"
     }
  }
  
  if (-not [system.string]::IsNullOrEmpty($secondaryLogin)) {
     $valid = isValidAccount $secondaryLogin
     if (-not $valid) {
       Write-Host "$secondaryLogin is not valid @$site"
     }
  }
}

You can change all initialization variables. It is recommended, however, to maintain the “$noExpirationDate” as it is.

Putting it all together

Previously we split up the different sections of the script. The next code snippet puts it all together.

Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction silentlycontinue

# retrieves from active directory the user and verifies if the user exists, the account is expired, or the account was disabled 
function isValidAccount ($userName) { 
  
  $userName = $userName.Substring($userName.IndexOf("") + 1) 
  
  $searcher = New-Object System.DirectoryServices.DirectorySearcher 
  $searcher.SearchRoot = New-Object System.DirectoryServices.DirectoryEntry 
  $searcher.PageSize = 100 
  $searcher.Filter = "(&(objectCategory=User)(sAMAccountName=" + $username + "))" 
  $results = $searcher.FindAll() 
  
  foreach ($result in $results){ 
    $user = $result.GetDirectoryEntry() 
    $expirationDate = $user.psbase.InvokeGet("AccountExpirationDate") 
    
    $date = [DateTime]::Parse($expirationDate) 
    if (($date -lt [DateTime]::Now) -and ($date -gt $noExpirationDate)) { 
      return $false 
    } 
    return !$user.psbase.invokeget('accountdisabled') 
  } 
  
  return $false; 
}

# specify the web application to browse through 
$webAppUrl = "http://lrd01sha" 
$webApp = Get-SPWebApplication $webAppUrl 
# active directory "no expiration" date 
$noExpirationDate = [DateTime]::Parse("1/1/1970")

$allSites = $webApp.Sites

# browse all site collections and check the primary and secondary owner 
foreach ($site in $allSites) { 
  $primaryLogin = $site.Owner.UserLogin 
  $secondaryLogin = $site.SecondaryContact.UserLogin 
  
  if (-not [system.string]::IsNullOrEmpty($primaryLogin)) { 
     $valid = isValidAccount $primaryLogin 
     if (-not $valid) { 
       Write-Host "$primaryLogin is not valid @$site" 
     } 
  } 
  
  if (-not [system.string]::IsNullOrEmpty($secondaryLogin)) { 
     $valid = isValidAccount $secondaryLogin 
     if (-not $valid) { 
       Write-Host "$secondaryLogin is not valid @$site" 
     } 
  } 
}

Summary

In this blog post we mentioned how important the primary and secondary site collection owners are in a SharePoint environment. Therefore, we have also to ensure that these users are still valid users in Active Directory. With a PowerShell script we showed how to verify this. Extend the script to you needs and improve it with better reporting capabilities.

Hope this helps,
Patrick

This entry was posted in IT-Pro by Patrick Lamber. Bookmark the permalink.
Patrick Lamber

About Patrick Lamber

Patrick Lamber is a long-standing .NET Developer and has offered SharePoint consulting, development, and training services to customers since the launch of SharePoint 2007. He is a both a Microsoft Certified Trainer and a Microsoft Certified IT Professional, and holds Microsoft Certified Professional Development certifications for SharePoint. Patrick founded NUBO with the aim of developing a team of specialized SharePoint professionals delivering great solutions. Furthermore, in his role as SharePoint Competence Manager Patrick is also responsible for building up the SharePoint team for the company Blu Systems GmbH in Munich. Patrick speaks three languages (German, Italian, and English), meaning most of his work is focused on Italy, Germany, Austria, and Switzerland.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Powered by Sweet Captcha
Verify your real existence,
Drag to bring the piggy his cigar.
  • captcha
  • captcha
  • captcha
  • captcha