Search all AD Computers for specific Process and report

This topic contains 9 replies, has 2 voices, and was last updated by Profile photo of Larry Clevenger Larry Clevenger 3 years, 4 months ago.

  • Author
    Posts
  • #11969
    Profile photo of Larry Clevenger
    Larry Clevenger
    Participant

    I need to create a script that will poll Active Directory for all systems then check each system for a specific process and then write a report on what was found or not found including the name of the machine? Ive tried different ways of accomplishing this but cannot seem to get my process right. Can anyone give me a good place to start as in would I run a single report then run another powershell against that one or would I keep it all withing the same script? Thanks in advance.

  • #11970
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    It would take a pretty long time to run, but here's the basic idea:

    $processToFind = 'notepad.exe'
    
    Get-ADComputer -Filter * |
    Select-Object -Property @(
        @{ Name = 'ComputerName'; Expression = { $_.Name } }
        @{ Name = 'ProcessFound'; Expression = { $null -ne (Get-WmiObject Win32_Process -ComputerName $_.Name -Filter "Name = '$processToFind'") } }
    )
    

    The speed of this can probably be improved in one of several ways (splitting it into multiple jobs, passing many computer names at once to Get-WmiObject, etc), but they all make the code more complex, and I wanted to keep the example simple for now.

  • #11971
    Profile photo of Larry Clevenger
    Larry Clevenger
    Participant

    Thanks for the start, could I invoke that lookup per computer? Just thinking of the speed side now. I obviously dont know what I am doing yet as I was trying the following which is obviously wrong.

    $strCategory = "computer"

    $objDomain = New-Object System.DirectoryServices.DirectoryEntry

    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    $objSearcher.SearchRoot = $objDomain
    $objSearcher.Filter = ("(objectCategory=$strCategory)")

    $colProplist = "name"
    foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}

    $colResults = $objSearcher.FindAll()

    foreach ($objResult in $colResults)
    {$objComputer = $objResult.Properties; $objComputer.name}

    foreach ($i in $colResults)
    {
    $objComputer = $i.GetDirectoryEntry()
    Get-Process smc -ErrorAction write-error
    }

  • #11972
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    Well, your code is a bit more verbose (by virtue of being written to use .NET classes directly instead of a Cmdlet), but it's not wrong. Right up to the Get-Process line, your script does almost the same thing my sample code did. Here's a small tweak to your code to make it output objects similer to what I posted. I stuck with using Get-Process instead of WMI, to match what your code was originally doing.

    $strCategory = “computer”
     
    $objDomain = New-Object System.DirectoryServices.DirectoryEntry
     
    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    $objSearcher.SearchRoot = $objDomain
    $objSearcher.Filter = (“(objectCategory=$strCategory)”)
     
    $colProplist = “name”
    foreach ($i in $colPropList){ $null = $objSearcher.PropertiesToLoad.Add($i) }
     
    $colResults = $objSearcher.FindAll()
    
    # I'm not sure what you were trying to accomplish with this first loop, so I've commented it out.
    < #
    foreach ($objResult in $colResults)
    {$objComputer = $objResult.Properties; $objComputer.name}
     #>
    
    
    foreach ($result in $colResults)
    {
        $name = $result.Properties['Name']
        $process = Get-Process -Name smc -ComputerName $name -ErrorAction SilentlyContinue
    
        New-Object psobject -Property @{
            ComputerName = $name
            ProcessFound = $null -ne $process
        }
    }
    
    
  • #11975
    Profile photo of Larry Clevenger
    Larry Clevenger
    Participant

    Thanks so much Dave, sorry I am so green currently at dealing in powershell it gives me fits.

    Would there be a way to pass on the process found false and true to do something if true such as exclude and put in a SMCInstalled.csv and then take the false listings and do something else such as a file copy then kick off the installation after the copy completes? This is just a question not nessaraly what code would that be, just trying to get the feel how to handle the output of the script.

    Thanks again!

  • #11976
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    Sure. If you can think of it, PowerShell can probably do it.

    Here's an example of how that might work. I'm going back to using Get-ADComputer in the example, simply because it's shorter.

    Get-ADComputer -Filter * |
    ForEach-Object {
        $computer = $_
    
        $process = Get-Process -Name 'smc' -ComputerName $computer.Name
        
        # Note:  Here, you probably want to add some error handling to distinguish between a computer where the command was successful
        # and the process isn't running, versus an error running Get-Process (computer offline, etc.)
    
        if ($null -ne $process)
        {
            $computer | Select-Object -Property Name | Export-Csv -Path .\SMCInstalled.csv -Append
        }
        else
        {
            # Do your file copy, kick off the installer, etc.
        }
    }
    
  • #11977
    Profile photo of Larry Clevenger
    Larry Clevenger
    Participant

    Could I add that if/else statements to the end of the previous longer code after the foreach? I have major issues when I try do such a step and previously have been calling on script to kick of multipule which isnt practical as my scripts are starting to grow and needing to be more complex.

  • #11978
    Profile photo of Larry Clevenger
    Larry Clevenger
    Participant

    Maybe something like this then?

    $strCategory = “computer”

    $objDomain = New-Object System.DirectoryServices.DirectoryEntry

    $objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    $objSearcher.SearchRoot = $objDomain
    $objSearcher.Filter = (“(objectCategory=$strCategory)”)

    $colProplist = “name”
    foreach ($i in $colPropList){ $null = $objSearcher.PropertiesToLoad.Add($i) }

    $colResults = $objSearcher.FindAll()

    foreach ($result in $colResults)
    {
    $name = $result.Properties['Name']
    $process = Get-Process -Name smc -ComputerName $name -ErrorAction SilentlyContinue

    New-Object psobject -Property @{
    ComputerName = $name
    ProcessFound = $null -ne $process
    }
    }

    if ($null -ne $process)
    {
    $computer | Select-Object -Property Name | Export-Csv -Path C:\symantecinstallation\reports\SMCInstalled.csv -Append
    }
    else
    {
    # Do your file copy, kick off the installer, etc.

    $os = (Get-WmiObject -computername $Computer -class Win32_OperatingSystem ).osarchitecture
    If ($os -eq “64-bit”) {
    Write-host "running 64 $Computer"
    $arSourceFolders1 = ("\\serverlocation\sep_clients\msi\My Company_1 STL_Clients_WIN64BIT_Workstation\Symantec Endpoint Protection version 12.1.2015.2015 - English")
    $arDestinationFolders1 = ("\\$computer\C$\Windows\Temp\My Company_1 STL_Clients_WIN64BIT_Workstation\Symantec Endpoint Protection version 12.1.2015.2015 - English")
    $cmd1 = robocopy $arSourceFolders1 $arDestinationFolders1 /MIR /XA:SH /ipg:0 /w:0 /log:c:\symantecInstallation\robocopy\robocopy_"$server"log.txt
    start-process $cmd1
    Write-Host "Copy 64bit Symantec Install Complete $Computer"

    elseif ($os -eq “32-bit”) {
    Write-Host "running 32 $Computer"
    $arSourceFolders = ("\\Serverlocation\sep_clients\msi\My Company_1 STL_Clients_WIN32BIT_Workstation\Symantec Endpoint Protection version 12.1.2015.2015 - English")
    $arDestinationFolders = ("\\$Computer\C$\Windows\Temp\My Company_1 STL_Clients_WIN32BIT_Workstation\Symantec Endpoint Protection version 12.1.2015.2015 - English")
    $cmd2 = robocopy $arSourceFolders2 $arDestinationFolders2 /MIR /XA:SH /ipg:00 /w:0 /log:c:\symantecInstallation\robocopy\robocopy_"$server"log.txt
    start-process $cmd2
    Write-Host "Copy 32bit Symantec Install Complete $Computer"
    }
    }
    }

  • #11980
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    That check for $process being null would go inside the foreach loop, right after the "$process = Get-Process" line. You can get rid of the call to New-Object if you like; it doesn't look like you'll be using that object for anything, since all the logic will be right there inside the loop.

    As for managing complexity in your script code, there's kind of an art to that. You break the code into meaningful chunks and put those chunks into functions (with names that make it very clear what the function is doing.) That way the code that calls those functions can be taken in at a glance and understood perfectly, without having to know all of the little details about how they work. For example:

    Get-ADComputer -Filter 'operatingSystem -like "*Server*"' -Properties operatingSystem |
    Select-Object -Property Name,operatingSystem |
    Export-Csv -Path .\Servers.csv
    

    You can tell exactly what that small pipeline is doing without having to know what steps are involved with searching ActiveDirectory based on the Filter and Properties arguments, or how to convert an object into CSV format.

    There are lots of books on the topic of software design, refactoring, etc, but many of them are heavily based on object-oriented techniques that don't typically apply to a PowerShell script. Still, they're good reads if you're interested in getting better at this sort of thing. I'd recommend "Code Complete, Second Edition" by Steve McConnell as a good place to start.

  • #11988
    Profile photo of Larry Clevenger
    Larry Clevenger
    Participant

    ok thanks for the help i seem to have made a mess attempting to direct traffic of what goes where with if statements now. If anyone in interested in helping me out will you please let me know, I wont post the script till then as it is becoming rather large and it seems to double space when i post it here.

You must be logged in to reply to this topic.