Configuring Firefox via policy (registry)

Firefox supports a JSON configuration file on all platforms. If you’re only working with Firefox and are dealing with a number of different platforms, this would probably be the preferred way to go about setting policy, since it’s pretty easy to use.

However, since I’m primarily dealing with Windows desktops, we’ll just use the registry. Mozilla’s docs list the registry path as “GPO” for some reason, but they do show the registry path for all of their policies, so yay. Firefox policy works the same way as Chromium. Extensions are a bit different, but we’ll get to that.

Mozilla’s policy docs can be found here.

Policies of interest

You can find a sample implementation of these policies in my BrowserPowerShell git repository. See the “ConfigureFirefox.ps1” script.

Trust system root certificates

Automatically update Firefox

Enable SSO with Entra credentials stored in Windows

Misc. features to disable

Disable the browser’s password manager

Hide nags and splash screens

Remove advertisements

Security and privacy options

Cookies

Tracking Protection

DoH

Safe Browsing

Installing extensions

Mozilla docs here.

Firefox’s ForceInstallList is a JSON object at Policies\ExtensionSettings. Here is the Mozilla example:

{
  "*": {
    "blocked_install_message": "Custom error message.",
    "install_sources": ["https://yourwebsite.com/*"],
    "installation_mode": "blocked",
    "allowed_types": ["extension"]
  },
  "uBlock0@raymondhill.net": {
    "installation_mode": "force_installed",
    "install_url": "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi"
  },
  "adguardadblocker@adguard.com": {
    "installation_mode": "force_installed",
    "install_url": "https://addons.mozilla.org/firefox/downloads/latest/adguardadblocker@adguard.com/latest.xpi"
  },
  "https-everywhere@eff.org": {
    "installation_mode": "allowed",
    "updates_disabled": false
  }
}

We can import it to a PSObject, make our changes, then export it again:

PS C:\Users\liam\projects\BrowserPowerShell> $String = @"
>> {
>>   "*": {
>>     "blocked_install_message": "Custom error message.",
>>     "install_sources": ["https://yourwebsite.com/*"],
>>     "installation_mode": "blocked",
>>     "allowed_types": ["extension"]
>>   },
>>   "uBlock0@raymondhill.net": {
>>     "installation_mode": "force_installed",
>>     "install_url": "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi"
>>   },
>>   "adguardadblocker@adguard.com": {
>>     "installation_mode": "force_installed",
>>     "install_url": "https://addons.mozilla.org/firefox/downloads/latest/adguardadblocker@adguard.com/latest.xpi"
>>   },
>>   "https-everywhere@eff.org": {
>>     "installation_mode": "allowed",
>>     "updates_disabled": false
>>   }
>> }
>> "@
PS C:\Users\liam\projects\BrowserPowerShell> $ExtensionSettings = $String | ConvertFrom-Json
PS C:\Users\liam\projects\BrowserPowerShell> $ExtensionSettings | fl

*                            : @{blocked_install_message=Custom error message.; install_sources=System.Object[];
                               installation_mode=blocked; allowed_types=System.Object[]}
uBlock0@raymondhill.net      : @{installation_mode=force_installed;
                               install_url=https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi}
adguardadblocker@adguard.com : @{installation_mode=force_installed;
                               install_url=https://addons.mozilla.org/firefox/downloads/latest/adguardadblocker@adguard.com/latest.xpi}
https-everywhere@eff.org     : @{installation_mode=allowed; updates_disabled=False}

PS C:\Users\liam\projects\BrowserPowerShell> $ExtensionSettings.'uBlock0@raymondhill.net'

installation_mode install_url
----------------- -----------
force_installed   https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi

This process will become checking for existence, verifying the installation_mode is set correctly, and verifying that the install_url is set correctly. I was lazy, so I just nuke the extension’s settings and start over, since it’s easier and I don’t care about Firefox.

We’re also converting everything to hashtables because PSObjects are slightly annoying and it’s past my bedtime.

You must specify a full install_url for a Firefox extension, not just the ID. You can find IDs in about:debugging once you’ve installed the extension in question.

Here is my lazy setter to force install Firefox extensions:

function Set-GeckoExtension {
  param (
    [Parameter(Mandatory)]
    [string]$ExtensionId
    ,
    [Parameter(Mandatory)]
    [ValidateSet(
      'allowed',
      'blocked',
      'force_installed',
      'normal_installed'
    )]
    [string]$InstallationMode
    ,
    [string]$InstallUrl = $null
  )

  $PolicyPath = "HKLM:\SOFTWARE\Policies\Mozilla\Firefox"
  $ValueName = "ExtensionSettings"

  # Ensure registry path exists
  if (-not (Test-Path $PolicyPath)) {
    New-Item -Path $PolicyPath -Force | Out-Null
  }

  # Read existing JSON from registry property

  $ExistingJson = (Get-ItemProperty -Path $PolicyPath -Name $ValueName).$ValueName

  # Attempt to deserialize. Create a new object if we don't have valid JSON at ExtensionSettings.
  try {

    if ($ExistingJson) {

      $Obj = $ExistingJson | ConvertFrom-Json -ErrorAction Stop

      # convert PSObjects from JSON to hashtables
      $Settings = @{}
      foreach ($Key in $Obj.PSObject.Properties.Name) {

        $Settings[$Key] = @{}
        
        foreach ($Prop in $Obj.$Key.PSObject.Properties.Name) {
          $Settings[$Key][$Prop] = $Obj.$Key.$Prop
        } # foreach

      } # foreach

    } # if
    else { $Settings = @{} } # if no existing JSON, use an empty hashtable

  } # try
  catch {

    Write-Warning "Existing ExtensionSettings do not contain valid JSON. Starting over."
    $Settings = @{}

  }

  # For simplicity, recreate the extension object with desired parameters

  $Settings[$ExtensionId] = @{
    installation_mode = $InstallationMode
  }
  if ($InstallationMode -eq "force_installed" -and $InstallUrl) {

    $Settings[$ExtensionId].install_url = $InstallUrl

  } else {

    Write-Warning "Extension $($ExtensionId) is being force_installed, but no install URL was specified. Installation will fail!"

  }

  $Json = $Settings | ConvertTo-Json -Depth 99 -Compress

  Set-ItemProperty -Path $PolicyPath -Name $ValueName -Value $Json

  Write-Host "Extension settings updated for '$($ExtensionId)'."

}

This, too, is in the BrowserPowerShell repository, which you can find here.