• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar
  • Skip to footer
ControlUp Community

ControlUp Community

Connect, Learn, and Grow

  • Blog
  • Podcast
  • Meetups
  • Archives
  • Categories
    • ControlUp One Platform
    • ControlUp for Apps
    • ControlUp for Compliance
    • ControlUp for Desktops
    • ControlUp Scripts & Triggers
    • ControlUp Synthetic Monitoring
    • ControlUp for VDI
  • Topics
  • Events
    • Logos & Wallpaper
    • ControlUp.com
  • Join

Creating an Alert for Windows 11 Upgrades Using SCCM and Event Logs

Posted on August 2, 2024

The members of the ControlUp community discussed creating an alert for when users upgrade to Windows 11, and potential methods to determine this information. There was talk of using SCCM for confirmation, querying event logs, and even using a custom script that was shared in the text. The script was described as an experiment that the user can share if others are interested.


Read the entire ‘Creating an Alert for Windows 11 Upgrades Using SCCM and Event Logs’ thread below:

Happy Friday All

is is possible to create a trigger based on when you upgrade to Windows 11

So eg , today 9am you are still on Windows 10 , you then get a popup from our IT team to upgrade to Windows 11

(you see this as a opportunity to miss a few meetings and you update 😄 ) i want to create a alert to show that you just upgraded to Windows 11 once your device comes online again


The message part is easy

The "send it only once after upgrade" might require some additional thought.

How are you performing the actual upgrade?


its done , via the SCCM team and thats the hard part , some users will start the update once they get the popup , others will dismiss and only do it later when they are ready

so for me the important past is , you were on Win 10 and you are now on 11 so send the alert

Hello Joe , Happy Friday


Thinking this through. Ideally you would have some actual confirmation that the upgrade is completed. That information most reliably lives in SCCM. A query to that and then writing it in a custom index would be the nicest approach.

If that’s not feasible, we need to look for something local. Maybe windows event log based

Once its in an index it’s easy. You could even tie a survey into it asking for feedback how to upgrade process went (maybe NPS with an open text question)

We obviously would see a change in OS version in _devices, but I think there is no trigger on differential (e.g. if before it was windows 10 but now windows 11 -> do something)

we have os_install_date in the device_install_date index

But that probably would also include new machines, not just once that are upgraded


i dont see these

we have os_install_date in the device_install_date index


Hm. maybe a custom script we have in our lab. Let me check


Thank you 🙂


It’s in the library


I love this community


although this is a different index, but should contain the same information

I suspect the script I have in my lab was some experience to determine device age. Maybe it’s a @member project lol

