Получение информации Exchange, включая Edge с помощью PowerShell

 ак что последний месяц я учился делать сценарии PowerShell. Я новичок в написании сценариев и решил поделиться своим первым сценарием обмена из-за чистого волнения. Я хотел бы выразить особую благодарность Шей Леви @ https://blogs.microsoft.co.il/blogs/ScriptFanaticШей оказал мне огромную помощь в этом деле. @ https://powershellcommunity.org когда я сталкиваюсь с проблемами, с которыми сталкивается любой типичный новичок (особенно со сценариями). Другие люди также оказали нам большую помощь.

Я начал этот сценарий на прошлой неделе и буду добавлять его по мере получения новых знаний. Скелет скрипта до сих пор дополнен 1 функцией (для получения информации о диске) и позволит мне легко добавить в скрипт дополнительные функции. Я хотел предоставить метод динамической проверки того, является ли сервер пограничным сервером, и запрос на проверку подлинности, чтобы позволить PowerShell получать информацию с моих пограничных серверов.

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

Вот сценарий, который у меня есть до сих пор:

# Define what paramters (-role) can be utilized when running the switch
Param(
[string] $role = "All"
)

# Define what values (Mbx, Cas, Etc.) can be utilized when using our -role parameter
switch ($role)
{
 Mbx { $role = "IsMailboxServer" }
 Cas { $role = "IsClientAccessServer" }
 Um { $role = "IsUnifiedMessagingServer" }
 Hub { $role = "IsHubTransportServer" }
 Edge { $role = "IsEdgeServer" }
}

# Test host to see if it replies to ping prior to allowing information gather functions to proceed.
function Ping-Host
{
 $result = Gwmi -Query "SELECT * FROM Win32_PingStatus WHERE Address='$server'"
 if ($result.statuscode -eq 0) {$true} else {$false}
}

# Function to display disk information. If the server being checked is an Edge Server, you will be prompted for authentication.
function Get-DiskInformation
{
 ""
 "Server: $server"
 if ($server.isEdgeServer)
 {
 $erroractionpreference = "SilentlyContinue"
 $cred = Get-Credential
 if ($cred) { $disk = Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server -Credential $cred }
 else { Write-Warning "Could not obtain disk information for $server due to no credentials being provided" ; break }
 $erroractionpreference = "Continue"
 }
 else { $disk = Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server }

 foreach($d in $disk)
 {
 $obj = New-Object PSObject
 $obj | Add-Member NoteProperty "Size(G)" ([math]::round($d.Size/1GB,2))
 $obj | Add-Member NoteProperty "Freespace(G)" ([math]::round($d.FreeSpace/1GB,2))
 $obj | Add-Member NoteProperty "Used(G)" ([math]::round($d.Size/1GB - $d.FreeSpace/1GB,2) )
 $obj | Add-Member NoteProperty "Freespace(%)" ([Math]::Round((($d.FreeSpace/1GB) / ($d.Size/1GB) * 100),2))
 $obj | Add-Member NoteProperty "Usedspace(%)" ([Math]::Round(((1 - ($d.FreeSpace/1GB) / ($d.Size/1GB)) * 100),2))
 $obj
 }
}

# Place servers into the $colServers variable which will later be fed into Functions
if ($role -ne "All" -and $role -ne "IsEdgeServer") { $colServers = Get-ExchangeServer | Where-Object {$_.$role -eq "True" -and $_.IsEdgeServer -ne "True"} }
elseif ($role -ne "All" -and $role -eq "IsEdgeServer") { $colServers = Get-ExchangeServer | Where-Object {$_."IsEdgeServer" -eq "True"} }
else { $colServers = Get-ExchangeServer }

if (!$colServers) { Write-Warning "There are no servers of the specified type to gather information for." }
else
{
 foreach ($server in $colServers)
 {
 if (Ping-Host -eq "$true")
 {
 Get-DiskInformation
 }
 else
 {
 Write-Warning "$server is not pingable"
 }
 }
}
Есть один фрагмент сценария, который я хочу разобрать. Это функция получения-дезинформации. Раньше у меня была функция, написанная как таковая.

Obtaining Exchange Information including Edge with PowerShell

