我终于成功地在具有AllSigned
执行策略的客户端上使用 PowerShell 检测脚本。(提示:它在安装后开始工作最新的服务包并使用Adam Meltzer 的解决方法。
既然使用 PowerShell 脚本进行应用程序检测已经很实用,那么我对以下事情产生了疑问:
- SCCM 客户端在什么环境中运行 PowerShell 检测脚本?系统?用户?
- 上下文是否取决于您在部署类型中选择的是“为用户安装”还是“为系统安装”?
关于这个主题的文档非常少。我发现的有关 SCCM PowerShell 检测脚本的最佳资源是这篇 Kloud 博客文章然而,它对背景问题却保持沉默。
答案1
实验结果
我编写了一些 PowerShell,当作为检测脚本运行时,它会将检测脚本看到的环境变量转储到日志文件中。该脚本位于本答案的末尾。
然后,我通过部署具有不同“安装行为”和“登录要求”参数的部署类型,使此脚本由 SCCM 客户端运行。结果如下表所示:
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
是用户名cnX
是计算机名称
分析
上述结果令人惊讶,因为检测脚本运行的上下文似乎部分取决于应用程序是部署到用户还是系统。这足以让我感到惊讶,我第二次运行了测试。结果是一致的。
由上表我们可以初步做出以下假设:
- 当将应用程序部署给用户时,将以该用户的身份运行该应用程序的 PowerShell 检测脚本。
- 当应用程序部署到系统并且为系统安装部署类型时,该应用程序的 PowerShell 检测脚本将作为系统运行。
- 当应用程序部署到系统并且为用户安装部署类型时,将以登录用户的身份运行该应用程序的 PowerShell 检测脚本。
上述三个假设得到了测试结果的支持。可能还有一些其他变量没有经过测试,这些假设不成立。在使用 PowerShell 检测脚本时,它们至少是一组很好的初始假设。
上下文不匹配(小心!)
Jason Sandys 记录了对安装环境规则的类似测试。 如果你仔细阅读那篇文章,你可能会注意到安装上下文和检测脚本上下文的规则并不完全相同。以下是违规规则:
当应用程序的安装行为设置为“作为系统安装”时,安装程序将作为系统运行[无论是否部署给用户]。
当将应用程序部署给用户时,该应用程序的 PowerShell 检测脚本将以该用户的身份运行(无论安装行为是否设置为“作为系统安装”)。
这意味着具有“作为系统安装”安装行为的应用程序和部署到用户集合将使用系统上下文进行安装,但使用用户上下文进行检测。
编写安装行为为“作为系统安装”的应用程序检测脚本的人应小心谨慎,避免依赖在系统和用户上下文之间发生变化的任何环境部分。否则,对部署到系统集合的应用程序的检测可能会成功,而对部署到用户集合的完全相同的应用程序的检测可能会失败。
脚本
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)
}