PowerShell - Get User Profile Service info

# Get_UserProfileServices.ps1
# Dump out any info I can glean from the User Profile Synch for SharePoint.
#
# LukeB
# https://technet.microsoft.com/en-us/library/ee721049.aspx
#
if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null)
{
    Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}
[void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
 
$USERPROFILESERVICEPROXY = "Microsoft.Office.Server.Administration.UserProfileServiceProxy"
$USERPROFILEAPPLICPROXY = "Microsoft.Office.Server.Administration.UserProfileApplicationProxy"
$USERPROFILESERVICE = "Microsoft.Office.Server.Administration.UserProfileServiceInstance"
$SYNCHSERVICE = "Microsoft.Office.Server.Administration.ProfileSynchronizationServiceInstance"
 
###############################################################################################################
# -- helper function
function Get-SPfarmAdministrators {
  $adminwebapp = Get-SPwebapplication -includecentraladministration | where {$_.IsAdministrationWebApplication}
  $adminsite = Get-SPweb($adminwebapp.Url)
  $AdminGroupName = $adminsite.AssociatedOwnerGroup
  $farmAdministratorsGroup = $adminsite.SiteGroups[$AdminGroupName]
  return $farmAdministratorsGroup.users
}
 
# -- helper function
function Check-ADUserPermission( [System.DirectoryServices.DirectoryEntry]$entry, [string]$user, [string]$permission ) {
  $dse = [ADSI]"LDAP://Rootdse"
  $ext = [ADSI]("LDAP://CN=Extended-Rights," + $dse.ConfigurationNamingContext)
  $right = $ext.psbase.Children | ? { $_.DisplayName -eq $permission }
  if ($right -ne $null) {
   $perms = $entry.psbase.ObjectSecurity.Access | ? { $_.IdentityReference -eq $user } | ? { $_.ObjectType -eq [GUID]$right.RightsGuid.Value }
   return ($perms -ne $null)
  }
  else {
   Write-output "Permission '$permission' not found."
   return $false
  }
}
 
# -- helper function
# If the NetBIOS name of any domain that you are synchronizing with differs from its
# FQDN, you must enable NetBIOS domain names on the User Profile service application.
# So we should grab their FQDN and the NETBIOS name and compare the NETBIOS name with the first portion of the FQDN.
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public class NetUtil
{
  [DllImport("netapi32.dll", CharSet = CharSet.Auto)]
  static extern int NetWkstaGetInfo(string server, int level, out IntPtr info);
  [DllImport("netapi32.dll")]
  static extern int NetApiBufferFree(IntPtr pBuf);
  [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
  class WKSTA_INFO_100
  {
    public int wki100_platform_id;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string wki100_computername;
    [MarshalAs(UnmanagedType.LPWStr)]
    public string wki100_langroup;
    public int wki100_ver_major;
    public int wki100_ver_minor;
  }
  public static string GetMachineNetBiosDomain()
  {
   IntPtr pBuffer = IntPtr.Zero;
   WKSTA_INFO_100 info;
   int retval = NetWkstaGetInfo(null, 100, out pBuffer);
   info = (WKSTA_INFO_100)Marshal.PtrToStructure(pBuffer, typeof(WKSTA_INFO_100));
   string domainName = info.wki100_langroup;
   NetApiBufferFree(pBuffer);
   return domainName;
  }
}
"@
 
function Check-NETBIOS {
   $fqdn = $(Get-WmiObject Win32_ComputerSystem).Domain
   $domainpart= $($fqdn.split('.',2)[0]).ToUpper()
   $netbiosdomain = [NetUtil]::GetMachineNetBiosDomain()
   return $domainpart.Equals($netbiosDomain)
}
# -- helper function
function Get-DomainMode {
  $domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
  return $domain.DomainMode
}
# -- helper function
function get-DNSLookup {
   param ( $hostname )
try {
  $dnsResult = [System.Net.Dns]::GetHostEntry($hostname)
  $name = $dnsResult.HostName
  if ($name -notlike "*.*") { $name += " (from WINS)" }
  $ip =$dnsResult.AddressList
}
catch {
    $name="Host Not Resolved"
    $ip="Host Not Resolved"
  }
$object = New-Object PSObject -Property @{
               Name = $name
               IP = $ip
            }
return $object
}
 
###############################################################################################################
 
$ProfileServiceApplicProxy = Get-SPServiceApplicationProxy | where {$_.GetType().ToString() -eq $USERPROFILEAPPLICPROXY}
if ($ProfileServiceApplicProxy) {
  $ProfileServiceProxy = $ProfileServiceApplicProxy.Parent
  $PSAS=Get-SPProfileServiceApplicationSecurity -ProfileServiceApplicationProxy $ProfileServiceApplicProxy -ErrorAction SilentlyContinue
# $PSAS is the access rights
  $UPSP = New-Object PSObject -Property @{
    ServiceName = $ProfileServiceApplicProxy.Name
    AccessRights = $PSAS
  }
  write-output "User Profile Service Proxy:"
  $UPSP | FL
}
 
#
# check if the User Profile Service is present and running on this box
#
 
$UserProfileServiceInstances = Get-SPServiceInstance | where {$_.GetType().ToString() -eq $USERPROFILESERVICE}
if ($UserProfileServiceInstances) {
foreach ($UserProfileServiceInstance in $UserProfileServiceInstances) {
     if ($UserProfileServiceInstance -eq $null) { Continue }
     $UPSI = New-Object PSObject -Property @{
       Status = $UserProfileServiceInstance.Status
       Address = $UserProfileServiceInstance.Server.Address
       NetBIOSDomainNamesEnabled = $UserProfileServiceInstance.NetBIOSDomainNamesEnabled
       NetBIOSDomainNamesEnabledOK = $true
       NetBIOSDomainEqualsDomain = $true
       JobDefinitions = $UserProfileServiceInstance.Service.JobDefinitions
       JobHistoryEntries = $UserProfileServiceInstance.Service.JobHistoryEntries
       ProcessAccountName = "<none>"
     }
    
# The UPA property NetBIOSDomainNamesEnabled is used to control whether the CNC partition is included in the AD Connection or not. 
# By default it is false (not enabled) and the CNC and associated run steps are not included in the AD Connection configuration. 
# If it is enabled, then the CNC partition and run steps are included.
    
     $UPSI.NetBIOSDomainEqualsDomain = Check-NETBIOS
     if (!($UPSI.NetBIOSDomainEqualsDomain)) {
       if ($UPSI.$NetBIOSDomainNamesEnabled) { $UPSI.NetBIOSDomainNamesEnabledOK = $true }
       else { $UPSI.NetBIOSDomainNamesEnabledOK = $false }
     }
    
# Now find the account that is running the User Profile Service.
     $UPSWebApps=$UserProfileServiceInstance.Service.Applications
     foreach ($WebApp in $UPSWebApps) {
       if ($WebApp -eq $null) { Continue }
       $AppPool=$WebApp.ApplicationPool 
       $UPSI.ProcessAccountName = $AppPool.ProcessAccountName
     }
     write-output "User Profile Service Instance:"
     $UPSI
}
 
}
else { write-output "NO UserProfileServiceInstances!" }
 
# If the farm contains multiple servers running SP2010, and two or more servers are running the User Profile service,
# the timer job responsible for synchronization might fail. This happens when the server that runs the synchronization
# timer job is not running the synchronization service. To resolve this problem, grant the farm account the Remote Enable
# permission on the server that runs the synchronization service. Doing this enables the timer job to run successfully
# regardless of which server picks up the timer job.
 
#
# check if the User Profile Synchronization Service (FIM) is present and running
#
$ProfileSynchServiceInstances = Get-SPServiceInstance | where {$_.GetType().ToString() -eq $SYNCHSERVICE}
if ($ProfileSynchServiceInstances) {
  $ProfileSynchServiceInstance = $ProfileSynchServiceInstances | where {$_.Status -eq "Online"}
  If ($ProfileSynchServiceInstance) {

     $context = [Microsoft.Office.Server.ServerContext]::Default
     try {
      $UPAConfMgr = new-object Microsoft.Office.Server.UserProfiles.UserProfileConfigManager($context)
      $UPAConnMgr = $UPAConfMgr.ConnectionManager

      $CurrentContext=$UPAConnMgr.getenumerator()
      foreach ($connection in $CurrentContext) {
        if ($connection -eq $null) { Continue }
        $PSSIDisplayName = $connection.DisplayName
        $PSSIAccountDomain = $connection.AccountDomain
        $PSSIUserName = $connection.AccountUserName
      }

      $PSSI = New-Object PSObject -Property @{
        Status = $ProfileSynchServiceInstance.Status
        Server = $ProfileSynchServiceInstance.Server.Name
        ServerDNSresolve = $true
        Service = $PSSIDisplayName
        UserName = $PSSIUserName
        AccountDomain = $PSSIAccountDomain.ToUpper()
        UserNameIsManagedAccount = $false
        UserNameIsFarmAccount = $false
        UserNameIsFarmAdmin = $false
        UserNameHasReplicatingDirectoryChanges = $false
        UserNameHasPreWin2kcompatible = $false
      }
    
      $PSSIUserName = $PSSI.AccountDomain + "\" + $PSSI.UserName

# test that the servername resolves.
# we should run some resolver tests on the actual synch-service server.
#   
      $DNSresult = get-DNSLookup $PSSIserver
      if ($DNSresult.Name -eq "Host Not Resolved") {  
                  $PSSI.ServerDNSresolve = $false }
      else { $PSSI.ServerDNSresolve = $true }
    
# 1. You MUST run the UserProfileSynchService Instance as the Farm Account.
# 2. the Farm Account MUST be a local administrator of the machine running the UPS Service Instance >>> during provisioning only <<<.
    
# now we need to check if that is a Farm Admin account
      $FarmAccount = (Get-SPFarm).DefaultServiceAccount
      if ( $FarmAccount.Name -eq $PSSIusername ) { 
                 $PSSI.UserNameIsFarmAccount = $true }
      else { $PSSI.UserNameIsFarmAccount = $false } 
# now we should also check if it is a Farm Administrator
      $isFarmAdmin = Get-SPfarmAdministrators | where-object {$_.UserLogin -eq $PSSIUserName}
      if ($isFarmAdmin) { 
                 $PSSI.UserNameIsFarmAdmin = $true }
      else { $PSSI.UserNameIsFarmAdmin = $false } 
    
# $LocalAdminGroup =[ADSI]"WinNT://$PSSIserver/Administrators"
# $members = @($LocalAdminGroup.psbase.Invoke("Members"))
# $members | foreach {$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)}
    
# check if the account has the AD access permissions   
      $ReplicatingDirectoryChanges = "Replicating Directory Changes"
      $dse = [ADSI]"LDAP://Rootdse"
      $dNC = [ADSI]("LDAP://" + $dse.defaultNamingContext)
    
      $result = Check-ADUserPermission $dNC $PSSIusername $ReplicatingDirectoryChanges
      if ($result) {  
                  $PSSI.UserNameHasReplicatingDirectoryChanges=$true }
      Else { $PSSI.UserNameHasReplicatingDirectoryChanges=$false }
     
# DirSynch will need "Pre-Windows 2000 Compatible Access", so check if they have that.
# Why? Because under the covers we grab the token-groups-global-and-universal (TGGAU) attribute. And the account
# will need the rights to get that. Depending on the domain mode, this will fail. Yes, really. (KB331951)
# So set it and we are safe.
    
      $DomainMode = Get-DomainMode
      if ( ($DomainMode -eq "Windows2000MixedDomain") -or
           ($DomainMode -eq "Windows2000NativeDomain") -or
           ($DomainMode -eq "Windows2003InterimDomain") -or
           ($DomainMode -eq "Windows2003Domain") ) {
    
        $PreWin2kcompatible = "Pre-Windows 2000 Compatible Access"
        $dse = [ADSI]"LDAP://Rootdse"
        $dNC = [ADSI]("LDAP://" + $dse.defaultNamingContext)
   
        $result = Check-ADUserPermission $dNC $PSSIusername $PreWin2kcompatible
        if ($result) { 
                   $PSSI.UserNameHasPreWin2kcompatible = $true }
        Else { $PSSI.UserNameHasPreWin2kcompatible = $false }
      }

      $isMgdAccount = Get-SPManagedAccount | where {$_.Username -eq $PSSIusername }
      if ($isMgdAccount) {  
                  $PSSI.UserNameIsManagedAccount = $true }
      Else { $PSSI.UserNameIsManagedAccount = $false }
    
    write-output "Profile Service Synch Instance:"
    $PSSI
   }
   catch { 
       write-output "NO UserProfileConfigManager!" 
   }
  }
}
else {
   write-output "NO ProfileSynchServiceInstances!"
}