There was supposed to be a lot more data in the result, but I figured out that the functions I was using had to be defined within the job, since it’s a separate Powershell process. But I’m still having output problems, I believe it’s related to the way I’m handling the background jobs–which is a skill I’m trying to hone.
First I build an object based on computers found in AD. Since I support a large number of different offices, I can’t guarantee the server I’m using will have RSAT installed for the AD module, so I use ADSI.
# Build the AD object with all computer objects found
$adsi = $null
$adsi = [adsisearcher]"objectcategory=computer"
# To return only the enabled computer objects, use '!userAccountControl:1.2.840.113556.1.4.803:=2'
$adsi.filter = "(&(objectClass=Computer)(!userAccountControl:1.2.840.113556.1.4.803:=2))"
$EnabledADComputerADSI = $adsi.FindAll()
$EnabledADComputer = Foreach ($E in $EnabledADComputerADSI){
$obj = $E.Properties
$props = @{
Computer = [string]$obj.name
OSName = [string]$obj.operatingsystem -replace
'Windows','Win' -replace
'Professional','Pro' -replace
'Standard','Std' -replace
'Ultimate','Ult' -replace
'Enterprise','Ent' -replace
'Business','Biz' -replace
'with', 'w/' -replace
'Media Center','MedCtr'
Description = [string]($obj.description)
AD_OU = [string]($obj.distinguishedname) -replace
'^CN=[\w\d-_]+,\w\w=','' -replace
',OU=','/' -replace ',DC=.*'
LastLogon = [datetime]::FromFileTime([string]$obj.lastlogon)
ADCreated = [datetime]($obj.whencreated)[0]
}
New-Object -TypeName PSObject -Property $props
}
Next, I use a ping function to filter out any offline hosts. The result is a nice table, no issues here.
Then I take that object and run it through a foreach loop to kick off a job per device. This might be overly complicated, but it’s the only way I’ve figured out how to grab data from remote PCs in parallel. Since I support a large number of different offices, I can’t guarantee PSRemoting will be turned on (in most cases it’s not).
# Set the initial conditions for my makeshift throttle limit.
$MyCurrentJobCount = 0
$MyThrottleLimit = 16 # Number of concurrent jobs/threads
$MajorThottleInterval = 750 # Space between job checks when queue is full (in milliseconds)
$MinorThottleInterval = 10 # Space between new job starts to fill the queue (in milliseconds)
$MyJobNamePattern = 'Job-nmAudit-' # prevents other parallel scripts from slowing down this script.
$Tasks = New-Object System.Collections.ArrayList # Main Object for Output of live info
foreach ($C in $OnlineADComputer)
{
# This is my makeshift throttle limit
While ($MyCurrentJobCount -ge $myThrottleLimit){
# Pause longer for a full queue
if ($MyCurrentJobCount -eq $myThrottleLimit){
sleep -Milliseconds $MajorThottleInterval
}else{
sleep -Milliseconds $MinorThottleInterval
}
$MyCurrentJobCount = (Get-Job -Name "$MyJobNamePattern*" |
where {$_.State -eq 'Running'}).count
}
$Item = New-Object -TypeName psobject -Property @{
Name = "$($MyJobNamePattern)$($C.Computer)"
Return = $null
Job = Start-Job -Name "$($MyJobNamePattern)$($C.Computer)" -ScriptBlock {
$C = $using:C
# Define functions
# ...
# Use functions to get data from remote PCs, store to variables
# Create psobject as output for all data grabbed
# ...
### sample of data grabbed:
# Check out the value for last logon.
$prevLogon = if(([datetime]$C.LastLogon) -lt [datetime]'1/1/1990')
{'Unknown'}else{$C.LastLogon}
# Build PSObject Property hashtable
$obj = New-Object -TypeName PSObject -Property @{
Device = $C.Computer
OSName = $C.OSName
AD_OU = $C.AD_OU
LastLoggedOnUser = $LastUser
BiggestUserFolder = $BiggestUserFolder
SystemDriveLetter = $SysDrvInfo.SystemDrive
WUSvcStatus = $WUServiceStatus
WUAgentVersion = $WUAgentInfo.Version
WUAgentDate = $WUAgentInfo.Date.ToShortDateString()
SMB_Admin = $SMBAccess
RemoteServiceMgt = $RmtSvcMgt
RemoteEvtLogMgt = $RmtEvtLogMgt
RemoteWMIAccess = $WMIAccess
SystemDriveSpaceInGB = $SysDrvSpaceInGB
}
return $obj
} # Job = ... scriptblock {...
} # $Item = @{...
$Tasks.Add($Item) | Out-Null
} # Foreach ($C in $OnlineADComputer){...
Get-Job -Name ($Tasks.Name) | Wait-Job -Timeout 300 | Out-Null
Foreach ($T in $Tasks)
{
$T.Return = Get-Job ($T.Name) | Receive-Job
Remove-Job ($T.Name)
}
$OnlineTable = $Tasks |select -exp Return
When I check the array after the foreach loop, I see the jobs, I can wait until they finish, but the output is not there when I receive it. Nothing. Even if the scripts returned nothing or errors, I should still get a table with nulls, the device name, and a handful of other properties from the first AD table like OSName and LastLoggedOnUser.
I’m about to scrap it and start from scratch because this thing is so big I’m losing my place every five mins. So even if you can’t figure out what my problem is, based on the snippets I’ve shared maybe you can point me to a template I can use to do this more efficiently.