Friday, March 26, 2010

Automate SharePoint 2010 Farm Backups with Powershell

***Update (01/07/2013): We recently noticed that installing KB2506143 (Windows Management Framework 3.0) breaks the ability for SharePoint Powershell to run the backup. Uninstall it and the script should start working again.***

We've had a lot of requests for more details on how to setup automated SharePoint backups with PowerShell, so we've gone ahead and created a detailed ebook which outlines the steps and permissions you need to get your backups up and running. In addition to screen shots, there are also copies of the scripts we use as well as an email notification section that will let you know if your backups failed. Simply click on the Buy Now button below and you can pay with your Paypal account or credit card. At only $9.99 USD, it's thousands cheaper than a 3rd party solution and will let you sleep a little sounder at night. If you're not satisfied, we'll gladly refund your money! For the basic steps minus some of the more advanced funtionality, see below.


I recently built several server farms for our developers to work on. In order to make sure I could restore the farms to their original condition, I setup the following automated backup process. Below is a brief outline of the steps.

1. Create a folder on a local drive of the SharePoint 2010 server called backups (E:\backups). Share that folder as "backups" and give the account you used to install SharePoint as well as the farm and SQL database accounts full access (share permissions and NTFS).

2. Create a folder in E:\backups called Scripts. Inside there you create 4 files:

backupsharepointfarm.ps1 – This script will backup your entire farm to the share you created. This script will contain the following:

Add-PsSnapin Microsoft.SharePoint.Powershell
Backup-SPFarm -Directory \\ServerName\Backups -BackupMethod full

cleanbackups.ps1 – This script will check the spbrtoc.xml file and delete backups older than 7 days so you don’t run out of disk space. You can change $days value. This script will contain the following:

# Location of spbrtoc.xml
$spbrtoc = "E:\Backups\spbrtoc.xml"

# Days of backup that will be remaining after backup cleanup.
$days = 7

# Import the Sharepoint backup report xml file
[xml]$sp = gc $spbrtoc

# Find the old backups in spbrtoc.xml
$old = $sp.SPBackupRestoreHistory.SPHistoryObject |
? { $_.SPStartTime -lt ((get-date).adddays(-$days)) }
if ($old -eq $Null) { write-host "No reports of backups older than $days days found in spbrtoc.xml.`nspbrtoc.xml isn't changed and no files are removed.`n" ; break}

# Delete the old backups from the Sharepoint backup report xml file
$old | % { $sp.SPBackupRestoreHistory.RemoveChild($_) }

# Delete the physical folders in which the old backups were located
$old | % { Remove-Item $_.SPBackupDirectory -Recurse }

# Save the new Sharepoint backup report xml file
$sp.Save($spbrtoc)
Write-host "Backup(s) entries older than $days days are removed from spbrtoc.xml and harddisc."

Backup.bat – This is a simple batch file to run the above backupsharepointfarm.ps1 script. Create a scheduled task to run it every night. The file contains the following:

powershell -command E:\Backups\Script\BackupSharePointFarm.ps1

Clean.bat – This batch file will run the script to clean out older backup files (cleanbackup.ps1). The file contains the following:

powershell -command E:\Backups\Script\cleanbackups.ps1

3. Create a scheduled task that will run both of the .bat files you created and set them to run at night when no one’s around. You have to make sure the scheduled tasks are set to run with the SharePoint farm account. After a full week you’ll have a directory with 7 days worth of backups similar to the following:







Note: In order to run PowerShell commands on your server, you need to open powershell and execute the following command: Set-ExecutionPolicy Unrestricted

There are several factors involved in running a successful scripted backup which are covered by Todd Klindt in the following Microsoft SharePoint forum thread: http://social.technet.microsoft.com/Forums/en-US/sharepoint2010setup/thread/f755e7c1-bd0a-4c00-9be6-bbca83cf666b/.

1.Your Central Admin app pool account must have read/write access to the location of the backups.

2.Your SQL Service account must have read/write access to the location of the backups.

3.If you're running a farm backup from STSADM or Windows PowerShell, the account you're running it as must have read/write access to the location of the backups

4.The location must be accessible from the SharePoint machine the backup is running on.

5.The location must be accessible from the SQL instance that SharePoint is trying to back up.

