Using Ubiquiti switches and Mitel phones with PC port passthrough on different VLANs

A while back I wrote a article about doing this with a older Dell switch but recently needed to do the same thing using a Ubiquiti switch. With a upcoming phone system upgrade I will be changing out about 130 old digital phones to IP phones and a lot of the cubicles don’t have extra Ethernet ports. So I will be using the 1Gb passthrough port on the new Mitel 6930w phones. But to keep everything separate the phones will be on one VLAN and the PCs on another. Configuration within the UniFi ecosystem is a lot different then command line Dell switches, personally once you get used to it I think it’s a lot faster and easier.

Like in the other article the user/PC network is VLAN 10 (10.40.10.) and the phones are on VLAN 20 (10.40.20.). First the setup within the UniFi Network software where you define custom ports (Settings -> Profiles -> Ethernet Ports -> Create New):

  • Name: Phones (Tagged) with User Passthrough (Untagged)
  • Primary Network: VLAN 10 Users
  • Traffic Restriction: All other VLANs except for VLAN 20

Save that profile then go into a switch where you will be plugging in a phone. In the switches Port Manager go to a port you will be using this on, select it, then scroll down to Advanced and switch it from Auto to Manual. You can now check the Feature box for Ethernet Port Profile and select your profile. Note that with the newest UI what this really is doing is manually setting the port profile above for you. So you can do this without setting up custom profiles but doing it this way is easier and personally quicker to see whats going on.

Now that the switch is setup you modify DHCP just like in the previous article by adding a Mitel specific Option 43 to both VLANs. First we will add the option on the Users VLAN 10 to tell the phones to switch over to VLAN 20:;vlan=20

Then add Option 43 to the Phones VLAN 20:;sw_tftp=(serverIP);call_srv=(serverIP);l2p=6v6;vlan=20;dscp=46v46s26

And your done. The phone will boot up and send it’s initial DHCP request untagged which the switch will tag as VLAN 10. DHCP will get this and option 43 will tell the phone to switch over to VLAN 20. It will, reboot, and then make another DHCP request tagged with VLAN 20. It will then get the correct server info. Any computer you plug into the passthrough port will get tagged by the switch back to VLAN 10 and everything should work together which keeping the traffic separate.

Need Password prompt in Outlook after clearing everyone’s sessions

Long story short one of our vendors got hacked and sent out a phishing scam email using a SharePoint link and session hijacking that was able to bypass MFA. One of my users, who was waiting on a quote from this vendor, clicked it and entered their credentials. A week later their email was used to send out the same phishing email.

We followed the recommended Microsoft steps to clean things up. I did a content search on his account to see what he sent out and then emailed everyone letting them know it was a phishing attempt and if they fell for it they should reset their own passwords and contact their IT people. I then sent out a internal email saying if anyone clicked on the link they also need to change their password and to remind everyone about phishing emails. Then to be extra careful we forced a logout of all 365 sessions using PowerShell:

Get-AzureADUser | Revoke-Azureaduserallrefreshtoken

Which should have been the equivalent of going into the admin center on each user and clicking “Sign out of all sessions”. We told everyone that they might have to re-login to things and to reboot. Then followed up with some exports of everyones inbox rules, forwarding rules, etc, to make sure no one else was affected. It was a super fun Friday afternoon into evening.

The following Monday 95% of our users are completely fine, no issues, everything works. 5% of our users are getting the “Need Password” prompt at the bottom right in Outlook right after it finishes syncing all folders. If they try to get to public folders, other mailboxes they have access to, etc Outlook hard locks for a couple minutes and then connects and looks fine. Opening up the “Outlook Connection Status” during this time and you can see under status it saying “Connecting….” for each one then disconnected, over and over until it figures it out and connects. But then will go back to Need Password after a couple minutes. But if you simply click on where it says “Need Password” as soon as it pops up it connects the user with no other issues and works fine. So they were operational but with a annoying work around.

So I search google and the majority of things tell people to set a couple of registry keys which Microsoft has a entire article about not doing. Instead I followed the steps in that article which didn’t help at all. Tried the Microsoft support assistant for this issue, also didn’t work. Then moved on to having users change passwords and reboot, disconnect their accounts under “Access work or school” and reconnect, delete the Outlook profile and recreate, etc. All the standard Outlook is broken things to try but nothing seemed to work.

