PowerShell (After Logon) script not running

mainkowitsch

New Member
Messages
4
Reaction score
0
Hello there :)

I'm very new to NTLite and PowerShell scripting, so please be kind :)


I'm trying to create a USB Windows 11 24H2 installation stick that automatically downloads and installs the latest versions of my software.
In the "Before logon" section, I added a script to download and install Chocolatey and the latest NVIDIA driver — no problem there.


However, when I add the same script in the "After logon" section, the system doesn't execute it.


I tried adding it directly as a .ps1 file in the "After logon" section as well as with the command:

powershell -NoProfile -ExecutionPolicy Bypass -File "E:\Documents\NTLite\Programme\software.ps1"

Still, nothing happens.
I read many threads of this forum... cant consider why the script is not working.
If I run the script manually from CMD using the same command, it works fine.
What am I doing wrong?
 
The only differences between executing an app or script from (Before logon) and (After logon):

1. Before logon runs as SYSTEM, and the primary user account's profile hasn't been provisioned yet.
2. After logon runs as the primary user, but with Admin rights. The user profile has been provisioned, so HKCU changes can be applied.

Do you mind attaching your PS script? Otherwise it's difficult to understand what dependency it may have.
 
sure, thanks for help :)

I've used some code snippets from other topics.
if I add all the software one by one as command ala "winget install CPU.... yadayada " it works...
but in a script or bat, it seems that there is an non visible error :(

Code:
$Packageswinget = @(
    'Microsoft.DotNet.Runtime.7'
    'Microsoft.DotNet.Runtime.8'
    'Microsoft.DotNet.Runtime.9'
    '7zip.7zip'
    'Microsoft.PowerToys'
    'CreativeTechnology.SoundBlasterCommand'
    'Valve.Steam'
    'EpicGames.EpicGamesLauncher'
    'CPUID.CPU-Z'
    'TechPowerUp.GPU-Z'
    'Discord.Discord'
    'OBSProject.OBSStudio'
    'Elgato.StreamDeck'
    'realix.hwinfo'
    'Logitech.GHUB'
    'Guru3D.Afterburner'
    'Mozilla.Firefox'
    'Notepad++.Notepad++'
    'NIitesoft.NTLite'
)

$Packagesmsstore = @(
    'CoreDirector' 
    'icloud'
    '9NCBCSZSJRSB'
    'rufus'
    'enpass'
)

Add-Type -MemberDefinition @"
    [DllImport("kernel32.dll", SetLastError=true)] public static extern IntPtr GetStdHandle(int handle);
    [DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode);
"@ -Namespace Win32 -Name NativeMethods

$Handle = [Win32.NativeMethods]::GetStdHandle(-10)
$null = [Win32.NativeMethods]::SetConsoleMode($Handle, 0x0080)

$Version = winget | Select-String 'Winget'
$Host.UI.RawUI.WindowTitle = $Version
mode con: cols=120 lines=30

foreach ($App in $Packageswinget) {
    try {
        if ($App -eq 'Blizzard.BattleNet') {
            winget install $App -s winget -h -l "C:\Program Files" --accept-source-agreements --accept-package-agreements
        } else {
            winget install $App -s winget -h --accept-source-agreements --accept-package-agreements
        }
    }

    catch {
        Write-EventLog -LogName Application -EventID 1 -EntryType Error -Source winget -Message "winget install: $($_.Exception.Message)"
        }
}
foreach ($App in $Packagesmsstore) {
            winget install $App -s msstore -h --accept-source-agreements --accept-package-agreements
        }
        Exit 1
 
Last edited:
You left out the magic word: winget.

winget is bundled as part of DesktopAppInstaller UWP package, and isn't a standalone Win32 app. This causes known problems during (Before logon), because ClipSvc running as a background service hasn't yet acquired a per-machine license for this installed copy of Windows.

It will in a few minutes, which causes a race condition.

Insert this line before your first foreach loop:
Code:
Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe

If you add manual commands to Post-Setup, be careful of the file paths. You may not have an actual "E:\Documents\NTLite\Programme\software.ps1" file when Post-Setup is running.
 
ok, but I want to start this script "After Logon" but there it won't start. No matter what I do, it seems like the .ps1 script is being skipped.
 
There's some creative editing in your script. Here's a corrected/updated version:

Code:
$winget_Packages = @(
    'Microsoft.DotNet.Runtime.7'
    'Microsoft.DotNet.Runtime.8'
    'Microsoft.DotNet.Runtime.9'
    '7zip.7zip'
    'Microsoft.PowerToys'
    'CreativeTechnology.SoundBlasterCommand'
    'Valve.Steam'
    'EpicGames.EpicGamesLauncher'
    'CPUID.CPU-Z'
    'TechPowerUp.GPU-Z'
    'Discord.Discord'
    'OBSProject.OBSStudio'
    'Elgato.StreamDeck'
    'realix.hwinfo'
    'Logitech.GHUB'
    'Guru3D.Afterburner'
    'Mozilla.Firefox'
    'Notepad++.Notepad++'
    'Nlitesoft.NTLite'
)

$Store_Packages = @(
    'CoreDirector'
    'iCloud'
    'Spotify'
    'Rufus'
    'Enpass'
)

function Install-Package {
    param(
        [Parameter(Mandatory=$true)]
        [string]$AppName,

        [Parameter(Mandatory=$true)]
        [ValidateSet('winget','msstore')]
        [string]$Source
    )

    try {
        switch ($AppName) {
            'Blizzard.BattleNet' {
                winget install $AppName -s $Source -l "C:\Program Files" --accept-source-agreements --accept-package-agreements
            }
            default {
                winget install $AppName -s $Source --accept-source-agreements --accept-package-agreements
            }
        }
    }
    catch {
        Write-EventLog -LogName Application -EventID 1 -EntryType Error -Source winget -Message "winget install ${AppName}: $($_.Exception.Message)"
    }
}

$ConsoleWidth = 120
$ConsoleHeight = 30

Add-Type -MemberDefinition @"
    [DllImport("kernel32.dll", SetLastError=true)] public static extern IntPtr GetStdHandle(int handle);

    [DllImport("kernel32.dll", SetLastError=true)] public static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
    [DllImport("kernel32.dll", SetLastError=true)] public static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode);
