Возникла потребность просмотреть на сервере exchange сколько писем отправляют и получают пользователи по дням со статистикой в мегабайтах, после изучения интернета был найден скрипт Nuno Mota не устроило что скрипт делает текстовый вывод, что не удобно для постоянного использования и для генерации html отчетов. Скрипт был переработан:

  • вывод сделан объектами
  • добавлены поля
  • исправлен баг когда первая запись выводилась неправильно


Получение количества отправленных и полученных писем по дням с exchange сервера.

#### Variables #####
# период за который будем смотреть статистику, считается от сегодня
$PeriodIndays = 7
# дата по которую смотрим, должна быть меньше чем стартовая
$EndPeriod = Get-date -hour 0 -minute 0 -second 0
# если пишешь вручную помни что дата задается наоброт ММ/ДД/ГГГГ  тоесть "09/5/2016"
$StartPeriod = ($EndPeriod).AddDays( -$PeriodIndays )



####################################################################################
$From = $StartPeriod
$To = $EndPeriod

[Int64] $intSent = 0
[Int64] $intRec = 0
[Int64] $intSentSize = 0
[Int64] $intRecSize = 0
$Total = 0
$TotalSent = 0
$TotalRec  = 0

$MailPerDay = @()
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010

Do {
    $From = $From.AddDays(1)
    $To = $From.AddDays(1)

    $intSent = $intRec = $intSentSize =  $intRecSize = 0

    (Get-TransportServer) | Get-MessageTrackingLog -ResultSize Unlimited -Start $From -End $To | ForEach {  
        # Sent E-mails  
        If ($_.EventId -eq "RECEIVE" -and $_.Source -eq "STOREDRIVER") 
        { 
            $intSent++ 
            $intSentSize += $_.TotalBytes 
        } 
          
        # Received E-mails  
        If ($_.EventId -eq "DELIVER") 
        { 
            $intRec++ 
            $intRecSize += $_.TotalBytes 
        } 
    }

    $props = [ordered]@{   Date=$From
                           Sent=$intSent
                           SentSizeMB=[Math]::Round($intSentSize/1MB, 0)
                           Recived=$intRec
                           RecivedSizeMB=[Math]::Round($intRecSize/1MB, 0)
                           TotalPerDayInMB=[Math]::Round(($intRecSize+$intSentSize)/1MB, 2)
                           TotalPerDayInGB=[Math]::Round(($intRecSize+$intSentSize)/1GB, 2)
                        }

    $obj = New-Object -TypeName PSObject -Property $props

    $MailPerDay += $obj

    $TotalSent += $intSentSize
    $TotalRec += $intRecSize
} While ($To -lt (Get-Date))  

$MailPerDay | ft

Write-Host "всего отправлено за отчетный период $([Math]::Round( $TotalSent/1GB, 2)) гигабайт"
Write-Host "всего получено за отчетный период   $([Math]::Round(  $TotalRec/1GB, 2)) гигайбайт"
Write-Host "всего получено и отправлено за период $([Math]::Round( ($TotalSent + $TotalRec)/1GB, 2)) гигабайт"

Чтобы получить красивый html отчет, например чтобы отправить по почте ниже дополненный скрипт:




#### Variables #####
# период за который будем смотреть статистику, считается от сегодня
$PeriodIndays = 7
# дата по которую смотрим, должна быть меньше чем стартовая
$EndPeriod = Get-date -hour 0 -minute 0 -second 0
# если пишешь вручную помни что дата задается наоброт ММ/ДД/ГГГГ  тоесть "09/5/2016"
$StartPeriod = ($EndPeriod).AddDays( -$PeriodIndays )
# путь до html файла
$PathFile = 'c:\EmailSendAndreceived.html'

####################################################################################
$From = $StartPeriod
$To = $EndPeriod

[Int64] $intSent = 0
[Int64] $intRec = 0
[Int64] $intSentSize = 0
[Int64] $intRecSize = 0
$Total = 0
$TotalSent = 0
$TotalRec  = 0

