Outlook Powershell Script - powershell

I have been doing some digging and trying to find a way to remove a PST from Outlook with a script. I don't entirely know how to script but am trying to learn. I found this old Stackoverflow but I am unsure how to actually enter the information I need into it.
How to disconnect PST file from Outlook using Powershell?
The script in question is below.
$Outlook = new-object -com outlook.application
$Namespace = $Outlook.getNamespace("MAPI")
$PSTtoDelete = "c:\test\pst.pst"
$PST = $namespace.Stores | ? {$_.FilePath -eq $PSTtoDelete}
$PSTRoot = $PST.GetRootFolder()
$PSTFolder = $namespace.Folders.Item($PSTRoot.Name)
$namespace.GetType().InvokeMember('RemoveStore',[System.Reflection.BindingFlags]::InvokeMethod,$null,$namespace,($PSTFolder))
I understand the third line, where to enter the file path to the PST itself but I am not sure what to enter for the rest of the lines.
I know this is a total newbie question, but any help would be super appreciated.
Thank you!

Here is a function i just wrote hope it helps
function Remove-OutlookStore($StoreFilePath){
get-process | where { $_.Name -like "Outlook" }| kill
$Outlook = new-object -com outlook.application
$Namespace = $Outlook.GetNamespace("mapi")
$Store = $namespace.Stores | ?{$_.FilePath -like $StoreFilePath} | %{$_}
$namespace.RemoveStore($Store.GetRootFolder())
get-process | where { $_.Name -like "Outlook" }| kill
}
Remove-OutlookStore -StoreFilePath C:\Test\Test.pst

Related

Delete mail based on subject from outlook by power shell script

I wrote a script to delete particular mails from particular outlook account but it's not deleting mails based on mail subject . Can anybody tell me what is wrong in my code
CODE
$Outlook = New-Object -ComObject Outlook.Application
# Delete an Email from the folder Inbox with Subject Title "Action"
$EmailInFolderToDelete = $Outlook.Session.Folders.Item(1).Folders.Item("Inbox").Items
$EmailInFolderToDelete | ft SentOn, Subject, SenderName, To, Sensitivity -AutoSize -Wrap
$EmailToDelete = $EmailInFolderToDelete | Where-Object {$_.Subject -eq "Test mail";}
$EmailToDelete.Delete()
It's not showing the desired result and not deleting the particular mail from particular outlook account . Can anybody help me on this .
Based on the code on this hey scripting guy blog post. It worked just fine.
$olFolders = “Microsoft.Office.Interop.Outlook.olDefaultFolders” -as [type]
$outlook = new-object -comobject outlook.application
$namespace = $outlook.GetNameSpace(“MAPI”)
$folder = $namespace.getDefaultFolder($olFolders::olFolderInBox)
$emailToDelete = $folder.items | Where-Object {$_.Subject -eq "Test mail";}
$EmailToDelete.Delete()

Slow Output in Outlook via Powershell

I'm using Powershell to access Outlook Mails by creating COM Object.
When I search for particular Mail . PowerShell Iterates through all the mails due to which my output result is really slow and taking very long time.
I have already tried using Descending paramater in Sort-Object or filtering out by date but still results are slow.
$outlook = New-Object -comobject outlook.application
$inbox = $outlook.GetNamespace("MAPI")
$find = $inbox.GetDefaultFolder(6)
$find.Items | Where-Object{$_.SentOn -gt '27-Oct-2019 12:00 PM'}| Select-
Object -Property Subject,SentOn
Can someone please help me to generate faster results or provide a way to filter my search for particular time period.
Never loop through all items in your code. After all, you wouldn't write a SELECT query in SQL without a WHERE clause, would you?
Use Items.Find/FindNext or Items.Restrict to let the store provider do the job.
From the comment from #bluuf I looked into the EWS (exchange web service) and came up with this solution and its about 50% faster than your script.. Maybe it helps for you
$startDate = Get-Date
$MailboxSMTP = "peter.parker#home.com"
$dllpath = "C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\Mashup\Microsoft.Exchange.WebServices.dll"
[void][Reflection.Assembly]::LoadFile($dllpath)
$service = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1)
$service.UseDefaultCredentials = $true
$service.AutodiscoverURL($mailboxSMTP)
$mbMailbox = new-object Microsoft.Exchange.WebServices.Data.Mailbox($mailboxSMTP)
$inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
write-host "Number or unread Messages : " $inbox.UnreadCount
$emails = $inbox.FindItems(10000) | Where-Object { $_.DateTimeSent -gt '27-Oct-2019 12:00 PM'}
$endDate = Get-Date
New-TimeSpan -Start $startDate -End $endDate
Found the script here https://www.msxfaq.de/code/testews.htm
EDIT:
You can filter the results with EWS in a similar way as in #Dmitry solution
write-host "Number or unread Messages : " $inbox.UnreadCount
$view = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1)
$x = $inbox.FindItems($(New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsGreaterThanOrEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::DateTimeReceived, '2019-10-27')),$view)
Or you can use it to filter for different attributes
$filter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::IsRead, $false)
Or
$filter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThanOrEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived,'2019-10-10')
$x = $inbox.FindItems($filter,$view)