“`#requires -runasadministrator

requires -Version 3.0

.SYNOPSIS

Retrieve timestamps that could be candidates for the best OS installation date

.DESCRIPTION

The script retrieves the installation date of the operating system by querying the registry key path "HKLM:\SYSTEM\Setup\Status\SysprepStatus". It also retrieves the last write time of the "C:\Windows\Setup\State\State.ini" file. Additionally, it retrieves the creation date of the computer object in a domain environment or null if not in a domain. It then retrieves the details of the operating system and hardware using CIM instances. Finally, it finds the oldest user profile directory in "C:\Users" excluding the "Public" folder.

.PARAMETER None

.EXAMPLE

.\GetInstalldate.ps1

Runs the script and displays the system information.

.OUTPUTS

The script outputs a custom object containing the following properties:

  • tags: Edge device tags
  • device_group: Edge device group
  • first_user_creation_date: The creation date of the oldest user profile directory.
  • sysprep_date: The installation date of the operating system.
  • os_install_date: The installation date of the operating system.
  • state_ini_last_write_time: The last write time of the "C:\Windows\Setup\State\State.ini" file.
  • computer_id_when_created: The creation date of this devices domain ID if domain joined
  • first_user: The name of the oldest user profile directory.
  • os_name: The name of the operating system.
  • os_version: The version of the operating system.
  • hw_manufacturer: The manufacturer of the hardware.
  • hw_model: The model of the hardware.
  • hw_serial_number: The serial number of the hardware.

>

Define the registry key path

$RegistryPath = "HKLM:\SYSTEM\Setup\Status\SysprepStatus"

$NativeRegQuery = ‘[DllImport("advapi32.dll", CharSet = CharSet.Auto)]

public static extern Int32 RegQueryInfoKey(

Microsoft.Win32.SafeHandles.SafeRegistryHandle hKey,

StringBuilder lpClass,

Int32 lpCls, Int32 spare, Int32 subkeys,

Int32 skLen, Int32 mcLen, Int32 values,

Int32 vNLen, Int32 mvLen, Int32 secDesc,

out System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime

);’

$regData = Add-Type $NativeRegQuery -Name GetRegData -Namespace RegQueryInfoKey -Using System.Text -PassThru

function getRegTime($regPath) {

$reg = get-item $regPath -force

if ($reg.handle) {

$time = New-Object System.Runtime.InteropServices.ComTypes.FILETIME

$result = $regData::RegQueryInfoKey($reg.Handle, $null, 0,0,0,0,0,0,0,0,0, [ref]$time)

if ($result -eq 0) {

$low = [uint32]0 -bor $time.dwLowDateTime

$high = [uint32]0 -bor $time.dwHighDateTime

$timeValue = ([int64]$high -shl 32) -bor $low

return [datetime]::FromFileTime($timeValue)

}

}

}

Clear-Host

$sysPrepDate = (getRegTime $RegistryPath)

$ErrorActionPreference = "silentlyContinue"

$stateIniLastWriteTime = (Get-Item "C:\Windows\Setup\State\State.ini").LastWriteTime

$setupActLogLastWriteTime = (Get-Item "C:\Windows\Panther\setupact.log").LastWriteTime

if (Get-WmiObject -Class Win32_ComputerSystem | Select-Object -ExpandProperty PartOfDomain) {

$searcher = New-Object system.directoryservices.directorysearcher

$searcher.PropertiesToLoad.AddRange(@(‘whencreated’))

$null = $searcher.Filter = "(name=$env:ComputerName)"

$computerWhenCreated = $searcher.FindOne().Properties.whencreated

} else {

$computerWhenCreated = $null

}

$OperatingSystem = Get-CimInstance -ClassName Win32_OperatingSystem -ErrorAction Stop

$Hardware = Get-CimInstance -ClassName Win32_ComputerSystem -ErrorAction Stop

$Bios = Get-CimInstance -ClassName Win32_BIOS -ErrorAction Stop

$OSDisplayVersion = $null

$WindowsCurrentVersion = Get-Item ‘HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion’

‘DisplayVersion,ReleaseId’ -split ‘,’ | ForEach-Object {

$KeyName = $_

if ([string]::IsNullOrWhiteSpace($OSDisplayVersion)) {

$OSDisplayVersion = $WindowsCurrentVersion.GetValue($KeyName)

}

}

Define the path to the users’ profiles

Define the path to the users’ profiles

$usersPath = (Get-ItemProperty -path ‘HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList’ -Name ‘ProfilesDirectory’).ProfilesDirectory

Get the list of user profile directories, excluding the Public folder

$oldestProfile = $null

$ignoreProfiles = @(‘Default’,’Default User’,’Public’,’All Users’,’desktop.ini’, ‘Administrator’)

Get-ChildItem $usersPath | Where-Object { $.PSIsContainer -and $.Name -notin $ignoreProfiles } | Sort-Object CreationTime | Select-Object -First 1 | ForEach-Object {$oldestProfile = $_}

$EdgeDXRegistrationDate = [datetime]::MaxValue

Get-ChildItem -Path ‘C:\ProgramData\Avacee\sip_agent\cachefiles’ | Where-Object {$_.Name -in @(‘DeviceAccessToken.txt’,’uid.txt’)} | ForEach-Object {

$fileObj = $_

if ($fileObj.LastWriteTime -lt $EdgeDXRegistrationDate) {

$EdgeDXRegistrationDate = $fileObj.LastWriteTime

}

}

$dateList = @()

function ConvertToDateString($date) {

if ($date.Year -eq 9999) {

return $null

}

if ($date.Year -lt 2000) {

return $null

}

if ($date) {

return $date.Date.ToUniversalTime().ToString("yyyy-MM-dd")

}

return $null

}

$customObject = $null

Create a custom object

$customObject = [PSCustomObject]@{

tags = "$env:EDXDEVICETAGS"

device_group = "$env:EDXDEVICEGROUP"

sysprep_date = ConvertToDateString($sysPrepDate)

state_ini_last_write_time = ConvertToDateString($stateIniLastWriteTime)

setup_act_log_last_write_time = ConvertToDateString($setupActLogLastWriteTime)

computer_id_when_created = ConvertToDateString($computerWhenCreated)

os_name = $OperatingSystem.Caption

os_patch = $OSDisplayVersion

os_version = $OperatingSystem.Version

os_install_date = ConvertToDateString($OperatingSystem.InstallDate)

hw_manufacturer = $Hardware.Manufacturer

hw_model = $Hardware.Model

bios_install_date = ConvertToDateString($Bios.InstallDate)

bios_release_date = ConvertToDateString($Bios.ReleaseDate)

first_user = $oldestProfile.Name

first_user_creation_date = ConvertToDateString($oldestProfile.CreationTime)

edge_registration_date = ConvertToDateString($EdgeDXRegistrationDate)

}

$ignoreDates = @(‘edge_registration_date’,’bios_release_date’)

$dateList = $customObject.psobject.properties.name | Where-Object {$ -match "(date|time|when_created)$" -and $ -notin $ignoreDates} | ForEach-Object {

$FieldName = $_

$StrValue = $customObject.$FieldName

if ($StrValue){

$Value = ([datetime]::ParseExact($StrValue, "yyyy-MM-dd", $null))

} else {

$Value = $null

}

[pscustomobject]@{

Key = $FieldName

Value = $Value

}

} | Where-Object {$null -ne $_.Value} | Sort-Object -Property Value

$maxDate = $dateList | Measure-Object -Property Value -Maximum | Select-Object -ExpandProperty Maximum

$minDate = $dateList | Measure-Object -Property Value -Minimum | Select-Object -ExpandProperty Minimum

$maxDateFields = $dateList | Where-Object {$_.Value -eq $maxDate} | Select-Object -ExpandProperty Key

$minDateFields = $dateList | Where-Object {$_.Value -eq $minDate} | Select-Object -ExpandProperty Key

$customObject | Add-Member -MemberType NoteProperty -Name "max_date" -Value (ConvertToDateString($maxDate))

$customObject | Add-Member -MemberType NoteProperty -Name "min_date" -Value (ConvertToDateString($minDate))

$customObject | Add-Member -MemberType NoteProperty -Name "max_date_fields" -Value ($maxDateFields -join ",")

$customObject | Add-Member -MemberType NoteProperty -Name "min_date_fields" -Value ($minDateFields -join ",")

Write-Output("### SIP DATA BEGINS ###")

Write-Output(ConvertTo-Json $customObject -Compress)

Write-Output("### SIP DATA ENDS ###")

Write-Output("### SIP EVENT BEGINS ###")

Write-Output("OS installdate has run")

Write-Output("### SIP EVENT ENDS ###") “`