6.This is why all the examples are UNCs, \\server\share, and not local paths, C:\backups

That’s about it. Give it a try and let me know if you have any problems. The file and share permissions are critical!


41 comments:

Alex said...

Hi,

maybe you could help me...

I get the error back:
Exception calling "RemoveChild" with "1" argument(s): "Object reference not set
to an instance of an object."
At C:\Scripts\cleanspbackup.ps1:21 char:43
+ % { $sp.SPBackupRestoreHistory.RemoveChild( <<<< $_) }

and also
Remove-Item : Cannot bind argument to parameter 'Path' because it is null.
At C:\Scripts\cleanspbackup.ps1:26 char:16
+ % { Remove-Item <<<< $_.SPBackupDirectory -Recurse }


do you had this problem as well?

thanks
Alex

Imperfect IT said...

Hi Alex,

I suspect the error is the result of the script not finding any backups older than 14 days (or however long you told it to go back). Once you have backup folders older than the time you specified the error should stop.

Let me know how it goes.

dcox said...

Nice site. Thanks you so much.

Trond said...

I get the same 2 error messages as Alex above:

-----
Exception calling "RemoveChild" with "1" argument(s): "Object reference not set
to an instance of an object."
At C:\Backup\cleanbackups.ps1:21 char:43
+ % { $sp.SPBackupRestoreHistory.RemoveChild <<<< ($_) }
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

SPId : 8995d589-aa2f-4b3d-8893-1023f1e1b193
-----

-----
Remove-Item : Cannot bind argument to parameter 'Path' because it is null.
At C:\Backup\cleanbackups.ps1:26 char:16
+ % { Remove-Item <<<< $_.SPBackupDirectory -Recurse }
+ CategoryInfo : InvalidData: (:) [Remove-Item], ParameterBinding
ValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,M
icrosoft.PowerShell.Commands.RemoveItemCommand

Backup(s) entries older than 2 days are removed from spbrtoc.xml and harddisc.
-----

I try to run the script as Farm account who has full access to the backup directory without success. The backup directory have 1 week old backups (1 per day), but I get the error messages even if I put $days = 1.

Please advice.

Thanks, Trond

Trond said...

I've found that the pipe ("|") is missing in this command above, but that doesn't help on my problem:

$old = $sp.SPBackupRestoreHistory.SPHistoryObject | ? { $_.SPStartTime -lt ((get-date).adddays(-$days)) }

Here is my problem:

PS C:\backup>
PS C:\backup> $old


SPId : 5bbe9f43-8b98-42c0-a931-fa2e64d14af9
SPRestoreId : 2b69e94b-0341-4586-a64f-a9783af58606
SPRequestedBy : ITPM\sp-admin-tst
SPBackupMethod : Full
SPRestoreMethod : Overwrite
SPStartTime : 06/16/2010 14:15:30
SPFinishTime : 06/16/2010 14:17:48
SPIsBackup : False
SPConfigurationOnly : False
SPBackupDirectory : \\ITPM-SP-TST\Backup_SP\spbr0006\
SPDirectoryName :
SPTopComponent : Farm\Microsoft SharePoint Foundation Web Application\Shar
ePoint - test.corp.itpm80
SPTopComponentId : d09b503b-96f5-49a6-9dbf-3ee1fa7604d0
SPWarningCount : 0
SPErrorCount : 0

SPId : 5bbe9f43-8b98-42c0-a931-fa2e64d14af9
SPRequestedBy : ITPM\sp-admin-tst
SPBackupMethod : Full
SPRestoreMethod : None
SPStartTime : 06/16/2010 13:14:46
SPFinishTime : 06/16/2010 13:41:44
SPIsBackup : True
SPConfigurationOnly : False
SPBackupDirectory : \\ITPM-SP-TST\Backup_SP\spbr0006\
SPDirectoryName : spbr0006
SPDirectoryNumber : 6
SPTopComponent : Farm
SPTopComponentId : 25933671-7acd-420e-adf9-ce8e6ae8980e
SPWarningCount : 0
SPErrorCount : 0

