Modify Thunderbird Settings using Logon Scripts

Last time with VBScript, I swear it…

While working at UVM on their Exchange 2016 deployment, we came across an interesting environmental anomaly (UVM has lots of those). The popular Thunderbird IMAP mail client installer had been customized by someone (*cough*) to handle various local environment quirks in the UW IMAP server deployment. Once of these was the use of a “mailbox path prefix” variable. When UVM migrated to Dovecot IMAP many years back now, this setting became obsolete, but I, er, I mean *someone* never removed this setting from the custom T-bird installer. Surprisingly, it appears that thousands of users in the environment have the IMAP path prefix setting defined. I guess people really loved that custom Thunderbird installer?

Smug satisfaction with the success of *someone’s* mail client installer evaporated quickly when migrating Thunderbird users to Exchange. Why? As it turns out, this setting unexpectedly causes Thunderbird to point to a non-existent mailbox folder, and thus gives the impression that the Exchange migration had resulted in the deletion of all IMAP server folders. Gah!

It took me only an hour or two to figure out how to fix this problem using PowerShell, but I then discovered that it was not really practical to package PowerShell scripts for execution on non-domain-joined computers. Why?

  1. By default, PowerShell does not allow execution of scripts on new non-domain-joined Windows computers. But even if you could work around that problem…
  2. PowerShell will not trust code signatures unless they explicitly were imported into the “Trusted Publishers” branch of the user’s certificate store.

So, PowerShell is not going to be of overly much use to me today, since we want this script to run on-demand in addition to as a logon script. It really would be nice if I could have taken the time to learn C#, C++, Visual Basic, or some other “real” programming language, wouldn’t it? Because now I have to fall back on VBScript again.

Last time… Last time…

The script below will detect the “mail/” IMAP path prefix and delete it if present. It also will set the server polling interval to 10 minutes if set longer than 10 (29 was the default previously, which does not work well with Exchange IMAP). If Thunderbird is running, the user will be prompted to restart their mail client:

Manifests and Digital Signatures for Self Extracting Scripts

It has been quite in this little corner of the blogosphere lately. Must be because I am not doing anything, right?

Wrong. I have run out of time to blog. But today I will make an exception because I need to provide an update to an old post:

As a substitute for becoming a real programmer, I have for years been writing VBScripts and wrapping them up with the Z-Zip Self-Extracting executable. After the release of Windows 8, this model became more difficult. Out-of-box, the 7-Zip self extractor started generating application compatibility troubleshooter pop-ups on clients. Even prior to that, clients would get warnings asking them “do you really want to execute this scary unsigned possibly-from-a-murdering-hacker” when they launched our executables.

The solution for this is, of course, to add an application manifest to the self-extractor, and then to digitally sign the resulting executable. Easy, right?

I actually did this a few years ago for our venerable Wi-Fi profile installation tool. It was not quite easy, and unfortunately I never did get the process fully automated. The roadblock was in automating the addition of a manifest to the application. Microsoft’s tool for this, “mt.exe”, from the Windows SDK, consistently corrupts my executables. Others in the blogosphere have identified the tool “Resource Hacker” to fill this need:

I added this tool to my ugly-old script packaging batch files, and had good success with eliminating the program compatibility dialogs:

..bin\resource_hackerResourceHacker.exe -addoverwrite %fname%.exe, %fname%.exe, %fname%.manifest, 24,1,

I also was able to streamline the signing process with the following batch code:

set fname=fixThunderbirdMailboxPath
set SDKPath="C:Program Files (x86)Windows Kits10binx86"
set TimeStampURL=""
set /P CertPath="Enter the full path to the PKCS12/PFX signing certificate:"
set /P CertPass="Enter the password for certificate file:"
%SDKPath%signtool.exe sign /f "%CertPath%" /p "%CertPass%" /t %TimeStampUrl% /v %fname%.exe

Renewing SSL Certificates on ADFS 3.0 (Server 2012 R2)

I recently had to replace the public-facing service communication certificates on our primary ADFS deployment on Server 2012 R2. I followed a procedure that I thought had a reasonable chance of actually doing what I wanted it to:

  1. Obtained a new private key with signed certificate.
  2. Saved the file to a pfx, and imported it onto each node in the ADFS cluster
  3. Set permissions on the certificate according to documentation
  4. Used the ADFS MMC -> Certificates -> Set Service Communications Certificate.

