How to Enable or Disable Show File Extensions with PowerShell

Visibility into file extensions is a small yet significant factor in maintaining system security and usability. When Windows Explorer hides file extensions by default, users might inadvertently execute malicious files disguised under seemingly harmless names.

For IT professionals and managed service providers (MSPs), automating the configuration of this setting is crucial across user environments. This article explores how to enable or disable show file extensions with PowerShell, allowing admins to take control of this setting with precision and efficiency.

Background

By default, Windows hides file extensions for known file types, which can lead to confusion or even security risks. For example, a file named report.pdf.exe may appear as report.pdf, tricking users into executing a harmful application. Microsoft has maintained this behavior for years, but enterprise environments often prefer visibility for all file types to reduce risk and support transparency.

This PowerShell script addresses that concern by modifying a specific registry key—HideFileExt—across user profiles and system levels. It enables or disables file extensions display based on an input parameter, automating what would otherwise be a tedious and error-prone manual configuration process.

The 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 {
    
    
    
}

 

Detailed Breakdown

At a high level, this script performs the following operations:

  1. Validates Input and Environment
    It starts by accepting an -Action parameter (Enable or Disable) and optionally a $RestartExplorer flag. It confirms script elevation and checks whether it runs under a user context or SYSTEM.
  2. Defines Helper Functions
    • Test-IsElevated and Test-IsSystem determine execution privileges.
    • Get-UserHives enumerates user profiles by parsing registry SIDs.
    • Set-RegKey handles registry path creation and value setting robustly.
  3. Configures Registry Settings
    Based on the -Action parameter:

    • It sets HideFileExt to 0 (show extensions) or 1 (hide extensions).
    • Modifications apply to:
      • HKLM for system-level changes.
      • HKCU or HKU\<SID> for each user.
  4. Manages User Hives
    For SYSTEM-level execution, it loads ntuser.dat for offline profiles, writes the setting, and unloads them post-operation.
  5. Handles Explorer Restart
    If -RestartExplorer is passed or set via an environment variable, it stops and restarts explorer.exe to ensure changes take effect.

Visual Aid Suggestion: A flowchart showing:

  • Decision point for SYSTEM vs. user context
  • Branches for each registry location update
  • Optional path to Explorer restart

Potential Use Cases

Case Study: Managed IT Service Deployment

An MSP is onboarding a new client with 100+ endpoints. To adhere to cybersecurity best practices, they want file extensions visible to all users. Rather than logging into each device manually, the technician packages this script into a NinjaOne automation policy. The script runs under the SYSTEM account during off-peak hours and sets the registry value for all users—online or offline—ensuring uniformity and compliance in under an hour.

Comparisons

Manual Approach

Users can enable this setting via the Windows Explorer UI:
View → Show → File name extensions
However, this only impacts the current user session and is not scalable.

Group Policy

GPOs can configure this setting but are limited to domain-joined machines and require AD infrastructure.

PowerShell GPO Configuration Scripts

Alternative scripts may only target HKCU without addressing offline users, limiting effectiveness in multi-user or shared environments.

This script’s thoroughness in modifying HKLM and all HKU profiles—even loading ntuser.dat—makes it uniquely robust.

FAQs

Q: Does the script require admin privileges?

Yes. SYSTEM or administrator access is needed to write to HKLM and load user hives.

Q: Can it run on non-domain machines?

Absolutely. It works with local, domain, and AzureAD users.

Q: Will changes apply instantly?

Not always. Restarting explorer.exe or logging off/on ensures immediate effect.

Q: What if a profile is currently in use?

The script detects loaded hives and skips reloading them, reducing disruption.

Implications

Visibility into file extensions enhances security by helping users avoid deceptive files. Configuring this at scale, especially via automation, reduces administrative overhead and increases system hygiene. On the other hand, overly aggressive deployment (e.g., forcibly restarting Explorer without user warning) may frustrate end users. Thoughtful implementation is key.

Recommendations

  • Test Before Broad Deployment: Validate the script on a few endpoints to observe registry behavior and user experience.
  • Leverage NinjaOne Automation: Run the script under SYSTEM context with -Action Enable for maximum coverage.
  • Use Restart Parameter Wisely: If deploying during business hours, avoid restarting Explorer without prompting.
  • Audit User Profiles: Include logic to exclude service or default accounts if unnecessary.

Final Thoughts

Controlling file extension visibility is a deceptively simple yet vital part of Windows security hardening. This PowerShell script brings enterprise-grade configurability to environments of any size—local, hybrid, or cloud. When integrated with NinjaOne’s automation engine, IT professionals can deploy the change across fleets of devices confidently, ensuring consistent policy enforcement with minimal manual effort.

For MSPs and internal IT teams alike, automating settings like these translates into saved time, reduced risk, and a more secure user experience. Whether you’re rolling out new workstations or maintaining legacy systems, this script is a powerful tool in your configuration management arsenal.

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.

Categories:

You might also like

×

See NinjaOne in action!

By submitting this form, I accept NinjaOne's privacy policy.

NinjaOne Terms & Conditions

By clicking the “I Accept” button below, you indicate your acceptance of the following legal terms as well as our 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 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).