running powershell scripts as scheduled tasks - powershell

I got a couple of scripts which perform mailboxes moves to the cloud on office 365. The scripts sometimes fails and others not, I don't have any clue on why it
fails but I guess it is because the service is not available when the task is executed, anyways, the point is that these scripts are running as scheduled tasks and one must be
executed before of other, how I could detect if the first tasks failed and if so then reschedule the second task.
The tasks are running powershell v2 cmdlets on windows 2008 r2 so I'm using Task Scheduler.
I wonder if I should save some entries into a text file, to use a different task scheduler or maybe run each job using powershell, in the following way:
foreach ($script in $scripts)
{
#check status in a text file
$job = start-job -Filepath c:\myscript.ps1 -AsJob
Wait-Job $job -Timeout 180
}
What would be the best approach?
Thanks,

You can use schtasks.exe to query / enable or reschedule your tasks. Or you can use the COM object of the scheduler (beware schtasks output is language dependant). With one or other method check the lastTaskResult and reschedule or not your other task.
Examples:
Get all tasks of the root folder using COM object
icm -AsJob -JobName getTasks -ComputerName $computername -ScriptBlock{
$Schedule = new-object -com("Schedule.Service")
$Schedule.connect($env:computername)
$Tasks = $Schedule.getfolder("\").gettasks(0)
$Tasks | Select-Object Name,Path,State,Enabled,LastRunTime,LastTaskResult
}
Get a particular task with schtasks
schtasks /query /TN "\Microsoft\Windows\Autochk\Proxy" /v /fo csv |ConvertFrom-Csv

Related

Trying to run a chkdsk on c:\ automatically

Trying to run a chkdsk on all drives of the computer. Having issues with having C:\ start at all.
Trying to use SendKeys to answer "Would you like to schedule this volume to be checked the next time the system restarts? (Y/N)" but having no luck. Where am I going wrong?
$host.ui.RawUI.WindowTitle = "BOHCdrivefix"
$FixCDrive = Start-Job -Name cDriveFix -ScriptBlock {chkdsk c:/f}
$wshell = New-Object -ComObject wscript.shell;
$wshell.AppActivate('BOHCdrivefix')
sleep 3
$wshell.SendKeys('y')
$wshell.SendKeys('~')
wait-job $FixCDrive
Receive-Job $FixCDrive | Out-File -FilePath D:\temp\cDriveFix.txt
shutdown -r -f -t 0
I would like to answer Y to the question then shutdown the PC and have it start the chkdsk
From Start-Job document:
The Start-Job cmdlet starts a Windows PowerShell background job on
the local computer.
A Windows PowerShell background job runs a command without
interacting with the current session.
Hence, you can't send any key from the current session to a command running inside a background job. Sending a key must be inside the background job. Fortunately, chkdsk.exe accepts use pipeline operator (which sends the results of the preceding command to the next command) as follows:
$FixCDrive = Start-Job -Name cDriveFix -ScriptBlock {Write-Output 'y'|chkdsk.exe c:/f}
or (using echo alias for Write-Output cmdlet):
$FixCDrive = Start-Job -Name cDriveFix -ScriptBlock {echo 'y'|chkdsk.exe c:/f}
Please note:
The type of the file system is NTFS.
Cannot lock current drive.
Chkdsk cannot run because the volume is in use by another
process. Would you like to schedule this volume to be
checked the next time the system restarts? (Y/N)
To answer "yes" to above question asked by chkdsk c:/f (fix file system errors on the boot partition), you must press Y, followed by Enter.
Honestly said, I'm not sure whether Write-Output cmdlet sends Enter into the pipeline. If not, force output of the new line as follows:
Write-Output "y$([System.Environment]::NewLine)"|chkdsk.exe c:/f}

How can I run powershell in the background if I want execution policy Bypass?

