[Перевод] Создание отчетов в PowerShell

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

Секреты создания отчетов Глава 33


Эта глава содержит

  1. Работа с HTML фрагментами
  2. Создание стильных HTML отчетов
  3. Отправка отчетов поэлектронной почте

В этой главе мы рассмотрим приемы работы с PowerShell при создании отчетов. PowerShell не блещет если нужно работать со строками, старайтесь использовать для этого объекты. Чем больше вы будете использовать при создании отчетов объекты, тем лучше вы сможете сделать обработку.

33.1 Что не нужно делать.
Начнем главу с того что мы считаем примером плохой техники создания отчетов. Мы постоянно встречаем такой стиль. Большая часть IT профессионалов не задумываются об этом и увековечивают в коде стиль из других языков, таких как VBScript.
Следующий код написан в стиле, который как мы надеемся вы не будете применять, и который вы увидите в коде менее информированных системных администраторов.
Листинг 33.1 Плохо спроектированный скрипт инвентаризации

param ($computername)
Write-Host '------- COMPUTER INFORMATION -------'
Write-Host "Computer Name: $computername"

$os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername
Write-Host " OS Version: $($os.version)"
Write-Host " OS Build: $($os.buildnumber)"
Write-Host " Service Pack: $($os.servicepackmajorversion)"

$cs = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername
Write-Host " RAM: $($cs.totalphysicalmemory)"
Write-Host " Manufacturer: $($cs.manufacturer)"
Write-Host " Model: $($cd.model)"
Write-Host " Processors: $($cs.numberofprocessors)"

$bios = Get-WmiObject -Class Win32_BIOS -ComputerName $computername
Write-Host "BIOS Serial: $($bios.serialnumber)"
Write-Host ''
Write-Host '------- DISK INFORMATION -------'
Get-WmiObject -Class Win32_LogicalDisk -Comp $computername –Filt 'drivetype=3' |
Select-Object @{n='Drive';e={$_.DeviceID}},
@{n='Size(GB)';e={$_.Size / 1GB -as [int]}},
@{n='FreeSpace(GB)';e={$_.freespace / 1GB -as [int]}} | Format-Table –AutoSize


Код приведенный в листинге 33.1 произведет вывод подобный этому
image
Фигура 33.1 вывод, основанный на строках.

Как видно этот скрипт работает, Дон Джонс (один из авторов) видя вывод из скрипта чистыми строками гневно произносит поговорку с участием божества и щенков (наверное, ругательство в оригинале Don has a saying involving angry deities and puppies that he utters whenever he sees a script that outputs pure text like this). Прежде всего — этот сценарий может выводить только на экран, т.к. вывод ведется через Write-Host. В большинстве случаев, когда вы будете использовать Write-Host, вы будете делать это неправильно. Было бы неплохо если была бы возможность вывести эту информацию в файл, или в HTML? Вы можете добиться этого изменяя все Write-Host на Write-Output, но это по-прежнему будет неправильный путь.

Есть более эффективные способы формирования отчета, и это причина по которой мы написали эту главу. Во-первых, мы предложили бы для каждого блока или функции, где происходит генерация информации создавать один объект содержащий всю нужную информацию. Чем больше вы разобьете на блоки код, тем больше вы сможете повторно использовать эти блоки. В нашем плохом примере, первый раздел «информация о компьютере», должно осуществляться функцией которую мы напишем. Ее можно будет использовать во всех отчетах подобного вида. В разделе «информация о диске» данные указываются одним объектом, объединять информацию из разных источников не нужно, но все командлеты на Write должны уйти. (прим переводчика. Как надо делать смотрите в примере к разделу 33.2.1 Получение исходной информации)

Исключения из каждого правила
Есть исключения из каждого правила. Ричард Сиддэвэй проводит много времени проводя аудит чужих систем. Он делает это стандартным набором сценариев. Сценарии предназначены для производства вывода, которые затем идут либо непосредственно в документ Word, либо генерируются файлики которые затем вставляются в документ. Таким образом первоначальные данные могут быть очень быстро получены и просмотрены, так что их анализ и обсуждение не задерживается.

Правила нарушающиеся в этих сценариях следующие

  1. Выход представляет собой смесь текста и объектов
  2. Вывод сразу отформатирован


Это осознанное решение, так как известно, как будут применяться скрипты и как должен выглядеть отчет. Мораль этой истории выводить объектами, и быть готовым выйти за пределы парадигмы если на то есть причины.

33.2 Работа с HTML фрагментами и файлами
Хитрость нашего способа в том, что ConvertTo-HTML можно использовать двумя различными способами. Первый способ — производить полную HTML страницу, второй — производить HTML фрагмент. Этот фрагмент всего лишь HTML таблица с данными что были переданы в командлет, мы произведем каждую секцию отчета в виде фрагмента, а затем соберем фрагменты в полную HTML страницу.