$MailPerDay = @()
Add-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010

Do {
    $From = $From.AddDays(1)
    $To = $From.AddDays(1)

    $intSent = $intRec = $intSentSize =  $intRecSize = 0

    (Get-TransportServer) | Get-MessageTrackingLog -ResultSize Unlimited -Start $From -End $To | ForEach {  
        # Sent E-mails  
        If ($_.EventId -eq "RECEIVE" -and $_.Source -eq "STOREDRIVER") 
        { 
            $intSent++ 
            $intSentSize += $_.TotalBytes 
        } 
          
        # Received E-mails  
        If ($_.EventId -eq "DELIVER") 
        { 
            $intRec++ 
            $intRecSize += $_.TotalBytes 
        } 
    }

    $props = [ordered]@{   Date=$From
                           Sent=$intSent
                           SentSizeMB=[Math]::Round($intSentSize/1MB, 0)
                           Recived=$intRec
                           RecivedSizeMB=[Math]::Round($intRecSize/1MB, 0)
                           TotalPerDayInMB=[Math]::Round(($intRecSize+$intSentSize)/1MB, 2)
                           TotalPerDayInGB=[Math]::Round(($intRecSize+$intSentSize)/1GB, 2)
                        }

    $obj = New-Object -TypeName PSObject -Property $props

    $MailPerDay += $obj

    $TotalSent += $intSentSize
    $TotalRec += $intRecSize
} While ($To -lt (Get-Date))  

$MailPerDay | ft

Write-Host "всего отправлено за отчетный период $([Math]::Round( $TotalSent/1GB, 2)) гигабайт"
Write-Host "всего получено за отчетный период   $([Math]::Round(  $TotalRec/1GB, 2)) гигайбайт"
Write-Host "всего получено и отправлено за период $([Math]::Round( ($TotalSent + $TotalRec)/1GB, 2)) гигабайт"

############# HTML generating #############
$frag1 = $MailPerDay | sort -Property Date -Descending | ConvertTo-Html -As table -Fragment -PreContent "<h2>всего получено за период $([Math]::Round( $TotalSent/1GB, 2)) <br> всего отправлено за период $([Math]::Round(  $TotalRec/1GB, 2))<br>всего отправлено и получено за период $([Math]::Round( ($TotalSent + $TotalRec)/1GB, 2))</h2>" | Out-String

