17.03.2023

Windows Terminal'de 2 farklı powershell profili

Windows Terminal'e birden fazla profil eklemek mümkün. Ama aynı powershell sürümü için birden fazla profil dosyası (powershell oturumu başladığında çalıştırılacak, tek profil durumunda Belgelerim\WindowsPowershell konumunda bulunan *.ps1 uzantılı dosya) nasıl yapılır sorusunun cevabı şu adreste var. Çözüm, için Windows Terminal'in settings.json dosyasını ($env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState konumunda) açıp, yeni yaratılan profilin içine 

"commandline": "powershell.exe -noprofile -noexit -command \"invoke-expression '. ''C:/PsProfile.ps1''' \"",

gibi bir satır eklemek. Burada yeni oturum çalıştırılıdığında C:\PsProfile.ps1 gibi bir dosyanın "dot sourcing" yöntemi ile çalıştırılması sağlanmış (noktaya dikkat!). Bu şekilde farklı prompt'lar, farklı diğer şeyler yaratılabilir. Bundan sonra Windows Terminal ile diğer görünüm ayarları yapılabilir. Örneğin Exhange Management Shell'e bağlanmak için PsProfile.ps1 içine

$cred = Get-Credential -Username "<domain\user>" -Message "Credential to connect to Exchange server"
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://<exchange_url>/PowerShell/ -Credential $cred -Authentication Kerberos
Import-PSSession $Session -Verbose

yazarak Exhange'e özgü bir tema oluşturabiliriz. Her profilin kendine özgü bazı alias'ları, değişkenleri profil dosyasına yazılabilir.

15.03.2023

Manjaro duvar kağıtları, Simon Stalenhag, Sven Dahlstrand, bash ve Powershell

Manjaro KDE'de duvar kağıtları konusunda değişik seçenekler var. Her açılışta zevkinize göre değişik duvar kağıtlarından birini sizin için ayarlayabiliyor.

 

Seçeneklerden biri çok hoşuma gitti. İsmine bakınca koleksiyonun adının Simon Stalenhag olduğunu gördüm. Aratınca bir sürü benzer duvar kağıdının sanal alemde bulunabildiğini farkettim. Hemen Windows'a uyarlamak geldi aklıma. Önce wallpaperflare.com sitesini buldum. Ama buradaki tam boyuttaki resimler maalesef base64 formatında. Bunları powershell ile kaydetmenin  bir yolunu bulamadım. Browser ile statik olarak yüklenmiyorlar, Invoke-WebRequest ile yaptığım işlemde sayfada yer almıyor, muhtemelen bir Javascript ile yerine konuyor. Daha fazla seçenek ararken benzer zevke sahip bir sanal alem bireyi Sven Dahlstrand'ın çalışmasına denk geldim:

ROOT_URL="http://simonstalenhag.se"
MAX_PROCESSES=30
curl -s "${ROOT_URL}" |
grep -Eio "bilderbig[^\"]+\.jpg" |
sort | uniq |
xargs -P "${MAX_PROCESSES}" -n 1 basename |
xargs -P "${MAX_PROCESSES}" -n 1 -I {} curl -z {} -f -O "${ROOT_URL}/bilderbig/{}"

Bunu anlamak bile uzun sürdü. Anladıklarımı özetleyeyim.

- İlk 2 satırda sanatçının çalışmalarının yer aldığı web sayfasının adresi ile xargs komutu ile aynı anda çalıştırılacak süreç sayısı değişkenlere atanmış.

- 3. satırda web sayfasının içeriği alınmış ve 4. satırda içinde bilderbig geçen ve uzantısı .jpg olan adresleri regular expressions ile ayırmış

- 5. satırda bunları sırayıp içinden tekrarlayan kayıtları silmiş

- basename, http://simonstalenhag.se/bilderbig/image.jpg gibi bir adresin içinden sadece image.jpg bilgisini ayırıyor.

- En son satır ise 30 paralel süreçte dosyalar indirilerek her biri aynı isimle mevcut klasöre kaydediliyor.

Bunu powershell ile yapmanın yollarından biri:

$root_url = "http://simonstalenhag.se"
$content = iwr $root_url
$content.Links.href | where {$_ -like "bilderbig*"} | gu | sort |
% { iwr -uri ($root_url+"/"+$_) -OutFile (Split-Path $_ -Leaf) } 

olabilir. Birebir aynısı değil. 30 paralel işlem yok. curl komutunun -f parametresi, dosyaların olup olmadığını da kontrol ediyor, ben etmedim. Invoke-WebRequest her seferinde üzerine yazar. Bu davranışı değiştirecek bir yol bulamadım ama -OutFile parametresini terk edip, çıkış Out-File cmdlet'ine yönlendirmek (pipe) gibi yöntemlere geçilebilir.

