zamanlanmış görevler etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
zamanlanmış görevler etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

2026-06-11

Uygulama kilitlenince bildirim gelsin

Windows bir makine üzerinde çalışan bir uygulamamız var. Bu uygulama, nedendir bilinmez, bazen kilitleniyor. Böyle durumlar sonrasında yapmasını beklediğimiz işlemi yapamadığı için kesinti oluyor. Bunun önüne geçmek istiyorum.

Windows, uygulamanın bu şekilde kilitlendiği (Application hang) durumunda Uygulama olay günlüğüne 1002 kimliği ile bir kayıt düşüyor.

Logname = Application
ProviderName = Application Hang
Id = 1002

Bu olay kaydında kilitlenen uygulamanın başlatıldığı tarih ve saat bilgisi de yer alıyor. Bu bilgileri şimdilik eposta yoluyla bana atmasını istiyorum. Bu amaçla bir betik oluşturdum. Betik, son 1 dakika içinde oluşan bu tipteki bir olayları listeleyip sonuncusunu bir değişkene atıyor.

$ev1 = Get-WinEvent -FilterhashTable @{
LogName='Application';
ID=1002;
StartTime=(Get-Date).AddMinutes(-1);
ProviderName='Application Hang'
} -ErrorAction SilentlyContinue |
    Where-Object {$_.Properties[0].Value -eq "uygulama.exe"} |
    Select-Object -First 1

Olay kaydı içinde şu bilgiler var:

Uygulama adı : $_.Properties[0]

Uygulama kimliği : $ev1.Properties[2].Value 

Başlangıç zamanı : $ev1.Properties[3].Value

Kilitlenme tipi : $ev1.Properties[9].Value

Ancak başlangıç zamanı şurada yazdığım gibi, FILETIME verisi. Bunu okunabilir bir şekle getirmek için

Get-Date -Format "yyyy-MM-dd HH:mm:ss" -Date ([datetime]::FromFileTimeUtc($ev1.Properties[3].Value))

kullanıyorum. Ayrıca gelen eposta içinde bilgisayarın başlatılma zamanı da olsa iyi olur. Bu sebeple CIM içinden bu bilgiyi de çekiyorum:

Bilgisayar açılış : (gcim win32_operatingsystem).LastBootUpTime

Tarih verilerini görüntülemede yyyy-MM-dd HH:mm:ss şeklinde format görmek istediğim için bunu da dönüştürüyorum:

Get-Date -Format "yyyy-MM-dd HH:mm:ss" -Date ((gcim win32_operatingsystem).LastBootUpTime)

Olay kaydında bir de sonlanma zamanı var, Properties[4] içinde, ama bu alanda hep  4294967295 verisi var. Bu da aslında aslında henüz süreç resmi olarak sonlanmadığı için 0xFFFFFFFF değerine eşit olan

[uint32]::MaxValue

Dolayısıyla bu alanı kullanmamıza gerek yok.

Bu betiği C:\Scripts\betik1.ps1 gibi bir konuma kopyaladıktan sonra zamanlanmış görevi oluşturmak için aşağıdaki kodu kullandım.

schtasks /Create
        /TN "Uygulama Sonlanma Bildirimi"
        /SC ONEVENT
        /EC "Application"
        /MO "*[System[Provider[@Name='Application Hang'] and (EventID=1002)]]"
        /TR "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\betik1.ps1"

Böyle bir durumda eposta göndermesin, sadece uygulamayı sadece yeniden başlatılsın denebilir. Bu durumda basit bir şekilde

ps uygulama.exe | kill
start uygulama.exe

gibi içeriğe sahip bir betik iş görür. 

2024-07-05

Zamanlanmış görevler ile yeniden başlatma ayarlamak

Daha önce şu yazımda tek satırlık powershell komutları ile bir sunucuyu yeniden başlatma fikirleri paylaşmıştım.

Daha iyi bir fikrim var.

Yeniden başlatma zamanlamasını kendi bilgisayarımdan yapmak istiyorum. Tek tek yeniden başlatılacak bilgisayarlara uzak masaüstü veya uzak PSSsession bağlantıları ile değil. Aşağıdaki gibi schedule-restart.ps1 dosyasını oluşturuyorum.

param(    
    [string]$Computername,
    [string]$ScheduledTime="23:00"
)
Invoke-Command -Computername $Computername -ScriptBlock {
    $trig1 = New-JobTrigger -At $ScheduledTime -Once
    $obj1 = Register-ScheduledJob -Trigger $trig1 -Name "YenidenBaslatma" -ScriptBlock {Restart-Computer -Force}
    "Görev oluşturuldu: $($obj1.Name)"
    "Zaman : $($trig1.At)"
}

