Outlook Interop Folder.CopyTo Method - Merge Inbox - powershell

I'm Trying to copy Mail Items from one mailbox to another, but when i use the Folder.Copy Method to copy the Inbox folder of one mailbox to the other, it's not merge the data but creating Folder Named Inbox1,
Here's my code:
$outlook = New-Object -ComObject outlook.application
$namespace = $Outlook.GetNameSpace("mapi")
$namespace.Logon("Outlook")
$LocalStore = $Namespace.Stores[3]
$RemoteStore = $Namespace.Stores[1]
$LocalFolders = $LocalStore.GetRootFolder().folders
$RemoteFolders = $RemoteStore.GetRootFolder().folders
$RemoteInbox = $RemoteFolders | ? {$_.Name -eq "Inbox"}
$LocalInbox = $LocalFolders | ? {$_.Name -eq "Inbox"}
$RemoteInbox.CopyTo($LocalInbox.Parent)
To workaround i can use the Items Copy :
Foreach ($Item in $RemoteInbox.Items)
{
$Copy = $Item.Copy()
[void]$Copy.Move($TargetFolder)
}
But it's much slower, and if i have subfolders it need special care with extra code,
Search the web with no solution found
Any help is appreciated

This is to be expected - if there is already an existing folder with the same name, MAPI will return MAPI_E_COLLISION - see IMAPIFolder::CopyFolder.
Outlook detects that error and creates a folder with a unique name.
You can copy items in a batch using IMAPIFolder.CopyMessages, but Extended MAPI requires C++ or Delphi. If using Redemption is an option (I am its author), you can use its RDOItems.CopyMultiple method. You can create an array of entry ids from the source folder using RDOItems.MAPITable.ExecSQL and pass it to RDOItems.CopyMultiple.

Related

olFolderInbox returns incomplete result

I'm trying to write a PowerShell script that automates the way to retrieve all my emails with sender information in outlook and importing it on a text file.
I monitored this script that I created returns incomplete results.
Below here is my code for:
$namespace = $Outlook.GetNameSpace("MAPI")
$inbox = $namespace.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
$emails = $inbox.items
ForEach ($email in $emails){
write-host $email.Subject
}
If "incomplete results" means that it's not returning all the emails you're expecting, there are a couple of things that I ran into when working with emails in Powershell:
It won't grab emails that are in folders under the Inbox. You have to call each folder separately. I had to setup a recusive loop to compile a list of them
Not all of your emails are actually stored in Outlook. By default, Outlook only pulls the last year of email form an email server. Sometimes it can show messages that exist on the server but they aren't actually downloaded.
EDIT: Here's the recursive function I built to get all the folders and subfolders within the Inbox.
# Create an ArrayList and immediately add the Inbox as the first folder in the list
[System.Collections.ArrayList] $folderList = #([PSCustomObject]#{
FolderPath = $inbox.FolderPath
EntryID = $inbox.EntryID
})
# Call the function to get all the folders and subfolders in the Inbox folder
Get-MailFolders $inbox.Folders
# Recusive function that will get all the folders and subfolders in the parent folder
function Get-MailFolders ($parent) {
foreach ($child in $parent) {
Write-Host "." -NoNewLine
$folderList.Add([PSCustomObject]#{
FolderPath = $child.FolderPath
EntryID = $child.EntryID
}) | Out-Null
Get-MailFolders ($child.Folders)
}
}
Continuing form my comment
Search for:
'PowerShell read outlook email name and subject'
hit(s)
https://devblogs.microsoft.com/scripting/use-powershell-to-data-mine-your-outlook-inbox
Read most recent e-mail from outlook using PowerShell
http://jon.glass/blog/reads-e-mail-with-powershell
olFolderInbox = 6
$outlook = new-object -com outlook.application;
$mapi = $outlook.GetNameSpace("MAPI");
$inbox = $mapi.GetDefaultFolder($olFolderInbox)
# Grab the specific properties from the messages in the Inbox:
$olFolderInbox = 6
$outlook = new-object -com outlook.application;
$mapi = $outlook.GetNameSpace("MAPI");
$inbox = $mapi.GetDefaultFolder($olFolderInbox)
$inbox.items|Select SenderEmailAddress,to,subject|Format-Table -AutoSize
# Results
<#
SenderEmailAddress To Subject
------------------ -- -------
notify#twitter.com Jonathan Glass Thomas Garnier (#mxatone) retweeted one...
mailing-list#rifftrax.com Riff Rediscover Puppets in our latest short!
notify#twitter.com Jonathan Glass [ Gunther ] (#Gunther_AR) retweeted one...
#>

