Downloading .xlsx attachment from Outlook of Specific Date using Powershell - powershell

I have the below script. This $Tests shows the list of .xlsx attachment of specific date but is not able to download and throws an error. Please find the below script.
Add-type -assembly "Microsoft.Office.Interop.Outlook"
$olDefaultFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = New-Object -comobject Outlook.Application
$mapi = $outlook.GetNameSpace(“MAPI”)
$inbox = $mapi.GetDefaultFolder(6)
$FilePath= "c:\temp\Test\"
$subfolder = $inbox.Folders | Where-Object {$_.Name -eq “Test”}
$mail=$subfolder.Items |Select-Object -Property "ReceivedTime",#{name="Attachments";expression={$_.Attachments|%{$_.DisplayName}}} | Where-Object{$_.attachments -match ".xlsx" -and ($_.receivedtime -match "9/15/2020")} | Select-Object "attachments"
$Test = $mail.attachments
foreach ($out in $test) {$_.attachments|foreach {
Write-Host $_.filename
$Filename = $_.filename
If ($out.Contains("xlsx")) {
$_.saveasfile((Join-Path $FilePath "$out")) }}}
I am able to filter the .xlsx Attachments with Specific Date. But after this, I don't know how to save/download them.

Working with com objects can be rather frustrating in powershell. I recommend you get extremely familiar with Get-Member. You really have to interrogate each object. I've simplified your script as well as tested thoroughly. It will download each matching attachment (name) from each match email (received date)
Add-type -assembly "Microsoft.Office.Interop.Outlook"
$olDefaultFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = New-Object -comobject Outlook.Application
$mapi = $outlook.GetNameSpace(“MAPI”)
$inbox = $mapi.GetDefaultFolder(6)
$FilePath= "c:\temp\Test\"
$subfolder.Items | Where-Object {$_.receivedtime -match "9/20/2020" -and $($_.attachments).filename -match '.xlsx'} | foreach {
$filename = $($_.attachments | where filename -match '.xlsx').filename
foreach($file in $filename)
{
Write-Host Downloading $file to $filepath -ForegroundColor green
$outpath = join-path $filepath $file
$($_.attachments).saveasfile($outpath)
}
}
You may use this for more of an "in-line" approach.
Add-type -assembly "Microsoft.Office.Interop.Outlook"
$olDefaultFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = New-Object -comobject Outlook.Application
$mapi = $outlook.GetNameSpace(“MAPI”)
$inbox = $mapi.GetDefaultFolder(6)
$FilePath= "c:\temp\Test\"
$subfolder.Items | Where-Object {$_.receivedtime -match "9/20/2020" -and $($_.attachments).filename -match '.xlsx'} | foreach {
foreach($attachment in $($_.attachments | where filename -match '.xlsx'))
{
Write-Host Downloading $attachment.filename to $filepath -ForegroundColor green
$attachment.SaveAsFile((join-path $FilePath $attachment.filename))
}
}

Related

Powershell Error: "There is not enough memory or disk to complete the operation"

I'm running a powershell script to read multiple word documents. When running to around 700 documents, it shows error "There is not enough memory or disk to complete the operation".
Here is my code
$excel = New-Object -ComObject Excel.application
$source = 'powershell/attachments'
$docs = Get-ChildItem -Path $source -Recurse -Filter *cover*.docx
$XL = New-Object -ComObject Excel.Application
#Open the workbook
$WB = $XL.Workbooks.Open("powershell/result.xlsx")
#Activate Sheet1, pipe to Out-Null to avoid 'True' output to screen
$WB.Sheets.Item("Sheet1").Activate() | Out-Null
$SearchArray = #('employment', 'source of income', 'US address', 'residential address', 'ID', 'driver license', 'visa', 'passport', 'I-20', 'Social Security Card', 'information update form', 'w9', 'w8', 'tax', 'email address')
$word = New-Object -ComObject Word.application
foreach ($doc in $docs) {
$Document = $word.Documents.Open($doc)
$CVSInfo = $Document.Paragraphs | ForEach-Object{
foreach ($SerchText in $SearchArray) {
$_.Range.Text | Where-Object { $_-match $SerchText} | ForEach-Object {
$_-split ' ' | Select-Object -Last 1
}
}
}
$PathArray = $doc.FullName
#Launch Excel
#Find first blank row #, and activate the first cell in that row
$FirstBlankRow = $($xl.ActiveSheet.UsedRange.Rows)[-1].Row + 1
$XL.ActiveSheet.Range("A$FirstBlankRow").Activate()
#Create PSObject with the properties that we want, convert it to a tab delimited CSV, and copy it to the clipboard
$Record = [PSCustomObject]#{
'ID' = $PathArray
'Context' = $CVSInfo
}
$Record | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip
#Paste at the currently active cell
$XL.ActiveSheet.Paste() | Out-Null
# Save and close
$WB.Save() | Out-Null
}
$WB.Close() | Out-Null
$XL.Quit() | Out-Null
#Release ComObject
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($XL)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word)
Thanks in advance!
It looks like you have hundreds of Word documents open. Don't forget to close them in each iteration of the loop:
$Document.Close()

