Author Posts

April 17, 2018 at 1:37 pm

I have a task I want performed but I wasn't sure if powershell had the capabilities to do it.

What I'm essentially wanting is something that will monitor an exchange mailbox and whenever an email comes in with an attachment it will strip the attachment and save it to a directory, and if the mailbox gets an email without an attachment it saves the email as a text file to a directory.

Can this task be accomplished with powershell? And if so can anyone point me in the right direction?

Thanks!

April 17, 2018 at 1:40 pm

I'd rate this a very poor use case for PowerShell. It could be done, but PowerShell isn't designed as a background monitoring service, and the modules available for getting into a mailbox are few and far between. This'd be far better as a .NET-written service that taps into Exchange's own event loop, rather than having to continually log into a mailbox and scan for new messages.

PowerShell as any kind of "monitoring tool" isn't a stunning way to start the day, IMO.

April 17, 2018 at 1:59 pm

As Don said, this is possible with PowerShell using EWS, and a PowerShell script can be invoked on startup as a scheduled task to act like a "service" if need. I've done both but probably would be better as an Exchange Transport Agent.

This approach however has its own considerations as you need to be careful with the C# code as you can easily crash the transport service.

April 17, 2018 at 2:02 pm

It probably wouldn't be real time, but more of a 'scan the mailbox every hour, anything with attachment pull them and put them in location XYZ then delete email, anything without an attachment strip to text and put in directory ABC then delete email'.

I dont know if that makes it a better case for powershell or not

April 17, 2018 at 3:07 pm

i actually do this very thing with PS, however i also utilise microsoft orchestrator to do the monitoring


$olFolderInbox = 6 
$outlook = new-object -com outlook.application; 
$ns = $outlook.GetNameSpace("MAPI"); 
$inbox = $ns.GetDefaultFolder($olFolderInbox)
$messages = $inbox.items 
$messcount = $messages.count 




foreach($message in $messages){ 


##############Save Attachments################

$filepath = "c:\temp\moodle\" 
$message.attachments|foreach { 
    Write-Host $_.filename 
    $attr = $_.filename 

    $_.saveasfile((Join-Path $filepath $_.filename))

    $a = $_.filename 
    If ($a.Contains("")) { 
    $_.saveasfile((Join-Path $filepath $a)) 
                             } 
  } 


Search-Mailbox "sender" -SearchQuery "from:moodle@domain.com" -DeleteContent -Force

may take some fettling to suite your needs but will be a start, for this example i expect a mail every hours with an attachment. and i am only interested in one mail at a time hence removing the mails at the end.

April 17, 2018 at 3:23 pm

I appreciate the help Mark! Unfortunetly this is a shared mailbox that I don't actually connect to via Outlook but was looking for a way to do this on the exchange side.

I'm using the below code to just query my mailboxes last 10 emails, but I'm not sure if theres a way to write each to a text file

param($mailboxName = "name@domain.com",
$smtpServerName = "8.8.8.8"
)
 
# Load the EWS Managed API
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
 
  $Exchange2016 = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016
  
  # create EWS Service object for the target mailbox name
  $exchangeService = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList $Exchange2016
  
  $exchangeService.UseDefaultCredentials = $true
  $exchangeService.AutodiscoverUrl($mailboxName)
 
  # bind to the Inbox folder of the target mailbox
  $inboxFolderName = [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox
  $inboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($exchangeService,$inboxFolderName)
 
  # Optional: reduce the query overhead by viewing the inbox 10 items at a time
  $itemView = New-Object -TypeName Microsoft.Exchange.WebServices.Data.ItemView -ArgumentList 10
  # search the mailbox for messages older than 15 minutes
  $dateTimeItem = [Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived
  $15MinutesAgo = (Get-Date).AddMinutes(-15)
  $searchFilter = New-Object -TypeName Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThanOrEqualTo -ArgumentList $dateTimeItem,$15MinutesAgo
  $foundItems = $exchangeService.FindItems($inboxFolder.Id,$searchFilter,$itemView)

With $foundItems I thought I would be able to see the body/save attachments but I'm not able to do either (from what I can tell)

  foreach($item in $foundItems)
  {
	#displays nothing
	$item.body
  
	if($item.hasattachments)
	{
		#somehow save attachment to a file location..but unsure how
	}
	else
	{
		#save the email as a text file to a file location
	}
  }

April 17, 2018 at 3:42 pm

Try doing $Item | Get-Member — that will show you exactly what properties and methods are available for you to pull from. 🙂

April 17, 2018 at 9:26 pm

Hey guys,

Figured out a way if anyone is interested...for me this will be a daily task that cleans out a mailbox by finding all the emails with attachments and strips the attachment and saves it to a directory and all emails without attachments (which the body is a identical to the attachments in the other emails) and saves it as text to another directory.

Thanks for the help guys

# Name of the mailbox to pull attachments from
$MailboxName = 'mailbox@domain.com'
 
# Location to move attachments
$downloadDirectory = '\\wheverever\attachmentdirectory\'
 
# Path to the Web Services dll
$dllpath = "C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll"
[VOID][Reflection.Assembly]::LoadFile($dllpath)
 
# Create the new web services object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016)
 
# Create the LDAP security string in order to log into the mailbox
$sidbind = "LDAP://"
$aceuser = [ADSI]$sidbind
 
# Auto discover the URL used to pull the attachments
$service.AutodiscoverUrl($aceuser.mail.ToString())
 
# Get the folder id of the Inbox
$folderid = new-object  Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
$InboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)

$view = new-object Microsoft.Exchange.WebServices.Data.ItemView(2000)
$frFolderResult = $InboxFolder.FindItems($view)
 
 #Gets the properties of the body in text form, not html
$PropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
$PropertySet.RequestedBodyType = [Microsoft.Exchange.WebServices.Data.BodyType]::Text 
 
# Loop through the emails
foreach ($miMailItems in $frFolderResult.Items){
	
	if($miMailItems.HasAttachments)
	{
		# Load the message with a TEXT body
		$miMailItems.Load($propertySet)
	 
		# Loop through the attachments
		foreach($attach in $miMailItems.Attachments){
	 
			# Load the attachment
			$attach.Load()
	 
			# Save the attachment to the predefined location
			$fiFile = new-object System.IO.FileStream(($downloadDirectory + “\” + (Get-Date).Millisecond + "_" + $attach.Name.ToString()), [System.IO.FileMode]::Create)
			$fiFile.Write($attach.Content, 0, $attach.Content.Length)
			$fiFile.Close()
		}
	}
	else
	{
		# Load the message with a TEXT body
		$miMailItems.Load($propertySet)
		#path is whatever\subject_time
		$Name = "$($miMailItems.subject)_$(get-date($miMailItems.DateTimeReceived) -Format MMddyyy_HHmmss).txt"
		
		$Name = $Name -replace [Regex]::Escape("["), ""
		$Name = $Name -replace [Regex]::Escape("]"), ""
		$Name = $Name -replace [Regex]::Escape(":"), ""
		$Name = $Name -replace ";", ""
		
		$path = ("\\SERVER\EMAIL_AS_TEXT\" + $Name)
		$miMailItems.body.text | Out-File $path
	}
    # Mark the email as read
	$miMailItems.isread = $true
    $miMailItems.Update([Microsoft.Exchange.WebServices.Data.ConflictResolutionMode]::AlwaysOverwrite)
 
    #Delete the message (optional)
    [VOID]$miMailItems.Move("DeletedItems")
}