Quick post about MBSA

  • Context

A PM.org member asked to the list:

We use MBSA with a few down-level OS’s as well as Windows 10 in several offline environments, and until this month, it still worked.

  • Problem

It appears that MBSA isn’t supported anymore: https://docs.microsoft.com/en-us/windows/security/threat-protection/mbsa-removal-and-guidance

  • Solution

Another member suggested the following:

Why not use WUA script to scan for updates offline ?

If you’ve a downloaded version of the wsusscn2.cab file that you indicate as the FilePath parameter of the following script, you can do:

Start-WUOfflineScan -FilePath C:\temp\wsusscn2.cab -Verbose

Function Start-WUOfflineScan {
Start an offline WUA scan
Start an offline WUA (Windows Update Agent) scan using wsusscn2.cab.
Specifies the path to the wsusscn2.cab file to be used to perform the offline scan.
.PARAMETER IncludeSupersededUpdate
Specifies to include superseded updates in the results if any.
Start-WUOfflineScan -FilePath C:\temp\wsusscn2.cab -Verbose
You can get the offline wsusscn2.cab from:
Official doc is:
[ValidateScript({Test-Path -Path $_ -PathType Leaf})]
Begin {
if ($FilePath -match '^~') {
$FilePath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FilePath)
try {
$UpdateSvc = New-Object -ComObject Microsoft.Update.ServiceManager
} catch {
Write-Warning -Message "Failed to query if server is configured against a WSUS server because $($_.Exception.Message)"
Function Test-WUARebootRequired {
try {
(New-Object -ComObject 'Microsoft.Update.SystemInfo').RebootRequired
} catch {
Write-Warning -Message "Failed to query COM object because $($_.Exception.Message)"
Process {
if (-not(Test-WUARebootRequired)) {
try {
# Create a session
$Session = New-Object -ComObject Microsoft.Update.Session
# Import the the offline cab
$UpdateService = $UpdateSvc.AddScanPackageService('Offline Sync Service',"$($FilePath)", 1)
$Searcher = $Session.CreateUpdateSearcher()
$Searcher.ServerSelection = 3 #ssOthers
if ($IncludeSupersededUpdate) {
$Searcher.IncludePotentiallySupersededUpdates = $true
$Searcher.ServiceID = $UpdateService.ServiceID.ToString()
$Criteria = "IsInstalled=0 and DeploymentAction='Installation' or IsPresent=1 and DeploymentAction='Uninstallation' or IsInstalled=1 and DeploymentAction='Installation' and RebootRequired=1 or IsInstalled=0 and DeploymentAction='Uninstallation' and RebootRequired=1"
# Search for updates
$SearchResult = $Searcher.Search($Criteria)
} catch {
Write-Warning -Message "Failed to search for missing updates because $($_.Exception.Message)"
if ($SearchResult) {
Write-Warning -Message "The search result status code was $($SearchResult.ResultCode)"
if ($SearchResult.ResultCode -eq 2) {
# Output what was found
if (($SearchResult.Updates).Count -ne 0) {
$SearchResult.Updates |
ForEach-Object {
Write-Verbose -Message "Missing update: $($_.Title)"
} else {
Write-Verbose -Message 'There are no updates to install' -Verbose
} else {
Write-Warning -Message 'Failed to search for updates'
Write-Warning -Message "The search result status code was $($SearchResult.ResultCode)"
} else {
Write-Warning -Message 'A reboot is pending'
End {}

MSEdge start page

  • Context

I needed to configure a few Windows 10 computers as if they were a kiosk computer.
These 5 computers are part of a workgroup. They are not part of a domain or AD joined or whatever.
I installed the new Microsoft Edge (chromium based) browser and have been tasked to set a start page.

  • Problem

Some settings that can be defined to set the StartPage for example don’t work when the computer is part of a workgroup.
They only work when the computer is part of a domain or is managed by a MDM 😦

It feels like the Apple guru told me that I cannot change the default browser on an iPhone, that Safari is good for me and that I’ve to stick with it 😦 D’oh!

The behavior is documented on this page and says:

If you browse the address edge://policy, you can see the settings that are not applied. Their Status reports ‘Error, Ignored’:

  • Solution

I took me 4 days to come up with a solution. 2 more days than I took me to come up with ideas that led to @sys:doesnotexist : Disallow AutoPlay/Autorun from Autorun.inf.

It wasn’t straightforward. Here’s a quick overview of the ideas I explored:

  1. Observe the behavior of msedge.exe: what dll it loads, what registry keys it queries to find out that the computer is part of a workgroup and is not domain joined or Azure AD joined.
  2. Lie somehow to the computer and make it believe it’s part of a domain.
  3. Provision a temporary Azure AD tenant, configure Intune, push a MSEdge configuration, enroll the device and observe what’s being done at enrolloment.

It appears that MSEdge.exe reads all the registry values under HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge.
It determines after that if the device is part of a workgroup, or is domain-joined or AAD-joined.
It uses and loads the following Dlls: C:\Windows\System32\mdmregistration.dll, C:\Windows\System32\dsreg.dll,…

To join the computer to a domain that’s unreachable. I provisioned a offline blob using djoin.exe.

.\djoin.exe /requestODJ /loadfile C:\blob.dat /windowspath C:\Windows /localos

The 3 most interesting registry keys written by the above djoin.exe command are:

The problem with an offline join is that there’s a slowdown because the netlogon service is trying to reach the domain controller and the Built-in local groups (users and administrators) have the SID of the Domain Users and Domain Admins assigned as members.

The Intune provisioning path is actually the most promising. It takes only a few hours to configure before you can enroll the device. The device should be Azure AD joined and not Azure AD registered to have its security baseline being pushed.

Procmon.exe shows that MSEdge.exe reads the following registry keys:

It looks first for the key name (a GUID) under HKLM\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts.

It uses this key name to open HKLM\SOFTWARE\Microsoft\Enrollments\ and look for a DWORD value named EnrollmentType

0x0 means not enrolled or to be reset according to this page, 0x6 MDM enrolled.

It also reads HKLM\SYSTEM\CurrentControlSet\Control\CloudDomainJoin but this key and its info aren’t required to make MSEdge.exe think it’s MDM enrolled.

Here’s my solution:

Windows Registry Editor Version 5.00
'@ |
Out-File 'C:\Windows\edge.dat' -Encoding 'Ascii'
regedit.exe --% /s C:\Windows\edge.dat
view raw fix-MSedge.ps1 hosted with ❤ by GitHub

At the end, I’ve got Edge Chromium running on a workgroup based computer, reading and applying all the settings under the HKLM\SOFTWARE\Policies key including the RestoreOnStartup and the HomepageLocation values.

Happy days 😎

AutoLogon on Windows 10

  • Context

I’ve been using Autologon for a while on some dedicated computers since Windows XP.
I needed it to configure the user environment/session on hundreds of computers. In other words, I simply needed automation. The combination of both the autologon and a logonscript allowed to do it quickly in a consistent, repeated and reliable manner.
It worked well on Windows 7 by following the steps described in this support page, named How to turn on automatic logon in Windows and without modifying anything in the registry keys used to set it up on Windows XP.

  • Problem

We had to change our Windows 7 computers before its end of life on the 14th of January, 2020.
We decided to use the 1909 branch of Windows 10 but failed to get the autologon working using the technique and registry keys that worked on Windows 7 and since XP 😦
The above support page were not updated yet. In January 2020, it still applied to Windows XP, Windows 7,…
Now that it has been updated, any client Windows operating system has been removed.

We were not the only ones who reported the same issue: it just didn’t work on Windows 10.

There was no explanation on the behavior we observed and no official guidance on how to do it.

There was something underlying that was modifying the registry values. The result was that autologon failed.
It looked like the keyboard layout behavior I described earlier in this blog post.

  • Solution

I used the technique I’ve shown in my previous blog post. Running a scheduled task as NT AUTHORITY\SYSTEM account (S-1-5-18) that sets the registry keys made our day 🙂

Here’s what the code looks like in the post-installation script of our computers.
There’s a script autologon.ps1 dropped in C:\Windows.
It already contains everything required to autologon. The domain name, the username, the password,..
It first takes the last 2 digits of the computername and uses it in the username.
All the standard domain accounts have the same password and they can only logon interactively. There’s a group policy that prevents any lateral movement so that the credential of one account cannot be used on the other computer over the wire.

$desk = -join "$($env:computername)"[-2..-1]
$key = 'HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon'
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('delete',"$($key)",'/v','AutoLogonCount','/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('delete',"$($key)",'/v','AutoLogonChecked','/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('add',"$($key)",'/v','DefaultUserName','/t','REG_SZ','/d',"MyUserPrefix$($desk)",'/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('add',"$($key)",'/v','DefaultPassword','/t','REG_SZ','/d','""','/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('add',"$($key)",'/v','DefaultDomainName','/t','REG_SZ','/d','MyDomainName','/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('add',"$($key)",'/v','AutoAdminLogon','/t','REG_SZ','/d','1','/f')
& (Get-Command "$($env:systemroot)\system32\reg.exe") @('add',"$($key)",'/v','ForceAutoLogon','/t','REG_DWORD','/d','0x1','/f')
'@ | Out-File 'C:\Windows\autologon.ps1' -Encoding 'Ascii'
try {
$errHT = @{ ErrorAction = 'Stop' }
$aHT = @{
Execute = 'C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe'
Argument = '-Exec Bypass -File "C:\Windows\autologon.ps1"'
$HT = @{
TaskName = 'Set-AutoLogon'
User = 'S-1-5-18' #'nt authority\system'
Force = [switch]::Present
Action = (New-ScheduledTaskAction @aHT @errHT)
Register-ScheduledTask @HT @errHT
Write-Verbose -Message "Successfully registered autlogon scheduled task" -Verbose
} catch {
Write-Warning -Message "Failed to register autologon scheduled task because $($_.Exception.Message)"
view raw autologon.ps1 hosted with ❤ by GitHub

About the Applocker service

  • Problem

I use both PowerShell and Applocker a lot. It’s quite natural to do the following

Set-Service -Name AppIDSvc -StartupType Automatic

Instead of configuring it, I get an ‘Access Denied’ 😦

  • Cause

It appears that

Starting with Windows 10, the Application Identity service is now a protected process. Because of this, you can no longer manually set the service Startup type to Automatic by using the Sevices snap-in.

Source: https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-application-control/applocker/configure-the-application-identity-service

It also documents the official two ways of configuring the service StartupType to automatic.
Note that if your device is domain joined, you can use a Domain based GPO to change the service StartupType instead of using LGPO.exe

It’s protected and I can see in the registry the following indicator: the LaunchProtected dword value set to 0x2

  • Solution

I came up with a 3rd (longer) way of doing it (that could be a very long one-liner).

# Get the StartupType
Get-CimInstance -ClassName Win32_Service -Filter "Name='AppIDSvc'"

$cmd = 'Set-Service -Name AppIDSvc -StartupType Auto'
$aHT = @{
 Execute = 'C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe'
 Argument = '-Exec Bypass -Command "{0}"' -f "$($cmd)"
$HT = @{
 TaskName = 'ConfigAppIdSvc'
 User = 'S-1-5-18' # 'NT Authority\System'
 Force = [switch]::Present
 Action = (New-ScheduledTaskAction @aHT)
Register-ScheduledTask @HT | 

# Wait a little bit and get the StartupType
Get-CimInstance -ClassName Win32_Service -Filter "Name='AppIDSvc'"

NB: Although the service is running under the NT Authority\LocalService (S-1-5-19), it requires the NT Authority\System (S-1-5-18) to modify its StartupType.