PowerShell ne s’ouvre pas sur Windows Server : corriger l’erreur CLR « HRESULT 0x80070005 – Accès refusé » et fiabiliser .NET Framework

Sur Windows Server 2019, PowerShell peut se fermer aussitôt en affichant “Starting the CLR failed with HRESULT 80070005” (Accès refusé). Ce guide fournit une résolution éprouvée (.NET Framework, ACL, AppLocker/WDAC), un diagnostic pas‑à‑pas et un contournement fiable via PowerShell 7.

Sommaire

PowerShell qui se ferme au lancement avec erreur d’accès refusé

En environnement Windows Server 2019 Datacenter (version 1809, build 17763.5576), l’ouverture de Windows PowerShell 5.1 peut brièvement afficher la fenêtre puis la refermer. Depuis cmd.exe, la commande powershell retourne : Starting the CLR failed with HRESULT 80070005 — ce qui correspond à une erreur Access denied.

Les tentatives classiques (mises à jour cumulatives, DISM avec source ISO, tentative de retrait d’une “fonctionnalité PowerShell”, etc.) n’y changent parfois rien. Aucun journal “parlant” n’est forcément créé et il n’y a pas de minidump, car l’échec intervient très tôt : le Common Language Runtime du .NET Framework ne parvient pas à démarrer.

Ce que cela signifie concrètement

Windows PowerShell 5.1 repose sur le runtime .NET Framework 4.x (CLR v4.0.30319). Si ce runtime est endommagé, bridé par des droits ou bloqué par une politique, PowerShell s’arrête net. Le code 0x80070005 pointe vers un accès refusé : fichier, dossier, clé de Registre, ou chargement d’assembly interdit.

Causes probables

  • Corruption partielle ou installation incomplète de .NET Framework 4.8 (fichiers GAC, composants CLR, Registre).
  • ACL erronées sur C:\Windows\Microsoft.NET\* ou sur le Global Assembly Cache (C:\Windows\Microsoft.NET\assembly).
  • Politiques d’exécution AppLocker, WDAC (Code Integrity) ou Software Restriction Policies bloquant powershell.exe ou des DLL .NET.
  • EDR/antivirus interférant avec le chargement des DLL.
  • Profil utilisateur corrompu ou variables d’environnement atypiques (COMPLUS_*) injectées par un outil.
  • Confusion sur les “fonctionnalités Windows” : PowerShell 5.1 fait partie de l’OS (non désinstallable), seule la “console PowerShell 2.0” est une fonctionnalité optionnelle.

Réponse et solution brève

  1. Réparer ou réinstaller .NET Framework 4.8 avec l’installateur hors‑ligne ; au besoin exécuter d’abord l’outil de réparation .NET. Redémarrer ensuite.
  2. Installer PowerShell 7 en parallèle (MSI), qui s’appuie sur .NET moderne et est indépendant du .NET Framework 4.x.

Si PowerShell 7 (pwsh.exe) fonctionne alors que PowerShell 5.1 ne démarre pas, le problème est quasi exclusivement cantonné au runtime .NET Framework.

Guide pas à pas

Réparer ou réinstaller .NET Framework

Procédure recommandée :

  1. Télécharger l’installateur hors‑ligne de .NET Framework 4.8 sur un poste sûr et le copier sur le serveur affecté.
  2. Exécuter d’abord l’outil officiel de réparation .NET (.NET Framework Repair Tool) si disponible dans votre référentiel interne.
  3. Lancer ensuite l’installateur .NET 4.8 en mode réparation ; si la réparation échoue, procéder à une réinstallation propre.
  4. Redémarrer le serveur.
  5. Tester immédiatement : cmd.exepowershell -nologo -noprofile -c "$PSVersionTable.PSVersion".

Bon à savoir : Windows Server 2019 ne prend pas en charge .NET Framework 4.8.1 ; restez sur 4.8 pour ce système.

Installer PowerShell en version moderne en parallèle

