20.08.2021

Fedora ve Ubuntu'da unutulan parolayı sıfırlamak

Parolalar unutulur. Unutulduğunda sıfırlanmaları gerekir. Bu her işletim sistemi için geçerli.

Ubuntu ile başlayalım. Açılıştaki Grub menüsünden recovery seçeneği ile başlamak gerek. Recovery genelde Advanced Options altında kurulu her çekirdek (kernel) sürümü için ayrı ayrı verilmiştir. En yenisi ile başlayabiliriz. Bu adımdan sonra bir nonktada aşağıdaki gibi bir ekran ile karşılaşacağız.

 
Burada kırmızı ile belirtilen root seçimi ile ilerleyeceğiz. Bir sonrak aşamada 

Press Enter for maintenance
(or press Control-D to continue):

mesajı bizi karşılayacak. Enter'a basıp root ile giriş yapacağımız terminali açalım. Control-D sadece normal açılışa devam etmek için. Bizim istediğimiz bu değil.

Artık burada 

# passwd <kullanıcı_adi>

komutu ile parolamızı değiştirebiliriz. Yetki hatası alırsak

# mount -o remount,rw /

komutu ile diskimizi yazma modunda mount ederek passwd komutunu tekrar deneyebiliriz.

Eğer kullanıcı adımızı da unuttuysak

ls /home

komutu ile sisteme oturum açmış kullanıcıların ev klasörleri arasından seçim yapabiliriz. Şifre değiştirme işlemi tamamlandıktan sonra

# reboot

komutu ile sistemi tekrar başlatabiliriz.

Fedora için işler biraz daha farklı. Açılışta grub menüsündeyken Fedora'nın satırında "e"ye basarak düzenleme moduna geçip aşağıdaki ekranda kırmızı ile işaretlediğim satırı

load_video
set gfxpayload=keep
insmod gzio
linux ($root)/vlinux-5.13.9-200.fc34.x86_64 root=/dev/mapper/fedora_localhost\
--live-root ro resume=/dev/mapper/fedora_localhost--live-swap rd.lvm.lv=fedora\
_localhost-live/root rd.lvm.lv=fedora_localhost-live/swap rhgb quiet
initrd ($root)/initramfs-5.13.9-200.fc34.x86_64.img

Şununla değiştirmemiz gerek:

rd.break enforcing=0

Değiştirdikten sonra bu ayalarla sistemi başlatmak için Ctrl+X'e basalım. Sistem yine grafik arayüz olmadan tek kullanıcılı modda açılacak. Burada yine Ubuntu'daki gibi

Press Enter for maintenance
(or press Control-D to continue):

Ekranı gelecek. Enter'la devam edelim. Bu noktada dosya sistemi /sysroot altında sadece okuma kipinde bağlanmış bulunuyor. Fedora'da root partition'ı yazma kipinde tekrar bağlamak ve şifreyi değiştirmek için:

# mount -o remount,rw /sysroot
# chroot /sysroot
# passwd <username>

yapmak gerek. 

Authentication token manipulation error

hatası aldıysak bir önceki adımlarımızı kontrol etmemiz gerek; dosya sistemi rw olarak bağlanmamış demektir.

İşler bittiğinde 2 kez exit yazıp enter'a basarak tekrar başlatacağız.

Açılışta Fedora SELinux koruması bize /etc/shadow dosyasının SELinux etiketlerinin yanlış olduğuyla ilgili uyarılar verebilir. Bir komut satırı açıp şunları yazmamız gerekecek:

# sudo restorecon -v /etc/shadow

Bunun öncesinde

# ls -Z /etc/shadow

system_u:object_r:unlabeled_t:s0 /etc/shadow

olan SELinux etiketinin sonrasında

# ls -Z /etc/shadow

system_u:object_r:shadow_t:s0 /etc/shadow

olarak değiştirildiğini görebiliriz. Son olarak

# sudo set enforcing 1

ile SELinux'u tekrar etkinleştirebiliriz.

