Tuesday, June 30, 2015

Upload files to Exchange Public Folders using PowerShell

The following script can be used to upload files from the file system to a named folder in Exchange Public Folders. Each file will be uploaded in turn, and if successful, the source file deleted. Any files that fail to be uploaded are listed when the script completes, and remain in the file system.

The Script

The Public Folders store name highlighted in yellow must be changed to the value you see in Outlook when browsing Public Folders:
image
Other parameters to change are highlighted in green.
# Get Start Time
Write-Host "-----------------------------"
$start = (Get-Date)
Write-Host ("Start time: " + $start)
Write-Host "-----------------------------"
# 0 Setup source and target (leave no trailing \)
$source = "C:\Apps\Upload to PF\Batch 1"
$targetFolder = "All Public Folders\Test\Batch 1"
$failArray = @()
# 1 Initiate connection to Outlook
$outlook = new-object -comobject Outlook.Application
$mapi = $outlook.GetNamespace("MAPI")
# 2 Connect to PF
$pfStore = $mapi.Session.Stores.Item("Public Folders - Jonny.Trees@youremailaddress.com")
# 3 Connect to folder in PF
$currentFolder = $pfStore.GetRootFolder()
$folderPathSplit = $targetFolder.Split("\")
for ($x = 0; $x -lt $folderPathSplit.Length; $x++)
{
    $currentFolder = $currentFolder.Folders.Item($folderPathSplit[$x])
}
if ($currentFolder -eq $null)
{
    ("Could not find folder: " + $folderPath)
    return
}
Write-Host ""
("Found folder: " + $currentFolder.FolderPath)
Write-Host ""
# 4 Get the source files and volume
$volume = ("{0:N2}" -f ((Get-ChildItem -path $source -recurse | Measure-Object -property length -sum ).sum /1GB) + " GB")
$volumeMB = ("{0:N2}" -f ((Get-ChildItem -path $source -recurse | Measure-Object -property length -sum ).sum /1MB) + " MB")
("Volume to be moved: " + $volume + ", " + $volumeMB)
Write-Host ""
$source = $source + "\*"
$sourceFiles = Get-ChildItem -path $source -include *.msg
# 5 Copy the files
$counter = 1
$failCounter = 0
foreach($file in $sourceFiles)
{
    try
    {
        $item = $mapi.OpenSharedItem($file)
        Write-Host -nonewline ("Moving file " + $counter + " of " + $sourceFiles.Count + " ")
        [void]$item.Move($currentFolder)
        Remove-Item -literalpath $file
        Write-Host "Success"
    }
    catch
    {
        $failArray += $file
        $failCounter = $failCounter + 1
        Write-Host "Fail"
    }
    finally
    {
        $counter = $counter + 1
    }
}
[System.Media.SystemSounds]::Exclamation.Play();
# Get End Time
$end = (Get-Date)
$scriptExecutionTime = $end - $start
# Print results
Write-Host ""
Write-Host "-----------------------------"
Write-Host ("End time: " + $end)
Write-Host "-----------------------------"
Write-Host ""
Write-Host ("Fail count: " + $failCounter)
Foreach ($file in $failArray)
{
    Write-Host $file
}
Write-Host ""
Write-Host ("" + ($counter - $failArray.Count -1 ) + " of " + ($counter - 1) + " files totalling " + $volume + " were uploaded successfully")
Write-Host ("Script execution time: " + $scriptExecutionTime.hours + " hours, " + $scriptExecutionTime.minutes + " minutes and " + $scriptExecutionTime.seconds + " seconds")
Write-Host ("Average time per file: " + [math]::Round($scriptExecutionTime.totalseconds/$sourceFiles.Count,1) + " seconds")

 

Output

The output of the script is as follows.
-----------------------------
Start time: 06/30/2015 11:32:32
-----------------------------
Found folder: \\Public Folders\All Public Folders\Test\Batch 1
Volume to be moved: 0.02 GB, 20.03 MB
Moving file 1 of 3 Success
Moving file 2 of 3 Fail
Moving file 3 of 3 Success

-----------------------------
End time: 06/30/2015 11:32:45
-----------------------------
Fail count: 1
C:\Apps\Upload to PF\Batch1\Large file.msg
2 of 3 files totalling 0.02 GB were uploaded successfully
Script execution time: 0 hours, 0 minutes and 13 seconds
Average time per file: 4.3 seconds
In this case the file failed to upload because the file size exceeded that allowed by Exchange. Note that the warm up time for this script adds to the average value you see of 4.3 seconds. Uploading batches of 5,000 items, 1-2GB in size in total typically takes 0.5 seconds per file in my production environment.