So the last month I’ve been learning how to do PowerShell scripting. I’m new at scripting and figured I would share my first Exchange script due to sheer excitement. I’d like to give a special thanks to Shay Levy @ https://blogs.microsoft.co.il/blogs/ScriptFanatic. Shay has been a tremendous help over @ https://powershellcommunity.org when I run into issues that any typical novice runs into (especially with scripting). The other folks have been a great help as well.

I started this script last week and will be adding to it as I gain more knowledge. The skeleton of the script is complete with 1 function so far (to get disk information) and will allow me to easily add more functions to the script. I wanted to provide a method to dynamically check if a server was an Edge Server and prompt for authentication to allow PowerShell to obtain information from my Edge Servers.

With an Edge server, it is not going to be a part of your corporate domain . It may be a part of a forest/domain dedicated in your DMZ, but not your corporate forest/domain. Typically if you want to pull information from a domain-joined machine that is a part of your corporate domain, you just run a command against it and it’ll work if you have the access to do so. But since the Edge server is a part of a workgroup or DMZ forest/domain, it’s not that easy. This script will check if you are trying to check an Edge Server, and if so, it will display on the console that it is checking for Edge authentication and prompt you for credentials for that Edge Server.

Here’s the script I have so far:

# Define what paramters (-role) can be utilized when running the switch
Param(
[string] $role = "All"
)

# Define what values (Mbx, Cas, Etc.) can be utilized when using our -role parameter
switch ($role)
{
 Mbx { $role = "IsMailboxServer" }
 Cas { $role = "IsClientAccessServer" }
 Um { $role = "IsUnifiedMessagingServer" }
 Hub { $role = "IsHubTransportServer" }
 Edge { $role = "IsEdgeServer" }
}

# Test host to see if it replies to ping prior to allowing information gather functions to proceed.
function Ping-Host
{
 $result = Gwmi -Query "SELECT * FROM Win32_PingStatus WHERE Address='$server'"
 if ($result.statuscode -eq 0) {$true} else {$false}
}

# Function to display disk information. If the server being checked is an Edge Server, you will be prompted for authentication.
function Get-DiskInformation
{
 ""
 "Server: $server"
 if ($server.isEdgeServer)
 {
 $erroractionpreference = "SilentlyContinue"
 $cred = Get-Credential
 if ($cred) { $disk = Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server -Credential $cred }
 else { Write-Warning "Could not obtain disk information for $server due to no credentials being provided" ; break }
 $erroractionpreference = "Continue"
 }
 else { $disk = Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server }

 foreach($d in $disk)
 {
 $obj = New-Object PSObject
 $obj | Add-Member NoteProperty "Size(G)" ([math]::round($d.Size/1GB,2))
 $obj | Add-Member NoteProperty "Freespace(G)" ([math]::round($d.FreeSpace/1GB,2))
 $obj | Add-Member NoteProperty "Used(G)" ([math]::round($d.Size/1GB - $d.FreeSpace/1GB,2) )
 $obj | Add-Member NoteProperty "Freespace(%)" ([Math]::Round((($d.FreeSpace/1GB) / ($d.Size/1GB) * 100),2))
 $obj | Add-Member NoteProperty "Usedspace(%)" ([Math]::Round(((1 - ($d.FreeSpace/1GB) / ($d.Size/1GB)) * 100),2))
 $obj
 }
}

# Place servers into the $colServers variable which will later be fed into Functions
if ($role -ne "All" -and $role -ne "IsEdgeServer") { $colServers = Get-ExchangeServer | Where-Object {$_.$role -eq "True" -and $_.IsEdgeServer -ne "True"} }
elseif ($role -ne "All" -and $role -eq "IsEdgeServer") { $colServers = Get-ExchangeServer | Where-Object {$_."IsEdgeServer" -eq "True"} }
else { $colServers = Get-ExchangeServer }

if (!$colServers) { Write-Warning "There are no servers of the specified type to gather information for." }
else
{
 foreach ($server in $colServers)
 {
 if (Ping-Host -eq "$true")
 {
 Get-DiskInformation
 }
 else
 {
 Write-Warning "$server is not pingable"
 }
 }
}

There’s one piece of the script I want to disuss. It’s the function for Get-DiskInformation. I used to have the function written as such.

