SharePoint 2016: Active Directory Import timer job does not run

This is an interesting "gotcha" that came up recently:

Problem:

The Active Directory Import (UserProfileADImportJob) timer job does not run.  It’s enabled and scheduled to run (default every 5 minutes), but never runs.
The result is that the user profiles never get imported.

 

Cause:

All the servers in the farm that are running the User Profile Service are also not allowed to run timer jobs of type “Service Job”.

Since the AD Import timer job is a "service job", and it can only run on servers running the User Profile service, that means there are no servers in the farm that are allowed to run the job.

-- You’ll see this entry in the SharePoint ULS logs on the servers that are running the User Profile service:

OWSTIMER.EXE (0x08AC) 0x08F4 SharePoint Foundation Timer 7f9q Medium  Job definition Microsoft.Office.Server.UserProfiles.ADImport.UserProfileADImportJob, id 999f5f95-7b20-4bb9-aa6e-d60147702e08 not applicable, ignoring

-- And you’ll see this on the other servers that are NOT running the User Profile service (which is normal and expected):
OWSTIMER.EXE (0x09AC) 0x12E4 SharePoint Foundation Timer 7f9r Medium Job definition Microsoft.Office.Server.UserProfiles.ADImport.UserProfileADImportJob, id 999f5f95-7b20-4bb9-aa6e-d60147702e08 has no online instance for service Microsoft.Office.Server.Administration.UserProfileService, id 8a10898b-c52f-40d6-b1cf-9ac83333c8fe, ignoring

Resolution:

You can fix this by either starting the User Profile Service on a server where “service jobs” are allowed, or you can set the AllowServiceJobs property to “true” on the existing servers running the User Profile service.

You can use this PowerShell script to accomplish the latter:
#This will check to make sure there is at least one User Profile Service instance in the farm,
#and that the AllowServiceJobs property is set to true for the timer instance on those server(s)
$SIs = Get-SPServiceInstance | ?{$_.typename -match "profile service"}
$Farm = Get-SPFarm
$FarmTimers = $Farm.TimerService.Instances
$1onLine = $false
foreach ($SI in $SIs)
{If($SI.Status -eq "Online")
{$1onLine = $true
foreach ($FT in $FarmTimers)
{if($SI.Server.Address.ToString() -eq $FT.Server.Name.ToString())
{if($FT.AllowServiceJobs -eq $false)
{write-host -ForegroundColor Red "Service jobs are NOT enabled on UPS server" $FT.Server.Name.ToString()
write-host -ForegroundColor green "Enabling service jobs on server: " $FT.Server.Name.ToString()
$FT.AllowServiceJobs = $true
$FT.Update()
}
else{write-host -ForegroundColor green "Service jobs already enabled on server: " $FT.Server.Name.ToString()}
}
}

}
}
If($1onLine -eq $false ){write-host -ForegroundColor red "No instances of User Profile Service online in this farm!"}

 

More Information:

-- The “AllowServiceJobs” property does not exist in SharePoint versions prior to 2016.

-- You can use this PowerShell script to check if all instances of the Timer service are online, and to check the values of AllowServiceJobs, and AllowContentDatabaseJobs:
$farm = Get-SPFarm
$FarmTimers = $farm.TimerService.Instances
foreach ($FT in $FarmTimers){write-host "Server: " $FT.Server.Name.ToString(); write-host "Status: " $FT.status; write-host "Allow Service Jobs: " $FT.AllowServiceJobs; write-host "Allow Content DB Jobs: " $FT.AllowContentDatabaseJobs;"`n"}