History of previous feature updates

  • Context

I’ve discovered that the history of previous versions you had before running a feature update is stored in the registry under HKLM\System\Setup

  • Solution

I wrote a function to read the information under this key:

Function Get-PreviousUpgrade {
<#
.SYNOPSIS
Get info about previous feature updates
.DESCRIPTION
Get info about previous feature updates being used to upgrade Windows 10 to a new branch.
.EXAMPLE
Get-PreviousUpgrade
#>
[CmdletBinding()]
Param()
Begin {}
Process {}
End {
try {
$names = (Get-Item -Path 'HKLM:\SYSTEM\Setup' -ErrorAction Stop).GetSubKeyNames() |
Where-Object { $_ -match '^Source\sOS\s\(Updated\son\s' }
} catch {
Write-Warning -Message "Failed to read the registry key because $($_.Exception.Message)"
}
if ($names) {
$names |
ForEach-Object {
$date = ([regex]'^Source\sOS\s\(Updated\son\s(?<Date>.+)\)').Matches($_) |
Select-Object -ExpandProperty Groups |
Select-Object -Last 1 -ExpandProperty Value
$FromBranch = (Get-ItemProperty -Path (Join-Path -Path 'HKLM:\SYSTEM\Setup' -ChildPath $_) -Name 'ReleaseId').'ReleaseId'
[PSCustomObject]@{
Date = [datetime]$date
Branch = $FromBranch
}
}
} else {
Write-Warning -Message 'No previous upgrades found'
}
}
}

Here’s how to use it 🙂

Get-PreviousUpgrade | 
Where  { $_.Branch -eq '1809' } | 
Select -Property Date

Friday fun: Windows Feature Update progress

  • Context

I started recently to launch some feature updates of Windows 10 in a scheduled task running as ‘NT AUTHORIY\SYSTEM’ account. It also means that the UI is hidden for whoever is logged on the computer.

I was also looking for some additional info about:
– how to detect that a feature is running
– how to detect that a feature has just finished running
– how to detect that a reboot is pending because of the feature update that just ran

  • Solution

I started investigating using procmon.exe and tracked what’s written under HKLM\System\setup.

I noticed that I can track the progress of a feature update by using a one-liner:

While (ps setup* ) { Write-Progress -Activity Upgrade -PercentComplete "$((gp HKLM:\SYSTEM\Setup\MoSetup\Volatile -Name SetupProgress).SetupProgress)" -Status "at $((gp HKLM:\SYSTEM\Setup\MoSetup\Volatile -Name SetupProgress).SetupProgress)%" }

It appears that a SetupProgress value is written under the registry key HKLM:\SYSTEM\Setup\MoSetup\Volatile
Here’s a more readable splatted version of the one-liner:

While (Get-Process -Name setup*) {
 $k = 'HKLM:\SYSTEM\Setup\MoSetup\Volatile'
 $n = 'SetupProgress'
 $p = Get-ItemProperty -Path $k -Name $n
 $HT = @{
  Activity =  'Upgrade'
  PercentComplete = "$($($p).$n)"
  Status = "at $($($p).$n)%"
 }
 Write-Progress @HT
}

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 ?
https://docs.microsoft.com/en-us/windows/win32/wua_sdk/using-wua-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 {
<#
.SYNOPSIS
Start an offline WUA scan
.DESCRIPTION
Start an offline WUA (Windows Update Agent) scan using wsusscn2.cab.
.PARAMETER FilePath
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.
.EXAMPLE
Start-WUOfflineScan -FilePath C:\temp\wsusscn2.cab -Verbose
.NOTES
You can get the offline wsusscn2.cab from:
http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab
Official doc is:
https://docs.microsoft.com/en-us/windows/win32/wua_sdk/using-wua-to-scan-for-updates-offline
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory)]
[ValidateScript({Test-Path -Path $_ -PathType Leaf})]
$FilePath,
[switch]$IncludeSupersededUpdate
)
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)"
}
break
}
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:
HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\JoinDomain
HKLM\CurrentControlSet\Control\Lsa\OfflineProvisioning
HKLM\SYSTEM\CurrentControlSet\Control\Lsa\OfflineJoin

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
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge]
"BrowserSignin"=dword:00000000
"BrowserAddProfileEnabled"=dword:00000000
"NonRemovableProfileEnabled"=dword:00000001
"HomepageIsNewTabPage"=dword:00000000
"NewTabPageLocation"="about:blank"
"SmartScreenEnabled"=dword:00000001
"SSLErrorOverrideAllowed"=dword:00000000
"ShowHomeButton"=dword:00000001
"HideFirstRunExperience"=dword:00000001
"PasswordManagerEnabled"=dword:00000000
"SSLVersionMin"="tls1.2"
"RestoreOnStartup"=dword:00000004
"SitePerProcess"=dword:00000001
"AuthSchemes"="ntlm,negotiate"
"SmartScreenPuaEnabled"=dword:00000001
"PreventSmartScreenPromptOverride"=dword:00000001
"DefaultPluginsSetting"=dword:00000002
"NativeMessagingUserLevelHosts"=dword:00000000
"HomepageLocation"="https://myhomepage&quot;
"ClearBrowsingDataOnExit"=dword:00000001
"PreventSmartScreenPromptOverrideForFiles"=dword:00000001
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge\ExtensionInstallBlocklist]
"1"="*"
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge\RestoreOnStartupURLs]
"1"="https://myhomepage&quot;
[-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Provisioning\OMADM\Accounts\FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF]
[-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments\FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Enrollments\FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF]
"EnrollmentType"=dword:00000000
'@ |
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 | 
Start-ScheduledTask

# 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.

Quick post about: LCM failed to deserialize configuration status

    • Context

I’ve been running some DSC configuration after a feature update to fix missing or broken things left behind.
I’ve encountered some computers where a DSC config was forgotten and running for quite some time.

    • Problem

On these computers, I sometimes get an error saying LCM failed to deserialize configuration status when I run the Get-DscConfigurationStatus cmdlet.

Here are some potential reasons why it fails when I run the the Get-DscConfigurationStatus cmdlet.


The DSCStatusHistory.mof is about 10MB


And there are 50K files in the C:\Windows\system32\Configuration\ConfigurationStatus\ folder.

    • Solution

Here’s what I run to fix it. Kudo to Dave Wyatt who posted the solution on the powershell.org forum. Thanks Dave 🙂

ren C:\Windows\System32\Configuration\DSCStatusHistory.mof DSCStatusHistory.mof.prev
dir C:\Windows\System32\Configuration\ConfigurationStatus | del -Force

If you do not wait that the LCM applies at least once, I get another message:

But, when I do and the LCM runs at least once, I get it fixed 😀

About PowerShell update notifications

It’s nice that we have such a good documentation about_update_notifications 🙂

What is unclear for me that could be improved is what kind of environment variable are required and when the check happens.

First let’s see how these notifications look like:

I naively set an environment variable inside the current user profile.

if ($PSVersionTable.PSEdition -eq 'Core') {
 Set-Item -Path Env:\POWERSHELL_UPDATECHECK -Value 'Off'
}

It means that on Windows, a ‘Process‘ environment variable being set using PowerShell profiles doesn’t work.
The notification check happens before a profile is processed.

Let’s test a ‘User‘ environment variable

# Set
[Environment]::SetEnvironmentVariable(
 'POWERSHELL_UPDATECHECK',
 'Off',
 [System.EnvironmentVariableTarget]::User
)
# Get
[Environment]::GetEnvironmentVariables(
 [System.EnvironmentVariableTarget]::User
)

A new shell or process should be started to take into account this new User environment variable defined.
The notification check doesn’t occur in this case.

Delete first the User env. variable.

# Set to NUL to delete
[Environment]::SetEnvironmentVariable(
 'POWERSHELL_UPDATECHECK',
 $null,
 [System.EnvironmentVariableTarget]::User
)
# Get
[Environment]::GetEnvironmentVariables(
 [System.EnvironmentVariableTarget]::User
)

Set a ‘Machine‘ environment variable

# Set
[Environment]::SetEnvironmentVariable(
 'POWERSHELL_UPDATECHECK',
 'Off',
 [System.EnvironmentVariableTarget]::Machine
)
# Get
[Environment]::GetEnvironmentVariables(
 [System.EnvironmentVariableTarget]::Machine
)

A new shell or process should be started to take into account this new Machine environment variable defined.

The variable appears to be defined in the PowerShell environment drive and can be retrieved using a .Net method call.

What else works?
If you are in a Command Prompt, this works:

If you are in a Windows PowerShell console,

Set-Item -Path Env:\POWERSHELL_UPDATECHECK -Value 'Off'

The environment variable can be inherited by the parent process whatever its type.

Conclusion: The type is actually irrelevant.
You have multiple choices to set this environment variable. Either in the Windows PowerShell profile (if pwsh.exe is started from there), or in the Machine or User environment variables (if pwsh.exe is started by double-clicking or has cmd.exe as a parent process). What doesn’t work is a Process environment variable defined in a PowerShell (Core) profile.

How to clear a local Applocker policy

There are 3 main actions in this menu when you edit the local Applocker policy. You can Import, Export and Clear a policy.

Let’s see how one can clear a local Applocker policy.

If you use Windows PowerShell, you can directly access the built-in Applocker module.
In this case, you can use the following shortcut:

$null | New-AppLockerPolicy -User EveryOne -EA 0 | 
Set-AppLockerPolicy -Verbose

NB: EA is the Alias of ErrorAction and 0 means SilentlyContinue.
It’s required to avoid displaying a message saying:

New-AppLockerPolicy : Cannot validate argument on parameter ‘FileInformation’. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.

.
Even if there’s an error thrown, a Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.AppLockerPolicy is created and sent to the output stream.

Unfortunately, the same shortcut cannot be used within PowerShell 7.0.1 (current latest version).
You need the following 2 steps:

# step 1: write an empty policy to a file
$null | New-AppLockerPolicy -User EveryOne -EA 0 -Xml | 
Out-File ~/Documents/empty.xml

# step 2: import that file
Set-AppLockerPolicy ~/Documents/empty.xml

NB: Notice the addition of the -XML switch in the first step.

Here’s another approach for Windows PowerShell that looks like the example provided by Microsoft, named delete-an-applocker-rule that tells you actually how to clear *all* the rules.

The following example doesn’t write a file to disk and directly clears the local Applocker Policy

#Requires -Module Applocker
#Requires -PSEdition Desktop
#Requires -RunAsAdministrator
Function Clear-ApplockerLocalPolicy {
[CmdletBinding()]
Param()
Begin {}
Process {
Try {
$null = Get-AppLockerPolicy -Local -ErrorAction SilentlyContinue
[Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.AppLockerPolicy]::FromXml(
@'
<AppLockerPolicy Version="1">
<RuleCollection Type="Exe" EnforcementMode="NotConfigured" />
<RuleCollection Type="Msi" EnforcementMode="NotConfigured" />
<RuleCollection Type="Script" EnforcementMode="NotConfigured" />
<RuleCollection Type="Dll" EnforcementMode="NotConfigured" />
<RuleCollection Type="Appx" EnforcementMode="NotConfigured" />
</AppLockerPolicy>
'@
) |
Set-AppLockerPolicy -ErrorAction Stop
Write-Verbose -Message 'Successfully cleared local Applocker policy'
} catch {
Write-Error $_
}
}
End {}
}

NB: Notice the first call at line 11 to a built-in command from the Applocker module. It’s used to avoid this error message: Unable to find type [Microsoft.Security.ApplicationId.PolicyManagement.PolicyModel.AppLockerPolicy].
If that first call at line 11 is missing, when you do the following, you get:

Weird, isn’t? If you’ve an explanation, please add a comment 🙂