Although this is technically a blog, it's primary content is a series of articles on how to get Firefox working in a corporate Windows environment. Later ones build on earlier ones, so you might want to use the Table of Contents on the right to read through it chronologically instead of reading straight down from here.

Opening Local Files from Firefox

So you have an intranet which has links to files on a share drive using the file: protocol, like file:///G:/Company Policy.doc.  Internet Explorer opens these files fine, but Firefox does nothing.  You could add your intranet to IE Tab Plus if you wanted to, but there should be a way to make Firefox do this right?  No need to switch out browser engines unnecessarily right?  There is:
If you're using my script, add these three lines to firefoxSettings:
var firefoxSettings = new Array(
    "capability.policy.policynames", "localfilelinks",
    "capability.policy.localfilelinks.sites", "http://intranet",
    "capability.policy.localfilelinks.checkloaduri.enabled", "allAccess"
    );
Change http://intranet to your own URL.  localfilelinks.sites is a space separated list so you can have more than one if you need them.

The Switch Has Been Thrown

So I'm not waiting for the reimaging to complete after all.  I made another Firefox package with SetDefaultBrowser turned on and the desktop icon enabled, set it to push out, and turned off the IE desktop icon via group policy.  Every computer will switch to Firefox being the default browser the next time they're rebooted.  You're welcome internet.

I have to say using IE Tab Plus made me much more confident in this.  We do have at least one major IE-only site and having it Just Work for users automatically and transparently is a major plus.  I don't have to send out e-mail explaining how that one has to be visited differently and get the inevitable calls from the people who forget.  It Just Works.  Less hassle for them, less hassle for me.

Dealing With Those Pesky IE-Only Sites (IE Tab Plus vs. IE View)

Unfortunately IE-only sites seem to be unavoidable.  However, we can use extensions to help make this a manageable problem.  There are two we're going to be looking at which use slightly different approaches.  Both of them can be configured through user.js so you can use my script to manage the settings centrally, including the rules that determine which sites require IE.

The first is IE Tab Plus:
  • PRO: Provides a seamless user experience.  Flagged sites are loaded in Firefox.  It looks and (mostly) behaves like a regular Firefox tab, but the contents are actually rendered in IE.
  • PRO: Provides cookie syncing between the browsers, though I haven't tested this.
  • PRO: Uses regular expressions to flag sites, which are more powerful.
  • CON: A tab can become stuck in IE mode depending on how you browse.  If you go to an IE site and then click on a bookmark or type in an URL, you'll switch back to Firefox, but if you keep browsing by clicking links on the page, you'll stay in IE even after leaving the flagged site.
    UPDATE: Turns out you can use the "Switch Back to Firefox in New Page" option to fix this, but that prevented a login from working at an important IE-only site so I'm not going to trust this feature.
  • CON: Can't access IE settings while browsing in IE mode.  If you need to change the security zone you're out of luck.
  • CON: Minor unexpected behaviors.  If you right click on a link to try to bookmark it, it will go to IE's favorites instead.  If you use the star on the URL bar or the Bookmarks menu, it will work correctly.
  • CON: Uses regular expressions to flag sites, which could be confusing for people not familiar with them.
The other is IE View:
  • PRO: Flagged sites load in a separate IE window.  No confusion about how a site is being rendered or side effects from mixing browser UIs.
  • PRO: Can access all IE settings during IE browsing.
  • PRO: Sites are flagged by the beginning of the URL, which is more straightforward.
  • CON: Flagged sites launching a separate window with a completely different UI is a jarring user experience.  Users won't have access to Firefox bookmarks unless they go back to a Firefox window.
  • CON: The simpler rule system means you need a separate rule for URLs with "www" and without, for http and https, for each subdomain, and all combinations thereof.
I'm going with IE Tab Plus for the better user experience, though with some mild trepidation.  Fortunately it provides an icon on the status bar to let you know which engine it's using and to easily switch between the two, which will make troubleshooting easier.  I'm also a programmer so regular expressions don't faze me.

Whichever way you go, use this article to make the extension an MSI and this article to configure it via the login script.  I've updated the script so all configuration keys are at the beginning and added support specifically for these two extensions so configuring the rule lists is easier.


