Create a folder hierarchy in an Outlook PST file


Lately I have been busy creating archive mailboxes and importing PST data to the archive mailboxes (which will be the subject of a future post).

As part of that project we found a subset of people whose idea of “personal folders” was to drag all of their mail to a folder on a network share as .msg files.

I was asked if we could import these files like we import .pst files, and without thinking* I said “Sure!”

After returning to the office I started looking into importing .msg files, and realized that it was not as easy as importing .pst files, but should still be doable.

I set a few assumptions as follows:

1)      The data can be on any file share (or any PC) anywhere in the network.

2)      I won’t have access to that data.

3)      The IT staff requesting the import will have access.

4)      Neither the IT staff nor I will have permission on the end user’s mailbox.

I originally thought about using the Exchange Web Services Managed API to import directly into the user’s archive mailbox, but did not want to mess with permissions.

I then thought that I’d use the Outlook com object to create a PST on my workstation, create a set of folders, and import all .msg files into the corresponding folders in the PST.

A quick Bing search led me to Import .msg files into Outlook using PowerShell which gave me information on importing one file into one folder, so I was off to a good start. However, I did not want these messaged in the mailbox, I wanted them in a PST file.

I also wanted to create the PST file on the fly, instead of requiring that the IT staff create a file, so I did another quick Bing search for how to create a PST file and found How can I create an Outlook PST file using .Net?, which I converted to PowerShell as:

$outlook = New-Object -ComObject outlook.application

$namespace = $outlook.GetNamespace(“MAPI”)

$namespace.AddStore(“PST_Test.pst”)

$namespace.Session.Folders.GetLast().Name = (“PST_Test”)

$TargetFolder = $namespace.Session.Folders.GetLast()

 

I created a set of .msg files in a folder as follows:

\\server\share\karl\msgfiles\folder1
..
\\server\share\karl\msgfiles\folder41

Each folder had a varying number of email messages which I created with a script I will document in a later post.

I was able to create folders 1 through 41 and import all of the messages with no problem, so I let the people who asked know that I was now ready to test importing .msg files into archive mailboxes.

They provided me with a set of test data which was arranged more like:

\\server\share\karl\msgfiles\folder1
\\server\share\karl\msgfiles\folder1\folder2
\\server\share\karl\msgfiles\folder1\folder2\Folder3
\\server\share\karl\msgfiles\folder4
\\server\share\karl\msgfiles\folder4\folder5\folder6
..
\\server\share\karl\msgfiles\folder41

My initial script only created folders to two deep, so I needed to find a method to create the folder structure from a file system to the PST file. Once again, off to Bing, where I found the following:

Copy Folder Structure from Explorer into Outlook 2013

Create Outlook Folders from a List of Folder Names

Neither of these was what I was looking for, but they got the “can do” in me going, and I decided that I could definitely do this in PowerShell.

I came up with this:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038

$path = “\\server\share\karl\msgfiles\”
if ($path.EndsWith(“\”)){
    $path = $path.TrimEnd(“\”)
}
$outlook = New-Object -ComObject outlook.application 
$namespace = $outlook.GetNamespace(“MAPI”)
$namespace.AddStore(“PST_Test.pst”)
$namespace.Session.Folders.GetLast().Name = (“PST_Test”)
$TargetFolder = $namespace.Session.Folders.GetLast()
foreach ($folder in Get-ChildItem -Path $Path -Directory -Recurse){
   if ((split-path $folder.FullName -Parent) -eq $Path){
       try{
            Write-Output “$($folder) will be created”
           [void]$TargetFolder.Folders.Add($folder)
       }
           catch{}
   }
   else{
       $FolderCount = $folder.FullName.Remove(0,$path.Length).Split(“\”).Count
       $TargetFolder = $namespace.Session.Folders.GetLast()
       for ($i = 2; $i -lt $FolderCount;$i++){
           $FolderName = $folder.FullName.Remove(0,$path.Length).Split(“\”)[$i]
           $RootName = $folder.FullName.Remove(0,$path.Length).Split(“\”)[$i1]
           try{
               $TargetFolder = $TargetFolder.Folders.Item($RootName)
           }
           catch{}
           Write-Output “$($FolderName) will be created in $($RootName)”
           try{
               [void]$TargetFolder.Folders.Add($FolderName)
               $TargetFolder = $TargetFolder.Folders.Item($FolderName)
           }
           catch{
               $TargetFolder = $TargetFolder.Folders.Item($FolderName)
           }
       }
   }
}

I then found that .msg files were also in the root of \\server\share\karl\msgfiles, and I had not created a “root” folder in the PST file.

I decided to create an “Inbox” folder if there were .msg files in the root of the file share:

001
002
003
004
005
006
007
008
009

if (Get-ChildItem -Path $path\*.msg){
   $Folder = “Inbox”
   $TargetFolder = $namespace.Session.Folders.GetLast()
   Write-Output “$($folder) will be created”
   try{
       [void]$TargetFolder.Folders.Add($folder)
   }
   catch{}
}

 

And that’s how I create a folder structure from a set of file folders into a PST file.

The whole script to create the folder structure looks like this:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047

$path = “\\server\share\karl\msgfiles\”
if ($path.EndsWith(“\”)){
    $path = $path.TrimEnd(“\”)
}
$outlook = New-Object -ComObject outlook.application 
$namespace = $outlook.GetNamespace(“MAPI”)
$namespace.AddStore(“PST_Test.pst”)
$namespace.Session.Folders.GetLast().Name = (“PST_Test”)
$TargetFolder = $namespace.Session.Folders.GetLast()
foreach ($folder in Get-ChildItem -Path $Path -Directory -Recurse){
   if ((split-path $folder.FullName -Parent) -eq $Path){
       try{
            Write-Output “$($folder) will be created”
           [void]$TargetFolder.Folders.Add($folder)
       }
           catch{}
   }
   else{
       $FolderCount = $folder.FullName.Remove(0,$path.Length).Split(“\”).Count
       $TargetFolder = $namespace.Session.Folders.GetLast()
       for ($i = 2; $i -lt $FolderCount;$i++){
           $FolderName = $folder.FullName.Remove(0,$path.Length).Split(“\”)[$i]
           $RootName = $folder.FullName.Remove(0,$path.Length).Split(“\”)[$i1]
           try{
               $TargetFolder = $TargetFolder.Folders.Item($RootName)
           }
           catch{}
           Write-Output “$($FolderName) will be created in $($RootName)”
           try{
               [void]$TargetFolder.Folders.Add($FolderName)
               $TargetFolder = $TargetFolder.Folders.Item($FolderName)
           }
           catch{
               $TargetFolder = $TargetFolder.Folders.Item($FolderName)
           }
       }
   }
}
if (Get-ChildItem -Path $path\*.msg){
    $Folder = “Inbox”
    $TargetFolder = $namespace.Session.Folders.GetLast()
    Write-Output “$($folder) will be created”
    try{
       [void]$TargetFolder.Folders.Add($folder)
   }
   catch{}
}

 

I say “the whole script” as this could be run as a standalone script to simply create folders, but it’s part of the larger Import-MSGFile script which will be the topic of a future post.

Next post: Creating sample .msg files.

*I seem to say “Sure!” without thinking about it quite often – thankfully PowerShell has always pulled me out of the fire.

Advertisement
  1. Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: