ConfigMgr: Deploying appx apps during imaging

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.

Resetting all users’ start menu for Office 2019

With the plan to move to Office 2019, I wanted to be able automatically reset the start menu for all users on a computer. In my powershell script that installs Office 2019, I also have it search all local users profiles for their start menu layout and replace old Office 2016 entries with Office 2019.

If ($null -eq (Get-PSDrive HKU -ErrorAction SilentlyContinue)) { New-PSDrive -Name HKU -PSProvider Registry -Root HKEY_USERS | Out-Null }
$LoggedOnUsers = (Get-CimInstance -Class Win32_LoggedOnUser | Select-Object Antecedent -Unique).Antecedent.Name
$HiveList = "HKLM:\SYSTEM\CurrentControlSet\Control\hivelist"
$Hives = Get-Item -Path $HiveList | Select-Object -ExpandProperty Property | Where-Object {$_ -like "*\REGISTRY\USER\S-*" -And $_ -notlike "*_Classes*"}
$Users = (Get-ChildItem $env:SystemDrive\Users -Force -Exclude 'All Users','Public' | Where-Object {$_.PSIsContainer}).Name
ForEach ($Name in $Users) {
	$ntuserpath = "$env:SystemDrive\Users\$Name\ntuser.dat"
	If (Test-Path $ntuserpath) {
		$ntuserpath = '"' + $ntuserpath + '"'
		$RegPath = $null
		If ($LoggedOnUsers -like "*$Name*") {
			# User is logged in, will get HKU path
			ForEach ($Hive in $Hives) {
				$HiveValue = (Get-ItemPropertyValue -Path "$HiveList" -Name "$Hive") -Replace "\\Device\\HarddiskVolume[0-9]*","$env:SystemDrive"
				If ($HiveValue -like "*\$Name\*") { $RegPath = $Hive.ToUpper().Replace("\REGISTRY\USER","HKU"); break }
			}
		}
		If ($null -eq $RegPath) { $RegPath = "HKLM\ntuser"; $runreg = Start-Process -FilePath REG.exe -ArgumentList "LOAD $RegPath $ntuserpath" -PassThru -Wait -WindowStyle Hidden }
		$PSRegPath = $RegPath.Replace("\",":\")
		
		# Reset TileStore for Start Menu refresh
		If ($Name -notlike "Default*") {
			$LocalLayout = "$env:SystemDrive\Users\$Name\AppData\Local\Microsoft\Windows\Shell\LayoutModification.xml"
			If ((Test-Path "$LocalLayout") -And ($FixOfficeLayout)) {
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\WINWORD.EXE","Microsoft.Office.WINWORD.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\EXCEL.EXE","Microsoft.Office.EXCEL.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\POWERPNT.EXE","Microsoft.Office.POWERPNT.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\MSPUB.EXE","Microsoft.Office.MSPUB.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\ONENOTE.EXE","Microsoft.Office.ONENOTE.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("{7C5A40EF-A0FB-4BFC-874A-C0F2E0B9FA8E}\Microsoft Office\Office16\OUTLOOK.EXE","Microsoft.Office.OUTLOOK.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace("Microsoft.Office.OUTLOOK.EXE.16","Microsoft.Office.OUTLOOK.EXE.15") | Set-Content "$LocalLayout"
				(Get-Content -Path "$LocalLayout").Replace('Version="2"','Version="1"') | Set-Content "$LocalLayout"
				$RegTileStore = Get-ChildItem -Path "$PSRegPath\SOFTWARE\Microsoft\Windows\CurrentVersion\CloudStore\Store\Cache\DefaultAccount" -ErrorAction SilentlyContinue | Where-Object {$_.Name -like "*start.tilegrid*curatedtilecollection*"} | Select -ExpandProperty Name -First 1
				If ($null -ne $RegTileStore) {
					$RegTileStore = $RegTileStore.Substring($RegTileStore.IndexOf("\DefaultAccount\")+16)
					Remove-Item -Path "$PSRegPath\SOFTWARE\Microsoft\Windows\CurrentVersion\CloudStore\Store\Cache\DefaultAccount\$($RegTileStore)" -Force -Recurse -ErrorAction SilentlyContinue
				}
			}
			If ($RegPath -eq "HKLM\ntuser") {
				[gc]::collect()
				$runreg = Start-Process -FilePath REG.exe -ArgumentList "UNLOAD $RegPath" -PassThru -Wait -WindowStyle Hidden; Write-Output "$($runreg.ExitCode)"
			}
		}
	}
} # End of ForEach Users

This searches for all basic Office 2016 application entries in LayoutModification.xml and puts in the Office 2019 entries. It then clears out the registry key that will force the start menu refresh when each user logs back in.

ConfigMgr: -RemoveDetectionClause with Set-CM(Script/Msi)DeploymentType

Note April 3, 2019: CM1902 has been released with a fix to properly handle version numbers for detection clauses.

Note December 7, 2018:  Using New-CMDetectionClauseWindowsInstaller with ExpectedValue (ProductVersion) seems to work, but the final xml data shows the version number is a string instead. For .msi detections, I end up having to go in and manually browse for the file and set up the detection that way. Hoping it’ll be fixed in CM1902.

I began writing PowerShell scripts to help automate updating/replacing applications in ConfigMgr. About half of the programs are .exe with PowerShell detection and the other half are .msi using windows installer detection. Microsoft’s current documentation for removing detection clauses just has [-RemoveDetectionClause <String[]>] without any extra information.

Testing the command (Set-CMScriptDeploymentType -ApplicationName “appname” -DeploymentTypeName “apptitle” -RemoveDetectionClause “blahblah”) returns a warning message: “WARNING: Detection clause with a logical name of ‘blahblah’ was not found and will be ignored for removal.” I then looked into the deployment types SDMPackageXML and found two different id types. One called ‘LogicalName’ and the other called ‘SettingLogicalName’. Manually tested both entries and the SettingLogicalName id worked.

# Get application and deployment type title
$AppName = "Google Chrome"
$AppTitles = (Get-CMApplication -ApplicationName "$($AppName)" | ConvertTo-CMApplication).DeploymentTypes.Title
ForEach ($Title in $AppTitles) {
   $SDMPackageXML = (Get-CMDeploymentType -ApplicationName "$($AppName)" -DeploymentTypeName "$($Title)").SDMPackageXML

   # Regex to retrieve all SettingLogicalName ids. Will be duplicates for every one but doesn't seem to be an issue.
   [string[]]$OldDetections = (([regex]'(?<=SettingLogicalName=.)([^"]|\\")*').Matches($SDMPackageXML)).Value

   # Create your new detection clause(s) and remove all of the previous ones
   $MsiClause = New-CMDetectionClauseWindowsInstaller -ProductCode "<MSIGUID>" -ExpectedValue "<MSIVERSION>" -ExpressionOperator GreaterEquals -Value
   Set-CMScriptDeploymentType -ApplicationName "$($AppName)" -DeploymentTypeName "$($Title)" -AddDetectionClause ($MsiClause) -RemoveDetectionClause $OldDetections
}

This is a basic example of getting all current detection clauses of a deployment type and removing them with a newly created clause. (Getting an msi’s guid & version separately)

Testing Windows 10 in-place upgrade task sequence

I’ve been trying to make smaller/quicker task sequences for doing Windows 10 in-place upgrades. I wanted to be able to get almost everything done hidden in the background (Setting variable TSDisableProgressUI to True) before the first restart would even prompt.

I noticed by using the step ‘Upgrade Operating System’ it will automatically reboot when the setup requests. I tried increasing the variable SMSTSRebootTimeout so the first prompt would give users enough time if someone was logged on. The problem with this is that there will be more than one prompt which isn’t necessary after the first as the upgrade isn’t done yet.

Read more

Dell’s –speaker=off and Realtek Maxx Audio

I had a couple labs of OptiPlex 3050s that I noticed were not auto-detecting any Speakers or Headsets therefore the audio would be completely disabled. These machines I had gone in to the BIOS and turned off the Internal Speaker (Integrated Audio was still enabled). The Realtek driver was installed but the Maxx Audio shortcut/program weren’t there at all (under C:\Program Files\Waves”). If I tried to manually install the Maxx Audio msi, it just returns the error ‘This software is not supported on the current device’ (I eventually realized running the msi by itself no matter what will just give back that message). Read more

Windows Defender Offline and WinRE

By default I always use “reagentc /disable” so users with local administrative rights don’t do a reset of their whole computer.  With Windows 10 1703 now out and Defender’s Offline scan a bit more obvious in the Security Center, I noticed it wasn’t working at all.

Advanced scans

I wasn’t realizing that everything is all tied together with Windows RE. My current option seems to just have a PowerShell script to quickly run “reagentc /enable; Start-MpWDOScan” and manually run “reagentc /disable” after it starts back up.