Configuring IE Tab Plus

IE Tab Plus requires three settings be defined in user.js to not pop up a configuration screen.  Add these three lines to firefoxSettings in the login script:
var firefoxSettings = new Array(
   "coral.ietab.mode", "classic",
   "coral.ietab.version", "99.99",
   "coral.ietab.is1209SelectionDone", true
   );
If mode isn't set it will pop up the configuration dialog.  The advanced mode supports cookie and AdBlock sync but I'm not getting into those.  If version isn't set or is lower than the extension version, it will open up a tab of the changelog the first time it starts.  Fortunately we can set it higher than the extension version with no ill effects so we don't have to worry about changing this with every update.  At some point they added a new price comparison feature and I guess they wanted to pop up again so old users can see it, so setting is1209SelectionDone makes it think they already have.

There's also a separate variable ieTabRules which is where you put the regular expressions.  The login script will convert them into a coral.ietab.rulelist setting for you.
var ieTabRules = new Array(
   "http://*update.microsoft.com/*",
   "http://www.windowsupdate.com/*"
   );
If you want to configure any other settings go to about:config in your browser, look at what's available under coral.ietab, and add what you want to firefoxSettings.


Configuring IE View

IE View doesn't require any settings be configured other than the rules, so you can just enter them in ieViewURLs and the login script will convert them into an ieview.forceielist setting for you.  Remember that you have to include all permutations of their beginnings.
var ieViewURLs = new Array(
   "http://www.somesite.com",
   "http://somesite.com",
   "https://www.somesite.com",
   "https://somesite.com"
   );
If you want to configure any other settings go to about:config in your browser, look at what's available under ieview, and add what you want to firefoxSettings.

Watching Mozilla's Progress

If you want to follow Mozilla's progress towards creating its own MSIs and supporting group policy, there's two pages you can follow:
As of this writing the MSI feature appears to be in active development.  Patches are being submitted and reviewed, so it looks like we'll get official Firefox MSIs at some point in the not too distant future.

The group policy feature, on the other hand, doesn't have much going on.  It looks like FrontMotion is the best we're going to get for now.

AutoConfiguring XMarks for the Logged In User (v3.6)

(These are the instructions for Firefox 3.6 and XMarks BYOS 1.0.  If you want the newest instructions, go here.)

This is what it's all been leading up to.  This is what I started this blog for, because I couldn't find anyone else online who accomplished it.

So let's back up.  I wanted to roll out Firefox as the default browser in my organization.  So that means pushing it out via Active Directory so I can keep it updated.  If I just installed a plain vanilla copy on my image it would quickly go out of date.

However, I also want people's important settings to follow them from computer to computer.  I consider bookmarks to be in that category.  You can do this with roaming profiles, but after seeing how slow this was on someone else's setup I've been determined to avoid that.

You can redirect Internet Explorer's Favorites folder by changing two registry keys with a login script:
  • HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\Favorites
  • HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders\Favorites
But Firefox doesn't have something similar, which has held it back until now.  There's a bookmark syncing extension called XMarks, and you can even sync to a private server, but I don't want people to have to configure it themselves.  They're not going to remember the settings if I use a private server, and most aren't going to bother setting up an XMarks account if I don't.  Plus I've found it's always, always worth the extra effort to have things Just Work™.  It's a better experience for them, and it saves you trouble in the long run.  So here we go.


Set up a WebDAV Server

You need a server for XMarks to sync to, and it supports both WebDAV and FTP.  They recommend WebDAV so that's what we're using.

I have a Windows Server 2003 machine I can add it to, so the rest of the instructions in this section are for IIS 6.  You can use Apache or whatever else you want but you're on your own.  This page might help though.

Add IIS and WebDAV to the server if they're not already installed.  Go to Control Panel > Add or Remove Programs > Add/Remove Windows Components > Application Server > Internet Information Services (IIS) > World Wide Web Service and check off Word Wide Web Service and WebDAV Publishing.

Now go to Administrative Tools > Internet Information Services (IIS) Manager, find your web site (Default Web Site or one you created if you already had it installed) and go into its Properties.