33.2.1 Получение исходной информации
Мы начнем с того что соберем данные в объекты, по одному объекту для каждого раздела отчета. В нашем случае будет два объекта — информация о компьютере и информация о дисках. Условимся что для кротости и ясности мы пропустим обработку ошибок и другие тонкости. В реальных условиях мы добавили бы их. Get-WMIObject сама по себе производит объект содержащий информацию по дискам. Значит нужно еще создать функцию выдающую объект с информацией о компьютере.

function Get-CSInfo {
param($computername)
$os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername
$cs = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername
$bios = Get-WmiObject -Class Win32_BIOS -ComputerName $computername

#property names with spaces need to be enclosed in quotes
$props = @{ComputerName = $computername
           'OS Version' = $os.version
           'OS Build' = $os.buildnumber
           'Service Pack' = $os.sevicepackmajorversion
           RAM = $cs.totalphysicalmemory
           Processors = $cs.numberofprocessors
           'BIOS Serial' = $bios.serialnumber}

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

Write-Output $obj
}


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

33.2.2 Производство фрагментов HTML отчетов.

Теперь мы можем использовать написанную функцию чтобы получить отчет в HTML

$frag1 = Get-CSInfo –computername SERVER2 | ConvertTo-Html -As LIST -Fragment –PreContent '

Computer Info

' | Out-String


Мы долго двигались к этому трюку, так что его обязательно нужно разобрать:
1. Вы сохраняете вывод в виде фрагмента HTML в переменную с именем $frag1, позже мы сможем вставить его в нужное место вывода либо целиком сохранить в файл.
2. запускается Get-CSInfo, ему передается имя компьютера с которого мы хотим получить данные, сейчас мы прописываем имя компьютера жестко, в будущем мы заменим его на переменную.
3. Получившийся вывод подаем на ConvertTo-HTML, эта команда формирует на выходе фрагмент HTML в виде вертикального списка, а не горизонтально. Список будет имитировать вид старого отчета по негодной-технике-выводить-информацию.
4. Мы используем параметр –PreContent чтобы добавить надпись перед табличкой отчета. Мы добавили теги чтобы получился жирный заголовок.
5. Все что получилось — это и есть трюк — передается дальше на Out-String. Вы увидите что ConvertTo-HTML поставит кучу всего в конвейер. Вы видите, что в конвейер пишутся строки, коллекции строк, всякие разные другие объекты. Все это приведет в конце к проблемам, когда вы попытаетесь собрать это все в окончательную HTML страницу, вместо мы просто подали на Out-String и получили на выходе старую добрую строку.
 
Можно пойти дальше и произвести второй фрагмент. Это легче т.к. не нужно писать функцию, генерация HTML будет выглядеть точно также. Единственное отличие что мы соберем данные этой секции в таблицу, а не список:  

$frag2 = Get-WmiObject -Class Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName SERVER2 |
Select-Object @{name='Drive';expression={$_.DeviceID}},
@{name='Size(GB)';expression={$_.Size / 1GB -as [int]}},
@{name='FreeSpace(GB)';expression={
$_.freespace / 1GB -as [int]}} | ConvertTo-Html -Fragment -PreContent '

Disk Info

' | Out-String


У нас есть оба фрагмента, можно приступить к формированию окончательного отчета.

33.2.3 Сборка финального HTML файла
Сборка включает в себя добавление ваших двух фрагментов, и таблицы стилей. Использование таблицы стилей CSS и языка выходит за рамки этой книги. Таблица стилей позволяет контролировать форматирование HTML страницы так чтобы она выглядела намного лучше. Если вы хотите хороший учебник и ссылки на дополнительные материалы по CSS, проверьте  http://www.w3schools.com/css/

$head = @'

'@

ConvertTo-HTML -head $head -PostContent $frag1, $frag2 -PreContent "

Hardware Inventory for SERVER2

"


 Создается таблица стилей $head, в переменной типа строка описывается нужный стиль. Затем эта переменная передается в параметр –head, а ваши фрагменты перечисляются через запятую в параметре –PostContent. Также добавляется заголовок отчета в параметре –PreContent. Сохраните весь сценарий как C: \ Good.ps1 и запустите его следующим образом:
./good > Report.htm
Это перенаправит вывод в файл Report.htm, который будет красив, как на рисунке 33.2
 
image
Рисунок 33.2 отчет из нескольких фрагментов
 
Может быть это не произведение искусства, но это отчет который выглядит лучше чем отчет на экране которым начиналась эта глава. В листинге 33.2 показан завешенный скрипт, где вы можете задать имя компьютера, по умолчанию localhost. В заголовке прописан [CmdletBinding ()], что позволяет использовать –verbose. В теле скрипта вставлены Write-Verbose, вы можете видеть что делает каждый шаг.
 
Листинг 33.2 скрипт HTML инвентаризации