SPId : 7a418010-0df6-4dd5-965d-287445b6566b
SPRequestedBy : ITPM\sp-admin-tst
SPBackupMethod : Full
SPRestoreMethod : None
SPStartTime : 06/16/2010 11:16:30
SPFinishTime : 06/16/2010 11:46:53
SPIsBackup : True
SPConfigurationOnly : False
SPBackupDirectory : \\ITPM-SP-TST\Backup_SP\spbr0005\
SPDirectoryName : spbr0005
SPDirectoryNumber : 5
SPTopComponent : Farm
SPTopComponentId : 25933671-7acd-420e-adf9-ce8e6ae8980e
SPWarningCount : 0
SPErrorCount : 0

SPId : b25e4e9c-8485-4411-9d34-6855b6d90202
SPRequestedBy : ITPM\sp-admin-tst
SPBackupMethod : Full
SPRestoreMethod : None
SPStartTime : 06/16/2010 09:59:02
SPFinishTime : 06/16/2010 10:28:05
SPIsBackup : True
SPConfigurationOnly : False
SPBackupDirectory : \\ITPM-SP-TST\Backup_SP\spbr0004\
SPDirectoryName : spbr0004
SPDirectoryNumber : 4
SPTopComponent : Farm
SPTopComponentId : 25933671-7acd-420e-adf9-ce8e6ae8980e
SPWarningCount : 0
SPErrorCount : 0

SPId : 846a61ca-470b-4f11-914c-2cb7bfa630e4
SPRequestedBy : ITPM\sp-admin-tst
SPBackupMethod : Full
SPRestoreMethod : None
SPStartTime : 06/16/2010 09:17:52
SPFinishTime : 06/16/2010 09:45:17
SPIsBackup : True
SPConfigurationOnly : False
SPBackupDirectory : \\ITPM-SP-TST\Backup_SP\spbr0003\
SPDirectoryName : spbr0003
SPDirectoryNumber : 3
SPTopComponent : Farm
SPTopComponentId : 25933671-7acd-420e-adf9-ce8e6ae8980e
SPWarningCount : 0
SPErrorCount : 0


PS C:\backup> % { $sp.SPBackupRestoreHistory.RemoveChild($_) }
Exception calling "RemoveChild" with "1" argument(s): "Object reference not set
to an instance of an object."
At line:1 char:43
+ % { $sp.SPBackupRestoreHistory.RemoveChild <<<< ($_) }
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

PS C:\backup>

Anyone understanding what's wrong here?

Thanks, Trond

Trond said...

Hello again,

I found the solution myself now, there are 2 more missing pipes in the example above ("|"):


# Delete the old backups from the Sharepoint backup report xml file
$old | % { $sp.SPBackupRestoreHistory.RemoveChild($_) }


# Delete the physical folders in which the old backups were located
$old | % { Remove-Item $_.SPBackupDirectory -Recurse }


Thanks to this webpage: http://blog.wauwwie.nl/default.aspx

Problem solved! ;-)

Regards Trond

Imperfect IT said...

Thanks Trond! The pipes must have gone missing when I did the cut and paste. I've fixed the scripts.

Alex crvc said...

Hi,

I have a problem with batch file that creates backup just blink on the screen and disappear. The problem is cause because of need to Add-PsSnapin Microsoft.SharePoint.Powershell.
This is include in backupsharepointfarm.ps1 script but it doesn't work. If anyone have solution please wrote it for me.

Thanks

Anonymous said...

Hi, Thanks for this post!

Is it possible to set the destination of the backups to be a share on a different machine if appropriate permissions are given?

Imperfect IT said...

Hi Alex,

You may want to have a look at the following sites:

http://social.technet.microsoft.com/Forums/en-US/sharepoint2010setup/thread/fcb77654-0f13-42e0-b181-6e52242fe9d6

And

http://msdn.microsoft.com/en-us/library/ee537913.aspx

Imperfect IT said...

Anonymous,

You can definitely write backups to a UNC. I created a share and set the share permissions and security permissions and it works like a charm.

BTC Solutions Development Team said...

If you get the object reference error, it is because you haven't set the sql server permissions properly - for more explanation see http://stackoverflow.com/questions/3889063/backup-sharepoint-2010-using-powershell-backup-spfarm

BTC Solutions Development Team said...

I had an issue with the line that compares the date and had to change it to this:

# Find the old backups in spbrtoc.xml
$old = $sp.SPBackupRestoreHistory.SPHistoryObject |
? { ( (
[datetime]::ParseExact($_.SPStartTime, "MM/dd/yyyy HH:mm:ss", [System.Globalization.CultureInfo]::InvariantCulture)
) -lt (get-date).adddays(-$days)
)
}

ddod said...

Any thoughts on which user to use when scheduling these scripts. I have read that you should use your farm account. But the account needs to have local admin privileges. Microsoft does not recommend giving the farm account user this privilege.

Kaléu said...

Hi there.

The backup code worked, but the clean backup script just works if i use powershell manually. It gives me last run result (0xFFFD0000) if I try to schedule with Task Scheduler. Any ideias?

Thx.

Imperfect IT said...

Hi Kaleu,

Please make sure that the account used to run the Cleanup task is the install account (not the farm account). Also, make sure the farm, install and SQL database accounts have full security and share permissions on the Backup folder where the backups and spbrtoc.xml file are.

Regards

Kaléu said...

Hi IIT.

Thx for the answer. Unfortunatelly, I don't think it's a permission issue, cause I use the same account of the backup task and the backup works like a charm.

I'm still stucked on that :-(. Any more ideas?

Thanks again.

Anonymous said...

thanks for the great article , what if i need to deploy full and then differential backup ? would you please guide me to do that ?

regards,

Vladimir Sotirov said...

Thank you for the nice post. I combined the two scripts into one and added email notification on error.
#Clear the error object and set it to SilentlyContinue
$error.clear()
$erroractionpreference = "SilentlyContinue"

#Add The SharePoint Powershell
Add-PsSnapin Microsoft.SharePoint.Powershell | Out-File -FilePath C:\Scripts\backupsharepointfarm.log -Append

#Run the Backup farm command
Backup-SPFarm -Directory \\ServerName\Backups -BackupMethod full | Out-File -FilePath C:\Scripts\backupsharepointfarm.log -Append

# Location of spbrtoc.xml
$spbrtoc = "\\ServerName\Backups\spbrtoc.xml"

# Days of backup that will be remaining after backup cleanup.
$days = 3

# Import the Sharepoint backup report xml file
[xml]$sp = gc $spbrtoc | Out-File -FilePath C:\Scripts\backupsharepointfarm.log -Append

# Find the old backups in spbrtoc.xml
$old = $sp.SPBackupRestoreHistory.SPHistoryObject | ? { $_.SPStartTime -lt ((get-date).adddays(-$days)) } | Out-File -FilePath C:\Scripts\backupsharepointfarm.log -Append
if ($old -eq $Null)
{
Write-Output "No reports of backups older than $days days found in spbrtoc.xml.`nspbrtoc.xml isn't changed and no files are removed.`n" | Out-File -FilePath C:\Scripts\backupsharepointfarm.log -Append
}
else
{
# Delete the old backups from the Sharepoint backup report xml file
$old | % { $sp.SPBackupRestoreHistory.RemoveChild($_) } | Out-File -FilePath C:\Scripts\backupsharepointfarm.log -Append

# Delete the physical folders in which the old backups were located
$old | % { Remove-Item $_.SPBackupDirectory -Recurse } | Out-File -FilePath C:\Scripts\backupsharepointfarm.log -Append

# Save the new Sharepoint backup report xml file
$sp.Save($spbrtoc) | Out-File -FilePath C:\Scripts\backupsharepointfarm.log -Append
Write-Output "Backup(s) entries older than $days days are removed from spbrtoc.xml and harddisc." | Out-File -FilePath C:\Scripts\backupsharepointfarm.log -Append
}

#Check if any error has occured and send an alert email if it did.
if ($error[0])
{
Write-Output "An error has occurred" | Out-File -FilePath C:\Scripts\backupsharepointfarm.log -Append;
for ($i=0; $i -le $error.count; $i++) {Write-Output $error[$i] | Out-File -FilePath C:\Scripts\backupsharepointfarm.log -Append}

###First, the administrator must change the mail message values in this section
$FromAddress = "SharepointBackup@yourcompany.com"
$ToAddress = "alerts@yourcompany.com"
$MessageSubject = "Sharepoint backup failed."
$MessageBody = "The Sharepoint Server backup Failed. Please check."
$SendingServer = "mail.yourcompany.com"

###Create the mail message
$SMTPMessage = New-Object System.Net.Mail.MailMessage $FromAddress, $ToAddress, $MessageSubject, $MessageBody

###Send the message
$SMTPClient = New-Object System.Net.Mail.SMTPClient $SendingServer
$SMTPClient.Send($SMTPMessage)

}