I personally changed the TCP port to 8082 and made a DNS alias "FirefoxBookmarks" to the server so I can move it around if need be, but that's not required.

Go to the Home Directory tab and make sure Read and Write are checked off.  You also want to go to the folder it shows under Local Path and make sure Everyone has read and write NTFS permissions on them (right click the folder in Explorer > Properties > Security.)

Go to the HTTP Headers tab and click MIME Types.  Add .json as text/css.

You should be all set on this part.  Make sure syncing works with a manually installed and configured copy of XMarks BYOS before continuing.


Pushing Out the Configuration

Hey, remember the script from this post?  Yeah, go get that.  Change the webDAVURL variable to point to the server you just set up.  You can run it manually on your own computer until you're sure it's working correctly, but when you roll this out you want it to be a login script in Active Directory.

These are the settings it creates for you in user.js:
  • extensions.xmarksbyos.syncOnShutdownAsk = false
    Remember how I like things to Just Work™?  Well part of that is not risking the server's copy going out of date so they're wondering where their new bookmarks are, so I force the shutdown sync.  It only takes a second or two, and this way they don't have an unnecessary popup asking them about it either.
  • extensions.xmarksbyos.url-bookmarks = "[webDAVURL]/[username]-bookmarks.json"
    This is where their bookmarks are going to be saved.
  • extensions.xmarksbyos.url-passwords = "[webDAVURL]/[username]-passwords.json"
    This is where their passwords will be saved if they set it up.  I didn't bother trying to make it happen automatically.
  • extensions.xmarksbyos.username = "[username]"
    Don't know if it's strictly necessary, but we want XMarks to think it's fully configured so it doesn't open the setup window.
We still have the little problem of the password.  It isn't stored in user.js so we can't push it out that way.  It isn't required by WebDAV so we don't even need it, but XMarks thinks it does and will pop up the setup window without it.  Grr.

So it's time to hack XMarks.


Editing XMarks BYOS

So if you haven't caught on by now, this is all based on XMarks BYOS, a stripped down version of XMarks that lets you use your own server and doesn't have the extra fluff like ratings that I didn't want anyway.  Go get the XPI and unpack it as described in this article.  Use those instructions to check install.rdf too since it might have the maxVersion issue described there.

We need to make two edits.  The first is in chrome\content\foxmarks-settings.js.  Find "get passwordNoPrompt()" and add "return "ignore";" as the first line so it looks like this:


This makes XMarks think it always has a password.  Now it thinks it's fully configured and won't pop up a settings dialog, but thanks to some quirk in the code (or lets be honest, our hacking, since I'm not hugely familiar with XMarks' internals) it won't actually start syncing automatically.  If you perform a manual sync it will succeed and then start syncing on its own, but it won't start on it's own without that kick start.

So we have another edit to make.  In chrome\content\foxmarks-statusbar.js find "function StatusBarLoad()" and add "foxmarks.synchronize(true);" right before the return statement so it looks like this:


Now when the status bar widget loads, it will force a background sync (not the pop-up window kind.)  This successfully kick starts the process and it will now sync automatically on its own.  As a bonus it forces it to happen as soon as Firefox is opened, which is nice if someone's on a different computer as they don't have to wait for their bookmarks to show up.

(Why StatusBarLoad()?  I could force a sync in a number of places but it was always the one that pops up the syncing window.  I was looking at the status bar code while trying to examine the background sync mechanism and managed to do this.  And when you get something that works you stop looking, right?  So status bar code it is, just because it works.)

So now with those two changes, plus the change to install.rdf if you needed it, you can now package and push out your edited copy of XMarks BYOS as an MSI using the rest of the instructions here.

Hey, couldn't I have just done all this for you and let you download the pre-hacked MSI?  No.  XMarks BYOS isn't open source as far as I know, so I can't redistribute it.  I can just give you instructions on how to do it yourself.


That's It!

So now you're where I am.  You can push out Firefox, Flash, and XMarks BYOS via Active Directory.  Also PlainOldFavorites or any other extensions you want to add.  XMarks will autoconfig thanks to our generated user.js and a couple of code hacks.  Everything will sync to our internal WebDAV server, and everything will be transparent to the end user.  No configuration or pop-ups, they just get their bookmarks on any computer automatically.  It took a lot of effort (I had to figure all this out, you just had to read it!) but it's always beautiful when something Just Works™.

