Some newer drivers for Windows 10 have been moving to using a separate app for the interface (Intel Graphics, Dell Touchpad, Dell Power Manager Service). Microsoft is referring to this setup as Universal Windows drivers.
When imaging with ConfigMgr, you can’t provision apps using Add-AppxProvisionedPackage. It also seems that you cannot properly add an app on to a computer until at least 1 user profile has been loaded on to the system first.
For my scripts that install these specific drivers, I added a part that detects if it’s in a ConfigMgr environment and creates a local script & scheduled task to check for a user profile to be loaded and then add the apps.
# Example app name for Dell Waves Maxx Audio Pro Param( [string]$action = "INSTALL", [string]$asadmin = "0" ) $program = "WavesMaxxAudioProforDell" [version]$version = "1.1.131.0" Function RunProgram { Write-Output "$program" Write-Output "$version" $scriptLetter = $scriptDir.Substring(0,2) If ($scriptLetter -eq "\\") { Write-Output "Running from network path." $netDir = $scriptDir } Else { If (Get-CimInstance -Class Win32_LogicalDisk -Filter "DeviceID = '$scriptLetter'" | Where-Object {$_.DriveType -ne 4}) { Write-Output "Running from local drive." $netDir = $scriptDir } Else { Write-Output "Running from mapped drive." $MappedDrive = (Get-PSDrive | Where-Object {$_.Name -eq $scriptLetter.Substring(0,1)} | Select -First 1).DisplayRoot $netDir = $scriptDir.Replace("$scriptLetter","$MappedDrive") } } # Get all appx/appxbundle to install $AppxBundles = @() $AppxBundles += (Get-ChildItem -Path "$netDir\*" -Include *.appx,*.appxbundle | Where-Object {$_.Name -like "*$($program)*"}).FullName # Verifying software isn't already installed. If not, will run installer. $verify = VerifyInstall If ($verify) { $CcmExec = Get-Process -Name CcmExec -ErrorAction SilentlyContinue [string[]]$Dependencies = (Get-ChildItem -Path "$netDir\Dependencies\*" -Recurse -Include *.appx,*.appxbundle).FullName If ((Test-Path "HKLM:\SOFTWARE\Classes\Microsoft.SMS.TSEnvironment") -And ($null -ne $CcmExec)) { Write-Output "Install appx through scheduled task after final startup" $AppxBundles = $Dependencies + $AppxBundles } # Go through all found files and install them ForEach ($AppxBundle in $AppxBundles) { $programApp = $AppxBundle.Substring($AppxBundle.LastIndexOf('\')+1) $programApp = $programApp.Substring(0,$programApp.IndexOf("_")) If ((Test-Path "HKLM:\SOFTWARE\Classes\Microsoft.SMS.TSEnvironment") -And ($null -ne $CcmExec)) { Copy-Item -Path "$AppxBundle" -Destination "$env:WinDir\Temp\$($programApp).appx" -Force If (-Not (Test-Path "$env:WinDir\Temp\AddAppxPackages.ps1")) { # Generate script to locally provision the appx afterwards New-Item -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Type File -Force Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value 'Function AddAppx {' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' param($App)' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' Write-Output "Looking up $App"' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' If (Get-Process -Name "Dism*") { Wait-Process -Name Dism* -Timeout 60 -ErrorAction SilentlyContinue; Start-Sleep -Seconds 5 }' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' If (-Not (Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -like "*$App*"})) {' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' Write-Output "Attempting to add appx."' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' $Files = Get-ChildItem -Path "$env:WinDir\Temp\*" -Include *.appx,*.appxbundle | Where-Object {$_.Name -like "$App*"} | Select -ExpandProperty FullName' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' ForEach ($File in $Files) {' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' Try {' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' Add-AppxProvisionedPackage -Online -PackagePath "$File" -SkipLicense -ErrorAction SilentlyContinue' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' } Catch {}' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' }' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' }' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' If (Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -like "*$App*"}) {' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' Write-Output "Appx is detected. Cleaning up."' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' (Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -like "*$App*"})' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' Unregister-ScheduledTask -TaskName "AddAppxPackages" -Confirm:$false -ErrorAction SilentlyContinue' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' Get-ChildItem -Path "$env:WinDir\Temp\*" -Include *.appx,*.appxbundle | Where-Object {$_.Name -like "$App*"} | Remove-Item -Force' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' }' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value '}' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value 'Start-Transcript -Path $env:WinDir\Logs\AddAppxPackages.log' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value '$chkDate = (Get-Date).AddMinutes(-5)' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value "`$Users = Get-ChildItem -Path $env:SystemDrive\Users -Exclude Public,'All Users','Default','ADMINI~1','defaultuser0','Default User' | Where-Object {`$_.PSIsContainer} | Where-Object {`$_.CreationTime -le `$chkDate}" Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value 'If ($null -ne $Users) {' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' $programApps = @()' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value " `$programApps += '$($programApp)'" Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' ForEach ($pApp in $programApps) {' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' AddAppx $pApp' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value ' }' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value '} Else { Write-Output "No users have logged in yet. Waiting." }' Add-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1" -Value 'Stop-Transcript' } Else { # Add another programApp to the already created script Write-Output "Scheduled script already exists. Adding $programApps to the script." (Get-Content -Path "$env:WinDir\Temp\AddAppxPackages.ps1").Replace('ForEach ($pApp in $programApps) {',"`$programApps += '$($programApp)'`n ForEach (`$pApp in `$programApps) {") | Set-Content "$env:WinDir\Temp\AddAppxPackages.ps1" } # Create scheduled task at computer startup If (-Not (Get-ScheduledTask -TaskName "AddAppxPackages" -ErrorAction SilentlyContinue)) { Write-Output "Creating scheduled task for adding appx package." $A = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-ExecutionPolicy Bypass -File $env:WinDir\Temp\AddAppxPackages.ps1" $P = New-ScheduledTaskPrincipal -UserID "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount -RunLevel Limited $T = @() $T += New-ScheduledTaskTrigger -AtStartup $T += New-ScheduledTaskTrigger -AtLogon $T[1].Delay = 'PT8M' $S = New-ScheduledTaskSettingsSet -Compatibility Win8 -AllowStartIfOnBatteries -ExecutionTimeLimit 00:30:00 -DontStopIfGoingOnBatteries $Task = New-ScheduledTask -Action $A -Principal $P -Trigger $T -Settings $S -Description "AddAppxPackages" Register-ScheduledTask -TaskName "AddAppxPackages" -InputObject $Task -Force } Else { Write-Output "Scheduled task for adding appx package already exists." } } Else { [string]$PackagePath = $AppxBundle Add-AppxProvisionedPackage -Online -PackagePath $PackagePath -SkipLicense -DependencyPackagePath $Dependencies # Skip force adding the package if during OSD/MDT $Add = $true If (($env:UserName.ToUpper() -eq "$($env:ComputerName.ToUpper())$") -Or ($env:UserName.ToUpper() -eq "SYSTEM")) { $Add = $false } If ((Get-Process -Name TsProgressUI -ErrorAction SilentlyContinue) -And ($env:UserName.ToUpper() -eq "ADMINISTRATOR")) { $Add = $false } If ($Add) { Write-Output "Installing package to -AllUsers" Get-AppxPackage -AllUsers | Where-Object {$_.PackageFullName -like "*$program*"} | ForEach {Add-AppxPackage -DisableDevelopmentMode -Register "$($_.InstallLocation)\AppXManifest.xml" -ErrorAction SilentlyContinue} } } } # End of ForEach AppxBundles } Else { Write-Output "Found to already be installed" } } # End of RunProgram # Runs a check through all installed software (verified through HKLM registry) Function VerifyInstall { param ($action = "VERIFY") $verifyi = $true $Manual = $false # If in ConfigMgr TS or there is no valid user yet, do a manual check If (Test-Path "HKLM:\SOFTWARE\Classes\Microsoft.SMS.TSEnvironment") { $Manual = $true } $chkDate = (Get-Date).AddMinutes(-5); $Users = Get-ChildItem -Path $env:SystemDrive\Users -Exclude Public,'All Users','Default','ADMINI~1','defaultuser0','Default User' | Where-Object {$_.PSIsContainer} | Where-Object {$_.CreationTime -le $chkDate} If ($null -eq $Users) { $Manual = $true } # Detect If ($Manual) { # Check for pending file install by scheduled task $Appx = Get-ChildItem -Path "$env:WinDir\Temp" -Filter "*$($program)*" -ErrorAction SilentlyContinue If ($null -ne $Appx) { $verifyi = $false } $Appx = (Get-AppxPackage -AllUsers | Where-Object {$_.Name -like "*$($program)*"} | Where-Object {$_.PackageUserInformation.UserSecurityId.Sid -ne "S-1-5-18"} | Select-Object -First 1) If ($null -ne $Appx) { [version]$DisplayVersion = $Appx.Version $verifyi = $false } } Else { $Appx = (Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -like "*$($program)*"} | Select-Object -First 1) If ($null -ne $Appx) { [version]$DisplayVersion = $Appx.Version $verifyi = $false } } If ($verifyi -eq $false) { If ($action -eq "UNINSTALL") { Get-AppxProvisionedPackage -Online | Where {$_.PackageName -like "*$($program)*"} | Remove-AppxProvisionedPackage -Online Get-AppxPackage | Where {$_.PackageFullName -like "*$($program)*"} | Remove-AppxPackage } If ($DisplayVersion -lt $version) { $verifyi = $true } # If program can be installed over older version } return $verifyi } # End of VerifyInstall # Start: Check for administrator status $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) -ErrorAction SilentlyContinue $scriptDir = split-path -parent $MyInvocation.MyCommand.Definition $scriptPath = '"' + $MyInvocation.MyCommand.Definition + '"' Clear-Host If ($currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { If (($null -eq $psISE) -Or ($Host.Version.Major -ge 5)) { Start-Transcript -Path "$env:WINDIR\Logs\$program.log" -NoClobber -Append -Force; $Host.UI.RawUI.WindowTitle = "$program" } If ($action.ToUpper() -eq "UNINSTALL") { VerifyInstall $action } Else { RunProgram } If (($null -eq $psISE) -Or ($Host.Version.Major -ge 5)) { Stop-Transcript } } Else { Write-Output "Warning: PowerShell is not running as an Administrator." If ($asadmin -eq "0") { $asadmin = "1" Start-Process PowerShell -Verb runas -ArgumentList '-File',$scriptPath,$asadmin } Else { Write-Output "" Write-Output "Error: You do not have Administrative rights to this computer." Start-Sleep -s 15 } }
This will copy the appx file to the %WINDIR%\Temp directory as well as create a script that checks for a user that has been previously logged on (uses a 5min check so it doesn’t attempt to run during the initial logon). It also detects if the script already exists, in case of more than one app needing to be deployed, and just adds the second one to the variable to attempt to add later. After a successful completion, the script will delete the appx files and scheduled task.
I do put these as separate applications that get installed alongside like any other application to a device during the imaging process. For detection of the ‘installed application’, I use a quick powershell script as well.
# Detect.ps1 $program = "WavesMaxxAudioProforDell" [version]$version = "1.1.131.0" $Manual = $false # If in ConfigMgr TS or there is no valid user yet, do a manual check If (Test-Path "HKLM:\SOFTWARE\Classes\Microsoft.SMS.TSEnvironment") { $Manual = $true } $chkDate = (Get-Date).AddMinutes(-5); $Users = Get-ChildItem -Path $env:SystemDrive\Users -Exclude Public,'All Users','Default','ADMINI~1','defaultuser0','Default User' | Where-Object {$_.PSIsContainer} | Where-Object {$_.CreationTime -le $chkDate} If ($null -eq $Users) { $Manual = $true } # Detect If ($Manual) { # Check for pending file install by scheduled task $Appx = Get-ChildItem -Path "$env:WinDir\Temp" -Filter "*$($program)*" -ErrorAction SilentlyContinue If ($null -ne $Appx) { return $true } $Appx = (Get-AppxPackage -AllUsers | Where-Object {$_.Name -like "*$($program)*"} | Where-Object {$_.PackageUserInformation.UserSecurityId.Sid -ne "S-1-5-18"} | Select-Object -First 1) If ($null -ne $Appx) { [version]$DisplayVersion = $Appx.Version If ($DisplayVersion -ge $version) { return $true } } } Else { $Appx = (Get-AppxProvisionedPackage -Online | Where-Object {$_.DisplayName -like "*$($program)*"} | Select-Object -First 1) If ($null -ne $Appx) { # Check for installed application [version]$DisplayVersion = $Appx.Version If ($DisplayVersion -ge $version) { return $true } } }
Note: My current use for this is just for single driver applications with a single app (no dependencies) at a time.