Paralel işlem yapabilmek için de Powershell Core kullanılabilir. Son satırda Foreach-Object'in alias'ı olarak kullanılan "%" sonrasında -Parallel ve hatta -ThrottleLimit eklenebilir:

% -Parallel -ThrottleLimit 30 { ... }

Sonrasında indirilen resimlerden birini duvar kağıdı olarak ayarlamak için daha önce bulduğum Set-Wallpaper fonksiynu kullanılabilir.

13.03.2023

Birkaç güzel site

Windows terminalde fazla zaman geçirenler için faydalı birkaç site. Eskiden sadece nerdfonts.com vardı. Şimdi programmingfonts.org var. Çeşitli fontları, çeşitli renk temasında, çeşitli programlama dilleri ile deneme imkanı sunan görünümler, sıfır çaba ile test edilebilyor.

Varsayılan Windows Terminal renk temaları çok iç açıcı olmayabilir. Bir renk teması ayarlamak da üstün çabalar gerektirebilir. Bu iki ucun ortasında konumlanan bir çalışma olarak windowsterminalthemes.dev güzel bir çalışma.

Beğendimiz temayı uygulamak için ekranın altındaki mavi "Get theme" butonuna basmak yetmiyor. Bu, sadece json formatındaki yapılandırmayı clipboard'a kopyalıyor. Daha sonra bunu Windows Terminal'e uygulamak için programın json dosyasını favori editörünüzde $env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json dosyasını açarak "schemes" bölümü altına aşağıdaki gibi kopyalanan içeriği yapıştırmak

ve daha sonra da Windows Terminal'in ayarlarından önce "Defaults"a gelip, oradan Appearance'a, daha sonra da Color Scheme'e gelip yeni eklediğimiz temanın adını seçmek gerek.

Nihayet, bir tane de bash promptunu özelleştirmek için bir site. Prompt'ta yer almasını istediğimiz öğeleri seçip, renkleri de ayarladıktan sonra altta çıkan export PS1="..." gibi satırı koplayıp ev klasörümdeki .bashrc dosyasının sonuna yapıştırmak yeterli. Preview bölümü de var.



12.03.2023

oh-my-posh yeni sürüm

Bu oh-my-posh'un derdi ne? Sürekli değişen bir yapı. Önce powershell gallery ile dağıtım, sonra winget ile, sonra Microsoft Store ile, sonra tekrar winget ile. Daha önce powershell terminali özelleştirmek ile ilgili yazıdğım yazıları güncellemektense yeni durumu yazmak istedim.

En son sürüm winget'te. Hatta winget'te de iki farklı sürüm var, bugün itibariyle.

PS> winget

Name       Id                        Version Match                Source

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

oh-my-posh XP8K0HKJFRXGCK            Unknown                      msstore

Oh My Posh JanDeDobbeleer.OhMyPosh   14.12.1 Moniker: oh-my-posh  winget

Sanıyorum en güncel sürüm, alttaki satırda görünen winget kaynağından gelen sürüm numarası 14.12.1 olan. Artık powershell modülü olarak gelmediğinden profil dosyasında Install-Module gibi bir işleme gerek yok. Artık $env:LOCALAPPDATA klasörünün altında kuruluyor ve ana bileşen oh-my-posh.exe adında bir exe dosyası. Bir kez oh-my-posh.exe2yi çalıştırdıktan sonra hala Get-PoshThemes komutunu çalıştırarak ya da ohmyposh.dev/docs/themes sitesinden hangi seçeneklerimiz var görebiliyoruz. Hatta Get-PoshThemes'i çalıştırdıktan sonra artık nasıl promptumuzu düzenlememiz gerektiği de belirtiliyor. Profil dosyamızı istediğimiz metin editörü ile (notepad, cods vs) ile açıp içine şöyle bir satır eklememiz yeterli:

oh-my-posh init pwsh --config "$env:POSH_THEMES_PATH/bubbles.omp.json" | Invoke-Expression

Bir sürü tema ve segment var. Segment'ler, prompt'ta gözüken bilgi bölümleri; bulunduğumuz klasör, saat, tarih gibi. Diyelim bir temayı seçtik ama küçük bazı değişiklikler yapmak istiyoruz. Bu durumda $env:POSH_THEMES_PATH/ klasöründeki *.omp.json dosyasının bir kopyasını alarak bu kopyayı düzenleyerek devam edebiliriz. Bu dosyaların yapızı şu şekilde:

{
    "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json",
    "blocks": [

        {
            "alignment": "left",
            "segments": [
                {
                ...
                } ,
                ...
             ],
             "type": "prompt"
        },
        {
            "alignment": "right",
            "segments": [
                {
                ...
                } ,
                ...
            ],
            "type": "prompt
        }
    ],
    "final_space": true,
    "version": 2
}
"blocks" bölümünün içine birden fazla virgülle ayrılmış ve küme parantezi içine alınmış alt block öğeleri gelebiliyor. Bu block öğelerini terminalin sağına veya soluna hizalayabiliyoruz. Her block öğesinin içinde birden fazla segment olabiliyor. Segmentler de virgülle ayrılmış ve küme parantezi içine alınmış olacak. Segment'lerin listesi ve kullanımı ile ilgili bütün ayrıntılar için yine ohmyposh.dev sitesinde segments bölüne bakabiliriz. Örneğin içinde bulunulan klasörü yazmak için path segmentine bakalım. Her segment'in olduğu gibi path'in de bir style verisi var. Sadece klasör ağacındaki son altklasörün adını yazmak için "folder", tam yolu yazmak için "full" kullanılabilir. Eğer seçtiğimiz temanın segmentlerini yeterli bulmadıysak veya konumları hoşumuza gitmediyse yerlerini bu kurallara göre değiştirebiliriz.

Her temanın segmentlerinin nasıl görüntüleneceğini belirleyen birkaç küçük özellik var; yazı rengi (foreground), fon rengi (background), öncü (leading_diamond) ve artçı (trailing_diamon) karakterler gibi. Öncü ve artçı karakterler köşeli veya yumuşak hatlara sahip açılış ve kapanış öğeleri olabilir.


28.02.2023

Powershell ile IIS kurulumu

Server Manager'ın dashboard'undan role (Web Server) olarak ekleniyor (kuruluyor).

Powershell ile de bir feature olarak kuruluyor.

Önce ne tür feature'larımız var, bakalım:

PS> Get-WindowsFeature -Name *

Bu komudun sonucunda Server Manager'daki ağaç yapısına benzer bir yapı görüntülenir. Hedefimiz Web Server (IIS) olduğundan Web ile başlayan feature'ları görelim:

PS> Get-WindowsFeature -Name Web*

Ve nihayet Web-Server'ı kuralım:

PS> Install-WindowsFeature -Name Web-Server -IncludeManagementTools

Grafik arayüz ile kurulumda da varsayılan olarak yönetim araçları kuruluma dahil edilir. Bunun için IncludeManagementTools parametresini kullandım.


---

https://www.rootusers.com/how-to-install-iis-in-windows-server-2019/

27.02.2023

Timespan'i tarih ve saate çevirmek

Bazı yerlerde tarih ve saat verisi yerine bir "timespan" veri türünün kullanıldığını görüyorum. Bu timespan veritipi, 1 Ocak 1970 00:00:00'ı milat kabul ederek bu tarih, saat, dakika ve saniyeden sonra geçen saniyeleri bize söyler. Örneğin 1659590505 gibi bir veri, bu dijital milat tarihinden sonra 1,659,590,505 saniye geçtiğini gösterir. Bunu mantıklı bir tarih saat verisine çevirmek için

PS> (Get-Date 01.01.1970).AddSeconds(1659590505)

4 Ağustos 2022 Perşembe 05:21:45

kullanabiliriz. Burada 01.01.1970'in de, dönülen tarih ve saatin de UTC (merkezi saat) olduğu varsayılır. Yani üzerine bulunulan saat dilimini (Türkiye için +03:00) eklemek gerekir. Tam tersi için

PS> [int][double]::Parse((Get-Date (get-date).touniversaltime() -UFormat %s))

kullanılması önerilmiş, epochconverter.com sitesinde. Python'da ise

>>> import time

>>> int(time.time())

kullanılmış. Timestamp diye kısalttığım veri tipi aslında epoch timetamp ya da Unix timestamp olarak adlandırılıyor. .Net framework'te 8 byte olarak yer kaplayan ve 1.1.0000 tarihinden 31.12.9999 tarihine kadar olan aralıkta saniye hassasiyetinde çözünürlüğe sahip bir veri tipi. Ancak Unix, 4 byte hassasiyete sahip bu veri tipi ile hem yarısı kadar hafıza kullanımı hem de Unix/Linux dünyasında bir standardizasyon sunuyor. 32 bitlik tamsayı kullandığımızı varsayarak timestamp kullanarak 01.01.1970 00:00:00 ile 07.02.2106 06:28:16 zaman aralığında saniye hassasiyetinde çözünürlük mümkün. Bazı durumlarda signed int kullanılmışsa son tarih 19.01.2038 03:14:08 olabilir. Önümüzde kabaca bi 15 sene var, yeni bir Y2K38 dijiatal kıyameti için.