Emmanuel ISSALY said...

Great and nice article, now any idea about catching an error and sending a mail if it occurs? should be easy with powershell :)

Anonymous said...

works perfectly, Thanks.

just one add, that be sure that that user who will be running the task schedule can run powershell commands. for this just run the .bat script in shell manually and see if it gives errors or not.

Thanks.

bickb8 said...

Worked as advertised. Thanks!

rckymtnrider said...

Just set up the backupsharepointfarm.ps1 on my new server. The backup works fine. However the script does not exit, either when run within Powershell or from the backup.bat file created. I only modified the "Backup-SPFarm - Direcotory..." line.

Any ideas?

Thanks for the blog.

Jeff

chris.gump said...

First off I want to say thank you for posting this write up for this task, it is greatly appreciated for a beginner administrator like myself.

So the back up portion is working with out flaw, however when I run the cleanup I am getting a error that I am not sure what to fix. I have copied and pasted the script from here and I have also retyped it char for char and I get the same error either way.

Missing expression after unary operator '-'.
At line:1 char:2

+ - <<<< noclue c:\sharepointbackup\scripts\sharepointcleanupscript.ps1
+CategoryInfo : ParserError: (-:Sting) [], ParentContainsErrorRecordException
+FullyQualifiedErrorId : MissingExpresionAfterOperator

Any advice or guidance on this issue would be greatly appreciated.

Thanks much
Chris

Imperfect IT said...

Sounds like a problem with you script. Here's a copy of one of the ones we're using right now:

# Location of spbrtoc.xml
$spbrtoc = "\\networkshare\spbrtoc.xml"

# Days of backup that will be remaining after backup cleanup.
$days = 3

# Import the Sharepoint backup report xml file
[xml]$sp = gc $spbrtoc

# Find the old backups in spbrtoc.xml
$old = $sp.SPBackupRestoreHistory.SPHistoryObject |
? { $_.SPStartTime -lt ((get-date).adddays(-$days)) }
if ($old -eq $Null) { write-host "No reports of backups older than $days days found in spbrtoc.xml.`nspbrtoc.xml isn't changed and no files are removed.`n" ; break}

# Delete the old backups from the Sharepoint backup report xml file
$old | % { $sp.SPBackupRestoreHistory.RemoveChild($_) }

# Delete the physical folders in which the old backups were located
$old | % { Remove-Item $_.SPBackupDirectory -Recurse }

# Save the new Sharepoint backup report xml file
$sp.Save($spbrtoc)
Write-host "Backup(s) entries older than $days days are removed from spbrtoc.xml and harddisc."

Anonymous said...

Thanks Imp!

Worked flawlessly first run!

VTQnetops!

Carol Chisholm said...

I'm having problems with the $old variable always being null.
I'm in Europe so my date formats are different.

Craig Liess said...

This has been working flawless for us for 6 months.. and now that it's 2013, the backup script is deleting EVERYTHING in the folder and stripping virtually everything out of spbrtoc.xml except:





Anyone else run into this?

-Craig

Imperfect IT said...

We recently noticed that installing KB2506143 (Windows Management Framework 3.0) breaks the ability for SharePoint Powershell to run the backup. Uninstall it and the script should start working again.

Craig Liess said...

The Backup itself is working like a champ for me, but the cleanup for whatever reason, is not. Imperfect IT, any idea what may cause the cleanup to delete the entire contents of the backup folder? We're using the exact scripts above, and they've been working perfectly for months!

DubAhU said...

Having the same symptoms as Craig. I don't have KB2506143 installed. Both the backup and cleanup scripts seem to run fine if I execute them myself. The madness seems to happen when the scheduled tasks run, which is when I'm not here. Overnight, the backup I manually created yesterday was deleted. When I just ran a backup and then the delete job, it functions normal (backup happened, delete job told me there were no backups older than 7 days). I'm going to schedule them to run now, and see what happens.

