Exchange. EWS отчет по статусу писем

Рассмотрим способ получения статистики по письму из массовой рассылки, внутри вашей компании, с помощью EWS

  1. Сначала нужно получить библиотеку EWS
    Удобно скачать библиотеку из репозитория Powershell.
    Но для этого может потребоваться сначала подключить провайдер пакетов NuGet:

    Register-PackageSource -Name MyNuGet -Location https://www.nuget.org/api/v2 -ProviderName NuGet Установить-PackageProvider -Name NuGet-Force

    Раньше библиотеку можно было скачать с сайта Microsoft, но сейчас ссылки на нее удалены

    Далее поиск и установка пакета с библиотекой:

    Find-Package Exchange.WebServices.Managed.Api | Install-Package
  2. После установки нужно указать в следующем скрипте путь к библиотеке, для переменной $dllpath

    #Useage Examples:
    #$AllUserMailboxs = Get-mailbox -RecipientTypeDetails UserMailbox -ResultSize unlimited
    #foreach ($Mailbox in $AllUserMailboxs) {
    #Write-host "Checking:"$mailbox.Windows
    #.\ReadMsgstatus.ps1 -userName "Admin@xyz.com" -password "AdminPass" `
    #-mailbox "USERPrimarySMTP@xyz.com" -subject "Test Msg" -sender "sender@xzy.com" -userName 'AdminUserName' -SentDate "04/30/21"}
    
    param (
    $sender,
    $subject,
    $mailbox,
    $userName,
    $SentDate
    )
    
    $SQ = "Received:$SentDate AND From:`"$Sender`" AND Subject:`"$subject`""
    $report=@()
    $itemsView=1000
    $uri=[system.URI] "https://mail.DomainName.ru/EWS/Exchange.asmx"
    
    $dllpath = "C:\Program Files\PackageManagement\NuGet\Packages\Exchange.WebServices.Managed.Api.2.2.1.2\lib\net35\Microsoft.Exchange.WebServices.dll"
    Import-Module $dllpath
    
    $AccountWithImpersonationRights=$userName # Учетка админа под которой будем работать. У нее должны быть полномочия управлять целевым ящиком
    $MailboxToImpersonate=$mailbox # Ящик в котором мы хотим производить действия.
    
    ## Set Exchange Version
    $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2016
    $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
    $service.url = $uri
    
    $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SMTPAddress,$MailboxToImpersonate);
    
    $Folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId ([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxToImpersonate)
    $view = New-Object Microsoft.Exchange.WebServices.Data.ItemView -ArgumentList $ItemsView
    $propertyset = New-Object Microsoft.Exchange.WebServices.Data.PropertySet ([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly )
    
    $view.PropertySet = $propertyset
    $items = $service.FindItems($Folderid,$SQ,$view)
    
    if ($items -ne $null)
    { # Если во входящих найдены письма - добавляются в отчет
    $emailProps = New-Object Microsoft.Exchange.WebServices.Data.PropertySet ([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
    $mail = [Microsoft.Exchange.WebServices.Data.EmailMessage]::Bind($service, $items.ID, $emailProps)
    $datam=$mail | Select @{N="Sender";E={$_.Sender.Address}},@{N="USER";E={$mailbox}},Isread,Subject,DateTimeReceived,@{n="Folder";e={"Inbox"}}
    $report+=$datam #| ?{ $_.DateTimeReceived -match "$SentDate" }
    }
    Else
    { # Иначе строится структура папок ящика и ищет в них
    Write-Host "Mail Not Found in Inbox Folder for:"$mailbox -f Yellow -NoNewline
    Write-Host " Checking Deleted Item Folder"
    $MailboxRootid= new-object Microsoft.Exchange.WebServices.Data.FolderId ([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxToImpersonate)
    $MailboxRoot=[Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$MailboxRootid)
    $FolderList = new-object Microsoft.Exchange.WebServices.Data.FolderView(100)
    $FolderList.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
    $findFolderResults = $MailboxRoot.FindFolders($FolderList)
    
    $allFolders = $findFolderResults | ? {$_.DisplayName -eq "Deleted Items" -or $_.DisplayName -eq "Удаленные"}
    $count = $allFolders.Count
    $DI = $allFolders
    
    # Цикл который перебирает все найденные папки с удаленными, пока не будет найдено письмо
    $Folderids=$DI.ID
    
    foreach($Folderid in $Folderids){
    $items = $service.FindItems($Folderid,$SQ,$view)
    if($items -ne $null){break}
    }
    
    if ($items.count -eq $null)
    {
    write-host "Item not found in the Deleted item folder, Now Checking in the Recover Deleted items Folder"
    $itemsView=90000
    $view = New-Object Microsoft.Exchange.WebServices.Data.ItemView -ArgumentList $ItemsView
    $propertyset = New-Object Microsoft.Exchange.WebServices.Data.PropertySet ([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IdOnly)
    $view.PropertySet = $propertyset
    $MailboxRootid= new-object Microsoft.Exchange.WebServices.Data.FolderId `
    ([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Recoverableitemsroot,$MailboxToImpersonate)
    $MailboxRoot=[Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$MailboxRootid)
    $FolderList = new-object Microsoft.Exchange.WebServices.Data.FolderView(100)
    $FolderList.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
    $findFolderResults = $MailboxRoot.FindFolders($FolderList)
    $Deletions = $findFolderResults | ? {$_.DisplayName -eq "Deletions"}
    $Folderid=$Deletions.ID
    $items=$service.FindItems($Folderid,$SQ,$view)
    
    if ($items.count -eq $null){
    
    Write-Host "Item Not Found in the Dumpsters."
    Write-host "Checking in other folders"
    
    $MailboxRootid= new-object Microsoft.Exchange.WebServices.Data.FolderId `
    ([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxToImpersonate)
    $MailboxRoot=[Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$MailboxRootid)
    $FolderList = new-object Microsoft.Exchange.WebServices.Data.FolderView(100)
    $FolderList.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
    $findFolderResults = $MailboxRoot.FindFolders($FolderList)
    $allFolders=$findFolderResults | ? {$_.FolderClass -eq "IPF.Note"} | select ID,Displayname
    $allFolders=$allFolders | ? { `
    $_.DisplayName -notlike "Inbox" -and `
    $_.DisplayName -notlike "Deleted Items" -and `
    $_.DisplayName -notlike "Drafts" -and `
    $_.DisplayName -notlike "Sent Items" -and `
    $_.DisplayName -notlike "Outbox"}
    $allfoldersCount=$allfolders.id.count
    $counter=0
    $itemFound=$false
    if ($allFolders) {
    do { Write-Host "Checking Email Item in Folder:"$allfolders[$counter].DisplayName
    
    $folderID=$allfolders[$counter].ID
    $items =$service.FindItems($Folderid,$SQ,$view)
    if($items.count -eq $null) {Write-Host "Item Was Not Found in Folder:"$allfolders[$counter].DisplayName -ForegroundColor Yellow}
    else{
    Write-Host "Item Was Found in Folder:"$allfolders[$counter].DisplayName -ForegroundColor Green
    $emailProps = New-Object Microsoft.Exchange.WebServices.Data.PropertySet ([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
    $mail = [Microsoft.Exchange.WebServices.Data.EmailMessage]::Bind($service, $items.ID, $emailProps)
    $data=$mail | Select @{N="Sender";E={$_.Sender.Address}},@{N="USER";E={$mailbox}},Isread,Subject,DateTimeReceived,@{n="Folder";e={$allfolders[$counter].DisplayName}}
    $report+=$data
    $itemFound=$true} $counter++ } until ($counter -eq $allfoldersCount -or $itemFound -eq $true) }}
    
    
    else{ $emailProps = New-Object Microsoft.Exchange.WebServices.Data.PropertySet ([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
    $mail = [Microsoft.Exchange.WebServices.Data.EmailMessage]::Bind($service, $items.ID, $emailProps)
    $data=$mail | Select @{N="Sender";E={$_.Sender.Address}},@{N="USER";E={$mailbox}},Isread,Subject,DateTimeReceived,@{n="Folder";e={"Deletions"}}
    $report+=$data } }
    else{ $emailProps = New-Object Microsoft.Exchange.WebServices.Data.PropertySet ([Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties)
    $mail = [Microsoft.Exchange.WebServices.Data.EmailMessage]::Bind($service, $items.ID, $emailProps)
    $data=$mail | Select @{N="Sender";E={$_.Sender.Address}},@{N="USER";E={$mailbox}},Isread,Subject,DateTimeReceived,@{n="Folder";e={$DI.DisplayName}}
    $report+=$data }
    } $report

    Скрипт представляет собой немного подправленную версию скрипта от Sunil Chauhan из его блога, где есть еще много примеров работы с EWS.

  3. Вызов скрипта выполняем следующим образом
    Add-PSSnapin Microsoft.Exchange.Management.PowerShell.SnapIn
    $mailboxes = (get-mailbox -resultsize unlimited -RecipientTypeDetails UserMailbox | select -ExpandProperty WindowsEmailAddress) | select -ExpandProperty Address
    
    
    $sender = 'email@DomainName.ru'
    $subject = 'Ежедневная рассылка'
    $SentDate = "04/30/21"
    $result = foreach($mailbox in $mailboxes ){
    
    C:\ReadMsgstatus.ps1 -sender $sender -subject $subject -mailbox $mailbox -userName 'AdminUserName' -SentDate $SentDate
    
    }
    
    Clear-Variable DeletedReadCount,InboxReadCount,DeletedUnreadCount,InboxUnreadCount
    # Прочитанные в удаленных
    $result | ? { $_.IsRead } | ? { $_.Folder -eq "Удаленные" -or $_.Folder -match "Delet" } | Group-Object Folder | ForEach-Object {$DeletedReadCount += $_.Count}
    # Прочитанные во входящих
    $result | ? { $_.IsRead } | ? { $_.Folder -ne "Удаленные" -or $_.Folder -notmatch "Delet" } | Group-Object Folder | ForEach-Object {$InboxReadCount += $_.Count}
    # Непрочитанные в удаленных
    $result | ? { !$_.IsRead } | ? { $_.Folder -eq "Удаленные" -or $_.Folder -match "Delet" } | Group-Object Folder | ForEach-Object {$DeletedUnreadCount += $_.Count}
    # Непрочитанные во входящих
    $result | ? { !$_.IsRead } | ? { $_.Folder -ne "Удаленные" -or $_.Folder -notmatch "Delet" } | Group-Object Folder | ForEach-Object {$InboxUnreadCount += $_.Count}
    
    
    $List = New-Object 'System.Collections.Generic.List[System.Object]'
    $List.add( ( @{'Дата отправки'=$SentDate;'Тема письма'=$subject;'Прочитанные во входящих'=$InboxReadCount;'Непрочитанные во входящих'=$InboxUnreadCount;'Прочитанные в удаленных'=$DeletedReadCount;'Непрочитанные в удаленных'=$DeletedUnreadCount} | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }) )
    $List | ft 'Тема письма','Дата отправки','Прочитанные во входящих','Прочитанные в удаленных','Непрочитанные во входящих','Непрочитанные в удаленных'
  4. Для того что бы скрипт работал и попадал в чужие ящики от лица административной учетной записи нужно настроить имперсонализацию:

    New-ManagementRoleAssignment –Name "EWS_impersonation" –Role "ApplicationImpersonation" –User "AdminUserName"
  5. Для того что бы данный скрипт мог работать с самого сервера Exchange нужно внести изменение в реестр

    New-ItemProperty HKLM:\System\CurrentControlSet\Control\Lsa -Name "DisableLoopbackCheck" -Value "1" –PropertyType dword


    В противном случае мы будем получать ошибку авторизации 401

Добавить комментарий

Ваш адрес email не будет опубликован.