Ek 2022-05-24: Etiketleme işini bütün disk için otomatik yapmasını sağlamak için 2 kez exit yazıp çıkmadan hemen önce

touch /.autorelabel

yazabiliriz. Ama bu büyük bir disk için uzun sürecektir.

---

https://docs.fedoraproject.org/en-US/fedora/latest/system-administrators-guide/kernel-module-driver-configuration/Working_with_the_GRUB_2_Boot_Loader/

Server üzerinde bir süreç sonlanınca eposta göndersin

Temel olarak Windows olay günlüğüne belli bir yeni olay düştüğünde eposta göndermek veya başka bir komut çalıştırmak mümkün. Ben de bir sürecin sonlanması durumunda eposta göndereceğim. Sürecin sonlanmasını da Sysinternals arası Sysmon ile denetleyeceğim. İstenmesi durumunda:

  • Bir sürecin başlaması veya sonlanması
  • Bir dosyanın oluşturulması veya silinmesi
  • Kayıt defterinde bir değer oluşturulması veya silinmesi
  • Belirli bir IP adresine/porta ağ bağlantısı yapılması

gibi durumlarda istenen hareket yapılabilir.

Öncelikle sysmon'u istenen sürecin sonlanması durumu için bir yapılandıralım. İlk iş, sistemde var olan bir sysmon yapılandırması bulunuyor mu, bakalım:

> sysmon64 -c

Bu satırdan sonra "Sysmon is not installed on this computer" yazdıysa sistemde var olan bir yapılandırma yoktur ve sıfırdan başlayabiliriz. Eğer bir yapılandırma varsa onu bozmadan üzerine yapılandırma yapmak gerek. -c anahtarıyla gösterilen xml dosyasını bulup yapılandırmamızı bunun üzerine yapmamız gerekecek. Bu konumuz dışında. Eğer varolan bir yapılandırma varsa ve sıfırlamak istiyorsak

> sysmon64 -c --

ile sıfırlayabiliriz.

Tekrar başa dönelim; "Sysmon is not installed on this computer" yazdıysa sistemde hiç sysmon hizmet sürücüsü kurulmamış demektir. Bu durumda sysmon'u -i anahtarıyla çalıştırarak hizmet sürücüsünü kurabiliriz. Ben 64-bitlik sürümünü kullanacağım. Ayrıca ilk çalıştırmada son kullanıcı lisans anlaşmasını (EULA) da kabul etmem gerek (-accepteula).

> sysmon64 -accepteula -i process_end.xml

Terminalimiz Powershell ise dosyaları belirtirken önlerine tam adlarını (aynı klasördeysek de .\process_end.xml şeklinde) yazmamız gerek. Tab tamamlaması bu durumda çok işe yarar.

process_end.xml dosyası oluşturmadan sadece -i anahtarını kullanarak başlarsak varsayılan ayarlar geçerli olur ve takibini yapmak istediğimiz olay sadece bizim ilgilendiğimiz uygulama için değil, sistemdeki tüm uygulamalar için yaratılır, ve çok fazla eposta almaya başlarız. Bunun için böyle bir xml dosyası ile hedefimizi daraltmamız gerekecek.

