PowerBI Reports for Windows 10 Feature Update Compliance

This morning I saw an interesting tweet from Sandy Zeng with a Log Analytics workbook she’d created for W10 feature updates based on Update Compliance data. I’d been meaning to create a similar report for that myself in PowerBI for some time, so I took inspiration from her tweet and got to work on something!

Microsoft’s Update Compliance solution can be used to report on software updates and feature updates status across your estate from Windows telemetry data. If you use Desktop Analytics, you can even combine the data for richer reporting.

Log Analytics allows exporting of queries in Power Query M formula language, which can be imported into PowerBI to create some nice reports.

Here’s a screenshot of what I ended up with. You can filter the data to view devices with Safeguard holds, for example, which since Windows 10 2004 has been a show stopper for many wanting to upgrade…

There are pages for 2004 and 20H2, as well as a page listing some of the known Safeguard hold IDs that have been publicly disclosed by Microsoft.

You can download this report for your own use with the links below. Note I have created two reports – the first assumes you have both Update Compliance and Desktop Analytics using the same Log Analytics workspace. If this is not the case for you, download the second report which doesn’t link to DA data and just uses Update Compliance. The only data I’ve included from DA is the make and model since these can be helpful in analysing devices affected by Safeguard holds.

Windows 10 Feature Update Compliance

Windows 10 Feature Update Compliance (no DA)

To create your own report, you’ll need the latest version of PowerBI desktop installed, with preview support for dynamic M query parameters.

Open PowerBI and go to File > Options and settings > Options > Preview features and enable Dynamic M Query Parameters.

Restart PowerBI and open the downloaded PBI template. Upon opening, you’ll be prompted for the Log Analytics workspace ID. You can find this on the Overview pane of your workspace in the Azure portal.

The reports contain data with a timespan of the last 48 hours, but you can change this if you want by editing the queries in the Advanced editor, and changing the value “P2D”.

You also might want to play around with the LastScan filter so you only get devices with a recent scan date, and avoid duplicates.

Hope it’s helpful!

Using a LiteDB portable database with your PowerShell project

I was working on a PowerShell project recently where I needed to reference a large number of items – more than I wanted to add as an array or hash table in script, or even an external file like an XML document. So I searched for a portable, standalone database that I could distribute with the script and happened across LiteDB, a lightweight, single-file NoSQL database. It seemed the ideal solution as there’s a .Net code library for it, and so far I’m pretty impressed.

In this blog I’ll cover some of the basics for working with a LiteDB database using PowerShell.

First you need to download the package from NuGet, then extract it. Grab the LiteDB.dll file from ..\lib\net45 folder and add it to your project.

Add the library to your PowerShell session:

Add-Type -Path "C:\Temp\LiteDB.dll"

Create a database (this can also be used to open the same database):

$database = [LiteDB.LiteDatabase]::new("C:\Temp\CountryCodeDatabase.db")

You can also open a database in read-only mode. This can be useful if the database will be placed in a location where the user may not have write access, such as ProgramFiles or ProgramData.

$database = [LiteDB.LiteDatabase]::new("Filename='C:\Temp\CountryCodeDatabase.db';ReadOnly=$true")

Create (or open) a collection (think table). In this case, I’m adding an automatically generated Id at the same time:

$Collection = $database.GetCollection("CountryCodes",[LiteDB.BsonAutoId]::Int64)

Now you can add a record (or document in NoSQL speak) using a BsonDocument:

$BsonDocument = [LiteDB.BsonDocument]::new()
$BsonDocument["Country"] = "United Kingdom"
$BsonDocument["Code"] = "44"
$null = $Collection.Insert($BsonDocument)

Or you can insert many records at once:

