Come impostare un browser predefinito per tutti gli utenti con PowerShell: Una guida passo per passo

La gestione del browser predefinito per più profili utente in un sistema Windows può essere un compito difficile, soprattutto in ambienti aziendali dove la coerenza e la conformità sono fondamentali. Automatizzando questo processo con uno script PowerShell si semplifica il compito, assicurando che ogni utente del sistema abbia la stessa esperienza di navigazione.

Questo articolo esplorerà uno  script PowerShell progettato per impostare un browser predefinito per tutti gli utenti  su un computer Windows, fornendo ai professionisti IT e ai Managed Service Provider (MSP) un potente strumento per mantenere il controllo sui propri ambienti.

Background

In un ambiente IT, in particolare nelle aziende o negli MSP, è fondamentale mantenere un’esperienza utente coerente. Quando si tratta di più utenti su un singolo computer o su una rete, l’impostazione manuale del browser predefinito per ogni profilo può richiedere molto lavoro ed essere soggetta a errori. Uno script PowerShell che automatizzi questo processo non solo fa risparmiare tempo, ma garantisce anche l’uniformità, che può essere essenziale per la conformità e il supporto agli utenti.

Questo specifico script è una versione modificata di uno script di Danysys, progettato per cambiare il browser predefinito per tutti gli utenti aggiornando le chiavi di registro. Lo script è altamente adattabile e supporta i browser più diffusi come Mozilla Firefox, Google Chrome e Microsoft Edge. Utilizzando lo script, gli amministratori IT possono garantire che tutti gli utenti abbiano il browser corretto impostato come predefinito, indipendentemente dal numero di profili o dallo stato del sistema.

Lo script per impostare un browser predefinito:

#Requires -Version 5.1

<#
.SYNOPSIS
    Sets the default browser for all users.
.DESCRIPTION
    Sets the default browser for all users.
.EXAMPLE
    -Browser "Mozilla Firefox" -RestartExplorer

    Setting default browser of Mozilla Firefox for Administrator.
    Setting 
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice\Hash changed from 2q7+uVxu0/A= to FKcuHm4FMN4=
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice\ProgId changed from ChromeHTML to FirefoxURL-308046B0AF4A39CB
    Setting 
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice\Hash changed from zR3ANZC6jVI= to clMyDtJdxck=
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\https\UserChoice\ProgId changed from ChromeHTML to FirefoxURL-308046B0AF4A39CB
    Setting 
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\htm\UserChoice\Hash changed from IQfza9L6Tfw= to t8+HFkmUAd0=
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\htm\UserChoice\ProgId changed from ChromeHTML to FirefoxHTML-308046B0AF4A39CB
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.htm\UserChoice\Hash changed from IQfza9L6Tfw= to t8+HFkmUAd0=
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.htm\UserChoice\ProgId changed from ChromeHTML to FirefoxHTML-308046B0AF4A39CB
    Setting 
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\html\UserChoice\Hash changed from 7CcRlkLW3ik= to q0Eix6jwLFg=
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\html\UserChoice\ProgId changed from ChromeHTML to FirefoxHTML-308046B0AF4A39CB
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.html\UserChoice\Hash changed from 7CcRlkLW3ik= to q0Eix6jwLFg=
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.html\UserChoice\ProgId changed from ChromeHTML to FirefoxHTML-308046B0AF4A39CB
    Setting 
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\xhtml\UserChoice\Hash changed from IC7TXk1anlM= to y2gIOuiaLb0=
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\xhtml\UserChoice\ProgId changed from ChromeHTML to FirefoxHTML-308046B0AF4A39CB
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.xhtml\UserChoice\Hash changed from IC7TXk1anlM= to y2gIOuiaLb0=
    Registry::HKEY_USERS\S-1-5-21-528047445-1317477324-4168425688-500\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.xhtml\UserChoice\ProgId changed from ChromeHTML to FirefoxHTML-308046B0AF4A39CB
    Restarting Explorer.exe

