Skip to content

PowerShellPlusRobocopy

candera edited this page Jan 21, 2012 · 1 revision
###########################################################################################
# Backup PST files using ShadowSpawn/Robocopy
# Created 2011-05-09 - Benjamin Meis
# Updated 2011-11-03 - Benjamin Meis
# This script requires the following directory structure on where the script is hosted:
# \
# --PST_Backup_Script.ps1
# --Tools\
#        --robocopy.exe (xp version)
#        --vcredist-2010_x64.exe
#        --vcredist-2010_x64.exe
# --ShadowSpawn_winxp\
#        --ShadowSpawn.exe
#        --ShadowSpawn.pdb
# --ShadowSpawn_win7\
#        --64-bit\
#                --ShadowSpawn.exe
#                --ShadowSpawn.pdb
#        --32-bit\
#                --ShadowSpawn.exe
#                --ShadowSpawn.pdb
# Note: Execution policy on your machine must be set to allow the running of Powershell Scripts
#    e.g. Set-ExecutionPolicy Unrestricted -Force (your own security policies may not allow for unrestricted, please be sure to check with your sysadmin)
###########################################################################################
# Initiate variables
$curDate = $(Get-Date -UFormat "%Y%m%d")
$companyDir = "some_local_directory"
$logDir = "$companyDir\Logs\PST_Backups"
if (!(Test-Path -PathType Container -Path $logDir)) {New-Item -Path $logDir -ItemType Container}
Start-Transcript -Path "$logDir\PST_Backup_Log_$curDate.txt"
Write-Host "$(Get-Date -format g) Beginning PST Backup `r"
Write-Host "$(Get-Date -format g) Setting Variables `r"
$wmiWinVer = (Get-WmiObject Win32_OperatingSystem).Caption
$wmiWinArch = (Get-WmiObject Win32_OperatingSystem).OSArchitecture
$backupServer = "server_for_pst_backups"
$backupShare = "share_for_pst_backups"
$scrSvr = "server_for_script"
$scrShare = "share_for_script"
$scrFolder = "script_folder_on_share"
$scriptDir="\\$scrSvr\$scrShare\$scrFolder"
$compName = $env:computername
$winVer = "" #Left blank intentionally
$userDirs = "" #Left blank intentionally
$appDataDir = "" #Left blank intentionally
$shadowDir = "" #Left blank intentionally
$toolsDir = "$scriptDir\Tools"
$errCode = 0
$errServer = "server_for_error_logs"
$errShare = "share_for_error_logs"
$sucServer = "server_for_success_logs"
$sucShare = "share_for_success_logs"
$copyStatus = "" #Left blank intentionally
switch -regex ($wmiWinVer)
    {
        "Microsoft Windows 7*" {$winVer = "win7"; $userDirs = "C:\Users"; $appDataDir = "AppData\Local"; $shadowDir = "\\$scrSvr\$scrShare\$scrFolder\ShadowSpawn_win7\$wmiWinArch"}
        "Microsoft Windows XP*" {$winVer = "winxp"; $userDirs = "C:\Documents And Settings"; $appDataDir = "Local Settings\Application Data"; $shadowDir = "\\$scrSvr\$scrShare\$scrFolder\ShadowSpawn_winxp"}
        default {$winVer = "Unknown"}
    }
