Обработка почтового ящика Exchange средствами PowerShell

26.05.2014

В работе системного администратора бывают случаи проведения служебного расследования по деятельности сотрудников компании. Например, когда служба безопасности компании подозревает кого-то из работников в "сливе" информации конкурентам. Обычно в этом случае служба безопасности просит сделать выгрузку почтового ящика сотрудника в файл.

Примечание. Выгрузка личной информации работника даже из корпоративной среды предприятия незаконна, если сам работник не дал на это согласие. Поэтому небходимо при приеме на работу ознакомить нового сотрудника с политиками безопасности компании и обязать подписать документ в котором определено, что работники дают согласие на доступ службы безопасности к личной информации сотрудника, хранящейся на рабочем компьютере и серверах компании, при расследовании инцидентов с письменного разрешения директора по персоналу.

На первом этапе расследования службе безопасности требуется общая информация о деятельности сотрудника: контакты, названия писем, названия файлов на компьютере, распечатка звонков с корпоративного телефона и прочая общая информация.

То есть в случае с почтой полная выгрузка почтового ящика, которая описана в статье Exchange - Выгрузка почтового ящика в PST-файл не подходит. Необходимо просто сформировать таблицу из писем в почтовом ящике, а для этой задачи больше подходят программные средства, чем клиент Outlook и PST-файлы. Например, можно обработать почтовый ящик с помощью PowerShell.

Для подключения к почтовому серверу Exchange из PowerShell используются библиотеки Exchange Web Services Managed API 2.0, которые можно найти на сайте Microsoft. Подробные примеры использования Exchange Web Services (EWS) можно найти в блоге Glen Scales.

Для выгрузки почтового ящика с сервера Exchange в CSV-файл я написал PowerShell-скрипт, который получает письма из папок "Входящие" и "Отправленные", извлекает из писем необходимые поля ("От кого", "Кому", "Тема", "Дата", "Размер") и записывает результат в CSV-таблицу на рабочем столе текущего пользователя.

Итак, текст скрипта:

# Полезные статьи по теме Exchange Web Services
# Part 1  http://gsexdev.blogspot.ru/2012/01/ews-managed-api-and-powershell-how-to.html
# Part 2  http://gsexdev.blogspot.ru/2012/01/ews-managed-api-and-powershell-how-to_23.html
# Part 3  http://gsexdev.blogspot.ru/2012/02/ews-managed-api-and-powershell-how-to.html
# Part 4  http://gsexdev.blogspot.ru/2012/02/ews-managed-api-and-powershell-how-to_22.html
# Part 5  http://gsexdev.blogspot.ru/2012/05/ews-managed-api-and-powershell-how-to_28.html
# Part 6  http://gsexdev.blogspot.ru/2012/03/ews-managed-api-and-powershell-how-to_27.html
# Part 7  http://gsexdev.blogspot.ru/2012/04/ews-managed-api-and-powershell-how-to.html
# Part 8  http://gsexdev.blogspot.ru/2012/05/ews-managed-api-and-powershell-how-to.html
# Part 9  http://gsexdev.blogspot.ru/2012/05/ews-managed-api-and-powershell-how-to_28.html
# Part 10 http://gsexdev.blogspot.ru/2012/06/ews-managed-api-and-powershell-how-to.html

# Этот скрипт использует Microsoft Exchange Web Services Managed API 2.0
# http://www.microsoft.com/en-us/download/details.aspx?id=35371

# Почтовый ящик для выгрузки
# - перед открытием чужого почтового ящика необходимо выдать текущему пользователю полные права на него и
#   перевойти в сессию почтового сервера
$MailboxName = "VBochkarev@domain.com"

# Очистка переменной ScriptResult
Remove-Variable ScriptResult -ErrorAction SilentlyContinue
$ScriptResult = @()

# Загрузка библиотек Microsoft Exchange Web Services DLL
$DllPath = "C:\Program Files\Microsoft\Exchange\Web Services\2.0\Microsoft.Exchange.WebServices.dll"
[void][Reflection.Assembly]::LoadFile($DllPath)

