利用工作流:foreach -parallel,invoke-command,script constructuring

时间:2015-06-20 10:45:30

标签: powershell powershell-workflow

从运行PowerShell v5 April预览的计算机上运行:

  • 尝试从AD获取(是的,Windows 2003,它们需要消失!)服务器列表并记下找到的服务器总数
  • Test-Connection向所有人提出并注意到总体响应
  • 在所有服务器上调用一个命令(需要不同的凭据)来运行一堆命令来收集有关每个服务器的信息并输出到它们上的文件
  • 创建新的PSDrives以映射到每个服务器以下载文件并记下下载的总文件(即响应ping并成功调用命令/下载结果的服务器之间的差异)
  • 然后运行Get-Printers cmdlet,该cmdlet将无法在PowerShell v2上运行,并输出到与本地计算机上下载结果相同的文件夹中的文件

我有一个工作脚本 - 它非常慢,我想学习和改进它,使它更快,更优雅。我发现很难写出来并解释哈哈。如果你试图解决这个问题,你会用什么方式解决它?

开始制定更好的方法:

获取服务器列表并存储在变量中即可。

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

获得总数是可以的。

$Total2003s = $2003s.count

现在可能有一些不再存在的2003服务器没有从AD中删除,所以让它们全部ping。

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

如果$ 2003s中有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. 在ping服务器的小工作流程中,我无法(计算出如何)将结果存储在变量中以查看总数(使用$variable = Test-Connection ...Test-Connection | New-Variable,因此,如果我看不到广告中的服务器与响应的服务器之间存在差异,那么ping它们是没有意义的。我可以使用

    $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一起工作,所以工作流程赢了“甚至加载。

  3. 似乎对于我的脚本的每一步我想要的每一个好处,都有一个使得它不值得做的交易破坏者,但我确信有一种方法:)

    整个工作但很慢的事情,编辑删除敏感的东西等只是为了得到这个概念。它可以被制作成具有参数和详细输出等的函数,但这也是一个待办事项。我想看看它是否可以先加速。

    $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 个答案:

答案 0 :(得分:2)

我总是发现工作流程有点棘手,因为它们与“常规”PowerShell略有不同。您可以使用jobs来减少困难。这样的事情会给你一个列表,其中只包含那些响应$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 的服务器的名称:

{{1}}