DubAhU said...

Well I could make no rhyme or reason for it. Same thing happened on my production and development farms. I simply deleted the spbrtoc.xml file and let it recreate with a new backup. That seems to have done the trick in both environments. Nothing I could come up with to why this happened other than the year change and me happening to notice it because of a larger than expected amount of free space on the drive.

NWBlue said...

Hi
The backup is running perfectly but I'm having problems with the clean backup. I am using the latest version of the script but get the following errors.


Exception calling "RemoveChild" with "1" argument(s): "Object reference not set
to an instance of an object."
At line:1 char:50
+ $old | % { $sp.SPBackupRestoreHistory.RemoveChild <<<< ($_) }
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException

and

Remove-Item : Cannot bind argument to parameter 'Path' because it is null.
At line:1 char:23
+ $old | % { Remove-Item <<<< $_.SPBackupDirectory -Recurse }
+ CategoryInfo : InvalidData: (:) [Remove-Item], ParameterBinding
ValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,M
icrosoft.PowerShell.Commands.RemoveItemCommand

all help gratefully received.

Brian Fischer said...
This comment has been removed by the author.
Brian Fischer said...

I'm having the same issue as Craig Liess above. I think the problem stems from the script not differentiating the year? I had backups in Nov, Dec, and Jan. I told the script to delete anything beyond 75 days. The only backups it kept were the ones in late Nov and all of Dec (anything beyond 75 days were indeed deleted). However, the most recent Jan backups were deleted. I'm guessing the script screwed up on the year and thought that the recent Jan backups were from last year.

I'm going to test and see if the script adjustment to the date compare that BTC Solutions Development Team posted here on Jan 24, 2011 will fix the issue

Brian Fischer said...

Looks like the above script by BTC Solutions Development Team works but he forgot a line to handle the return of Null items.

if ($old -eq $Null) { write-host "No reports of backups older than $days days found in spbrtoc.xml.`nspbrtoc.xml isn't changed and no files are removed.`n" ; break}

I found this info at this address:
http://stackoverflow.com/questions/4627391/how-to-correct-this-powershell-script-that-auto-deletes-sharepoint-backups

Anonymous said...

When i run the clean.bat, i get the following error:
Get-Content : Access to the path 'd:\Backup\Farm' is denied.
At C:\Scripts\CleanFarmBackup.ps1:8 char:12
+ [xml]$sp = gc $spbrtoc
+ ~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (D:\Backup\Farm:String) [Get-Content], UnauthorizedAccessException
+ FullyQualifiedErrorId : GetContentReaderUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetContentCommand

Any ideas how to solve this?

Carl said...

Anyone using this with SharePoint 2013? I suppose I could pay for it, set it all up and try it, but it would be nice to know someone else had success with it first.

Thanks!

Dan@Desenv said...

I'm having a error when the task will run on the scheduled time. The issue it's about admin privilegies because when i run the task logged in everything works fine. Any help would be great!

Kurt Zimmerman said...

Good day... I found your PS scripts several months ago and will honestly say all was working until the end of the year hit. What I'm finding is the backup works just fine however the purge process to maintain the 7 days of backups appears not to be working correctly. I have found that it is not only purging off the > 7 day old backups, it is also purging off the current backup. For now I have disabled the purge process until I can find a solution. I am wondering if anyone else has reported this problem.

Kurt Zimmerman

Kurt Zimmerman said...

Greetings.... Yesterday I posted a comment where by I discovered there was a bug in your script. Turns out that when comparing the date from the XML file (SPStartTime) it is doing a string compare and not a date compare. I've fixed the syntax to resolve this problem. The problem arose when crossing over into a new year. (less than 7 days into the new year). Because of the string compare the current backup was being purge along with the oldest. I corrected the script by changing this line:

$old = $sp.SPBackupRestoreHistory.SPHistoryObject |
? { [DateTime]::Parse($_.SPStartTime) -lt ((get-date).adddays(-$days)) }

I've tested this and it works.

HNY
Kurt Zimmerman
Sr. DBA
Lefrak Organization