Bu betikle uzaktaki bir bilgisayar için yeniden başlatma görevi oluşturmak istediğimde şu şekilde bir kullanım yeterli:

.\schedule-restart.ps1 -ComputerName uzakpc

Varsayılan yeniden başlatma zamanı saat 23:00. Eğer değer vermezsek bu saatte, -ScheduledTime ile değer verirsek başka bir saatte yeniden başlatılacak. Zamanlama bir kez, bugün için geçerli olacak. Yani tekrarlama yok.

Peki bu isimde bir görev daha varsa ne olur? Bir önceki seferden oluşturduğum bir zamanlanmış görev varsa bir tane daha oluşturmayacaktır. İsmini farklı seçerek bir görev daha oluşturabilirim, ama bu durumda gereksiz görev kalabalığı olur. Aşağıda kırmızı ile vurguladığım bölüm, önce aynı isimle bir görev olup olmadığını kontrol ediyor, sonra bunu silmeyi öneriyor.

param(    
    [string]$Computername,
    [string]$ScheduledTime="23:00"
)
$bSilme = $false 
Invoke-Command -Computername $Computername -ScriptBlock {
$prev1 = Get-ScheduledJob -Name "YenidenBaslatma" -EA Silent
    if ($prev1) {
$resp1 = Read-Host "Aynı isimde bir görev daha var. Silinsin mi (H/e)?"
if ($resp1 -eq "E" -or $resp1 -eq "e") {
try {
Unregister-ScheduledJob -InputObject $prev1
}
catch {
$_.ErrorDetails.Message
}
}
else {
"Silmemeyi sectigin icin yeni gorev olusturulmayacak"
$bSilme = $true
}
    }
else {

        $trig1 = New-JobTrigger -At $ScheduledTime -Once
        $job1 = Register-ScheduledJob -Trigger $trig1 -Name "YenidenBaslatma" -ScriptBlock {Restart-Computer -Force}
        "Görev oluşturuldu: $($job1.Name)"
        "Zaman : $($trig1.At)"
    }
}

Takıntılı olanlar, bu görevi daha karmaşık sonuçlar yaratmadan silmek isteyebilir. Bunun için de bir delete-restartjob.ps1 betiği oluşturdum.