Write-Verbose 'definiting CSS'
$head = @'
<style>
body { background-color:#ffffff; font-family:Tahoma; font-size:12pt; }
td, th { border:1px solid black; border-collapse:collapse; }
th { color:white; background-color:black; }
table, tr, td, th { padding: 2px; margin: 0px }
table { font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif; font-size: 14px; border-radius: 10px; border-spacing: 0; text-align: center; }
th { background: #BCEBDD; color: white; text-shadow: 0 1px 1px #2D2020; padding: 10px 20px; }
th, td { border-style: solid; border-width: 0 1px 1px 0; border-color: white; }
th:first-child, td:first-child { text-align: left; }
th:first-child { border-top-left-radius: 10px; }
th:last-child { border-top-right-radius: 10px; border-right: none; }
td { padding: 10px 20px; background: #F8E391; }
tr:last-child td:first-child { border-radius: 0 0 0 10px; }
tr:last-child td:last-child { border-radius: 0 0 10px 0; }
tr td:last-child { border-right: none; }
</style>
'@

$Date = Get-Date
$rep = ConvertTo-HTML -head $head -PostContent $frag1 -PreContent "<h1>Email Sent received $Date</h1>" | Out-String
$rep | Out-File $PathFile

Последний сгенерирует html-файл и положит по дефолту в каталог С.
Поделиться с друзьями
-->

Комментарии (29)


  1. krasaval
    19.09.2016 14:59
    +1

    Get-TransportServer лучше заменить на Get-TransportService, т.к. он скоро будет исключен

    Так же неплохо было бы исключить из общей статистики письма HealthMailbox и inboundproxy


    1. pak-nikolai
      19.09.2016 15:01

      может стать несовместимым с exchange 2010


      1. krasaval
        19.09.2016 15:18

        хм, действительно, только начиная с 2013.

        Еще наверное удобнее время считать с начала дня, что-то вроде этого:

        $EndPeriod = (Get-date -hour 0 -minute 0 -second 0)
        $StartPeriod = ($EndPeriod).AddDays( -$PeriodIndays )


        1. LoadRunner
          19.09.2016 16:54

          Что-то торкнуло меня в Вашем комментарии, только сейчас понял, что именно.
          00:00:00 — это начало нового дня. Если выставить EndPeriod на начало дня, то мы не получим статистику писем этого дня, только если какому-то письму посчастливится прийти ровно в 00:00:00 (и то на сервере оно обрабатывается какое-то время и получает разные отметки от разных фильтров и соединителей отправки\получения).


  1. LoadRunner
    19.09.2016 16:18

    Делал похожий велосипед, с авторизацией, выбором периода и пользователей. Только на C#.

    Я правильно понимаю, что скрипт корректно отработает только из-под учётной записи с необходимыми правами?


    1. pak-nikolai
      19.09.2016 19:48

      да


  1. krasaval
    19.09.2016 17:25

    EndPeriod можно выставить и на начало завтрашнего дня, что бы получить статистику с начала дня, до текущего периода. Удобнее когда базовая точка отсчета — 0:00:00, а не время запуска скрипта.


    1. pak-nikolai
      20.09.2016 07:38

      учтено


  1. kerberos464
    19.09.2016 19:49

    не подскажете, как сделать так, чтобы можно было запускать этот скрипт дистанционно, а не только с того сервера, на котором установлен Exchange?
    проблема в том, что часть коммандлетов, например, Get-Mailbox, подгружается, а часть нет, в т.ч. Get-TransportServer.
    пробовал и через RemoteExchange.ps1, и через Import-PSSession:
    $ExchangeAddress = «mail.domain.ru»
    $ExchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri «http://$ExchangeAddress/PowerShell»
    Import-PSSession $ExchangeSession


    1. pak-nikolai
      19.09.2016 19:56

      он и не запустится локально т.к. модули есть только на машине с эксчейнджем. сделайте лучше invoke-command
      насчет сессии странно, по идее должно работать правильно. Сегодня не могу проверить


    1. pak-nikolai
      20.09.2016 07:20

      действительно есть такая проблема. командлет постоянно выдает ошибку при запуске из удаленной сессии или через invoke-command. я не нашел способа кроме как копировать на нужную машину и исполнять оттуда, или использовать сторонние решения типа psexec

      получается нужно создавать на удаленной машине файл, его исполнять и результаты работы сгружать обратно


    1. n01d
      20.09.2016 08:23

      Через Enter-PSSession и дописать импорт соответствующих модулей перед вызовом командлетов. Без явного импорта командлеты могут не подхватываться.


      1. n01d
        20.09.2016 08:30

        Простите, не увидел сразу в коде подключения PSSnapin`ов… В интернетах пишут, что таки да — часть командлетов не работает по удалёнке. На всякий случай: https://technet.microsoft.com/en-us/library/dd297932%28v=exchg.141%29.aspx


        1. pak-nikolai
          20.09.2016 09:53

          мне кажется это связано както с ограничением WMI эксчейнджа, либо c непольной реализацией обертки PoSH, по идее если реализовать этот же трэкинг лог низкоуровнево должно заработать хотя бы через invok-command и pssession


    1. LoadRunner
      20.09.2016 08:37
      +2

      Установите соответствующую роль администрирования из установочного образа, без установки остальных ролей exchange.


  1. feldwebel
    19.09.2016 19:56

    received


    1. pak-nikolai
      20.09.2016 07:38

      спасибо исправлено


  1. n01d
    20.09.2016 08:25

    Скажите, а зачем отчёты в HTML? Чем не устраивает CSV, Или на крайняк XLSX? HTML же статичный, а табличку можно фильтровать и сортировать как нравится. Кто отправил больше всех? — и вы судорожно бегаете глазами по HTML отчёту, вместо того, чтобы отсортировать по столбцу.


    1. pak-nikolai
      20.09.2016 08:51

      html для отчетов, csv для работы


  1. CucTemaTexHuk
    20.09.2016 08:53
    +1

    Спасибо за статью.
    Делал что то подобное на PowerShell и прикручивал к система мониторинга PRTG для визуализации.

    image


    1. pak-nikolai
      20.09.2016 08:54

      к prtg как прикручивал? у меня такая же стоит


      1. CucTemaTexHuk
        20.09.2016 11:57
        +1

        Скрипт работает на сервере PRTG. Скрипт подключается к Exchange через командлеты

        $session=New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://exchange/powershell
        Import-PSSession $session

        Скрипт делает расчет только за указанную дельту времени (я установил 1 час), и рассчитывает только две переменные. Сколько получено байт "$ReceivedTOTAL" и сколько отправлено байт "$SentTOTAL"
        Вывод результата нужно делать через «write-host» но при этом в формате XML.

        #Выводим результат в XML формате для PRTG.
        write-host '<?xml version="1.0" encoding="Windows-1252" ?>'
        
        write-host '<prtg>'
        
        write-host	'<result>'
        write-host		'<channel>Exchange received traffic</channel>'
        write-host		'<customUnit>Bytes</customUnit>'
        write-host		'<value>'$ReceivedTOTAL'</value>'
        write-host	'</result>'
        
        write-host	'<result>'
        write-host		'<channel>Exchange send traffic</channel>'
        write-host		'<customUnit>Bytes</customUnit>'
        write-host		'<value>'$SentTOTAL'</value>'
        write-host	'</result>'
        
        write-host '</prtg>'
        
        

        В PRTG создаешь группу которая прослушивает любой действующий IP, т.к. сенсор в этой группе будет запускать PowerShell скрипт, а скрипт сам подключается к Exchange. Т.е. нам не важен какой IP будет прослушивать данная группа. Я использовал IP 127.0.0.1
        Далее в группе создаешь сенсор «XML Custom EXE/Script Sensor». В поле «EXE/Script» указываешь скрипт PowerShell, предварительно выложив его на сервере PRTG по пути «c:\Program Files (x86)\PRTG Network Monitor\Custom Sensors\EXEXML». В поле «Scanning Interval» выставляешь с какой периодичностью будет запускаться скрипт. Периодичность равна дельте времени в скрипте.
        И Все. Ждешь когда накопится статистика.

        Вроде мы ничего не забыл. С удовольствием отвечу на Ваши вопросы если, что то не понятно.


        1. krasaval
          21.09.2016 15:17

          А как рассчитывается дельта? Где хранится предыдущее значение?


          1. CucTemaTexHuk
            21.09.2016 18:08
            +1

            Дельта не рассчитывается. Она устанавливается в ручную. Например, нужно знать сколько писем пришло за 1 час. 1 час это и будет дельта. Ее мы задаем в скрипте, и в настройке сенсора в PRTG, поле «Scanning Interval». Предыдущее значение скрипт не хранит. Он все время рассчитывает за дельту времени. В моем случае за один час назад (дельта). Как только скрипт рассчитал значение, он их передает в PRTG через «write-host» в формате XML. Смотрите код выше. PRTG строит графики, и хранит последние полученные значения. Смотрите мой скриншот. Период хранения полученной информации, задается настройками PRTG.

            Если есть интерес, могу завтра выложить полный код скрипта.


            1. krasaval
              21.09.2016 18:15

              было бы интересно взглянуть на полный текст скрипта


              1. CucTemaTexHuk
                22.09.2016 06:51
                +1

                Скрипт для сбора статистики почтового трафика (отправлено / получено) в байтах на Exchange за дельту времени и отправка в PRTG
                ###################################################################################
                # Скрипт для сбора статистики почтового трафика (отправлено / получено) в байтах
                # на Exchange за дельту времени.
                ###################################################################################
                #
                #Сохраняем ошибки в файл "TrafficExchange2XML-Error.xml" и продолжаем выполнение скрипта "continue"
                trap {$_ | Export-Clixml TrafficExchange2XML-Error.xml; continue}
                #Отключаем вывод Warning, предварительно запомним текущее значение, для того что бы вернуть его обратно.
                $CurrentWarningPreference = $WarningPreference
                $WarningPreference = "SilentlyContinue"
                #Подключаемся к Exchange
                #Если консоль Exchange Management Shell установлена
                #add-pssnapin Microsoft.Exchange.Management.PowerShell.E2010 
                #Если консоль Exchange Management Shell НЕ установлена.
                $session=New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://exchange/powershell
                Import-PSSession $session
                #Получаем все CAS сервера
                #Если работает авто определение
                #$CASs = (Get-ClientAccessServer).Name
                #Если НЕ работает авто определение
                $CASs = "CAS01", "CAS02", "CAS03"
                #Дельта времени подсчета.
                $DeltaTime = New-TimeSpan -Hours 1
                #Переменные для подсчета почтового трафика
                [int]$ReceivedTOTAL = 0
                [int]$SentTOTAL = 0
                #Для каждого CAS сервера расчитываем трафик за период времени $DeltaTime
                ForEach ($CAS in $CASs)
                      {
                      $Received = Get-MessageTrackingLog -Server $CAS -ResultSize unlimited -Start ((Get-Date) - $DeltaTime) -End (Get-Date) -EventId RECEIVE | Measure-Object TotalBytes -sum
                      $Send = Get-MessageTrackingLog -Server $CAS -ResultSize unlimited -Start ((Get-Date) - $DeltaTime) -End (Get-Date) -EventId SEND | Measure-Object TotalBytes -sum
                      $ReceivedTOTAL = $ReceivedTOTAL + $Received.Sum
                      $SentTOTAL = $SentTOTAL + $Send.Sum
                      }
                #Выводим результат в XML формате для PRTG.
                write-host '<?xml version="1.0" encoding="Windows-1252" ?>'
                
                write-host '<prtg>'
                
                write-host	'<result>'
                write-host		'<channel>Exchange received traffic</channel>'
                write-host		'<customUnit>Bytes</customUnit>'
                write-host		'<value>'$ReceivedTOTAL'</value>'
                write-host	'</result>'
                
                write-host	'<result>'
                write-host		'<channel>Exchange send traffic</channel>'
                write-host		'<customUnit>Bytes</customUnit>'
                write-host		'<value>'$SentTOTAL'</value>'
                write-host	'</result>'
                
                write-host '</prtg>'
                #Разрываем сессию с CAS сервером Exchange.
                Remove-PSSession $session
                #Возвращаем вывод Warning
                $WarningPreference = $CurrentWarningPreference
                


                1. pak-nikolai
                  22.09.2016 11:30

                  слушай оформи это в статью, у меня тоже стоит 15я PRTG и я туда сгребаю все что можно.
                  оформи плиз способ как прикручивать туда все что хочешь, все что можно посчитать. Бывает необходимость а нормальной статьи на русском не видел.
                  Нужна пошаговая инструкция как добавлять в ПРТГ кастомные счетчики.

                  я на эту статью ссылку просто буду давать, а не писать как что делать


                  1. CucTemaTexHuk
                    22.09.2016 13:09
                    +1

                    Попробую. Ни разу еще не писал статьи для Хабра.


                    1. 4mz
                      22.09.2016 20:19

                      Бешено плюсую, также используем Prtg, ждём!