Count the number of objects in folders on remote servers

Hello everyone!
I want to count the number of objects in folders on remote servers, and if the number of objects is 0, then the condition “everything is ok, continue”
and if at least one folder is not empty then stop the script.
paths and folder names on servers are the same
My script is below, tell me correctly, did I write it?

$ServerList = @('Server1', 'Server2')
$SessionList = $ServerList | Foreach-Object -Process  { New-PSSession -ComputerName $_ }

$CountScriptBlock = {
$a = Get-ChildItem -Path "c:\temp" -Recurse
$b = Get-ChildItem -Path "c:\temp1" -Recurse
$c = $a.Count + $b.Count
$c
}

$d = $SessionList | Foreach-Object -Process {
   Invoke-Command -Session $_ -ScriptBlock $CountScriptBlock
} 
    if($d -eq 0)
    {Write-Host "everything is OK continue "}
    else{
    Write-Host "the folders are not empty, stop the script" -ForegroundColor Red
    };
$d 

hmmm … what’s your actual question? Does the script do what you want? What exactly do you mean with “Stop the script”?

May I suggest an a bit little cleaner approach?

$ServerList = @('Server1', 'Server2')
$SessionList = New-PSSession -ComputerName $ServerList

Invoke-Command -Session $SessionList -ScriptBlock {
    [PSCustomObject]@{
        ComputerName     = $env:COMPUTERNAME
        ObjectCountTemp  = (Get-ChildItem -Path 'c:\temp' -Recurse).count
        ObjectCountTemp1 = (Get-ChildItem -Path 'c:\temp1' -Recurse).count
    }
}

yes, the script does what I want, but I don’t like the way I wrote it, so I asked for cleaner code.
this is a snippet of the main script, this script block has a condition that should check that the folders are empty and then continue with the next script in which the services are started sequentially and if the folders are not empty the script should stop because services can only be started in if the folders are cleaned up.

your code is showing good results, but I am not quite sure how to continue the condition from it in order to continue the script. I lack knowledge.
If you tell me, I will be very grateful.
I need the sum of all folders on all servers and all folders must be 0 in order to proceed with the script.
from your code i came up with this to add a variable to the if condition

$ServerList = @('server1', 'server2')
$SessionList = $ServerList | Foreach-Object -Process  { New-PSSession -ComputerName $_ }

$CountScriptBlock = {(Get-ChildItem -Path 'c:\temp', 'c:\temp1' -Recurse).count
}

$d = $SessionList | Foreach-Object -Process {
   Invoke-Command -Session $_ -ScriptBlock $CountScriptBlock
} 
    if($d -eq 0)
    {Write-Host "everything is OK, continue"}
    else{
    Write-Host "the folders are not empty, stop" -ForegroundColor DarkGreen
    };
$d 

If you want to have the folders empty why don’t you make them empty? Instead of checking the amount of files or folders in them you could simply clear them and continue with your script.

@Olaf Here’s what happens: first, the processes are stopped, then the folders are cleaned, then the folders are checked for emptiness, and only if they are really empty, the services are started. The fact is that folders are not always cleaned up, if the process has not ended, this does not allow deleting files in folders and, as a result, files are not deleted.

I’d wait until the processes are stopped before I proceed with deleting the files. :wink:

@Olaf using this? Or do you need to make some kind of loop?

wait-process -name notepad
$nid = (Get-Process notepad).id
Stop-Process -Id $nid -Verbose
Wait-Process -Id $nid

Well … as usual … it depends. :wink: But since you took the example from the official documentation it should work.

Only if you have some really stubborn processes eager to keep running even when they’ve received the stop signal you could use a loop to make sure your script will not run indefinitely.

@Olaf Wrote a function like this to terminate the process with child processes and wait for it to complete. But now the problem is with the withdrawal. If the process is no longer there, then an error is displayed that the process could not be found. And I can’t get rid of this output in any way, -ErrorAction SilentlyContinue “out-null” doesn’t help.

function Kill-Tree {
    Param($ppid)
    Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $ppid } | ForEach-Object { Kill-Tree $_.ProcessId}
    Stop-Process -Id $ppid -Force -Verbose
    Wait-Process -Id $ppid}


$nid = (Get-Process @('notepad', 'pdf24') -ErrorAction SilentlyContinue).id