Is it possible to extract recipient email address from a .msg file? [duplicate]

This question already has an answer here:
How to get email address from the emails inside an Oulook folder via PowerShell?
(1 answer)
Closed 4 years ago.
In order to compile a list of recipients from a batch of .msg files, I'm trying to achieve this with powershell. I can grab the Recipient name, but not their emails. Their address entry shows as System._ComObject
Any advice to what I'm doing wrong and how I can fix this? Thank you.
$outlook = New-Object -ComObject Outlook.Application
$namespace = $outlook.GetNameSpace("MAPI")
Get-ChildItem $msgPath -Filter *.msg |
ForEach-Object{
$msg = $outlook.Session.OpenSharedItem($_.FullName)
$recipient = $msg.Recipients
$address = $recipient.Address
$recipient
}
$outlook.quit()
Thanks to Palle Due: https://stackoverflow.com/a/47264921/361842
$outlook = New-Object -ComObject Outlook.Application
$namespace = $outlook.GetNameSpace("MAPI")
Get-ChildItem $msgPath -Filter *.msg |
ForEach-Object{
$msg = $outlook.Session.OpenSharedItem($_.FullName)
$msg.Recipients | ForEach-Object{
$_.PropertyAccessor.GetProperty("http://schemas.microsoft.com/mapi/proptag/0x39FE001E")
}
}
$outlook.quit()
Regarding the special property value; there's a helpful list here: https://stackoverflow.com/a/45296809/361842

Powershell: Get Attachments of E-Mails from PST File

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
}

How to Open; SaveAs; then Close an Excel 2013 (macro-enabled) workbook from PowerShell4

