2016-01-08 51 views
1

我创建了以下脚本来重置特定主机文件中所有计算机上的本地管理员帐户的密码。该脚本功能正常并提供了有用的输出,但速度很慢,因为它一次只能运行一台机器。从后台作业获取输出

# function to convert a secure string to a standard string 
function ConvertTo-String { 
    param(
     [System.Security.SecureString] $secureString 
    ) 

    $marshal = [System.Runtime.InteropServices.Marshal] 

    try { 
     $intPtr = $marshal::SecureStringToBSTR($secureString) 
     $string = $marshal::PtrToStringAuto($intPtr) 
    } 
    finally { 
     if($intPtr) { 
      $marshal::ZeroFreeBSTR($intPtr) 
     } 
    } 
    $string 
} 

$clients = Get-Content -Path C:\scripts\utilities\hostnames_online.txt 
$adminUser = "Administrator" 

# prompt for password and confirm 
do { 
    $ss1 = Read-Host "Enter new password" -AsSecureString 
    $ss2 = Read-Host "Enter again to confirm" -AsSecureString 

# compare strings - proceed if same - prompt again if different 
    $ok = (ConvertTo-String $ss1) -ceq (ConvertTo-String $ss2) 
    Write-Host "Passwords match" 

    if(-not $ok) { 
     Write-Host "Passwords do not match" 
    } 
} 
until($ok) 

# set password variable to string value 
$adminPassword = ConvertTo-String $ss1 

# setup job to reset password on each client 
foreach($client in $clients) { 
    $status = "OFFLINE" 
    $isOnline = "OFFLINE" 

    if((Test-Connection -ComputerName $client -Quiet -Count 1 -Delay 1) -eq $true) { 
     $isOnline = "ONLINE" 
    } 

    # change the password 
    try { 
     $localAdminAccount = [adsi]"WinNT://$client/$adminuser,user" 
     $localAdminAccount.SetPassword($adminPassword) 
     $localAdminAccount.SetInfo() 
     Write-Verbose "Password change completed successfully" 
    } 
    catch { 
     $status = "FAILED" 
     Write-Verbose "Failed to change password" 
    } 

    # create psobject with system info 
    $obj = New-Object -TypeName PSObject -Property @{ 
     ComputerName = $client 
     Online = $isOnline 
     ChangeStatus = $status 
    } 

    $obj | Select computerName, Online, changeStatus | Out-File -FilePath C:\test.txt -Append 

    if($status -eq "FAILED" -or $isOnline -eq "OFFLINE") { 
     $stream.writeline("$client -t $status") 
    } 
} 

$adminPassword = " " 

Write-Verbose "Complete" 

Invoke-Item C:\test.txt 

为了使脚本运行得更快,我将其设置为使用后台作业,以便它可以同时在多个客户端上运行。但是,现在我的文本文件中没有输出。新脚本如下。 43行和79-89是变化。我需要对该脚本进行哪些更改才能提供它在第一个版本中的输出?我已经尝试了一些其他的方式来获得比我目前有上线89

# function to convert a secure string to a standard string 
function ConvertTo-String { 
    param(
     [System.Security.SecureString] $secureString 
    ) 

    $marshal = [System.Runtime.InteropServices.Marshal] 

    try { 
     $intPtr = $marshal::SecureStringToBSTR($secureString) 
     $string = $marshal::PtrToStringAuto($intPtr) 
    } 
    finally { 
     if($intPtr) { 
      $marshal::ZeroFreeBSTR($intPtr) 
     } 
    } 
    $string 
} 

$clients = Get-Content -Path C:\scripts\utilities\hostnames_online.txt 
$adminUser = "Administrator" 

# prompt for password and confirm 
do { 
    $ss1 = Read-Host "Enter new password" -AsSecureString 
    $ss2 = Read-Host "Enter again to confirm" -AsSecureString 

# compare strings - proceed if same - prompt again if different 
    $ok = (ConvertTo-String $ss1) -ceq (ConvertTo-String $ss2) 
    Write-Host "Passwords match" 

    if(-not $ok) { 
     Write-Host "Passwords do not match" 
    } 
} 
until($ok) 

# set password variable to string value 
$adminPassword = ConvertTo-String $ss1 