Kill-Tree $nid

Stop-Process: Cannot bind argument to “Id” parameter because it is NULL.
line: 4 character: 22

  • Stop-Process -Id $ ppid -Force -Verbose
  •   + CategoryInfo: InvalidData: (:) [Stop-Process], ParameterBindingValidationException
      + FullyQualifiedErrorId: ParameterArgumentValidationErrorNullNotAllowed, Microsoft.PowerShell.Commands.StopProcessCommand
    
    

Wait-Process: Unable to validate argument for parameter “Id”. The argument is empty or null. Specify a non-null non-null argument after
then repeat the execution of the command.
line: 5 character: 22

  • Wait-Process -Id $ ppid}
  •   + CategoryInfo: InvalidData: (:) [Wait-Process], ParameterBindingValidationException
      + FullyQualifiedErrorId: ParameterArgumentValidationError, Microsoft.PowerShell.Commands.WaitProcessCommand

I’d like to recommend for you to start to learn how to debug your own code. :wink:

You start with creating an array of process ids with the following command:

$nid = (Get-Process @('notepad', 'pdf24') -ErrorAction SilentlyContinue).id

Now you provide this array of process ids to your function where you use it a kind of directly for your Where-Object filter … you provide the variable $nid and use it in your function internally as the variable $ppid.

Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $ppid }

So actually you can use this to test it:

$nid = (Get-Process @('notepad', 'pdf24') -ErrorAction SilentlyContinue).id
Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $nid }

Assumed you started both processes you should get at least two objects in return, right? Try it! :wink:
If you don’t get two objects try to figure out why. :smirk:

@Olaf Thanks for your help, in the end I gave birth to such a script to check and complete the process, it seems to work as it should.

$Error.Clear()
$nid = (Get-Process @('notepad', 'pdf24') -ErrorAction SilentlyContinue).id


function Kill-Tree {
    Param($nid)
    Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $nid } | ForEach-Object { Kill-Tree $_.ProcessId}
    Stop-Process -Id $nid -Verbose -Force
    Wait-Process -Id $nid}

if($nid -eq $null)
    {Write-Host 'No running processes'}
    else{
    Kill-Tree $nid
    }

May I doubt that? :wink: Did you try the test I suggested in my last post? What did you get?

@Olaf I have the id of any of the specified processes in $ nid. Even if there is only one worker process, its id is passed into a variable, then a verbose tells me that it is ending this process and then waits for that process to finish.
If there are no running processes, I get a null value, which is processed by the condition, and if the condition tells me that it has a null value, then I can be sure that there are no running processes and move on to another script block to clean up folders.
something is wrong?
The only thing is, when I tested on a test server, verbose says it stop the process and wait-process throws an error that it cannot find the process.
otherwise everything seems to be going as it should.

VERBOSE: Performing the operation “Stop-Process” on target “notepad (15812)”.
VERBOSE: Performing the operation “Stop-Process” on target “notepad (36936)”.
Cannot find a process with the process identifier 15812.
+ CategoryInfo : ObjectNotFound: (15812:Int32) [Wait-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenId,Microsoft.PowerShell.Commands.WaitProcessCommand
+ PSComputerName : server1

Cannot find a process with the process identifier 36936.
+ CategoryInfo : ObjectNotFound: (36936:Int32) [Wait-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenId,Microsoft.PowerShell.Commands.WaitProcessCommand
+ PSComputerName : server1

What do you get in return when you run this code having both processes started?

$nid = (Get-Process @('notepad', 'pdf24') -ErrorAction SilentlyContinue).id
Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $nid }

if you just run this piece, nothing will happen. Maybe I didn’t understand you

both processes now work, if i run your code then nothing happens, no output

That’s my point. So the command in your function

Get-CimInstance Win32_Process | Where-Object { $_.ParentProcessId -eq $nid } | ForEach-Object { Kill-Tree $_.ProcessId}

cannot work if you provided more than one process. :wink:

yes you are right checked the function separately and it does not work. rejoiced early.

@Olaf

$nid = (Get-Process 'notepad', 'pdf24').id

if($nid -eq $null)
    {Write-Host 'No running processes'}
    else{
    Stop-Process -Id $nid -Verbose -force
    Wait-Process -Id $nid
    }

Probably the easiest option is to do it like this