windowspowershelldelete-fileremote-management

Why doesn't Remove-Item work properly when removing multiple files but single files are removed fine?


I am attempting to create a powershell script to both shutdown the windows update service as well as clear out the update cache in C:\Windows\SoftwareDistribution\ on a large list of devices that I am connecting to with PSSession. Sounds easy enough however I cant seem to get Remove-Item to delete all the files and folders in this directory. Below is the main part of the script showing the commands I am issuing on each machine:

# Attempts to reset the Windows Update service on all machines in the CSV file
foreach ($machine in $csv)
{
    try
    {
        # Attempts to create a powershell session on the machine to remotely issue commands
        $session = New-PSSession -ComputerName $machine.Name -ErrorAction Stop

        # Attempts to shutdown the Windows Update service
        Invoke-Command -Session $session -ScriptBlock {Stop-Service -Name 'wuauserv' -Force}

        # Deletes the Windows Update cache folder
        Invoke-Command -Session $session -ScriptBlock {
            Start-Sleep -Seconds 1
            #Remove-Item -Path 'C:\Windows\SoftwareDistribution\' -Recurse -Force
            Get-ChildItem -Path 'C:\Windows\SoftwareDistribution\' -Recurse -Force | Remove-Item -Recurse -Force
        }

        # Restarts the Windows Update service
        Invoke-Command -Session $session -ScriptBlock {
            Start-Sleep -Seconds 1
            Start-Service -Name 'wuauserv'
        }

        # Schedules a system reboot at 9pm for good measure
        Invoke-Command -Session $session -ScriptBlock{
            [datetime]$restartTime = '9PM'
            [datetime]$currentTime = Get-Date
            [int]$restartDelay = ($restartTime - $currentTime).TotalSeconds
            shutdown -r -t $restartDelay
        }

        # Closes the Powershell session
        Remove-PSSession $session

        # Sets the outcome of the machine to be written to WindowsUpdateReset.csv
        [string]$outcome = 'Completed'
    }

At first I was attempting to use Remove-Item -Path 'C:\Windows\SoftwareDistribution\' -Recurse -Force to recursively delete all the files but kept running into errors stating that the path couldn't be found or the directory was not empty, after looking around I found lots of mentions that this doesn't work consistently and that Get-ChildItem -Path 'C:\Windows\SoftwareDistribution\' -Recurse -Force | Remove-Item -Recurse -Force would work better. This did resolve those errors but now I get access denied errors. I tried using Get-ChildItem -Path 'C:\Windows\SoftwareDistribution\' -Recurse | foreach { Remove-Item $_.FullName -Recurse -Force } but this didn't change the outcome. I am a local admin with full rights on all of these machines, I can delete the files without any issues both in file explorer and with Remove-Item if I do each file individually but as soon as I attempt to run it recursively on all the files I get access denied errors. Ive even tried to run this locally on the device to see if maybe PSSession was causing an issue but no changes. I saw that this could potentially be caused by file locking but after messing with this I found the files weren't locked, especially since I can delete them individually. Ive been banging my head against this for a bit now and figured I may need some additional insight.


Solution

  • Instead of piping the output from Get-ChildItem, let Remove-Item traverse the given path, most likely what is happening is that a parent folder is being removed together with all its children but then those already deleted items are being piped again to Remove-Item.

    In summary, change:

    Get-ChildItem -Path 'C:\Windows\SoftwareDistribution\' -Recurse -Force | Remove-Item -Recurse -Force
    

    To use only Remove-Item with the * at the end of your path symbolizing you only want to get rid of child items in SoftwareDistribution but not the folder itself:

    Remove-Item 'C:\Windows\SoftwareDistribution\*' -Recurse -Force
    

    Alternatively, what should also work is to remove -Recurse from Get-ChildItem so only immediate child items are piped:

    Get-ChildItem -Path 'C:\Windows\SoftwareDistribution\' -Force | Remove-Item -Recurse -Force