PowerShell脚本无法从Windows任务计划程序正常工作

问题描述:

概述: 我有一个脚本,用于通过WMI在SCCM中查询新的应用程序请求。当在PowerShell控制台中手动启动时(无论是否升高,无关紧要),该脚本完美工作。我需要通过任务计划程序运行脚本。目前设置为使用管理员凭据运行,并选中“以最高权限运行”复选框。PowerShell脚本无法从Windows任务计划程序正常工作

问题: 无法从Windows Server 2008 R2中的任务计划程序正确运行。没有错误报告(任务调度返回0错误代码),但它似乎并没有过去进行读取的行:

$GetAppRequest = Get-WmiObject -Class SMS_UserApplicationRequest -Namespace root/SMS/site_$SiteCode | Where-Object {$_.CurrentState -like "1"} | ForEach-Object { 

以下是完整的脚本:

#Hard-coded variables 
$SiteCode = "MySiteCode" 
$ComputerName = "My-SCCM-Server" 
$GUIDFilePath = "C:\Scripts\SCCM\GUIDList.txt" 
$FilePath = "C:\Scripts\SCCM" 
$smtpServers = "smtp1.domainname.com","smtp2.domainname.com" 
$reliableSmtpServer = $null 
$logpath = "C:\Scripts\SCCM\RequestLog.txt" 

#logging functionality 
function log($message, $type){ 
    $date = get-date 
    $string = "$date $type : $message" | Out-File $logpath -Append 
} 

#does log exist? 
if (gi -Path $logpath -ErrorAction SilentlyContinue){ 
    #yep, the log exists! 
    $logContent = cat $logpath 
    log -message "Script called and log opened for writing." -type "Info" 
} else { 
    #nope, the log doesn't exist, let's make one. 
    write "Can't find log file, creating a new one." 
    $newFileResult = New-Item -Path $logpath -ItemType File -ErrorAction Stop 
    if ($newFileResult.Exists){ 
    log -message "new log file created" -type "Info" 
    } #end if 
} #end else 

#Email variables 
$from = "[email protected]" 
$to = "[email protected]" 
$subject = "New SCCM Application Approval Requests" 

#Determine which SMTP to use. 
$smtpServers | ForEach-Object { 
    if ($reliableSmtpServer -eq $null){ 
    if (Test-Connection -ComputerName $_ -ErrorAction SilentlyContinue){ 
     write "Reliable SMTP server found: $_" 
     $reliableSmtpServer = $_  
    } #end if test-connection 
    } #end if reliableSmtpServer exists 
} #end foreach SMTP server 

if ($reliableSmtpServer){ 
    log -message "Reliable SMTP server found, $reliableSmtpServer" -type "Info" 
} else { 
    log -message "No reliable SMTP server could be found" -type "Error" 
} 

#Get the entries from GUIDList.txt 
if ($GetGUID = Get-Content -Path $GUIDFilePath -ErrorVariable guidReadError { 
    write "Successfully read $GUIDFilePath" 
    log -message "Successfully read $GUIDFilePath" -type "Info" 
} else { 
    Write-Error -Message "Couldn't read GUIDfile..." 
    log -message "Failed to read GUIDFile" -type "Error" 
} 

#Get all Application Requests with a CurrentState of "1" 
log -message 'Attempting to get all Application Requests with a CurrentState of 1' -type "Info" 
$GetAppRequest = Get-WmiObject -Class SMS_UserApplicationRequest -Namespace root/SMS/site_$SiteCode | Where-Object {$_.CurrentState -like "1"} | ForEach-Object { 
    log -message "App found, $_.Application" -type "Info" 
    if ($GetGUID -contains $_.RequestGuid) { 
    Write-Host "Application request $($_.RequestGuid) already present" 
    log -message "Application request $($_.RequestGuid) already present" -type "Info" 
    } else { 
    $appUser = $_.User 
    $appName = $_.Application 
    $appComment = $_.Comments 
    $Body = @" 
    Application request: $appName 
    User: $appUser 

    Comment: $appComment 
    "@ #This row can't contain any blank spaces or tabs 

    log -message "New record found: $appUser, $appName, $appComment" -type "Info" 

    #Email configuration 
    Send-MailMessage -SmtpServer $reliableSmtpServer -From $from -To $to -Subject $subject -Body $body -ErrorVariable mailError 
    if (!($mailError)){ 
    write "Message successfully sent to : $to" 
    log -message "Message successfully sent to : $to" -type "Info" 
    } else { 
    Write-Error -message "Failed to send email!" 
    log -message "Failed to send email!" -type "Error" 
    } #end else 

    #Append the current objects GUID to GUIDList.txt 
    Write "Appending $($_.RequestGUID) to $GUIDFilePath" 
    log -message "Appending $($_.RequestGUID) to $GUIDFilePath" -type "Info" 
$_.RequestGuid | Out-File $GUIDFilePath -Append 

    } #end else statement 
} #end forEach 

#Remove the GUIDList.txt file and re-create it when there's more than 100 entries 
$GUIDCount = $GetGUID.Count 
if ($GUIDCount -gt 100) { 
    log -message "Greater than 100 GUID entries, clearing list." -type "Info" 
    Get-Item $GUIDFilePath | Remove-Item 
    New-Item -Path $FilePath -Name GUIDList.txt -ItemType file 
} 

#Create a new log once the log file exceeds 1000 lines. 
$logCount = $logContent.Count 
if ($logCount -gt 1000) { 
    log -message "Log file is too long, removing log" -type "Warning" 
    Remove-Item $logpath 
} 

这是计划任务XML:

<?xml version="1.0" encoding="UTF-16"?> 
<Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task"> 
    <RegistrationInfo> 
    <Date>2014-05-09T17:10:48.6636926</Date> 
    <Author>domainname\myUserAccount</Author> 
    <Description>Runs a script located at C:\scripts\SCCM to determine if there are any new application requests and notify IT staff via Email.</Description> 
    </RegistrationInfo> 
    <Triggers> 
    <TimeTrigger> 
     <Repetition> 
     <Interval>PT15M</Interval> 
     <StopAtDurationEnd>false</StopAtDurationEnd> 
     </Repetition> 
     <StartBoundary>2014-05-09T17:12:04</StartBoundary> 
     <ExecutionTimeLimit>PT1H</ExecutionTimeLimit> 
     <Enabled>true</Enabled> 
    </TimeTrigger> 
    </Triggers> 
    <Principals> 
    <Principal id="Author"> 
     <UserId>domain\AdminAccount</UserId> 
     <LogonType>Password</LogonType> 
     <RunLevel>HighestAvailable</RunLevel> 
    </Principal> 
    </Principals> 
    <Settings> 
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy> 
    <DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries> 
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries> 
    <AllowHardTerminate>true</AllowHardTerminate> 
    <StartWhenAvailable>false</StartWhenAvailable> 
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable> 
    <IdleSettings> 
     <StopOnIdleEnd>true</StopOnIdleEnd> 
     <RestartOnIdle>false</RestartOnIdle> 
    </IdleSettings> 
    <AllowStartOnDemand>true</AllowStartOnDemand> 
    <Enabled>true</Enabled> 
    <Hidden>false</Hidden> 
    <RunOnlyIfIdle>false</RunOnlyIfIdle> 
    <DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession> 
    <UseUnifiedSchedulingEngine>false</UseUnifiedSchedulingEngine> 
    <WakeToRun>false</WakeToRun> 
    <ExecutionTimeLimit>P3D</ExecutionTimeLimit> 
    <Priority>7</Priority> 
    </Settings> 
    <Actions Context="Author"> 
    <Exec> 
     <Command>powershell.exe</Command> 
     <Arguments>-noprofile -file "C:\scripts\sccm\Notify.ps1"</Arguments> 
    </Exec> 
    </Actions> 
</Task> 
+0

,没有在其他提供的解决方案的*我的PS脚本将不能作为计划任务运行*问题帮助?我在右边的相关列表中统计了7个潜在的重复数据,甚至没有搜索其他数据。你有没有调查过其他问题的解决方案? –

+0

是的,我读过这些。没有什么是有帮助的,因为他们倾向于关联那些没有交互式会话或高程的情况下无法调用的事物。我已经打高程,因为我正在用最高级别的priv和管理员credentails跑步。至于交互会话,这只是一个WMI查询,所以它应该工作?如果您发现某些您认为会有帮助的内容,请将其指向我的方向。 – Mokilok

+0

我建议你添加一个'陷阱'来捕获任何异常。你可以在你的日志函数后添加'trap {log -message($ _ | fl * -f | out-string)-type“Exception”}并检查日志是否抛出异常? –

您是不是要将Get-WmiObject ... | Foreach-Object {行的结果赋值给$ GetAppRequest变量?我的意思是Foreach-Object循环的输出被该赋值所捕获,这看起来不是有意的,因为你不再使用该变量。

我建议您对变量执行赋值,然后将变量单独传递给Foreach-Object cmdlet,并将其间的某些日志记录分别传递给Foreach-Object cmdlet。此外,我们可以将Get-WmiObject包装在try{}catch{}构造中以捕获该cmdlet可能抛出的任何错误;确保捕获错误时,我们设置-ErrorAction Stop

try { 
    $GetAppRequest = Get-WmiObject -Class SMS_UserApplicationRequest -Namespace root/SMS/site_$SiteCode -ErrorAction Stop 
} 
catch { 
    log -message "Get-WmiObject cmdlet failed" -type "Error" 
    log -message $_.Exception.Message.ToString() -type "Error" 
} 

if(-not $GetAppRequest) { 
    log -message "Failed to retrieve WMI data" -type "Error" 

} elseif(-not ($GetAppRequest = $GetAppRequest | Where-Object {$_.CurrentState -like "1"})) { 
    log -message "No results with CurrentState = 1" -type "Info" 
} 

$GetAppRequest | ForEach-Object { 
    ... 
+0

您是否在elseif测试中遗漏了'-not'? –

+0

很有可能。是写这个“盲目”而不是测试我提交的一切!将编辑... –

+0

我遵循你的建议@CharlieJoynt。从任务管理器运行时,日志文件现在会导致“无法检索WMI数据”。仍然不确定为什么这会发生。我认为其他人对配置文件中的独特内容所说的话可能是对的,但我没有在该服务器上使用任何$ PROFILE文档。此外,无论在计划任务的操作中是否具有-noprofile开关,我都会得到相同的错误,因为我没有看到加载用户配置文件的一个要点,所以我选择将其包含在内。 – Mokilok

请与“运行,只有当用户登录”和hidd未选中复选框。

+0

你能解释一下为什么你认为这将是必需的吗?我使用不同于我的用户帐户的凭据在服务器上运行脚本。 – Mokilok

+0

好吧,我遇到了罕见脚本的相同问题,仍然以服务器上的登录用户身份运行。甚至看到一些使用adobe reader命令行的.net服务需要登录用户才能工作。所以请试试看,并给我们反馈。 –