Gelelim xml dosyasının oluşturulmasına. Şu anda bende sürüm 13.22 yüklü. Bu sürümde şema sürümü de 4.70. Bunu da xml dosyamızda belirtmemiz gerekecek. 13.22 ile birlikte 4.70 veya öncesi şema sürümleri de kullanılabilir ama sürümler arası fark bazen beklenmeyen sonuçlar verdiği için (RuleGrup'ların mantıksal operatörleri varsayılan işleme biçimi gibi) ne yaptığımızı bildiğimizden emin olmamız gerek. XML dosyasındaki ilk satır şema versiyonu:

<Sysmon schemaversion="4.7">

Bu satırdan sonra hash algoritmamızı belirtelim. Olay günlüğüne çalıştırılan her uygulamanın hash'i de hesaplanıp yazılacak.

<HashAlgorithms>sha256</HashAlgorithms>

Sadece 1 ölçütüm (süreç sonlanma) olduğu için daha fazla karıştırmadan bununla ilgili process terminate tag'ını girelim

<EventFiltering>

<ProcessTerminate onmatch="include">

Takip ettiğimiz sürecin hangi özelliği ile ilgileniyoruz? Örnek olarak sistemdeki process explorer'ı takip edelim (procexp64.exe)

<Image condition="end with">procexp64.exe</Image>

Nihai olarak açık taglarımızı kapatlım:

</ProcessTerminate>

</EventFiltering>

</Sysmon>

Her process explorer'ın sonlanmasında "Microsoft-Windows-Sysmon/Operational" EWT olay günlüğüne 5 numaralı olay kayıtları düşer. Görmek için olay günlüğünde Uygulama ve Hizmet Logları (Applications and Services Logs)>Microsoft>Windows>Sysmon>Operational altına bakmak gerek. Gelelim bu olay kayıtları için bir hareket oluşturmaya. Olay günlüğüne bir tane 5 numaralı olay düştükten sonra olay görüntüleyicisi penceresinin sağ kısmında "Bu olaya göreve ekle (Attach Task to this Event)"e tıklayarak işleme devam edebiliriz. Uygulama çalıştırmanın dışındaki diğer seçenekler (eposta göndermek ve bir mesaj görüntülemek) artık terk edilmiş hareketler. Biz eposta gönderebilmek için bir powershell script'i kullanacağız. Şöyle bir script işimizi görür:

$EmailFrom = "gonderen@mailadresi.com"
$EmailTo = "alici@alantaraf.com"
$Subject ="procexp sonlandı"
$Body = "Bekledigimiz Process Explorer'ın sonlanması şu an gerçekleşti."
$SMTPServer = "smtpsunucu.mailadresi.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)

Bu dosyanın ilk 5 satırı sırasıyla gönderenin eposta adresi, alıcının eposta adresi, eposta mesajının konusu, mesajın içeriği ve eposta sunucumuz şeklinde. Bunları uygun şekilde değiştirmek gerek.

Ya da Send-MailMessage cmdlet'ini kullanarak gönderebiliriz:

$smtpParams= @{
  From = "gonderen@mailadresi.com"
  To = "alici@alantaraf.com"
  Subject ="procexp sonlandı"
  Body = "Bekledigimiz Process Explorer'ın sonlanması şu an gerçekleşti."
  SMTPServer = "smtpsunucu.mailadresi.com"
}

Send-MailMessage @smtpParams

Bu scriptin C:\Users\metin\Belgeler\eposta_gonder.ps1 gibi bir konumuna kaydedildiğini varsayalım.

En son olaya görev ekleme aşamasında kalmıştık. Bu pencerede3 kutu var. Birincisi çalıştırılacak program. Buraya powershell.exe'nin konumunu yazmamız gerek. Bu çoğu durumda 

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

şeklindedir. Argumanlar için yapmamız gereken birinci iş geçici olarak Execution Policy'yi devre dışı bırakmamız ve scriptimizi bir -File anahtarı ile göstermemiz. Hepsi birlikte şöyle olabilir:

-ExecutionPolicy Bypass -File C:\Users\metin\Belgeler\eposta_gonder.ps1

Üçüncü kutu sadece çalışacak script bulunduğu klasörle ilgili bir dosya işlemi yapacaksa gerekli. Bizimki gerekmeyeceği için boş bırakabiliriz. Boş kalmasın diyenler C:\Users\metin\Belgeler\ gibi birşey yazabilir.

19.08.2021

Regular Expressions

Bu konu bir blog yazısında anlatılacak kadar kısa bir konu değil; farkındayım. Ama konunun ana hatlarını yine de özetlemek istiyorum.

Bir metnin içinde (düzyazı, program, bir klasördeki dosya içerikleri, URL dizileri, dosys sistemi yolları vs) bir "dünya" var mı yok mu aramak istiyoruz. Örneğin bir klasördeki txt uzantılı dosyaların içinde. Bunu powershell ile yapmanın yolu

> Select-String -Pattern "dünya" -Path *.txt

Bu arama bize bazı sonuçlar döner. Ama diyelim ki dosyaların bazılarında "ü" yerine "u" kullanıldığını farkettik. Bu durumda "dunya" anahtar kelimesini de içerecek şekilde aramamızı değiştirelim. Ayrıca varsayılan parametre sıralaması ve alias kullanımıyla komutu biraz kısalatalım.

> sls "dünya|dunya" *.txt

"|" operatörü VEYA işlemi görür. Burada değişen 1 harf olduğu için bunu ü VEYA u şeklinde de yapabilirdik.

> sls "d[u|ü]nya" *.txt

Köşeli parantezler, bir karakter kümesi belirtmek için kullanılır. Örneğin sadece d,u,ü,n,y ve a'dan oluşan 5 veya 6 harfli kelimeleri bulmak istersek

> sls "[adnuüy]{5,6}" *.txt

yazabiliriz. Bu satırla sadece dünya ve dunya anahtar kelimelerini bulmakla kalmayacağız, ayrıca içinde herhangi bir sırada bu harflerin 5 veya 6'sı geçen başka kelimeleri de bulacağız. Ayrıca bunlar tam kelimeler de olmayacak, kelimenin herhangi bir bölümünde bu harflerin 5 veya 6'sının geçmesi yeterli olacak.

Bazı özel operatörleri listeleyelim:

?Önceki karakter 0 veya 1 kez geçecek
*Önceki karakter 0 veya daha fazla geçecek.
+Önceki karakter 1 veya daha fazla geçecek.
{n,}Önceki karakter n veya daha fazla geçecek.
{n,m}Önceki karakter en az n kere, en fazla m kere geçecek. Arasındaki sayılarda da olabilir.
. Yeni satır karakteri hariç her tekil karakteri bulur.
[a-z]Yalnızca "a" ve "z" arazındaki karakterleri bulur.
[^abc]a, b ve c olmayan her karakteri bulur.
^Satırın başını bulur.
$Satırın sonunu bulur.
( )Gruplandırma için. Parantezin içindeki ifade hatırlanır ve daha sonra \n veya $n gibi bir operatör ile kullanılabilir.
(?:)Aynen ( ) gibi gruplandırma için kullanılır, ama parantezin içindeki ifade hatırlanmaz
|veya operatörü
\wtüm harfleri bulur.
\Wharf olmayan karakterleri bulur.
\sBoşluk karakterlerini bulur.
\SBoşluk olmayan karakterleri bulur.
\dSayıları bulur.
\DSayı olmayanları bulur.
\AString'in başını bulur.
\ZString'in sonunu bulur. Bir yeni satır karakteri varsa ondan hemen öncesini bulur.
\zString'in sonunu bulur.Yeni satır karakterini de atlar.
\GEn son aramanın bulduğu noktanın bir sonrasını bulur.
\bKelime sınırlarını bulur.
\BKelime olmayan sınırları bulur\.

Bir string değişkenimiz olsun, aşağıdaki gibi

> $str1 = "Bugün günlerden Pazartesi"

Regular expression ifadelerimizin nasıl çalıştığını bu string üzerinde deneyerek görelim. Powershell'de -match  operatörü regular expression kullanır. Ama -like kullanmaz. Değiştirme operatörü -replace de regular expression kullanır. Değişkenin içinde "gün" geçiyor mu bakalım:

> $str1 -match "gun"

False

Sonuç altta "False" olarak verildi. "u" ve "ü" alternatifli olarak deneyelim:

> $str1 -match "g[u|ü]n"

True

Bu şekilde bir eşleşme bulundu. Pazartesi geçen kelimeleri Salı ile değiştirmek için

> $str1 -replace "Pazartesi","Salı"

Bugün günlerden Salı

Çok karşılaşılabilecek bir şey, ay\gün\yıl şeklinde yazılan tarihlerin gün.ay.yıl şeklinde çevrilmesi olabilir. Bunun için back reference denen gruplandırma operatörü olarak yukarıdaki tabloda listelediğim parantezleri kullanmalıyız. İşlem şu şekilde olacak, 2 haneli ay bilgisi bulunacak, 2 haneli gün bilgisi bulunacak, bunlar yer değiştirilecek ve ayraç olarak kullanılan "\" yerine "." konacak ve sonrasında gelen 4 haneli yıl bilgisi değiştirilmeyecek. Bunun için bulduğu gün, ay ve yıl verilerini hatırlaması gerekecek.

> $str1 = "bugün 11\20\1974"

> $str1 -replace '(\d{2})\\(\d{2})\\(\d{4})','$2.$1.$3'

bugün 20.11.1974

Burada çift tırnak yerine tek tırnak kullanmaya dikkat.

Çok sık yaşadığım bir konu, bir klasörde bir dosya arıyorum. Dosya adında geçen bir anahtar kelimeye göre aramak kolay. Ama 2 anahtar kelimem var ve hangisi önce gelecek, hangisi sonra gelecek, aralarında başka kelime olacak mı, bu ayrıntılar önemli değil. Sadece bu 2 anahtar kelimenin dosya adında geçmesini istiyorum. Bunun için yapılması gereken [3]:

> dir * | where {$_.Name -match "(?=.*format)(?=.*powershell)"}

Benzer şekilde bir klasördeki dosyaların içinde de geçmesini istediğim 2 anahtar kelime için arama yapabilirim [9]:

> sls "(?=.*black)(?=.*star)" *-d.txt

Ekleme  2022-07-13: linux komut satırı aracı grep'in alışkanlıkları biraz farklı. farketebildiğim istisnaları şöyle:

\d    ->  [[:digit:]]

\w   ->  [[:blank:]]

ayrıca [8]'de anlatıldığı gibi şu "karakter sınıfları" da var:

[[:alpha:]]        Alfabetik karakterler

[[:alphanum:]]    Alfanumberik karakterler

[[:lower:]]        küçük harfler

[[:upper:]]        büyük harfler

Bunlara "genişletilmiş düzenli ifadeler" (extended regular expressions) denmiş. Bunları kullanmak için grep'e -E anahtarını sağlamamız istenmiş. Örneğin Manjaro'da linux çekirdeği kurulma tarihlerini /var/log/pacman.log dosyasından süzmek için

> grep -E "installed linux[[:digit:]]{2,3}[[:blank:]]" /var/log/pacman.log

kullandım. Her yeni çekirdek kurulumunda installed linux, güncellemesinde ise upgraded linux, sonrasında ise duruma göre  2 veya 3 karakterlik sürüm numarasını yazıyordu. Yeni çekirden yardımcı paketlerle geldiğinden bir tire karakteriyle bu yardımcı karekterler de listelenmesin diye [[:blank:]] de kullandım.

Ek 2023-06-04: Bazen de grep ile "içine geçmeyen"leri bulmak gerekir. Bu durum için grep için -v anahtarı önerilmiş. Şöyle olabilir:

> grep "icinde_gesin" /var/log/birdosya | grep -v "gecmesin"

Ek 2023-10-17: İçinde IP adresi olan bir string değişkenimiz var. Bu string'in içinde geçen IP adresini görmek istiyoruz. Yöntemlerden biri

PS> ($metin | select-string -Pattern "([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})").Matches[0].Value

diğeri de

PS> $metin -match "([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})"
PS> $matches[0]

---

[1] https://en.wikipedia.org/wiki/Regular_expression

[2] https://regexr.com/

[3] https://www.baeldung.com/string-contains-multiple-words

[4] https://www.regular-expressions.info/

[5] https://www3.ntu.edu.sg/home/ehchua/programming/howto/Regexe.html

[6] https://regex101.com/

[7] https://www.geeksforgeeks.org/write-regular-expressions/

[8] https://linuxize.com/post/regular-expressions-in-grep/

[9] https://www.rexegg.com/regex-lookarounds.html

16.08.2021

Powershell, diziler ve Add-Member

Şöyle bir hedefim var; Bir klasördeki dosyaları tablo olarak listelerken bir sütunda dosya sistemindeki sahip bilgisini de görmek istiyorum. Boş bir dizi oluşturayım.

> $dosyalar = @()

Sonra bu diziyi dosya bilgileri ile doldurayım.

> dir | % { $dosyalar += [PSCustomObject]@{

    Isim = $_.FullName

    Boyut = $_.Length

 }

Bu adımda $dosyalar dizisi şöyle olur:

Isim                          Boyut

-----                         -----

D:\Klasor\Dosya1.txt             22

D:\BaskaKlasor\Dosya2.doc      1366

Bu şekilde Isim ve Boyut sütunları olan bir dizi oluştu. Şimdi NTFS dosya sisteminde dosyayı oluşturan kullanıcı olan "sahip" (owner) bilgisini de eklemek istiyorum.

Oluşturma aşamasında da sahip bilgisini ekleyebilirdik, ama örnek olması açısından bu nesneye oluşturduktan sonra ekleyelim.

> Add-Member -InputObject $dosyalar -MemberType ScriptProperty -Name Sahip -Value {(Get-Acl $this.Isim).Owner}

Bu işlem sonunda şu şekilde bir tablomuz olur:

Isim                          Boyut    Sahip

-----                         -----    -----

D:\Klasor\Dosya1.txt             22    metin

D:\BaskaKlasor\Dosya2.doc      1366    arda

12.08.2021

Powershell ile metin dosyalarını incelemek

Grafik arayüzlü metin editörleri varken ne gerek var Powershell'e. Ama çok büyük metin dosyaları ile çalışırken bütün dosyayı açıp incelemek sorun olabilir. Çok büyük derken örneğin 250 GB. Bu kadar dosyayı açıp belleğe almak bile ciddi bir iş.

Onun yerine Get-Content ve Select-String gibi cmdlet'ler kullanılabilir mi?

Öncelikle alias'ları da kullanarak dosyanın başından 20 satırını bir inceleyelim.

> cat -First 20 .\network.log

First, aslında TotalCount parametresinin alias'ı. Başka bir alias da Head.

Bir de sondan 20 satırı görelim

> cat -Last 20 .\network.log

Last da aslında Tail parametresinin bir alias'ı. TotalCount ve Tail yerine First ve Last kullanmak daha kolay hatırlanabilir gibi geliyor bana. Ya da linux terimleri olarak Head ve Tail.

Başlangıçtan örneğin 5 satır atlayarak geri kalanı oku gibi bir komutu cat ve select-object'in birleşimi ile sağlayabiliriz: 

> cat .\network.log | selet-object -skip 5

Buraya kadar gördüklerimizden dosyanın genel bütün satırlarının System.Net.Sockets ile başladığını gördük diyelim. Peki bununla başlamayanlar var mı? (bkz [1],[2])

> sls "^(?!System.Net.Sockets)" .\network.log

Ve diyelim ki dosyanın içinde bir IIS logu benzeri bir yapı bulduk. Virgülle ayrılmış değerler. İlk alan tarih, daha sonra saat, kaynak ve hedef IP adresleri, kaynak ve hedef portları gibi:

2021-08-10,09:38:22,192.168.7.3,8.8.8.8,1356,443

Bu satırlardan da sadece kaynak IP adresi ve kaynak hedef portu ile ilgileniyoruz. Bu alanları alıp ekrana basmak için kullanabileceğimiz ConvertFrom-String cmdlet'i var. -Delimiter parametresi ile verilen karakterlerle ayrılmış alanları ayrıştırıp P1'den P(N)'e kadar alan isimlerini otomatik yaratıp kullanımımıza sunuyor. Bizim örneğimizde kaynak IP ile hedef portu 4. ve 6. alanalar olduğu için bunları aşağıdaki gibi süzebiliriz:

> sls "^(?!System.Net.Sockets)" .\network.log | ConvertFrom-String | select P4,P6

P4        P6
-------   ---
8.8.8.8   443
1.1.1.1   443

gibi bir sonuç elde edilir.

 Dosyanın içinde 192.168 ile başlayan IP adresleri var mı diye bir bakalım:

> sls "192\.168\.\d{1,3}\.\d{1,3}" .\network.log

Ya da tarih (örneğin 2021-08-10 formatında) yazıyor mu hiç:

> sls "\d{4}-\d{2}-\d{2}" .\network.log

Belki de tarih formatımız 2021.08.10 veya 20210810 da olabilir:

> sls "\d{4}.?\d{2}.?\d{2}" .\network.log

Tüm bunları yaparken bütün dosyayı değil de ilk 1000 veya son 1000 satır üzerinden işlem yapmak da isteyebiliriz. Bunun için "|" operatörü ile cmdlet'leri bağlayabiliriz:

> cat -First 1000 .\network.log | sls "\d{4}.?\d{2}.?\d{2}"

---

[1] https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_regular_expressions?view=powershell-7.1

[2] https://www.tutorialspoint.com/powershell/powershell_regex.htm

6.08.2021

Çok sayıdaki olay kaydını tarihlerine göre gruplamak

Bir bilgisayarda sistem olay günlüğünde çok sayıda diskle ilgili 51 kimlikli kaydın olduğunu gördüm. Genel bir bakışla olayların birkaç günde yoğunlaştığını farkettim.

Get-WinEvent -computer BILGISAYAR -FilterHashTable @{LogName="System";ProviderName="disk";Id=51} | Measure-Object

ile 30.000+ olay kaydı olduğu saydım, ama bunlar her güne düzgün yayılmamıştı. Tarihe göre gruplayarak bir günlük sayıya ulaşmak istedim ama 

Get-WinEvent -computer BILGISAYAR -FilterHashTable @{LogName="System";ProviderName="disk";Id=51} | Group-Object -Property TimeCreated

gibi bir sorgu işe yaramaz, çünkü TimeCreated sadece tarih değil saat, dakika ve saniye bilgilerini de içerir. Dolayısıyla en iyi ihtimalle güne göre değil, saniyeye göre sayı verebilir ki bu da isteğimizin çok daha üstünde bir çözünürlük.

Bunun yerine yeni bir alan oluşturmalı ve o alanı TimeCreated alanının sadece tarih bilgileri ile donatmalıyız. Kısa bir arama ile daha önce hiç kullanmadığım bir yöntem kullanan şu Stackowerflow gönderisine ulaştım:

Get-EventLog -Logname system -Source "Microsoft-Windows-GroupPolicy" -EntryType "Information" |
    Add-Member Day -MemberType ScriptProperty -Value { $this.TimeGenerated.ToString('dd.MM.yyyy') } -PassThru |
    Group-Object 'Day', 'Source'

Aslında bu miktarda olayı kendi bilgisayarıma aktarıp da saymak yerine yerinde yapmak daha mantıklı olurdu. Bu sebeple Invoke-Command ile yukarıdaki bilgiyi de kullanarak

Invoke-Command -ComputerName BILGISAYAR -Command { Get-WinEvent -FilterHashTable @{LogName="System";ProviderName="disk";Id=51} | Add-Member Day -MemberType ScriptProperty -Value { $this.TimeCreated.ToString("dd.MM.yyyy"} -PassThru | Group-Object "Day", "Source" -NoElement

aşağıdaki sonuca ulaştım:

Count Name               PSComputerName
----- ----               --------------
 2542 19.04.2021         BILGISAYAR
 9044 26.01.2021         BILGISAYAR
19841 25.01.2021        
BILGISAYAR

Babadan kalma yöntemlerle yapmak isteseydik

Invoke-Command -computer BILGISAYAR -command { Get-WinEvent -FilterHashtable @{LogName="System";ProviderName="disk";Id=51} | Select Id,@{N="Day";E={$_.TimeCreated.ToString("dd.MM.yyyy")}} | Group-Object 'Day' -NoElement}

gibi bir sorgu oluşturabilirdik.