Pour garantir la continuité d’exploitation :

  1. Installer PowerShell 7 via le package MSI (canal stable). L’installation n’écrase pas PowerShell 5.1.
  2. Vérifier l’exécution : cmd.exepwsh -NoLogo puis $PSVersionTable.
  3. Le cas échéant, ajouter C:\Program Files\PowerShell\7 au PATH système et créer un raccourci pour les administrateurs.

Si pwsh fonctionne tandis que powershell.exe échoue, vous avez une preuve solide d’un problème circonscrit à .NET Framework.

Validation rapide après réparation

  • Bitness : tester les deux exécutables :
    • 64 bits : C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
    • 32 bits : C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe
  • À blanc : powershell -noprofile -nologo (évite toute influence de profil).
  • Version : powershell -c "$PSVersionTable" et powershell -c "[Environment]::Version".

Contrôles essentiels côté .NET Framework

Confirmer la présence de la version installée

reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" /v Release

Si la valeur Release ne correspond pas à .NET 4.8 (par ex. valeur trop basse), mettez à niveau. À défaut d’un tableau de correspondance, retenez l’heuristique : une valeur indiquant au moins 4.8 est requise sur Windows Server 2019.

Contrôler les autorisations de fichiers

Des ACL dégradées sur les dossiers .NET/CLR provoquent typiquement l’erreur d’accès refusé au démarrage du CLR.

icacls C:\Windows\Microsoft.NET\ /T > C:\temp\acl_dotnet.txt
icacls C:\Windows\Microsoft.NET\assembly /T >> C:\temp\acl_dotnet.txt

Inspectez acl_dotnet.txt : des lignes ACCESS DENIED ou des propriétaires anormaux (Administrators à la place de TrustedInstaller, par exemple) doivent alerter. Corrigez à partir d’un serveur sain (export/import d’ACL), ou laissez SFC/DISM restaurer les descripteurs par défaut.

Examiner les politiques de contrôle d’exécution

AppLocker/WDAC/SRP peuvent bloquer autant l’exécutable que les DLL .NET.

  • Journaux AppLocker : Observateur d’événements → Applications and Services Logs ▸ Microsoft ▸ Windows ▸ AppLocker.
  • Journaux WDAC/Code Integrity : Observateur d’événements → Applications and Services Logs ▸ Microsoft ▸ Windows ▸ CodeIntegrity.
  • Si des règles existent, autorisez explicitement C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe et les répertoires .NET (C:\Windows\Microsoft.NET\*).
  • En mode audit, vérifiez la présence d’entrées “would be blocked”. Convertissez ensuite en mode imposé une fois validé.

Localiser l’accès refusé avec Process Monitor

Process Monitor (Sysinternals) est l’outil le plus efficace pour identifier la ressource exacte refusée :

  1. Filtrer Process Name = powershell.exe.
  2. Lancer PowerShell pour reproduire l’échec, puis arrêter la capture.
  3. Trier/filtrer sur Result = ACCESS DENIED et analyser le premier refus.

Les chemins cités (DLL du CLR, GAC, clés Registre HKLM\SOFTWARE\Classes/HKCR, sous‑clés .NET) guident la correction d’ACL ou la mise à jour des règles AppLocker/WDAC.

Activer les journaux de liaison d’assembly

À n’activer que temporairement :

reg add "HKLM\SOFTWARE\Microsoft\Fusion" /v ForceLog /t REG_DWORD /d 1 /f
reg add "HKLM\SOFTWARE\Microsoft\Fusion" /v LogFailures /t REG_DWORD /d 1 /f
reg add "HKLM\SOFTWARE\Microsoft\Fusion" /v LogResourceBinds /t REG_DWORD /d 1 /f
reg add "HKLM\SOFTWARE\Microsoft\Fusion" /v LogPath /t REG_SZ /d C:\FusionLogs /f
mkdir C:\FusionLogs

Relancez PowerShell pour générer des traces détaillant le chargement d’assemblies. Remettez ensuite les valeurs à zéro et supprimez le dossier de logs pour éviter toute collecte persistante.

