How to accept confirmation Automatically in PowerShell for Outlook
I have script for Export attachments from email from Outlook - see next
It works correctly on one PC, but on another PC is there a problem:
Outlook gives message and wants answer:
Permit Denny Help
If I manually click on Permit or Denny it works correctly. I want to automate it.
Can you give me some suggestion how to do it in PowerShell?
I have tried to set Outlook to not give this message but I didn’t success.
My script:
# <-- Script --------->
# script works with outlook Inbox folder
# check if email have attachments with ".txt" and save those attachments to $filepath
# path for exported files - attachments
$filepath = "d:\Exported_files\"
# create object outlook
$o = New-Object -comobject outlook.application
$n = $o.GetNamespace("MAPI")
# $f - folder „dorucena posta“ 6 - Inbox
$f = $n.GetDefaultFolder(6) # 6 - Inbox
# select newest 10 emails, from it olny this one with attachments
$f.Items| select -last 10| Where {$_.Attachments}| foreach {
# process only unreaded mail
if($_.unread -eq $True) {
# processed mail set as read, not to process this mail again next day
$_.unread = $False
$SenderName = $_.SenderName
Write-Host "Email from: ", $SenderName
# process all attachments
$_.attachments|foreach {
$a = $_.filename
If ($a.Contains(".txt")) {
Write-Host $SenderName," ", $a
# copy *.txt attachments to folder $filepath
$_.saveasfile((Join-Path $filepath "$a"))
}
}
}
}
Write-Host "Finish"
# <------ End Script ---------------------------------->
I found that security prompt is generate on line
" $SenderName = $_.SenderName "
Realy I dont need to use SenderName and I deleted this line.
Now script works OK without any message.
Related
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...
#>
I need to run a scheduled task that will trigger at 7am daily and search a folder for any files that have a modified date that’s changed in the last day or 24 hours. I’m stuck on whether what I’ve done so far is the best way to do this check and also I’m not sure how to get this to email out a file with the list of file or files that have changed in the last 24 hours. I don’t think the FileSystemChecker is worth the amount of time it seems to get that running as I’ve read it can be troublesome. I’m trying to do something that just looks for files with a modified date that’s changed. I don’t have to look for deleted files or added files email the folder. If nothing has changed then I need to send the email to a different group of folks than I do if there are files that changed. I’m stuck on how to do the email part. The other part I’m stuck on is getting this to accept a unc path so I can run the task from another server.
Get-Item C:\folder1\folder2\*.* | Foreach { $LastUpdateTime=$_.LastWriteTime $TimeNow=get-date if (($TimeNow - $LastUpdateTime).totalhours -le 24) { Write-Host "These files were modified in the last 24 hours "$_.Name } else { Write-Host "There were no files modified in the last 24 hours" } }
First of all, do not try and cram all code in a single line. If you do, the code gets unreadable and mistakes are easily made, but hard to spot.
What I would do is something like this:
$uncPath = '\\Server1\SharedFolder\RestOfPath' # enter the UNC path here
$yesterday = (Get-Date).AddDays(-1).Date # set at midnight for yesterday
# get an array of full filenames for any file that was last updates in the last 24 hours
$files = (Get-ChildItem -Path $uncPath -Filter '*.*' -File |
Where-Object { $_.LastWriteTime -ge $yesterday }).FullName
if ($files) {
$message = 'These files were modified in the last 24 hours:{0}{1}' -f [Environment]::NewLine, ($files -join [Environment]::NewLine)
$emailTo = 'folskthatwanttoknowaboutmodifications#yourcompany.com'
}
else {
$message = 'There were no files modified in the last 24 hours'
$emailTo = 'folskthatwanttoknowifnothingismodified#yourcompany.com'
}
# output on screen
Write-Host $message
# create a hashtable with parameters for Send-MailMessage
$mailParams = #{
From = 'you#yourcompany.com'
To = $emailTo
Subject = 'Something Wrong'
Body = $message
SmtpServer = 'smtp.yourcompany.com'
# any other parameters you might want to use
}
# send the email
Send-MailMessage #mailParams
Hope that helps
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 ""
Running the following script:
$FileToCheck = Get-Item -Path $folder/test.zip -ErrorAction SilentlyContinue
$EmailSplat = #{
To = 'business#email.com'
CC = 'admin#email.com'
#SmtpServer = 'smtp.server.net'
From = 'my#email.com'
Priority = 'High'
}
$folder = "C:\test\"
# first condition: 'If the file does not exist, or was not created today, an e-mail should be sent that states "File not created" or similar.'
if ((-not $FileToCheck) -or ($FileToCheck.CreationTime -le (Get-Date).AddDays(-1))) {
$EmailSplat.Subject = 'File not Found or not created today'
$EmailSplat.building = 'This is the email building'
Send-MailMessage #EmailSplat
# second condition 'If the file exists and was created today, but has no content, no e-mail should be sent.'
} elseif (($FileToCheck) -and ($FileToCheck.Length -le 2)) {
#third condition and the default condition if it does not match the other conditions
} else {
$EmailSplat.Subject = 'Active Directory Accounts To Check'
$EmailSplat.building = Get-Content -Path/test.zip //maybe add the file??
Send-MailMessage #EmailSplat
}
Goal: Check if file .zip is complete, once is complete it sends an email to let business that file is good to go. I'm running the script, getting no errors but no alert emails either.
Build on: Add maybe a time that the email could be sent. For example, script will run every morning, at 6:00 the email gets sent to users to notify that the file is complete.
The $Folder variable needs to be on the line before the "$FileToCheck = Get-Item..." line since it uses that variable.
There is no such parameter "building" in the Send-MailMessage cmdlet. I think you are after Body, since you are trying to Get-Content...?
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/send-mailmessage?view=powershell-6
Another note is that Get-Content won't be able to read the contents of a zip file. You would need to unzip the file then read the file or add the file as an attachment.
This is an example of using Get-Content on an zip file that has only a text file in it:
PK
ï¸ÌN_S³ test.txtsdafasdfPK
ï¸ÌN_S³ test.txtPK 6 .
Add $ErrorActionPreference = "Stop" at the top of the script, so errors are shown.
Use the Attachments parameter to add files, building is not a valid parameter of Send-MailMessage
Get-Content is not needed, just add the path to the attachment:
$EmailSplat.Attachments = "Path/test.zip"
So something like this:
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest
$folder = "C:\test"
$fileToCheck = Get-Item -Path (Join-Path $folder test.zip) -ErrorAction SilentlyContinue
$emailOptions = #{
"To" = "business#email.com"
"CC" = "admin#email.com"
"SmtpServer" = "smtp.server.net"
"From" = "my#email.com"
"Priority" = "High"
}
# first condition: If the file does not exist, or was not created today, an e-mail should be sent that states "File not created" or similar.
if ((-not $fileToCheck) -or ($fileToCheck.CreationTime -le (Get-Date).AddDays(-1)))
{
$emailOptions.Subject = "File not Found or not created today"
Send-MailMessage #emailOptions
}
elseif ($fileToCheck -and ($fileToCheck.Length -le 2))
{
# second condition: If the file exists and was created today, but has no content, no e-mail should be sent.
}
else
{
# third condition and the default condition if it does not match the other conditions
$emailOptions.Subject = "Active Directory Accounts To Check"
$emailOptions.Attachments = $fileToCheck.FullName
Send-MailMessage #emailOptions
}
Here's what is happening. I have this handy powershell script I've written up to connect to EWS using Impersonation, read plain-text files containing email messages, and send them one at a time in a loop. At the end there is a little Inbox and Sent Items folder clean up. (Kudos to my friends at stackoverflow, as many of the issues I already encountered in writing this have been solved by looking up the answers right here.)
Everything works great except ToRecipients.Add, which works a little _too_well_.
$EmailMessage.ToRecipients.Add($DName)
When the loop iterates to read the next file, the $EmailMessage.ToRecipients array retains it's information after executing
$EmailMessage.SendAndSaveCopy($SentItems.Id).
I had somehow expected a SendAndSaveCopy to discard all information in $EmailMessage, but it does not (Send, Save, and then Discard, right?).
So when sending one file, there is no issue whatsoever. But when sending multiple files, each subsequent file just adds its email addresses on to the end of the $EmailMessage.ToRecipients array. So the second file is sent to the first file's recipients AND the second file's recipients. The third file goes to first, second, and third file recipients. The more files, the bigger the mess.
I've tried various things at the end of the loop, before it iterates, to clear out the ToRecipients, but have not had any luck. Powershell rejects any direct assignment of any kind. I expected there to be a ToRecipients.Clear or .Empty or .Remove or something like that, but I haven't found anything that works. These approaches fail:
Remove-Variable $EmailMessage
Remove-Variable $EmailMessage.ToRecipients
Remove-Variable $EmailMessage.ToRecipients[x] #rejects all counter values
Clear-Variable $EmailMessage
Clear-Variable $EmailMessage.ToRecipients
Clear-Variable $EmailMessage.ToRecipients[x] #rejects all counter values
$EmailMessage.ToRecipients = NULL
$EmailMessage.ToRecipients = ""
I can loop through and read every value, get a count of how many ToRecipients there are, print them out, and all sorts of other nifty things, but it seems to be read-only and I haven't found any way to clear them out before iterating.
I have thought of a couple "band-aid" approaches, which I haven't tried yet:
1. Put it in a function with local variables, and $EmailMessage will poof on the function exit (with any luck).
2. Just process one file, and at the end of the script right before exiting, check to see if any files remain to process, and call the script again using Invoke-Expression.
Really, this seems like overkill, and a work around to the original problem rather than a direct fix. I should just be able to clear out the email addresses before the loop iterates, right?
Here is the full code:
<#
Script Overview
1. Opens EWS services for shared mailbox using an appid
2. Reads files from c:\outbox\ containing email to send, one per file
3. Sends the mail message in each file found
4. Renames each file found
5. Moves sent items into c:\sentemail\
6. Deletes any emails in the Inbox or Sent Items folders that are older than 14 days ($purgebeforedate))
#>
#Variables
## Define UPN of the Account that has impersonation rights
$AccountWithImpersonationRights = "myappid"
$appidpasswd = "mysupersecretpassword"
##Define the SMTP Address of the mailbox to impersonate
$MailboxToImpersonate = "autoemail#mysite.mydomain"
##Define CAS URL (Client Access Server) - can be found in system registry if necessary
$CASURL = "https://my.site.and.domain/ews/exchange.asmx"
$ewsApiDownload = "http://www.microsoft.com/en-us/download/details.aspx?id=35371"
## Define Exchange web services DLL path (requires 2.0 for our environment)
$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"
##Define folders for incoming and archived email files
$GetFolder = "c:\outbox\" #folder where input email message files are in plaintext
$SaveFolder = "c:\sentemail\" #folder to move sent email files to after processed
$PurgeBeforeDate = (Get-Date (Get-Date).AddDays(-14) -format G) #format m/d/yyyy hh:mm:ss ap- ::Date is 14 days ago [AddDays(-14)]
##Define location of error log on the local windows server where this script runs
$errorLog = "c:\sentemail\MyAutoSentEmail.log"
# Post a note in the error log that the script has started
$date = Get-Date -format G #Default date and time output used in log file.
Add-Content $errorLog $date": Email Service script started."
Add-Content $errorLog $date": Old sent email delete cutoff - $PurgeBeforeDate"
## Load Exchange web services DLL
Import-Module $dllpath
#Exit script if importing EWS API fails.
if ($? -eq $false)
{
$date = Get-Date -format s #Default date and time output used in log file. (was G before)
Add-Content $errorLog $date": Faied to load EWS, ensure it is installed:"$ewsApiDownload
Add-Content $errorLog $date": EWS API expected location:" $dllpath
Add-Content $errorLog $date": Failed to import the EWS API. Script terminated."
exit
}
## Set Exchange Version (our site requires Exchange2010_SP2)
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
## Create Exchange Service Object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
#Get valid Credentials using UPN for the ID that is used to impersonate mailbox
$service.Credentials = New-Object System.Net.NetworkCredential($AccountWithImpersonationRights, $appidpasswd);
## Set the URL of the CAS (Client Access Server)
$service.Url = New-Object Uri($CASURL)
##Login to Mailbox with Impersonation
#Write-Host 'Using ' $AccountWithImpersonationRights ' to Impersonate ' $MailboxToImpersonate
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxToImpersonate );
#Connect to the Inbox and display basic statistics
$SentFolder = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems,$ImpersonatedMailboxName)
$SentItems = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$SentFolder)
if ($? -eq $false) #Exit script if binding to folder fails.
{
Add-Content $errorLog $date": Failed to bind to the specified mailbox folder. Script terminated."
exit
}
#For info only, can uncomment to get a list of how many items are in the Inbox and how many are unread
#Write-Host 'Total Item count for Inbox:' $Inbox.TotalCount
#Write-Host 'Total Items Unread:' $Inbox.UnreadCount
#get the filenames in the outgoing-email directory into $files so they may be read one at a time
$files = Get-ChildItem $GetFolder
if ($files.Count -gt 0)
{ #if there are files to process
#Create new email message to send out
$EmailMessage = New-Object Microsoft.Exchange.WebServices.Data.EmailMessage -ArgumentList $service
$EmailMessage.Body = New-Object Microsoft.Exchange.WebServices.Data.MessageBody
$EmailMessage.Body.BodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::HTML
$EmailMessage.From = $MailboxToImpersonate
for ($i=0; $i -lt $files.Count; $i++) { #start for $i loop for each filename found
$emailfile = Get-Content $files[$i].FullName #read file into $emailfile
$To = "empty" #init $To to empty
$Subject = "empty" #init $Subject to empty
$Body = "empty" #init $Body to empty
$Bodytext = "" #init $Bodytext - place to accumulate body lines before adding to email message, gets html return on end of each line
$Bodyhtml = "" #init $Bodyhtml - body lines are placed here in parallel, no html carriage return added
foreach ($Data in $emailfile) { #put each line of the file into $Data and process it
if ( ($Data.StartsWith("to:")) -and ($Subject -eq "empty") )
{ #add recipients to the email message, all to: lines must be before the subject: line
$To = "found" #mark $to line(s) as having been found
Write-Host "TO: line found - " $Data
$Data = $Data.Substring(3) #get all chars after subject:
if ($Data.Contains(";"))
{ #if the email address passed in contains multiples separated by semicolons, split and add them
$DataNames = $Data -split ';' #split $Data into an array of semicolon separated substrings
foreach ($DName in $DataNames) { #validate each semicolon separated email address and add it
$EmailMessage.ToRecipients.Add($DName)
Write-Host $date": TO: address added: "$DName
} #end foreach $DName in $DataNames
} else { #else if no semicolon, just add what you have
$EmailMessage.ToRecipients.Add($Data)
Write-Host $date": TO: address added: "$DName
} #end if email address passed doesn't contain a semicolon
} #end add recipients to $To
if ( ($Data.StartsWith("subject:")) -and ($Body -eq "empty") )
{ #add subject line to the email message, must come before any body: line(s)
$Data = $Data.Substring(8) #get all chars after subject:
$EmailMessage.Subject = $Data
$Subject = "found" #mark $subject line as having been found
Write-Host "SUBJECT: line found - " $Data
} #end add recipients to $To
if ($Data.StartsWith("body:"))
{ #first line of body found
$Data = $Data.Substring(5) #get all chars after subject:
$Bodytext = $Data
$Bodyhtml = $Data
$Body = "found" #mark $body line as having been found
Write-Host "BODY: line found - " $Data
} #end add recipients to $To
elseif ($Body -eq "found")
{ #accumulate the remaining lines into the $bodytext string with '<br />' as carriage returns between lines
$Bodytext = -join($Bodytext, '<br />', $Data); #text lines get an html carriage return
$Bodyhtml = -join($Bodyhtml, $Data); #html lines don't get an html carriage return added, shouldnt need it
Write-Host "body data found - " $Data
} #end accumulate the remaining lines into the $bodytext string with '<br />' as carriage returns between lines
} #end foreach - done processing this text file
$CurrentDateAndTime = $(get-date -f yyyy-MM-dd-HH-mm-ss) #date format to add to the front of the filename when moving/renaming
$OldFileName = $files[$i].FullName
$NewFileName = [io.path]::GetFileName($OldFileName) #just the filename without the path for rename later
Write-Host "OldFileName - " $OldFileName
if ( ($To -eq "found") -and ($Subject -eq "found") -and ($Body -eq "found") )
{ #if all parts found, add body text to the new email message and send it off
if ( ( $Bodyhtml -Match "<" ) -and ( $Bodyhtml -Match ">" ) ) {
$EmailMessage.Body.Text = $Bodyhtml } else {
$EmailMessage.Body.Text = $Bodytext
} #end if html set body.text to html, otherwise set to bodytext with '<br />' crlf's added
$EmailMessage.SendAndSaveCopy($SentItems.Id)
Add-Content $files[$i].FullName "EMAIL-SENT-AT: $CurrentDateAndTime"
Add-Content $errorLog $date": Email File Sent Successfully:"$OldFileName
$NewFileName = -join($CurrentDateAndTime, "-$i-", $NewFileName)
} Else { #add error messages to output file, and select a name that includes NOTSENT so it is obvious
Add-Content $errorLog $date": NOTSENT-ERROR in file: "$OldFileName
if ( $To -eq "empty") {
Add-Content $files[$i].FullName "NOTSENT-ERROR: NO TO: LINE FOUND"
Add-Content $errorLog $date": NOTSENT-ERROR: NO TO: LINE FOUND"
}
if ( $Subject -eq "empty") {
Add-Content $files[$i].FullName "NOTSENT-ERROR: NO SUBJECT: LINE FOUND"
Add-Content $errorLog $date": NOTSENT-ERROR: NO SUBJECT: LINE FOUND"
}
if ( $Body -eq "empty") {
Add-Content $files[$i].FullName "NOTSENT-ERROR: NO BODY: LINE FOUND"
Add-Content $errorLog $date": NOTSENT-ERROR: NO BODY: LINE FOUND"
}
$EmailMessage.Delete #delete new composition if it is not set up properly
$NewFileName = -join($CurrentDateAndTime, "-$i-NOTSENT-", $NewFileName) #note in filename if email was not sent
} #end send-or-not-to-send if
Write-Host "NewFileName - " $NewFileName
#rename and move the file with the date and time it was sent (or tried to be sent)
$NewFileName = -join($SaveFolder, $NewFileName)
Move-Item $OldFileName $NewFileName
$EmailMessage.ToRecipients.Clear() ##BenH - clears ToRecipients before next file is processed so they don't accumulate
} #end for $i loop for each filename found
} #end if there are files to process
if ($? -eq $false)
{
Add-Content error.log $date": error sending message. Script terminated."
exit
}
#Delete items from the Sent Items folder that are too old
#Get the ID of the folder to move to, by searching up from the mailbox root folder
$RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service, [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
$fvFolderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1)
$fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
$SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,"Sent Items")
$findFolderResults = $RootFolder.FindFolders($SfSearchFilter,$fvFolderView) #search from mailbox root folder for a folder named Sent Items
if ($? -eq $false)
{
#Write-Host 'Unable to locate destination folder:' $findFolderResults
Add-Content error.log $date": Unable to locate the Sent Items folder for cleanup. Script terminated."
exit
}
$sentitemsfolder = $findFolderResults.Folders[0] #save reference to the Sent Items folder in $sentitemsfolder
$SfSearchFilterI = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,"Inbox")
$findFolderResults = $RootFolder.FindFolders($SfSearchFilterI,$fvFolderView) #search from mailbox root folder for a folder named Inbox
if ($? -eq $false)
{
#Write-Host 'Unable to locate destination folder:' $findFolderResults
Add-Content error.log $date": Unable to locate the Inbox folder for cleanup. Script terminated."
exit
}
$inboxfolder = $findFolderResults.Folders[0] #save reference to the Inbox folder in $inboxfolder
#search through the Sent Items and Inbox folders for items older than $purgebeforedate and soft delete them
$puItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(500, 0, [Microsoft.Exchange.WebServices.Data.OffsetBasePoint]::Beginning)
$puItemView.PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly, [Microsoft.Exchange.WebServices.Data.ItemSchema]::Subject, [Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived)
$puItemView.Traversal = [Microsoft.Exchange.WebServices.Data.ItemTraversal]::Shallow
#Sent Items scan for old items to delete
$puItems = $null #init FindItems results to null before executing find
do
{ #start do
$puItems = $service.FindItems($sentitemsfolder.Id,$puItemView) #find all items in Sent Items
if ($puItems.Items.Count -gt 0)
{ #if Sent Items folder not empty, inspect all items found
foreach($Item in $puItems.Items)
{
if ($item.datetimereceived -le $PurgeBeforeDate) #compare email date to purge cutoff date
{
#Write-Host "Delete Item with date time received" $item.DateTimeReceived
[void]$item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete)
#Note: exchange email internal date is in format of : 5/31/2016 8:02:19 AM (get-date -format G)
} #end compare email date to purge cutoff date
} #end foreach $Item in $puItems.items
} #end if Sent Items folder not empty
$puItemView.Offset += $puItems.Items.Count
} #end do
while($puItems.MoreAvailable -eq $true)
#Inbox scan for old items to delete
$puItems = $null #init FindItems results to null before executing find
do
{ #start do
$puItems = $service.FindItems($inboxfolder.Id,$puItemView) #find all items in Sent Items
if ($puItems.Items.Count -gt 0)
{ #if Sent Items folder not empty, inspect all items found
foreach($Item in $puItems.Items)
{
if ($item.datetimereceived -le $PurgeBeforeDate) #compare email date to purge cutoff date
{
#Write-Host "Delete Item with date time received" $item.DateTimeReceived
[void]$item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::SoftDelete)
#Note: exchange email internal date is in format of : 5/31/2016 8:02:19 AM (get-date -format G)
} #end compare email date to purge cutoff date
} #end foreach $Item in $puItems.items
} #end if Sent Items folder not empty
$puItemView.Offset += $puItems.Items.Count
} #end do
while($puItems.MoreAvailable -eq $true)
#script done
Add-Content $errorLog $date": Email Service script finished."
exit
##############################################################################
And here are some sample email input files, where lines start with to:, subject:, and the first line of the message body starts with body:
Sample File 1:
to:sam.lemon#lemonade.org
subject:ews mail test
body:testing line 1
testing line 2
testing line 3
testing line 4
done testing
Sample File 2:
to:bill.hickock#wild.west.org
subject:ews mail test
body:testing line 1
testing line 2
testing line 3
testing line 4
done testing
With these two sample files, the first one would be sent to sam.lemon#lemonade.org as expected. But the second one would go to both sam.lemon#lemonade.org and bill.hickock#wild.west.org, which it should not, since the second file is only for bill.hickock#wild.west.org.
Thanks in advance!
Zistrosk
#BenH - Here's what I coded and what came out in white on the Powershell ISE console. No errors or warnings, just these info messages, and the email addressees were not cleared or removed, same symptoms of the problem persisted. Does my coding look right?
$EmailMessage.ToRecipients.Clear
MemberType : Method
OverloadDefinitions : {void Clear()}
TypeNameOfValue : System.Management.Automation.PSMethod
Value : void Clear()
Name : Clear
IsInstance : True
$EmailMessage.ToRecipients.Remove
MemberType : Method
OverloadDefinitions : {bool
Remove(Microsoft.Exchange.WebServices.Data.EmailAddress
emailAddress)}
TypeNameOfValue : System.Management.Automation.PSMethod
Value : bool
Remove(Microsoft.Exchange.WebServices.Data.EmailAddress
emailAddress)
Name : Remove
IsInstance : True
Note: added the following line above, thanks to #BenH, which fixes the issue and clears the ToRecipients array before the loop iterates and the next file is read, preventing an unwanted accumulation of TO addresses:
$EmailMessage.ToRecipients.Clear() ##BenH - clears ToRecipients before next file is processed so they don't accumulate