Everything seemed to go okay, but after a bit we started to get some complaints that some of our users could not access the Office 365 Pro Plus software download page. This was a curiosity to me, because I could not reproduce the problem. A colleague later noticed a raft of SSL errors in the System event log on one of the ADFS nodes, and disabled it in the load balancer configuration.

When I finally got around to investigating, I noticed that the system log reported problems from source ‘HTTPEvent’, with details DeviceObject: DeviceHttpReqQueue, Endpoint: (and also Endpoint: What gives?

I found a related TechNet Blog that shed some light on the subject:

According to this document, after setting the Service Communications Certificate in the MMC, you must run:


to fetch the certificate thumbprint of the Service Communications Cert. Take note of the certificate thumbprint, then run:

Set-ADFSSslCertificate -Thumbprint [yourThumbprint]

“Set-AdfsSslCertificate” will fix the HTTP.SYS bindings used by ADFS. Apparently the MMC does not set the bindings, which is pretty annoying because this leaves the service in a pretty darn broken state. The HTTP bindings are mentioned in this TechNet documentation:
BUT, the docs do not explicitly state that the Set-AdfsSslCertificate cmdlet needs to be run on all of the ADFS server nodes in your farm. This also is a key missing detail.

Good Documentation… you always take it for granted, until you don’t have it anymore.

Note above that I mentioned a binding problem with the address This was a carry-over from our initial deployment of ADFS 3. Back then, Microsoft did not provide a health check URL for ADFS, and the supplemental binding was needed to allow health monitor connections from our F5 load balancer without using SNI, which is required by ADFS 3.0, but not supported on the F5. These days (and if you have KB2975719 installed), you can instead monitor the following URL from your F5:


More details on this solution can be found here:

While it is nice having a proper health check, problems can arise when your ADFS server HTTP bindings go sour. It would seem that nothing is perfect.

Ping plotting with PowerShell

Waaaay back we used to use a spiffy little tool called “ping plotter” to discover vacant IP addresses on our subnets. I had not had to do an exhaustive study of this for awhile. When it came up again today, I thought “I’ll bet we can do that with two lines of PowerShell.” But I was wrong… it took three lines, since I needed to initiate an array variable:

#Initialize $range as an array variable:
$range = @()

#Populate $range with integers from 2 though 254 
#  (for a Class C ipv4 subnet):
for ($i=2; $i -le 254; $i++) {$range += $i}

#Write out IP addresses for systems that do not have registered DNS 
#  names in the IP subnet
$range | % {$ip = "192.168.1." + $_ ; $out = & nslookup $ip 2>&1; `
  if ($out -match 'Non-existent domain') {write-host $ip}}

Now some variations… write out only addresses with no DNS entry and that do not respond to ping. (This will help to weed out addresses that are in use that for whatever reason to not have a DNS name.):

$range | % {$ip = "132.198.102." + $_ ; $out = & nslookup $ip 2>&1; `
  if ($out -match 'Non-existent domain') {$out2 = & ping $ip -n 1; `
  if ($out2 -match 'Destination host unreachable') {write-host $ip}}}

… and perhaps most usefully, write out addresses with no DNS entry and that cannot be located using ARP. (This will weed out in use addresses with no DNS and a firewall that blocks ICMP packets.):

$range | % {$ip = "132.198.102." + $_ ; $out = & nslookup $ip 2>&1; `
  if ($out -match 'Non-existent domain') {& ping $ip -n 1 > $null; `
  $out2 = arp -a $ip; if ($out2 -match 'No ARP Entries') {write-host $ip}}}

Server 2012 boot hang in vSphere/ESXi

Over the past year or so we have been having some problems with Server 2012 and 2012 R2 virtual machines hanging during reboot operations. The systems hang at the “spash screen”, showing the Windows logo and the ring of spinning dots… forever!

Finally I was able to find an fix for this problem here:

From a tip-off here:

The problem? Well, probably it is best that you just read the TechNet social thread, if you really want to know. It is none too exciting, and all very aggravating. The fix? Run a PowerShell script, then vMotion your machines to force ESXi to re-read the VMX file for your guests.

I am posting my variation on the script in the KB here, because VMware’s script is incomplete, and difficult to read.

#Script intended to correct the bug where Windows Server 2012+ systems on VM Hardware v10 will hang at the 
#boot-up splash screen.  Problem is caused by the failure of ESX to clear the "TS" counter on system reboot.

Set-PSDebug -Strict

#Initialize the VIToolkit:
if ( (Get-PSSnapin -Name VMware.VimAutomation.Core -ErrorAction SilentlyContinue) -eq $null ) {
    Add-PsSnapin VMware.VimAutomation.Core

#Connect to your virtual center:
$viServ = ""
Connect-VIServer -Server $viServ

#Get all VMs in the vCenter:
$vms = Get-VM 
#Loop though the VMs:
ForEach ($vm in $vms){
	#Get a "View" object for each VM.  Views expose useful data that is not contined in the VM object:
	$vmv = Get-VM $vm | Get-View
	$name = $vmv.Name
	$guestid = $vmv.Summary.Config.GuestId
	if ($guestid -like "windows8*Guest") {
		#windows8*Guest will match Windows 8 client 32-bit and 64-bit, as well as 
		# "windows8Server64Guest" (which is Windows Server 2012 and 2012 R2).
		#We need to update the VMX file for the VM, which is dong using a VirtualMachineConfigSpec.
		$vmx = New-Object VMware.Vim.VirtualMachineConfigSpec
		$vmx.extraConfig += New-Object VMware.Vim.OptionValue
		$vmx.extraConfig[0].key = "monitor_control.enable_softResetClearTSC"
		$vmx.extraConfig[0].value = "TRUE"
		write-host "Edited" $  
		$ | out-file -FilePath c:localtempsoftTscOut.txt -NoClobber -Append

Discover user rights in SharePoint

Among the top items capable of derailing your whole day or week are requests from auditors.  Who has access to a resource?  When did they exercise those rights?  In the pas few months, I have had several requests of this sort related to SharePoint rights.  Since I have once again started working on our SharePoint 2010-to-2013 migration project, and most of the SharePoint Powershell cmdlets were fresh in my mind, I though I would take a crack at this somewhat intimidating task.

As usual, writing a useful script took more time that I would have liked, but I am fairly pleased with the results.  The final product makes heavy use of Regular Expressions.  Special thanks go out to RegEx Hero, an online .NET regular expressions tester:
AND, of course, to the site:

Using .NET-style RegEx named capture groups, I was able to eliminate redundant loops though the SharePoint web site list, thus making it possible to crawl all SharePoint web and site-level ACLs in only a few minutes. Hurray!

This code will work only on SharePoint 2010 farms that use Windows authentication. There may be limitations related to sites with multiple Windows domains as well. I will need to update this script in the near future to handle claims authentication, but we will cross that bridge when we come to it.

The script has some pretty convoluted loops that may not make any intuitive sense… I have tried to insert comments to explain what is going on. If you do choose to use this script in your environment and find it difficult to understand, feel free to contact me with questions.

Owing to the agonizing pain of attempting to embed complex PowerShell code in WordPress, this script now is provided as a GitHub “Gist”. Enjoy!

Migrating to the SCCM UDI for OSD, part 6c: Operations – Applications

Continued from:

The following is a procedure for updating Application information in the UVM UDI environment.  Use it as a template for your own operations:

  1. When adding a new application (or app version): Categorize the application:
    1. In the SCCM console under Software Library->Application Management->Applications, select the new application and get “properties”.
    2. Under the “General Information” tab, Click “Select” next to the “Administrative Categories”.  Add the new application to an existing App Category or create a new category, as appropriate.  Applications that are categorized will be added to UDI. Non-categorized applications will not be available.
  2. Update the UDI config files:
    1. Run C:localscriptsCM-TaskSequencesbuild-UDIAppList.ps1.
    2. Locate the “MDT 2013 Files” package in the SCCM console under Software Library->Application Management->Packages, and run an “Update Distribution Points” action.  Verify that distribution was successful before proceeding.

And that is the whole story of the SCCM/UDI migration here at UVM to date. Clearly there is room for improvement. I will try to keep this series updated with revisions as we make them. As always, I hope that these posts will be of help to others in similar situations. If any of the code in this series makes its way into your environment, please let me know. I also am happy to answer any [short] questions about the topics covered here.

-J. Greg Mackinnon | Systems Administrator | University of Vermont
Phone: 802-656-8251 | Web:

Series Index:

Corral those thoughts before they run off…