Vérifications système complémentaires

  • SFC : même si DISM est passé, une vérification d’intégrité peut restaurer des fichiers protégés. sfc /scannow
  • Fonctionnalités Windows : la “Console PowerShell 2.0” est une option distincte et obsolète. Ne pas confondre avec PowerShell 5.1 (composant de l’OS non désinstallable).
  • Nouveau profil : créer un administrateur local neuf et tester pour exclure un profil corrompu.
  • EDR/AV : désactiver temporairement (si vos politiques le permettent) pour écarter un blocage de DLL au chargement.

Plan de diagnostic structuré

Symptôme/constatCe que cela indiqueAction prioritaire
PowerShell 5.1 se ferme immédiatement, 0x80070005Échec du CLR .NET 4.x (accès refusé)Réparer/réinstaller .NET 4.8 puis redémarrer
pwsh.exe fonctionneProblème cantonné à .NET FrameworkPoursuivre réparation .NET 4.8, corriger ACL/WDAC
Événements AppLocker/CodeIntegrityBlocage par politiqueCréer des règles d’autorisation ciblées
Procmon montre ACCESS DENIED sur GACACL corrompuesRestaurer ACL/SFC, aligner avec un serveur sain
Échec identique sous nouvel utilisateurCause systèmeFocaliser sur .NET/ACL/politiques

Procédure de correction des ACL en sécurité

Si un refus précis est identifié (par exemple dans C:\Windows\Microsoft.NET\assembly) :

  1. Éviter les changements massifs (pas de icacls C:\Windows /grant ...).
  2. Comparer avec un serveur sain : icacls "C:\Windows\Microsoft.NET\assembly" /T /save C:\temp\good_gac.acl icacls "C:\Windows\Microsoft.NET\assembly" /T /verify
  3. Sur le serveur affecté, restaurer précisément les entrées manquantes sur les chemins signalés par Procmon.
  4. Si des fichiers protégés sont atteints, laisser SFC les rétablir plutôt que de reprendre la propriété manuellement.

En dernier recours, vous pouvez exporter les ACL d’un serveur sain (/save) et les /restore sur les mêmes chemins, après sauvegarde de l’état actuel et approbation.

Points d’attention qui font gagner du temps

  • Ne vous acharnez pas à désinstaller une “fonctionnalité PowerShell” : cela cible la console obsolète, pas PowerShell 5.1 intégré à l’OS.
  • N’essayez pas d’installer .NET 4.8.1 sur Windows Server 2019 : ce n’est pas la bonne base.
  • Testez les deux architectures (System32/SysWOW64) pour cerner un blocage 32/64 bits.
  • Vérifiez le chemin résolu : where powershell.exe doit pointer vers System32\WindowsPowerShell\v1.0. Une redirection ou un binaire remplacé par un agent peut être en cause.
  • Variables d’environnement : traquez des COMPLUS_* inhabituelles dans Système et Utilisateur. Supprimez‑les pour tester.

Exemples de commandes utiles

:: Vérifier la version .NET installée
reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" /v Release

\:: Lancer PowerShell sans profil
powershell -noprofile -nologo

\:: Tester l’exécutable 64 et 32 bits
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -nologo
"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" -nologo

\:: Vérifier l'intégrité des fichiers système
sfc /scannow

\:: Capturer les ACL de .NET
icacls C:\Windows\Microsoft.NET\ /T > C:\temp\acl\_dotnet.txt

\:: Activer/collecter les logs Fusion (temporaire)
reg add "HKLM\SOFTWARE\Microsoft\Fusion" /v LogFailures /t REG\_DWORD /d 1 /f 

Diagnostic avancé avec Process Monitor

Quelques filtres qui aident :

  • Process Name est powershell.exe
  • Result contient ACCESS DENIED ou NAME NOT FOUND (pour repérer une recherche d’assembly non trouvée qui débouche sur un refus ailleurs)
  • Path contient \Microsoft.NET\, \assembly\, mscor*, clr.dll, fusion.dll
  • Operation : CreateFile, Load Image, RegOpenKey, RegQueryValue

Interprétez dans l’ordre d’apparition. Le premier refus bien formé est souvent la cause, les refus suivants ne sont que des effets de bord. Corrigez l’ACL ou la règle AppLocker/WDAC sur l’élément pointé, puis retestez.

