Использование преимуществ рабочих процессов: foreach -parallel, invoke-command, структурирование скриптов

С компьютера под управлением PowerShell v5, предварительный просмотр:

  • Попытка получить список (да, Windows 2003, они должны быть удалены!) Серверов из AD и записать общее количество найденных серверов
  • Test-Connection им всем и обратите внимание на общую отзывчивость
  • вызовите команду (для которой требуются разные учетные данные) на всех серверах, чтобы запустить набор команд для сбора информации о каждом сервере и вывода в файл на них
  • создайте новые PSDrive для сопоставления с каждым сервером для загрузки файла и отметьте общее количество загруженных файлов (т. е. разницу между серверами, которые отвечают на эхо-запрос и успешно вызывают команду / результаты загрузки)
  • затем запустить Get-Printers командлет, который не запускается в PowerShell v2 и выводится в файл в той же папке, что и загруженные результаты на локальном компьютере

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

Чтобы начать работать лучше:

Получение списка серверов и сохранение в переменной - это нормально.

$2003s = Get-ADComputer -Filter {OperatingSystem -eq "Windows server 2003"} -Properties  OperatingSystem

Получение итогов - это нормально.

$Total2003s = $2003s.count

Теперь может быть несколько серверов 2003, которые больше не существуют, которые не были удалены из AD, поэтому давайте пинговать их всех.

$Responsive2003s = Test-Connection $2003s.dnshostname -Delay 1 -Count 1 -ErrorAction SilentlyContinue

Это будет длиться вечно, если, скажем, в $2003 есть 300 объектов, а половина не отвечает.

Measure-Command {Test-Connection $2003s.dnshostname -Delay 1 -Count 1 -ErrorAction SilentlyContinue}

7 минут...

Итак, я подумал, эй, давайте создадим рабочий процесс Powershell, чтобы я мог использовать foreach -parallel, Если я напишу быстрый рабочий процесс, чтобы получить серверы от AD и foreach Test-Connection:

Measure-Command {Workflow-Testconnection}

27 секунд....

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

Я придумал два контрольно-пропускных пункта.

  1. В небольшом рабочем процессе для проверки связи с серверами я не могу (выработать, как) сохранить результаты в переменной, чтобы увидеть общее количество (либо с помощью $variable = Test-Connection ..., или же Test-Connection | New-Variableтак что нет смысла пинговать их, если я не вижу разницы между серверами в рекламе и серверами, которые отвечают. я могу использовать

    $2003sDnsHostname | foreach {
      Get-Printer -ThrottleLimit 500 -ErrorAction SilentlyContinue -ComputerName $_ |
        Sort-Object -Property Portname |
        FT -AutoSize |  Out-File -FilePath "D:\$($_) - Printers.txt"
    }
    

    но это медленно, и если я использую более быстрый рабочий процесс, я не могу использовать Format-Table на выходе Get-Printers,

  2. Я пытался сделать весь процесс рабочим процессом, используя foreach -parallel в нужных местах, но потому что у меня есть $creds = Get-Crendential работать с моим Invoke-Command -Credential $Creds рабочий процесс даже не загружается.

Кажется, что для каждой выгоды, которую я хочу с каждым шагом в моем сценарии, есть нарушитель, который делает это не стоит, но я уверен, что есть способ:)

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

$SVRAcctCreds = Get-Credential

#Enable the ActiveDirectory module as first time users might not have it already on their computer, and it doesn't hurt to enable it again if it's already there
Enable-WindowsOptionalFeature -Online -FeatureName RemoteServerAdministrationTools-Roles-AD-Powershell -NoRestart

$2003s = Get-ADComputer -Filter {OperatingSystem -eq "Windows server 2003"} -Properties operatingsystem
$2003sDnsHostname = $2003s.dnshostname
$Total = $2003s.count
$Responsive2003s = Test-Connection $2003s.dnshostname -Delay 1 -Count 1 -ErrorAction SilentlyContinue
$TotalResponsive2003s = $Responsive2003s.count