$Hash = @{
    "United States" = 1
    "Brazil" = 55
    "Netherlands" = 31
    "Lebanon" = 961
    "Iceland" = 354
}
Foreach ($Country in $Hash.Keys)
{
    $BsonDocument = [LiteDB.BsonDocument]::new()
    $BsonDocument["Country"] = $Country
    $BsonDocument["Code"] = $Hash["$Country"]
    $null = $Collection.Insert($BsonDocument)
}

To view the entries in the database file you can use LiteDB.Studio. Connect to the database file, double-click the collection and Run the select statement:

You can query the database directly in the UI:

You can also query in PowerShell, there are several ways to do that, for example:

$Collection.Find("Code = '44'")
$Collection.Query().Where("Code = '44'").ToDocuments()
$Collection.FindById(1)

All the above queries return the same result:

If you want to reference or retrieve a specific value from the result, it depends how you queried it, for example

$Result = $Collection.Find("Code = 354")
$Result.RawValue["Country"].RawValue

$result = $Collection.Query().Where("Code = 354")
$Result.First()["Country"].RawValue

$Result = $Collection.FindById(3)
$Result["Country"].RawValue

You could also view an entire collection, for example:

($Collection.FindAll()).ToList() | foreach {
    [pscustomobject]@{
        Id = $_["_id"].RawValue
        Country = $_["Country"].RawValue
        Code = $_["Code"].RawValue
    }
}

If you want to update a record with new values:

$Result = $Collection.FindById(1)
$Result["Code"] = "16"
$Collection.Update($result)

To delete a record:

$Collection.Delete(2)

View the changes in LiteDB Studio:

Finally, dispose the database object to release it and commit the records:

$database.Dispose()

Pretty simple really. LiteDB is a handy way to store a large number of items such as settings or properties that you need to distribute with a PowerShell script or project.

Prevent Users from Disabling Toast Notifications – Can it be Done?

Another toast notifications post – this time to deal with an issue where users have turned off toast notifications. In my deployment of Windows 10 feature updates for example, I use toast notifications to inform users an update is available. Once we hit the installation deadline, the notifications become more aggressive and display more frequently and do not leave the screen unless the user actions or dismisses them. But we found that some users turn off toast notifications altogether – perhaps they just don’t like any notifications, or perhaps they don’t like being reminded to install the feature update.

In any case, since toast notifications are a key communications channel with our users, it’s important for us that they stay enabled.

Users can disable toast notifications in Settings > System > Notification & actions – simply turn off the setting Get notifications from apps and other senders.

There is also a group policy setting that can disable toast notifications and lock the setting so the user can’t turn it back on.

However, I was surprised to find no setting to do the opposite thing – turn notifications on and lock the setting preventing the user from turning them off..

What I did find is a registry key that enables or disables toast notifications in the user context, but it doesn’t take effect without restarting a service called Windows Push Notifications User Service.

Here’s the registry key. Setting it to 1 enables notifications and 0 disables.

Because this is not being done by group policy, you can’t lock the setting unfortunately. But what you can do is use a Configuration Manager compliance baseline, or even Proactive remediations in MEM, to detect and remediate and turn notifications back on if a user has turned them off. It needs to run with sufficient frequency to be effective.

Here is a detection script for MEMCM that will check the registry key and if it exists and is set to zero, will flag non-compliance.

$ToastEnabled = Get-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications" -Name ToastEnabled -ErrorAction SilentlyContinue | Select -ExpandProperty ToastEnabled
If ($ToastEnabled -eq 0)
{
    Write-host "Not compliant"
}
Else
{
    Write-host "Compliant"
}

And here’s a remediation script that will set the registry key to the ‘enabled’ value, and restart the push notifications service.

Set-ItemProperty -Path "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\PushNotifications" -Name ToastEnabled -Value 1 -Force
Get-Service -Name WpnUserService* | Restart-Service -Force

Remember to run these in the user context and allow remediation.

With this active, we can’t completely prevent users from turning off notifications altogether, but if they do, we’ll turn them back on. If they want to fight with the remediation, that’s on them 🙂