Güncellemelerden sonra yeniden başlatmaya gerek var mı?

Halen kullanımda olan (çalışan) uygulamanın güncellenebilmesi için sonlandırılıp tekrar çalıştırılması gerekir. Hareket halindeki arabanın lastiklerinin değiştirilememesi gibi birşey. Çalışan uygulamanın karmaşık bir bağımlılık ağı varsa bu durum sistemin kapatıp açılmasını gerektirebilir. Bu elbette sadece yeni özelliklere sahip güncellemenin devreye alınabilmesi için gereklidir. Güncellenmemiş eski sürümle çalışmaya devam etmek için yeniden başlatmamak söz konusu, ama bazen yeni sürüm güvenlik açıklarını da kapatmışsa en kısa sürede yeniden başlatmak gerekebilir.

Windows'da güncellemelerden sonra sistem tepsisinde çıkan bir simge yeniden başlatma gerektiren durumlardan bizi haberdar edebilir. Ancak bunu bir script içinden belirlemek ya da uzak bilgisayardaki durumu görebilmek için şu yöntem kullanılabilir:

function Test-PendingReboot
{
    if (Get-ChildItem "HKLM:\Software\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" -EA Ignore)
    { return $true }
    if (Get-Item "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired" -EA Ignore)
    { return $true }
    if (Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -EA Ignore)
    { return $true }

    try {
        $util = [wmiclass]"\\.\root\ccm\clientsdk:CCM_ClientUtilities"
        $status = $util.DetermineIfRebootPending()

        if(($status -ne $null) -and $status.RebootPending){
            return $true
        }
    }
    catch{}

    return $false
}

Linux'ta güncellemeler sonrasında sistemi yeniden başlatmaya gerek olup olmadığını gösteren bir belirteç yok. Fedora için dnf-utils (veya yum-utils) paketi ile gelen needs-restarting kullanılması öneriliyor:

$ needs-restarting -r

Sistem başlangıcından bu yana hiçbir çekirdek kütüphane veya hizmet güncellenmedi.
Yeniden başlatmaya gerek yok.

ya da

$ needs-restarting -r

Sistem başlangıcından bu yana güncellenen çekirdek kütüphaneler veya hizmetler:
  * glibc
  * kernel
  * linux-firmware

Bu güncellemeleri tam olarak kullanmak için yeniden başlatma gereklidir.
Daha fazla bilgi için: https://access.redhat.com/solutions/27943

gibi bir sonuç dönebilir. Ubuntu'da ise /var/run/reboot-required dosyasının içeriğini kontrol etmek önerilmiş. Aşağıdaki gibi dosyanın bulunmaması yeniden başlatmaya gerek olmadığını gösterir.

$ cat /var/run/reboot-required

cat: /var/run/reboot-required: No such file or directory

ya da aşağıdaki gibi bir sonuç yeniden başlatmanın gerekli olduğunu gösterir:

$ cat /var/run/reboot-required

*** System restart required ***

Ubuntu'dayken needs-restarting, Fedora'dayken ise reboot-required'ı yazmamak için benim bulduğum yöntem bir blog oluşturup her çalıştırma öncesinde blog'daki ilgili girişe bakmak.

---

[1] https://www.cyberciti.biz/faq/how-to-check-if-centos-rhel-needs-a-full-reboot/

[2] https://www.funoracleapps.com/2021/04/how-to-find-if-linux-os-needs-reboot.html

6.02.2023

Powershell'de string'i sabit uzunluklu parçalara bölme

String verilerini farklı şekillerde dizi (array) veri tipine dönüştürmek mümkün. Örneğin her boşluk karakterini ayraç olarak kullanarak aşağıdaki $sehirler değişkeninden bir şehirler dizisi üretmek mümkün.

PS> $sehirler = "Bursa İstanbul Ankara İzmir"

PS> $sehirler -split " "
Bursa
İstanbul
Ankara
İzmir

herhangi bir ayraç karakterini kullanarak bölümleme yapılabilir. ConvertFrom-CSV ile virgülle ayrılmış değerlerden bir tablo yapmak, ya da ConvertFrom-String ile CSV'nin daha genel hali olarak herhangi bir ayraç ile ayrılmış string'den parçalar oluşturmak mümkün

PS> $sehirler | ConvertFrom-String

P1    P2       P3     P4
--    --       --     --
Bursa İstanbul Ankara İzmir

Ama sabit genişlikli bölümleme nasıl yapılır, uygun bir yöntem bulamamıştım. Şu adreste verilen yöntem amaca yönelik güzel bir yol ve üstelik -split operatörünün daha regexp kullanımına uygun olduğunu da hatırlatmak açısından faydalıydı:

PS> $sehirler -split '(.{3})' | ?{$_}

Bur
sa
İst
anb
ul
Ank
ara
İz
mir

En sondaki ?{$_} bölümü her 3 karakterlik satırdan sonra 1 boş satır ekleMEmesi için.

Bu örnek üzerinden çok anlamlı değil belki ama genel olarak sabit genişlikli string dizileri oluşturmak için kullanışlı.

2.02.2023

Powershell betiklerini elektronik olarak imzalamak

Müthiş bir koruma yöntemi olmasa da powershell'de varsayılan olarak betiklerin çalıştırılmalarını engelleyen bir politika var. Yani yeni bir Windows bilgisayar aldınız/kurdunuz. Varsayılan olarak powershell terminalinden sadece komutlar girebilirsiniz. ps1 uzantılı betik dosyalarını çalıştırmanız engellenir. Eğer bu engeli kaldırmak isterseniz Set-ExecutionPolicy ile çalıştırma politikanızı değiştirmelisiniz. Şu çalıştırma seviyeleri vardır:

AllSigned: Tüm betikler imzalı olsun.

ByPass: Yasakları takma

RemoteSigned: Sadece download edilmiş betiklerin imzalı olmalarını bekle. Yerel oluşturulmuş betiklerin imzalı olmasını bekleme (bir dosyanın nasıl download edilmiş olduğunu anlarız? ZoneIdentifier ile).

Restricted: Tüm betikleri engelle.

Default: Varsayılan olarak iş istasyonları için restircted, sunucular için remotesigned.

Undefined: Politika tanımlı değil, default uygulanır. Niye ayrıca buna ihtiyaç duyulmuş, bilmiyorum.

Unrestircted: Patron çıldırdı, herşey serbest!

Amacımız Execution Policy'yi AllSigned yapabilmek için tüm scriptleri imzalamak için gereken adımları özetlemek. Önce bir sertifikaya ihtiyacımız var. Sertifka edinmenin pek çok yolu var. Bir sertifika hizmeti veren firmadan para ile almak bir yöntem. Yerel ağımızda bize bu hizmeti verecek bir sunucu (CA, certificate authority) varsa oradan da edinebiliriz. Bunların hiçbiri yoksa, sadece deneme amaçlı kullanılması önerilen "kendi kendine imzalanmış" bir sertifika da oluşturabiliriz. Sonuçta Windows Sertifika Deposu'nda bulunan ve kod imzalamak amacıyla üretilmiş (bu zorunlu) bir sertifikamız olsun. Buna bir PSDrive olan cert: sürücüsünden şu şekilde erişip bir değişkene atayabilriz:

PS> $sertifika = (Get-ChildItem cert:\CurrentUser\My -CodeSigningCert)[0]

Bu konumda birden fazla varsa ilkini seçer. Eğer illa "kendi kendine imzalanmış" sertifikamız olsun diyorsak

PS> New-SelfSignedCertificate -Subject "CodeSigning" -CertStoreLocation cert:\CurrentUser\My -Type CodeSigningCert -NotAfter (Get-Date).AddYears(5) -Keylength 4096 -Friendlyname PowershellSignature

Burada gözüken adımız "CodeSigning" olacak, sertifika cert:\CurrentUser\My (yani geçerli kullanıcının kişisel sertifikalar bölümünde) saklanacak, sertifikanın tipi CodeSigninCert olacak, 5 yıl geçerliliğe sahip olacak, anahtar uzunluğu 4096 bit olacak ve ismi PowershellSignature olacak. Eğer bunu kullancaksak ve geçerli kullanıcının kişisel sertifikalarında başka sertifika yoksa bunu bir değişkene atamak için yapacağımız şu olacaktır:

PS> $sertifika = Get-ChildItem cert:\CurrentUser\My -CodeSigningCert

Sonra Set-AuthenticodeSignature cmdlet'ini kullanarak ps1 uzantılı dosyamızı imzalayalım:

PS>  Set-AuthenticodeSignature -Filepath C:\scripts\sample.ps1 -Certificate $sertifika

İmzalanan powershell dosyasının sonuna yorum için kullanılan "#" karakteri ile başlayan 47 satırlık bir hash bölümü eklenecek. Bu hash, dosyamızın imzası olacaktır. İmzalandıktan sonra dosyada bir değişiklik olursa dosyayı tekrar imzalamamız gerekecektir.

