30.04.2024

Uzak masaüstü bağlantıları

Microsoft-Windows-TerminalServices-LocalSessionManager/Operational altındaki olaylar hakkında bulduğum birkaç küçük ipucu:

Genelde gördüğüm 3 olay var:

24: Session disconnected

25: Session reconnected

40: Yukarıdaki 2 olaya eşlik eden, reconnection ve disconnection olaylarına ait reason code'lar verililir.

Reason code yeni yapılan bağlantının ya da kesilen bağlantının sebebi hakkında bazı küçük bilgiler içerir. Şu ve şu sitelere göre:

0: Kullanıcı uzak masaüstü oturumunu pencerenin çarpısına basarak (ya da ağ bağlantı sorunu sebebiyle) kapatmıştır. Bu olay 24 olayı ile birlikte olur. Ayrıntılar 24 olayında belirtilir.

5: Oturumun kapanma sebebi bu oturumun başka bir oturumla değiştirilmesi. Aslında şu demek; disconnect edilmiş bir oturum kalmıştı, yeniden o oturuma bağlanıldı.

Ben sık görmesem de başka reason code'lar:

2: Admin kullanıcı oturumunu sonlandırdı (Admin has signed off/ Terminated the user session.)

3: Boş kalma süresi aşıldı (Idle Session Timer limit reached.)

4: İzin verilen süreden daha fazla bağlanıldığı için sonlandırıldı.

6: Hafıza yetersiz

7: Sunucu bağlantıya izin vermedi

8: Sunucu güvenlik sebebiyle bağlantıyı reddetti.

9: Sunucu güvenlik sebebiyle bağlantıyı reddetti.

10: Kullanıcı adı ve şifrenin tekrar girilmesi gerekli

11: Kullanıcı etkinliği sebebiyle sonlanma.

12: Kullanıcı oturumu kapattı.

26: Ağ kesintisi veya grup ilkesi / admin müdahalesi ile (belki de boş kalma süresi, etkinleştirilmemiş sunucu)

---

https://community.spiceworks.com/t/remote-desktop-services-disconnect-code/375433/7

https://learn.microsoft.com/tr-tr/windows/win32/termserv/extendeddisconnectreasoncode?redirectedfrom=MSDN


25.04.2024

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

24.04.2024

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/

22.04.2024

Bilgisayar hata mesajları ve kullanıcılar

Genelde yaklaşım "erkanda bişey çıktı ama tamama bastım"dan ibaret olan durumlar. Bu konuda her gördüğümde güldüğüm Jonathan Nightingale imzalı aşağıdaki karikatürize hata mesajını paylaşmaya değer buluyorum.



17.04.2024

PSReadline ve komut geçmişinde aramalar yapmak

Linux'ta (bash veya zsh) Ctrl+R aracılığıyla geçmişte arama yapmak mümkün. Powershell'de de benzer bir işlev var. Ctrl+R'ye bastığımda yazdığım anahtar kelime ile geçmişte geriye doğru bir arama yapabiliyorum. İlk çıkan sonuçtan sonra aramayı devam ettirerek bir öncekine ulaşmak için Ctrl+R'ye basmaya devam edebiliyorum. Ctrl+S ise aramanın yönünü değiştirerek ileriye doğru aramaya geçiyor. Arama geçmiş komutların içinde geçen herhangi bir parçaya göre yapılıyor.

Benzer bir işlev de F8 ile mümkün. Bu ise aramayı sadece yazdığım anahtar kelime ile başlayanlar ile kısıtlıyor. F8'e basmaya devam ederek geçmişte bir adım geriye gidebiliyor veya Shift+F8 ile arama yönünü çevirerek ileriye doğru gidebiliyorum.

Bu işlevleri bana sunan PSReadLine'ın kısayol tuşlarının listesini aşağıdaki komutla almak mümkün [1]:

Get-PSReadlineKeyHandler | ? {$_.function -like '*hist*'}

PSReadLine'ın bütün klavye kısayolları atamalarını görmek için

