Supprimer un fichier verrouillé avec PowerShell et MoveFileEx

Certains fichiers refusent de disparaître sous Windows, même après l’arrêt du service associé. C’est typiquement le cas lors d’une mise à jour ou d’une désinstallation d’agent, par exemple un agent de supervision ou de sécurité encore verrouillé par un processus.

Quand Remove-Item, l’explorateur Windows ou une méthode WMI échouent, une solution propre consiste à ne pas forcer la suppression immédiatement. On demande à Windows de supprimer le fichier au prochain redémarrage, avant que les services et processus ne puissent le verrouiller à nouveau.

Pour cela, on peut utiliser PowerShell avec l’API Windows MoveFileEx et l’option MOVEFILE_DELAY_UNTIL_REBOOT.

Pourquoi utiliser PowerShell pour supprimer un fichier verrouillé ?

Certains fichiers restent verrouillés même après l’arrêt du service censé les utiliser. Dans ce cas, les méthodes classiques échouent : suppression depuis l’explorateur, Remove-Item, script de désinstallation ou commande WMI.

Le cas typique : un agent installé dans Program Files, encore verrouillé par un processus, un service, un antivirus ou un composant chargé trop tôt au démarrage. Résultat : la mise à jour ou la désinstallation reste bloquée sur un fichier impossible à supprimer.

Présentation du script PowerShell pour supprimer un fichier

Voici le script qui utilise l’API Windows via la fonction MoveFileEx pour marquer un fichier à supprimer lors du prochain redémarrage :

PowerShell
Function Remove-ItemAnyway {
    [CmdletBinding()]
    Param(
        [parameter(Mandatory=$true)]
        [string] $Path
    )

    Add-Type @'
        using System;
        using System.Text;
        using System.Runtime.InteropServices;

        public class Posh
        {
            public enum MoveFileFlags
            {
                MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004
            }

            [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);

            public static bool MarkFileDelete(string sourcefile)
            {
                return MoveFileEx(sourcefile, null, MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT);
            }
        }
'@

    $Path = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($PWD.Path, $Path))

    Try {
        Remove-Item $Path -ErrorAction Stop
    } Catch {
        $deleteResult = [Posh]::MarkFileDelete($Path)
        If ($deleteResult) {
            Write-Warning -Message "$Path has been marked for deletion"
        }
    }
}

Comment fonctionne ce script ?

  • Add-Type : Permet d’intégrer du code C# directement dans PowerShell pour utiliser la fonction MoveFileEx du fichier kernel32.dll.
  • MoveFileEx : Fonction native de l’API Windows qui permet de déplacer, renommer ou marquer un fichier pour suppression au redémarrage.
  • $Path : Le chemin du fichier à supprimer, reconstruit pour s’assurer qu’il est complet et correct.
  • Remove-Item : La cmdlet tente d’abord de supprimer le fichier de façon classique.
  • Catch : Si la suppression échoue (par exemple, accès refusé), le fichier est marqué pour suppression au prochain redémarrage.

Exemple d’usage

Supposons que vous ayez un fichier récalcitrant nommé ZabbixAgent.exe dans le dossier C:\Program Files\Zabbix que vous n’arrivez pas à supprimer.

PowerShell
Remove-ItemAnyway -Path "C:\Program Files\Zabbix\ZabbixAgent.exe"

Ce script tentera de supprimer le fichier. Si cela échoue, il marquera le fichier pour qu’il soit supprimé automatiquement au prochain redémarrage.

Étendre le script

Le script utilise ici la fonction MoveFileEx avec l’option MOVEFILE_DELAY_UNTIL_REBOOT, mais il est possible d’étendre ses capacités en utilisant d’autres flags proposés par l’API, comme MOVEFILE_REPLACE_EXISTING ou MOVEFILE_COPY_ALLOWED. Cela pourrait être utile dans des cas spécifiques où le fichier doit être déplacé ou remplacé plutôt que simplement supprimé. Voici une liste des autres options disponibles :

C#
Public enum MoveFileFlags
{
    MOVEFILE_REPLACE_EXISTING = 0x00000001,
    MOVEFILE_COPY_ALLOWED = 0x00000002,
    MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004,
    MOVEFILE_WRITE_THROUGH = 0x00000008,
    MOVEFILE_CREATE_HARDLINK = 0x00000010,
    MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
}

Bien que ces options existent, leur utilité dans le cadre de la suppression de fichiers est limitée.

Conclusion

MoveFileEx ne supprime pas un fichier verrouillé immédiatement. La fonction demande à Windows de le supprimer au prochain redémarrage, avant qu’un service ou un processus ne le verrouille à nouveau.

Un administrateur peut utiliser cette approche pour débloquer une désinstallation, une mise à jour d’agent ou un nettoyage automatisé. Il doit toutefois cibler précisément le fichier concerné et contrôler les opérations différées avant de redémarrer le serveur.


Sources

heyscriptingguy

leeholmes

Microsoft


Avatar de Mickael R.

Laisser un commentaire