Comment activer ou désactiver l’affichage des extensions de fichiers avec PowerShell

La visibilité des extensions de fichiers est un facteur peu important mais significatif dans le maintien de la sécurité et de la facilité d’utilisation du système. Lorsque l’explorateur Windows masque les extensions de fichiers par défaut, les utilisateurs peuvent exécuter par inadvertance des fichiers malveillants dissimulés sous des noms apparemment inoffensifs.

Pour les professionnels de l’informatique et les fournisseurs de services gérés (MSP), l’automatisation de la configuration de ce paramètre est cruciale dans tous les environnements d’utilisateur. Cet article explique comment activer ou désactiver l’affichage des extensions de fichiers avec PowerShell, ce qui permet aux administrateurs de prendre le contrôle de ce paramètre avec précision et efficacité.

Contexte

Par défaut, Windows masque les extensions de fichiers pour les types de fichiers connus, ce qui peut entraîner une certaine confusion, voire des risques pour la sécurité. Par exemple, un fichier nommé report.pdf.exe peut apparaître sous la forme report.pdf, ce qui incite les utilisateurs à exécuter une application nuisible. Microsoft maintient ce comportement depuis des années, mais les entreprises préfèrent souvent avoir une visibilité sur tous les types de fichiers afin de réduire les risques et de favoriser la transparence.

Ce script PowerShell répond à ce problème en modifiant une clé de registre spécifique (HideFileExt) pour tous les profils d’utilisateurs et tous les niveaux du système. Il permet d’activer ou de désactiver l’affichage des extensions de fichiers en fonction d’un paramètre d’entrée, automatisant ainsi ce qui serait autrement un processus de configuration manuel fastidieux et sujet aux erreurs.

Le script

#Requires -Version 5.1

<#
.SYNOPSIS
    Enables or disables showing the file extensions in Windows Explorer.
.DESCRIPTION
    Enables or disables showing the file extensions in Windows Explorer.
By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use.
    Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. 
    Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. 
    Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. 
    Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. 
    Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. 
    Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. 
    EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA).

PARAMETER: -Action "Enable"
    Enables showing the file extensions in Windows Explorer.
.EXAMPLE
    -Action "Enable"
    ## EXAMPLE OUTPUT WITH Action ##
    [Info] Enabling showing file extensions for user tuser
    [Info] Successfully enabled showing file extensions for user tuser

.NOTES
    Minimum OS Architecture Supported: Windows 10, Windows Server 2016
    Release Notes: Initial Release
#>

[CmdletBinding()]
param (
    [Parameter()]
    [ValidateSet("Enable", "Disable")]
    [String]$Action,
    [Parameter()]
    [Switch]$RestartExplorer = [System.Convert]::ToBoolean($env:restartExplorer)
)