"@ -Namespace Win32 -Name NativeMethods

$Handle = [Win32.NativeMethods]::GetStdHandle(-10)
$Mode = 0

$null = [Win32.NativeMethods]::GetConsoleMode($Handle, [ref]$Mode)
$null = [Win32.NativeMethods]::SetConsoleMode($Handle, ($Mode -band 0x0080))

$Version = winget -v
$Host.UI.RawUI.WindowTitle = "winget $Version"

$Host.UI.RawUI.WindowSize = New-Object System.Management.Automation.Host.size($ConsoleWidth, $ConsoleHeight)
$Host.UI.RawUI.BufferSize = New-Object System.Management.Automation.Host.size($ConsoleWidth, 2000)

foreach ($App in $winget_Packages) {
    Install-Package $App 'winget'
}

foreach ($App in $Store_Packages) {
    Install-Package $App 'msstore'
}

For some reason, I had to skip over PowerToys during manual testing. The downloads kept hanging (twice).
 
wow.. o_O i'm overwhelmed

thanks!!!!

But one question comes to my mind. Is it possible that the winget package is not immediately ready when the windows install routine is finished?

I've tried "my code" and your code with office 365 as "something to do before the winget commands starts". voila both codes starts running.

Another topic, but thats not a very important... If i add 'Blizzard.BattleNet' to the script, it bricks my whole windows.
The install routine of the launcher shows at the end "try again as admin" and from there on the whole script returns only errors and my whole windows is out of control. notepad is not starting anymore. explorer.exe cant be started and after a restart the explorer wont start.
What does the installation routine do with the system????? xD
 
But one question comes to my mind. Is it possible that the winget package is not immediately ready when the windows install routine is finished?

I've tried "my code" and your code with office 365 as "something to do before the winget commands starts". voila both codes starts running.
This is known as a race condition. Depending on what's going on in the background, some install sequences might try to run winget before it's ready to run. Other times, Windows has finished already and winget is ready.

My experience is based on testing different Windows releases, and if the preventive fix is a simple extra command, then it's worth adding to make sure the script always works.

Another topic, but thats not a very important... If i add 'Blizzard.BattleNet' to the script, it bricks my whole windows.
The install routine of the launcher shows at the end "try again as admin" and from there on the whole script returns only errors and my whole windows is out of control. notepad is not starting anymore. explorer.exe cant be started and after a restart the explorer wont start.
What does the installation routine do with the system????? xD
Not sure. I install Battle.Net the old fashioned way, by downloading the installer from Blizzard's website.