<#
.DESCRIPTION
Retrieves inventory information and produces HTML
.EXAMPLE
./Good > Report.htm
.PARAMETER
The name of a computer to query. The default is the local computer.
#>
[CmdletBinding()]
param([string]$computername=$env:computername)
# function to get computer system info
function Get-CSInfo {
param($computername)

$os = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $computername
$cs = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computername
$bios = Get-WmiObject -Class Win32_BIOS -ComputerName $computername
$props = @{'ComputerName'=$computername
           'OS Version'=$os.version
           'OS Build'=$os.buildnumber
           'Service Pack'=$os.sevicepackmajorversion
           'RAM'=$cs.totalphysicalmemory
           'Processors'=$cs.numberofprocessors
           'BIOS Serial'=$bios.serialnumber}
$obj = New-Object -TypeName PSObject -Property $props
Write-Output $obj
}
Write-Verbose 'Producing computer system info fragment'
$frag1 = Get-CSInfo -computername $computername | ConvertTo-Html -As LIST -Fragment -PreContent '

Computer Info

' | Out-String Write-Verbose 'Producing disk info fragment' $frag2 = Get-WmiObject -Class Win32_LogicalDisk -Filter 'DriveType=3' -ComputerName $computername | Select-Object @{name='Drive';expression={$_.DeviceID}}, @{name='Size(GB)';expression={$_.Size / 1GB -as [int]}}, @{name='FreeSpace(GB)';expression={$_.freespace / 1GB -as [int]}} | ConvertTo-Html -Fragment -PreContent '

Disk Info

' | Out-String Write-Verbose 'Defining CSS' $head = @' '@ Write-Verbose 'Producing final HTML' Write-Verbose 'Pipe this output to a file to save it' ConvertTo-HTML -head $head -PostContent $frag1,$frag2 -PreContent "

Hardware Inventory for $ComputerName

"

Использование скрипта

PS C:\> $computer = SERVER01
PS C:\> C:\Scripts\good.ps1 -computername $computer |
>> Out-File "$computer.html"
>>
PS C:\> Invoke-Item "$computer.html"


Скрипт генерирует HTML файл, который можно использовать в будущем, и выводит на экран отчет. Имейте в ввиду что функция Get-CSInfo может использоваться повторно. Поскольку она выводит объект, а не текст вы можете его использовать в самых разных местах где потребуется выводить туже информацию.
Если вам понадобилось добавить добавить еще информации к отчету, то для добавления новой секции вам будет нужно:

  • Написать функцию или команду генерирующую объект, с информацией новой секции отчета.
  • Создать из этого объекта HTML фрагмент и сохранить в переменную.
  • Добавить эту переменную в список переменных команды сборки окончательного отчета. Таким образом вы дополните отчет.
  • Все


 Да этот отчет — это текст. В конечном счете каждый отчет будет текстом, потому что текст — это то что мы читаем. Сутью этого метода является то что все остается объектами до последнего момента. Вы позволяете PowerShell форматировать за вас. Рабочие элементы этого скрипта могут быть скопированы и использованы в другом месте, что невозможно сделать с помощью исходного текста в начале главы.
 
33.3 отправка электронного письма
Что может быть лучше HTML отчета? Отчет, который автоматически придет на email!
 
К частью PowerShell уже содержит командлет Send-MailMessage. Немного исправим наш скрипт:

Write-Verbose 'Producing final HTML'
Write-Verbose 'Pipe this output to a file to save it'
ConvertTo-HTML -head $head -PostContent $frag1,$frag2 -PreContent "

Hardware Inventory for $ComputerName

" | Out-File report.htm Write-Verbose "Sending e-mail" $params = @{'To'='whomitmayconcern@company.com' 'From'='admin@company.com' 'Subject'='That report you wanted' 'Body'='Please see the attachment.' 'Attachments'='report.htm' 'SMTPServer'='mail.company.com'} Send-MailMessage @params


мы изменили конец конвейера перенаправив вывод в файл. Затем использовали Send-MailMessage в качестве вложения. Можно отправить HTML как само тело сообщения. Вам не нужно для этого создавать файл на диске, вы можете взять вывод с конвейера непосредственно. Вот альтернативный пример

Write-Verbose 'Producing final HTML'
$body = ConvertTo-HTML -head $head -PostContent $frag1,$frag2 -PreContent "

Hardware Inventory for $ComputerName

" | Out-String Write-Verbose "Sending e-mail" $params = @{'To'='whomitmayconcern@company.com' 'From'='admin@company.com' 'Subject'='That report you wanted' 'Body'=$Body 'BodyAsHTML'=$True 'SMTPServer'='mail.company.com'} Send-MailMessage @params


Здесь мы построили параметры Send-MailMessage в хеш таблице и сохранили их в переменной $Param. Это позволяет использовать splat технику и скормить все параметры команде сразу. Нет никакой разницы что вы наберете их как параметры или укажете через хэш таблицу, работать будет в любом случае, но так лучше читать.
 
33.4 Итог
Построение отчетов, безусловно большая потребность для администраторов, мы хотели показать, что PowerShell хорошо подходит для этой задачи. Хитрость заключается в том, чтобы создавать отчеты таким образом, чтобы извлечение информации отделить от форматирования и создания выхода. На самом деле PowerShell способен предоставить большие возможности по форматированию и выводу при небольшом количество работы с вашей стороны.

© Habrahabr.ru