Well it turns out Microsoft put out a advisory that users with a Online Archive in a Exchange Hybrid setup were having trouble if the Autodiscover URL was pointing to the on prem server. So it ended up being a really bad coincidence. But this got me thinking why do I need to point Autodiscover to the on prem server at all if 100% of our mailboxes are migrated. It’s just a extra unneeded step which at best adds a little time to autodiscover compared to going directly to Microsoft and at worst if our internet or Exchange is down could prevent users from connecting.

Time to change that. I deleted my internal DNS autodiscover HOST record and put in a autodiscover CNAME record pointing to Microsofts Did the same for my GoDaddy DNS record. Once that was done I removed the SCP value from the internal server:

Get-ClientAccessService | Set-ClientAccessService -AutoDiscoverServiceInternalUri $Null

Flushed DNS on my own computer, rebooted, and verified Outlook connected without issues. I then updated the SRV records to also point directly up just in case in DNS under Domain -> _tcp -> _autodiscover and pointed that to

It’s now been like this for a couple months with no issues and since we have 0 mailboxes hosted locally there is no reason not to go directly to Microsoft for autodiscover.

Quickly getting media info using MediaInfo

Years ago I started ripping all my DVD’s and BluRays to my NAS so I could stream them without having to load the disk into my XBox, which is my only player in the house. Although the process is a pain it is so much more convenient to be able to stream a movie to any TV in the house and on the plus side when taking a trip I can now download the movies to my phone or tablet and bring them with me. The problem is the file size….some movies are massive and halfway through ripping my collection I started to run out of space. So I started looking into Handbrake to compress them into a more manageable size. But first I wanted to get a idea of everything I had, mainly so I could start compressing the largest files first.

I started using MediaInfo which is a great program to get almost every piece of information from a media file. The problem was when scanning a lot of files, especially across a network, it would lock up and crash. I’m assuming because it’s retrieving hundreds of properties from each file it just gets overloaded. But they also make a command line version which lets you poll files for only the info you want which can run a lot quicker.

So after lots of trial and error using the command line version I made a PowerShell script that automates the command line MediaInfo program to get the basics: File name, duration, size, resolution height and width, and what was used to encode it so I could see whether it was already compressed with Handbrake or if it was the original rip from MakeMKV. Copy and paste this into your PowerShell editor, change the initial variables, and run:

# This script uses the MediaInfo CLI ( ) to 
# quickly catalog media into a CSV file.  Change the initial variables to match your system then run

# Path to the root folder where you have your media
$RootFolder = "X:\Movies"

# Path to the MediaInfo CLI executable
$MediaInfoCLI ="C:\Temp\MediaInfo.exe"

# CSV File to create for your media's info
$CSVExport = "C:\Temp\MovieExport.csv"

# File types to include in the search.  Seperate by a comma like @("*.mkv","*.mp4","*.mpg") etc
$FileTypes = @("*.mkv","*.mp4")

# Add a resolution column to the file.  Once this finishes running a formula will be displayed that you
# can insert into excel to guesstimate the resolution.  Turn off to skip this.
$CreateResolutionColumn = 1

# Display status as the script is running.  0 for None, 1 for basic status, 2 for full status
$DisplayStatus = 1

#Display the exported file once complete.  0 for No, 1 for Yes
$DisplayExport = 1

### Do not change anything under here unless you know what you are doing

