When working with PowerShell detection scripts in System Center Configuration Manager (SCCM), the execution context depends primarily on the deployment type configuration. Here's the technical breakdown:
- System Context: Runs under
NT AUTHORITY\SYSTEM
when deployment type is set to "Install for system" or "Install for system if resource is device; otherwise install for user" - User Context: Runs under the logged-on user's identity when deployment type is set to "Install for user"
The execution context affects several key aspects of your detection scripts:
# Example of context-aware detection script
$context = "System"
if ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name -ne "NT AUTHORITY\SYSTEM") {
$context = "User"
}
# Registry access example
if ($context -eq "System") {
$regPath = "HKLM:\Software\MyApp"
} else {
$regPath = "HKCU:\Software\MyApp"
}
# Output compliance status
if (Test-Path $regPath) {
Write-Output "Installed"
} else {
Write-Output "Not installed"
}
As mentioned in the original post, getting PowerShell scripts to work with AllSigned
execution policy requires specific handling:
- Ensure you're running SCCM 2012 R2 SP1 or later
- Implement proper script signing procedures
- Consider the bypass mechanism documented by Adam Meltzer
Common problems and solutions:
Issue | Solution |
---|---|
Access denied errors | Verify the execution context matches your resource access requirements |
Missing user-specific data | Use Get-WmiObject -Class Win32_Process -Filter "Name = 'explorer.exe'" | ForEach-Object { $_.GetOwner().User } to identify logged-on users |
Registry redirection | Account for WoW64 redirection in 32-bit processes |
For complex scenarios requiring both system and user context checks:
# Detect installation status across all user profiles
function Test-AllUserInstallations {
$systemInstall = Test-Path "HKLM:\Software\MyApp"
$userInstall = $false
# Check all user hives
Get-ChildItem "HKU:\" | Where-Object { $_.Name -match 'S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]+$' } | ForEach-Object {
if (Test-Path "$($_.PSPath)\Software\MyApp") {
$userInstall = $true
}
}
return ($systemInstall -or $userInstall)
}
After extensive testing with Configuration Manager 2012 R2 SP1 and later versions, I've confirmed that PowerShell detection scripts behave differently based on execution context. Here's what every admin needs to know:
The SCCM client runs PowerShell detection scripts in the following contexts:
# Example detection script showing context awareness
$context = if ([System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem) {
"SYSTEM"
} else {
"USER ($env:USERNAME)"
}
Write-Output "Running as: $context"
The execution context depends directly on the deployment type configuration:
- Install for System: Scripts execute under
NT AUTHORITY\SYSTEM
context - Install for User: Scripts execute under the logged-in user's context (with potential permissions issues)
Consider this registry detection example where context matters:
# Detects Office 365 installation - requires SYSTEM context for machine-wide registry
$path = "HKLM:\SOFTWARE\Microsoft\Office\ClickToRun\Configuration"
if (Test-Path $path) {
$version = Get-ItemProperty $path | Select-Object -ExpandProperty VersionToReport
if ($version -match "16\.0") {
Write-Output "Installed"
exit 0
}
}
exit 1
Through real-world testing, I've documented these critical behaviors:
Scenario | Execution Policy | Signing Requirement | Typical Use Case |
---|---|---|---|
System Context | AllSigned | Mandatory | Machine-wide installations |
User Context | RemoteSigned | Recommended | User-specific applications |
When scripts fail, use this diagnostic approach:
# Enhanced diagnostic script for SCCM detection
$logPath = "$env:TEMP\SCCM_Detection_Debug.log"
Start-Transcript -Path $logPath
try {
# Your detection logic here
$result = Test-Path "C:\Program Files\App\app.exe"
$result | Out-File $logPath -Append
# Context verification
[PSCustomObject]@{
UserContext = $env:USERNAME
SystemContext = [System.Security.Principal.WindowsIdentity]::GetCurrent().IsSystem
ExecutionPolicy = Get-ExecutionPolicy
} | Out-File $logPath -Append
exit [int](-not $result)
}
catch {
$_ | Out-File $logPath -Append
exit 1
}
finally {
Stop-Transcript
}
Remember that SCCM caches script results for 1 hour by default. Use the Application Evaluation Cycle to force immediate re-evaluation when testing.