How to export all URLs of Firefox tabs at once

I’ve seen the following script proposed on the technet script gallery that should show me “How to export all URLs of Firefox tabs at once”.

I said “should” because it actually doesn’t always work on my computer and there’s no error handling in any way in the proposed code 😦

First, to save you the hassle of trying that script, you can just open Tools/Options in Firefox and set the following. Even if you’ve 1000 tabs, the browser will open in a few minutes and restore the gazillion tabs without you to have to temporarily record the URL of each Firefox tabs that you want to open in the next session:firefox-tabs-02

Back to the proposed script. It actually threw the following error:
Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.
Why? Because when you have a gazillion of tabs (whatever that means), your Mozilla .js file that stores the JSON data about your tabs can be quite big.
I found the following post in the Windows PowerShell forum that made me go this way:

try {
Add-Type -AssemblyName System.Web.Extensions
$javaScriptSerializer = New-Object System.Web.Script.Serialization.JavaScriptSerializer
$javaScriptSerializer.MaxJsonLength = [System.Int32]::MaxValue
$javaScriptSerializer.RecursionLimit = 99
(Get-Content -Encoding UTF8 -Raw -Path "$($env:APPDATA)\Mozilla\Firefox\Profiles\*\sessionstore-backups\recovery.js" -ErrorAction Stop).Trim('()')
) | Select -First 1
)['tabs'] |
Where { -not($_.hidden) } |
ForEach-Object {
$e = @($_.entries)[-1]
url = ($e)['url']
title= ($e)['title']
} catch {
Write-Warning -Message "Failed because $($_.Exception.Message)"
view raw sample-json.ps1 hosted with ❤ by GitHub

Quick tip: change ConfigMgr updates maximum run time

With the new servicing model being applied to pre-Windows 10 operating systems, it’s a good idea to change the default “maximum run time” of the monthly cumulative updates.
For example, I’ve:

Here’s a way to achieve this easily with PowerShell.
The -Name parameter of the Get-CMSoftwareUpdate accepts wildcards although its documentation says the opposite.
The -Fast is explained in the following warning message:

WARNING: ‘Get-CMSoftwareUpdate’ supports -Fast for retrieving objects without loading lazy properties. Loading lazy properties can cause significant performance penalties. If it is not necessary to utilize the lazy properties in the returned object(s), -Fast should be used. This warning can be disabled by setting $CMPSSuppressFastNotUsedCheck = $true.

# 1. View what updates will be targeted:
Get-CMSoftwareUpdate -Fast -Name "*Security Monthly Quality Rollup for Windows*" |
Select Max*,*DisplayName
#NB: MaxExecutionTime is in seconds in this case

# 2. Set the maximum run time to 120 minutes
Get-CMSoftwareUpdate -Fast -Name "*Security Monthly Quality Rollup for Windows*"  | 
Set-CMSoftwareUpdate -MaximumExecutionMins 120 -Verbose

Local security policy using Pester

In my previous post, I’ve shown how to use DSC to configure the local security policy. If you only want to get the compliance status of the system, Pester might be more suitable.

I first create a hastable of settings like this:

# secedit.exe /export /Cfg C:\secpol.txt /areas SECURITYPOLICY
(Get-Content -Path 'C:\secpol.txt' -ReadCount 1) `
-match '^([A-Z\s0-9_\\]+)=(.*)$' -replace '=',',' |
ConvertFrom-Csv -Header Key,Value1,Value2 | ForEach-Object {
    '    @{'
    "        Key = '{0}'" -f $_.Key
    "        Value1 = '{0}'" -f $_.Value1
    if ($_.Value2) {
    "        Value2 = '{0}'" -f $_.Value2
    '    },'

and I populate the file secpol.ps1 using the output of the above command.
The content looks like this. The hashtable is stored inside an array named $SecurityPolicy.

Now in the test file secpol.tests.ps1, I’ve:

$here = Split-Path -Parent $MyInvocation.MyCommand.Path
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
. "$here\$sut"
#region Security policy
Describe 'Security Policy' {
BeforeAll {
& (gcm secedit.exe) @('/export','/Cfg','C:\Windows\temp\secpol.SECURITYPOLICY.txt','/areas','SECURITYPOLICY')
$CurrentSecPolicy = (Get-Content -Path 'C:\Windows\temp\secpol.SECURITYPOLICY.txt' -ReadCount 1) `
-match '^([A-Z\s0-9_\\]+)=(.*)$' -replace '=',',' |
ConvertFrom-Csv -Header Key,Value1,Value2
$SecurityPolicy | ForEach-Object -Process {
$k = $v1 = $v2 = $null
$k = $_.Key
$v1= $_.Value1
$v2 = $_.Value2
if ($v2) {
It "should have its $k policy type set to $v1" {
($CurrentSecPolicy | Where { $_.Key -eq $k }).'Value1' -eq $v1 | Should be $true
It "should have its $k policy value set to $v2" {
($CurrentSecPolicy | Where { $_.Key -eq $k }).'Value2' -eq $v2 | Should be $true
} else {
It "should have its $k policy set to $v1" {
($CurrentSecPolicy | Where { $_.Key -eq $k }).'Value1' -eq $v1 | Should be $true

Let’s see how to use it

Invoke-Pester ~/documents/pester/secpol


Fine, my system is compliant.
But, let’s I change the Maximum password age using gpedit.msc

Now, Pester reports my system as not being compliant:
But, the only drawback with my quick’n dirty code is that it doesn’t tell you what value was found instead of the expected 42 in this case.