PARAMETER: -Browser "Mozilla Firefox"
    Set the default browser to either "Mozilla Firefox", "Google Chrome" or "Microsoft Edge".

PARAMETER: -Restart Explorer
    Restarts Explorer.exe so that the desktop icons for .html files refresh immediately.
    
LICENSE:
    Modified version from: https://github.com/DanysysTeam/PS-SFTA/blob/22a32292e576afc976a1167d92b50741ef523066/SFTA.ps1
    This script incorporates the `Get-HexDateTime` and `Get-Hash` functions from Danysys, without which it would not be possible.
    
    LICENSE: https://github.com/DanysysTeam/PS-SFTA/blob/22a32292e576afc976a1167d92b50741ef523066/SFTA.ps1
    MIT License
    
    Copyright (c) 2022 Danysys. <danysys.com>

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.

.OUTPUTS
    None
.NOTES
    Minimum OS Architecture Supported: Windows 10+
    Release Notes:
    Initial Release
By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://ninjastage2.wpengine.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).
#>

[CmdletBinding()]
param (
    [Parameter()]
    [String]$Browser,
    [Parameter()]
    [Switch]$RestartExplorer = [System.Convert]::ToBoolean($env:restartExplorer)
)

begin {
    if ($env:browser -and $env:browser -notlike "null") {
        $Browser = $env:browser
    }

    # If no browser is selected, terminate with an error message.
    if (-not $Browser) {
        Write-Host "[Error] Please select at least one browser!"
        Exit 1
    }

    # Test if running as Administrator
    function Test-IsElevated {
        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
        $p = New-Object System.Security.Principal.WindowsPrincipal($id)
        $p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
    }

    # Test if running as System
    function Test-IsSystem {
        $id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
        return $id.Name -like "NT AUTHORITY*" -or $id.IsSystem
    }

    function Get-HexDateTime {
        # This function was created by DanySys at https://github.com/DanysysTeam/PS-SFTA
        [OutputType([string])]
    
        $now = [DateTime]::Now
        $dateTime = [DateTime]::New($now.Year, $now.Month, $now.Day, $now.Hour, $now.Minute, 0)
        $fileTime = $dateTime.ToFileTime()
        $hi = ($fileTime -shr 32)
        $low = ($fileTime -band 0xFFFFFFFFL)
        ($hi.ToString("X8") + $low.ToString("X8")).ToLower()
    }

    function Get-Hash {
        # This function was created by DanySys at https://github.com/DanysysTeam/PS-SFTA
        [CmdletBinding()]
        param (
            [Parameter( Position = 0, Mandatory = $True )]
            [string]
            $BaseInfo
        )
    
        function local:Get-ShiftRight {
            [CmdletBinding()]
            param (
                [Parameter( Position = 0, Mandatory = $true)]
                [long] $iValue, 
                
                [Parameter( Position = 1, Mandatory = $true)]
                [int] $iCount 
            )
        
            if ($iValue -band 0x80000000) {
                Write-Output (( $iValue -shr $iCount) -bxor 0xFFFF0000)
            }
            else {
                Write-Output ($iValue -shr $iCount)
            }
        }
    
        function local:Get-Long {
            [CmdletBinding()]
            param (
                [Parameter( Position = 0, Mandatory = $true)]
                [byte[]] $Bytes,
        
                [Parameter( Position = 1)]
                [int] $Index = 0
            )
        
            Write-Output ([BitConverter]::ToInt32($Bytes, $Index))
        }
    
        function local:Convert-Int32 {
            param (
                [Parameter( Position = 0, Mandatory = $true)]
                [long] $Value
            )
        
            [byte[]] $bytes = [BitConverter]::GetBytes($Value)
            return [BitConverter]::ToInt32( $bytes, 0) 
        }
    
        [Byte[]] $bytesBaseInfo = [System.Text.Encoding]::Unicode.GetBytes($baseInfo) 
        $bytesBaseInfo += 0x00, 0x00  
        
        $MD5 = New-Object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
        [Byte[]] $bytesMD5 = $MD5.ComputeHash($bytesBaseInfo)
        
        $lengthBase = ($baseInfo.Length * 2) + 2 
        $length = (($lengthBase -band 4) -le 1) + (Get-ShiftRight $lengthBase 2) - 1
        $base64Hash = ""
    
        if ($length -gt 1) {
        
            $map = @{PDATA = 0; CACHE = 0; COUNTER = 0 ; INDEX = 0; MD51 = 0; MD52 = 0; OUTHASH1 = 0; OUTHASH2 = 0;
                R0 = 0; R1 = @(0, 0); R2 = @(0, 0); R3 = 0; R4 = @(0, 0); R5 = @(0, 0); R6 = @(0, 0); R7 = @(0, 0)
            }
        
            $map.CACHE = 0
            $map.OUTHASH1 = 0
            $map.PDATA = 0
            $map.MD51 = (((Get-Long $bytesMD5) -bor 1) + 0x69FB0000L)
            $map.MD52 = ((Get-Long $bytesMD5 4) -bor 1) + 0x13DB0000L
            $map.INDEX = Get-ShiftRight ($length - 2) 1
            $map.COUNTER = $map.INDEX + 1
        
            while ($map.COUNTER) {
                $map.R0 = Convert-Int32 ((Get-Long $bytesBaseInfo $map.PDATA) + [long]$map.OUTHASH1)
                $map.R1[0] = Convert-Int32 (Get-Long $bytesBaseInfo ($map.PDATA + 4))
                $map.PDATA = $map.PDATA + 8
                $map.R2[0] = Convert-Int32 (($map.R0 * ([long]$map.MD51)) - (0x10FA9605L * ((Get-ShiftRight $map.R0 16))))
                $map.R2[1] = Convert-Int32 ((0x79F8A395L * ([long]$map.R2[0])) + (0x689B6B9FL * (Get-ShiftRight $map.R2[0] 16)))
                $map.R3 = Convert-Int32 ((0xEA970001L * $map.R2[1]) - (0x3C101569L * (Get-ShiftRight $map.R2[1] 16) ))
                $map.R4[0] = Convert-Int32 ($map.R3 + $map.R1[0])
                $map.R5[0] = Convert-Int32 ($map.CACHE + $map.R3)
                $map.R6[0] = Convert-Int32 (($map.R4[0] * [long]$map.MD52) - (0x3CE8EC25L * (Get-ShiftRight $map.R4[0] 16)))
                $map.R6[1] = Convert-Int32 ((0x59C3AF2DL * $map.R6[0]) - (0x2232E0F1L * (Get-ShiftRight $map.R6[0] 16)))
                $map.OUTHASH1 = Convert-Int32 ((0x1EC90001L * $map.R6[1]) + (0x35BD1EC9L * (Get-ShiftRight $map.R6[1] 16)))
                $map.OUTHASH2 = Convert-Int32 ([long]$map.R5[0] + [long]$map.OUTHASH1)
                $map.CACHE = ([long]$map.OUTHASH2)
                $map.COUNTER = $map.COUNTER - 1
            }
    
            [Byte[]] $outHash = @(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
            [byte[]] $buffer = [BitConverter]::GetBytes($map.OUTHASH1)
            $buffer.CopyTo($outHash, 0)
            $buffer = [BitConverter]::GetBytes($map.OUTHASH2)
            $buffer.CopyTo($outHash, 4)
        
            $map = @{PDATA = 0; CACHE = 0; COUNTER = 0 ; INDEX = 0; MD51 = 0; MD52 = 0; OUTHASH1 = 0; OUTHASH2 = 0;
                R0 = 0; R1 = @(0, 0); R2 = @(0, 0); R3 = 0; R4 = @(0, 0); R5 = @(0, 0); R6 = @(0, 0); R7 = @(0, 0)
            }
        
            $map.CACHE = 0
            $map.OUTHASH1 = 0
            $map.PDATA = 0
            $map.MD51 = ((Get-Long $bytesMD5) -bor 1)
            $map.MD52 = ((Get-Long $bytesMD5 4) -bor 1)
            $map.INDEX = Get-ShiftRight ($length - 2) 1
            $map.COUNTER = $map.INDEX + 1
    
            while ($map.COUNTER) {
                $map.R0 = Convert-Int32 ((Get-Long $bytesBaseInfo $map.PDATA) + ([long]$map.OUTHASH1))
                $map.PDATA = $map.PDATA + 8
                $map.R1[0] = Convert-Int32 ($map.R0 * [long]$map.MD51)
                $map.R1[1] = Convert-Int32 ((0xB1110000L * $map.R1[0]) - (0x30674EEFL * (Get-ShiftRight $map.R1[0] 16)))
                $map.R2[0] = Convert-Int32 ((0x5B9F0000L * $map.R1[1]) - (0x78F7A461L * (Get-ShiftRight $map.R1[1] 16)))
                $map.R2[1] = Convert-Int32 ((0x12CEB96DL * (Get-ShiftRight $map.R2[0] 16)) - (0x46930000L * $map.R2[0]))
                $map.R3 = Convert-Int32 ((0x1D830000L * $map.R2[1]) + (0x257E1D83L * (Get-ShiftRight $map.R2[1] 16)))
                $map.R4[0] = Convert-Int32 ([long]$map.MD52 * ([long]$map.R3 + (Get-Long $bytesBaseInfo ($map.PDATA - 4))))
                $map.R4[1] = Convert-Int32 ((0x16F50000L * $map.R4[0]) - (0x5D8BE90BL * (Get-ShiftRight $map.R4[0] 16)))
                $map.R5[0] = Convert-Int32 ((0x96FF0000L * $map.R4[1]) - (0x2C7C6901L * (Get-ShiftRight $map.R4[1] 16)))
                $map.R5[1] = Convert-Int32 ((0x2B890000L * $map.R5[0]) + (0x7C932B89L * (Get-ShiftRight $map.R5[0] 16)))
                $map.OUTHASH1 = Convert-Int32 ((0x9F690000L * $map.R5[1]) - (0x405B6097L * (Get-ShiftRight ($map.R5[1]) 16)))
                $map.OUTHASH2 = Convert-Int32 ([long]$map.OUTHASH1 + $map.CACHE + $map.R3) 
                $map.CACHE = ([long]$map.OUTHASH2)
                $map.COUNTER = $map.COUNTER - 1
            }
        
            $buffer = [BitConverter]::GetBytes($map.OUTHASH1)
            $buffer.CopyTo($outHash, 8)
            $buffer = [BitConverter]::GetBytes($map.OUTHASH2)
            $buffer.CopyTo($outHash, 12)
        
            [Byte[]] $outHashBase = @(0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
            $hashValue1 = ((Get-Long $outHash 8) -bxor (Get-Long $outHash))
            $hashValue2 = ((Get-Long $outHash 12) -bxor (Get-Long $outHash 4))
        
            $buffer = [BitConverter]::GetBytes($hashValue1)
            $buffer.CopyTo($outHashBase, 0)
            $buffer = [BitConverter]::GetBytes($hashValue2)
            $buffer.CopyTo($outHashBase, 4)
            $base64Hash = [Convert]::ToBase64String($outHashBase) 
        }
    
        $base64Hash
    }

    # Helper function for setting registry keys
    function Set-RegKey {
        param (
            $Path,
            $Name,
            $Value,
            [ValidateSet("DWord", "QWord", "String", "ExpandedString", "Binary", "MultiString", "Unknown")]
            $PropertyType = "DWord"
        )
        if (-not $(Test-Path -Path $Path)) {
            # Check if path does not exist and create the path
            New-Item -Path $Path -Force | Out-Null
        }
        if ((Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue)) {
            # Update property and print out what it was changed from and changed to
            $CurrentValue = (Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name
            try {
                Set-ItemProperty -Path $Path -Name $Name -Value $Value -Force -Confirm:$false -ErrorAction Stop | Out-Null
            }
            catch {
                Write-Host "[Error] Unable to set registry key for $Name at $Path please see below error!"
                Write-Host "[Error] $($_.Exception.Message)"
                exit 1
            }
            Write-Host "$Path\$Name changed from $CurrentValue to $($(Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name)"
        }
        else {
            # Create property with value
            try {
                New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType $PropertyType -Force -Confirm:$false -ErrorAction Stop | Out-Null
            }
            catch {
                Write-Host "[Error] Unable to set registry key for $Name at $Path please see below error!"
                Write-Host "[Error] $($_.Exception.Message)"
                exit 1
            }
            Write-Host "Set $Path\$Name to $($(Get-ItemProperty -Path $Path -Name $Name -ErrorAction SilentlyContinue).$Name)"
        }
    }

    # Retrieves all accounts on a system.
    function Get-UserHives {
        param (
            [Parameter()]
            [ValidateSet('AzureAD', 'DomainAndLocal', 'All')]
            [String]$Type = "All",
            [Parameter()]
            [String[]]$ExcludedUsers,
            [Parameter()]
            [switch]$IncludeDefault
        )
    
        # User account SID's follow a particular patter depending on if they're azure AD or a Domain account or a local "workgroup" account.
        $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}$" } 
        }
    
        # We'll need the NTuser.dat file to load each users registry hive. So we grab it if their account sid matches the above pattern. 
        $UserProfiles = Foreach ($Pattern in $Patterns) { 
            Get-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\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 } }
        }
    
        # There are some situations where grabbing the .Default user's info is needed.
        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"
    
                $DefaultProfile | Where-Object { $ExcludedUsers -notcontains $_.UserName }
            }
        }
    
        $UserProfiles | Where-Object { $ExcludedUsers -notcontains $_.UserName }
    }

    # This is used to check that the browser is installed.
    function Find-UninstallKey {
        [CmdletBinding()]
        param (
            [Parameter(ValueFromPipeline = $True)]
            [String]$DisplayName,
            [Parameter()]
            [Switch]$UninstallString
        )
        process {
            $UninstallList = New-Object System.Collections.Generic.List[Object]
    
            $Result = Get-ChildItem -Path Registry::HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Get-ItemProperty | Where-Object { $_.DisplayName -like "*$DisplayName*" }
            if ($Result) { $UninstallList.Add($Result) }
    
            $Result = Get-ChildItem -Path Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Get-ItemProperty | Where-Object { $_.DisplayName -like "*$DisplayName*" }
            if ($Result) { $UninstallList.Add($Result) }
    
            # Programs don't always have an uninstall string listed here so to account for that I made this optional.
            if ($UninstallString) {
                $UninstallList | Select-Object -ExpandProperty UninstallString -ErrorAction SilentlyContinue
            }
            else {
                $UninstallList
            }
        }
    }
}
process {
    if (-not (Test-IsElevated)) {
        Write-Host "[Error] Access Denied. Please run with Administrator privileges."
        exit 1
    }

    # Protocols and file associations to set
    $Protocols = "http", "https"
    $Files = "htm", "html", "xhtml"

    # Handlers for each product
    switch ($Browser) {
        "Google Chrome" {
            $DisplayName = "Chrome"
            $urlID = "ChromeHTML"
            $htmlID = "ChromeHTML"
        }
        "Microsoft Edge" {
            $DisplayName = "Edge"
            $urlID = "MSEdgeHTM"
            $htmlID = "MSEdgeHTM"
        }
        "Mozilla Firefox" {
            $DisplayName = "Firefox"
            $urlID = "FirefoxURL-308046B0AF4A39CB"
            $htmlID = "FirefoxHTML-308046B0AF4A39CB"
        }
        default {
            Write-Host "[Error] Only the following browsers can be made the default. 'Google Chrome','Microsoft Edge' or 'Mozilla Firefox'."
            exit 1
        }
    }

    if (-not (Find-UninstallKey -DisplayName "$DisplayName")) {
        Write-Host "[Error] $Browser is not installed. Please ensure it's installed System-Wide prior to running this script."
        exit 1
    }

    $UserProfiles = Get-UserHives -Type "All"
    # Loop through each profile on the machine
    Foreach ($UserProfile in $UserProfiles) {
        # Load User ntuser.dat if it's not already loaded
        If (($ProfileWasLoaded = Test-Path Registry::HKEY_USERS\$($UserProfile.SID)) -eq $false) {
            Start-Process -FilePath "cmd.exe" -ArgumentList "/C reg.exe LOAD HKU\$($UserProfile.SID) `"$($UserProfile.UserHive)`"" -Wait -WindowStyle Hidden
        }

        # The hex date and user experience don't really change
        $userExperience = "User Choice set via Windows User Experience {D18B6DD5-6124-4341-9318-804003BAFA0B}"
        $hexDateTime = Get-HexDateTime


        Write-Host "`nSetting default browser of $Browser for $($UserProfile.UserName)."

        # Set protocol association registry keys
        $Protocols | ForEach-Object {
            Write-Host "Setting "
            $Protocol = $_

            $ToBeHashed = "$Protocol$($UserProfile.SID)$urlID$hexDateTime$userExperience".ToLower()
            $Hash = Get-Hash -BaseInfo $ToBeHashed

            Set-RegKey -Path "Registry::HKEY_USERS\$($UserProfile.SID)\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\$Protocol\UserChoice" -Name "Hash" -Value $Hash -PropertyType String
            Set-RegKey -Path "Registry::HKEY_USERS\$($UserProfile.SID)\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\$Protocol\UserChoice" -Name "ProgId" -Value $urlID -PropertyType String
        }

        # Set file association registry keys
        $Files | ForEach-Object {
            Write-Host "Setting "
            $File = $_

            $ToBeHashed = ".$File$($UserProfile.SID)$htmlID$hexDateTime$userExperience".ToLower()
            $Hash = Get-Hash -BaseInfo $ToBeHashed

            Set-RegKey -Path "Registry::HKEY_USERS\$($UserProfile.SID)\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\$File\UserChoice" -Name "Hash" -Value $Hash -PropertyType String
            Set-RegKey -Path "Registry::HKEY_USERS\$($UserProfile.SID)\Software\Microsoft\Windows\Shell\Associations\UrlAssociations\$File\UserChoice" -Name "ProgId" -Value $htmlID -PropertyType String
            Set-RegKey -Path "Registry::HKEY_USERS\$($UserProfile.SID)\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.$File\UserChoice" -Name "Hash" -Value $Hash -PropertyType String
            Set-RegKey -Path "Registry::HKEY_USERS\$($UserProfile.SID)\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.$File\UserChoice" -Name "ProgId" -Value $htmlID -PropertyType String
        }
            
        # Unload NTuser.dat
        If ($ProfileWasLoaded -eq $false) {
            [gc]::Collect()
            Start-Sleep 1
            Start-Process -FilePath "cmd.exe" -ArgumentList "/C reg.exe UNLOAD HKU\$($UserProfile.SID)" -Wait -WindowStyle Hidden | Out-Null
        }
    }

    # Restart explorer if requested
    if ($RestartExplorer) {
        Write-Host "`nRestarting Explorer.exe as requested."

        # Stop all instances of Explorer
        Get-Process explorer | Stop-Process -Force
        
        Start-Sleep -Seconds 1

        # Restart Explorer if not running as System and Explorer is not already running
        if (!(Test-IsSystem) -and !(Get-Process -Name "explorer")) {
            Start-Process explorer.exe
        }
    }
}
end {
    
    
    
}

 

Analisi dettagliata

Lo script funziona interagendo con il registro di Windows, in particolare prendendo di mira gli hive del registro specifici dell’utente che memorizzano le impostazioni predefinite delle applicazioni. Ecco una descrizione passo per passo di come funziona lo script:

1. Inizializzazione dello script:

  • Lo script inizia verificando se viene eseguito con privilegi elevati (accesso come amministratore). In caso contrario, lo script si interrompe, garantendo che solo il personale autorizzato possa apportare modifiche a livello di sistema.

2. Gestione dei parametri:

  • Vengono accettati due parametri: il browser da impostare come predefinito e uno switch per riavviare il processo Explorer.exe. Il parametro del browser accetta “Mozilla Firefox”, “Google Chrome” o “Microsoft Edge” e sceglie come impostazione predefinita quella della variabile d’ambiente, se non viene fornita direttamente.

3. Convalida del browser:

  • Prima di procedere, lo script controlla se il browser specificato è installato sul sistema cercando nel registro di sistema la sua chiave di disinstallazione. Se il browser non è installato, lo script mostra un messaggio di errore.

4. Gestione dei profili:

  • Lo script recupera un elenco di tutti i profili utente presenti sul computer esaminando il registro di sistema alla ricerca di impostazioni specifiche dell’utente. Identifica il SID (Security Identifier) di ogni utente e carica l’hive del registro di sistema per modificare le impostazioni predefinite del browser.

5. Impostazione del protocollo e delle associazioni di file predefiniti:

  • Per ogni profilo utente, lo script modifica il registro per impostare il browser selezionato come gestore predefinito per i tipi di file HTTP, HTTPS, HTM, HTML e XHTML. Ciò comporta la generazione di un hash univoco per le impostazioni, per garantire che siano riconosciute da Windows come valide e sicure.

6. Riavviare Explorer (facoltativo):

  • Se si utilizza l’opzione -RestartExplorer, lo script si interrompe e riavvia il processo Explorer.exe. Questo passaggio è necessario per applicare immediatamente le modifiche e aggiornare le icone del desktop associate ai tipi di file interessati.

Casi d’uso potenziali

Immagina un amministratore IT che gestisce una grande rete aziendale con centinaia di utenti. L’azienda ha recentemente scelto Firefox come browser predefinito grazie alle sue migliori caratteristiche di sicurezza.

Invece di impostare manualmente Firefox come browser predefinito sul profilo di ogni utente, l’amministratore può eseguire questo script in tutta la rete, assicurando che ogni utente, indipendentemente dalla frequenza di accesso o dallo stato del profilo, utilizzi Firefox per impostazione predefinita. In questo modo non solo si risparmia tempo, ma si riduce anche la probabilità di errori da parte degli utenti o di non conformità alle policy aziendali.

Confronti

Sebbene questo script PowerShell offra un approccio semplificato, esistono altri metodi per ottenere lo stesso risultato, come l’utilizzo dei Criteri di gruppo o la distribuzione di uno strumento di gestione dei software. I Criteri di gruppo offrono un modo più centralizzato per applicare le impostazioni del browser, ma possono essere complessi da configurare e potrebbero non coprire tutti gli scenari, specialmente con i profili specifici dell’utente.

Gli strumenti di gestione dei software, invece, spesso comportano costi aggiuntivi e richiedono una curva di apprendimento. Questo script raggiunge un equilibrio offrendo una soluzione personalizzabile e senza costi che si integra direttamente nell’ambiente Windows esistente.

Domande frequenti

D: Questo script può essere utilizzato su sistemi con versioni di Windows precedenti a Windows 10?

R: Lo script è progettato per Windows 10 e versioni successive. Utilizza strutture di registro e protocolli specifici che potrebbero non essere disponibili nelle versioni precedenti di Windows.

D: Cosa succede se un utente installa un nuovo browser dopo l’esecuzione dello script?

R: Se viene installato un nuovo browser dopo l’esecuzione dello script, questo potrebbe tentare di impostarsi come predefinito. In questi casi, eseguendo nuovamente lo script si riapplicheranno le impostazioni desiderate.

D: È possibile modificare lo script per includere altri browser non elencati nei parametri?

R: Sì, lo script può essere adattato per supportare altri browser aggiungendo i rispettivi identificatori di protocollo e di gestore di file.

Implicazioni

Utilizzando questo script, i professionisti IT possono imporre la coerenza del browser in tutti i profili utente, un aspetto importante per la sicurezza e la conformità. Tuttavia, gli amministratori devono essere consapevoli delle potenziali implicazioni, come l’annullamento delle preferenze degli utenti e la necessità di assicurarsi che tutti i browser necessari siano installati a livello di sistema prima di eseguire lo script.

Raccomandazioni

  • Testa in ambiente controllato: Prima di distribuire questo script su un’intera rete, è consigliabile testarlo in un ambiente controllato per assicurarti che si comporti come previsto.
  • Assicurati che tutti i browser siano installati: Controlla che il browser predefinito selezionato sia installato su tutti i sistemi in cui verrà eseguito lo script.
  • Considera la comunicazione con gli utenti: Informa gli utenti della modifica, in particolare se le preferenze predefinite del browser verranno ignorate.

Considerazioni finali

Per i professionisti IT e gli MSP, la gestione efficiente degli ambienti utente è fondamentale. Questo script PowerShell offre una soluzione efficace per standardizzare il browser predefinito per tutti i profili utente di un sistema Windows. Sebbene NinjaOne fornisca strumenti completi per la gestione degli endpoint, tra cui la distribuzione dei software e la gestione dei profili utente, questo script può essere un eccellente complemento, offrendo un controllo dettagliato sulle impostazioni del browser.

Passi successivi

La creazione di un team IT efficiente ed efficace richiede una soluzione centralizzata che funga da principale strumento per la fornitura di servizi. NinjaOne consente ai team IT di monitorare, gestire, proteggere e supportare tutti i dispositivi, ovunque essi si trovino, senza la necessità di una complessa infrastruttura locale.

Per saperne di più su NinjaOne Endpoint Management, fai un tour dal vivo, o inizia la tua prova gratuita della piattaforma NinjaOne.

Categorie:

Ti potrebbe interessare anche

×

Guarda NinjaOne in azione!

Inviando questo modulo, accetto La politica sulla privacy di NinjaOne.

Termini e condizioni NinjaOne

Cliccando sul pulsante “Accetto” qui sotto, dichiari di accettare i seguenti termini legali e le nostre condizioni d’uso:

  • Diritti di proprietà: NinjaOne possiede e continuerà a possedere tutti i diritti, i titoli e gli interessi relativi allo script (compreso il copyright). NinjaOne ti concede una licenza limitata per l’utilizzo dello script in conformità con i presenti termini legali.
  • Limitazione d’uso: Puoi utilizzare lo script solo per legittimi scopi personali o aziendali interni e non puoi condividere lo script con altri soggetti.
  • Divieto di ripubblicazione: In nessun caso ti è consentito ripubblicare lo script in una libreria di script appartenente o sotto il controllo di un altro fornitore di software.
  • Esclusione di garanzia: Lo script viene fornito “così com’è” e “come disponibile”, senza garanzie di alcun tipo. NinjaOne non promette né garantisce che lo script sia privo di difetti o che soddisfi le tue esigenze o aspettative specifiche.
  • Assunzione del rischio: L’uso che farai dello script è da intendersi a tuo rischio. Riconosci che l’utilizzo dello script comporta alcuni rischi intrinseci, che comprendi e sei pronto ad assumerti.
  • Rinuncia e liberatoria: Non riterrai NinjaOne responsabile di eventuali conseguenze negative o indesiderate derivanti dall’uso dello script e rinuncerai a qualsiasi diritto legale o di equità e a qualsiasi rivalsa nei confronti di NinjaOne in relazione all’uso dello script.
  • EULA: Se sei un cliente NinjaOne, l’uso dello script è soggetto al Contratto di licenza con l’utente finale (EULA) applicabile.