Where to from here?  Well in my organization I haven't actually made Firefox the default browser yet.  That's going to come as part of the reimaging I'll be doing very soon.  However I did push out this entire system ahead of time so the people who were already using Firefox would have their bookmarks all synced up and safe beforehand.  No issues so far.

As we transition to Firefox being the default browser and spend some time living with it I'll post any other issues and workarounds I come across.  I didn't find anything else like this on the web so this is my contribution to helping people use Firefox at work.

Configuring user.js From a Login Script

Now we need to be able to push out settings to Firefox from the server.  There are two ways to do this.

The first is to use FrontMotion Firefox Community Edition, as that bakes in support for group policy.  Frankly, that's a superior way to handle the built in settings because my method has some bad side effects.  One is that it only changes on login as opposed to whenever the policies refresh, and two is that it removes the ability to import IE favorites the first time Firefox starts.

Why do I use my way anyway?  Because I need to be able to configure the extensions centrally as well, not just Firefox.  PlainOldFavorites adequately takes care of the IE import issue.

The script below is used to configure XMarks via user.js, which is what this is all leading up to.  You can easily strip out the XMarks settings and substitute your own.  Save it as a JS file (i.e. SetupFirefox.js) and set it as a login script via group policy.

Here's a walkthrough of what it does:
  • Finds Firefox since it can be in a few possible locations.
  • Gets the path of every existing Firefox profile for the logged in user, assuming all are in the default location.
  • If there aren't any profiles, creates one.  This is important because Firefox uses randomized profile folder names.  If the user has never opened Firefox yet, we can't generate user.js for it because we don't know where it should go.  We don't want their first use of Firefox to be unconfigured until their next login, so we use "firefox.exe -CreateProfile" to force it.  This is invisible to the user, a Firefox window won't open.  However, it does prevent the IE favorites import dialog from appearing on the first open which is why we needed PlainOldFavorites.
  • Generates user.js for all profiles, new or preexisting.  Any existing user.js will be overwritten.
  • Optionally copies extensions from their install location to the user profile if that's required for the extension to work.
var fs = new ActiveXObject("Scripting.FileSystemObject")
var network = new ActiveXObject("WScript.Network");

var shell = new ActiveXObject("WScript.Shell");
var env = shell.Environment("PROCESS")

var userName = network.UserName.toLowerCase();
var computerName = network.ComputerName.toLowerCase();



// Section: Configuration
// ___________________________________________________________________________
//
// Remember that any backslashes in strings have to be doubled, such as
// "C:\\Folder1\\Folder2" or "\\\\ServerName\\Share".


/* var: firefoxLocations
   An array of possible places to find Firefox.exe.  Add more if you need them.
*/
var firefoxLocations = new Array(
   "C:\\Program Files\\Mozilla Firefox\\firefox.exe",
   "C:\\Program Files (x86)\\Mozilla Firefox\\firefox.exe"
   );


/* var: webDAVURL
   The URL of your WebDAV server that XMarks is going to sync with.  Don't
   include a slash on the end.
*/
var webDAVURL = "http://firefoxbookmarks:8082";


/* var: firefoxSettings
   An array of key-value pairs specifying the settings you want included in
   user.js.  For example:

   : var firefoxSettings = new Array(
   :    "sample.boolean.setting", false,
   :    "sample.integer.setting", 0,
   :    "sample.string.setting", "value"
   :    );
*/
var firefoxSettings = new Array(
   "extensions.xmarksbyos.syncOnShutdownAsk", false,
   "extensions.xmarksbyos.url-bookmarks", webDAVURL + "/" + userName + "-bookmarks.json",
   "extensions.xmarksbyos.url-passwords", webDAVURL + "/" + userName + "-passwords.json",
   "extensions.xmarksbyos.username", userName,

   "extensions.xmarks.syncOnShutdown", 1,
   "extensions.xmarks.syncOnShutdownAsk", false,
   "extensions.xmarks.useOwnServer", true,
   "extensions.xmarks.url-bookmarks", webDAVURL + "/" + userName + "-bookmarks.json",
   "extensions.xmarks.url-passwords", webDAVURL + "/" + userName + "-passwords.json",
   "extensions.xmarks.username", userName
);