# Подключение к серверу Exchange 2010 с текущими правами пользователя
$Service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService `
 ([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010)
$WindowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$SidBind = "LDAP://<SID=" + $WindowsIdentity.user.Value.ToString() + ">"
$AceUser = [ADSI]$SidBind
$Service.AutodiscoverUrl($AceUser.mail.ToString())

# Открытие папки 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)

# Создание фильтра поиска писем
$SearchFilterRead = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo `
 ([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::IsRead, $True)
$SearchFilterNotRead = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo `
 ([Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::IsRead, $False)
$SearchFilterCollection = `
 new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection `
 ([Microsoft.Exchange.WebServices.Data.LogicalOperator]::Or);
$SearchFilterCollection.add($SearchFilterRead)
$SearchFilterCollection.add($SearchFilterNotRead)

# Задание начальной позиции и количества писем в коллекции
$offset = 0;
$view = new-object Microsoft.Exchange.WebServices.Data.ItemView(100, $offset)

# Задание расширенных свойств для подгрузки
$psPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet `
 ([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)

While (($results = $InboxFolder.FindItems($SearchFilterCollection, $view)).Items.Count -gt 0)
{
   # Подгрузка расширенных свойств текущей коллекции писем
   [Void]$service.LoadPropertiesForItems($results,$psPropset)

   # Наполнение рабочей переменной свойствами писем из коллекции
   ForEach ($item in $results)
   {
       $MailboxContent = New-Object System.Object
       $MailboxContent | Add-Member -MemberType NoteProperty -Name Folder -Value $InboxFolder.Displayname
       $MailboxContent | Add-Member -MemberType NoteProperty -Name FromAddress -Value $item.From.Address
       $MailboxContent | Add-Member -MemberType NoteProperty -Name ToRecipients -Value `
         ([string]$item.ToRecipients) # преобразование массива в строку
       $MailboxContent | Add-Member -MemberType NoteProperty -Name CcRecipients -Value `
         ([string]$item.CcRecipients) # преобразование массива в строку
       $MailboxContent | Add-Member -MemberType NoteProperty -Name BccRecipients -Value `
         ([string]$item.BccRecipients) # преобразование массива в строку
       $MailboxContent | Add-Member -MemberType NoteProperty -Name Subject -Value $item.Subject
       $MailboxContent | Add-Member -MemberType NoteProperty -Name Received -Value $item.DateTimeReceived
       $MailboxContent | Add-Member -MemberType NoteProperty -Name Sent -Value $item.DateTimeSent
       $MailboxContent | Add-Member -MemberType NoteProperty -Name Size -Value $item.Size
       $ScriptResult += $MailboxContent
   }
  
   # Перемещение курсора текущей позиции и настроек количества писем следующей коллекции
   $offset += $results.Items.Count
   $view = new-object Microsoft.Exchange.WebServices.Data.ItemView(100, $offset)
}

# Получение списка подпапок
$fvFolderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView(1000);
$fvFolderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
$ffResponse= $InboxFolder.FindFolders($fvFolderView);

# Получение писем подпапок
ForEach ($folder in $ffResponse.Folders)
{
   # Задание начальной позиции и количества писем в коллекции
   $offset = 0;
   $view = new-object Microsoft.Exchange.WebServices.Data.ItemView(100, $offset)

   # Задание расширенных свойств для подгрузки
   $psPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet `
     ([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)

   While (($results = $InboxFolder.FindItems($SearchFilterCollection, $view)).Items.Count -gt 0)
   {
       # Подгрузка расширенных свойств текущей коллекции писем
       [Void]$service.LoadPropertiesForItems($results,$psPropset)

       # Наполнение рабочей переменной свойствами писем из коллекции
       ForEach ($item in $results)
       {
           # Write Information regarding every e-mail in the folder
           $MailboxContent = New-Object System.Object
           $MailboxContent | Add-Member -MemberType NoteProperty -Name Folder -Value `
             $InboxFolder.Displayname
           $MailboxContent | Add-Member -MemberType NoteProperty -Name FromAddress -Value `
             $item.From.Address
           $MailboxContent | Add-Member -MemberType NoteProperty -Name ToRecipients -Value `
             ([string]$item.ToRecipients) # преобразование массива в строку
           $MailboxContent | Add-Member -MemberType NoteProperty -Name CcRecipients -Value `
             ([string]$item.CcRecipients) # преобразование массива в строку
           $MailboxContent | Add-Member -MemberType NoteProperty -Name BccRecipients -Value `
             ([string]$item.BccRecipients) # преобразование массива в строку
           $MailboxContent | Add-Member -MemberType NoteProperty -Name Subject -Value $item.Subject
           $MailboxContent | Add-Member -MemberType NoteProperty -Name Received -Value `
             $item.DateTimeReceived
           $MailboxContent | Add-Member -MemberType NoteProperty -Name Sent -Value $item.DateTimeSent
           $MailboxContent | Add-Member -MemberType NoteProperty -Name Size -Value $item.Size
           $ScriptResult += $MailboxContent
       }
     
       # Перемещение курсора текущей позиции и настроек количества писем следующей коллекции
       $offset += $results.Items.Count
       $view = new-object Microsoft.Exchange.WebServices.Data.ItemView(100, $offset)
   }
}

# Открытие папки SentItems почтового ящика
$FolderId = new-object Microsoft.Exchange.WebServices.Data.FolderId `
 ([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::SentItems,$MailboxName)
$InboxFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($Service,$FolderId)

# Задание начальной позиции и количества писем в коллекции
$offset = 0;
$view = new-object Microsoft.Exchange.WebServices.Data.ItemView(100, $offset)

# Задание расширенных свойств для подгрузки
$psPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet `
 ([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)

While (($results = $InboxFolder.FindItems($SearchFilterCollection, $view)).Items.Count -gt 0)
{
   # Подгрузка расширенных свойств текущей коллекции писем
   [Void]$service.LoadPropertiesForItems($results,$psPropset)

   # Наполнение рабочей переменной свойствами писем из коллекции
   ForEach ($item in $results)
   {
       # Write Information regarding every e-mail in the folder
       $MailboxContent = New-Object System.Object
       $MailboxContent | Add-Member -MemberType NoteProperty -Name Folder -Value $InboxFolder.Displayname
       $MailboxContent | Add-Member -MemberType NoteProperty -Name FromAddress -Value $item.From.Address
       $MailboxContent | Add-Member -MemberType NoteProperty -Name ToRecipients -Value `
         ([string]$item.ToRecipients) # преобразование массива в строку
       $MailboxContent | Add-Member -MemberType NoteProperty -Name CcRecipients -Value `
         ([string]$item.CcRecipients) # преобразование массива в строку
       $MailboxContent | Add-Member -MemberType NoteProperty -Name BccRecipients -Value `
         ([string]$item.BccRecipients) # преобразование массива в строку
       $MailboxContent | Add-Member -MemberType NoteProperty -Name Subject -Value $item.Subject
       $MailboxContent | Add-Member -MemberType NoteProperty -Name Received -Value $item.DateTimeReceived
       $MailboxContent | Add-Member -MemberType NoteProperty -Name Sent -Value $item.DateTimeSent
       $MailboxContent | Add-Member -MemberType NoteProperty -Name Size -Value $item.Size
       $ScriptResult += $MailboxContent
   }

   # Перемещение курсора текущей позиции и настроек количества писем следующей коллекции
   $offset += $results.Items.Count
   $view = new-object Microsoft.Exchange.WebServices.Data.ItemView(100, $offset)
}

$ScriptResult | Export-Csv ((Get-Childitem env:USERPROFILE).Value + "\Desktop\PSOutput.csv") `
 -Delimiter "`t" -Encoding UTF8