Author Posts

July 30, 2014 at 11:21 am

I was wondering if someone would be able to give me some tips on handling non terminating errors through the pipeline?

Here is an example of what i would like to do.

Get-ADUser -Filter {Enabled = 'False'} -Properties Modified | Where Modified -LT (Get-Date).AddDays(-30) | Remove-ADUser -Confirm:$false 

Basically find all users that are disabled where the last modified time stamp is less than 30 days ago and delete them.
So what i'm looking for is how to handle and error if one where to occur so that it could be logged

I thought of using try catch, but if an error occurs then the entire command stops. I want it to be a non terminating error, so that it will continue to process all the users that it find, but then have a way to see if there where any error so that i can log them

Any suggestions?

July 30, 2014 at 11:24 am

You really need to move from a one liner into a more structured script. The pipeline is great for quick, linear tasks. But when logic (IF and error, THEN this) starts to get involved, you've moved out of what the single-pipeline model is for.

July 30, 2014 at 11:31 am

Generally, to deal with non-terminating errors (where there's no expected terminating errors happening), I prefer to use the -ErrorVariable parameter. For example:

Get-ADUser -Filter {Enabled = 'False'} -Properties Modified |
Where Modified -LT (Get-Date).AddDays(-30) |
Remove-ADUser -Confirm:$false -ErrorVariable removeUserErrors

# Here, $removeUserErrors will be an empty collection if everything worked, or will contain
# one or more ErrorRecord objects if any of the calls to Remove-ADUser failed.

if ($removeUserErrors.Count -gt 0) {
    $removeUserErrors | Out-File some-File.txt

July 30, 2014 at 11:57 am

Is there any specific advantage (say speed maybe) to doing this in a foreach loop or through the pipeline using the error variable?

Is there a "best practice" or are both solution equally acceptable?

I'm in the process of writing an AD user management script that handles account creation, modification, disabling, and deletion, and have a good chuck of it completed, so i want to be sure that my error handling is rock solid.


July 30, 2014 at 12:02 pm

If you're going to be processing multiple objects, dealing with them individually in a ForEach loop lets you handle errors individually, and then loop back to process the next object. Internally, that's what most cmdlets do, and it's a common and legitimate model.

Batch processing is for when you don't care about individual failures (eg., non-terminating), usually, or when you perhaps only need to capture all errors but not necessarily relate them back to an object or deal with them independently.

July 30, 2014 at 12:05 pm

There's also a bit of a balancing act between memory utilization and execution time. Using the pipeline allows you to stream objects and not have to store the entire result set in memory at any one time, but tends to be slower than the foreach loop in many cases. AD throws another wrench in the gears, as pipelines that take too long to finish executing can wind up "timing out." The actual error message you'd see is something like "Invalid enumeration context", and it's something to keep in mind if your environment is large.

July 30, 2014 at 12:27 pm

Thanks for the tips guys. I think I'll be using the ForEach loop.

July 31, 2014 at 10:47 am

Hey guys,

I have a follow up question. In the script that i writing to delete users from AD, i have my ForEach loop that loops through each of the users that are disabled longer than a specific retention period, (say 30 days) to be consistent with the first example i posted.

In this loop I query i get the Home Directory variable and split it on the \ to determine the file server and name of the shared folder
Next using WMI I query the server to find out the physical path of the share.
I then delete the share and the user account.

So to delete the home directory folders and it's files from the server i was planing on using Invoke-Command....I work for a school board, and some home directories are on our main file server, but others are on our school server to avoid on unnecessary network traffic. I figured that while going through my foreach loop i could create a new psobject with the server name and physical folder location and store it to an array...this way once i'm out of that foreach loop I can create a new foreach loop that would loop through a unique list of servername and run invoke command only once on each server, thus reducing the number of remote sessions that i create and improve on the efficiency.

.....So with all that said, I'm wondering when deleting multiple directories from a remote server using invoke-command what is the best was to handle errors.

Here is the code i'm currently working on.

        Write-Verbose "Checking for users that need to be deleted`n"
        try {
            # Query Active Directory for a list of disabled used in the Disabled user OU where the last modified date is less than the retention period specified in the Configuration.xml file (ex 120 days)
            $UsersToDelete = Get-ADUser -Filter {Enabled -eq "False"} -Properties EmployeeID, Modified, HomeDirectory -SearchBase $DisableOU -SearchScope Subtree -Server $DC | Where Modified -LT (Get-Date).AddDays(-($Configuration.Configuration.DisabledAccountRetentionPeriod))
        } catch {

            $_.Exception.Message | Tee-Object -FilePath $ErrorLogPath -Append | Write-Error
            "Unable to successfully Query Active Directory to find list of users to delete." | Tee-Object -FilePath $ErrorLogPath -Append | Write-Error

        $FoldersToDelete = @()

        ForEach ($user in $UsersToDelete){

            Write-Verbose "`tQuerying Active Directory user $($user.Name), for home directory path." 

            # Get the Home directory variable from AD
            if(-not $user.HomeDirectory){
                "The following user does not have a Home Directory specified." | Tee-Object -FilePath $ErrorLogPath -Append | Write-Error

            # Split the Home directory variable into its two parts. Server name and share name.
            $ServerName = $user.HomeDirectory.Replace('\\','').Split('\')[0]
            if(-not $ServerName){
                "No home directory server name could be found based on the Active Directory Information." | Tee-Object -FilePath $ErrorLogPath -Append | Write-Error

            $ShareName = $user.HomeDirectory.Replace('\\','').Split('\')[1]
            if(-not $ShareName){
                "No share name could be found based on the Active Directory Information." | Tee-Object -FilePath $ErrorLogPath -Append | Write-Error

            Write-Verbose "`tQuerying Server: $ServerName for share named: $ShareName"
            $Share = Get-WmiObject -Class Win32_Share -ComputerName $ServerName -Filter "Name='$ShareName'"

            if(-not $Share){
                "No share named : $ShareName could be found on the specified server : $ServerName." | Tee-Object -FilePath $ErrorLogPath -Append | Write-Error

            $Path = $Share.Path

            $FoldersToDelete += New-Object PSObject -Property @{
                ServerName = $ServerName
                Path = $Path

            Write-Verbose "`tDeleting Home Drive Share for $($user.Name)"
                 $_.Exception.Message | Tee-Object -FilePath $ErrorLogPath -Append | Write-Error
                "Unable to delete share name : $ShareName from server $ServerName" | Tee-Object -FilePath $ErrorLogPath -Append | Write-Error

            Write-Verbose "`tDeleting Active Directory accounts identified for deletion"
                # Delete the User Account in Active Directory
                $user | Remove-ADUser -Confirm:$false -WhatIf
                 $_.Exception.Message | Tee-Object -FilePath $ErrorLogPath -Append | Write-Error
                "Unable to delete Active Directory User : $($user.Name) - EmployeeID: $()" | Tee-Object -FilePath $ErrorLogPath -Append | Write-Error

        # Loop through each file server where home directories need to be deleted
        ForEach ($server in ($FoldersToDelete | Select -ExpandProperty ServerName -Unique)){

            # Get a list of folders that need to be deleted for the current home directory
            $FolderPaths = $FoldersToDelete | Where -Property ServerName -EQ $ServerName | Select -ExpandProperty Path
            Write-Verbose "`tDeleting the following home directories from $server.`n"
            $FolderPaths | ForEach{ Write-Verbose "`t`t$_"}

            Write-Verbose "`tInvoking command on remote server to delete the specified home directories"
            # Make a call to the server to have it delete the specified folders recursively
            Invoke-Command -ComputerName $server -ScriptBlock {Remove-Item -LiteralPath $FolderPaths -Recurse -Force}


August 2, 2014 at 2:28 pm

I may be wrong here so someone else can correct me if so

Regarding your question about error handling in invoke-command i believe you would handle that within the Script block so for example

Invoke-Command -ComputerName $server -ScriptBlock {

try {
Remove-Item -LiteralPath $FolderPaths -Recurse -Force
catch {
some exception text


Something else you could also do is something like this incase the try catch does not work.

Remove-Item -LiteralPath $FolderPaths -Recurse -Force -ErrorAction SilentlyContinue -ErrorVariable Testing
$testing | Out-File c:\temp\error.log -Append

so you would then write the errors to a log file and then you can read those over afterwards.