Invoke-Command -ComputerName ($2003s.dnshostname) -Credential $SVRAcctCreds -ThrottleLimit 100 -ErrorAction SilentlyContinue -ScriptBlock {
  #Make folder for output
  mkdir Z:\2003Migration | Out-Null

  #Serial number, model number, output to file
  Get-WmiObject win32_computersystem |
    Select-Object Manufacturer, Model | Format-List |
    Out-File -Append -FilePath Z:\2003Migration\"$env:SiteCode $env:SiteName"_MigrationData.txt
  Get-WmiObject win32_bios | Select-Object SerialNumber |
    Out-File -Append -FilePath Z:\2003Migration\"$env:SiteCode $env:SiteName"_MigrationData.txt
  Systeminfo | Select-String "Install Date:" |
    Out-File -Append -FilePath Z:\2003Migration\"$env:SiteCode $env:SiteName"_MigrationData.txt
}

##### Download Gathered data from servers
$2003s | ForEach-Object {
  New-PSDrive -ErrorAction SilentlyContinue -PSProvider FileSystem -Name $_.name -Credential $SVRAcctCreds -Root "\\$($_.dnshostname)\z$\2003Migration"
} | Out-Null
Get-PSDrive | where name -Like "SVR*" | foreach {
  Copy-Item "$($_.Name):" -Recurse -Destination d:\ -ErrorAction SilentlyContinue
} 
$TotalPSDrives = (Get-PSDrive | where name -Like "SVR*").count

# Report other information

"Total number of servers on Windows Server 2003 in AD, matching by OperatingSystem " | Out-File -Append -FilePath "d:\2003Migration\_Total Server counts.txt"
"$Total" | Out-File -Append -FilePath "d:\2003Migration\_Total Server counts.txt"

"Total Number of servers that responded to a ping command" | Out-File -Append -FilePath "d:\2003Migration\_Total Server counts.txt"
"$TotalResponsive2003s" | Out-File -Append -FilePath "d:\2003Migration\_Total Server counts.txt"

"Total Number of servers that ran the commands and returned data" | Out-File -Append -FilePath "d:\2003Migration\_Total Server counts.txt"
"Total Number of servers that ran the commands and returned data downloaded to D:\2003Migration"
"$TotalPSDrives " | Out-File -Append -FilePath "d:\2003Migration\_Total Server counts.txt"
"$TotalPSDrives "
"Mismatch means server could be pinged but could not run a powershell session to invoke commands, possible hard drive full? Powershell remoting not enabled?"

####Printers
# Didn't have enough time to work out how to only ask responsive servers the printers, so ask all, takes longer, then clean up empty files
$2003sDnsHostname | foreach {
  Get-Printer -ThrottleLimit 500 -ErrorAction SilentlyContinue -ComputerName $_ |
    Sort-Object -Property Portname | FT -AutoSize |
    Out-File -FilePath "D:\2003Migration\$($_) - Printers.txt"
}
Get-ChildItem D:\2003Migration | where Length -EQ 0 | Remove-Item
#clean up text files left on server
Invoke-Command -ComputerName ($2003s.dnshostname) -Credential $SVRAcctCreds -ThrottleLimit 100 -ErrorAction SilentlyContinue -ScriptBlock {
  Remove-Item "Z:\2003migration" -Recurse -ErrorAction SilentlyContinue
}

1 ответ

Я всегда находил рабочие процессы немного хитрыми, потому что они ведут себя немного иначе, чем "обычный" PowerShell. Вместо этого вам может быть труднее использовать рабочие места. Примерно так получился бы список с именами только тех серверов, которые ответили на Test-Connection:

$Responsive2003s = $2003s | % {
  Start-Job -ScriptBlock {
    Param($name, $address)
    if (Test-Connection $address -Delay 1 -Count 1 -EA SilentlyContinue) {
      $name
    }
  } -ArgumentList $_.Name, $_.IPv4Address
} | Wait-Job | Receive-Job
Другие вопросы по тегам