Set-AuthenticodeSignature'ı çalıştırdıktan sonra Status kısmında UnknownError görürsek bu, işlemi yapmak için yükseltilmiş ayrıcalıklara ihtiyacımız olduğunu gösterir. Muhtemelen normal kullanıcı haklarımızla CurrentUser değil de LocalMachine altından bir sertifika okumuşuzdur. Normal yetkilerimizle işlem yapmak için sertifikayı CurrentUser altına koymak daha pratik bir yol.

Normal şartlar altında bir sertifikaya güvenebilmek için o sertifikayı imzalayan (bize veren) kurumun kök sertifikasına da güvenmemiz gerek. Yani o da bizim sertifika depomuzda olmalı. "kendinden imzalı" sertifikalar için böyle birşey olmadığından sertifika zincirlerinde bir güvensizlik sarı bir ünlem veya kırmızı çarpı ile kendini belli edebilir. Bu durumun kolay çözümü "kendinden imzalı" sertifikamızın bir koyasını da "Güvenilir Kök Sertifika Yetkilileri" (Trusted Root Certification Authorities) koymaktır.

Son olarak execution policy'mizi AllSigned yapabiliriz, ama bunu yapmak için yine yükseltilmiş ayrıcalıklara (yönetici yetkilerine) ihtiyacımız olacak:

PS> Set-ExecutionPolicy AllSigned

Betiği her değiştirdiğimizde bu sürece tekrar katlanmamak için bu işi bir de yapacak bir betik yazabiliriz (hata denetimini atladım). İmzalanacak dosyayı -filename parametresi ile belirtmek gerek. Bu dosyayı oluşturduktan sonra çalıştırabilmek için yukarıdaki satırları bir kez daha kullanarak bunu da imzalamamız gerek.

function imzala {
    param(
        [string]$filename 
    )

    $s = Get-ChildItem cert:\CurrentUser\My -CodeSigningCert
    Set-AuthenticodeSignature -Filepath $filename -Certificate $s
}

1.02.2023

Powershell ile bilgisayarlar arasında dosya aktarımı

Eğer bir etki alanına üye iki bilgisayar söz konusuysa

PS> Copy-Item \\pc1\paylasim\dosya1 -Destination \\pc2\ortak

şeklinde dosya aktarımı yapılabilir. ama daha karmaşık bir durum olduğunu varsayalım. Bu iki bilgisayar da etki alanının üyesi olmasın. İki bilgisayar için de bir powershell uzak oturumu başlatmış olduğumuzu varsayalım

PS> $oturum1 = New-PSSession -Computername pc1 -Credential (Get-Credential "pc1\user1")

PS> $oturum2 = New-PSSession -Computername pc2 -Credential (Get-Credential "pc2\user2")

Maalesef, iki oturum arasında bir seferde dosya aktarabilmek için şöyle bir şansımız yok:

PS> Copy-Item C:\klasor1\dosya1.txt -Destination C:\klasor2 -FromSession $oturum1 -ToSession $oturum2

Bunu yapmayı denersek powershell bize FromSession ve ToSession parametrelerinin aynı anda kullanılamayacağını söyler. Bunun yerine kopyalamak istediğimiz dosyayı önce kendi bilgisayarımıza aktarabilir, daha sonra pc2'ye aktarabiliriz:

PS> Copy-Item C:\klasor1\dosya1.txt -Destination D:\depo -FromSession $oturum1

PS> Copy-Item D:\depo\dosya1.txt -Destination C:\klasor2 -ToSession $oturum2

Ya da pc1'deki oturuma girip, $oturum2 nesnesini bu bilgisayarda oluşturup, hedef dosyayı doğrudan pc2'ye aktarabiliriz. Burada sorun pc1'deki oturumda get-credential gibi bir cmdlet'i çağırmak olurdu. Bu durumda da iki seçeneğimiz var. Birincisi credential nesnesini kendi makinemizde oluşturup, bunu $using ile pc1'e göndermek:

PS> $cred2 = Get-Credential "PC2\user2"

PS> Enter-PSSession -Session $oturum1

[pc1] PS> $oturum_pc2 = New-PSSession -Computer pc2 -Credential $using:cred2

[pc1] PS> Copy-Item C:\klasor1\dosya1.txt -Destination C:\klasor2 -ToSession $oturum_pc2

İkinci seçeneğimizde ise credentials nesnesi yerine pc1 üzerindeki oturumda şifreyi açıkça yazmak:

PS> Enter-PSSession -Session $oturum1

[pc1] PS> $kullanici = "PC2\user2"

[pc1] PS> $parola = ConvertTo-SecureString -String "parolamiz" -AsPlainText -Force

[pc1] PS> $cred2 = New-Object -TypeName System.Management.Automation.PSCredential -Argumentlist $kullanici, $parola