# Get our media info.
if (Test-Path -Path $MediaInfoCLI -PathType Leaf) { #Make sure MediaInfo exists
    $timer = [Diagnostics.Stopwatch]::StartNew()
    # The information to pull out of our media.  If you change any of this you will also have to change the 
    # header that is created.  Note the variable for new line since MediaInfo needs different sections on 
    # different lines even though it's exporting everything to a single line.  This info is put into a temp
    # file for MediaInfo to then read.
    $NL = "`r`n"
    $InfoToPull = 'General;"""%FileName%""",%FileSize/String%,%Encoded_Application%,' + $NL + 'Video;%Width%,%Height%,%Duration/String%' + $NL
    $TempFile = New-TemporaryFile
    $InfoToPull | Out-File -Encoding ascii -FilePath $TempFile

    # Create a header for our CSV file.
    If ($CreateResolutionColumn -eq 0) {
        'File Name, Size, Encoder, Width, Height, Duration, Notes' | Out-File -Encoding utf8 -FilePath $CSVExport
    } Else {
        'File Name, Size, Encoder, Width, Height, Duration, Resolution, Notes' | Out-File -Encoding utf8 -FilePath $CSVExport

    If ($DisplayStatus -ge 1) {
        #Display all our variables if option is set
        Write-Host "MediaInfo found at $MediaInfoCLI"
        Write-Host "Searching $RootFolder for all files matching these extensions:" $FileTypes.Replace("(", "").Replace(")", "").Replace("*.", "")
        Write-Host "Writting results to $CSVExport"
        Write-Host "Retrieving list of files...."
    $ListOfFiles = Get-ChildItem -Path $RootFolder -Recurse -File -Include $FileTypes
    If ($DisplayStatus -ge 1) {
        $GetFilesTimer = [math]::Round($timer.elapsed.totalseconds,2)
        Write-Host "Found" $ListOfFiles.count "files in" $GetFilesTimer "seconds"
    ForEach ($file in $ListofFiles) {
        If ($DisplayStatus -eq 2) {
            Write-Host "Getting info for $file..."
        If ($DisplayStatus -eq 1) {
            Write-Host -NoNewline "."
        $Args = @("--Output=file://$TempFile",$file)
        & $MediaInfoCLI $Args | Out-File -encoding utf8 -Append -FilePath $CSVExport
    If ($DisplayStatus -ge 1) {
        $FinishedTimer = [math]::Round($timer.elapsed.totalseconds,2)
        Write-Host "`n"
        Write-Host "Finished exporting info.  Took" $FinishedTimer "seconds"
    Remove-Item $TempFile  # remove the temp file used by MediaInfo

    if ($CreateResolutionColumn -eq 1) {
        Write-Host "`n"
        Write-Host "For Resolution column:  Copy and paste the following text into Row 2's resolution column then extend it down to the other rows:"
        Write-Host "=IF(D2>3830,""4K"",IF(E2=2160, ""4K"",IF(D2 <=719,IF(D2="""",""Need"",""SD""),IF(D2 <= 1900,IF(E2 > 1070, ""1080"", ""SD""),""1080""))))"
        Write-Host "`n"
        Write-Host "Then make sure to save the file as a standard xls/xlsx/ods to retain formatting."
    If ($DisplayExport -eq 1) {
        Start-Process $CSVExport
} Else {
    Write-Host "MediaInfo could not be found at $MediaInfoCLI.  Please double check it's location."

The script is pretty bare bones, not a lot of error checking other then making sure the CLI MediaInfo program is found, but does the job quickly.

Blank screen at Windows login with no Explorer

Patch Tuesday just happened a couple days ago and once again I had a single computer in the office that logged into a blank black screen. This is now the third computer over the last year to do this immediately after rebooting due to Windows updates. Trying to manually start Explorer.exe didn’t work. Scan disk came back clean. Safe mode gave the same results. Booting off a Windows 10 USB and doing a startup repair didn’t help. But thankfully the following fix has worked each time to get us back working.

Start with a CTRL+ALT+DEL to open Task Manager then File -> Run new task. Enter CMD and check off the box for “Create this task with administrative privileges”. Then run each of these commands in order:

Dism /Online /Cleanup-Image /CheckHealth
Dism /Online /Cleanup-Image /ScanHealth
Dism /Online /Cleanup-Image /RestoreHealth
SFC /ScanNow

Once all three are done reboot with a shutdown -r -t 0 and you should be back to normal.

Removing Orphaned GPO Settings Listed as Extra Registry Settings

Our domain has gone through multiple upgrades over the years from Server 2003 to the current Server 2019 domain controllers with a mix of 2012 and 2019 member servers. Each time there is a group policy admin template update I download it and copy it over to the central store. Today I went to edit a GPO object and noticed when checking the settings being applied under the “settings” tab there was a “Extra Registry Settings” section for a old version of Office under the administrative templates section:

After some research a couple things said to download the old templates, install them, remove the settings, then remove the templates. But that got me thinking on how old some of the templates probably are at this point and how I probably also had a bunch of no longer needed settings in GPO.

So I decided to start “fresh”. I first reviewed Microsoft’s document on creating and managing the central store for group policy admin templates and also downloaded the latest templates which as of this post were the Windows 10 November 2021. I also grabbed the latest Microsoft Office templates and made sure to check where the OneDrive ones were located as I would need those. I installed the Windows 10 ADMX to C:\Temp\Win10 and the Office ADMX to C:\Temp\Office on my own computer.

I then made a backup of my current templates by browsing to \\\SYSVOL\\Policies and creating a directory called “Old PolicyDefinitions”. I went into the PolicyDefinitions folder, selected everything, cut it, and moved it to my old folder. I then copied all the new templates from my computer, Windows 10, Office, and OneDrive, to the now empty PolicyDefinitions folder.

Now that everything was shiny and new I went through every GPO and checked for that Extra Registry Settings section and there were a bunch. References to Office 14.0 and 15.0 that are no longer used, SkyDrive, misc Windows XP settings, depreciated settings that are just not in the latest templates, etc. Now there are two choices here. Either I had to screen shot each one of these screens, switch back to the old templates, remove things, and switch back or thankfully there is a PowerShell command you can use on the domain controller:

Remove-GPRegistryValue -Name "Default Domain Policy" -key "HKLM\Software\Policies\Microsoft\OneDrive" -ValueName KFMSilentOptOut

Name is your GPO name. Key is going to be whatever is in the extra settings list. HKLM for anything computer config related, HKCU for anything user config related. You can also delete entire keys by omitting the -ValueName part but you will have to delete subkeys first. I.e. Office\14.0\Word\Common needs to be deleted before Office\14.0\Word can be deleted. I deleted a bunch, hit F5 in Group Policy Management so the settings screen would refresh, then delete what was left.

After a hour or so I had all my GPOs cleaned up with all the obsolete junk removed and also went through a bunch of settings that don’t need applied any more. Between the two I probably removed 100 entries which should speed up group policy processing if even slightly.

Setting up Mitel VoIP phones with PC ports on different VLANs

Recently ran into a situation where we were taking over a office that had a single Ethernet run to each desk that we needed to provide new IP phone along with PC connectivity. Now I don’t really like doing this, multiple points of failure, more complicated config, etc but running new cabling was going to be cost prohibitive so I have to work with what I got.

The core switch was going to be a older Dell N3024p, the phones Mitel 5340. We use DHCP exclusively for the Mitels using Option 43 which there is already a good guide on setting up. The tricky part was getting the phone on one VLAN but the PC port on a different VLAN and not having to manually set anything up on the phones. So this is what we ended up doing.

For this network VLAN 10 was for users/computers (10.40.10.*) and VLAN 20 was for phones (10.40.20.*). The switch ports on the Dell were setup as “General” ports using the following config:

switchport mode general
switchport general pvid 10
switchport general allowed vlan add 10
switchport general allowed vlan add 20 tagged
switchport general allowed vlan remove 1
switchport voice vlan 20

So any packet arriving at the switch that was not tagged would be tagged VLAN 10. It also accepted tagged packets as long as they were from VLAN 20. After removing the default VLAN 1 nothing else would be accepted on the port. I also set VLAN 20 as voice traffic as the Dell switches have some QoS for voice that is enabled.

Now the tricky part. I want the phone to request DHCP but by default it’s going to start off on VLAN 10 since it’s traffic won’t be tagged at first. In comes Option 43. I add the manufacture specific DHCP option to the DHCP scope for the users subnet, 10.40.10.*, since that’s what the phone will first request on and I only put in enough for the phone to switch over to the phones vlan:;vlan=20

The phone gets this, sets itself to VLAN 20, and retries DHCP. Now the request is coming from the 10.40.20.* subnet which in DHCP has the full Option 43 string:;sw_tftp=(serverIP);call_srv=(serverIP);l2p=6v6;vlan=20;dscp=46v46s26

So now it gets the correct config from the correct subnet. And anything plugged into the PC port gets passed through untagged which gets put on the users subnet and pulls DHCP from there correctly.

Batch Converting Excel XLS files to XLSX

A little while back I posted a macro to batch covert Visio VSD files to VSDX files which got a decent number of people messaging me. Recently I found how many excel files we had using the old format which just like old Visio files take up a lot of extra space. So I went through and modified my Visio converter over for Excel. So here is a step by step to write your own Excel file converter:

  1. Open a new Excel document. Save it as a “Excel Macro-Enabled Workbook (*.xlsm)
  2. In the first cell put something like “To run the conversion hit ALT+F11 to open the program then F5 to run it”.
  3. Hit ALT+F11 to open up the Microsoft Visual Basic for Applications screen
  4. Right click “ThisWorkbook” at the top left then Insert -> Module
  5. In the module copy and paste the following in:
Public FilesAttempted As Integer
Public FilesConverted As Integer
Public FilesDeleted As Integer
Public FilesSkipped As String

Sub ConvertToXlsx()
FilesAttempted = 0
FilesConverted = 0
FilesDeleted = 0
FilesSkipped = ""
Dim FileSystem As Object
Set FileSystem = CreateObject("Scripting.FileSystemObject")

Dim HostFolder As String
Dim DeleteOriginal As Boolean
Dim RemovePersonal As Boolean

''' HostFolder is directory to start at.  Change to your base directory.
HostFolder = "C:\temp"

''' DeleteOriginal will delete the original file as long as the xlsx was created.  Either True or False
DeleteOriginal = False

DoFolder FileSystem.GetFolder(HostFolder), DeleteOriginal

MsgBox "Conversion complete! " & vbCrLf & vbCrLf & "Files attempted: " & FilesAttempted & vbCrLf & "Files converted: " & FilesConverted & vbCrLf & "Files deleted: " & _
    FilesDeleted & vbCrLf & "Files with issues: " & vbCrLf & FilesSkipped, vbOKOnly + vbInformation, "Conversion Complete"
End Sub

Sub DoFolder(Folder, DeleteOriginal)
  On Error GoTo ErrHandler:
  Dim SubFolder
  For Each SubFolder In Folder.SubFolders
    DoFolder SubFolder, DeleteOriginal
  Dim File
  Dim myWorkbook As Workbook
  For Each File In Folder.Files
    ' For each file name sure its a xls and not a temp file
    If ((Right(File, 3) = "xls") And (Right(File, 4) <> "~xls")) Then
      FilesAttempted = FilesAttempted + 1
      ' Open the file
      Set myWorkbook = Workbooks.Open(File)
      ' Save as a xlsx and increase our counter
      myWorkbook.SaveAs Filename:=File & "x", FileFormat:=xlOpenXMLWorkbook
      myWorkbook.Close (False)
      FilesConverted = FilesConverted + 1
      ' Delete the original if set and the new xlsx exists
      If ((DeleteOriginal = "True") And (FileExists(File & "x"))) Then
        SetAttr File, vbNormal
        Kill File
        FilesDeleted = FilesDeleted + 1
      End If
    End If
  Exit Sub
Debug.Print "Error encountered.  Error number: " & Err.Number & " - Error description: " & Err.Description
  If File <> "" Then
    FilesSkipped = FilesSkipped & File & vbCrLf
    GoTo NextFile:
  End If
End Sub

Function FileExists(ByVal FileToTest As String) As Boolean
   FileExists = (Dir(FileToTest) <> "")
End Function

Change the HostFolder to the directory you want to run this on and hit F5 to run. It will open each Excel workbook with a xls extension in that directory, and all sub directories, then save it as a xlsx. If you want it to automatically delete the old xls file change the DeleteOriginal variable to True or just manually delete them after conversion.

A special Rpc error occurs on server xxxxx These certificates are tagged with following Send Connectors

So our SAN SSL certificate was coming up for renewal and I really wanted to expand what was covered by it to include more devices. In general anything internal was using a self signed certificate and anything external used the SAN SSL. That worked fine but when it came time for renewal I figured why not just get a wildcard SSL and assign it to anything I could. Also would keep me from having to update subject names when there was a change.

So I bought a Wildcard SSL from GoDaddy and started assigning it to everything. Switched out the certificate on our firewall and VPN clients, mail server, web server, etc. Everything seemed to work fine. Then it came time to remove the old one once things were tested. Removing from IIS was fine, removing from the firewalls likewise fine, but removing from Exchange Control Panel gave the error in the title.

Now we do have a Exchange Online Hybrid deployment setup with centralized mail transport. We use a cloud based Barracuda spam/malware filter so all email in and out of the company goes through them then to our internal mail server. Any mailbox on Exchange Online then goes from our mail server to it and back. Well apparently when I set this up it made a send connector to route mail to Exchange Online and since it uses TLS attached the certificate that was currently being used which I was now trying to delete.

Unfortunately you can’t just go into the Send Connector in the ECP and reassign the certificate but you can do it by following some steps based on the Microsoft Set-SendConnector page. First get the list of your send connectors and the list of your certificates:


Copy the send connector that was in the error message and also the thumbprint for your new certificate. Next we will use that certificate to pull out the information needed to assign to the send connector and assign it:

$cert = Get-ExchangeCertificate -Thumbprint (your thumbprint)
$tlscertificatename = "<i>$($cert.Issuer)<s>$($cert.Subject)"
Set-SendConnector "Outbound to Office 365" -TlsCertificateName $tlscertificatename

Once your send connectors are updated you should be able to remove the old certificate. Also if you are using TLS on your receive connectors you will want to do the exact same thing but using the Set-ReceiveConnector command.

Setting up a Raspberry Pi Zero W for Pi-hole

I’ll start by saying there are lots of guides to do this already but none of them worked for me from beginning to end and I pulled parts from different ones to get things working. So after some trial and error I wrote my own guide. Some other sites I used as reference: A good quick start guide from Reddit, Info on changing the locale and keyboard layout for US, and Setting up a headless Pi Zero. I’m also assuming if you are reading this article that you already know what Pi-hole is and want to implement it on your network.

First things first. You will need a Pi Zero W (if you have a local MicroCenter they have them in store for $5 pretty often), a 8Gb micro SD card, a micro usb cable to power the zero, and a way to write to the SD card, either a SD card adapter or your computer if it has a slot for it. You may also want a case to put yours in if you plan on mounting it and don’t want to short anything out (my personal favorite that runs about $6). If you want to actually see whats going on you will also need a mini HDMI to HDMI cable although hopefully you don’t need to do that. And if you want to get really fancy you can get a Pi Zero W with header pins (or solder some on yourself) and a display such as the Adafruit one to show the IP address and some status info.

Lets grab the software you’ll need:

Then put it together:

  • Format the SD card using the SD Card Formatter utility.
  • Using balenaEtcher burn the Raspbian image to the SD card. By default it will “eject” the SD card once its done. You also might get Windows warnings about a problem with the disk, ignore these and close/cancel the warnings.
  • Pop out the SD card and put it back in. You might get the same warnings, again ignore them.
  • Using Notepad (or Notepad++) create a file named “ssh” with no extension and a single space in it. Save that to the root of the SD card along with the other files.
  • Again using a notepad program create a file called “wpa_supplicant.conf” with the following text (using your country code, SSID, and password) and save that to the root of the SD card. If your SSID is being broadcast you do not need the scan_ssid line as it will slow down the connecting slightly although it doesn’t hurt to have it in there (see here for more info on this file):
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev

  • You should have everything on the SD card needed to boot up and connect to your WiFi. Properly eject the SD card from your computer, pop it into the Pi, and boot it up.
  • Give it 90 – 120 seconds to boot. Since this guide is for getting Pi-Hole setup you will want to have a static IP address for your Pi. Go into your router and find out what the current IP address is and also set a reservation or lock the current IP address to your device.
  • Until it reboots it will be at whatever IP it was assigned. Start up PuTTY and put in the current IP address and connect. You will get a security warning, click Yes to accept the keys.

Your Pi is now on your network so the hard part is done. Next is to configure some basic settings and install Pi-hole

  • Login using the default user “pi” and password “raspberry”
  • Now by default the Pi is setup for the EN_GB locale which means your @ symbol and some others aren’t where they normally are which caused me some issues. So lets tell the Pi to add the EN_US locale and change our keyboard layout.
  • Type in sudo raspi-config and enter
    • Select option 4 for Localization then the first option for Locale
    • In the list scroll down and select en_US.UTF-8 UTF-8 by hitting the space bar to select it then enter
    • It will ask you to select a default language for the system. Select en_US.UTF-8 and enter. Give it a minute while it makes the change.
    • At this point for me I tried to change the keyboard layout but it didn’t work. It seems the Pi needs to be rebooted for these changes to take effect so first get out of the config menu by hitting the right arrow twice and selecting Finish
  • Back at the prompt type in sudo reboot. You will be disconnected, give the Pi a minute or so to reboot. Remember if you set a DHCP reservation the IP address may change when it comes back up.
  • Connect with PuTTY and login again. Easiest way is to click the PuTTY icon in the top left corner of the program and select “Restart Session” then log back in or just close and reopen the software.
  • Go back into the config with sudo raspi-config and enter
    • Select option 4 then 2 for change timezone. Select your country and city for your timezone (US -> Eastern for example).
    • Select option 4 again then 3 for the keyboard. In the list you should have a Generic 104-key PC option. Select that then English (US) or whatever keyboard layout you’d prefer. If you don’t see English (US) click Other first then it should be in the list. Hit enter to select then select the actual keyboard layout. You will be asked a couple more questions, select the appropriate answer (usually the defaults).
    • You can also go into the same menu to select US as your wireless country.
  • Now would also be a good time to change your password to something other then the default raspberry. Select the first option and enter a new password.
  • Select Finish to exit the config menu.

So the next recommended thing to do is update the Pi to the latest software:

  • At the terminal prompt type sudo apt-get update -y and enter. It might take a minute or two.
  • Next type sudo apt-get upgrade -y and enter. Chances are this will take 10 – 20 minutes as updates are downloaded and installed.

Once the updates are done reboot again with sudo reboot, log back in using PuTTY, and install Pi-hole:

  • At the terminal prompt type in curl -sSL | bash and enter

Go through the prompts and answer as you want it setup. Note the admin webpage password at the end as you will use that to login at http://YourPiAddress/admin. Lastly set your router DHCP to hand out the Pi-hole IP address for DNS instead of itself and you should be all set.

Resetting Windows Update on Domain Joined Computers

Windows Update & WSUS have been a thorn in my side for many many years. When it works its great but when it doesn’t it can be very frustrating to figure out what went wrong. Over the years I’ve had to rebuilt WSUS twice, once when it just stopped pushing updates and another time where it imploded itself and corrupted the database. Recently we had a number of computers, around 10%, stop reporting back to WSUS for status. They also reported no updates available when checking for updates using WSUS. We tried all the troubleshooters, DISM cleanups, etc, but nothing seemed to work. I thought maybe it was WSUS again but that wouldn’t make sense with so many successfully getting updated. Even tried the Microsoft recommendations on resetting Windows Update but in our case BITS didn’t want to stop. And related to this we were getting Task Host errors on shutdown for those machines with the reason being “AutomaticUpdateHost” which would make sense if BITS was stuck.

After some testing we found that the Microsoft recommendations did work when in safe mode. Problem was doing this as easily with the least amount of downtime and hands on touching. To that end I created a series of three batch files. The first one, run as a administrator, will set the boot options to safeboot with networking and reboot:

bcdedit /set {default} safeboot network
shutdown -r -t 5

The second is most of the Microsoft recommendations along with others I’ve found on the internet:

@echo off
echo Stopping Windows Update and BTIS services…
net stop bits /y
net stop wuauserv /y
net stop appidsvc /y
net stop cryptsvc /y
echo Killing any windows updates in process
taskkill /im wuauclt.exe /f
echo Deleting some files…
del /s /q /f "%ALLUSERSPROFILE%\Application Data\Microsoft\Network\Downloader\qmgr.dat" del /s /q /f "%ALLUSERSPROFILE%\Microsoft\Network\Downloader\qmgr.dat"
del /s /q /f "%SYSTEMROOT%\WindowsUpdate.log"
rd /s /q "C:\WINDOWS\SoftwareDistribution"
rd /s /q "%SYSTEMROOT%\system32\Catroot2"
del %USERPROFILE%\AppData\Local\Temp* /s /q
for /d %%x in (%USERPROFILE%\AppData\Local\Temp*) do @rd /s /q "%%x"
del %systemroot%\Temp* /s /q
for /d %%y in (%systemroot%\Temp*) do @rd /s /q "%%y"
echo Reset the BITS service and the Windows Update service to the default security descriptor.
echo Deleting registry keys….
reg delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" /v AccountDomainSid /f
reg delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" /v PingID /f
reg delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" /v SusClientId /f
reg delete "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate" /v SusClientIDValidation /f
REG DELETE "HKLM\Software\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v LastWaitTimeout /f
REG DELETE "HKLM\Software\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v DetectionstartTime /f
Reg Delete "HKLM\Software\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v NextDetectionTime /f
echo Re-registering Windows Update components…
regsvr32.exe /s c:\windows\system32\atl.dll
regsvr32.exe /s c:\windows\system32\urlmon.dll
regsvr32.exe /s c:\windows\system32\mshtml.dll
regsvr32.exe /s c:\windows\system32\shdocvw.dll
regsvr32.exe /s c:\windows\system32\browseui.dll
regsvr32.exe /s c:\windows\system32\jscript.dll
regsvr32.exe /s c:\windows\system32\vbscript.dll
regsvr32.exe /s c:\windows\system32\scrrun.dll
regsvr32.exe /s c:\windows\system32\msxml.dll
regsvr32.exe /s c:\windows\system32\msxml3.dll
regsvr32.exe /s c:\windows\system32\msxml6.dll
regsvr32.exe /s c:\windows\system32\actxprxy.dll
regsvr32.exe /s c:\windows\system32\softpub.dll
regsvr32.exe /s c:\windows\system32\wintrust.dll
regsvr32.exe /s c:\windows\system32\dssenh.dll
regsvr32.exe /s c:\windows\system32\rsaenh.dll
regsvr32.exe /s c:\windows\system32\gpkcsp.dll
regsvr32.exe /s c:\windows\system32\sccbase.dll
regsvr32.exe /s c:\windows\system32\slbcsp.dll
regsvr32.exe /s c:\windows\system32\cryptdlg.dll
regsvr32.exe /s c:\windows\system32\oleaut32.dll
regsvr32.exe /s c:\windows\system32\ole32.dll
regsvr32.exe /s c:\windows\system32\shell32.dll
regsvr32.exe /s c:\windows\system32\initpki.dll
regsvr32.exe /s c:\windows\system32\wuapi.dll
regsvr32.exe /s c:\windows\system32\wuaueng.dll
regsvr32.exe /s c:\windows\system32\wuaueng1.dll
regsvr32.exe /s c:\windows\system32\wucltui.dll
regsvr32.exe /s c:\windows\system32\wups.dll
regsvr32.exe /s c:\windows\system32\wups2.dll
regsvr32.exe /s c:\windows\system32\wuweb.dll
regsvr32.exe /s c:\windows\system32\qmgr.dll
regsvr32.exe /s c:\windows\system32\qmgrprxy.dll
regsvr32.exe /s c:\windows\system32\wucltux.dll
regsvr32.exe /s c:\windows\system32\muweb.dll
regsvr32.exe /s c:\windows\system32\wuwebv.dll
echo Resetting Winsock…
netsh winsock reset
echo Resetting WinHTTP proxy…
netsh winhttp reset proxy
echo Resetting the services as automatic…
sc.exe config wuauserv start= auto
sc.exe config bits start= delayed-auto
sc.exe config cryptsvc start= auto
sc.exe config TrustedInstaller start= demand
sc.exe config DcomLaunch start= auto
echo Restarting services…
net start bits
net start wuauserv
net start appidsvc
net start cryptsvc
net start DcomLaunch
echo Telling Windows to detect updates…
wuauclt.exe /resetauthorization
wuauclt.exe /detectnow
wuauclt.exe /reportnow
PowerShell.exe (New-Object -ComObject Microsoft.Update.AutoUpdate).DetectNow()

Then finally once that runs through is the third batch file to set the computer back to a normal boot:

bcdedit /deletevalue {default} safeboot
shutdown -r -t 5

I put all three into a folder and placed it on the desktops of the troubled computers then ran each in sequence. First the computer rebooted in safe mode, then Windows Update gets reset along with deleting all temp files, then the computer reboots normally.

So far every computer this was run on has reported in. Not sure what causes this in the first place but at least we have a quick solution now.

Note: The script was written to be run in regular mode so its stopping services that are normally already stopped in safe mode but was just reused for this purpose on the machines where BITS kept getting stuck.