SharePoint PowerShell Timer Jobs: Run PowerShell scripts in SharePoint Timer Service context.

I’ve created a new project that combines my two primary technical passions: SharePoint 2010 and PowerShell. Smiley

The project “SharePoint PowerShell Timer Jobs” lets you create, modify and delete SharePoint timer jobs that run PowerShell scripts in the context of the SharePoint Timer Service!

It’s on Codeplex – including source code:

I hope you will love it! (If so please let me know!) – Please feel free to extend the project. If you do so please share your extensions with the community!!!

PowerShell Timer Jobs could be very useful. I’ll use it for “SharePoint Warmup”. Therefore I create a PowerShell script some months ago. This I will port to a PowerShell Timer Job and post it on my blog…


Now let’s have a look…


Here’s a screenshot of the “System Settings” page of the SharePoint 2010 Central Administration:


Use “Manage PowerShell Jobs” to create, modify or delete PowerShell jobs.

On the admin page you can select an existing timer job or select “<new>” to create a new one.

If you create a new one you have to configure it and name it. Then save the timer job. After that you’ll be able to edit the script.

In this screenshot you see an existing timer job:

Here the “Edit” button is enabled. The script below the “Edit” button is read only!

Click “Edit”… Here is what you will see:

In the Edit dialog you can enter your PowerShell script and save it.

The Edit dialog does not validate your script! It have to be valid. Or you will see errors in the history list.

At the management page you can enable or disable existing jobs using the checkbox. Don’t forget to click the “Update” button after you changed something!

At the “System Settings” page of the Central Administration you see the link “Review PowerShell Job Execution History”. Using this link you will be redirected to the history list. All outputs of all of your PowerShell jobs will be collected here.

The list will not be cleared automatically! – But… You could create a SharePoint PowerShell Timer Job for this purpose 🙂 …

The last thing you should know: All the jobs you create are accessible at common SharePoint Job admin pages  like “Review Job definitions” and “Check job status” (Central Administration -> Monitoring)

Here you see a screenshot of the “Review Job definitions” page of SharePoint 2010:

This is a screenshot of the Job definitions detail page:

At least a screenshot of the “Check job status” page of SharePoint:

Some more details…

The PowerShell script of each timer job will be executed in a dedicated PowerShell runtime environment (“Runspace”).

For the PowerShell runspace I’ve created a PowerShell Host implementation. This implementation does not support any user interaction! Be sure your script does not need to interact with the user, e.g. for delete confirmation.

The script you enter will be surrounded with this code:

Add-PSSnapIn Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue | out-null  
$o = Invoke-Command -ScriptBlock 
<your code> 
$p = $o | out-string 

For script development you should use this code frame too! But if you copy the script code from the development tool, e.g. Windows PowerShell ISE, to PowerShell Timer Jobs, be sure only to copy <your code>.

Furthermore you need to enable the execution of unsigned PowerShell scripts in the context of the SharePoint Timer Service. This service normally runs under the Farm account. Have a look into the “Services” management console.

Before you use the tool be sure you know what you do! – PowerShell scripts can damage your farm! BE CAREFULL!!!!I’m not responsible for any damages.

You shoud test the tool in your own environment. There could be errors in the tool!!! I’ve tested it in my dev environment, but maybe in yours it does not work properly!

Feel free to extend the tool. But if you do so please publish your code to the community.

You must not remove my name or the link to the projects homepage from any file or page of the project. – Please don’t do that. Thanks 🙂


…you need to deploy the .WSP file to sharepoint and activate the SharePoint features in the Central Administration. The history list have to resist on the CA!!!

SharePoint Warm Up – Now with “Timeout”

In January 2011 I published a SharePoint Warm Up script based on PowerShell.

Some days ago blog reader Tamas published an comment:

“How to set a connection timeout”.


Here we go now!


I’ve extended the Warm Up Script. I hope, this helps some of you, especially Tamas Smiley

# SharePoint Warmup Script
# by Ingo Karstein
# 2011/01/26
# 2011/08/02

#<---- Improvement starts here
#region MyWebClient
    Add-Type -ReferencedAssemblies "System.Net" -TypeDefinition @"
    using System.Net;

    public class MyWebClient : WebClient
        private int timeout = 60000;
        public MyWebClient(int timeout)
            this.timeout = timeout;
        public int Timeout
                return timeout;
                timeout = value;
        protected override WebRequest GetWebRequest(System.Uri webUrl)
            WebRequest retVal = base.GetWebRequest(webUrl);
            retVal.Timeout = this.timeout;
            return retVal;