[pc1] PS> $oturum_pc2 = New-PSSession -Computername pc2 -Credential $cred2

[pc1] PS> Copy-Item C:\klasor1\dosya1.txt -Destination C:\klasor2 -ToSession $oturum_pc2

En az güvenli olan yöntem, sanıyorum en sondaki oldu, çünkü parolayı açık açık girmek zorunda kaldık.

31.01.2023

Etki alanında olmayan makinelere powershell ile uzaktan bağlanmak

Kısacası PSRemoting. Uzun konu. Ama pratik olarak etki alanında (domain) olmayan makinelerin birbirlerine nasıl uzaktan bağlanacaklarına hızlıca bakalım.

İki makine de workgroup modunda. Bağlantıyı başlatarak karşı tarafta bağlanacak makineye "kaynak", kendisine bağlanılmasını bekleyecek makineye de "hedef" diyelim.

Bu ikisinin de güvenlik duvarları açık olabilir. Kaynak makinenin dışarı çıkışının engellenmediğini varsayarak bu tarafta yapılacak birşey yok. Ama hedef makine dinleme modunda olduğundan bu makinede uygun portlar açacak ayarların yapılması gerekecek. Ayrıca hedef makinede bu bağlantıyı bekleyecek (dinleyecek) bir yapı da olmalı. Bu da WinRM hizmeti. Varsayılan olarak yeni sistemlerde bu kapalı geliyor. Bunun da açılması gerekecek. Birkaç küçük ayrıntı daha var. Şükür ki bunları otomatik yapan bir cmdlet var. Hedef makinede sadece şunu yazmak yeterli:

PS> Enable-PSRemoting

Eğer bunu yazınca ağ profilimiz ile ilgili şikayet ediyorsa Public profildeyiz demektir. İki seçeneğimiz var; ya -SkipNetworkProfileCheck parametresini kullanarak tekrar deneyebiliriz ya da ağ profilimizi değiştirmeliyiz. Bence ikincisi daya iyi bir seçenek. Değiştirmek için

PS> Set-NetConnectionProfile -NetworkCategory Private

Hedef makine ile işimiz bitti. Şimdi sıra kaynak makinede. Powershell ile bağlanırken kullanıcı adı ve şifre bilgileri karşı makineye gönderileceğinden karşıdaki makinenin gerçekten güvenilir olup olmadığından emin olmamız istenir. Bu sebeple rastgele makinelere bağlanmayı denerken kullanıcı adı ve şifre bilgilerini göndermememiz için güvenilir bilgisayarlar listesi oluşturulmuş. Sadece bu listedeki bilgisayarlara bağlanabiliriz. Bu liste de WSMan sürücüsünde localhost\Client\TrustedHosts gibi bir adrestedir. Mevcut içeriğini görüntülemek için

PS> Get-Item WSMan:\localhost\Client\TrustedHosts

kullanabiliriz. Hedef makinemizin adının da "hedef" olduğunu varsayarsak bu dosyaya bu bilgisayarın ismini girmemiz gerek. Şöyle bir komut kullanabiliriz:

PS> Set-Item WSMan:\localhost\Client\TrustedHosts -Value "hedef"

Bu komut TrustedHosts'un içeriğini değiştirir. Birden fazla ekleme yapmak için -Concatenate parametresi kullanılabilir. Bir de "sunucu" adında bir makine daha ekleyelim:

PS> Set-Item WSMan:\localhost\Client\TrustedHosts -Value "hedef" -Concatenate

Bu aşamada artık bağlanabiliriz:

PS> Enter-PSSession -Computername hedef -Credential (Get-Credential "hedef\kullanici")

Burada sorun varsa belki hedef makinedeki güvenlik duvarını tekrar kontrol etmek isteyebiliriz.

PS> Get-NetFirewallRule "WINRM-HTTP-In-TCP*"

Bu komut sonucunda 2 kural dönecektir. İkisi de 5985/TCP portunu etkinleştiren kuraldır ama özelliklerini aşağıaki gibi kıyaslayayım:

Kural ismi
Ağ profiliKapsamPort/ProtokolPrivate Profilde
WINRM-HTTP-In-TCPPublic
Yerel alt ağ
5985/TCPDevre dışı
WINRM-HTTP-In-TCP-NoScopeDomain ve Private
-yok-5985/TCPEtkin

Bu tabloda vurgulamak istediğim, isminde NoScope geçen Domain ve Private profilleri için oluşturulmuş bir kural. Bizim profilimiz de Private olduğu için burada etkinleştirilmiş duruma olmalı. Diğer kuralda ise kapsam (scope) belirtilmiştir, sadece yerel alt ağ. Eğer olur da public profili etkinleştirirsek bizim kontrol etmemiz gereken kural bu olacak.

