In what context do SCCM Powershell detection scripts run in?

Solution 1:

Empirical Results

I wrote some PowerShell that, when run as a detection script, dumps the environment variables that the detection script sees to a log file. That script is at the end of this answer.

I then cause this script to be run by the SCCM client by deploying a Deployment Type with different "Installation Behavior" and "Logon requirement" parameters. The results are in the table below:

Test InstallationBehavior LogonRequirement                   DeployedTo LoggedOnUser ScriptRunAs
---- -------------------- ----------------                   ---------- ------------ -----------     
1.1a Install for user     Only when a user is logged on      un2        un2          un2        
1.1b Install for user     Only when a user is logged on      cn1        un2          un2        
1.1c Install for user     Only when a user is logged on      cn1        un1          un1        
1.2a Install for system   Only when a user is logged on      un2        un2          un2        
1.2b Install for system   Only when a user is logged on      cn1        un2          cn1        
1.2c Install for system   Only when a user is logged on      cn1        un1          cn1        
1.3a Install for system   Whether or not a user is logged on un2        un2          un2        
1.3b Install for system   Whether or not a user is logged on cn1        un2          cn1        
1.3c Install for system   Whether or not a user is logged on cn1        un1          cn1        

   
  • unX are usernames
  • cnX are computer names

Analysis

The above results are surprising because the context that a detection script runs in seems to depend in part upon whether the Application was deployed to a user or a system. This was enough of a surprise that I ran the tests a second time. The results were consistent.

We can tentatively draw the following hypotheses from the table above:

  1. When an Application is deployed to a user, a PowerShell detection script for that Application is run as that user.
  2. When an Application is deployed to a system and the Deployment Type is installed for the system, a PowerShell detection script for that Application is run as the system.
  3. When an Application is deployed to a system and the Deployment Type is installed for the user, a PowerShell detection script for that Application is run as the logged-in user.

The above three hypotheses are supported by the test results. There may well be some other variables that weren't tested where these hypotheses do not hold. They are, at least, a good set of initial assumptions when using PowerShell detection scripts.

Mismatched Contexts (Beware!)

Jason Sandys documented a similar test of the rules for installation context. If you read that post carefully, you might notice that the rules for installation context and detection script context are not quite the same. Here are the offending rules:

When an Application's installation behavior is set to "Install as System" the installer is run as system [regardless of deployment to user].

When an Application is deployed to a user, a PowerShell detection script for that Application is run as that user [regardless of whether installation behavior is set to "Install as System"].

This means that an Application that has installation behavior “Install as system” and is deployed to a user collection will use the system context for installation, but the user context for detection.

Someone writing detection scripts for Applications where installation behavior is "Install as System" should be careful to avoid relying on any part of the environment that changes between the system and user contexts. Otherwise, detection of an Application deployed to a system collection may succeed while detection of the exact same Application deployed to a user collection fails.

Script

function Write-EnvToLog
{
    $appName = 'script-detect-test'

    $logFolderPath = "c:\$appName-$([System.Environment]::UserName)"

    if ( -not (Test-Path $logFolderPath -PathType Container) )
    {
        New-Item -Path $logFolderPath -ItemType Directory | Out-Null
    }

    if ( -not (Test-Path $logFolderPath -PathType Container ) )
    {
        return
    }

    $logFileName = "$appName`__$((Get-Date).ToString("yyyy-MM-dd__HH-mm-ss")).txt"

    $fp = "$logFolderPath\$logFileName"

    Get-ChildItem Env: | Out-File $fp | Out-Null

    return $true
}

try
{
    if ( Write-EnvToLog ) { "Detected!" }
    [System.Environment]::Exit(0)
}
catch
{
    [System.Environment]::Exit(0)
}