/* var: ieTabRules
   An array of *regular expressions* to apply as filters with the IE Tab Plus
   extension.  If defined, it will create the coral.ietab.rulelist entry in
   firefoxSettings for you automatically.  Remember to double any backslashes.
*/
var ieTabRules = new Array(
   "http://*update.microsoft.com/*",
   "http://www.windowsupdate.com/*"
   );


/* var: ieViewURLs
   An array of URL prefixes to force to open in IE with the IE View extension.
   If defined, it will create the ieview.forceielist entry in firefoxSettings
   for you automatically.  These are simple strings instead of regular 
   expressions which may be easier to use.  You'll need separate entries for
   URLs with and without "www.", and for URLs under http and https.
*/
var ieViewURLs = new Array(
   );


/* var: copyExtensions
   Some extensions need to be in the profile directory to work.  Sigh.  This
   is an array of value pairs where the first is the directory where the
   extension resides as installed by the MSI, and the second is the name of
   the subfolder in Firefox\Profiles\xxx\extensions they should be copied to.
   For example:

   : var copyExtensions = new Array(
   :    "C:\\Program Files\\PlainOldFavorites", "{7E7165E2-0767-448c-852F-5FA8714F2C37}"
   :    );

   The contents will be copied over on each run.
*/
var copyExtensions = new Array(
   );



// Section: Code
// ____________________________________________________________________________
//
// You don't have to worry about anything below this point.


// Find Firefox

var firefoxLocation = undefined;

for (var i = 0; i < firefoxLocations.length; i++)
   {
   if (fs.FileExists(firefoxLocations[i]))
      {  
      firefoxLocation = firefoxLocations[i];
      break;
      }
   }

if (firefoxLocation === undefined)
   {  WScript.Quit();  }



// Create profile parent folder if it doesn't exist

var profileParentFolder = env.Item("appdata") + "\\Mozilla\\Firefox\\Profiles";

if (!fs.FolderExists(profileParentFolder))
   {  
   var pathSoFar = env.Item("appdata");
   var pathPieces = new Array("Mozilla", "Firefox", "Profiles");

   for (var i = 0; i < pathPieces.length; i++)
      {
      pathSoFar += "\\" + pathPieces[i];

      if (!fs.FolderExists(pathSoFar))
         {  fs.CreateFolder(pathSoFar);  }
      }
   }


// Get all the existing profile folder paths

var profileParentFolderObject = fs.GetFolder(profileParentFolder);

var profileFolders = new Array();
var profileEnum = new Enumerator(profileParentFolderObject.SubFolders);

for (; !profileEnum.atEnd(); profileEnum.moveNext())
   {
   profileFolders.push(profileParentFolder + "\\" + profileEnum.item().Name);
   }


// Create a profile if there isn't one already.

if (profileFolders.length == 0)
   {
   // 5 - Activate the window, leave its size alone
   // true - Wait for the command to finish to return.
   shell.Run("\"" + firefoxLocation + "\" -CreateProfile \"Default " + profileParentFolder + "\\Default\"", 5, true);
   profileFolders.push(profileParentFolder + "\\Default");
   }


// Generate rule lists if ieTabRules are defined.

if (ieTabRules != undefined && ieTabRules.length > 0)
   {
   // coral.ietab.rulelist

   var ruleList = "[";

   for (var i = 0; i < ieTabRules.length; i++)
      {
      if (i > 0)
         {  ruleList += ",";  }

      ruleList += "\\\"0," + ieTabRules[i] + "\\\"";
      }

   ruleList += "]";

   firefoxSettings.push("coral.ietab.rulelist", ruleList);


   // extensions.ietab2.filterlist

   var ruleList = "";

   for (var i = 0; i < ieTabRules.length; i++)
      {
      if (i > 0)
         {  ruleList += " ";  }

      ruleList += ieTabRules[i];
      }

   firefoxSettings.push("extensions.ietab2.filterlist", ruleList);
   }


