I have a script that is supposed to download all attachments from "Inbox" then move them to another folder named "Processed". I filter mails by subject. If exist then download all attachments and move the mails.
For example I have 5 mails with attachments (xls) the subject are named like:
BB_asd_green_dsa
AD_acv_blue_sf
FD__c_d_red_sa
CV_saas_white_sdsd
as_dc_asa_purple
When I start the script it saves 3 attachments, the mails with attachments are moved too. And then it stops.
Second start. It saves 1 attachment & moves 1 mail
And one last manual start script saves the last attachment and moves the mail.
For what reason doesn't the script save and move all attachments with their mails?
Here is the script:
$olFolderInbox = 6
$outlook = New-Object -Com Outlook.Application;
$ns = $outlook.GetNameSpace("MAPI");
#$ns.Logon("Outlook")
#$ns.SendAndReceive($true)
#Start-Sleep -s 0
$inbox = $ns.GetDefaultFolder($olFolderInbox)
$messages = $inbox.Items
Write-Host $messages.Count
foreach ($message in $messages) {
$msubject = $message.Subject
$b = $msubject.Filename
$filepath = "D:\Users\h.yordanov\Desktop\convertexltohtm\xls\"
$message.Attachments | foreach{
Write-Host $message.Filename
$a = $_.Filename
$msubject = $message.Subject
$msubjectW = $msubject -replace '[^0-9]', ''
$b = $msubjectW + ".xls"
if ($msubject.Contains("green") -or $msubject.Contains("blue") -or $msubject.Contains("green") -or $msubject.Contains("white") -or $msubject.Contains("purple")) {
$_.SaveAsFile((Join-Path $filepath $b))
}
}
$MoveTarget = $inbox.Folders.Item("Processed")
[void]$message.Move($MoveTarget)
}
I used this script to move the mails, but still not all 5 mails are moved at once:
$Date = [DateTime]::Now.AddDays(-180)
$Last6Months = $Date.tostring("MM/dd/yyyy")
$o = New-Object -comobject outlook.application
$n = $o.GetNamespace(“MAPI”)
$Account = $n.Folders | ? { $_.Name -eq 'h.yordanov#sample.com' };
$Inbox = $Account.Folders | ? { $_.Name -match 'Inbox' };
$TargetFolder = $Inbox.Folders.Item('Processed')
$Inbox.Items |
Where-Object -FilterScript {
$_.senton -ge "$Last6Months"
} |
ForEach-Object -Process {
[void]$psitem.Move($TargetFolder)
}
Btw i am using MS office Pro plus 2010 64-bit
Related
I would like to modify the script below, to send the email with embedded text, without removing the html attachments.
The data it should embed into the email is found at: "C:\Users\Users\Summary\SummaryCSV.txt"
Here is the Powershell Script which needs to be modified:
# Check to see we have all the arguments
If (Test-Path -Path "C:\Users\Users\Summary\SummaryCSV.txt") {
#Send Email with HTML as attachment and no text embedded
$FullPath=$args[0]
#Get an Outlook application object
$o = New-Object -com Outlook.Application
$mail = $o.CreateItem(0)
#2 = High importance message
$mail.importance = 1
$mail.subject = "Ready to be retrieved: $(get-date)"
$mail.body = "Summary $(get-date)"
#separate multiple recipients with a ";"
$mail.To = "email#email1.com"
# Iterate over all files and only add the ones that have an .html extension
$files = Get-ChildItem $FullPath
for ($i=0; $i -lt $files.Count; $i++) {
$outfileName = $files[$i].FullName
$outfileNameExtension = $files[$i].Extension
# if the extension is the one we want, add to attachments
if($outfileNameExtension -eq ".html")
{
$mail.Attachments.Add($outfileName);
}
}
$mail.Send()
# give time to send the email
Start-Sleep 5
# quit Outlook
$o.Quit()
#end the script
#exit
}
Thanks in advance for your assistance.
Based on your specifications:
# Check to see we have all the arguments
If ((Test-Path -Path C:\Users\Users\Summary\SummaryCSV.txt) -and (Test-Path -Path C:\Users\Users\Summary\SummaryCSV.txt)) {
#Send Email with HTML as attachment and no text embedded
$FullPath=$args[0]
$embeddedText = Get-Content -Path C:\Users\Users\Summary\SummaryCSV.txt -Raw
#Get an Outlook application object
$o = New-Object -com Outlook.Application
$mail = $o.CreateItem(0)
#2 = High importance message
$mail.importance = 1
$mail.subject = "Ready to be retrieved: $(get-date)"
$mail.body = "Summary $(get-date)`r`n$embeddedText"
#separate multiple recipients with a ";"
$mail.To = "email#email1.com"
# Iterate over all files and only add the ones that have an .html extension
$files = Get-ChildItem $FullPath
for ($i=0; $i -lt $files.Count; $i++) {
$outfileName = $files[$i].FullName
$outfileNameExtension = $files[$i].Extension
# if the extension is the one we want, add to attachments
if($outfileNameExtension -eq ".html") {
$mail.Attachments.Add($outfileName);
}
}
$mail.Send()
# give time to send the email
Start-Sleep 5
# quit Outlook
$o.Quit()
#end the script
#exit
}
I have a powershell script which downloads outlook mail attachments.
But I recieve below error:
Method invocation failed because [System.String] does not contain a method named 'saveasfile'.
I have used below script
Add-Type -Assembly "Microsoft.Office.Interop.Outlook"
$Outlook = New-Object -ComObject Outlook.Application
$Namespace = $Outlook.GetNameSpace("MAPI")
$inbox = $mapi.GetDefaultFolder(6)
$saveFilePath = "C:\temp\"
$subfolder = $inbox.Folders | Where-Object {$_.Name -eq “REPORTS”}
$mail = $subfolder.Items | Select-Object -Property Subject,SentOn,#{name="Attachments";expression={$_.Attachments|%{$_.DisplayName}}} | Where-Object{$_.attachments -match ".html" -and ($_.SentOn -gt '29-Oct-19 12:00:00 AM')}
foreach ($email in $mail)
{
if ($email.attachments.count -ge 1)
{
foreach ($attachment in $email.attachments)
{
$filename = $attachment.filename
$attachment.saveasfile((join-path $savefilepath $filename))
}
}
}
Please let me know how can I resolve this error.
Outlook's Attachments property's enumerator returns a String key, not an actual attachment object (this is why using an untyped or weakly-typed language like PowerShell for working with COM is a very bad idea).
While PowerShell wants to be used as a functional, pipelining language (which is a PITA to debug) - sometimes it's easier to use old-fashioned imperative code:
For( $i = 0; $i -le $email.Attachments.Count; $i++ ) {
$attachment = $email.Attachments.Item( $i )
$attachment.SaveAsFile( ( Join-Path $savefilepath $filename ) )
}
BTW, you don't need the if ($email.attachments.count -ge 1) statement because the For( $i = 0; $i... loop also checks the .Attachments.Count property too.
How do I reference a specific email address in this code it always directs to
the default email account. I have multiple email accounts in my outlook and I want to download emails from a different account which I want to reference by that email address . I have a feeling
$folder = $namespace.getDefaultFolder($olFolders::olFolderInBox)
has to be changed please give suggestions.
`[CmdletBinding(DefaultParameterSetName="All")] `
`Param(
[Parameter(Mandatory=$true,
Position=0,
HelpMessage='Folder path to store emails. Do not use quotation marks even if the path has spaces.',
ValueFromPipelineByPropertyName=$true
)]
[Alias("Destination", "Dest", "FullName")]
[String]$DestinationPath, `
[Parameter(ParameterSetName="All")]
[Parameter(Mandatory=$true,ParameterSetName="Unread")]
[Switch]$UnreadOnly,
[Parameter(ParameterSetName="Unread")]
[Switch]$MarkRead
)
#Removes invalid Characters for file names from a string input and outputs
the clean string
` #Similar to VBA CleanString() Method
#Currently set to replace all illegal characters with a hyphen (-)
Function Remove-InvalidFileNameChars {`
param(
[Parameter(Mandatory=$true, Position=0)]
[String]$Name
)
return [RegEx]::Replace($Name, "[{0}]" -f ([RegEx]::Escape([String][System.IO.Path]::GetInvalidFileNameChars())), '-')
}
#Test for destination folder nonexistence
if (!(Test-Path $DestinationPath)) {
#Set values for prompt and menu
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
"Confirmation Choice"
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
"Negative Response"
$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
$title = "Invalid Destination"
$message = "The folder you entered does not exist. Would you like to create the folder?"
#Prompt for folder creation and store answer
$result = $host.UI.PromptForChoice($title, $message, $options, 0)
#If yes, create.
if ($result -eq 0) {
New-Item $DestinationPath -ItemType Directory | Out-Null
Write-Host "Directory created."
}
#If no, exit
else {exit}
}
#Add a trailing "\" to the destination path if it doesn't already
if ($DestinationPath[-1] -ne "\") {
$DestinationPath += "\"
}
#Add Interop Assembly
Add-type -AssemblyName "Microsoft.Office.Interop.Outlook" | Out-Null
#Type declaration for Outlook Enumerations, Thank you Hey, Scripting Guy! blog for this demonstration
$olFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$olSaveType = "Microsoft.Office.Interop.Outlook.OlSaveAsType" -as [type]
$olClass = "Microsoft.Office.Interop.Outlook.OlObjectClass" -as [type]
#Add Outlook Com Object, MAPI namespace, and set folder to the Inbox
$outlook = New-Object -ComObject Outlook.Application
$namespace = $outlook.GetNameSpace("MAPI")
#Future Functionality to Receive Email before saving - Still Needs Testing
#$outlook.Session | Out-Null
#$outlook.Session.SendAndReceive($false) | Out-Null
$folder = $namespace.getDefaultFolder($olFolders::olFolderInBox)
#Iterate through each object in the chosen folder
foreach ($email in $folder.Items) {
#Get email's subject and date
[string]$subject = $email.Subject
[string]$sentOn = $email.SentOn
#Strip subject and date of illegal characters, add .msg extension, and combine
$fileName = Remove-InvalidFileNameChars -Name ($sentOn + "-" + $subject + ".msg")
#Combine destination path with stripped file name
$dest = $DestinationPath + $fileName
#Test if object is a MailItem
if ($email.Class -eq $olClass::olMail) {
#Test if UnreadOnly switch was used
if ($UnreadOnly) {
#Test if email is unread and save if true
if ($email.Unread) {
#Test if MarkRead switch was used and mark read
if ($MarkRead) {
$email.Unread = $false
}
$email.SaveAs($dest, $olSaveType::olMSG)
}
}
#UnreadOnly switch not used, save all
else {
$email.SaveAs($dest, $olSaveType::olMSG)
}
}
}
Think You can do something like this:
$outlook = New-Object -ComObject Outlook.Application
$namespace =$outlook.GetNameSpace("MAPI")
$namespace.Logon("Profilename","profilepassword",$false,$false)
Also you can use Assembly - Microsoft.Exchange.WebServices.dll and do something like this:
[Reflection.Assembly]::LoadFile("C:\Program Files (x86)\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll") > $nul
$getref = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2)
$getref.Credentials = New-Object Net.NetworkCredential('Account', 'Password', 'domain.local')
$getref.AutodiscoverUrl("Account#domain.com")
$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($getref,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
#Write-Host "Total Messages:" $inbox.TotalCount
$psPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$ivItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView($inbox.TotalCount)
$fiItems = $getref.FindItems($Inbox.Id,$ivItemView)
[Void]$getref.LoadPropertiesForItems($fiItems,$psPropset)
foreach($Item in $fiItems.Items){
if ($Item.From -EQ "Somemail#domain.com") {
New-Object -TypeName PSObject -Property #{
Emails = $Item.From
} | select Emails
}
}
Hello! You can load a body text (to html) something like this:
foreach($Item in $fiItems.Items){
if ($Item.Subject -match "Something special") {
$Item.Load()
$Save = ((Get-Date -Format "yyMMdd") + "-" + $Item.Subject[0] + ".html")
New-Item -Path "C:\file\exch\" -name $Save -ItemType file -value $Item.Body.Text
}
}
Currently, I'm trying to get attachment names from E-Mails within a PST-File. I want to do this via Powershell. My code bracket works fine so far, except for one thing. It Just writes System.__ComObject as the attachments name. Any ideas?
## Path where the PSTFiles are
$PSTArr = Get-ChildItem -Path .\ -Recurse
$Attachments = #()
## Processing
ForEach ($PST in $PSTArr) {
if ($PST.Name -like "*.pst") {
[string]$pstPath = $PST.FullName
Write-Output ("Checking File: " + $pstPath)
# Lets see if there is a running outlook process
$oProc = ( Get-Process | where { $_.Name -eq "OUTLOOK" } )
if ( $oProc -eq $null ) { Start-Process outlook -WindowStyle Hidden; Start-Sleep -Seconds 5 }
$outlook = New-Object -ComObject Outlook.Application
# Assign namespace
$namespace = $outlook.GetNamespace("MAPI")
# Add PST
$namespace.AddStore($pstPath)
$pstStore = ( $nameSpace.Stores | where { $_.FilePath -eq $pstPath } )
# Add RootFolder of the PST
$pstRootFolder = $pstStore.GetRootFolder()
# Get attachments of the E-Mails
$Attachments += $pstRootFolder.Items | Select Attachment
# Disconnect PST File
$namespace.GetType().InvokeMember('RemoveStore',[System.Reflection.BindingFlags]::InvokeMethod,$null,$namespace,($pstRootFolder))
}
}
(I'm looping through a Folder with PST's, which works fine)
Thanks for help / answers
Try this:
$Attachments = $pstRootFolder.Items | ForEach-Object {
$_.Attachments | Select-Object -ExpandProperty FileName
}
I am trying to move emails within outlook from one folder to another.
However, my code throws an error on processing of the last email.
"Unable to index into an object of type System.__ComObject."
it moves all other emails to the archive folder ok.
If only 1 email present, it will error....
If 2 or more emails present then it will move all but the last email and then error.
Code is
$filepath="Z:\"
$olFolderInbox = 6
$i=0
$outlook = new-object -com outlook.application;
$ns = $outlook.GetNameSpace("MAPI");
$inbox = $ns.Folders | ? { $_.Name -eq '#Whe ContractorRFPs' };
$trgtfldr = $inbox.Folders | ? { $_.Name -match 'Inbox' };
$mvdfldr = $inbox.Folders | ? { $_.Name -match 'Archived' };
$messages = $trgtfldr.items
for($i=($messages.count -1);$i -gt -1;$i--){
$($messages)[$i].move($mvdfldr)
}
You could set up your loop like this
$messages = $trgtfldr.items
$messageCount = $messages.count
for ($i = $messageCount - 1; $i -ge 0; $i--) {
$message = $messages.GetLast()
$message.move($mvdfldr)
}
Why not use a foreach loop?
$messages = $trgtfldr.items
foreach($message in $messages){
$message.move($mvdfldr)
}