I have this batch file which runs the powershell script.
I want to run it the background but if I run with "windowstyle hidden" still visible.
powershell -ExecutionPolicy ByPass -windowstyle hidden -File "C:\script.ps1"
You can run, e.g. long running scripts, as a jobs.
To start it you run
$job = Start-Job -ScriptBlock {Get-Process}
this will start the Get-Process cmdlet in the background. The script can be also some custom made script or a longer script, it doesn't need to be a one-liner.
You can check its status by running
$job | Get-Job
and to receive the output you run
$job | Receive-Job
just note that once the data is received, it's lost. You can only receive it once, after that it's up to you to save it in a variable or later processing.
Finally to remove the job from the queue you run
$job | Remove-Job
I use the following function:
function bg() {
Start-Process `
-WorkingDirectory (Get-Location) `
-NoNewWindow `
-FilePath "powershell" `
-ArgumentList "-NoProfile -command `"$args`" "
}
It starts a new powershell instance which is executed in background and allows the usage of cmdlets.
You call it like:
bg "Start-Sleep 2; get-location; write 'done' "

Powershell build step, fire and forget?

I am running the following powershell command in a build step using TFS 2018.
Start-Job -ScriptBlock {
Invoke-Command -FilePath \\MyServer\run.ps1 -ComputerName MyServer -ArgumentList arg1, arg2
}
Since I don't want the script to affect the build step it should simply fire and forget the script. Hence I am using Start-Job. But it seems that once the step is done the process is killed. Is there a way to maintain the process lifetime even though the build step is done or the build process is finished?
Additional information... the powershell script should run on the remote server. The script itself triggers an .exe with parameters.
To simply fire and forget, invoke the script with Invoke-Command -AsJob:
Invoke-Command -AsJob -FilePath \\MyServer\run.ps1 -ComputerName MyServer -Args arg1, arg2
Start-Sleep 1 # !! Seemingly, this is necessary, as #doorman has discovered.
This should kick off the script remotely, asynchronously, with a job getting created in the local session to monitor its execution.
Caveat: The use of Start-Sleep - possibly with a longer wait time -
is seemingly necessary in order for the remote process to be created before the calling script exits, but such a solution may not be fully robust, as there is no guaranteed timing.
Since you're not planning to monitor the remote execution, the local session terminating - and along with it the monitoring job - should't matter.
When do you want the script to stop running? You could use a do-while loop and come up with a <condition> that meets your needs.
Start-Job -ScriptBlock {
do{
Invoke-Command -FilePath \\MyServer\run.ps1 -ComputerName MyServer -ArgumentList arg1, arg2
Start-Sleep 2
}while(<condition>)
}
Alternatively, you could use the condition $true so it executes forever. You will have to stop the job later in the script when you no longer need it.
$job = Start-Job -ScriptBlock {
do{
Invoke-Command -FilePath \\MyServer\run.ps1 -ComputerName MyServer -ArgumentList arg1, arg2
Start-Sleep 2
}while($true)
}
Stop-Job $job
Remove-Job $job
I've added a Start-Sleep 2 so it doesn't lock up your CPU as no idea what the script is doing - remove if not required.
Why not something like this:
Invoke-Command -Filepath \\MyServer\Run.ps1 -Computername MyServer -Argumentlist Arg1,Arg2 -AsJob
$JobCount = (get-job).Count
Do
{
Start-Sleep -Seconds 1
$totalJobCompleted = (get-job | Where-Object {$_.state -eq "Completed"} | Where-Object {$_.Command -like "NAMEOFCOMMAND*"}).count
}
Until($totalJobCompleted -ge $JobCount)
#doorman -
PowerShell is natively a single threaded application. In almost all cases, this is a huge benefit. Even forcing multiple threads, you can see the child threads are always dependent on the main thread. If this wasn't the case, it would be very easy to create memory leaks. This is almost always a good thing as when you close the main thread, .Net will clean up all the other threads you may have forgotten about for you. You just happened to run across a case where this behaviour is not beneficial to your situation.
There are a few ways to tackle the issue, but the easiest is probably to use the good ol' command prompt to launch an independent new instance not based at all on your original script. To do this, you can use invoke-expression in conjunction with 'cmd /c'. See Below:
invoke-expression 'cmd /c start powershell -NoProfile -windowstyle hidden -Command {
$i = 0
while ($true) {
if($i -gt 30) {
break
}
else {
$i | Out-File C:\Temp\IndependentSessionTest.txt -Append
Start-Sleep -Seconds 1
$i++
}
}
}
'
This will start a new session, run the script you want, not show a window and not use your powershell profile when the script gets run. You will be able to see that even if you kill the original PowerShell session, this one will keep running. You can verify this by looking at the IndependentSessionTest.txt file after you close the main powershell window and see that the file keeps getting updated numbers.
Hopefully this points you in the right direction.
Here's some source links:
PowerShell launch script in new instance
How to run a PowerShell script without displaying a window?

Unable to resume a workflow via task scheduler

In a powershell window I run the following workflow:
workflow foo { Suspend-Workflow; "hello world" | Out-File c:\users\weijgerss\desktop\foo.txt }
Then to resume the workflow, I have the following scheduled via task scheduler triggered to run at startup:
Import-Module PSWorkflow
$jobs = Get-Job -state Suspended
$jobs > c:\users\weijgerss\desktop\zqqfff.txt
$resumedJobs = $jobs | resume-job -wait
$resumedJobs | wait-job
# Task scheduler action: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Normal -NoLogo -NoProfile -Command "&'c:\users\weijgerss\desktop\resume.ps1'"
The workflow does not get resumed neither at startup, nor if I manually trigger it via Task Scheduler. The contents of zqqfff.txt indicates that the task scheduler activated powershell cannot see the workflow. A regular powershell window can see the workflow when I run Get-Job.
(Both the normal powershell window and the task scheduler powershell instance run as same user.)
I used procmon to see what's going on and I can see from this that when powershell normally vs taskscheduler it's looking at different workflow persistence paths, namely:
C:\Users\weijgerss\AppData\Local\microsoft\windows\PowerShell\WF\PS\default\S-1-5-21-3519956147-933941082-741972881-500_EL (a normal powershell window uses this folder)
C:\Users\weijgerss\AppData\Local\microsoft\windows\PowerShell\WF\PS\default\S-1-5-21-3519956147-933941082-741972881-500_EL_NI (a task scheduler activated powershell instance uses this folder)
I'm totally stumped. How can I get a task scheduler activated powershell instance to see the same workflows as normal powershell window can?
The below scripts give you a solution that automatically resumes powershell workflows after a reboot/crash using task scheduler at system start up:
resume-workflows.ps1: (the first line below fixes the _NI issue mentioned in the question)
[System.Management.Automation.Remoting.PSSessionConfigurationData]::IsServerManager = $true
Import-Module PSWorkflow
Get-Job -State Suspended | Resume-Job -Wait| Wait-Job
resume-workflows.cmd: (works around a windows 8/server 2012 task scheduler bug)
#rem This is a workaround for task scheduler bug
#rem See: http://support.microsoft.com/kb/2968540
set "USERPROFILE=%USERPROFILE%\..\%USERNAME%"
set "APPDATA=%USERPROFILE%\AppData\Roaming"
set "LOCALAPPDATA=%USERPROFILE%\AppData\Local"
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NonInteractive -WindowStyle Normal -NoLogo -NoProfile -Command "&'c:\path\to\resume-workflows.ps1'"
To put it all together use the following powershell script to shedule resume-workflows.cmd to run at system start up:
$trigger = New-ScheduledTaskTrigger -AtStartup
$action = New-ScheduledTaskAction -Execute "c:\path\to\resume-workflows.cmd"
$currentuser = ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)
Register-ScheduledTask -TaskName "Resume $($currentuser.Replace('\', '-'))'s Powershell Workflows" `
-Trigger $trigger -Action $action -RunLevel Highest -User $currentuser `
-Password (Read-Host "Enter password for $currentuser")
Enjoy!
(ILSpy, sysinternal's procmon, plenty of google and a dash of windbg were all instrumental in bringing the above answer to you)

Background job does not seem to run

I have function in a powershell 2.0 script that I am launching as a bkground job
Start-Job -ScriptBlock {CopyDataToServer($uploadSessionGuid)} -Name $uploadSessionGuid
Then at the end of the script I have
Wait-Job -State Running -Timeout $LogCopyTimeout
Event though the job is showing as Running and then completed, nothing is copied to the server.
How can I debug this?
Roman is right about CopyDataToServer and $uploadSessionGuid probably not being defined in the runspace the job executes in (upvoted his answer). BTW I believe it is better to wait on a specific job object than for any job in the running state e.g.:
$job = Start-Job {param($path, $guid) . $path\lib.ps1; CopyDataToServer $guid} `
-arg $pwd,$uploadSessionGuid
Wait-Job $job
Receive-Job $job
Note that you can use the -ArgumentList parameter to pass in parameters to your scriptblock. While you can access these arguments in your scriptblock via $args, I prefer using a param block and naming the args. This example also shows how you can pass in the path to a PowerShell script containing the function CopyDataToServer which gets dot sourced into the job's runspace.
The script block {CopyDataToServer($uploadSessionGuid)} is invoked in a new runspace where the command CopyDataToServer or the variable $uploadSessionGuid might be not available. To check this instead of your job run this at first:
Start-Job -ScriptBlock {
Get-Command CopyDataToServer
Get-Variable uploadSessionGuid
}
Wait-Job -State Running
Get-Job | Receive-Job
If the job returns the command and the variable then the problem is elsewhere and debugging is not over. But if it fails or gets wrong results then this is the problem to be fixed (to make the command available and/or use a different way to supply the parameter).