// Generate ieview.forceielist if ieViewURLs are defined.

if (ieViewURLs != undefined && ieViewURLs.length > 0)
   {
   var forceList = "";

   for (var i = 0; i < ieViewURLs.length; i++)
      {
      if (i > 0)
         {  forceList += " ";  }

      forceList += ieViewURLs[i];
      }

   firefoxSettings.push("ieview.forceielist", forceList);
   }


// Generate user.js for all profiles

for (var i = 0; i < profileFolders.length; i++)
   {
   // true - overwrite if exists
   var userFile = fs.CreateTextFile(profileFolders[i] + "\\user.js", true);

   for (var s = 0; s < firefoxSettings.length; s += 2)
      {
      var settingLine = "user_pref(\"" + firefoxSettings[s] + "\", ";
      
      if (typeof(firefoxSettings[s+1]) == "string")
         {  settingLine += "\"" + firefoxSettings[s+1] + "\");";  }
      else if (typeof(firefoxSettings[s+1]) == "number" || typeof(firefoxSettings[s+1]) == "boolean")
         {  settingLine += firefoxSettings[s+1] + ");";  }
      else
         {  settingLine = "// Can't create setting \"" + firefoxSettings[s] + "\" for type " + typeof(firefoxSettings[s+1]) + ".";  }
      
      userFile.WriteLine(settingLine);
      }

   userFile.Close();

   if (copyExtensions != undefined && copyExtensions.length > 0)
      {
      var profileExtensionFolder = profileFolders[i] + "\\extensions";

      if (!fs.FolderExists(profileExtensionFolder))
         {  fs.CreateFolder(profileExtensionFolder);  }

      for (var e = 0; e < copyExtensions.length; e += 2)
         {
         if (fs.FolderExists(profileExtensionFolder + "\\" + copyExtensions[e+1]))
            {  fs.DeleteFolder(profileExtensionFolder + "\\" + copyExtensions[e+1]);  }

         if (fs.FolderExists(copyExtensions[e]))
            {  fs.CopyFolder(copyExtensions[e], profileExtensionFolder + "\\" + copyExtensions[e+1], true);  }
         }
      }
   }
Updated January 10, 2012:  Added the copyExtensions array for extensions that need to be in the user profile.  Also made the ieTabRules array generate both coral.ietab.rulelist and extensions.ietab2.filterlist properties.

Updated June 3, 2011:  Added settings for XMarks in addition to XMarks BYOS, which is needed to work with Firefox 4.

Updated June 9, 2010:  The settings are now specified at the beginning of the script in firefoxSettings. You don't have to deal with the code at the end of the script anymore. Also added arrays for the IE Tab Plus and IE View rule lists so they can be specified more easily.

Updated July 27, 2012:  Added computerName variable in case you wanted to push out settings per-computer.

Making Any Firefox Extension an MSI

Last updated September 2017

Now suppose you want to push out a particular extension to all your Firefox users.  You can build them into your Firefox MSI with FrontMotion's packaging service which is certainly convenient.  However, you can also roll it into its own independent MSI.  This has the benefit of being upgradable separately, and ultimately we're going to be altering an extension in a later article and this is going to be required to push it out.

Important: These instructions were written for legacy extensions, not the newer WebExtensions.  If you need older extensions to work you should use the Firefox 52 ESR releases, as they'll stop working in Firefox 57 and beyond.  The ESR release will be supported until May 2018.  When I make the switch off of that I'll update these instructions for WebExtensions.


Download and Extract

First, download your chosen extension as an XPI.  I'm going to be using Flashblock because there are no changes we need to make to it.  However, these instructions apply to any extension.

Go to the extension's page in addons.mozilla.org and right click on the Add to Firefox button.  Choose Save Link As and you'll get the raw XPI.

Now unpack the XPI to a folder.  XPIs are just ZIP files, so add .zip to the end of the file name and extract it.


Create the MSI

Now open install.rdf in a text editor.  You want to copy the contents of the <em:id> tag somewhere.  The first one, under the Description tag, not the one in the targetApplication group.  In this case Flashblock uses {3d7eb24f-2740-49df-8937-200b1cc08f8a}.

