Thanks Satyajit,
1) I did initially have a version of the script that worked sequentially and ran each function based on a few foreach statements, but the script has blown out since then... so I know my functions work.. I am just struggling with getting the array information
INTO and OUT of each invokeParallel script block.
2) Unfortunately we must use invoke-parallel because the entire process takes SO unbelievably long to complete, even for a single machine. Running it against 100 machines or so could easily take an entire day to complete.
3) You lost me completely :(
Jrv, normally I would in a heartbeat do as you say, but the issue is not so much with SCCM, as it is with my inability to code.. but I suppose you are right and they may have some solution to this problem already coded somewhere, but i doubt it will be muiltithreaded
which is what we really need..
Here is the code I am having troubles with.. have tried to include as much of it as i can.. things like $WorkingDirectory are not shown though.. that is the workingdirectory of the script when launched.
#Get our list of machines if it exists..
if (test-path -path "$inputfile" -erroraction SilentlyContinue) {
$MasterList = gc $inputfile
# If the list we just got IS NOT empty...
if ($MasterList -ne $null) {
Write-Host -foregroundcolor Green "Found: $Inputfile"
$MasterList
Start-sleep -seconds 2
foreach ($item in $Masterlist) {
$test = test-connection -computername $item -quiet -count 1
if ($test) {
"$item,Ping Succeeded,status,$(Get-Date -format "dd_MM_yyyy_HH:mm:ss:$((Get-Date).millisecond)")" | Out-File -literalpath $Resultfile -append
Write-Host -foregroundcolor green "$Item Successfully Pinged"
$PingingComputers += $Item
}
else {
"$item,Ping Failed,error,$(Get-Date -format "dd_MM_yyyy_HH:mm:ss:$((Get-Date).millisecond)")" | Out-File -literalpath $Resultfile -append
Write-Host -foregroundcolor red "$Item Failed Ping"
}
}
## # ## # ## # ## # ## # ## #
# THE BROKEN SECTION OF CODE # # THE BROKEN SECTION OF CODE # # THE BROKEN SECTION OF CODE # # THE BROKEN SECTION OF CODE # # THE BROKEN SECTION OF CODE # # THE BROKEN SECTION OF CODE #
## # ## # ## # ## # ## # ## #
# create empty array
$Sublist2 = @()
# if any machines pinged at all
if ($PingingComputers -ne $null) {
# Create objects for every item in $Sublist and populate empty array $Sublist2 with those objects with an empty property to be filled by the upcoming OS check "OS"
$PingingComputers |%{$Sublist2 += new-object PSObject -Property @{
Name = $_
OS = ""
}
}
# Start Multithread scriptblock for OS check
$Sublist2 | Invoke-Parallel -throttle $OSCheckThrottle -RunspaceTimeout $OScheckRunspaceTimeout -NoCloseOnTimeout -importvariables -importmodules -scriptblock {
# Start Multithread scriptblock for OS check
$OperatingSys = Get-WmiObject -ComputerName "$($_.Name)" -Class Win32_OperatingSystem -ev $err
$OperatingSys
#if we didn't throw trying to do the check then...
if ($err -eq $Null) {
if ($OperatingSys.Caption -notlike "*2003*" -and $OperatingSys.Caption -notlike "*XP*" -and $OperatingSys.Caption -notlike "*10*") {
$_.OS = $True
"$($_.Name),Found Supported OS $($Operatingsys.Caption),status,$(Get-Date -format "dd_MM_yyyy_HH:mm:ss:$((Get-Date).millisecond)")" | Out-File -literalpath $Resultfile -append
}
else {
$_.OS = $False
"$($_.Name),Found Unsupported OS $($Operatingsys.Caption),error,$(Get-Date -format "dd_MM_yyyy_HH:mm:ss:$((Get-Date).millisecond)")" | Out-File -literalpath $Resultfile -append
}
}
else {
"$($_.Name),Failed to perform OS Check: $err,error,$(Get-Date -format "dd_MM_yyyy_HH:mm:ss:$((Get-Date).millisecond)")" | Out-File -literalpath $Resultfile -append
}
# end multithread scriptblock for OS check
}
# end multithread scriptblock for OS check
$SuccessfulOSCheck = @()
$SuccessfulOSCheck = $Sublist2 | where {$_.OS -eq $True}
$SuccessfulOSCheck = $SuccessfulOSCheck.Name
if ($SuccessfulOSCheck -ne $null) {
Write-Host -Foregroundcolor green "Successful OS Check Computers:"
$SuccessfulOSCheck
# Count em' to Host:
Write-Host -foregroundcolor cyan "Count of Supported OS"
$SuccessfulOSCheck.count
Start-sleep -seconds 2
}
$Sublist3 = @()
if ($SuccessfulOSCheck -ne $null) {
$SuccessfulOSCheck |%{$Sublist3 += new-object PSObject -Property @{
Name = $_
State = ""
}
}
$Sublist3 | Invoke-Parallel -throttle $WMICheckThrottle -RunspaceTimeout $WMICheckRunspaceTimeout -NoCloseOnTimeout -importvariables -importmodules -scriptblock {
Set-Alias psexec "$WorkingDirectory\psexec\psexec.exe"
$computername = "\\$($_.Name)"
$Scriptblock = "winmgmt /verifyrepository"
## Prepare the command line for PsExec. We use the XML output encoding so that PowerShell can convert the output back into structured objects.
## PowerShell expects that you pass it some input when being run by PsExec this way, so the 'echo .' statement satisfies that appetite.
$commandLine = "echo . | powershell -Output XML "
## Convert the command into an encoded command for PowerShell
$commandBytes = [System.Text.Encoding]::Unicode.GetBytes($scriptblock)
$encodedCommand = [Convert]::ToBase64String($commandBytes)
$commandLine += "-EncodedCommand $encodedCommand"
## Collect the output and error output
$errorOutput = [IO.Path]::GetTempFileName()
$output = psexec /acceptEula $computername cmd /c $commandLine 2>$errorOutput
## Check for any errors
$errorContent = Get-Content $errorOutput
Remove-Item $errorOutput
if($errorContent -match "(Access is denied)|(failure)|(Couldn't)") {
$OFS = "`n"
$errorMessage = "Could not execute remote expression. "
$errorMessage += "Ensure that your account has administrative " +
"privileges on the target machine.`n"
$errorMessage += ($errorContent -match "psexec.exe :")
Write-Error $errorMessage
}
## Return the output for use in if statement
if ($output -eq "WMI repository is consistent") {
$_.State = $true
}
else {
$_.State = $false
}
# END Sublist3 scriptblock for WMI check
}
# END Sublist3 scriptblock for WMI check
$SuccessfulWMICheck = @()
$SuccessfulWMICheck += $Sublist3 | where {$_.State -eq $True}
$SuccessfulWMICheck = $SuccessfulWMICheck.Name
if ($SuccessfulWMICheck -ne $null) {
Write-Host -Foregroundcolor green "Successful WMI Check Computers:"
$SuccessfulWMICheck
# Count em' to Host:
Write-Host -foregroundcolor cyan "Count of Successful WMI check:"
$SuccessfulWMICheck.count
Start-sleep -seconds 2
}
## # ## ## # ## ## ## ##
# END OF THE BROKEN SECTION OF CODE # # # END OF THE BROKEN SECTION OF CODE # # # END OF THE BROKEN SECTION OF CODE # END OF THE BROKEN SECTION OF CODE #
## # ## ## # ## ## ## ##
# create empty array
$Sublist4 = @()
if ($SuccessfulWMICheck -ne $null) {
$SuccessfulWMICheck |%{$Sublist4 += new-object PSObject -Property @{
Name = $_
Installed = ""
}
}
# begin multithread Remediation scriptblock
$Sublist4 | Invoke-Parallel -throttle $RemediationThrottle -RunspaceTimeout $RemediationRunspaceTimeout -NoCloseOnTimeout -importvariables -importmodules -scriptblock {
# begin multithread Remediation scriptblock
# Write to log we are starting the process of remediation for this machine.
"$($_.Name),Starting Process,status,$(Get-Date -format "dd_MM_yyyy_HH:mm:ss:$((Get-Date).millisecond)")" | Out-file -literalpath $Resultfile -append
# Scriptblock is treated as new session of powershell needs to set alias in here instead of outside.
Set-Alias psexec "$WorkingDirectory\psexec\psexec.exe"
# in order to avoid the whole CredSSP bullshit, and since we don't have winRM enabled, we gotta send client installation files to machine's C:\temp
Copy-SCCMClientInstallation
# stop related services
Stop-OurServices
#Remove-qmgrdatfiles
#Remove-WUSoftwareDistributionFolder
Rename-WUSoftwareDistributionFolder
#Remove-CatRoot2Folder
#Reset-securitydescriptorbits
#Reset-securitydescriptorwuauserv
#Send-RegisterDLLs
Start-OurServices
#reset-Bitsadmin
# The regular uninstall falls over sometimes clearing cache folder, we do it manually for SCCM such that when IT attempts to do it, there are no problems.
Remove-CCMCacheFolder
Uninstall-SCCMClient
# This is a series of cleanup tasks that should run after the regular uninstall methods to make SURE sccm client is gone before we reinstall the client.
Remove-CCMSetupFolder
Remove-CCMFolder
Remove-CCMNamespace
Remove-SMSNamespace
# reinstall the client via psexec
Install-SCCMClient
# attempt to force windows update to update.. so far this doesn't ever appear to have done jack shit but it's part of microsoft's article to repair WUAgent..
Send-WUAgentDetectnow
# If theres a log file/s open for that machine at the end of this process, then wait $seconds and close it/them.
$Trace32Presence = Get-process trace32 | where {$_.MainWindowTitle -like "*$($_.Name)*"}
if ($Trace32Presence) {
$seconds = 3
Start-sleep -seconds $seconds
$Trace32Presence | Stop-Process
}
#-----------------------------------------------------#
#-----------------------------------------------------#
#-----------------------------------------------------#
# Start "installation successful?" check
#-----------------------------------------------------#
#-----------------------------------------------------#
#-----------------------------------------------------#
#"$($_.Name),Checking Installation Status,finalstatuscheck,$(Get-Date -format "dd_MM_yyyy_HH:mm:ss:$((Get-Date).millisecond)")" | Out-file -literalpath $Resultfile -append
#Start-Sleep -seconds 300
#Get-SCCMInstallationStatus
#-----------------------------------------------------#
#-----------------------------------------------------#
#-----------------------------------------------------#
# End installation check
#-----------------------------------------------------#
#-----------------------------------------------------#
#-----------------------------------------------------#
# END Remediation PARALLEL SCRIPT BLOCK HERE
}
# END Remediation PARALLEL SCRIPT BLOCK HERE
}
else {
Write-Host -foregroundcolor red "No Machines passed their WMI Check, cannot proceed"
}
}
else {
Write-Host -foregroundcolor red "No Machines passed their OS Check, cannot proceed"
}
}
else {
Write-Host -foregroundcolor red "No Machines pinged, cannot proceed"
}
}
else {
Write-Host -foregroundcolor red "servers.txt file: $inputfile is EMPTY $(Get-Date -format "dd_MM_yyyy_HH:mm:ss:$((Get-Date).millisecond)")"
}
# open the log path after 2 seconds...
explorer.exe $logfolder
}
else {
Write-Host -foregroundcolor red "No servers.txt file found in: $WorkingDirectory\Input\ $(Get-Date -format "dd_MM_yyyy_HH:mm:ss:$((Get-Date).millisecond)")"
}