Yes, it’s one of my experiments. Don’t think its the last one. I you’re interested, I can send you the last one Monday.

Continue reading and comment on the thread ‘Creating an Alert for Windows 11 Upgrades Using SCCM and Event Logs’.  Not a member? Join Here!


Categories: All Archives, ControlUp for Desktops, ControlUp Scripts & Triggers
Topics: Automation & Alerting, BIOS, ControlUp Agent, Logs, Microsoft, Microsoft SCCM, Microsoft Windows, Scripts, Triggers

Ask Us Anything, Connect, Learn, and Grow with the ControlUp Community!

Login to the ControlUp Community to ask us anything, stay up-to-date on what’s new and coming soon and meet other like-minded techies like you.

Not already a member? Join Today!

Primary Sidebar

ControlUp Academy

Enroll in ControlUp Academy for expert-led technical training, equipping you with skills to effectively deploy, manage, and grow your ControlUp investment.

Learn here >

Rotating Images

Hidden Gem from our Community on Slack!

ControlUp Betas - What's Coming Next?
NEW ControlUp Features - Stay Up-to-Date!
ControlUp Scripts - Scripting, Zero to Hero
Latest KB Articles - Be the First to Learn
Did you Know - with Sivan Kroitoru
Practical Perspectives Technical Use Case Training

Video Tutorials Library

Visit our technical how-to videos, offering step-by-step tutorials on advanced features, troubleshooting, and best practices.

Watch here >

ControlUp Blog

Check out the ControlUp blog for expert advice and in-depth analysis.

Read here >

ControlUp Script Library

Visit the ControlUp technical script library, which offers a multitude of pre-built scripts and custom actions for your monitoring and troubleshooting requirements.

See here >

ControlUp Support

Visit the ControlUp support home and to delve deeper into ControlUp solutions.

Browse here >

Download ControlUp RealTime DX

Start with ControlUp for real-time end-user environment insights, swift troubleshooting, and unprecedented performance optimization. Download now.

Download here >

Footer

      

ControlUp Community
Of Techie, By Techie, For Techie!

Terms of Use | Privacy Policy | Security
Dive Deeper, Learn more at ControlUp.com

  • facebook
  • twitter
  • youtube
  • linkedin

© 2023–2025 ControlUp Technologies LTD, All Rights Reserved.

We use cookies to ensure that we give you the best experience on our website. by continuing to use this site you agree to our Cookie policy..