begin {
    function Test-IsElevated {
        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
        $p = New-Object System.Security.Principal.WindowsPrincipal($id)
        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
    }
    function Test-IsSystem {
        # Get the current Windows identity of the user running the script
        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
    
        # Check if the current identity's name matches "NT AUTHORITY*"
        # or if the identity represents the SYSTEM account
        return $id.Name -like "NT AUTHORITY*" -or $id.IsSystem
    }
    function Get-UserHives {
        param (
            [Parameter()]
            [ValidateSet('AzureAD', 'DomainAndLocal', 'All')]
            [String]$Type = "All",
            [Parameter()]
            [String[]]$ExcludedUsers,
            [Parameter()]
            [switch]$IncludeDefault
        )
    
        # Define the SID patterns to match based on the selected user type
        $Patterns = switch ($Type) {
            "AzureAD" { "S-1-12-1-(\d+-?){4}$" }
            "DomainAndLocal" { "S-1-5-21-(\d+-?){4}$" }
            "All" { "S-1-12-1-(\d+-?){4}$" ; "S-1-5-21-(\d+-?){4}$" } 
        }
    
        # Retrieve user profile information based on the defined patterns
        $UserProfiles = Foreach ($Pattern in $Patterns) { 
            Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*" |
                Where-Object { $_.PSChildName -match $Pattern } | 
                Select-Object @{Name = "SID"; Expression = { $_.PSChildName } },
                @{Name = "Username"; Expression = { "$($_.ProfileImagePath | Split-Path -Leaf)" } }, 
                @{Name = "UserHive"; Expression = { "$($_.ProfileImagePath)\NTuser.dat" } }, 
                @{Name = "Path"; Expression = { $_.ProfileImagePath } }
        }
    
        # If the IncludeDefault switch is set, add the Default profile to the results
        switch ($IncludeDefault) {
            $True {
                $DefaultProfile = "" | Select-Object Username, SID, UserHive, Path
                $DefaultProfile.Username = "Default"
                $DefaultProfile.SID = "DefaultProfile"
                $DefaultProfile.Userhive = "$env:SystemDrive\Users\Default\NTUSER.DAT"
                $DefaultProfile.Path = "C:\Users\Default"
    
                # Exclude users specified in the ExcludedUsers list
                $DefaultProfile | Where-Object { $ExcludedUsers -notcontains $_.Username }
            }
        }
    
        # Return the list of user profiles, excluding any specified in the ExcludedUsers list
        $UserProfiles | Where-Object { $ExcludedUsers -notcontains $_.Username }
    }
    function Set-RegKey {
        param (
            $Path,
            $Name,
            $Value,
            [ValidateSet("DWord", "QWord", "String", "ExpandedString", "Binary", "MultiString", "Unknown")]
            $PropertyType = "DWord"
        )
    
        # Check if the specified registry path exists
        if (!(Test-Path -Path $Path)) {
            try {
                # If the path does not exist, create it
                New-Item -Path $Path -Force -ErrorAction Stop | Out-Null
            }
            catch {
                # If there is an error creating the path, output an error message and exit
                Write-Host "[Error] Unable to create the registry path $Path for $Name. Please see the error below!"
                Write-Host "[Error] $($_.Exception.Message)"
                exit 1
            }
        }
    
        # Check if the registry key already exists at the specified path
        if (Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue) {
            # Retrieve the current value of the registry key
            $CurrentValue = (Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name
            if ($CurrentValue -eq $Value) {
                Write-Host "$Path\$Name is already the value '$Value'."
            }
            else {
                try {
                    # Update the registry key with the new value
                    Set-ItemProperty -Path $Path -Name $Name -Value $Value -Force -Confirm:$false -ErrorAction Stop | Out-Null
                }
                catch {
                    # If there is an error setting the key, output an error message and exit
                    Write-Host "[Error] Unable to set registry key for $Name at $Path. Please see the error below!"
                    Write-Host "[Error] $($_.Exception.Message)"
                    exit 1
                }
                # Output the change made to the registry key
                Write-Host "$Path\$Name changed from $CurrentValue to $((Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name)"
            }
        }
        else {
            try {
                # If the registry key does not exist, create it with the specified value and property type
                New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType $PropertyType -Force -Confirm:$false -ErrorAction Stop | Out-Null
            }
            catch {
                # If there is an error creating the key, output an error message and exit
                Write-Host "[Error] Unable to set registry key for $Name at $Path. Please see the error below!"
                Write-Host "[Error] $($_.Exception.Message)"
                exit 1
            }
            # Output the creation of the new registry key
            Write-Host "Set $Path\$Name to $((Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name)"
        }
    }
    $ExitCode = 0
}
process {
    if ($env:action -and $env:action -notlike "null") { $Action = $env:action }
    if (-not $Action) {
        Write-Host -Object "[Error] You must specify an action (Enable or Disable)"
        exit 1
    }

    # Check if the action is valid
    if ($Action -ne "Enable" -and $Action -ne "Disable") {
        Write-Host -Object "[Error] The action '$Action' is invalid. 'Enable' or 'Disable' are the only valid actions"
        exit 1
    }

    if ((Test-IsSystem)) {
        # When running as a system account or elevated

        # Local Machine

        # Set the registry key if the action is Enable
        if ($Action -eq "Enable") {
            try {
                Write-Host -Object "[Info] Enabling showing file extensions for local machine"
                Set-RegKey -Path "Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "HideFileExt" -Value 0 -Force -ErrorAction Stop
                Write-Host -Object "[Info] Successfully enabled showing file extensions for local machine"
            }
            catch {
                Write-Host -Object "[Error] Failed to enable showing file extensions for local machine"
            }
        }

        # Set the registry key if the action is Disable
        if ($Action -eq "Disable") {
            try {
                Write-Host -Object "[Info] Disabling showing file extensions for local machine"
                Set-RegKey -Path "Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "HideFileExt" -Value 1 -Force -ErrorAction Stop
                Write-Host -Object "[Info] Successfully disabled showing file extensions for local machine"
            }
            catch {
                Write-Host -Object "[Error] Failed to disable showing file extensions for local machine"
            }
        }

        # User Profiles

        # Get all user profiles on the machine
        $UserProfiles = Get-UserHives -Type "All"
        $ProfileWasLoaded = New-Object System.Collections.Generic.List[object]

        # Loop through each profile on the machine
        ForEach ($UserProfile in $UserProfiles) {
            # Load User ntuser.dat if it's not already loaded
            If (!(Test-Path -Path Registry::HKEY_USERS\$($UserProfile.SID) -ErrorAction SilentlyContinue)) {
                Start-Process -FilePath "cmd.exe" -ArgumentList "/C reg.exe LOAD HKU\$($UserProfile.SID) `"$($UserProfile.UserHive)`"" -Wait -WindowStyle Hidden
                $ProfileWasLoaded.Add($UserProfile)
            }
            # Set the registry key if the action is Enable
            if ($Action -eq "Enable") {
                try {
                    Write-Host -Object "[Info] Enabling showing file extensions for user $($UserProfile.UserName)"
                    Set-RegKey -Path "Registry::HKEY_USERS\$($UserProfile.SID)\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "HideFileExt" -Value 0 -Force -ErrorAction Stop
                    Write-Host -Object "[Info] Successfully enabled showing file extensions for user $($UserProfile.UserName)"
                }
                catch {
                    Write-Host -Object "[Error] Failed to enable showing file extensions for user $($UserProfile.UserName)"
                }
            }

            # Set the registry key if the action is Disable
            if ($Action -eq "Disable") {
                try {
                    Write-Host -Object "[Info] Disabling showing file extensions for user $($UserProfile.UserName)"
                    Set-RegKey -Path "Registry::HKEY_USERS\$($UserProfile.SID)\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "HideFileExt" -Value 1 -Force -ErrorAction Stop
                    Write-Host -Object "[Info] Successfully disabled showing file extensions for user $($UserProfile.UserName)"
                }
                catch {
                    Write-Host -Object "[Error] Failed to disable showing file extensions for user $($UserProfile.UserName)"
                }
            }
        }

        # If user profiles were loaded, unload the profiles
        if ($ProfileWasLoaded.Count -gt 0) {
            ForEach ($UserProfile in $ProfileWasLoaded) {
                # Unload NTuser.dat
                [gc]::Collect()
                Start-Sleep 1
                Start-Process -FilePath "cmd.exe" -ArgumentList "/C reg.exe UNLOAD HKU\$($UserProfile.SID)" -Wait -WindowStyle Hidden | Out-Null
            }
        }
    }
    else {
        # When running as a user account

        # Set the registry key if the action is Enable
        if ($Action -eq "Enable") {
            try {
                Write-Host -Object "[Info] Enabling showing file extensions for user $($env:USERNAME)"
                Set-RegKey -Path "Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "HideFileExt" -Value 0 -Force -ErrorAction Stop
                Write-Host -Object "[Info] Successfully enabled showing file extensions for user $($env:USERNAME)"
            }
            catch {
                Write-Host -Object "[Error] Failed to enable showing file extensions for user $($env:USERNAME)"
            }
        }

        # Set the registry key if the action is Disable
        if ($Action -eq "Disable") {
            try {
                Write-Host -Object "[Info] Disabling showing file extensions for user $($env:USERNAME)"
                Set-RegKey -Path "Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" -Name "HideFileExt" -Value 1 -Force -ErrorAction Stop
                Write-Host -Object "[Info] Successfully disabled showing file extensions for user $($env:USERNAME)"
            }
            catch {
                Write-Host -Object "[Error] Failed to disable showing file extensions for user $($env:USERNAME)"
            }
        }
    }

    # Check if the $RestartExplorer flag is set
    if ($RestartExplorer) {
        # Display a message indicating that Explorer.exe is being restarted
        Write-Host "`nRestarting Explorer.exe as requested."

        try {
            # Stop all instances of Explorer
            if (Test-IsSystem) {
                Get-Process -Name "explorer" | Stop-Process -Force -ErrorAction Stop
            }
            else {
                Get-Process -Name "explorer" | Where-Object { $_.SI -eq (Get-Process -PID $PID).SessionId } | Stop-Process -Force -ErrorAction Stop
            }
        }
        catch {
            Write-Host -Object "[Error] Failed to stop explorer.exe"
            Write-Host -Object "[Error] $($_.Exception.Message)"
            $ExitCode = 1
        }
        
        # Pause for 1 second to ensure processes have fully stopped before restarting
        Start-Sleep -Seconds 1
    
        # If not running as the System account and Explorer.exe is not already running, start a new instance
        if (!(Test-IsSystem) -and !(Get-Process -Name "explorer" -ErrorAction SilentlyContinue)) {
            try {
                Start-Process -FilePath "$env:SystemRoot\explorer.exe" -Wait -ErrorAction Stop
            }
            catch {
                Write-Host -Object "[Error] Failed to start explorer.exe"
                Write-Host -Object "[Error] $($_.Exception.Message)"
                $ExitCode = 1
            }
        }
    }
    else {
        # If $RestartExplorer is not set, warn the user that they may need to manually restart Explorer.exe
        Write-Host -Object ""
        Write-Warning -Message "You may need to restart Explorer.exe for the script to take effect immediately."
    }

    # Exit the script with the predefined $ExitCode.
    exit $ExitCode
}
end {
    
    
    
}

 

Description détaillée

À un niveau élevé, ce script effectue les opérations suivantes :

  1. Validation des données et de l’environnement
    Il commence par accepter un paramètre -Action (Enable ou Disable) et éventuellement un indicateur $RestartExplorer. Il confirme l’élévation du script et vérifie s’il s’exécute dans un contexte utilisateur ou SYSTEM.
  2. Définit les fonctions d’aide
    • Test-IsElevated et Test-IsSystem déterminent les privilèges d’exécution.
    • Get-UserHives énumère les profils d’utilisateurs en analysant les SID du registre.
    • Set-RegKey gère la création de chemins d’accès au registre et la configuration de valeurs de manière optimale.
  3. Configure les paramètres du registre
    En fonction du paramètre -Action :

    • Il donne à HideFileExt la valeur 0 (afficher les extensions) ou 1 (masquer les extensions).
    • Les modifications s’appliquent à :
      • HKLM pour les modifications au niveau du système.
      • HKCU ou HKU\<SID> pour chaque utilisateur.
  4. Gestion des ruches d’utilisateurs
    Pour l’exécution au niveau SYSTÈME, il charge ntuser.dat pour les profils hors ligne, écrit les paramètres et les décharge après l’opération.
  5. Gestion du redémarrage de l’explorateur
    Si -RestartExplorer est transmis ou défini par une variable d’environnement, il arrête et redémarre explorer.exe pour s’assurer que les modifications sont prises en compte.

Suggestion d’aide visuelle: Un organigramme montrant :

  • Point de décision pour SYSTÈME par rapport au contexte de l’utilisateur
  • Branches pour chaque mise à jour de l’emplacement du registre
  • Chemin d’accès facultatif au redémarrage de l’explorateur

Cas d’utilisation potentiels

Étude de cas : Déploiement de services informatiques gérés

Une entreprise MSP est en train d’effectuer l’onboarding d’un nouveau client avec plus de 100 terminaux. Pour respecter les bonnes pratiques de cybersécurité, ils veulent que les extensions de fichiers soient visibles par tous les utilisateurs. Plutôt que de se connecter manuellement à chaque appareil, le technicien intègre ce script dans une stratégie NinjaOne d’automatisation. Le script s’exécute sous le compte SYSTÈME pendant les heures creuses et définit la valeur du registre pour tous les utilisateurs, qu’ils soient en ligne ou non, ce qui garantit l’uniformité et la conformité en moins d’une heure.

Comparaisons

Approche manuelle

Les utilisateurs peuvent activer ce paramètre via l’interface utilisateur de l’explorateur Windows :
Affichage → Afficher → Extensions de nom de fichier
Cependant, cela n’a d’impact que sur la session de l’utilisateur en cours et n’est pas extensible.

Stratégie de groupe (GPO)

Les GPO peuvent configurer ce paramètre, mais ils sont limités aux machines reliées à un domaine et nécessitent une infrastructure Active Directory.

Scripts PowerShell de configuration de GPO

Les scripts alternatifs peuvent ne cibler que HKCU sans tenir compte des utilisateurs hors ligne, ce qui limite l’efficacité dans les environnements multi-utilisateurs ou partagés.

La minutie avec laquelle ce script modifie HKLM et tous les profils HKU ainsi que le chargement de ntuser.dat, lui confère une fiabilité unique.

Questions fréquentes

Q : Le script nécessite-t-il des privilèges d’administrateur ?

Oui. L’accès SYSTEM ou administrateur est nécessaire pour écrire dans HKLM et charger les ruches (hive) utilisateur.

Q : Peut-il fonctionner sur des machines ne faisant pas partie d’un domaine ?

Absolument. Il fonctionne avec les utilisateurs locaux, de domaine et AzureAD.

Q : Les changements s’appliqueront-ils instantanément ?

Pas toujours. Le redémarrage d’explorer.exe ou la déconnexion permet d’obtenir un effet immédiat.

Q : Que se passe-t-il si un profil est en cours d’utilisation ?

Le script détecte les ruches chargées et évite de les recharger, ce qui réduit les perturbations.

Implications

La visibilité des extensions de fichiers renforce la sécurité en aidant les utilisateurs à éviter les fichiers trompeurs. La configuration à grande échelle, en particulier via l’automatisation, réduit les frais administratifs et améliore l’hygiène du système. D’autre part, un déploiement trop agressif (par exemple, le redémarrage forcé d’Explorer sans avertissement de l’utilisateur) peut frustrer les utilisateurs finaux. Une implémentation réfléchie est essentielle.

Recommandations

  • Tester avant le déploiement à grande échelle: Validez le script sur quelques terminaux pour observer le comportement du registre et l’expérience utilisateur.
  • Profiter de l’automatisation de NinjaOne: Exécutez le script dans le contexte SYSTEM avec -Action Enable pour une couverture maximale.
  • Utiliser le paramètre de redémarrage à bon escient: Si le déploiement a lieu pendant les heures de bureau, évitez de redémarrer Explorer sans y être invité.
  • Auditer les profils d’utilisateurs: Inclure une logique d’exclusion des comptes de service ou des comptes par défaut si cela n’est pas nécessaire.

Conclusion

Le contrôle de la visibilité des extensions de fichiers est un élément apparemment simple mais essentiel du renforcement de la sécurité de Windows. Ce script PowerShell apporte une configurabilité d’entreprise adaptée aux environnements de toute taille, qu’ils soient locaux, hybrides ou sur le cloud. Lorsqu’il est intégré au moteur d’automatisation de NinjaOne, les professionnels de l’informatique peuvent déployer le changement sur des flottes d’appareils en toute confiance, en garantissant une application constante des stratégies avec un minimum d’efforts manuels.

Pour les MSP comme pour les équipes informatiques internes, l’automatisation de ces paramètres se traduit par un gain de temps, une réduction des risques et une expérience utilisateur plus sûre. Qu’il s’agisse de déployer de nouveaux postes de travail ou de maintenir des systèmes existants, ce script est un outil puissant dans votre arsenal de gestion de la configuration.

Next Steps

Building an efficient and effective IT team requires a centralized solution that acts as your core service delivery tool. NinjaOne enables IT teams to monitor, manage, secure, and support all their devices, wherever they are, without the need for complex on-premises infrastructure.

Learn more about NinjaOne Remote Script Deployment, check out a live tour, or start your free trial of the NinjaOne platform.

Catégories :

Vous pourriez aussi aimer