function Get-DiskInformation
{
 ""
 "Server: $server"
 $(if ($server.isEdgeServer) { Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server -Credential (Get-Credential) }
 else { Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server }) | `
 Format-Table -autosize DeviceID,VolumeName,`
 @{Label="Size(G)";Expression={[math]::round($_.Size/1GB,2)}},`
 @{Label="Freespace(G)";Expression={[math]::round($_.FreeSpace/1GB,2)}},`
 @{Label="Used(G)";Expression={[math]::round($_.Size/1GB - $_.FreeSpace/1GB,2)}},`
 @{Label="Freespace(%)";Expression={[Math]::Round((($_.FreeSpace/1GB) / ($_.Size/1GB) * 100),2)}},`
 @{Label="Usedspace(%)";Expression={[Math]::Round(((1 - ($_.FreeSpace/1GB) / ($_.Size/1GB)) * 100),2)}}
}
Теперь проблема с тем, как я раньше ее писал, заключается в том, что она заставляет форматировать таблицу и определенный стиль. 
Так что, по сути, я заставляю выход выглядеть определенным образом. Пользователь не может динамически изменять способ отображения выходных данных
 и испортит форматирование, если вы, например, захотите поместить данные в лист Excel.

Теперь давайте взглянем на то, как я это сделал в окончательном сценарии, который я опубликовал.
function Get-DiskInformation
{
 ""
 "Server: $server"
 if ($server.isEdgeServer)
 {
 $erroractionpreference = "SilentlyContinue"
 $cred = Get-Credential
 if ($cred) { $disk = Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server -Credential $cred }
 else { Write-Warning "Could not obtain disk information for $server due to no credentials being provided" ; break }
 $erroractionpreference = "Continue"
 }
 else { $disk = Get-WmiObject Win32_LogicalDisk -namespace root\cimv2 –filter "DriveType = 3" -ComputerName $server }

 foreach($d in $disk)
 {
 $obj = New-Object PSObject
 $obj | Add-Member NoteProperty "Size(G)" ([math]::round($d.Size/1GB,2))
 $obj | Add-Member NoteProperty "Freespace(G)" ([math]::round($d.FreeSpace/1GB,2))
 $obj | Add-Member NoteProperty "Used(G)" ([math]::round($d.Size/1GB - $d.FreeSpace/1GB,2) )
 $obj | Add-Member NoteProperty "Freespace(%)" ([Math]::Round((($d.FreeSpace/1GB) / ($d.Size/1GB) * 100),2))
 $obj | Add-Member NoteProperty "Usedspace(%)" ([Math]::Round(((1 - ($d.FreeSpace/1GB) / ($d.Size/1GB)) * 100),2))
 $obj
 }
}

Как видите, все выглядит совсем по-другому. То, что я сделал здесь, было созданием объекта PowerShell. Это позволяет мне добавлять данные в этот объект, который в свою очередь позволяет мне передавать нашу команду во множество различных выходных данных, таких как Format-Table, Format-List (который будет использоваться по умолчанию, так как есть >=4 строки возвращаемых данных), Excel и т. д.. Я также добавил другие проверки ошибок, такие как если вы не введете пользователя/пароль для вашего пограничного сервера, он не будет выбрасывать кучу ошибок на экран. Вместо этого он предоставит хорошее предупреждающее сообщение, сообщающее вам, что учетные данные не были предоставлены.

Давайте рассмотрим пример вытягивания только информации о диске для почтового ящика (и да, это тестовая лаборатория, и Exchange работает на моем DC, что я никогда бы не сделал в производстве!).


Итак, что произойдет, если мы не зададим команду-role или не зададим все для команды role? То же самое произойдет, так как автоматически предполагается переключение ролей, и по умолчанию для команды используется все.


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


Мы все еще извлекали информацию для нашего присоединенного к домену сервера Exchange, но теперь, когда наш пограничный сервер запущен и мы можем пинговать его, он запрашивает у нас аутентификацию. На экране сервер, который он запрашивает, - это OCS-EXCEdge, который является именем нашего пограничного сервера. Итак, давайте введем наши учетные данные для этого сервера OCS-EXCEdge.

Теперь мы прошли аутентификацию на пограничном сервере и получили информацию о диске. Если бы мы вместо этого нажали кнопку Отмена в приглашении аутентификации, то получили бы:

И последнее, что я хочу показать, - это еще один безопасный охранник на случай, если вы введете неправильный тип сервера.