Keyman Prompts UAC at install while running initial keyboard installs

I’ve encountered an issue with my Keyman 18.0.236 deployment script that I deploy using PSADT (PowerShell Deployment Toolkit) in ConfigMgr via Software Center on client machines. This has worked great over the years, and you lovely folks helped me here recently to get that up to date from v15 to v18 and switch to using the exe installer so users didn’t get a UAC prompt when they tried to run the software.

When deploying Keyman, I first install Keyman itself:

## Install Keyman
        Execute-Process -Path "$dirFiles\keyman-18.0.236.exe" -Parameters "-s -o"

After this, I have some post-install steps that run:

## Initialize Keyman for the first time
        Execute-Process -Path "$envProgramFilesX86\Keyman\Keyman Desktop\kmshell.exe" -Parameters "-firstrun"

## Copy and install the SENCOTEN keyboard language pack
        Copy-File -Path “$dirSupportFiles\fv_sencoten.kmp” -Destination “$envProgramFilesX86\Keyman\Keyman Desktop\”
        Execute-Process -Path "$envProgramFilesX86\Keyman\Keyman Desktop\kmshell.exe" -Parameters "-i ""$envProgramFilesX86\Keyman\Keyman Desktop\fv_sencoten.kmp"" -s"

## Register the SENCOTEN language pack for each user on login (required in Keyman version 14 and up)
        Set-ActiveSetup -StubExePath "$envProgramFilesX86\Keyman\Keyman Desktop\kmshell.exe" -Arguments "-s -ikl fv_sencoten str-Latn-CA" -Description 'SENCOTEN language pack Keyman registration' -Key 'SENCOTENRegistration' -ContinueOnError $true

## Copy and install the Canadian Multilingual Standard Basic keyboard language pack
        Copy-File -Path “$dirSupportFiles\basic_kbdcan.kmp” -Destination “$envProgramFilesX86\Keyman\Keyman Desktop\”
        Execute-Process -Path "$envProgramFilesX86\Keyman\Keyman Desktop\kmshell.exe" -Parameters "-i ""$envProgramFilesX86\Keyman\Keyman Desktop\basic_kbdcan.kmp"" -s"

## Register the Canadian Multilingual Standard Basic language pack for each user on login (required in Keyman version 14 and up)
        Set-ActiveSetup -StubExePath "$envProgramFilesX86\Keyman\Keyman Desktop\kmshell.exe" -Arguments "-s -ikl basic_kbdcan fr-CA" -Description 'Canadian Multilingual Standard Basic language pack Keyman registration' -Key 'CanadianMultilingualRegistration' -ContinueOnError $true

## Set all Keyman keyboards as active for each user
        [scriptblock]$HKCURegistrySettings = {
            Set-RegistryKey -Key 'HKEY_CURRENT_USER\Software\Keyman\Keyman Engine\Active Keyboards\basic_kbdcan' -Name 'keyman id' -Value '0' -Type String -SID $UserProfile.SID -ContinueOnError:$True
            Set-RegistryKey -Key 'HKEY_CURRENT_USER\Software\Keyman\Keyman Engine\Active Keyboards\fv_sencoten' -Name 'keyman id' -Value '1' -Type String -SID $UserProfile.SID -ContinueOnError:$True
            Set-RegistryKey -Key 'HKEY_CURRENT_USER\Software\Keyman\Keyman Engine\Active Keyboards' -Name '0' -Value 'basic_kbdcan' -Type String -SID $UserProfile.SID -ContinueOnError:$True
            Set-RegistryKey -Key 'HKEY_CURRENT_USER\Software\Keyman\Keyman Engine\Active Keyboards' -Name '1' -Value 'fv_sencoten' -Type String -SID $UserProfile.SID -ContinueOnError:$True

## Disable checking for updates, showing on startup, and submitting telemetry data for all users
            Set-RegistryKey -Key 'HKEY_CURRENT_USER\Software\Keyman\Keyman Engine' -Name 'check for updates' -Value '0' -Type DWord  -SID $UserProfile.SID -ContinueOnError:$True
            Set-RegistryKey -Key 'HKEY_CURRENT_USER\Software\Keyman\Keyman Engine' -Name 'show startup' -Value '0' -Type DWord  -SID $UserProfile.SID -ContinueOnError:$True
            Set-RegistryKey -Key 'HKEY_CURRENT_USER\Software\Keyman\Keyman Engine' -Name 'automatically report errors' -Value '0' -Type DWord  -SID $UserProfile.SID -ContinueOnError:$True
            Set-RegistryKey -Key 'HKEY_CURRENT_USER\Software\Keyman\Keyman Engine' -Name 'automatically report usage' -Value '0' -Type DWord  -SID $UserProfile.SID -ContinueOnError:$True
        }

        Invoke-HKCURegistrySettingsForAllUsers -RegistrySettings $HKCURegistrySettings

This does a few things:
•Runs the first-run task for Keyman
•Copies the SENCOTEN keyboard to Keyman’s install directory
•Installs the SENCOTEN keyboard
•Sets ActiveSetup keys to be executed for each user since the keyboards need to be installed per user
•Copies the Canadian Multilingual keyboard
•Installs the Canadian Multilingual keyboard
•Sets ActiveSetup keys for this keyboard
•Set Keyman preferences per user

The way the ActiveSetup keys work with PSADT is that it creates the ActiveSetup keys, and then if there’s a user currently logged in, it executes them right away as the user.

The script itself executes cleanly and setup completes, however, if a user is logged in at the time of install, even though the install runs in Software Center as SYSTEM, a UAC prompt is triggered.