Buna alternatif olarak hedef makine SSH sunucusu varsa ssh ile de bağlanmak mümkün. O yazımda yazmadığım şekilde Windows makineye ssh yüklemek için (bugün için beta) artık winget de kullanabiliriz:

PS> winget install "OpenSSH Beta"

4.01.2023

Komut satırından sıkıştırma ve açma

 Eskiden sadece tar vardı. Bir klasörü tar ile sıkıştırmak için

$ tar -czvf compressed.tar.gz /home/user/dir1

Burada

    -c : Create (oluştur)

    -z :  zip (sıkıştır)

    -v : verbose (dosya ayrıntılarını görüntüle)

    -f : Filename (sıkıştırılmış dosya adını belirt)

Birden fazla klasör boşluk bırakarak ard arda yazılabilir.

Hariç tutulmak istenen klasör veya dosyalar (* wildcard karakteri kullanılarak) --exclude parametresiyle verilebilir.

$ tar -czvf compressed.tar.gz /home/usr/dir1 --exclude=/home/user/dosya1.txt --exclude=*.mp4 --exclude /home/user/Download

Bu örneklerdeki -z parametresi gzip sıkıştırma yöntemini kullanıyordu. Yeni bir sıkıştırma yöntemi olan bzip2'yi kullanmak (daha fazla sıkıştırma oranı için) -z'yi -j ile değiştirmek gerek. Örneğin

$ tar -cjvf compressed.tar.bzip2 /home/user/dir1

Sıkıştırılmış dosyayı açmak (ayıklamak/çıkarmak) için ise -c anahtarını -x (eXtract) ile değiştirmek gerek:

$ tar -xzvf compressed.tar.gz

Bu komut mevcut klasörün içine çıkartır. Başka bir klasöre çıkartmak için -C (büyük C) anahtarı kullanılır:

$ tar -xzvf compressed.tar.gz -C /tmp

tar komutundan başka bir de zip komutu var. Bu komut ile tek bir dosyayı sıkıştırma yapmak için

$ zip compressed.zip /home/user/file1.txt

bir klasörü, altındaki tüm içerik ile birlikte sıkıştırmak için ise

$zip -r -q -9 compressed.zip /home/user/Documents/

kullanılabilir. Burada

    -r : Recurse (alt klasörleri de dahil et)

    -q : Quiet (sıkıştırılan her içeriği yazma)

    -9 : En yüksek sıkıştırma oranını kullan (-0 en düşük, -9 en yüksek sıkıştırma oranı)

Sıkıştırılan dosyaya bir parola vermek için -e anahtarı kullanılabilir. Bu anahtardan sonra parola sorulur.

zip ile sıkıştırılmış dosyaları açmak için ise unzip komutunu kullanmak gerek:

    $ unzip compressed.zip

Burada kullanılabilecek bazı anahtarlar şöyle:

    -q : Quiet (ayrıntıları yazma)

    -d <klasör> : çıkarılacak konumu belirt

    -P <parola> : Büyük 'P' harfi ile parola korumalı dosya için parola belirt (DİKKAT: Komut geçmişinde görüntülenecek!)

    -x <*>    : eXlude (hariç tut)

    -l : Çıkarmadan sadece zip dosyasının içeriğindeki dosyaları görüntüle

Bazı bonus komutlar şöyle:

$ zipcloak compressed.zip    # varolan bir dosyayı parola korumalı yap

$ zipdetail compressed.zip  # sıkıştırılmış dosya ayrıntılarını görüntüle

$ zipinfo compressed.zip  # sıkıştırılmış dosya ayrıntılarını görüntüle

$ zipgrep kelime compressed.zip # sıkışmış dosyaların içeriğinde anahtar kelime araması yap

$ zipsplit -n 1024000 compressed.zip # sıkıştırılmış dosyayı 102400 byte (1 MB) bölümlere böl

Son zamanlarda daha da fazla sıkıştırma oranı veren 7zip de kullanılabilir. Windows için yüklenen 7-zip paketinde de 7z komutu aynı işlemleri yapabilir. Bir klasörü 7zip ile sıkıştırmak için:

$ 7z a compressed.7z /home/user/log_file

çıkartmak için de

$ 7z e compressed.7z -o /home/user/

Tabi Windows'da powershell ile gelen Compress-Archive ve Expand-Archive komutları da kullanılabilir:

PS> Compress-Archive -Path log_file -DestinationPath compressed.zip

Açmak için de

PS> Expand-Archive -Path D:\archive\compressed.zip -DestinationPath C:\Users\fethi\

kullanılabilir.