В некоторых сценариях вам нужно погромно анализировать входящие письма в почтовом ящике и извлекать из них различную информацию (отправитель, тема, тело письма). API Outlook позволяет получить прямой доступ к содержимому почтового ящика, отрыть список писем в ящике и прочитать их содержимое. В этой статье мы рассмотрим, как обращаться и обрабатывать письма в почтовом ящике Outlook из PowerShell.
Чтобы PowerShell мог получать доступ к содержимому ящика, Outlook должен быть запущен на компьютере. Вы можете проверить, запущен ли процесс outlook.exe, и запустить его в фоновом режиме с помощью команды:
$OutlookProc = ( Get-Process | where { $_.Name -eq "OUTLOOK" } )
if ( $OutlookProc -eq $null ) { Start-Process outlook.exe -WindowStyle Hidden; Start-Sleep -Seconds 5 }
Теперь нужно загрузить класс и создать экземпляр для доступа к Outlook:
Add-Type -Assembly "Microsoft.Office.Interop.Outlook"
$Outlook = New-Object -ComObject Outlook.Application
Для доступа к содержимому ящика используется пространство имен протокола MAPI:
$namespace = $Outlook.GetNameSpace("MAPI")
В почтовом ящике может быть несколько папок. Вы можете вывести список папок в ящике:
$NameSpace.Folders.Item(1).Folders | FT FolderPath
Можно вывести список папок в древовидном виде и посчитать количество писем в каждой папке:
Function Listfolders { param($Folders, $Indent) ForEach ($Folder in $Folders | sort-object name) { write-host $Indent$($Folder.Name)" ("$($Folder.Items.Count)")" Listfolders $Folder.Folders $Indent" " } } ListFolders $namespace.Folders ""
Чтобы определить папку по умолчанию для входящих писем, выполните команду (в зависимости от языковых/региональных настроек ящика, папка может называться Inbox или Входящие):
$inbox = $namespace.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
Вывести список писем в папке Входящие в формате: отправитель, получатель, тема письма, размер, дата получения.
$inbox.Items | ft SenderEmailAddress, To, Subject, Size, ReceivedTime
Можно использовать фильтры для поиска определенных писем. Например, нужно получить список писем, полученных за сегодня от определенного email адреса отправителя:
$currentDate = Get-Date -Format "MM/dd/yyyy"
$inbox.Items | Where-Object { $_.ReceivedTime -like "*$currentDate*" -and $_.SenderEmailAddress -eq "[email protected]"}
Можно вывести тему и содержимое письма. Можно получить текст письма в plaintext формате (свойство Body) или вывести HTML содержимое (HTMLBody). В этом примере мы выводим на экран текст последнего полученного письма :
$inbox.Items($inbox.Items.Count)|select SenderEmailAddress,subject,Body,HTMLBody|fl
Если письмо содержит вложение, вы можете сохранить файл вложения на диск:
$email= $inbox.Items($inbox.Items.Count)
if ($Email.Attachments.Count -gt 0) {
$Attachment = $Email.Attachments.Item(1)
$Attachment.SaveAsFile("C:\Downloads\$($Email.Attachments.Item(1).FileName)")
}
Чтобы удалить из ящика последнее полученное письмо:
$email= $inbox.Items($inbox.Items.Count)
$Email.Delete()
Возможность получать доступ к содержимому ящика Outlook из PowerShell можно использовать в различных сценариях автоматизации, когда нужно выполнять определенные действия при получении писем. Можно добавить скрипт проверки ящика в Task Sheduller и запускать его по расписанию. Например, так вы можете настроить простейший транслятор сообщений из почтового ящика в Телеграм, или завести заявку в ITSM при получении письма от пользователя.
Включен ПК, запущен Outlook постоянно… сложный сценарий
Подписка 365 и Power Automate в этом плане делает просто магию)
Согласен, что тут лишняя прослойка в виде аутлука.
Но зато довольно просто, и будет работать не только для m365 🙂
PS X:\> $namespace = $Outlook.GetNameSpace("MAPI")
Невозможно вызвать метод для выражения со значением NULL.
строка:1 знак:1
+ $namespace = $Outlook.GetNameSpace("MAPI")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Застрял на этом шаге…
PS
Разобрался
У меня тоже ругается — не нашел решение . Подскажите ?
Outlook установлен? почтовый профиль настроен?
Разобрался, но вообще не интуитивно…
Казалось бы, причем тут целая ветка реестра от других инсталляций Office.
Жесть.
Далее столкнулся с проблемой
«Listfolders : Имя «Listfolders» не распознано как имя командлета,»
Поправил. Там изначально функция для рекурсии была
Не ищет по дате
И не должна искать. В этом месте две ошибки:
1) «$currentDate = Get-Date -Format «MM/dd/yyyy». Не нужно создавать формат даты жёстко английским, если в реальности ReceivedTime обрабатывается в соответствие с текущими региональными настройками.
2) Where-Object { $_.ReceivedTime -like «*$currentDate*». Не нужно переводить дату в строку и работать как со строкамчерез like в то время как ReceivedTime имеет стандартный тип [DateTime].
Чтобы всё корректно работало в любых условиях, нужно дату получать за нужный промежуток как [DateTime] и дальше её стандартно отфильтровывать через gt/lt.
У меня когда-то была необходимость подключить PST архив и найти(хотя бы примерно), владельца архива. Архивы были разбросаны по 200+ компов в офисе.
Может кому пригодится, работает даже при удаленном расположении архива, главное, что бы сетевой путь был доступен. Хотя функцию проверял последний раз лет 8 может назад, возможно что-то и не работает.
function get-pstOwner
{
param (
[CmdletBinding()]
[parameter(
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true)]
[string]$pstPath
)
$pstOwner = $null
$outlook = new-object -com outlook.application
$namespace = $outlook.GetNameSpace("MAPI")
#удаление всех архивов из ящика, если они мешают загрузке почты
if ($namespace.Stores | ? { $_.ExchangeStoreType -eq 3 })
{
$Stores = $namespace.Stores | ? { $_.ExchangeStoreType -eq 3 }
foreach ($store in $Stores)
{
$rootStore = $store.GetrootFolder()
$namespace.GetType().InvokeMember('RemoveStore', [System.Reflection.BindingFlags]::InvokeMethod, $null, $namespace, ($rootStore))
}
}
#попытка подключения архива, если не вышло, возвращаем ошибку
$namespace.AddStoreEx($pstPath, 'olStoreDefault')
$pstStore = $namespace.Stores | ? { $_.FilePath -like $pstPath } #поиск подключенного архива
$rootFolder = $pstStore.GetRootFolder()
$folders = $rootFolder.Folders
foreach ($folder in $folders)
{
if ($pstOwner -ne $null)
{
break
}
if (($folder.Name -like "отправлен*") -or ($folder.name -like "Sent items") -and ($folder.Items.Count -gt 10))
{
$Items = $folder.Items | select -First 100 | foreach sendername
foreach ($item in ($items | select -Unique))
{
$countItems = $Items | ? { $_ -like $Item }
if (($countItems.count/$Items.Count) * 100 -gt 60)
{
$pstOwner = $item
}
}
}
else
{
if ($folder.items.count -gt "10")
{
$Items = $Folder.items | ? { $_.ReceivedByName -ne "" } | select -First 100 | foreach ReceivedByName
foreach ($Item in ($items | select -Unique))
{
$countItems = $Items | ? { $_ -like $Item }
if (($countItems.count/$Items.Count) * 100 -gt 60)
{
$pstOwner = $item
}
}
}
}
}
if ($pstOwner -ne $null)
{
$pstOwner
}
#отключние архива
$namespace.GetType().InvokeMember('RemoveStore', [System.Reflection.BindingFlags]::InvokeMethod, $null, $namespace, ($rootFolder))
Stop-Process -Name Outlook
#задержка ожидания завершения процесса, иногда копирование начинается до того, как outlook осводобит файл, что приводит к ошибке
#Start-Sleep -Seconds 4
$busy = $true
while ($busy -like $true)
{ $busy = (Test-FileLock -Path $pstPath) }
function Test-FileLock
{
param (
[parameter(Mandatory = $true)]
[string]$Path
)
$oFile = New-Object System.IO.FileInfo $Path
if ((Test-Path -Path $Path) -eq $false)
{
return $false
}
try
{
$oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
if ($oStream)
{
$oStream.Close()
}
$false
}
catch
{
# file is locked by a process.
return $true
}
}
}
А через powershell так же можно клиентом Thunderbird управлять ?