Issue with moving multiple items from one outlook folder to another - Powershell

I am trying to select multiple emails from on outlook inbox folder via mapi addressing and want to move a copy of these emails to another folder in the same inbox.
Unfortunately my script seems to do whatever it wants, sometimes copying 6 emails before stopping with following failure, sometimes stopping right with the first email.
Failure:
... "veeam")} | ForEach-Object {$_.Copy().Move($Namespace.Folders.Item("$ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [ForEach-Object], COMException
+ FullyQualifiedErrorId : System.Runtime.InteropServices.COMException,Microsoft.PowerShell.Commands.ForEachObjectCommand
I could not find any solution for this and I am sitting here confused since in another mailbox the code works just fine.
Of course I am setting the variables $Mailbox and $TempWorkPath beforehand.
Thanks in advance for your help.
Trying to run the code in a foreach-loop is less performant and ends with the same issue.
About 3 hours of google search did not help me at all.
Just moving the object causes the code to break, probably because of indexiation?
Add-Type -Assembly "Microsoft.Office.Interop.Outlook"
$OutlookSession = New-Object -ComObject Outlook.Application
$Namespace = $OutlookSession.GetNameSpace("MAPI")
$Namespace.Folders.Item("$Mailbox").Folders.Item("Posteingang").Items.Restrict('[UnRead] = True') | Where-Object {($_.Subject -match "ackup") -or ($_.SenderEmailAddress -match "veeam")} | ForEach-Object {$_.Copy().Move($Namespace.Folders.Item("$Mailbox").Folders.Item("Posteingang").Folders.Item("$TempWorkPath"))} | Out-Null
<# Do things with the selected/coppied emails #>
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($OutlookSession) | Out-Null
$OutlookSession = $null | Out-Null
In Theory an based on my tests in another folder this should work perfectly fine, create a copy of the email, move it to my folder and afterwards I can do things with it.
Well, I think I found my way around the issue. Running the command in a while loop instead of an foreach loop seems to work better.
$Inbox = $Namespace.Folders.Item("$Mailbox").Folders.Item("Posteingang").Items.Restrict('[UnRead] = True') | Where-Object {($_.Subject -match "ackup") -or ($_.SenderEmailAddress -match "veeam")}
$MailCounter = $Inbox.Count
$HelperForCounting = 0
while ($MailCounter -gt $HelperForCounting)
{
$Inbox[$MailCounter].Copy().Move($Namespace.Folders.Item("$Mailbox").Folders.Item("Posteingang").Folders.Item("$TempWorkPath"))
$MailCounter = $MailCounter - 1
}
Greetings
I also had this issue with processing emails on Outlook. My overall scheme is to process emails folder by folder. I traced the issue to the Emails.getNext() function. My completely uneducated guess is it has something to do with parallel processing of Emails and how it grabs them in ForEach() and getNext(). The problem went away by using the getLast().
Note in the following code it will just move all read emails to archive folder and then some unread emails to corporate dump folder and most unread emails to the unread folder. This is itself just a mutation on the .p0r email script. There is a > $null at the end of the function block is where I originally had it on the ForEach loop and it worked as one would expect, but it does not work on the While loop blocking function. Instead that had to be moved to the location in the move unread section. Still a lot of room for improvement, getting some strange com errors but it will process through an inbox so long as GetLast() email is moved out of the folder.
As for my rationale on the root cause, I noticed that the failure to read a whole inbox is dependent on the size of the inbox. So each run my go through 2/3 of the remaining emails in the inbox.
# OUTLOOK RULES #
#################
# OUTLOOK RULES #
#################
#Import Object Library?
Add-Type -assembly "Microsoft.Office.Interop.Outlook"
# VARIABLES
$index=0;
$pstPath = "C:\YOURPATHHERE"
# DISPLAY INFO
function display( [string]$subject, [string]$color , [string]$out) {
# REQUIRED LENGTH OF STRING
$len = 20
# STRINGS THAT ARE LONGER WILL BE CUT DOWN,
# STRINGS THAT ARE TO SHORT WILL BE MADE LONGER
if ( $subject.length -lt 20 ){
$toadd=20-$subject.length;
for ( $i=0; $i -lt $toadd; $i++ ){
$subject=$subject+" ";
}
$len = $subject.length
}
else { $len = 20 }
$index=$index+1
Write-host -ForegroundColor $color -nonewline " |" ((($subject).ToString()).Substring(0,$len)).ToUpper()
}
# CREATING OUTLOOK OBJECT
$outlook = New-Object -comobject outlook.application
$namespace = $outlook.GetNameSpace("MAPI")
# GETTING PST FILE THAT WAS SPECIFIED BY THE PSTPATH VARIABLE
$pst = $namespace.Stores | ?{$_.FilePath -eq $pstPath}
# ROOT FOLDER
$pstRoot = $pst.GetRootFolder()
# SUBFOLDERS
$pstFolders = $pstRoot.Folders
$fArchive = $pstFolders.Item("Archive")
# PERSONAL SUBFOLDER
$personal = $pstFolders.Item("Personal")
# INBOX FOLDER
$DefaultFolder = $namespace.GetDefaultFolder(6)
# INBOX SUBFOLDERS
$InboxFolders = $DefaultFolder.Folders
# DELETED ITEMS
$DeletedItems = $namespace.GetDefaultFolder(3)
# EMAIL ITEMS
$Emails = $DefaultFolder.Items
$workingFile = [IO.Path]::GetTempFileName()
# PROCESSING EMAILS
$currentWriteFolder = $pstFolders.Item("Archive")
While ($Emails.count -gt 0) {
$Email = $Emails.GetLast()
#Move all reads into Archive
if (!$Email.Unread) {
$email.move($fArchive) > $null
continue
}
#Filter unread items by sender
$WriteString = $Email.SenderEmailAddress.ToString()
[IO.File]::WriteAllLines($workingFile, $WriteString)
if (Select-String -Path $workingFile -Pattern "company") {
$email.move($currentWriteFolder.Folders.Item("globalcorp"))
continue
}
$email.move($pstFolders.Item("Unread"))
} # > $null
[IO.File]::Delete($workingFile)
Write-host ""

Download email attachments from outlook using powershell

I am using following code to download outlook email attachments and images to folder. But it is throwing an error:
Cannot index into a null array.
Any guess why?
$o = New-Object -ComObject outlook.Application
$ns = $o.GetNamespace("MAPI")
$f = $ns.Folders.Item(1)
$di = $f.Folders.item("Deleted Items")
$messagesWithAttachments = $di.items | Where-Object {$_.Attachments.Count -gt 0}
$messagesWithAttachments[0].Attachments.item(1).saveasfile("C:\test")
Provided there are emails with attachments, give a filename instead of folder in the saveasfile() method.
Eg: saveasfile("C:\test\test.txt")

Automate exporting public folder to PST using Outlook 2016

I'm searching for a way to automate using Outlook's Export to PST functionality in order to pull Public Folders named A-Z as individual PST files (because there is a PST size limit) and I'm hoping for a pure Powershell method.
What I have found so far isn't automated but it helped when I had to get someone's folder copied over quickly.
Add-Type -assembly "Microsoft.Office.Interop.Outlook"
$Outlook = New-Object -ComObject Outlook.Application
$namespace = $Outlook.GetNameSpace("MAPI")
#Get the public folder ID
$SourceFolder = $namespace.PickFolder() | Select EntryID
#Get the destination folder ID
$DestinationFolder = $namespace.PickFolder() | Select EntryID
$namespace.GetFolderFromID($SourceFolder).CopyTo($DestinationFolder)
The second post here https://serverfault.com/questions/180916/export-exchange-public-folder-to-pst-from-powershell looked helpful but I'm running into errors with the AutoDiscover URL and/or likely permission problems.
Also found this which looked but I don't want PSTs attached to my outlook.
Any pointers would be much appreciated!
Copy Exchange on-prem public folder to Local Outlook PST file.
Thanks to: https://github.com/misterGF/DevOps-PowerShell/blob/master/export-PFdata.ps1
https://www.vistax64.com/threads/looping-through-outlook-folders-and-subfolders-returning-numberof-e-mails.249828/
How to disconnect PST file from Outlook using Powershell?
Managed to work on this and got something functional, please use at your OWN risk. No error handling is performed.
function Get-MailboxFolder($folder)
{
Begin{
$Outlook = New-Object -ComObject Outlook.Application
$namespace = $Outlook.GetNameSpace("MAPI")
}
Process{
New-Item -Path 'E:\PST.archive\Full Backup\Enterprise West' -Name "$($folder.Name)" -ItemType Directory
foreach ($f in $folder.folders) {
#Location of PST files
$PSTPath = 'E:\PST.archive\Full Backup\Enterprise West' + '\' + "$($folder.Name)"
$PSTName = $($f.name) + '.pst'
$FullPST = $PSTPath + '\' + $PSTName
$namespace.AddStore("$FullPST")
$pstFolder = $namespace.Session.Folders.GetLast()
"Start Public Folder copy to local PST"
[void]$f.CopyTo($pstFolder)
}
}#End process block
End{
"Removing attached PSTs from Outlook"
$RemPST = $Outlook.Session.Stores | Where DisplayName -EQ 'Outlook Data File'
foreach ($pst in $RemPST){
$Outlook.Session.RemoveStore($pst.GetRootFolder())
}
}#end End Block
}
$WestFolders = $Outlook.Session.Folders.Item('Public Folders - YourNameHERE!!!!!').folders.item('All Public Folders').Folders.Item('Level-1').folders.item('Level-2').folders.item('Level-3')
Get-MailboxFolder $WestFolders
There is no automate method to export a public folder to a PST:
Export-Mailbox command won't work with public folders.
You can do it by using:
1) use outlook : Import/Export wizard
2) use third party tools

Copy Items in Outlook Using PowerShell

I am trying to copy some items from "Inbox" to "Sent Items\TEMP" in Outlook. So far I was able to pick up desired messages, based on the subject line, but then I try to copy them with no success. Where am I going wrong? Thanks! Please note, when i do a write-host to retrieve the item I'm looking for, it comes up with no issues, so I am able to retrieve the email with subject line of "Test", but not copy it.
Error message:
Cannot find an overload for "Copy" and the argument count: "1"
Code:
$outlook = New-Object -comobject outlook.application;
$mapi = $outlook.GetNamespace('MAPI').GetDefaultFolder(6);
$mapi2 = $outlook.GetNamespace('MAPI').GetDefaultFolder(5);
$subFolders2 = $mapi.Folders | ? {$_.FolderPath.EndsWith('TEMP')};
$mapi.Items | ForEach-Object {If ($_.Subject -Like '*TEst*'){$_.Copy($subFolders2)}$_.Save()}
MailItem.Copy does not take any parameters. It returns an instance of the MailItem object; you can then call MailItem.Move and pass the pointer to the destination folder.