PowerShell 脚本无法在 Windows 任务计划程序中正常运行
PowerShell script doesn't work correctly from Windows Task Scheduler
概览:
我有一个脚本,用于通过 WMI 在 SCCM 中查询新的应用程序请求。该脚本在 PowerShell 控制台中手动启动时运行良好(提升与否,无关紧要)。我需要通过 Task Scheduler 运行 脚本。当前设置为 运行,具有管理员凭据,并且 'Run with highest privileges' 的复选框已选中。
问题:
运行 无法从 Windows Server 2008 R2 中的任务计划程序正确执行。未报告任何错误(任务计划程序 returns 错误代码为 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 = "no.reply@domainname.com"
$to = "sccm-admin@domainname.com"
$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>
选中 "run only when the user is logged on" 并取消选中隐藏的复选框。
您的意思是将 Get-WmiObject ... | Foreach-Object {
行的结果分配给 $GetAppRequest 变量吗?我的意思是 Foreach-Object 循环的输出被该赋值捕获,这看起来不是故意的,因为您不再使用该变量。
我建议您执行对变量的赋值,然后将变量分别传递给 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 {
...
概览: 我有一个脚本,用于通过 WMI 在 SCCM 中查询新的应用程序请求。该脚本在 PowerShell 控制台中手动启动时运行良好(提升与否,无关紧要)。我需要通过 Task Scheduler 运行 脚本。当前设置为 运行,具有管理员凭据,并且 'Run with highest privileges' 的复选框已选中。
问题: 运行 无法从 Windows Server 2008 R2 中的任务计划程序正确执行。未报告任何错误(任务计划程序 returns 错误代码为 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 = "no.reply@domainname.com"
$to = "sccm-admin@domainname.com"
$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>
选中 "run only when the user is logged on" 并取消选中隐藏的复选框。
您的意思是将 Get-WmiObject ... | Foreach-Object {
行的结果分配给 $GetAppRequest 变量吗?我的意思是 Foreach-Object 循环的输出被该赋值捕获,这看起来不是故意的,因为您不再使用该变量。
我建议您执行对变量的赋值,然后将变量分别传递给 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 {
...