Office – Check out our new look

Hey Microsoft QA, you might want to check out your new look:

animated GIF of Office's horrible new 'Check out our new look' popup

It looks less “modern, coherent and familiar”, and more “Unreadable” and “lost the Title Bar”.

This has popped up on many systems recently, so Consider it Checked out and extremely Not now.

Update 2022-02-25 It just popped up again where it was previously dismissed
(did they realize it was garbled and wanted another chance to persuade?)

Comparing key frames…


Their fixed comparison seems to offer :

  • LOSS of support for Windows’ Title Bar accent colors
  • LOSS of the Quick Access Toolbar from the Title Bar
    …and of those, add just “Undo” back to the Ribbon (Customize the Ribbon could already do this)
  • LOSS of usable vertical space to 15% increased Ribbon sprawl
  • LOSS of usable horizontal space, reducing Style choices from 4 to 3.

So “modern, coherent and familiar” means: form over function!, set the designers loose!, dumb it down!
…aka a typical “Fresh New Look”.

Now, you can do stuff without clicking buttons, ’cause they’re annoying!

RMM Check: Unifi Health

At Slingshot, we’ve moved our Managed clients over to Unifi networking systems, and I wanted to use our RMM to directly monitor the Unifi Controller. The API isn’t officially documented, but I did find other resources: the community’s API documentation, CyberDrain’s examples, and the Unifi API browser. Add tons of other research and trial/error, and I finally accomplished a Powershell RMM script that monitors Device connections, resources/ports, and Alerts, and reports on a ton more.

Enough with the intros. Here’s an instance currently alerting in Solarwinds RMM:

…and More Info gives the full report (looks better in console — I wish SWRMM preserved whitespace):

Before I forget, some quick notes:

  • Needs a local account (limited admin/readonly) on controller
  • For UDMPs with firmware 1.6 or greater, use port 443; For older controllers, use port 8443
  • It defaults the Controller IP to the detected Gateway IP (we do a lot of UDMPs).
  • Several of the Device Status codes are documented nowhere, so this script might have the only public record of them (for posterity, I’ve figured out 2=pending adoption, 9=inform error, and 11=isolated).

And finally the Powershell — (self-consciously) still in progress with plenty of debug stubs, BUT with lots of useful production miles already under its belt:

<#	checkHealth-UnifiController.ps1
Purpose: for all sites on a controller, checks device connections, resources, ports, alerts
Author: 	Slingshot Solutions,
Params:		$IP (or hostname) of controller (default: detected gateway), [int]$PortNum (default:443), Username, PASSWORD
* needs a local account (limited admin/readonly) on controller
* For UDMPs with firmware 1.6 or greater, use port 443;  For older controllers, use port 8443

  $IP = (Get-wmiObject Win32_networkAdapterConfiguration |where-object{$_.IPEnabled -and $_.DefaultIPGateway}).DefaultIPGateway[0],
  $PortNum = 443, # or 8443 depending on controller version