# setup job to reset password on each client 
#----------------------------------------------------------------------------------------- 
$scriptBlock = { 
#----------------------------------------------------------------------------------------- 
    foreach($client in $clients) { 
     $status = "OFFLINE" 
     $isOnline = "OFFLINE" 

     if((Test-Connection -ComputerName $client -Quiet -Count 1 -Delay 1) -eq $true) { 
      $isOnline = "ONLINE" 
     } 

     # change the password 
     try { 
      $localAdminAccount = [adsi]"WinNT://$client/$adminuser,user" 
      $localAdminAccount.SetPassword($adminPassword) 
      $localAdminAccount.SetInfo() 
      Write-Verbose "Password change completed successfully" 
     } 
     catch { 
      $status = "FAILED" 
      Write-Verbose "Failed to change password" 
     } 

     # create psobject with system info 
     $obj = New-Object -TypeName PSObject -Property @{ 
      ComputerName = $client 
      Online = $isOnline 
      ChangeStatus = $status 
     } 

     $obj | Select computerName, Online, changeStatus | Out-File -FilePath C:\test.txt -Append 

     if($status -eq "FAILED" -or $isOnline -eq "OFFLINE") { 
      $stream.writeline("$client -t $status") 
     } 
    } 
} 
#----------------------------------------------------------------------------------------- 
Get-Job | Remove-Job -Force 

Start-Job $scriptBlock -ArgumentList $_ -Name AdminPWReset 

Get-Job 

While(Get-Job -State "Running") { 
    Start-Sleep -m 10 
} 

Receive-Job -name AdminPWReset | Out-File C:\test2.txt 
#----------------------------------------------------------------------------------------- 
$adminPassword = " " 

Write-Verbose "Complete" 

Invoke-Item C:\test.txt 
Invoke-Item C:\test2.txt 
+0

没有行号,你可以添加注释去代码显示你正在谈论哪些行? – Eris

+0

定义了$ stream吗?如果没有定义,则不能写入。 – Eris

+0

@Eris我在受影响的区域添加了这样的行------------------。另外,你对这个流是正确的。我会解决这个问题,但是我真正关心的第一个脚本的信息是 - $ obj |选择computerName,Online,changeStatus | Out-File -FilePath C:\ test.txt -Append – cmorris14

回答

1

您没有收到输出的输出,因为你动了你的整个循环到脚本块:

$scriptBlock = { 
    foreach ($client in $clients) { 
     ... 
    } 
} 

但初始化客户端列表($clients的脚本块,使可变$clients的脚本块是不同的(空)变量,因为它是在不同的范围。因为你的工作是遍历空列表,因此不会产生任何输出。

为了能够使用脚本块内的客户名单,你不得不使用using:范围修改:

$scriptBlock = { 
    foreach ($client in $using:clients) { 
     ... 
    } 
} 

或客户端列表传递到脚本块作为参数:

$scriptBlock = { 
    foreach ($client in $args[0]) { 
     ... 
    } 
} 

Start-Job -ScriptBlock $scriptBlock -ArgumentList $clients 

正如我可以从你的代码中看到你正在试图做的后者:

Start-Job $scriptBlock -ArgumentList $_ -Name AdminPWReset 

然而,这并不原因有二:

  • 在运行Start-Job没有当前对象的上下文,所以$_是空的,没有什么是传递到脚本块。
  • 该脚本块既没有Param()块,也没有使用自动变量$args,所以即使您将参数传递到scriptblock中也不会被使用。

虽这么说,你不希望传递$clients摆在首位,因为即使它的工作,它不会加快东西作为整个客户名单仍然会处理顺序。你真正想要做的是平行处理列表。对于你必须把刚刚测试到脚本块,然后开始一个作业为每一个客户在一个循环:

$scriptBlock = { 
    Param($client) 

    $status = "OFFLINE" 
    $isOnline = "OFFLINE" 

    if (Test-Connection -Computer $client -Quiet -Count 1 -Delay 1) { 
     $isOnline = "ONLINE" 
    } 
    ... 
} 

foreach ($client in $clients) { 
    Start-Job -Name AdminPWReset -ScriptBlock $scriptBlock -ArgumentList $client 
} 

while (Get-Job -State "Running") { 
    Start-Sleep -Milliseconds 100 
} 

Receive-Job -Name AdminPWReset | Out-File C:\test2.txt 
Remove-Job -Name AdminPWReset 

需要注意的是,如果你有目标主机的大量你可能需要使用一个job queue,所以您没有数百个作业并行运行。

+0

我试过了,现在工作正常,但重置密码的实际操作失败了,我似乎无法找出原因。 – cmorris14

+1

@ cmorris14可能是因为您在脚本块内部使用其他变量,而这些变量是在脚本块外部定义的(例如'$ adminuser')。还有其他一些关于你应该优化的scriptblock(比如连接测试应该在外面,以防止试图在首先不可用的计算机上启动作业。 –

+0

谢谢,让它工作。 – cmorris14