Now we need to download a nice piece of software called Advanced Installer to actually package this into an MSI.  They have a freeware edition, which is the same as the trial version except the Simple project type doesn't expire.  That's all we're going to need.

Fire it up, create a Simple project, and turn off the wizard.

In the Product Details section, change the Product Name, Version, and Company Name to whatever you want.

In the Install Parameters section, change Installation Type to "Per-machine only (fails if user is not administrator)".  We're going to need to access the HKEY_LOCAL_MACHINE part of the registry so this is required.  Since we're pushing it out via Active Directory it will always install under a privileged account anyway.

You don't need to check these, but I do anyway:
  • Limit to basic user interface.  Cuts out the nonsense if you ever have to install it manually, though you won't be able to install to anything other than the default location.
  • Don't save system restore points for installation.  Overkill for the changes we're making, and excluding it will make the install faster.
  • Perform only file costing and skip checking other costs.  Supposedly also makes the install faster.
Skip down to the Files and Folders section.  Right click on Application Folder and use Add Folder to add every subfolder of your unpacked XPI.   Then also use Add Files to include anything in the unpacked root folder.  It will look something like this when it's done:


Now go into the Registry section.  Go to HKEY_LOCAL_MACHINE/Software, right click and use New Key to create the hierarchy Mozilla/Firefox/Extensions one by one.  Then right click and choose New Value.

The name of the value will be the ID you copied from install.rdf.  In the case of Flashblock it's {3d7eb24f-2740-49df-8937-200b1cc08f8a}.  Choose String and Replace for the type, and put [APPDIR] for the value.  Do NOT include any quotes.  If you click Folder and choose the application folder that way it will add "[APPDIR] ".  I've found having the quotes around the path in the registry prevents Firefox from recognizing it.  It should look like this:


When you click OK it will show up on the right side in quotes.  That's okay.  If there were actual quotes in the value they would be doubled there.  Everything should now look like this:


Now head to the Media section.  Folder and File Name are just where you want the generated MSI to go.  Archive should be left on the defaults to make a self-contained MSI:


Now click Build to generate your MSI and you're done.

So what happens when it installs?  The MSI will create a folder on the hard drive with all the unpacked XPI files.  They'll be in Program Files\[Company Name]\[Product Name].  It will also add a registry key under Firefox/Extensions pointing to that path.  When Firefox sees that it will install the extension automatically.  Users won't be able to uninstall it as long as the registry key exists, though they will be able to disable it.

Some extensions like PlainOldFavorites don't work from C:\Program Files for whatever reason, they have to be in the user profile folder.  My login script can copy them over automatically if you add them to the copyExtensions variable.

Firefox is suspicious of extensions installed via the registry and may ask users if they really want them before enabling them.  This is great for consumers but bad for us.  Fortunately we can disable that feature.

Like the Flash MSIs it doesn't matter what order things are installed in.  If an extension is installed this way before Firefox it will be just fine.

Flash MSIs

Did you know you can get Flash MSIs from Adobe?  Well you can.  Just head on over to this page to apply for a license, which doesn't cost anything.
This is why I didn't want the version of Flash baked directly into the FrontMotion MSIs.  If you get them direct from Adobe you can update them separately.  Also, the IE version of the plugin is separate from the Firefox version, so unless you're disabling IE altogether you're going to want to manage that via MSI as well.  Might as well do them both the same way.

Also good news is that you don't have to worry about one installing before the other.  If you're pushing out Firefox and Flash via MSI and Flash happens to install first Firefox will pick it up just fine.

Tweaking the Firefox MSI (v3.6 and 4.0)

(These are the instructions for Firefox 3.6 and 4.0.  If you want the newest instructions, go here.)

So you downloaded the Firefox MSI but you don't like how it installs.  Maybe you don't want Flash baked into it because we can manage and upgrade that separately.  Maybe you don't want Firefox to become the default browser because you have IE-dependent software and it should only be an option.  Well we can fix that.