Get-PSReadLineKeyHandler

ve henüz atanmamış işlevleri görmek için ise

Get-PSReadLineKeyHandler -Unbound

kullanabiliriz. Yeni bir işlev ataması yapmak da mümkün. Örneğin henüz bir klavye ataması yapmadığım CaptureScreen işlevini Ctrl+Alt+p klavye kısayoluna atamak için

Set-PSReadLineKeyHandler -Chord 'Ctrl+Alt+p' -Function CaptureScreen

yapabiliriz. Herhangi bir klavye kısayolunun bir atamada kullanılıp kullanılmadığını sorgulamak için Shift+Alt+* (İngilizce kaynaklarda Alt+? denmiş ama Türkçe klavyelerde bu sıfır (0)'ın yanındaki asteriks (*)'e Shift ve Alt ile basmak demek) bastıktan sonra istenen klavye kısayoluna basmak yeterli. Klavyelerin sağ bölümünde bulunan keypad'deki tuşlar gibi sıradışı tuşların isimlerini öğrenmek için ise aşağıdaki komutu yazdıktan sonra istenen tuşa basmak yeterli:

[System.Console]::ReadKey()

2025-08-25 Ek: Yazmak istediğimiz komutu tahmin ederek bize öneriler sunan algoritma, bu önerileri iki farklı şekilde sunuyor. InlineView ya da  ListView. İkisinin arasında geçiş  yapmak için F2'ye basılabilir.

Get-PSReadLineOption | select PredictionViewStyle
PredictionViewStyle : ListView
Set-PSReadLineOption -PredictionViewStyle InlineView 

Jason Helmick şu videosunda önerilerin renklerini değiştirmeyi de anlatmış.

Set-PSReadlineOptions -Colors @{InlinePrection = "#8a0303"}

---

[1] https://serverfault.com/questions/891265/how-to-search-powershell-command-history-from-previous-sessions

[2] https://learn.microsoft.com/en-us/powershell/scripting/learn/shell/using-keyhandlers?view=powershell-7.4

15.04.2024

Windows'da betikleri bekletmek

Powershell'in Start-Sleep cmdlet'i betiklerde gerekli süre boyunca beklemek için ideal.

Start-Sleep -Seconds 5

Peki bunu komut satırındaki bat dosyalarında nasıl yaparız? Eskiden sleep gibi bir komut vardı, Windows'da, ama artık yok.

İlk yöntem belki de bat dosyasının içinden powershell komutu çalıştırmak olabilir.

powershell "start-sleep -seconds 5"

Bazı yaratıcı beyinler 5 kere ping atmanın ve çıkışı nul'a göndermenin işe yarayacağını söylemiş:

ping 1.1.1.1 -c 5 > nul

Güzel. Daha güzeli timeout:

timeout 5

Bir tuşa basana kadar beklemesi için pause olabilir:

pause
Press any key to continue...

Bunun eşdeğeri powershell'de Read-Host olabilir

Read-Host "Bir tuşa basın..."

Ayrıca [1] ve [2]'de choice ve waitfor komutlarından da bahsedilmiş:

waitfor /t 5 pause
choice /c AB /N /T 5 /D A /M "5 sn bekliyorum"

---

[1] https://www.wikihow.com/Delay-a-Batch-File

[2] https://stackoverflow.com/questions/1672338/how-to-sleep-for-five-seconds-in-a-batch-file-cmd

14.04.2024

Batch dosyalarında değişkenler

Powershell'de tarih ve saati bir değişkene atmak basit:

$tarih_saat = Get-Date -Format "yyyy-MM-dd HH:mm:ss"

Ancak bunun eşdeğeri batch dosyalarında bu şekilde olmuyor. Ben şöyle bir yol buldum:

echo @off
for /f "tokens=*" %%a in ('date /t') do set tarih=%%a
for /f "tokens=*" %%b in ('time /t') do set saat=%%b
set tarih_saat=%tarih%- %saat%