Использование преимуществ рабочих процессов: 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 секунд....
Просто заставить эту часть работать в одиночку сэкономит время, однако я не могу понять, каким образом структурировать мой сценарий / функцию / рабочий процесс или что-то еще будет работать лучше.
Я придумал два контрольно-пропускных пункта.
В небольшом рабочем процессе для проверки связи с серверами я не могу (выработать, как) сохранить результаты в переменной, чтобы увидеть общее количество (либо с помощью
$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
,Я пытался сделать весь процесс рабочим процессом, используя
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