I've seen users file complaints on GitHub about how Battle.Net doesn't follow the install correctly. /shrug.
 
Hi,

I don't do scripts like this (don't know how to) but would that be why winget installs sometimes, not always, randomly fail if simply added to Post-Setup->After Logon as commands? If I put he same commands in a BAT file and just run it manually after logon then everything installs fine.

Edit: Yeah, this answers this question:


So, I'm just gonna wait couple of minutes and run the winget bat file manually as I used to.
 
Last edited:
winget (on GitHub) does include a License.xml for provisioning offline images.

But NTLite doesn't have a native method to integrate the license when adding Appx packages. I don't think it's a technical problem, but more of how do you assign the right License.xml to its package using the UI.

You have four options:
1. Manually DISM provision winget (or DesktopAppInstaller) in an offline image with License.xml.

2. DISM provision winget with License.xml during the specialize pass, or from Post-Setup.

3. Add winget in NTLite. Force a package registration using Add-AppxPackage, so you're not waiting for WU to get around in a few minutes.

4. Wait 5-10 min. after you're logged into the desktop, for WU to get around to licensing & updating default Apps.
 
Thanks again Garlin! Option #4 is what I've been doing and I guess I'll stick with it as it's simple enough and it works. I was just wondering if I was doing something wrong.

I always give it a few minutes anyway to do all the postinstall stuff and then it needs to be rebooted anyway for some customizations to take effect. I'd say, it's more like 5 minutes, at most.
 
But NTLite doesn't have a native method to integrate the license when adding Appx packages. I don't think it's a technical problem, but more of how do you assign the right License.xml to its package using the UI.
This "licensing problem" was resolved last year (nuhi had done the necessary when I had tested the integration of an App and had this license problem)
So, NTLite adds licenses (from the updates page)
It searches for the affected app and integrates it. It also integrates without a license

nuhi can provide more explanation and checked if there is a problem with certain Apps (no problem with the ones I integrate)
 
Just add the accompanying License XML file to the Updates page with the APPX/MSIX file and it will connect them.
If not, please send me an example.
 
Just add the accompanying License XML file to the Updates page with the APPX/MSIX file and it will connect them.
If not, please send me an example.
Can you add multiple packages with multiple licenses? Does NTLite inspect the license to know which one matches its parent package?
Several offline packages simply name their files License.xml.
 
I integrate 4 apps (4 packages) at the same time and no problem with all 4
 
You didn't answer my question, how does NTLite know which license goes to which app? Does the license file have to match the PackageFamilyName, or does it read the XML contents to realize it belongs to which app?

My point is none of this is documented for someone to know the correct steps.
 
Can you add multiple packages with multiple licenses? Does NTLite inspect the license to know which one matches its parent package?
Several offline packages simply name their files License.xml.
It reads the license file content and matches the family name, also searches for internal licenses in the image during integration.
So if you meant can you add multiple packages and their licenses, yes - also filename itself doesn't matter.

If anyone sees any issue with the described, no matter how it works, let me know of the combination to download and test.
Recognized license file will show itself in the list as "MSIX License (PFM)".

I'll upgrade the UI to show connected license from the queue, like it does for "Dependencies", for now it connects the license during integration.
 
I'll upgrade the UI to show connected license from the queue, like it does for "Dependencies", for now it connects the license during integration.
It can always be useful for some people
 
When Update Downloader pulls the DesktopAppInstaller package, does it (silently) grab the license file at the same time? Because if it does, I can stop instructing users to force register the package before running their first winget command.
 
When Update Downloader pulls the DesktopAppInstaller package, does it (silently) grab the license file at the same time? Because if it does, I can stop instructing users to force register the package before running their first winget command.
It doesn't, as all images already have winget/appinstaller licenses built-in. Haven't seen any issues so far.
Not clear why Microsoft ships that file, could be for some older Win10 images and those that are shipped without the installer.

But do let me know if there is a scenario where you saw a difference integrating DesktopAppInstaller license, and I'll add it to the update list.

Note that winget command needs a full path when being executed the first time.
They can use the shorter "winget" command on the post-setup page, it does the full path replacement automatically in the background.

Same with all the apps, they are lazily initiated after a user creation (first login), that was very annoying until figured out as winget command would not work, then after some time it did - but full path immediately works and triggers that initialization.
If anyone has more info, do tell, this might have changed with subsequent Windows releases.
 
Back
Top