Cas d’école et remédiations

Traces ou indicesCause la plus probableRemède recommandé
Refus sur C:\Windows\Microsoft.NET\assembly\GAC_MSIL\*ACL GAC altéréesRestaurer ACL via SFC ou à partir d’un serveur sain
AppLocker journalise un blocage sur powershell.exe ou clr.dllRègle d’exécution trop stricteAjouter une règle d’autorisation (chemin & signature) et repasser en mode imposé après validation
Release .NET incohérent / installateur en réparation.NET incomplet/casséRéparer .NET 4.8 hors‑ligne et redémarrer
pwsh ok, powershell koProblème spécifique à .NET FrameworkStabiliser .NET 4.8 ; garder PowerShell 7 comme standard

Industrialisation et prévention

  • Standardiser PowerShell 7 sur les serveurs (sans retirer PowerShell 5.1) : vous gagnez en robustesse et en sécurité.
  • État de référence des ACL : conservez un export icacls des dossiers .NET sur un serveur “golden” pour comparaison rapide.
  • Politiques AppLocker/WDAC : privilégiez des règles par signature éditeur/chemin système, pas des chemins fragiles.
  • Surveillance : activez l’audit des échecs de chargement pour réagir en amont (journaux Fusion temporaires lors des déploiements sensibles).
  • Gestion des correctifs : inclure la réparation .NET et un test automatisé de lancement PowerShell dans vos pipelines post‑patch.

Résumé actionnable

  1. Réparer/réinstaller .NET Framework 4.8 puis redémarrer.
  2. Installer PowerShell 7 en parallèle pour assurer la continuité d’exploitation.
  3. Si échec : diagnostiquer l’ACCESS DENIED avec Process Monitor et vérifier AppLocker/WDAC ainsi que les ACL sur C:\Windows\Microsoft.NET\* et le GAC.
  4. Exécuter SFC, contrôler les journaux spécialisés (.NET/AppLocker), tester avec un nouveau profil.

Annexe : scripts minimaux pour gagner du temps

Collecte rapide à exécuter depuis cmd.exe (en tant qu’administrateur) :

:: Dossier de collecte
mkdir C:\Temp\PSFail >nul 2>&1

\:: Version .NET (Release)
reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" /v Release > C:\Temp\PSFail\dotnet\_release.txt

\:: ACL .NET
icacls "C:\Windows\Microsoft.NET" /T > C:\Temp\PSFail\acl\_dotnet.txt
icacls "C:\Windows\Microsoft.NET\assembly" /T >> C:\Temp\PSFail\acl\_dotnet.txt

\:: Emplacements PowerShell
where powershell.exe > C:\Temp\PSFail\where\_powershell.txt

\:: Journaux AppLocker et Code Integrity (événements récents)
wevtutil qe "Microsoft-Windows-AppLocker/EXE and DLL" /c:50 /f\:text > C:\Temp\PSFail\applocker.txt
wevtutil qe "Microsoft-Windows-CodeIntegrity/Operational" /c:50 /f\:text > C:\Temp\PSFail\codeintegrity.txt

\:: Test de lancement à blanc
powershell -noprofile -nologo -c "\$PSVersionTable" > C:\Temp\PSFail\ps\_try.txt 2>&1 

Conclusion

Le symptôme “Starting the CLR failed with HRESULT 80070005” résulte presque toujours d’un problème autour du .NET Framework ou d’un blocage d’accès au moment de charger le CLR. La démarche la plus efficace reste : réparer/réinstaller .NET 4.8, valider avec PowerShell 7, puis cibler l’accès refusé via Process Monitor et l’audit des politiques (AppLocker/WDAC). Avec quelques garde‑fous (référence d’ACL, règles d’exécution bien calibrées, tests post‑patch), vous éliminez durablement ce scénario sur Windows Server 2019.

En bref : corrigez .NET, isolez les refus, normalisez PowerShell 7 et documentez vos ACL/politiques — vous retrouverez un PowerShell stable et prévisible.

Sommaire