Copy Items in Outlook Using PowerShell - 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.

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...
#>

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")

Outlook Interop Folder.CopyTo Method - Merge Inbox

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.

Expressions are only allowed as the first element of a pipeline

I'm new at writing in powershell but this is what I'm trying to accomplish.
I want to compare the dates of the two excel files to determine if one is newer than the other.
I want to convert a file from csv to xls on a computer that doesn't have excel. Only if the statement above is true, the initial xls file was copied already.
I want to copy the newly converted xls file to another location
If the file is already open it will fail to copy so I want to send out an email alert on success or failure of this operation.
Here is the script that I'm having issues with. The error is "Expressions are only allowed as the first element of a pipeline." I know it's to do with the email operation but I'm at a loss as to how to write this out manually with all those variables included. There are probably more errors but I'm not seeing them now. Thanks for any help, I appreciate it!
$CSV = "C:filename.csv"
$LocalXLS = "C:\filename.xls"
$RemoteXLS = "D:\filename.xls"
$LocalDate = (Get-Item $LocalXLS).LASTWRITETIME
$RemoteDate = (Get-Item $RemoteXLS).LASTWRITETIME
$convert = "D:\CSV Converter\csvcnv.exe"
if ($LocalDate -eq $RemoteDate) {break}
else {
& $convert $CSV $LocalXLS
$FromAddress = "email#address.com"
$ToAddress = "email#address.com"
$MessageSubject = "vague subject"
$SendingServer = "mail.mail.com"
$SMTPMessage = New-Object System.Net.Mail.MailMessage $FromAddress, $ToAddress, $MessageSubject, $MessageBody
$SMTPClient = New-Object System.Net.Mail.SMTPClient $SendingServer
$SendEmailSuccess = $MessageBody = "The copy completed successfully!" | New-Object System.Net.Mail.SMTPClient mail.mail.com $SMTPMessage
$RenamedXLS = {$_.BaseName+(Get-Date -f yyyy-MM-dd)+$_.Extension}
Rename-Item -path $RemoteXLS -newname $RenamedXLS -force -erroraction silentlycontinue
If (!$error)
{ $SendEmailSuccess | copy-item $LocalXLS -destination $RemoteXLS -force }
Else
{$MessageBody = "The copy failed, please make sure the file is closed." | $SMTPClient.Send($SMTPMessage)}
}
You get this error when you are trying to execute an independent block of code from within a pipeline chain.
Just as a different example, imagine this code using jQuery:
$("div").not(".main").console.log(this)
Each dot (.) will chain the array into the next function. In the above function this breaks with console because it's not meant to have any values piped in. If we want to break from our chaining to execute some code (perhaps on objects in the chain - we can do so with each like this:
$("div").not(".main").each(function() {console.log(this)})
The solution is powershell is identical. If you want to run a script against each item in your chain individually, you can use ForEach-Object or it's alias (%).
Imagine you have the following function in Powershell:
$settings | ?{$_.Key -eq 'Environment' } | $_.Value = "Prod"
The last line cannot be executed because it is a script, but we can fix that with ForEach like this:
$settings | ?{$_.Key -eq 'Environment' } | %{ $_.Value = "Prod" }
This error basically happens when you use an expression on the receiving side of the pipeline when it cannot receive the objects from the pipeline.
You would get the error if you do something like this:
$a="test" | $a
or even this:
"test" | $a
I don't know why are trying to pipe everywhere. I would recommend you to learn basics about Powershell pipelining. You are approaching it wrong. Also, I think you can refer to the link below to see how to send mail, should be straight forward without the complications that you have added with the pipes : http://www.searchmarked.com/windows/how-to-send-an-email-using-a-windows-powershell-script.php

how to access string indexer on a collection in Powershell

Slowly learning Powershell ... I'm working on a script to query a third party AD/AM database (ldap). The specific LDAP property name that I want has a hyphen in the name.
I can do this in c# without thinking about it, but I don't want to fire up Visual Studio just to do some simple scripting stuff that changes frequently.
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
.....
$results = $objSearcher.FindAll()
foreach($result in $results) {
$item = $result.Properties
$item.some-property # this fails because of '-'
$result['some-property'] # 'Unable to index into an object of type System.DirectoryServices.SearchResult.'
}
You can also specify the property name via a variable:
$prop = 'some-property'
$result.$prop
You need to place curly braces around the hyphenated property name. This should work:
$item.{some-property}