background jobs and objects

This topic contains 2 replies, has 2 voices, and was last updated by Profile photo of Tony Pagliaro Tony Pagliaro 1 week ago.

  • Author
  • #55281
    Profile photo of Tony Pagliaro
    Tony Pagliaro

    I have a function that takes an object as a parameter which includes AD info like computer name and last login date, it goes out and finds a bunch of info from WMI, SMB, etc, builds and returns a new object, with a lot more data, including some of the input data.

    It works fine, but when I wrap it in a Starrt-Job scriptblock, all I get back is an array, like this.

    PS WINDOWS> $item.job| Receive-Job -Keep
    @{PSComputerName=localhost; RunspaceId=650db34a-e613-4085-8f26-dfc3504b62e8; PSShowComputerName=False}

    I'll post the monster script for inspection if I must, but first, is there anything I should look out for, any common mistakes?

  • #55327
    Profile photo of Don Jones
    Don Jones

    No, that's just how serialized objects look, particularly of the PSObject variety. What are you trying for?

  • #55368
    Profile photo of Tony Pagliaro
    Tony Pagliaro

    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]$
            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
                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')
                # 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.

You must be logged in to reply to this topic.