#Checking for VS 2010 C++ Redistributable (and installing if necessary)
function Get-RegInstalledPrograms($computer = '.') { 

$programs_installed = @{};
$error_action = 'Stop';
$reg_uninstall_paths = @('Software\Microsoft\Windows'`
	+ '\CurrentVersion\Uninstall');
$reg_uninstall_paths += @('Software\Wow6432Node\Microsoft'`
	+ '\Windows\CurrentVersion\Uninstall');
$pattern_valid_name = '^{[\w\W}]+\Z';
$hkey = 'LocalMachine';
	$registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($hkey, $computer);
	foreach ($reg_uninstall_path in $reg_uninstall_paths) {
		$reg_uninstall_key = $registry.OpenSubKey($reg_uninstall_path);
		if ($reg_uninstall_key -eq $null) {
			continue;
		}
		$key_names = $reg_uninstall_key.GetSubKeyNames();
		foreach ($key_name in $key_names) {
			$key_properties = $reg_uninstall_key.OpenSubKey($key_name);
			$name = $key_properties.GetValue('DisplayName');
			$version = $key_properties.GetValue('DisplayVersion');
			if (($key_name -imatch $pattern_valid_name) `
			-and ($name -ne $null)) {
				$programs_installed.$name = $version;
			}
			$key_properties.close();
		}
		$reg_uninstall_key.close();
	}
	$registry.close();
 

return $programs_installed;
}
if ($(Get-Service -Name RemoteRegistry).Status -eq "Stopped") {Set-Service -Name RemoteRegistry -Status Running -StartupType Automatic; Start-Sleep -Seconds 5}
$instPrograms = Get-RegInstalledPrograms
$x64InstFlag = "No"
$x86InstFlag = "No"
$instPrograms | foreach {$_.GetEnumerator()} | foreach {if ($_.Key -like "Microsoft Visual C++ 2010  x64 Redistributable*") {$x64InstFlag = "Yes"; Write-Host "$(Get-Date -format g) x64 VS Redistributable Found."}}
$instPrograms | foreach {$_.GetEnumerator()} | foreach {if ($_.Key -like "Microsoft Visual C++ 2010  x86 Redistributable*") {$x86InstFlag = "Yes"; Write-Host "$(Get-Date -format g) x86 VS Redistributable Found."}}
if ($wmiWinArch -eq "64-bit") 
{
    if ($x86InstFlag -eq "No")
    {
        Write-Host "$(Get-Date -format g) VS C++ x86 Redistributable not detected, installing..."
        Start-Process -Wait -FilePath "$toolsDir\vcredist-2010_x86.exe" -ArgumentList "/q /noreboot"
    }
    if ($x64InstFlag -eq "No")
    {
        Write-Host "$(Get-Date -format g) VS C++ x64 Redistributable not detected, installing..."
        Start-Process -Wait -FilePath "$toolsDir\vcredist-2010_x64.exe" -ArgumentList "/q /noreboot"
    }
}
else
{
    if ($x86InstFlag -eq "No")
    {
        Write-Host "$(Get-Date -format g) VS C++ x86 Redistributable not detected, installing..."
        Start-Process -Wait -FilePath "$toolsDir\vcredist-2010_x86.exe" -ArgumentList "/q /noreboot"
    }
}

Write-Host "$(Get-Date -format g) This computer is a $wmiWinVer computer and its user directories are located in $userDirs `r"
$userList = (Get-ChildItem $userDirs -Name)
Write-Host "$(Get-Date -format g) These users have profiles on this computer: `r"
Write-Host "$userList  `r"
# Looping through users directories
foreach ($user in $userList)
    {
        Write-Host "$(Get-Date -format g) Testing for $userDirs\$user\$appDataDir\Microsoft\Outlook `r"
        # If Outlook directory found, back it up
        if (Test-Path -PathType Container -Path $userDirs\$user\$appDataDir\Microsoft\Outlook)
        {
            Write-Host "$(Get-Date -format g) $user has an Outlook folder. `r"
            $sourceDir = "$userDirs\$user\$appDataDir\Microsoft\Outlook"
            $destDir = "\\$backupServer\$backupShare\$user\$compName"
            if (!(Test-Path -PathType Container -Path $destDir)) {New-Item -Path $destDir -ItemType Container}
            Write-Host "$(Get-Date -format g) Copying Outlook folder to backup location at $destDir `r"
            switch ($winVer)
            {
                "win7" {
                    . $shadowDir\ShadowSpawn.exe /verbosity=4 $sourceDir Q: robocopy /E /Z /NP /COPYALL /PURGE Q:\ $destDir | Out-Host
                    $copyStatus = $LastExitCode }
                "winxp" {
                    . $shadowDir\ShadowSpawn.exe /verbosity=4 $sourceDir Q: $toolsDir\robocopy /E /Z /NP /COPYALL /PURGE Q:\ $destDir  | Out-Host
                    $copyStatus = $LastExitCode }
                default { 
                    Write-Host "Unable to determine Windows version, copy cannot procede"
                    $copyStatus = 1 }
            }
            If ($copyStatus -eq 0) 
            {
                Write-Host "$(Get-Date -format g) Copy complete. `r"
            }
            Else
            {
                # Determine exit code for log flagging
                switch ($copyStatus)
                {
                    1 { Write-Host "$(Get-Date -format g) ShadowSpawn failed with error code $copyStatus.  Will copy log of this to \\$errServer\$errShare\$curDate\PST_Backup_Log_$compName.txt `r"
                        $errCode = $copyStatus }
                    2 { Write-Host "$(Get-Date -format g) ShadowSpawn failed with error code $copyStatus.  Will copy log of this to \\$errServer\$errShare\$curDate\PST_Backup_Log_$compName.txt `r"
                        $errCode = $copyStatus }
                    32769 { Write-Host "$(Get-Date -format g) Copy complete. `r" }
                    32770 { Write-Host "$(Get-Date -format g) Copy complete, but extra files/directories detected. May need to double-check log `r" }
                    32771 { Write-Host "$(Get-Date -format g) Copy complete, but extra files/directories detected. May need to double-check log `r" }
                    32772 { Write-Host "$(Get-Date -format g) Copy complete, but mismatched files/directories detected. May need to double-check log `r" }
                    32773 { Write-Host "$(Get-Date -format g) Copy complete, but mismatched files/directories detected. May need to double-check log `r" }
                    32774 { Write-Host "$(Get-Date -format g) Copy complete, but mismatched files/directories detected. May need to double-check log `r" }
                    32775 { Write-Host "$(Get-Date -format g) Copy complete, but mismatched files/directories detected. May need to double-check log `r" }
                    default { Write-Host "$(Get-Date -format g) Copy failed with error code $($copyStatus - 32768).  Will copy log of this to \\$errServer\$errShare\$curDate\PST_Backup_Log_$compName.txt `r"
                            $errCode = $($copyStatus - 32768) }
                } 
            }
        }
        else
        {
        Write-Host "$(Get-Date -format g) $user does not have an Outlook folder. `r"
        }
    }
Write-Host "$(Get-Date -format g) PST Backup complete. `r"
# Delete old log files to save space
Write-Host "$(Get-Date -format g) Beginning log rotation `r"
$fileList = Get-ChildItem $logDir
foreach ($file in $fileList) {
    Set-Variable -Name fileCreated -Scope Local -Value $(Get-ChildItem $logDir\$file)
    Set-Variable -Name fileAge -Scope Local -Value $(New-TimeSpan $fileCreated.CreationTime $(Get-Date))
    if ($fileAge.Days -gt 14) 
    {
        Write-Host "$(Get-Date -format g) $file is old, deleting `r"
        Remove-Item $logDir\$file
    }
        
}
Write-Host "$(Get-Date -format g) Log rotation complete `r"
Stop-Transcript
# Copy log files to appropriate location based on success or error and exit appropriately
If ($errCode -eq 0)
{
    if (!(Test-Path -PathType Container -Path "\\$sucServer\$sucShare\$curDate\")) {New-Item -Path "\\$sucServer\$sucShare\$curDate\" -ItemType Container}
    Copy-Item "$logDir\PST_Backup_Log_$curDate.txt" "\\$sucServer\$sucShare\$curDate\PST_Backup_Log_$compName.txt"
    exit 0
}
Else
{
    if (!(Test-Path -PathType Container -Path "\\$errServer\$errShare\$curDate\")) {New-Item -Path "\\$errServer\$errShare\$curDate\" -ItemType Container}
    Copy-Item "$logDir\PST_Backup_Log_$curDate.txt" "\\$errServer\$errShare\$curDate\PST_Backup_Log_$compName.txt"
    exit $errCode
}

Clone this wiki locally