Doing a search on the above Com operations yields links dating to '09 and even earlier. Perhaps it hasn't changed but I find myself bumping up against errors where 'it is being used by another process.' - even though no Excel app is open on my desktop. I have to reboot to resume.
To be clear - I'm trying to open an existing file; immediately SaveAs() (that much works), add a sheet, Save(); Close() - and then, importantly, repeat that cycle. In effect, I'm creating a few dozen new sheets within a loop that executes the above 'Open Master; SaveAs(); Edit Stuff; Save; Close;
From the examples I've seen this is not a typical workflow for PowerShell. Pasted at the very bottom is my provisional script - pretty rough and incomplete but things are opening what they need to open and adding sheet also works - until I know I have the right way to cleanly close stuff out I'm not worried about the iterations.
I've found a couple examples that address closing:
From http://theolddogscriptingblog.wordpress.com/2012/06/07/get-rid-of-the-excel-com-object-once-and-for-all/
$x = New-Object -com Excel.Application
$x.Visible = $True
Start-Sleep 5
$x.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($x)
Remove-Variable x
And from http://social.technet.microsoft.com/Forums/windowsserver/en-US/24e57b61-e792-40c1-8aff-b0a8205f48ab/updated-opened-excel-using-powershell?forum=winserverpowershell
Set-ItemProperty $path -name IsReadOnly -value $false
$Excel.ActiveWorkBook.Save()
$openfile.Close()
$openfile = $null
$Excel.Quit()
$Excel = $null
[GC]::Collect()
<>
function MakeNewBook($theWeek, $AffID){
$ExcelFile = "C:\csv\InvoiceTemplate.xlsm"
$Excel = New-Object -Com Excel.Application
$Excel.Visible = $True
$Workbook = $Excel.Workbooks.Open($ExcelFile)
$theWeek = $theWeek -replace "C:\\csv\\", ""
$theWeek = $theWeek -replace "\.csv", ""
$theWeek = "c:\csv\Invoices\" +$AffID +"_" + $theWeek + ".xlsm"
$SummaryWorksheet = $Workbook.worksheets.Item(1)
$Workbook.SaveAs($theWeek)
return $Excel
}
function MakeNewSheet($myBook, $ClassID){
$SheetName = "w"+$ClassID
#$Excel = New-Object -Com Excel.Application
#$Excel.Visible = $True
$wSheet = $myBook.WorkSheets.Add()
}
function SaveSheet ($myExcel)
{
#$WorkBook.EntireColumn.AutoFit()
#Set-ItemProperty $path -name IsReadOnly -value $false
$myExcel.ActiveWorkBook.Save()
$openfile= $myExcel.ActiveWorkBook
$openfile.Close()
$openfile = $null
$myExcel.Quit()
$myExcel = $null
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($myExcel)
Remove-Variable $myExcel
[GC]::Collect()
}
$theWeek = "C:\csv\wkStart2013-11-04.csv"
$x = Import-Csv $theWeek
foreach ($xLine in $x){
if ($x[0]){
$AffID = $x[1].idAffiliate
$myExcel = MakeNewBook $theWeek $x[1].idAffiliate
$ClassID = $x[1].idClass
MakeNewSheet $myExcel $ClassID
continue
}
SaveSheet($myExcel)
$AffID = $_.$AffID
$wID = $xLine.idClass
#MakeNewSheet($wID)
Echo "$wID"
}
As a follow up after playing around with this issue myself. I geared my solution around Ron Thompson's comment minus the function calls:
# collect excel process ids before and after new excel background process is started
$priorExcelProcesses = Get-Process -name "*Excel*" | % { $_.Id }
$Excel = New-Object -ComObject Excel.Application
$postExcelProcesses = Get-Process -name "*Excel*" | % { $_.Id }
# your code here (probably looping through the Excel document in some way
# try to gently quit
$Excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Excel)
# otherwise allow stop-process to clean up
$postExcelProcesses | ? { $priorExcelProcesses -eq $null -or $priorExcelProcesses -notcontains $_ } | % { Stop-Process -Id $_ }
My experience has been that the Quit method doesn't work well, especially when looping. When you get the error, instead of rebooting, open up Task Manager and look at the Processes tab. I'm willing to bet you'll see Excel still open -- maybe even multiple instances of it. I solved this problem by using Get-Process to find all instances of Excel and piping them to Stop-Process. Doesn't seem like that should be necessary, but it did the trick for me.
You should not have to keep track of processes and kill them off.
My experience has been that to properly and completely close Excel (including in loops), you also need to release COM references. In my own testing have found removing the variable for Excel also ensures no remaining references exist which will keep Excel.exe open (like if you are debugging in the ISE).
Without performing the above, if you look in Task Manager, you may see Excel still running...in some cases, many copies.
This has to do with how the COM object is wrapped in a “runtime callable wrapper".
Here is the skeleton code that should be used:
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$workbook = $excel.Workbooks.Add()
# or $workbook = $excel.Workbooks.Open($xlsxPath)
# do work with Excel...
$workbook.SaveAs($xlsxPath)
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
# no $ needed on variable name in Remove-Variable call
Remove-Variable excel
Try this
$filePath = "E:\TSMBackup\TSMDATAPull\ODCLXTSM01_VM.xlsx"
$excelObj = New-Object -ComObject Excel.Application
$excelObj.Visible = $true
$workBook = $excelObj.Workbooks.Open($filePath)
$workSheet = $workBook.Sheets.Item("Sheet1")
$workSheet.Select()
$workBook.RefreshAll()
$workBook.Save()
$excelObj.Quit()