how to access email from a shared outlook mailbox

I need to get a the latest email from a shared outlook mailbox.
The shared inbox is eg. "Server Backup" and the email that i want to get is located
inbox\backup report\
Here is some code that I can access my Inbox but can't do it for a subfolder
$Outlook = New-Object -ComObject Outlook.Application
$OutlookFolders = $Outlook.Session.Folders.Item(1).Folders
$OutlookInbox = $Outlook.session.GetDefaultFolder(6)
#read the latest email
$latestmail=$OutlookInbox.items | select -last 1
What you are after is to navigate a little further down.
$outlook = New-Object -Com Outlook.Application
$MAPI = $Outlook.GetNamespace("MAPI")
# Gets all mailboxes tied to the account
$Mailbox = $MAPI.Folders("SharedEmail#Company.com")
# Gets the Inbox folder
$Inbox = $mailbox.Folders("Inbox").Folders("backup report")
# Shows all emails from the Inbox
$contents = $Inbox.Items
$contents.Sort("ReceivedTime", $true)
$contents | select Subject, SenderName, CreationTime -First 1
EDIT: Since the above didn't work, try the below. It will search for the Inbox, then pipe that through to look for the Server Backup.
$Outlook = New-Object -ComObject Outlook.Application
$OutlookFolders = $Outlook.Session.Folders.Item(1).Folders
(($OutlookFolders | Where-Object {$_.FolderPath -like "*Inbox*"}).Folders | `
Where-Object {$_.FolderPath -like "*Server Backup*"}).Items | `
select Subject, SenderName -Last 1
$outlook = New-Object -comobject outlook.application
$namespace = $outlook.application.GetNamespace("MAPI")
$folder = $namespace.GetDefaultFolder(6)
#goto the inbox\backup eport and select the latest email and place it in variable
#$newreport
$newreport = $namespace.Folders.Item("Server
Backup").Folders.Item('Inbox').Folders.item('backups').items | select -first 1
Add-Type -assembly "Microsoft.Office.Interop.Outlook"
Add-Type -assembly "System.Runtime.Interopservices"
$outlook = new-object -com outlook.application;
$namespace = application.GetNameSpace("MAPI")
$i = 1
$Sender = "abc#123.com" # email id
$Sender1 = "cde#123.com" # email id
$myRecipient = $namespace.CreateRecipient("Email Id") # Email id of the shared mail box
$inbox = $Namespace.GetSharedDefaultFolder($myRecipient ,[Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
# Save email contect to a file in a location
$inbox.Items | ?{$_.SenderName -match $Sender -and $_.UnRead -eq "No" } | sort receivedtime -desc |
%{
$Filepath = "C:\temp\" + "email" + $filecount +".txt"
echo $_.body | Out-File -FilePath $Filepath #do stuff with body
$i = $i + 1
$filecount = " (" + $i + ")"
$_.Unread= $False #mark as read
}
# Save email attachment to a location
$inbox.Items | ?{$_.SenderName -match $Sender1 -and $_.UnRead -eq "No" } | sort receivedtime -desc |
%{
$Filepath = "C:\Temp\"
$_.attachments| ?{$_.saveasfile((Join-Path $filePath $_.FileName)) }
$_.Unread= $False #mark as read
}

Powershell and Outlook

I have a problem readiing emails from an Inbox using powershell - however I can red emails from another folder (Test) without a problem. The code I have is
$account = "aaaa.bbb#xxxx.net"
$o = New-Object -comobject outlook.application
$n = $o.GetNamespace(“MAPI”)
$Account = $n.Folders | ? {$_.Name -eq $account}
$f = $Account.Folders | ? {$_.Name -match "Test"}
$f.Items | ForEach {
Do Stuff
}
How do I amend the code so it read the emails from the Inbox and not the folder Test. Another thing is that there's another email account attached and that to has an inbox. How do I make sure I'm looking at the Inbox for the email address specified?
Thanks in Advance
G
I'm doing something similar and I'm using the following (I haven't tested yet on multiple mailboxes yet but it works for my own email)
Add-type -assembly "Microsoft.Office.Interop.Outlook" | out-null
$olFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = new-object -ComObject outlook.application
$namespace = $outlook.GetNameSpace("MAPI")
$emailAddress = "..."
$recipient = $namespace.CreateRecipient($emailAddress)
$folder = $namespace.GetSharedDefaultFolder($recipient, $olFolders::olFolderInbox)

calendar, exporting Outlook emails to CSV

My job is to export emails from Outlook default inbox folder to CSV file.
Here is the script:
cls
Function Get-OutlookInBox #getoutlookcontent
{
Add-type -assembly "Microsoft.Office.Interop.Outlook" | out-null
$olFolders = "Microsoft.Office.Interop.Outlook.olDefaultFolders" -as [type]
$outlook = new-object -comobject outlook.application
$namespace = $outlook.GetNameSpace("MAPI")
$folder = $namespace.getDefaultFolder($olFolders::olFolderInBox)
$folder.items |
Select-Object -Property Subject, ReceivedTime, SenderName, to
}
write-host "select date greater or equal to and hit enter"
Function date1
#calendar
{
[void]
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")
$objForm = New-Object Windows.Forms.Form
$objForm.Text = "Select a Date"
$objForm.Size = New-Object Drawing.Size #(190,190)
$objForm.StartPosition = "CenterScreen"
$objForm.KeyPreview = $True
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Enter")
{
$global:dtmDate=$objCalendar.SelectionStart
$objForm.Close()
}
})
$objForm.Add_KeyDown({
if ($_.KeyCode -eq "Escape")
{
$objForm.Close()
}
})
$objCalendar = New-Object System.Windows.Forms.MonthCalendar
$objCalendar.ShowTodayCircle = $False
$objCalendar.MaxSelectionCount = 1
$objForm.Controls.Add($objCalendar)
$objForm.Topmost = $True
$objForm.Add_Shown({$objForm.Activate()})
[void] $objForm.ShowDialog()
}
date1
$gt = (get-date).tostring() #getcurrent date
Get-OutlookInbox | #exporting
where { $_.ReceivedTime -gt [datetime] "$globa:dtmdate" -AND $_.ReceivedTime
-le
[datetime] "$gt" } | Export-Csv D:\Users\XXX\Desktop\test1.csv -
Encoding ASCII -NoTypeInformation -force
Parts of code are works perfectly fine separately.
I am able export emails when i am putting date in to code like that:
Get-OutlookInbox | #exporting
where { $_.ReceivedTime -gt [datetime] "8/21/2017" -AND $_.ReceivedTime
-le
[datetime] "8/24/2017" } | Export-Csv D:\Users\XXX\Desktop\test1.csv -
Encoding ASCII -NoTypeInformation -force
but while I am trying to put variable instead of exact values I am getting error msg Cannot convert value "" to type "System.DateTime". Error: "String was not recognized as a valid DateTime."
My question is how to convert variable in to datetime.
Second thing which I notice in this part of code
Get-OutlookInbox | #exporting
where { $_.ReceivedTime -gt [datetime] "8/21/2017" -AND $_.ReceivedTime
-le
[datetime] "8/24/2017" } | Export-Csv D:\Users\XXX\Desktop\test1.csv -
Encoding ASCII -NoTypeInformation -force
In that case operator -gt works perfectly fine for me even it shouldn't. Script exporting emails from date which I set not day after. But second part -le works like less than not less and equal. Which is confusing since I need to set date day+1. I have tried to put several comparison operators but result is always the same.

Save an open Excel sheet using Powershell

I am completely newbie to Powershell. Need your help in saving an opened excel sheet using Powershell.
Script goes something like this
$xlPasteValues = -4163
$xlCellTypeLastCell = 11
$xl = new-object -comobject excel.application
$xl.Visible = $True
$xl.DisplayAlerts = $False
$wb = $xl.Workbooks.Add()
$i = 1
$collection = Get-ChildItem C:\Test\* -include *.csv # Change the location of your CSV files here.
$length = 4
foreach ($item in $collection) {
$wb1 = $xl.Workbooks.Open("$item")
$array = $item.ToString()
$delim = "\"
$SheetName = $array.split($delim)
$s = $SheetName[2]
$sn = $s.split(".")
$nsn = $sn[0]
$ws1 = $wb1.worksheets | where {$_.name -eq $nsn}
Write-Host $item $nsn
$used = $ws1.usedRange
$used.Select()
$used.copy()
$wb.Activate()
$ws = $wb.Sheets.Add()
$ws2 = $wb.worksheets | where {$_.name -eq "sheet$i"}
[void]$ws2.Range("A1").PasteSpecial(-4163)
$ws2.name = $nsn
$i++
$wb1.Close()
}
Add-Type -AssemblyName Microsoft.Office.Interop.Excel
$xlFixedFormat =[Microsoft.Office.Interop.Excel.XlFileFormat]::xlWorkbookDefault
$Excel = New-Object -comobject Excel.Application
$Excel.Visible = $true
Your question was rather vague so I'm assuming that you want to know how to open and save an Excel document through Powershell.
Open your Excel Document using New-Object
$a = New-Object -COM "Excel.Application"
$a.Visible = $true
$b = $a.Workbooks.Open("C:\PATH\TO\YOUR\EXCEL\sheet.xlsx")
Save and close your document
$b.Save()
$b.Close()
Check out my PowerShell Excel Module on Github. You can also grab it from the PowerShell Gallery.
Then try:
$xlFileName="c:\temp\test.xlsx"
dir *.csv |
ForEach {
$sheetName=$_.Name.Split('.')[0]
Import-Csv $_.FullName |
Export-Excel $xlFileName -WorkSheetname $sheetName
}
Invoke-Item $xlFileName