#----> Improvement ends here

$urls= @("http://sharepoint.local", "http://another.sharepoint.local")

New-EventLog -LogName "Application" -Source "SharePoint Warmup Script" -ErrorAction SilentlyContinue | Out-Null

$timeout = 60000 #=60 seconds               #<--- Improvement!

$urls | % {
    $url = $_
    try {
        $wc = New-Object MyWebClient($timeout)        #<--- Improvement!
        $wc.Credentials = [System.Net.CredentialCache]::DefaultCredentials
        $ret = $wc.DownloadString($url)
        if( $ret.Length -gt 0 ) {
            $s = "Last run successful for url ""$($url)"": $([DateTime]::Now.ToString('yyyy.dd.MM HH:mm:ss'))" 
            $filename=((Split-Path ($MyInvocation.MyCommand.Path))+"lastrunlog.txt")
            if( Test-Path $filename -PathType Leaf ) {
                $c = Get-Content $filename
                $cl = $c -split '`n'
                $s = ((@($s) + $cl) | select -First 200)
            Out-File -InputObject ($s -join "`r`n") -FilePath $filename
    } catch {
          Write-EventLog -Source "SharePoint Warmup Script"  -Category 0 -ComputerName "." -EntryType Error -LogName "Application" `
            -Message "SharePoint Warmup failed for url ""$($url)""." -EventId 1001

        $s = "Last run failed for url ""$($url)"": $([DateTime]::Now.ToString('yyyy.dd.MM HH:mm:ss')) : $($_.Exception.Message)" 
        $filename=((Split-Path ($MyInvocation.MyCommand.Path))+"lastrunlog.txt")
        if( Test-Path $filename -PathType Leaf ) {
          $c = Get-Content $filename
          $cl = $c -split '`n'
          $s = ((@($s) + $cl) | select -First 200)
        Out-File -InputObject ($s -join "`r`n") -FilePath $filename

Me on “Hey, Scripting Guy” blog!

Let me say this in PowerShell language 🙂

"I'm $(for($i=0;$i -lt $infinite; $i++){'very'}) happy about the possiblity to publish "+`
"a guest article on Microsofts ""Hey, Scripting Guy"" blog today. Thanks to the "+`
"Microsoft Scripting Guy, Ed Wilson, for the invitation!"

Here it is:

Learn About Two CodePlex Projects: PS2EXE and RoboPowerCopy

It’s an article about my both projects “RoboPowerCopy” and “PS2EXE”.

PS2EXE: Tool for “converting” PowerShell scripts to “standalone” EXE files

In the last days I created the tool “PS2EXE”. It is able to “convert” PowerShell scripts to “standalone” EXE files.

Project site here: – It’s ALPHA in the current version

Version Update from to at 2011-08-22.

But: It does not convert the PowerShell script to an other language! It encapsulates the script with a lightweight PowerShell host written in C# and compiles the dynamically generated C# source code in memory to an EXE file. The resulting EXE is an .NET assembly that contains the source script encoded in Base64. The EXE includes all stuff that is needed to execute an PowerShell through the .NET object model. It is based on classes in the namespace System.Management.Automation that reperents the PowerShell engine. – Therefore the EXE file is not a real “standalone” EXE file. It needs PowerShell to be installed!!! And – of course – it needs .NET Framework v2.0. Furthermore “script execution” have to be allowed (see cmdlet: set-execultionpolicy). – The resulting EXE is “MSIL” and is able to execute as x64 or x86.

The tool “PS2EXE” itself is a PowerShell script! – It does the in-memory compilation and generates the EXE file. It uses the CSharpCodeProvider class of namespace Microsoft.CSharp.

The script is really simple. I contains a multiline string that represents the PowerShell host I’ve written. This is much more interesting than the PS2EXE.ps1 script itself. – Have a look into it!


Call  the script with this parameters:

-inputFile PowerShell script file
-outputFile file name (with path) for the destination EXE file
-debug (switch) generate debug info in the destination EXE file. The dynamically generated .CS file will stored beside the output EXE file. Furthermore a .PDB file will be generated for the EXE file
-verbose (switch) shows also verbose informations – if any.



This creates “test.exe” out of the PowerShell source file “test.ps1”

Limitations: It’s not easy to create a fully functional PowerShell host such as “Console host” (powershell.exe) or “ISE” (powershell_ise.exe). So there may be functionality that does not work properly.


The generated EXE can also be calls using command line options. There are 4 options that are used by the PowerShell host:

-debug Forces the EXE to be debugged. It calls “System.Diagnostics.Debugger.Break()”.
-extract:”Filename” Extracts the PowerShell script inside the EXE and saves it as “Filename”. The script will not be executed.
-wait At the end of the script execution it writes “Press a key…” and waits for a key to be pressed.
-end All following options will be passed to the script inside the EXE. All preceding options are used by the EXE and will not be passed to the script.


I create a script file containing this line of code:

$args | Write-Host


I save it as “c:test2.ps1” and convert it as EXE by using PS2EXE:


Sample 1.: “-wait” forces the “Hit any key…” message. All options following “-end” will be passed to the script.

Sample 2., 3. : The script will not get options preceding to “-end”.

Sample 4: “-wait” follows to “-end” and so it’s passed to the script. No message “Hit any key…”.

So. That’s it for the moment. Please feel free to modify the script and let me know.

Possible tasks:

  • Sign the script inside the EXE with code signature
  • Sign the EXE with code signature
  • Compress the script inside the EXE
  • Improve the PSHost implementation inside the EXE.

Have fun!

RoboPowerCopy – A PowerShell based RoboCopy clone

UPDATE 2021-07-23: Because of the Codeplex shutdown I have archived the project here:

Today I want to announce my latest development project.

RoboPowerCopy (v0.1.0.0)

It’s a PowerShell 2.0 based clone of Microsoft’s famous RoboCopy tool.

Get it on Codeplex:

RoboCopy tool is part of my daily work. At work and at home. – You all now it! I hope so Zwinkerndes Smiley

I spend the spare time of some weeks for this project and now it’s ready to be published as ALPHA version (v0.1.0.0). The development is still in progress!! – Hopefully there is somebody out there that will use it – beside me.

I’ve done this using the (also) famous PowerGUI (Community Edition) by Quest.

“But way” you may ask.

RoboCopy is an EXE file and it has closed source. We (the customers) are not able to change it’s behavior. E.g. I’m missing a “verify” feature for years. Some people say: “It’s a copy tool not a backup tool”. Okay. That’s right. But I want to be sure that the copied data is valid and identical to the source data.

Because of that I thought about the possibility to realize a RoboCopy clone with PowerShell. – So I did it now.

RoboPowerCopy is plain PowerShell. No assemblies or PowerShell extensions are needed. – It uses some inline C# code.

RoboPowerCopy should “understand” at least the commands (options) of RoboCopy. – Currently I’ve implemented a lot of RoboCopy’s abilities. Not all. But some new features on top, e.g. verify and “read-and-check-before-copy”. “Verify” uses .NET hashing algorithms to compare the written data to the source data. If the destination file already exists the “Read-and-check-before-copy” feature reads every portion of data from the destination file before it replaces the data portion on the destination file with the source file’s data. This should reduce the time that is needed to copy large files that have only a few binary changes.

As you may now .NET has no build in capabilities to handle with very long paths with more than 256 characters. I have developed a set of helper classes in C# that enable RoboPowerCopy to deal with very long paths. – This C# is included in the script an will be compiled at runtime into an assembly in memory.

Here are the abilities:

  • Long path support (!!)
  • Restartable mode using a temporary “copy header” at the end of the copied file.
  • Copy security: SACL, DACL, Owner
  • Copy file attributes and timestamps
  • Same option set as RoboCopy (not completely implemented)
  • Can be extended because it’s open source
  • Customizable
  • Pure PowerShell with some inline C# code
  • Compare file times as UTC
  • Modify it on any Windows Operation System >= Win XP / Windows 2003 Server with Microsofts PowerShell development tol Windows PowerShell Integrated Scripting Environment that is part of PowerShell.

These are the limitations:

  • No multithreading support
  • No “Job” files at the moment
  • Slower than RoboCopy (Because RoboPowerCopy is Powershell and/based on Managed Code but RoboCopy is native optimized code…)

The following options I have implemented (v0.0.1.0). There are some options that are not available in RoboCopy. These I’ve marked red.


since Version

/ACopy only files that have the Archive attribute set

/MRemoves Archive attribute of source file.

/A+:[HRAS]Add this file attributes to destination file

/A-:[HRAS]Remove this file attributes from destination file

/ECopy subfolder, including empty

/SCopy subfolders that are not empty. – “Not empty” means: at least one file is exists and is not excluded by any exclude option

/PURGERemove files on destination that does not exist on source

/MIRMirror the directory tree, like “/PURGE” combined with “/E”

/COPYALLCopy all file info: Timestamps, Attributes, Data, Owner, DACL, SACL

/COPY:[DATSOU]Copy selected file info: D=Data, A=Attributes, T=Timestamps, S=DACL, U=SACL, O=Owner

/DCOPY:[T]Copy selected directory info: T=Timestamps

/CREATEDoes only create the directory structure. Files will have zero size.

/SECCopy files with security. Like “/COPY:DATS”

/LEV:nCopy only n directory levels. 1=Copy only the given root directory.

/ISCopy even destination file seems to be the same as the source file.

/RCBWDuring overwrite this methos reads a file portion of data of the destination file and compares them with the source files portion of data before writing it. This my be useful while copying large files that have only few changes.

/VERIFYReread written data and compare them to the source data.

/SECFIXReplaces the security info of all destination files even if they are equal to the source.

/TIMFIXReplaces the timestamps of all destination files even if they are equal to the source.

/MAX:nMax file size to copy (in bytes)

/MIN:nMin file size to copy (in bytes)

/MAXAGEUsed to specify maximum file age based upon “LastWriteTime”. That will exclude files older than n days/date.
(If n < 1900 then n = no of days, else n = YYYYMMDD date).

/MINAGEUsed to specify minimum file age based upon “LastWriteTime”. That will exclude files newer than n days/date.
(If n < 1900 then n = no of days, else n = YYYYMMDD date).

/MAXLADUsed to specify maximum file age based upon “LastAccessTime”. That will exclude files unused since n days/date.
(If n < 1900 then n = no of days, else n = YYYYMMDD date).

/MINLADUsed to specify minimum file age based upon “LastAccessTime”. That will exclude files used since n days/date.
(If n < 1900 then n = no of days, else n = YYYYMMDD date).

/XD:<directory> [<directory> […]]Exclude this directories. Wildcards can be used for name matching.

/XF:<file> [<file> […]]Exclude this files. Wildcards can be used.

/XJExclude Junction Points, Symbolic Links (directories), Hard Links (files)

/XJDExclude Junction Points, Symbolic Links (directories)

/XJFExclude Hard Links (files)

/VDetailed output.

/VV[+][:<file>]Verbose output. If no file name is specified the messages will be written to the Powershell host using “write-verbose”

/CHUNK:nSize of the size of a portion of data during copy. In bytes. Default: 10mb.

/ZRestartable mode. A “copy header” will be used at the end of each file during the copy process. The header will be removed after copy is finished.

/BYTESShows file sizes in bytes

/R:nMaximum retry count if copy fails

/W:nWait duration before next retry. (In seconds)

/256Disable Long Path Support. This will use standard .NET classes (FileInfo, DirectoryInfo) instead of the special classes for file and directory names longer than 256 characters.

/NPNo progress – does not display “%”…

Using this option (except the red once) with RoboPowerCopy and RoboCopy should have the same result in both tools.

This features of RoboCopy does not work in RoboPowerCopy in v0.1.0.0:

  • Copy directory attributes
  • Set “Encrypted” attribute on destination files
  • Set “Compressed” attribute on destination files
  • …some more

Once more: It’s “ALPHA” in v0.1.0.0! – Therefore I want to ask for your help! – Please help to improve RoboPowerCopy. The project needs people how test and develop the script.

Please contact me at the projects Codeplex page if you would to spend your spare time for RoboPowerCopy.

Enumerate, add, update and remove SQL Server aliases by using PowerShell

I’ve created a PowerShell script for adding, updating, enumerating und removing SQL Server aliases. – You can use the script to create/remove/enumerate both 32bit und 64bit aliases in one step or only 32bit or only 64bit aliases.

You can change this script into a PowerShell module. Just see the comment below inside the script.

I’ve tested the script on Windows Server 2008 R2 with SQL Server 2008 R2 on it. – Please post your comments how it works on other systems.

Here is the script:

#region Author: Ingo Karstein / Blog:

SharePoint Warm Up with PowerShell

Every SharePoint farm needs a warm up Smile – After giving the IIS 7.5 Application Warmup a chance… I had to do it in another way…

I created a warmup script in PowerShell.

Here it is…

[Update 2011/08/03] New script with “timeout” capability:


$urls= @("http://sharepoint.local", "http://anothersharepoint.local")

New-EventLog -LogName "Application" -Source "SharePoint Warmup Script" -ErrorAction SilentlyContinue | Out-Null

$urls | % {
    $url = $_
    try {
        $wc = New-Object System.Net.WebClient
        $wc.Credentials = [System.Net.CredentialCache]::DefaultCredentials
        $ret = $wc.DownloadString($url)
        if( $ret.Length -gt 0 ) {
            $s = "Last run successful for url ""$($url)"": $([DateTime]::Now.ToString('yyyy.dd.MM HH:mm:ss'))" 
            $filename=((Split-Path ($MyInvocation.MyCommand.Path))+"lastrunlog.txt")
            if( Test-Path $filename -PathType Leaf ) {
                $c = Get-Content $filename
                $cl = $c -split '`n'
                $s = ((@($s) + $cl) | select -First 200)
            Out-File -InputObject ($s -join "`r`n") -FilePath $filename
    } catch {
          Write-EventLog -Source "SharePoint Warmup Script"  -Category 0 -ComputerName "." -EntryType Error -LogName "Application" `
            -Message "SharePoint Warmup failed for url ""$($url)""." -EventId 1001

        $s = "Last run failed for url ""$($url)"": $([DateTime]::Now.ToString('yyyy.dd.MM HH:mm:ss')) : $($_.Exception.Message)" 
        $filename=((Split-Path ($MyInvocation.MyCommand.Path))+"lastrunlog.txt")
        if( Test-Path $filename -PathType Leaf ) {
          $c = Get-Content $filename
          $cl = $c -split '`n'
          $s = ((@($s) + $cl) | select -First 200)
        Out-File -InputObject ($s -join "`r`n") -FilePath $filename

It will write a log file to the same directory as the script itself is stored.

Exceptions will be written to the event log!

Run it as “Scheduled Task” with the farm account credentials. – Check “Run with Highest Priviledges” !!!

Be sure this account can write to the scripts directory (for logging purpose)!

You can use this XML file for quick importing the scheduled task defintion. Therefore the script must be placed in the folder “C:Program FilesSharePoint Warmup”. The script itself must be named “sharepointwarmup.ps1”.

 <?xml version="1.0" encoding="utf-16"?>
 <Task version="1.2" xmlns="">
     <Principal id="Author">
   <Actions Context="Author">
       <Arguments>-command ".\SharePointWarmup.ps1"</Arguments>
       <WorkingDirectory>C:\Program Files\SharePoint Warmup</WorkingDirectory>

1. Copy this XML code to a file, then open the “Scheduled Tasks” management console of Windows.

2. Open the “Task Scheduler Library”, right-click on it’s node. Choose “Import”.

3. Then select the XML file.

4. In the task edit dialog: Change the execution account.

5. Save the task.

6. Now enter the password of the execution account in the credential dialog.

7. Now right-click on the created scheduled task.

8. Choose “Run” to test the task.

9. Have a look into the scripts folder in file system. there must be a file named “lastrunlog.txt” – This file will contain at most 200 lines of loggin informations!

The definitive guide of How to configure the SharePoint 2010 User Profile Service Application

I took me hours and hours to get to User Profile Service Application working.

These are the steps I’ve done in some scenarios.

Especial to migrate an SharePoint 2007 profile database to SharePoint 2010.

0. The databases for the User Profile Service Application must run on the standard instance of SQL Server. NEVER use a named instance. – Always use SQL Aliases instead!!!

1. (Skip this next step if you don’t need to migrate.) Backup the Shared Service Provider Database of your MOSS farm.

2. Restore the farm to your destination SQL Server instance and with the destination database name, e.g. “SP_SvcApp_UserProfile_Profiles”.

(In the MOSS farm the database was named “MOSS_SSP_Config” !!! –> It’s the configuration database of the Shared Service Provider.)

3. Create the User Profile Service Application with this PowerShell script:

#region Check x64 host
if( [System.IntPtr]::Size -ne 8) {
  Write-Error "Please use a x64 PowerShell host!"

#region Load SharePoint SnapIn and DLL
  Remove-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue
  Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue

  [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")  | out-null    

  #Check available SharePoint Cmdlets
  if( (Get-Command -Noun SPWeb*) -eq $null ) {
    Write-Error "SharePoint SnapIn not loaded. SharePoint cmdlets missing!"



# Profile database in my MOSS farm : MOSS_SSP_CONFIG 

# Settings

$farmname = "SP"

$mysiteHostLocation = "http://mysite.sharepoint.local"
$mysiteManagedPath = "/personal"


$spfarmcredentials = new-object -typename System.Management.Automation.PSCredential -argumentlist $spfarmuser, (ConvertTo-SecureString $spfarmpwd -AsPlainText -force)
$userprofileAppProfileDBName =("{0}_SvcApp_UserProfile_Profiles" -f $farmname)
$userprofileAppProfileSyncDBName =("{0}_SvcApp_UserProfile_Sync" -f $farmname)
$userprofileAppProfileSocialDBName =("{0}_SvcApp_UserProfile_Social" -f $farmname)
$svcAppPool=("{0}_AppPool_UserProfile" -f $farmname)

# Create application pool
$appPool = (Get-SPServiceApplicationPool -Identity $svcAppPool -ErrorAction SilentlyContinue)
if( $appPool -eq $null ) {
  $appPool = (New-SPServiceApplicationPool -Account $spfarmuser -Name $svcAppPool)

$userProfileSvcApp = (Get-SPServiceApplication  | ? { $_.TypeName -eq "User Profile Service Application" })

# Create service application
if( $userProfileSvcApp -eq $null ) {
  $userProfileSvcApp = (New-SPProfileServiceApplication -ApplicationPool $appPool -MySiteHostLocation  $mysiteHostLocation `
                          -MySiteManagedPath $mysiteManagedPath -ProfileDBName $userprofileAppProfileDBName `
                          -ProfileSyncDBName $userprofileAppProfileSyncDBName -SocialDBName $userprofileAppProfileSocialDBName `
                          -Name "User Profile Service Application" -SiteNamingConflictResolution "None" -Verbose)

# Create application proxy
$proxy = (Get-SPServiceApplicationProxy | ? { $_.Name -like "User Profile Service Application Proxy" } )
if( $proxy -eq $null ) {
  $proxy = (New-SPProfileServiceApplicationProxy -DefaultProxyGroup -Name "User Profile Service Application Proxy" -ServiceApplication $userProfileSvcApp)


During the creation of the User PRofile Service Application the MOSS database will be migrated!!

If you did no restore previously the profile database will be created in this step.

4. The farm account must be local admin on the farm server.

5. Go into the SQL Server Management Studio.

6. Set the farm account – in my example “sp_farm” – as SYSADMIN in your SQL Server instance.

7. Edit the User Logins for the (migrated) profile database.

The farm account should exist as login. – If not: add the account as “db_owner”.

Important: Set the schema for the farm account to “dbo”!

8. In the Windows Services Manager (Server Manager): restart a services that has the farm account as identity. OR restart the server!

9. Now: Log on locally with the farm account!! – Yes: logon with the farm account!

10. As farm account: go into the Central Administration –> Manage Services on Server –> Start the User Profile Synchronization service!

11. WAIT!

12. Press F5 on the “Managa Services on Server” page. Maybe wait some more Smile. Have a look into the the SharePoint log. You should find lots of message. Search for “ILM” (using ULSViewer of course).

13. Sometimes – if you are a lucky person – the “User Profile Synchronization” service is startet. – Maybe not. In this case: Start over! – Last time it took me three times to get it working!

14. At the end: remove the farm account from the SYSADMIN group!

[Update / 01/26/2011]

15. Don’t forget to reset the Security Settings (“Administrators”) for the User Profile Service Application after you recreated them. – Go into the Central Administration -> Manage Service Applications -> select your User Profile Service Application -> click “Administrators” in the Ribbon. – You may find this empty!!! This is not correct. Insert here at least the farm account with “Full Control” permissions. And don’t forget to insert the search crawl account with “Retrieve People Data for Search Crawlers” permissions. Otherwise your People Search will not work!

Please give me feedback on this! – The configuration of the User Profile Services Application is a incredible mess.