The first thing you need is Orca, a tool from Microsoft to create MSI transforms.  It's not the most user friendly thing but I'll walk you through it.  Yeah, that link is to someone's blog post, but as it says to get it direct from Microsoft you're going to have to download it as part of a big SDK.

Got it?  Good.  Fire it up and open your Firefox MSI.  We don't want to edit it directly so immediately after opening it go to Transform > New Transform.  Your changes will now be highlighted in green and put in a transform file instead.


Choosing Which Features to Install

First we have to go to the Feature section.  All of the Level properties are 3 except for F_SetDefaultBrowser.  That isn't very nice because it doesn't let us exclude Flash if we want to.  Double click on the Level value for ALL.[version].Mozilla_Firefox_en_US_ and F_FirefoxMain and set them both to 2.


Now we have options.  A level of 2 means just install Firefox.  3 means also install Flash, and 4 means also make it the default browser.  If you want the option of making it the default browser without installing Flash, make F_Flash 4 and F_SetDefaultBrowser 3.

Now head over to the Property section and change INSTALLLEVEL to what you want.  Anything in the Feature section that is less than or equal to this number will be installed.  I want Firefox without Flash or being the default browser so I'm setting it to 2.



The Desktop Icon

The Firefox MSI creates a special desktop icon for every user like Internet Explorer does.  Do you want that?  Probably not if you're not making it the default browser, so if that's the case we can remove it.

Go to the Registry section, sort by Key, right click on Software\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\[\{]EC8030F7-C20A-464F-9B0E-13A3A9E97384[\}], and choose Drop Row.


Here's where I admit I'm not familiar enough with this section of the registry.  Just dropping that key will work, but I don't know if I should also be dropping some of the other keys as well so there are no references to it.  Feel free to enlighten me in the comments.


Shortcuts

If you want to really get anal you can customize the shortcut as well.  (I make my images absolutely pristine, and that includes simplifying the All Programs menu.  This won't be as necessary when we move to Windows 7 and they can search it.)  It's all in the Shortcut section.  If you want the Firefox icon to be directly in All Programs instead of it's own unnecessary folder, you can change the Directory property from _PROGRAMMENUFOLDER_MOZILLA_FIREFOX to ProgramMenuFolder.  These values are defined in the Directory section.  You can also change the name to just Firefox or whatever else you want.



Finishing Up

Now that we have the tweaks we want, go to Transform > Generate Transform.  This will create a MST file you can use with your MSI.

To apply it in Active Directory, make sure you choose Advanced when creating a new package.


Then add the MST on the Modifications tab.  You won't be able to do this after the package is created.


That's it.  You now have a customized Firefox install via Active Directory.

Firefox MSIs

Mozilla doesn't provide MSIs directly.  For that you have to go to FrontMotion.  They have three offerings:
I'm going to be using the plain Firefox MSIs for the rest of these articles.  Why?  Users are more familiar with the original branding ("Mozilla Firefox" plus the icon we know and love) I'm going to show how to manage extensions via their own separate MSIs later (necessary because we're going to be editing one of them) and we're going to need to configure settings beyond what FrontMotion makes available via group policy (although that doesn't mean you can't use group policy for the supported settings and my method for the rest if that's your preference.)

Important: Legacy Firefox extensions (those not written as WebExtensions) are not going to be supported in Firefox 57 and beyond, so if you want to use them you should use the ESR 52 release.  That will be supported until May 2018.

This blog assumes some familiarity with installing software via group policy, so I'm not going to explain it here.  If you want to know how to install an MSI via Active Directory, go here:

Introduction

I like Firefox.  I use it at home.  At work I use Active Directory to manage our network, and therein lies the problem: Firefox doesn't really like Active Directory.

This is really a failure on Mozilla's part, and I suspect on the other alternative browsers as well.  If you want to make inroads in the corporate world you need to make it easy for the administrators who need to roll it out and configure it centrally.  You need to provide MSIs and support group policy, otherwise Internet Explorer will continue to be the default because it is just so much more manageable.  Say what you want about Microsoft but they take care of IT very well.

However, you can make it work, and this blog is going to explain how.  Hopefully this blog will be obsolete someday and you won't need these little tricks and hacks, but until then, this is how I made it work.