The script finishes cleanly and error free, but the result is that the SENCOTEN keyboard installs totally fine, followed by the Canadian keyboard not installing for the currently logged in user. If other users log in, the ActiveSetup keys run correctly and they receive both keyboards without a UAC prompt.

When I review the install log, the UAC prompt comes up when the second keyboard attempts to install for the logged in user:

Create scheduled task to run the process [C:\Program Files (x86)\Keyman\Keyman Desktop\kmshell.exe -s -ikl basic_kbdcan fr-CA] as the logged-on user
[C:\WINDOWS\System32\schtasks.exe] is a valid fully qualified path, continue.
Working Directory is [C:\WINDOWS\System32].
Executing [C:\WINDOWS\System32\schtasks.exe /create /f /tn PSAppDeployToolkit-ExecuteAsUser /xml "C:\Users\Public\PSAppDeployToolkit\PSAppDeployToolkit-ExecuteAsUser.xml"]
Execution completed with exit code [0].
Trigger execution of scheduled task with command [C:\Program Files (x86)\Keyman\Keyman Desktop\kmshell.exe -s -ikl basic_kbdcan fr-CA] as the logged-on user
[C:\WINDOWS\System32\schtasks.exe] is a valid fully qualified path, continue.
Working Directory is [C:\WINDOWS\System32].	
Executing [C:\WINDOWS\System32\schtasks.exe /run /i /tn PSAppDeployToolkit-ExecuteAsUser]...
Execution completed with exit code [0].
Waiting for the process launched by the scheduled task [PSAppDeployToolkit-ExecuteAsUser] to complete execution (this may take some time)...

[UAC PROMPT IS TRIGGERED] [CANCEL IT]

Exit code from process launched by scheduled task [0].

[SCRIPT CONTINUES…]

If I copy the command “C:\Program Files (x86)\Keyman\Keyman Desktop\kmshell.exe -s -ikl basic_kbdcan fr-CA” that the key is trying to run, and just run it manually in CMD as the logged in user, it proceeds to install the Canadian keyboard no problem.

The only hint as to what’s actually happening is the UAC prompt itself. The Program location listed in the UAC prompt is:

C:\Users\<LoggedInUser>\AppData\Local\Keyman\UpdateCache\keyman-18.0.238.exe -au

My current running suspicion is:
•Keyman is installing just fine
•Keyman installs the first keyboard, but by the time the second keyboard is triggered to be installed, Keyman has an update available and is trying to install it
•Because the ActiveSetup keyboard install task (kmshell.exe -s -ikl) is running as the currently logged on user, the update also tries to execute as that user, but can’t because they’re a standard user

Does this sound like the problem?

If so, I can think of two potential fixes:
•Maybe I can simply move my preferences code block that’s currently at the end, and move it before the keyboards are installed, or even before firstrun is triggered? That would ensure that by the time kmshell.exe runs, the preference for each user for check for updates is already set to 0.
•Is there some other way to suppress the -au (assuming this is ‘auto update’) check that early on?

Any other suggestions or something I might be missing?

Thanks in advance!

Hello again,
I agree from your detailed analysis; that is what is happening. The best step would be as you suggested to move the preferences block ## Disable checking for updates, showing on startup, and submitting telemetry data for all users after the firstrun but before the keyboards are installed.

Ross

Perfect, that did it. Thanks! Keyman installs clean and silently now, and the Canadian Multilingual keyboard shows up correctly now instead of the UAC prompt interrupting that part.

Just wanted to share some other strange behavior I came across in testing this, as it feels like there’s a bug in update handling.

•This issue did not occur, strangely enough, when the logged in user is logged in via Remote Desktop instead of locally; the script executed and never once prompted the user for UAC. Maybe a race condition for the update to be ready and the script happened to finish first?

•If I navigate to the UAC prompt path of AppData\Local\Keyman\UpdateCache on the machine that ran the Software Center install via Remote Desktop (and didn’t prompt for UAC), it still had keyman-18.0.238.exe in that directory, along with a cache.json file. Even though the preferences for that user are to not check for updates (as per install script assigned registry entries), every time Keyman is launched on that machine, Keyman displays a message that says “An update to Keyman has been downloaded and is ready to install.”. If I close Keyman, delete both the update executable and the cache.json file, then relaunch Keyman, that same Keyman update prompt is still there.

So, somewhere, Keyman gets stuck in a state where it’s still asking if you want to update, even though UpdateCache is empty, making me think there’s a flag being set somewhere else that’s telling Keyman there’s an update available and to run it, regardless of the state of the check for updates option or the UpdateCache contents. The only way I’ve found to get rid of the update popup is by clicking “Update” in that window, which will then end the process because there’s nothing to run (Keyman doesn’t launch after clicking Update; the window just goes away and Keyman never launches). If I examine the contents of UpdateCache after this, the cache.json file is back and populated with the latest version available online, but no executable exists. At this point, I can launch Keyman normally and it doesn’t ask for updates anymore.

Expected behavior would be that the update available box just never appears at all if the check for updates value is set to 0.

Hello,
That is good moving the check for update worked.

Expected behavior would be that the update available box just never appears at all if the check for updates value is set to 0 .

tldr; You have described two bugs in that behaviour, one I have already made a fix for but it is only on alpha but will be backported to the stable version soon. The other I will make an issue to fix.

The detail is that yes there is a state machine controlling the updates. The check for updates registry flag configuration is condition to moving through to the next state. The bug is if it has moved through to another state such as Downloading or WaitingRestart then it doesn’t re-check the flag. Easy enough fix. The bug already fixed is that it didn’t deal with a corrupt or removed cached till after pompting and accepting the update, already fixed(in alpha).

Finally, not that this effects the way your environment works. I will be improving the rejection of the update to enalble the user to reject the new update, not just a not now and not ask them again unless another new version is available.