# tweaks:
trap { $_; exit 1 }	# force RMM to treat unhandled runtime errors as ERROR!
#widen host buffer for better output on old PS versions:
$newsize=$pswindow.BufferSize; if($newsize.width -lt 100){$newsize.width=100}; $pswindow.BufferSize=$newsize
$newsize=$pswindow.WindowSize; if($newsize.width -lt 100){$newsize.width=100;$newsize.height=20}; $pswindow.WindowSize=$newsize
function short([string]$str,[int]$size){if($str.length -gt $size){return ($str.substring(0,$size-3)+"...")}else{ return $str}}
# vars:
$exitcode = 0
$report = ""
$FullRpt = ""
$StopWatch = [system.diagnostics.stopwatch]::startNew()

	write-host "PARAMETERS:"
	foreach ( $key in ((Get-Command -Name $MyInvocation.InvocationName).Parameters).Keys ) {
		$val = ((Get-Variable $key -ea SilentlyContinue).Value) -join ", "
		Write-Host "  $($key): $val" 
	write-error "Hey dummy, set your params!"
	exit 1000

$Credential = @{  username="$Username"; password="$Password"; remember=$True; strict=$True; } |ConvertTo-Json
if($PortNum -eq 443){ # UnifiOS 1.6 or newer:
	$BaseURI = "https://$($IP):$($PortNum)/proxy/network"
	$LoginURI = "https://$($IP):$($PortNum)/api/auth/login"
}elseif($PortNum -eq 8443){ # EdgeOS 1.5 firmware or older:
  $BaseURI = "https://$($IP):$($PortNum)"
	$LoginURI = "https://$($IP):$($PortNum)/api/login"

[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"
[Net.ServicePointManager]::ServerCertificateValidationCallback = { $True }
Try {
	$login = Invoke-RestMethod -uri $LoginURI -Method POST -Body $Credential -ContentType "application/json" -SessionVariable UniFiSession | out-null
}catch{	#wrong username/password = "The remote server returned an error: (400) Bad Request"
	write-error "* ERROR - Api Connection Error: $($_.Exception.Message)"    
	$exitcode = 1010
if($exitcode -eq 0){
	#write-host "CONNECTED`r`n"

if($exitcode -eq 0){
	try {
		$urlSites = "$($BaseURI)/api/stat/sites"
		$sites = Invoke-Restmethod -Uri $urlSites -Method GET -WebSession $UniFiSession
		#$report += "* ERROR - Sites Query Failed: $($_.Exception.Message)`r`n"
		write-error "* ERROR - Sites Query Failed: $($_.Exception.Message)`r`n"
		$exitcode = 1011

if($exitcode -eq 0){
	# DIRTY HACK: UDMPs lose their auth on the second call (Something ServerCertificateValidationCallback related), so just pound it back every time:
	Invoke-RestMethod -uri $LoginURI -Method POST -Body $Credential -ContentType "application/json" -SessionVariable UniFiSession | out-null

	$rptDevices = ""
	$rptAlarms = ""
	Foreach ($site in ${  # SITE
		try {
			$urlDevices = "$($BaseURI)/api/s/$($"
			$devices = Invoke-Restmethod -Uri $urlDevices -Method GET -ContentType "application/json" -Headers @{"Accept"="application/json"} -WebSession $UniFiSession
			write-error "* ERROR - Device Query Failed: $($_.Exception.Message)`r`n"
		$shortsitename = short $site.desc 7

		$FullRpt += "`r`nSITE '$($site.desc)' - $urlDevices :`r`n"

		$DeviceStateNames = @{0="disconnected"; 1="connected"; 2="pending adoption"; 3="3(?)"; 4="upgrading"; 5="provisioning"; 6="heartbeat missed"; 9="inform error"; 11="isolated" }
		$DeviceModelNames = @{"BZ2"="UniFi AP"; "BZ2LR"="UniFi AP-LR"; "U2HSR"="UniFi AP-Outdoor+"; "U2IW"="UniFi AP-In Wall"; "U2L48"="UniFi AP-LR"; "U2Lv2"="UniFi AP-LR v2"; "U2M"="UniFi AP-Mini"; "U2O"="UniFi AP-Outdoor"; "U2S48"="UniFi AP"; "U2Sv2"="UniFi AP v2"; "U5O"="UniFi AP-Outdoor 5G"; "U7E"="UniFi AP-AC"; "U7EDU"="UniFi AP-AC-EDU"; "U7Ev2"="UniFi AP-AC v2"; "U7HD"="UniFi AP-HD"; "U7SHD"="UniFi AP-SHD"; "U7NHD"="UniFi AP-nanoHD"; "UFLHD"="UniFi AP-Flex-HD"; "UHDIW"="UniFi AP-HD-In Wall"; "UCXG"="UniFi AP-XG"; "UXSDM"="UniFi AP-BaseStationXG"; "UCMSH"="UniFi AP-MeshXG"; "U7IW"="UniFi AP-AC-In Wall"; "U7IWP"="UniFi AP-AC-In Wall Pro"; "U7MP"="UniFi AP-AC-Mesh-Pro"; "U7LR"="UniFi AP-AC-LR"; "U7LT"="UniFi AP-AC-Lite"; "U7O"="UniFi AP-AC Outdoor"; "U7P"="UniFi AP-Pro"; "U7MSH"="UniFi AP-AC-Mesh"; "U7PG2"="UniFi AP-AC-Pro"; "p2N"="PicoStation M2"; "US48PRO"="UniFi Switch Pro 48"; "US8"="UniFi Switch 8"; "US8P60"="UniFi Switch 8 POE-60W"; "US8P150"="UniFi Switch 8 POE-150W"; "S28150"="UniFi Switch 8 AT-150W"; "USC8"="UniFi Switch 8"; "US16P150"="UniFi Switch 16 POE-150W"; "S216150"="UniFi Switch 16 AT-150W"; "US24"="UniFi Switch 24"; "US24P250"="UniFi Switch 24 POE-250W"; "US24PL2"="UniFi Switch 24 L2 POE"; "US24P500"="UniFi Switch 24 POE-500W"; "S224250"="UniFi Switch 24 AT-250W"; "S224500"="UniFi Switch 24 AT-500W"; "US48"="UniFi Switch 48"; "US48P500"="UniFi Switch 48 POE-500W"; "US48PL2"="UniFi Switch 48 L2 POE"; "US48P750"="UniFi Switch 48 POE-750W"; "S248500"="UniFi Switch 48 AT-500W"; "S248750"="UniFi Switch 48 AT-750W"; "US6XG150"="UniFi Switch 6XG POE-150W"; "USXG"="UniFi Switch 16XG"; "UGW3"="UniFi Security Gateway 3P"; "UGW4"="UniFi Security Gateway 4P"; "UGWHD4"="UniFi Security Gateway HD"; "UGWXG"="UniFi Security Gateway XG-8"; "UP4"="UniFi Phone-X"; "UP5"="UniFi Phone"; "UP5t"="UniFi Phone-Pro"; "UP7"="UniFi Phone-Executive"; "UP5c"="UniFi Phone"; "UP5tc"="UniFi Phone-Pro"; "UP7c"="UniFi Phone-Executive";
		"UDMPRO"="UniFi Dream Machine Pro"}

		Foreach ($device in (${
			$DeviceModelName = $DeviceModelNames[$($device.model)]
			$DeviceStateName = $DeviceStateNames[$device.state]

			if( $device.default -eq $True){	# PENDING ADOPTION:
				$vwireEnabled = ($device.vwireEnabled -eq $True)
				$discovered_via = $device.discovered_via	#scan or l2
				$FullRpt += "* $($device.type) $DeviceModelName, ip:$($device.ip), mac:$($device.mac), state:$DeviceStateName $($device.state), discovered_via:$discovered_via, wireless:$vwireEnabled`r`n"				
				$rptDevices += "* PENDING ADOPTION$(if($vwireEnabled){" (WIRELESS)"}): $shortsitename > ($DeviceModelName)`r`n"
				$exitcode = 1010

			}elseif ($device.adopted -eq $True){
				if($device.state -eq 0 -or $device.state -eq 9 -or $device.state -eq 11){	# DISCONNECTED, INFORM ERROR, ISOLATED:
					$FullRpt += "* '$($device.NAME)' - $($device.type) $DeviceModelName, ip:$($device.ip), mac:$($device.mac),`r`n    state:$($device.state) $DeviceStateName, adopted:$($device.adopted), disabled:$($device.disabled -eq $True)`r`n"				
					if($device.disabled -ne $True){	# don't care if it's disabled
						$rptDevices += "* $($DeviceStateName.toUpper()): $shortsitename > '$($'`r`n"
						$exitcode = 1011
				}else{	# OTHERWISE:					
					$uptime = new-TimeSpan -Seconds (0+ $device.'system-stats'.uptime)

					$FullRpt += "* '$($device.NAME)' - $($device.type) $DeviceModelName, ip:$($device.ip), mac:$($device.mac),`r`n    state:$($device.state) $DeviceStateName, uptime:$uptime, cpu:$([int]$device.'system-stats'.cpu)%, mem:$([int]$device.'system-stats'.mem)%, current:$(!$device.upgradable) (FW v$($device.version))`r`n"

					if( [math]::Round($device.'system-stats'.uptime) -lt "300") { 
						$rptDevices += "* $shortsitename > $($ : Disconnected `r`n"
						$exitcode = 1010
					if( [math]::Round($device.'system-stats'.cpu) -gt "90.0") { 
						$rptDevices += "* $shortsitename > $($ : CPU usage of $($device.'system-stats'.cpu)% `r`n"
						$exitcode = 1012
					if( [math]::Round($device.'system-stats'.mem) -gt "90.0") { 
						$rptDevices += "* $shortsitename > $($ : Memory usage of $($device.'system-stats'.mem)% `r`n"
						$exitcode = 1013
					if( $device.upgradable -eq $true ) {
	#					$rptDevices += "* $shortsitename > $($ : Firmware upgrade available <-- IGNORING til QA recovers`r`n"
	#					$exitcode = 1014

					#if($device.port_table.count -gt 0){	#don't need to see AP Pros' secondary ports
					if($device.type -eq 'usw'){
						$FullRpt += "  "+ ($device.port_table |ft port_idx,name,enable,up,is_uplink,port_poe,autoneg,speed,full_duplex,network_name -AutoSize |out-string).trim().replace("`n", "`n  ") +"`r`n"
					Foreach ($port in ${
						if($port.stp_state -eq "discard"){
							$rptDevices += "* $shortsitename > $($device.desc) > PORT $($ : blocked due to STP issues `r`n" 
							$exitcode = 1017


		Foreach ($device in $ |where-object {$}){	#weirdly duplicates with blanks otherwise
			$FullRpt += "* FW WAN1 '$($' - is_uplink:$($device.is_uplink), up:$($device.up), ip:$($device.ip), netmask:$($device.netmask), gateway:$($device.gateway)  `r`n"
			if($device.is_uplink -and $device.up -ne $True) { 
				$rptDevices += "* $shortsitename > WAN1 $($ : link down `r`n" 
				$exitcode = 1015
		Foreach ($device in $ |where-object {$}){#weirdly duplicates with blanks otherwise
			$FullRpt += "* FW WAN2 '$($' - is_uplink:$($device.is_uplink), up:$($device.up), ip:$($device.ip), netmask:$($device.netmask), gateway:$($device.gateway)  `r`n"
			if($device.is_uplink -and $device.up -ne $True) { 
				$rptDevices += "* $shortsitename > WAN2 $($ : link down`r`n" 
				$exitcode = 1016

		# DIRTY HACK: UDMPs lose their auth on the second call (Something ServerCertificateValidationCallback related), so just pound it back every time:
		Invoke-RestMethod -uri $LoginURI -Method POST -Body $Credential -ContentType "application/json" -SessionVariable UniFiSession | out-null

		try {
			$urlAlarms = "$($BaseURI)/api/s/$($"
			$alarms = Invoke-Restmethod -Uri $urlAlarms -Method GET -ContentType "application/json" -Headers @{"Accept"="application/json"} -WebSession $UniFiSession
			$report += "* ERROR - Alarm Query Failed: $($_.Exception.Message)"
			write-error "* ERROR - Alarm Query Failed: $($_.Exception.Message)"
		Foreach ($alarm in (${
			if(! $($alarm.handled_time)) {	# ???
				$rptAlarms += "* ALERT: $shortsitename > '$($alarm.ap_name)' : $($alarm.msg) @$([datetime]$alarm.datetime -f "yyyy-MM-dd-Hmmss") `r`n" 
				$exitcode = 1018


	$report += "$rptDevices `r`n"
	$report += "$rptAlarms `r`n"

if($exitcode -gt 0){
	write-host "FAIL - problems found:"
	write-host "OK - no problems found!"

 write-host "CHECKED DATA:"
 write-host " "($FullRpt.replace("`n", "`n  ")).trim() #format

write-host "PARAMETERS:"
foreach ( $key in ((Get-Command -Name $MyInvocation.InvocationName).Parameters).Keys ) {
	$val = ((Get-Variable $key -ea SilentlyContinue).Value) -join ", "
	Write-Host "  $($key): $val" 
write-host "CONTEXT:"
write-host "  Script Path:" (get-item $MyInvocation.InvocationName)
write-host "  Script Last Updated:" (get-item $MyInvocation.InvocationName).LastWriteTime " (try -5hrs [SW saves as UTC])"
write-host "  Execution Time: Total $($elapsed_Total)sec"

write-host "exitcode: $exitcode" 
exit $exitcode

NOTE: updates since publishing this have so far added:
* proper detection of the “pending adoption” state
* awareness of the undocumented “adopting” state
* awareness of the undocumented UBB, USP and USW-Flex-Mini models
If anyone’s dying to see the latest version, let me know in the comments. RSS feeds

Are you an ExcelMicro partner who needs to stay in the loop on what they’re doing?  

They’ve got some great services and folks, but it sounds like they’re swamped with a lot of internal changes over the last couple years.  Unfortunately, that leaves them a bit weak on partner communication…

Their ONLY official way to not get surprised by changes is to manually/constantly check their support portal web pages.  After extended nagging for them to publish ANY info on Proofpoint releases, they did finally add that a few months ago.  But still no mailing lists, no RSS feeds, no carrier pigeons — just keep clicking refresh til something shows up…

So I took matters into my own hands: Using Feed43’s fantastic service, I wrote custom regular expressions to scrape EM’s content, which I’ve published into my own RSS feeds.  If you’re looking to stay in the ExcelMicro loop, feel free to subscribe to these feeds:

I expect EM will eventually get official feeds so these aren’t needed, but til then, enjoy!

Fool me 8 times, shame on me.

  1. Reader
  2. Postini
  3. Calendar sync
  4. iGoogle
  5. Gears
  6. Code Search
  7. Pack
  8. and now Voice XMPP integration

These are a few of my favorite things,
…that Google has yanked out from under my feet.

And let’s not forget Gmail’s Activesync. (That’s why I switched to, and happily).

Why are geeks still trusting Google? We should know better by now.

You can remember dozens of other loved ones in The Google Graveyard


Long Live Reading

With Google Reader now buried, and
my preferred reading app NewsRob with it, I figured I’d share how things shook out in my quest to keep reading…

My targets:

  • content-focused web app, keyboard navigable, and somehow able to theme it dark (y’know, light the content, not the room 🙂
  • responsive mobile app with good caching, plugin support, and also dark-skinnable

On the web service side, I tried and found:

  • Yoleo – ok, but strange UI, and no mobile app!
  • The Old Reader – UI similar to Reader, but much too basic and slow
  • CommaFeed – almost perfect clone of Reader’s UI, and great features (including custom theming), but somewhat slow
  • NewsBlur – fast UI, but focused on its own slickness, and bad keyboard accessibility
  • Feedly – (originally NO web client, but they made one for Reader fans!) – fast UI, but missing +/- to zoom text.

For mobile apps, I tried:

  • CommaFeed – too little/late. Alpha released 2 days before Reader buried, and didn’t even have caching.
  • Feedly – too dog slow to be at all usable.
  • GReader – very sophisticated (and better performance than my previous try a year ago)

My friend Sean McCabe (who really needs a blog!) enlightened me that GReader supported Feedly, and that Feedly also imported Reader’s Starred Items. I have 6 years of Starred Items that I didn’t want to use, so that sealed the deal: Feedly+GReader it is!

Feedly’s only cons: missing +/- zoom, and themes only cover navbar — I figured I can handle that… But I just remembered the Chrome extension Stylebot, which can inject your own custom CSS into any page. I’ve just used it to create a black AND zoomed theme for Feedly. Perfecto.

Keep on reading!

RIP Reader Google

Google is shutting down Reader.


6 years ago, I made a lengthy search and was happy to find it, and I happily use it daily for 454 feeds on various computers and devices.  So I’m not sure what comes next.  Everyone seems to be sharing lists or gimmicks of Reader alternatives, but they all look like steps back to me.

For now, I’ve left a flower for Reader at the The Google Graveyard.

Beyond that, my strongest next inclination is to stop trusting Google with my stuff.

Dear Acer: It’s your own fault

So Microsoft is about to sell its own hardware running Windows.    And Acer is unhappy, because Microsoft is effectively competing with its own partners.

Now, working for a Microsoft Partner, I can understand the feeling.  I have my beefs with Microsoft, like how they’ve just disenfranchised Small Business Specialists and cut down Small Business Server at the knees
(The 30-day discontinuation of SBS on SA is really throwing us for a loop now, since we relied on TechSoup to provide affordable solutions to non-profits, and Techsoup only has SA software, not OEM).

But consider this:

Acer and their ilk have been making Windows suck.

How so?   What do you think you should get to do after first powering up your brand-new computer?   Hours manually uninstalling paid Norton or McAfee trials, a dozen manufacturer addons, and a dozen more partner promotions?   Me either.    Here’s our experience:

  • Once upon a time, we manually removed the junk, as a labor of love. 
  • A few years ago we started using PC-Decrapifier to help automate the process, followed by CCleaner for the remnants.  Down to an hour or so…
  • Last year, we started wiping the (brand new) systems, and scratch installing from Microsoft’s own media.   It’s some upfront work, but actually faster, and the result is so much better.  (The only downside is tracking down weird laptop drivers).


Last week we bought an Acer netbook, and (for some crazy reason) gave their install a shot.   After powering up and doing some standard Windows configuration, Acer started their first-run customization process .   Now I’ve done this a LOT, and know this process should take about minute or two.  

Instead, it took 45 minutes, and crashed with a BSOD.

Then, after finally getting past “buy me” promos, it was sluggish.   Task Manager showed 35% CPU gone to a McAfee trial, and 67% RAM used overall, when I HAD RUN NO PROGRAMS YET.   Did we buy a pet to run for our amusement, and do nothing useful?


Dear Acer, I don’t like the idea of Microsoft taking their ball back either, but you dropped it, and someone’s gotta run the bases.  


P.S.  Also noteworthy about the Microsoft shift is that’s how Apple sells:  unified software AND hardware.  Other criticisms aside, Apple delivers a pretty tight package.