param(    
    
[string]$Computername
)
Invoke-Command -Computername $Computername -ScriptBlock {
$job1 = Get-ScheduledJob -Name "YenidenBaslatma" -EA Silent
if ($job1) {
$trig1 = $job1 | Get-JobTrigger
"Bulunan gorev: $($job1.Name)"
"Zamanlamasi : $($trig1.At)"
$resp1 = Read-Host "Bu gorev silinsin mi (H/e)?"
if ($resp1 -eq "E" -or $resp1 -eq "e") {
$job1 | Unregister-Scheduled
}
else {
"Silinme iptal edildi."
}
}
else {
"Silinecek yeniden baslatma gorevi bulunamadi"
}

İyileştirmenin sonu yok. Daha neler eklenebilir, neler...

2024-05-28

Kapanan sunucudan haberdar olmak

Sunucular önemli. Onlar ne zaman yeniden başlamış, sebebi neymiş gibi sorular hep var. Bu soruların cevaplarını ben daha sormadan bulacak bir yöntemim olmasını istedim. Söz konusu sunucular Windows. Linux için de benzer bir yöntem bulunabilir elbet.

Windows'da bir zamanlanmış görev oluşturayım. D:\bildir.ps1 konumundaki powershell betiğini çalıştırsın.

$act1 = New-ScheduledTaskAction -Execute powershell.exe 
-Argument "-ExecutionPolicy Bypass -NoProfile -File D:\bildir.ps1"
-WorkingDirectory "D:\"

Tetikleyici olarak da Sistem açılışını seçeyim (beklenmeyen bir şekilde kapanan bir bilgisayarın kapanma anına ait bir tetikleme mümkün değil).

$trig1 = New-ScheduledTaskTrigger -AtStartup 

Bu işi bir kullanıcının hesabı ile değil, SYSTEM hesabı ile yapacağım. Yüksek yetkilere ihtiyacım yok, standart yetkiler yeterli.

$prin1 = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Limited 

Önce görev nesnesini oluşturup sonrasında kaydetmek istiyorum. Bu işi iki adımda yapacağım.

$tsk1  = New-ScheduledTask -Action $act1 -Principal $prin1 -Trigger $trig1
$tsk1 | Register-ScheduledTask -TaskName "YenidenbaslatmaBildirimi" -TaskPath "\Firma"

Sıra geldi D:\bildir.ps1 betiğini oluşturmaya. Bu aşamaya kadar bilgisayar çoktan yeniden başladı. Betik bize açılıştan sonra bildirim verecek. Bu amaca yönelik aşağıdaki içerik yeterli:

$tarih = Get-Date -Format "yyyy-MM-ddTHH-mm-ss"
$sunucu_adi=$env:COMPUTERNAME
$smtpParams = @{
   From = "$sunucu_adi@firma.com"
   To = "admin@firma.com"
   Body = "$tarih - $sunucu_adi yeniden basladi"
   Subject = "$sunucu_adi yeniden basladi"
   SmtpServer = "smtp.firma.com"
}
Send-MailMessage @smtpParams
Add-Content -Value "$tarih - $sunucu_adi" -Path D:\bildir.log

Bu kısmı sadece sunucunun ne zaman başladığını bildirdi. Biraz da nedene olaklanalım. Windows'da nedeni bulmak için birkaç farklı yere bakmak gerekebilir. Olası sebepler Windows Update, güç kaybı, operatör isteği veya kritik bir uygulamanın hata vermesi olabilir. Bütün bu olayları içine alan bir olay kaydı günlüğünü inceleyelim:

$olaylar = Get-WinEvent -FilterHashTable @{LogName='System';`
StartTime=(Get-Date).AddHours(-1);`
ID=19,20,21,22,26,27,41,109,1000,1001,1002,1074,1076,6005,6006,6008} -ea silent

Bazen hiçbir olay dönmeyebilir. Bu gibi durumlarda, hata vermemesi için sonuna -ea silent (-ErrorAction SilentlyContinue kısaltması) ekledim. Daha sonra bu olayların içeriklerini gönderilecek mesajın gövdesine ekleyelim. Yukarıdaki betikte mesajın gövdesi $smtpParams içinde kısa bir şekilde oluşturulmuştu. Örneği bozmamak için aşağıdaki kodu bu splat değişkeninin ardından, Send-MailMessage cmdlet'inden önce olmasını düşündüm.

foreach ($olay in $olaylar) {
    $Body += "$($olay.TimeCreated) - $($olay.Id) - $($olay.Message)"
}

Bu şekilde en üstte tarih ve saatin yanında sunucu adı ve "yeniden basladi" görünecek. Ardından son 1 saatte konuyla ilgili olabilecek tüm olay kayıtları listelenecek. Bu elbette iyileştirmeye açık.

2024-04-25

schtasks.exe ile zamanlanmış görevler

Bir RDP oturum açması sırasında çalışacak bir görev oluşturmak

    schtasks /Create 
        /TN "MyEventTask"
        /SC ONEVENT
        /EC "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational"
        /MO "*[System[Provider[@Name='Microsoft-Windows-TerminalServices-RemoteConnectionManager'] and (EventID=1149)]]"
        /TR "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\1149.ps1"

Kullanıcı oturum açsa da açmasa da çalışacak bir görev için

    schtasks /Create 
        /TN "MyEventTask"
        /SC ONEVENT
        /EC "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational"
        /MO "*[System[Provider[@Name='Microsoft-Windows-TerminalServices-RemoteConnectionManager'] and (EventID=1149)]]"
        /TR "powershell.exe -ExecutionPolicy Bypass -File C:\Scripts\1149.ps1"
        /RU SYSTEM
        /NP

Mevcut görevlerin hepsini export etmek için

schtasks /Query /XML /TN "*"

Sonra bu xml dosyalarından tekrar görevler oluşturmak için

dir *.xml | % { schtasks /Create 
/XML $_.FullName
/TN "$_.Name.replace('.xml','')"
/RU "new_username"
/RP "new_password"

Bir görev nesnesini parola kaydetmeden oluşturmak için /NP anahtarı,

Tam tersi, parolayı belirlemek için /RP <parola>

Görevin yetki seviyesini belirlemek için /RL <LIMITED|HIGHEST>

ONSTART, ONLOGON, ONEVENT olayları için rastgele bir gecikme ayarlamak için ise /DELAY mmmm:ss kullanılabilir.

---

https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks-query

2024-04-24

Powershell ile ileri seviye zamanlanmış görevler

Zamanlanmış görevler sadece belli periyotlarda değil, belli olayların ardından çalışacak şekilde de oluşturulabiliyor. Ama bunu Powershell ile yapmak biraz meşakkatli. Oturum açma sonrasında ve bilgisayarın başlangıcında çalıştırılacak görevler için trigger nesneleri aşağıdaki gibi oluşturulabiliyor:
$trig1 = New-ScheduledTaskTrigger -AtLogon
$trig2 = New-ScheduledTaskTrigger -AtStartup

Ancak örneğin uzakmasaüstü bağlantısı ardından (TerminalServices-RemoteConnectionManager/Operational / Id:1149) çalışacak bir trigger'ın oluşturulması şu şekilde:

$class = Get-CimClass -Class "MSFT_TaskEventTrigger" -ClassRoot "Root/Microsoft/Windows/TaskScheduler"
$trig3 = $class | New-CimInstance -ClientOnly
$trig3.Subscription = "<QueryList>`
<Query>`<Select Path='Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational'>`
    *[System[Provider[@Name='Microsoft-Windows-TerminalServices-RemoteConnectionManager'] `
    and (EventID=1149)]]</Select></Query></QueryList>"

(satırlar ` karakteri ile bölünmüştür) ile, ve görev oluşturulması/değişikliğinde çalışacak görevin trigger'ı

$trigger1 = Get-CimClass -ClassName "MSFT_TaskRegistrationTrigger" -Namespace "Root/Microsoft/Windows/TaskScheduler"

ile yapılabiliyor.

Bir powershell görevinin gizli oluşturulması için

$settings = New-ScheduledTaskSettings -Hidden

kullanılabilir. Belli bir kullanıcının masaüstü ile etkileşime geçebilmesi için

$principal = New-ScheduledTaskPrinciple -UserId "makine\kullanidi" -LogonType Interactive

seçilmeli. LogonType alanı için Group, Interactive, InteractiveOrPassword, None, Password, S4U, ServiceAccount gibi değerler var. Bir görevin kullanıcı oturum açsın ya da açmasın çalışmasını sağlamak için S4U kullanılabilir.

$principal = New-ScheduledTaskPrinciple -UserId "makine\kullanidi" -LogonType S4U

Çalışacak görevin yetkileri RunLevel ile belirtilebilir (Limited/Highest):

$principal = New-ScheduledTaskPrinciple -UserId "makine\kullanidi" -LogonType Interactive -RunLevel Highest

LocalService veya System kullanıcıları ile bir görev çalıştırmak için

$principal = New-ScheduledTaskPrincipal -UserId "LOCALSERVICE" -LogonType ServiceAccount
$principal = New-ScheduledTaskPrincipal -UserId "NT AUTHORITY\SYSTEM" -LogonType ServiceAccount

ve hatta bir grup yetkileriyle görev planlamak için

$principal = New-ScheduledTaskPrincipal -GroupId "BUILTIN\Administrators" -RunLevel Highest

kullanılabilir. Mevcut bir zamanlanmış görevin trigger'larından birini değiştirmek için aşağıdaki yöntem kullanılabilir:

$task = Get-ScheduledTask -TaskName "Gorev1"
$trig = $task.Triggers
$trig.Repetition.Duration = $null
$trig.Repetition.StopAtDurationEnd=$false
Set-ScheduledTask -InputObject $task

Birden fazla trigger eklemek için iki trigger bir dizi olarak atanabilir:

$task.Triggers = ($trigger1, $trigger2)

gibi. Bir dizüstü bilgisayardaki görevin bataryadan çalışıldığı anda bile çalışmasını sağlamak veya uyku kipindeki bilgisayarları uyandırmak için için New-ScheduledTaskSettingsSet'in seçenekleri kullanılabilir.

Tekrarlayacak görevler için RepetitionInterval'a aşağıdaki gibi New-TimeSpan cmdlet'i ile zaman aralığı atanabilir.

$trig1 = New-ScheduledTaskTrigger -At 13:30 -Once -RepetitionInterval (New-TimeSpan -Hours 1)

benzer şekilde -RandomDelay parametresine de bir New-TimeSpan ile zaman aralığı verilmeli

$trig1 = New-ScheduledTaskTrigger -AtLogon -RandomDelay (New-TimeSpan -Minutes 5)

gibi. Sonradan değiştirmek için ise

$task.Triggers[0].Repetition.RepetitionInterval = New-TimeSpan -Hours 1

"No mapping between account names and security IDs was done" gibi bir hata, var olmayan bir kullanıcı belirtildiğini gösterir. Runlevel için Highest seçilecekse Powershell penceresi de yüksek yetkilerle açılmış olmalı. Varsayılan ayarlara sahip bir görev için New-ScheduledTaskSettings ve New-ScheduledTaskPrincipal kullanmak gerekli değil.

Bir olay sonrasında çalıştırılacak görev oluşturduğumuzda çalışmıyorsa yapılabilecek şey göreve ait trigger'ı kontrol etmek olabilir. Genelde provider name yanlış yazdığımda yaşadığım bir hataydı. Kontrol etmek için trigger'ı açtığımda aşağıdaki gibi bir ekranla karşılaştım:


Source (ya da provider) kısmında "Microsoft-Windows-TerminalServices-TaskScheduler" ve "TaskScheduler" seçenekleri var. TaskScheduler gibi Uygulama ve Hizmet Günlüklerinde genelde birden fazla Source olmıyor. Bunlardan üstteki yanlış yazılmış. Ama gerek schtasks.exe gerek powershell ile görev oluştururken sadece TaskSheduler yazmak da yetmiyor. Bunun yerine tam yolu "Microsoft-Windows-TaskScheduler" olarak yazmak gerek, burada o sadece TaskScheduler olarak kısaltılabilir.

Zamanlanmış görevin en son çalıştığı zamanı, döndüğü durum/hata kodu gibi bilgilerini görmek için ise

Get-ScheduledTask -Taskname "gorev-1" | Get-ScheduledTaskInfo 

kullanılabilir. Dönülen hata/durum kodları ile ilgili şu bilgiler var:

0 => "Görev başarıyla çalıştırıldı",
1 => "Yanlış veya bilinmeyen görev",
2 => "Dosya bulunamadı.",
a => "Ortam yanlış.",
00041300 => "Bir sonraki döngüde çalışmaya hazır",
00041301 => "Görev şu anda çalışıyor",
00041302 => "Görev devre dışı",
00041303 => "Görev henüz hiç çalışmadı",
00041304 => "Bu görev için başka çalışma zamanı planlanmamış",
00041305 => "Görevin çalışması için bir veya daha fazla özellik atanmamış",
00041306 => "Görevin çalışması kullanıcı tarafından sonlandırıldı",
00041307 => "Ya trigger yok, ya da trigger'lar devre dışı",
00041308 => "Trigger'larda zaman belirtilmemiş",
80010002 => "Call was canceled by the message filter",
80041309 => "Görevin trigger'ı bulunamadı",
8004130A => "One or more of the properties required to run this task have not been set.",
8004130B => "There is no running instance of the task.",
8004130C => "The Task Scheduler service is not installed on this computer.",
8004130D => "The task object could not be opened.",
8004130E => "The object is either an invalid task object or is not a task object.",
8004130F => "No account information could be found in the Task Scheduler security database for the task indicated.",
80041310 => "Unable to establish existence of the account specified.",
80041311 => "Corruption was detected in the Task Scheduler security database",
80041312 => "Task Scheduler security services are available only on Windows NT.",
80041313 => "The task object version is either unsupported or invalid.",
80041314 => "The task has been configured with an unsupported combination of account settings and run time options.",
80041315 => "The Task Scheduler Service is not running.",
80041316 => "The task XML contains an unexpected node.",
80041317 => "The task XML contains an element or attribute from an unexpected namespace.",
80041318 => "The task XML contains a value which is incorrectly formatted or out of range.",
80041319 => "The task XML is missing a required element or attribute.",
8004131A => "The task XML is malformed.",
0004131B => "The task is registered, but not all specified triggers will start the task.",
0004131C => "The task is registered, but may fail to start.Batch logon privilege needs to be enabled for the task principal.",
8004131D => "The task XML contains too many nodes of the same type.",
8004131E => "The task cannot be started after the trigger end boundary.",
8004131F => "An instance of this task is already running.",
80041320 => "The task will not run because the user is not logged on.",
80041321 => "The task image is corrupt or has been tampered with.",
80041322 => "The Task Scheduler service is not available.",
80041323 => "The Task Scheduler service is too busy to handle your request. Please try again later.",
80041324 => "The Task Scheduler service attempted to run the task, but the task did not run due to one of the constraints in the task definition.",
00041325 => "The Task Scheduler service has asked the task to run.",
80041326 => "The task is disabled.",
80041327 => "The task has properties that are not compatible with earlier versions of Windows.",
80041328 => "The task settings do not allow the task to start on demand.",
80070002 :  The system cannot find the file specified - (https://itexperience.net/0xfffd0000-in-task-scheduler-when-running-powershell-script/, 800070002 yaziyordu)
800710e0 :  Operator refures the request. Check permissions. see https://windowsreport.com/0x800710e0/
8007010b :  Start in folder is incorrect
C000013A => "The application terminated as a result of a CTRL+C.",
C0000142 => "The application failed to initialize properly.",
FFFD0000 :  Path error, possibly for powershell scripts

---

https://xplantefeve.io/posts/SchdTskOnEvent

https://richardspowershellblog.wordpress.com/2014/09/12/multiple-triggers-on-a-scheduled-task/

https://www.pdq.com/powershell/set-scheduledtask/

https://lazyadmin.nl/powershell/how-to-create-a-powershell-scheduled-task/

2023-05-30

Scheduled Tasks vs Scheduled Jobs

Bir süredir gördüğüm ama farklarını bilmediğim iki farklı tip cmdlet seti arasındaki farkları, okuduğum eski ama çok güzel bir yazı aracılığıyla öğrendim; Powershell camiasına uzun yıllar "Microsoft Scripting Guy" lakabıyla değerli katkılarda bulunmuş Ed Wilson'ın yazılarından biri ile.

Powershell'de zamanlanmış görevler oluşturmak istersek karşımızda ScheduledTasks ve ScheduledJobs adında iki farklı kavram oluyor. Task ve Job kelimeleri Türkçe'de "görev"e karşılık geliyor. Bu sebeple anlatırken İngilizceleri üzerinden anlatacağım.

Windows ile birlikte gelen Görev Zamanlayıcısı arayüzü, Task olarak gruplanan görevlerin zamanlanması için bir araç. Geniş bir kullanım yelpazesi var. ps1 uzantılı powershell dosyaları da çalıştırılabilir, exe uzantılı çalıştırılabilir programlar da çalıştırılabilir.

Jobs ise Powershell'e özgü bir kavram. Terminal kavramında arkaplan görevleri vardır. Çalışması uzun zaman alacak komutları arka planda çalıştırmak isteyebiliriz. Bu zaman diliminde diğer komutun işlemini bitirmesini beklemeden başka işlemler de yapabiliriz. Bu sırada arkaplanda çalışan Job'ları takip edilebilir, bittiğinde sonuçları ve çıktılarını gözlemleyebiliriz. Hatta bazı cmdlet'lerin -AsJob parametersi vardır; çalışmayı arkaplanda yürütmek için. Scheduled Jobs ise bu tip arkaplan görevlerinin bir uzantısı gibi.

PS> $j = Invoke-Command -ComputerName localhost, Server01, Server02 -Command {Get-Date} -AsJob

Bu komutla sunucular üzerinde Get-Date komutunu yürütme işlemi arkaplanda yürütülür. Daha sonra

PS> $j

Id Name   PSJobTypeName State      HasMoreData   Location
-- ----   ------------- -----      -----------   --------
3  Job3   RemotingJob   Failed     False         localhost,Server...

komutu ile görevin durumu görüntülenebilir. Hatta 3 farklı sunucu bize çıktı ürettiği için bu arkaplan görevinin ayrıntıları için

PS> Get-Job -IncludeChildJobs

Id  Name   PSJobTypeName State      HasMoreData   Location    Command
--  ----   ------------- -----      -----------   --------    -------
3   Job3   RemotingJob   Failed     False         localhost,Server...
4   Job4                 Completed  True          localhost   Get-Date
5   Job5                 Failed     False         Server01    Get-Date
6   Job6                 Completed  True          Server02    Get-Date

yazılarak alt görevler listelenebilir. Daha sonra her birinin çıktılarını görmek için

PS> Receive-Job -Name Job6 -Keep | Format-Table ComputerName,
>> DateTime -AutoSize
ComputerName DateTime
------------ --------
Server02     Thursday, March 13, 2008 4:16:03 PM

yazılabilir. ScheduledJobs aslında, bu arkaplan görevlerinin otomasyonu için ScheduledTask atlyapısı ile birlikte Poweshell için oluşturulmuş bir modül. Jobs ile ilgili modülün adı PSScheduledJob. Bu modülün içindeki cmdlet'lere bakmak için

PS> gcm -m PSScheduledJob

yazabiliriz. Oluşturulan Scheduled Jobs nesnelerine dosya sistemi üzerinden C:\Users\<kullaniciadi>\AppData\Local\Microsoft\Windows\PowerShell\ScheduledJobs klasöründen erişebiliriz.

Zamanlanmış görevleri Powershell'den yönetmek için geliştirilen modül ise ScheduledTasks. Bu modül ile birlikte gelen cmdlet'lere de bakalım:

PS> gcm -m ScheduledTasks

Jobs sadece Poweshell komutları veya ps1 uzantılı dosyaları çalıştırmak üzere tasarlanmış. Tasks için böyle bir kısıtlama söz konusu değil. taskschd.msc ile eriştiğimizi MMC snap-in'i ile hem Task'ları hem de Job'ları yönetebiliriz. Ama bu snap in ile sadece Task oluşturabiliriz. Powershell ile oluşturulmuş Job'lara ise taskschd.msc içinden Görev Zamanlayıcı Kitaplığı>Microsoft>Windows>Powershell yolundan ulaşabiliriz.

Bir örnek olması açısından Powershell yardım kitaplığını güncelleyecek bir Job yaratalım. Bunu her oturum açtığımızda, 30 dakikalık rastgele gecikme sonrasında çalışacak şekilde ayarlayalım. İlk iş önce bir zamanlayıcı (trigger) yaratmak:

PS> $t = New-JobTrigger -AtLogon -RandomDelay 00:30:00

Sonra da zamanlayıcı kullanarak yeni bir Job kaydedelim.

PS> Register-ScheduledJob -Name "YardimGuncelle" -Trigger $t -ScriptBlock {Update-Help -Force}

Bu Job'ın dosya sisteminde ve tasksch.msc'deki görünümleri aşağıdaki gibi oldu.

 


 Benzer bir şekilde bir de Task oluşturalım. Yine zamanlayıcıdan başlayalım.

PS> $t1 = New-ScheduledTaskTrigger -AtLogon -RandomDelay 00:30:00

Task'lar sadece powershell komut veya betikleri olmadıkları için eylem nesnesi oluşturmalıyız.

PS> $a1 = New-ScheduledTaskAction -Execute powershell.exe -Argument "-ExecutionPolicy RemoteSigned -Command 'Update-Help -Force'"

Nihayet bu iki nesneyi de birleştirip bir Task olarak kaydedelim.

PS> Register-ScheduledTask -TaskName "YardimGuncelle2" -Trigger $t1 -Action $a1

Mevcut Job'ları veya Task'ları sorgulamak için ise sırasıyla

PS> Get-ScheduledJob

ve

PS> Get-ScheduledTask

cmdlet'leri kullanılabilir.

İki modül arasında işlevsellik farkları var. Örneğin Job'lar için kaçırılan görevlerin tekrar çalıştırılması gibi bir seçenek yok, Task'lar için var. Bunu sağlayan da New-ScheduledTaskSettingsSet cmdlet'inin -StartWhenAvailable parametresi.

2023-05-10

Zamanlanmış Görevler

Windows'da zamanlanmış görevler (scheduled tasks) veya yeni adıyla görev zamanlayıcısı (task scheduler) olarak bilinen bir çalışma yapısı var. Periyodik olarak veya belli bir olay olduğunda (ör. oturum açma) uygulanması gereken bir görev varsa bu yapıyı kullanıyoruz.

Bu şekilde belli bir zamanda çalıştırlmak üzere oluşturduğumuz nesnelere görev,  bu görevleri periyodik veya belli olaylar olduğunda çalıştırma mekanizmasına tetikleyiciler, görevlerin amacı olarak çalıştırılacak uygulamalara da etkinlik diyoruz.

Temel olarak çok basit bir çalışma yapısı var, ama her seçenek her durumda işe yaramıyor. Örneğin her 15 dakikada bir çalıştırılmasını istediğimiz görev sadece iki durumda 15 dakikada bir çalıştırılıyor:

- Bir kez çalışacak şekilde ayarlanan ve sonrasında yenileme belirtilen durumlar,

- Belli bir periyotta (günlük, aylık vs) çalışacak şekilde ayarlanan ve sonrasında yenileme belirtilen durumlar.

Ne kaldı? Bir olay olduktan sonra (oturum açma/kapama, bilgisayar açılışı, bir olay günlüğü kaydı olduğunda vs.) çalıştırılmak üzere ayarlanan görevler sonrasında yenileme belirtilmiş olsa da yenilenmiyor.

Ayrıca yukarıda görüldüğü gibi en altta bir "Gizli" kutucuğu var. Çalıştırılan görev normal şartlarda bir pencere açıyorsa bu pencerenin gizli olmasını istediğimiz durumlarda işaretleniyor. Benim çalıştırdığım görev bir Powershell betiğiydi. Bunu da her çalıştırmada görüntülemek istememiyordum. Ama bu "Gizli" kutucuğunu işaretlememe rağmen her seferinde bu pencere görüntüleniyordu. Buna çözüm olarak görevin yukarıda seçildiği gibi "Yalnızca kullanıcı oturum açtığında çalıştır" seçeneği seçilerek değil, "Kullanıcı oturum açmışsa da açmamışsa da çalıştır" seçeneği ile oluşturulmasını önerilmiş. Bu bazı durumlarda işe yarayabilir ama benim için kullanıcının oturum açmadığı durumlarda bir anlamı yoktu. Bu durumda işimize yarayabilecek bir çözüm de superuser.com'da buldum. Görev etkinliğine powershell.exe (ve ilgili script'i) değil de bir .vbs dosyasını göstererek içerği şu şekilde belirlemek önerilmiş (hepsi tek satırda):

WScript.CreateObject("WScript.Shell")
.Run "powershell -ExecutionPolicy Bypass -WindowsStyle Hidden -File C:\\Path\\To\\MyScript.ps1', 0,true


2017-12-28

Powershell ile belirtilen zamanda bir komut çalıştırma

Şöyle bir ihtiyacım var; sunucularımdan birinin bu gece yarısından sonra saat 03:00'te tekrar başlamasını istiyorum. Bunu zamanlanmış görevlerle yapabilirim. Ama powershell ile yapmak istiyorum. Açık bir powershell penceresi bırakarak şu komutları yazarsam bu mümkün:

Start-Sleep -Seconds (New-Timespan -End '2099-12-29 03:00').TotalSeconds; Restart-Computer

Burada Start-Sleep cmdlet'i belli bir süre beklemeyi sağlayacak. Ne kadar bekleyeceğini New-Timespan cmdlet'i ona söyleyecek. New-Timespan cmdlet'i şu andan itibaren 29.12.2017 saat 03:00'e kadar ne kadar zaman beklemesi gerektiğini hesaplayacak. TotalSeconds ile de bu süreyi saniye cinsinden Start-Sleep cmdlet'ine verecek [1]. Bu kadar süre beklendikten sonra da Restart-Computer cmdlet'i ile sistemin tekrar başlaması sağlanacak.

Ancak bu komut dizisi, kopyala ve yapıştır amaçları için biraz tehlikeli. Yukarıdaki komutu kopyalayıp doğrudan powershell penceresine yapıştırıp orada tarih ve saati düzenleyeyim dersek doğrudan işletebilir. Daha önce orada yıl olarak 2099 yerine 2017 yazdığı ve o tarih de geçmişte kaldığı için makineyi doğrudan yeniden başlatmaya geçiyordu. Bunun için oraya yıl olarak uzak bir tarih girdim. Bir metin düzenleyicide (ör. Notepad) tarihi değiştirmeyi seçebilir, ya da kabaca kaç saat ve dakika sonra başlatılacağını hesaplayarak Start-Sleep cmdlet'ine bir elle hesaplanmış saniye miktarı verebiliriz. Örneğin 3 saat 45 dakika sonra komut çalıştırmak için

Start-Sleep -Seconds ((3*60+45)*60); Restart-Computer

2024-07-04 ek: Hatta belki de bir zamanlanmış işler (Scheduled Jobs) nesnesi kullanabiliriz. Örneğin bugün 22:30'da çalışacak bir işlem için

$baslat = New-JobTrigger -Once -At 22:30
Register-ScheduledJob -Trigger $baslat -Name "Restart" -ScriptBlock {Restart-Computer -Force}

-Force parametresi olmadan olmadı. 

2024-11-20 ek: Konu sadece kapatma veya yeniden başlatma ise, bunu powershell kullanmadan komut satırı programı shutdown.exe ile de yapabiliriz. Örneğin yeniden başlatmak için

shutdown /r /t 3600 # 3600 saniye, yani 60 dakika sonra çalışacak

Burada 3600, saniye cinsinden gecikme miktarı. Powershell komut satırını kullanıyorsak buraya gecikmeyi hesaplayacak bir ifadeyi parantez içinde girebiliriz. Örneğin 1 saat 15 dk sonra yeniden başlatmak için

shutdown /r /t (75*60) # 75 dakika sonra çalışacak

Yeniden başlatma yerine bir kapatma işlemi yapmayı planlıyorsak, /r anahtarını /s ile değiştirmek gerek:

shutdown /s /t 3600 # 1 saat sonra kapanacak

Shutdown komutuyla verilen bir yeniden başlatma veya kapatma işlemini durdurmak için /a anahtarı (abort) kullanılabilir.

shutdown /a 
--- 

[1] http://www.madwithpowershell.com/2013/08/sleep-until-certain-time-with-powershell.html