Yeni bir makale ile sizlerle Windows Server 2012 R2 yeteneklerini paylaşmaya devam ediyoruz. Bu makalemizde de sizlerle Active Directory Domain ortamlarında PowerShell script kullanımı ile merkezden istenilen sunucular üzerinde kullanılan servis hesabı envanteri çıkarmayı adım adım inceliyoruz.
Domain ortamında çalışan sunucular üzerinde kontrolden çıkmış ve özellikle düzenli bir dökümantasyonu bulunmayan servis hesaplarının şifrelerini güvenlik ya da denetimde çıkan bulguyu ortadan kaldırmak için değiştirmek istediğinizde sizi engelleyen ya da aksiyon almanızı engelleyen en önemli kısıt ilgili servis hesabının nerelerde kullanıldığını bilmemenizdir. Sağlıklı ve güncel bir dökümantasyonda servis hesaplarının hangi sunucularda ve hangi uygulamalarda kullanıldığını bilmiyorsanız sizi zaman alıcı ciddi bir operasyon süreci bekliyor demektir. Tek tek ortamdaki tüm sunuculara bağlanacak ve servis listesinden ilgili servis hesabının nerelerde kullanıldığını keşfetmeniz gerekecektir. Ya da bu çıktıyı sağlayan ücretli uygulamalar tedarik etmeniz gerekecektir. İşte bu makalemizde size bu ihtiyacınızı adım adım PowerShell ile hazırlayacağımız bir script ile nasıl çıkartacağımızı anlatıyor olacağım.
Script yazımı için en rahat kullanabileceğiniz araç Windows işletim sistemi üzerinde gelen PowerShell ISE (Integrated Scripting Environment) aracıdır. Bu aracı kullanarak hem çoklu script yazımında ya da geliştirilmesinde rahat çalışabileceğiniz bir arabirime sahip olacaksınız, hem de scriptlerin çıktılarını da test amaçlı çalıştırararak görüntüleyebileceksiniz.
İlk olarak script dosyamızın başına yapacağımız çalışma ya da script dosyasının içeriğini tanımlayan birkaç satırdan oluşan açıklama ibaresini yerleştiriyorum:
<######################################################################
# Author : MESUT ALADAG
# Web : www.mesutaladag.com
# Date : 17th July 2014
# Version : 1.0
# Description : Bu PwShell script ile merkezden sunucular üzerindeki servis envanterini çekiyor olacağız.
######################################################################>
#Parametreler istege gore degistirilebilir.
PowerShell script programlama da açıklama satırı “#” işareti ile sağlanır. Dolayısıyla # gördükten sonraki ifadeler işletilmez.
Şimdi de active directory domain içerisinden Windows Server işletim sisteminde çalışan sunucu listesini çekiyoruz. Bunun için aşağıda gördüğünüz gibi Get-ADComputer komutunu OperatingSystem filtreleme parametresi ile alıyorum ve bu listeyi de x2k isimli bir değişkende saklıyorum. PowerShell’de değişken isimleri önünde $ karakterinin geldiğini de burada yeri gelmişken ifade edelim. Yine filtrelemeyi de –Filter parametresi ile belirtip, filtreleme kriterini de süslü parantez ya da küme parantezi içerisinde tanımlıyoruz. PowerShell script programlamada karşılaştırma operatörleri olan eşittir, büyüktür, küçüktür, istenilen bir ifade ile biten, başlayan vb. deyimlerin kullanımı alışık olduğumuz C#, VB.net ya da T-SQL gibi diğer dillerden farklılık içermektedir.
Get-ADComputer -Filter { OperatingSystem -Like ‘Windows Server*’ }
Karşılaştırma operatörlerinin PwShell içerisinde kullanımı özellik yani property (örneğin operatingsystem) adından sonra aşağıdaki kısaltmalardan uygun olanla kullanılır:
-eq : Eşittir (Equal)
-neq : Eşit değil (Not Equal)
-lt : Küçüktür (Less Than)
-gt : Büyüktür (Greater Than)
-ge : Büyük eşittir (Greater Than or equal)
-le : Küçük eşittir (Less Than or equal)
-like : olumlu anlamda çok sayıda karakteri ifade eder. DOS komut satırındaki “*” karşılığıdır. Örneğin yukarıda Windows Server ile başlayıp sonrası ne olursa olsun demiş olduk.
-notlike : olumsuz anlamda çok sayıda karakteri ifade eder. Belirtilen metin dışındaki karşılayan ifadeleri içerir.
-match : Olumlu anlamda regular expression ifadeleri için kullanılır.
-notmatch : Olumsuz anlamda regular expression ifadeleri için kullanılır.
-and : Ve koşulu için kullanılır. Sağında ve solunda belirtilen tüm koşulları sağlamalıdır.
-or : Veya koşulu için kullanılır. Sağında ve solunda belirtilen koşullardan bir tanesini sağlaması yeterlidir.
-not : Değil ifadesinin görevini sağlar. Kendisinden sonra belirtilen değerin dışındakiler anlamındadır. Bunun yerine “!” işareti de kullanılabilir.
Bunların dışında da ihtiyaca göre farklı karşılaştırma operatörleri bulunmaktadır. Ben uygulamamız içerisinde kullanabileceğimiz ve en sık kullanılanları genel olarak açıklamış oldum.
Yukarıdaki örnekte OperatingSystem -Like ‘Windows Server*’ ifadesi ile işletim sistemi adı Windows Server ile başlayanlar şartını koymuş olduk. Sonu Windows Server 2012 R2, Windows Server 2012, Windows Server 2003 vb. değerleri içeren tüm sunucu listesini getirmiş olacaktır.
Burada filtreleme kümesi içerisinde kriter olarak işletim sistemi bilgisini verdiğimiz için OperatingSystem özelliğini verdik. Diğer özelliklerin listesini “-filter *” şeklinde aşağıdaki gibi kullanarak elde edebilirsiniz.
Bu şekilde kullanımda bile tüm özellikler (property) gelmemektedir, en basitinden bizim sorguladığımız operatingsystem özelliğini göremiyoruz. Tüm özelliklerin listesini almak için komutun sonuna bir de “-properties*” parametresini de eklemek gerekir.
Bu komutla active directory içerisinde bilgisayar hesapları listesini tüm özellikleriyle çekmiş olduk.
Biz işletim sistemi özelliği Windows Server ile başlayanların listesini almak istediğimiz için filtre kısmında sadece operatingsystem özelliğinde Windows Server ile başlayanları listelemiştir.
Bu hatırlatmalardan sonra tekrar aşağdaki koda dönecek olursak bize Windows Server ile başlayan sunucu listesini getirecektir. Ve dönen sonuç setinde de sunucuların standart olarak gelen özellikleri listelenecektir.
Get-ADComputer -Filter { OperatingSystem -Like ‘Windows Server*’ }
Bizim ihtiyacımız ise sunucunun işletim sistemi bilgisidir. Fakat standart çıktı listesinde operatingsystem gelmemektedir. İşletim sistemi bilgisini almak için de aşağıdaki şekilde sorgunun sonuna işletim sistemi özelliğini de getir diyoruz:
Get-ADComputer -Filter { OperatingSystem -Like ‘Windows Server*’ } -Properties OperatingSystem
Bu son hali ile tekrar çalıştırdığımızda standart alanlarla beraber işletim sistemi bilgisini de getirmiş oldu. Buraya kadar aslında tamamen kriter ya da filtreleme için efor sarfettik ve sonuçta şu anda bize Windows Server işletim sisteminde çalışan suncucu listesi geldi. Fakat bize dönen sonuç setinde ihtiyacımız olan belirttiğimiz kritere uyan bilgisayar hesaplarının sadece bilgisayar adı bilgisi, diğer görüntülenen alanlara ihtiyacımız yok. Çünkü bilgisayar isimlerinin listesini alıp onlar üzerinde çalışacağız. Çıktıda sadece bilgisayar alanının görülmesi için gelen çıktıyı pipe işareti ile Select deyimine aktarıp, burdan da sadece Name bilgisini görüntüle diyoruz:
Get-ADComputer -Filter { OperatingSystem -Like ‘Windows Server*’ } -Properties OperatingSystem |Select Name
Pipe karakteri PowerShell’de kendisinden önceki komutun çıktısını değişmeden doğru bir biçimde kendisinden sonraki komuta taşıma görevini icra eder. Dolayısıyla bizim örneğimizde birinci komutun yapacağı iş Windows Server ile başlayan bilgisayar listesini çekmek, ikinci komut da bu gelen listeden sadece bilgisayar isimlerini listelemektir. İşte birinci komutun çıktısını ikinci komuta girdi olarak verilebilmesini sağlayan ara karakterimiz pipe simgesidir. PowerShell script programlama da çok yaygın olarak kullanıyoruz.
Son durumda elde ettiğimiz çıktı aşağıdaki şekildedir:
Bu gelen çıktıyı bir dosyaya yazdırmak istersek de yine bir pipe ile isim listesini çıktı olarak alıp dosyaya yazacak komut olan Out-File komutuna girdi olarak aşağıdaki gibi aktarıyoruz.
Get-ADComputer -Filter { OperatingSystem -Like ‘Windows Server*’ } -Properties OperatingSystem |Select Name | Out-File “C:\Servers.txt”
Out-File ile çıktıyı dosyaya göndermemizden dolayı ekranda herhangi bir kayıt listelenmedi. Dosyamızı açtığımızda gelen görüntü de aşağıdaki şekildedir.
Bu çıktıda bizi memnun etmeyen dosyanın ilk birkaç satırındaki boşluk ve özellik adı olan Name’in de gelmesidir. Bizim için önemli olan sunucu isimleri olduğu için biz özellik isminin de gelmeden sadece sırayla tüm sunucu isimlerinin bu dosyaya yazılmasını istiyoruz. Bu amaçla da Select komutunu verdiğimiz kısmı “Select -ExpandProperty Name” şeklinde güncelleyiyoruz. Buradaki fark –ExpandProperty deyimidir. Böylece yeni elde ettiğimiz çıktı aşağıdaki şekilde olacaktır:
An itibariyle aslında istediğimiz listeyi almış ve bir metin dosyasına çıktı olarak yazdırmış olduk. Şimdi bu listenin tamamına servis hesabı listesini tarama komutunu göndermeden önce sunucuları kendi içerisinde ulaşılabilir ve ulaşılamayanlar olarak da ayırıp, daha sonra sadece açık ve ulaşılabilir olan sunuculara komutu gönderelim istiyorum. Bunu da bildiğimiz klasik “Ping” komutu ile test edeceğiz. İsteğe bağlı olarak sizlere PowerShell 3.0 ile gelen ve bizim SuperPing olarak da isimlendirdiğimiz “Test-Connection” komutu ile de bunu gerçekleştirebilirsiniz. Neticede ping komutuna cevap olarak karşı sunucudan bir dönüş olacak ve ben dönen cevabın koduna bakara sunucunun durumunu tespit edip başarıyla ulaşılabilen sunucuları ayrıştırmış olacağım. Script dosyasının içerisine bilgi olarak PING komutuna dönen kodları aşağıdaki şekilde ekliyorum:
############################## PING Durum Kodlari #####################
######### 0 {“Success”}
######### 11001 {“Buffer Too Small”}
######### 11002 {“Destination Net Unreachable”}
######### 11003 {“Destination Host Unreachable”}
######### 11004 {“Destination Protocol Unreachable”}
######### 11005 {“Destination Port Unreachable”}
######### 11006 {“No Resources”}
######### 11007 {“Bad Option”}
######### 11008 {“Hardware Error”}
######### 11009 {“Packet Too Big”}
######### 11010 {“Request Timed Out”}
######### 11011 {“Bad Request”}
######### 11012 {“Bad Route”}
######### 11013 {“TimeToLive Expired Transit”}
######### 11014 {“TimeToLive Expired Reassembly”}
######### 11015 {“Parameter Problem”}
######### 11016 {“Source Quench”}
######### 11017 {“Option Too Big”}
######### 11018 {“Bad Destination”}
######### 11032 {“Negotiating IPSEC”}
######### 11050 {“General Failure”}
######### default {“Failed”}
############################## PING Durum Kodlari #####################
Bu listede benim için önemli olan “0” cevabi ile dönenler. O zaman şimdi biraz önce metin dosyasına çıktı olarak aktardığımız sunucuları dosyadan satır satır çağırıp, bunları PING komutu ile test edip, başarıyla bağlananları ayrıştıralım ve ServersUP.TXT isimli bir dosyaya yazdıralım.
Öncelikle servers.txt dosyasından sunucu isimlerini nasıl okuyacağız? Powershell script dilinde bir dosyanın içeriğini okumak için get-content komutunu ya da takma adı (alias name) olan kısaca gc kullanıyoruz. Aşağıdaki satırla servers.txt dosyası içerisindeki sunucu isimlerini satır satır okuyarak ServerArray isimli değişkene aktarıyoruz.
$ServerArray=gc “C:\Servers.txt”
ServerArray isimli değişkenin şu anki tipine yukarıdaki gibi $ServerArray.GetType() ile bakarsak ARRAY yani DİZİ tipinde olduğunu göreceksiniz. Böylece dosya içerisindeki tüm satırlardaki bilgi şu anda aslındaServerArray isimli dizi içerisinde. Hatta kontrol için dizinin ilk elemanını okutturarak bir test yapalım:
$ServerArray[0] ile ilk satırdaki sunucu adı olan MSTDC01 bulunduğunu görmüş olduk. Dizi içerisindeki kayıt sayısını da Count metodu ile listeleyebiliriz:
Şimdi dizi içerisinde kayıtlı bu sunuculara bir döngü içerisinde sırayla bağlanarak PING komutu ile bir test yapacağız.
PowerShell ile döngü kullanımında farklı methodlar ya da döngü tipleri bulunmaktadır. Bunlar :
• For : Belli bir sayaç değerinde dönen döngü tipi. En temel döngü metodudur.
• ForEach : Kendisine girdi olarak gelen koleksiyon içerisindeki kayıt sayısı kadar dönerek görevini icra eden temel döngü tipidir. For döngüsüne göre daha fazla parametre içeririr.
• ForEach-Object : Kendisine girdi olarak gelen koleksiyon içerisindeki kayıt sayısı kadar dönerek görevini icra eden temel döngü tipidir. Özellikle çıktının formatı ya da koleksiyon nesnesi üzerinde değişiklikler ve formatsal işlemler yapılacağında tercih edilir.
• While : Kendisinden sonra belirtilen koşul sağlandığı yani TRUE değer döndüğü müddetçe döner. For döngülerindeki sayaç miktarından ziyade belirtilen şarta bağlı dönerek farklılığını ortaya koyar.
• Do while : While döngüsünün farklı bir varyasyonudur. Farklı olarak her zaman ilk seferde çalışır ve komutlarını icra eder, sonrasında da while satırında belirtilen koşul sağlandıkça dönmeye devam eder.
• Do until : Bu da While döngüsünün farklı bir varyasyonudur. Farklı olarak her zaman ilk seferde çalışır ve komutlarını icra eder, sonrasında da until satırında belirtilen koşul sağlanmadığı müddetçe dönmeye devam eder. Until satırındaki koşul sağlandığı anda döngüden çıkar.
Not : Döngülerin farklı kullanımlarını gerek bu makalemizde gerekse de gelecekteki makalelerimizde uygulamalar içerisinde sizlerle paylaşıyor olacağız.
Biz buradaki örneğimizde ForEach döngüsünü kullanıyoruz. Bunun için aşağıdaki şekilde görüldüğü gibi döngü bloğumuzu açıyoruz:
Foreach($Server in $ServerArray)
{
}
Burada döngümüz $ServerArray dizisi içerisindeki kayıt sayısı kadar dönecek ve her döndüğünde o anki sıradaki kaydı $Server isimli değişkene alacaktır. Aslında burda string değerde üyeleri bulunan bir dizi yani array tipini kullanıyoruz. ForEach döngüsünden sonra mutlaka süslü parantez ile döngü bloğu açılır, içerisine ilgili kodlar yazılı ve sonrasında da yine süslü parantez ile döngü bloğu kapatılır. Eğer ForEach döngüsü içerisindeki kodlar kısa ve tek satırda kullanılabiliyorsa aynı satırda da kullanıldığı durumla olabilir. Bunu aşağıdaki şekilde de görmektesiniz.
Foreach($Server in $ServerArray) { Komutlar }
Sunuculardan PING komutu cevabını WMI nesnelerini ve WMI sorgu yapısını kullanarak çekeceğiz.
WMI açılımı Windows Management Instrumentation olan ve Windows işletim sistemleri üzerinde gerek lokal gerekse de uzaktan yönetimsel görevleri yerine getirmek için kullanılan sistem yönetimindeki öncelikli otomasyon teknolojisidir. İçerisinde komut kütüphanalerini, kendine has sorgulama dilini, methodlarını, property yani özelliklerini içerir. T-SQL sorgulama diline çok benzer yapıda kullanılan WQL (WMI Query Language) adında kendine has bir sorgulama dili vardır. Temel komutları T-SQL sorgulama dilinde olduğu gibi SELECT, FROM ve WHERE ifadeleridir. Tipik bir WQL ifadesi ya da WMI sorgusu Select ifadesi ile başlar.
Örneğin : “Select * from Win32_Bios”
From ifadesinden sonra hangi VMI sınıfından bilgi çekmek istiyorsanız o sınıf adı belirtilir. Örneğin yukarıdaki örnekte bilgisayarımızın BIOS bilgilerini listelemek için Win32_Bios sınıfını belirtiyoruz. Buradaki sınıf yani class T-SQL’deki sorguladığımız tablo ya da view karşılık gelmektedir.
Diğer yandan BIOS ile ilgili tüm bilgileri değil de sadece Name özelliğinde tutulan bilgisi görüntülemek için de Select ifadesinden sonra * yerine Name özelliğinin adını belirtiyoruz.
Örneğin : “Select Name from Win32_Bios”
Not : “*” ifadesi ilgili sınıfın tüm özellik yani property alanlarındaki bilgileri getirir.
PowerShell komut satırından ya da PowerShell scriptler içerisinde WMI sorgularını çalıştırmak için deGet-WmiObject komutu kullanılır. Böylece Get-VMIObject ile ilgili WMI sorgusu powershell içerisinde tetiklenerek gelen sonuca göre aksiyon alınır.
Örneğin yukarıdaki BIOS bilgisini getiren WQL sorgusunu powershell ile tetikleyerek sonucu görelim:
$sorgu=”Select * from Win32_Bios”
Get-WmiObject -Query $sorgu
Komutun çıktısında yukarıdaki görüldüğü gibi BIOS sınıfı içerisinde kayıtlı bilgisayarımıza ait BIOS bilgilerini almış olduk.
Burada bir $sorgu gibi bir ara değişken kullanmadan doğrudan Get-WMIObject satırında da belirtebiliriz:
Get-WmiObject -Query “Select * from Win32_Bios”
Şimdi gelelim bizim örneğimizdeki PING komutunun nasıl kullanılacağımıza:
PING işlemi için WMI içerisindeki win32_PingStatus kütüphanesini kullanacağız. Bu kütüphaneyi kullanarak sorgularımızı yazarken kriter olarak sunucu adını belirterek sunucu-sunucu sırayla kontrolümüzü gerekçekleştireceğiz. Örneğin; MSTDC01 sunucusuna PING ile istek göndermek için aşağıdaki sorguyu çalıştıracağız:
Get-WmiObject -Query “Select * from win32_PingStatus where Address=’MSTDC01′”
Burada Address alanı iletişim kurulmak istenen sunucu adına ait değeri içerir. Bizim tabi burdaki amacımız bu gönderdiğimiz isteğin sonucunu almak. Bunun için de bunun çıktısını $pingsonuc isimli bir değişkene aktaracağız. Bu durumda kodun güncel hali aşağıdaki gibi olacaktır:
$pingsonuc=Get-WmiObject -Query “Select * from win32_PingStatus where Address=’MSTDC01′”
Bu değişkene dönen bilgiler içerisinde yukarıdaki gibi $pingsonuc.statuscode ile dönen sonuç sorgulandığında “0” değerinin döndüğünü göreceksiniz. Yani MSTDC01 isimli sunucuya başarılı bir biçimde bağlandığımızı “0” değeri ile görmüş olduk.
Biz örneğimizde bu testi $ServerArray isimli sunucu isimlerini barındıran diziden tek tek sunucu isimlerini alıp, her satırı $server isimli değişkende tutarak PING testimizi yapacak ve dönen sonuç başarılı ise bunu ServersUP.txt isimli metin dosyasına aktaracağız.
Burada $ pingsonuc değişkeninden gelen çıktının değerini kontrol etmemiz gerekiyor. Programlamada bu tip kontrol işlerini IF ya da CASE gibi yapılarla gerçekleştiriyoruz. “Eğer değeri şu ise ŞUNU YAP, BU ise BUNU YAP gibi bir ya da daha fazla kritere göre koşullandırma yapabiliyoruz. Bizim örneğimizde şu an için sadece PING atınca sunucu ayakta mı, cevap veriyor mu yani dönen sonuç “0” mı değil mi ona bakacağız. Biz örneğimizde bu kontrol için IF koşul ifadesini kullanıyoruz.
#BASIT IF YAPISI
# IF (Koşul)
# {
# “Koşul True ise yapılacaklar”
# }
# ELSE
# {
# “Koşul False ise yapılacaklar”
# }
Biz örneğimizde sadece bağlanabiliyorsak kısmına baktığımız için sadece IF kontrolünden dönen sonuç “0” ise yapılacak aksiyonu yapıp script kodumuza devam edeceğiz. Dolayısıyla kullanacağımız IF cümlesi :
if($pingsonuc.StatusCode -eq 0) { “Çalışacak Komutlar” }
Dönen sonucun “0” dışında olanlar için biz burada bir aksiyon almayacağımız için ELSEIF ya da ELSE deyimlerini kullanma ihtiyacını duymuyoruz. Çalışacak Komutlar bölümünde de yapacağımız iş bağlantısı başarılı olan sunucu adını SERVERSUP.txt dosyasına yazdırmak ki bunu da OUT-File powershell komutu ile gerçekleştiriyoruz. Döngülerde olduğu gibi IF yapılarında da şart sağlanırsa çalışacak komutları süslü ya da küme parantezi içerisinde yazıyoruz.
Not: Kompleks IF kullanımları, ELSEIF ve ELSE kullanımlarını önümüzdeki PowerShell entegrasyonlu makalelerde anlatıyor olacağım.
Son durum itibariyle sorgumuza döngüyü de kapsayacak şekilde tekrar bakacak olursak:
$ServerArray=gc “C:\Servers.txt”
Foreach($Server in $ServerArray)
{
$pingsonuc = Get-WmiObject -Query “Select * from win32_PingStatus where Address=’$Server‘”
if($pingsonuc.StatusCode -eq 0) {$server | Out-File C:\ServersUP.txt –Append }
}
Burada yukarıdaki kullanımdan farklı olarak sunucu adını $server parametresinden okuyoruz. Out-File satırındaki –Append parametresi de önceki kayıtların üzerine yazmadan dosyaya ekleyerek yazmayı sağlıyor. Böylece eski değerleri de muhafaza etmiş oluyoruz. Genel olarak kodu açıklayacak olursak Servers.txt dosyası içerisinden tüm satırlar okunur ve $ServerArray isimli dizi değişkenine aktarılır. Daha sonra bu diziden indis sırasına göre kayıtlar okunur, okunan her kayır $server isimli değişkene atanır. WMI sorgusu ile ilgili sunucuya PING kontrolü yapılıp, dönen sonuç $pingsonuc değişkenine aktarılır. $pingsonuc değişkenine dönen bilgiler içerisinden StatusCode değeri “0” eşit ise sunucunun adı ServersUP.txt dosyasına yazılır, değilse döngü tekrar dönerek bir sonraki kaydı kontrol eder. Bu şekilde tüm kayıtlar kontrol edildikten sonra bağlantının başarıyla sağlandığı sunucular ServersUP.txt dosyasına ayrıştılmış oldu.
Şimdi sıradaki işlem başarıyla bağlantı sağlanan sunucularda kullanılan servis hesabı bilgisini almak. Bu işlem için de yine WMI komut kütüphanesini kullanacağız. Sunucular üzerindeki servislerle ilgili bilgileri aldığımız sınıf win32_service’ dir. Örneğin aşağıdaki komutla MSTDC01 isimli sunucu üzerindeki servislere ait detayları listeleyebilirsiniz:
Get-WmiObject win32_service -ComputerName MSTDC01
Biz örneğimizde sunucu isimlerini yine ServersUP.txt dosyasından okuyup, bir dizi değişkene aktarıp döngü ile çağırıyor olacağız. Ayrıca bir diğer önemli detay da genelde aşağıdaki servis hesaplarını kullanan servislerin de listelenmesine çok ihtiyaç duyulmaz:
· LocalSystem
· NT Service\MsDtsServer110
· NT Service\MSSQLFDLauncher
· NT Service\ReportServer
· NT Service\SQLSERVERAGENT
· NT AUTHORITY\NetworkService
· NT AUTHORITY\LocalService
· NT AUTHORITY\NETWORK SERVICE
· NT AUTHORITY\LOCAL SERVICE
Çünkü bizim için önemli olan bu lokal hesapların dışında domain ortamında açılmış ve kullanılan servis hesaplarının listesini almaktır. Bu isteğimizi de yine powershell’in where-object ya da bunun alias’ı olan Wherekomutu ile sağlayabiliriz. Bunun için aşağıdaki şekilde görüldüğü gibi MSTDC01 sunucusundan çektiğimiz servis listesini pipe simgesi ile where kriterine girdi olarak aktarıyoruz ve dönen sonuçlardan içerisinde yukarıdaki listede bulunan servis hesaplarını içerenleri almıyoruz.
Get-WmiObject win32_service -ComputerName MSTDC01 | where-object {($_.StartName -ne “localSystem”) -and ($_.StartName -ne “NT Service\MsDtsServer110”) -and ($_.StartName -ne “NT Service\MSSQLFDLauncher”) -and ($_.StartName -ne “NT Service\ReportServer”) -and ($_.StartName -ne “NT Service\SQLSERVERAGENT”) -and ($_.StartName -ne “NT AUTHORITY\NetworkService”) -and ($_.StartName-ne “NT AUTHORITY\LocalService”) -and ($_.StartName -ne “NT AUTHORITY\NETWORK SERVICE”) -and ($_.StartName -ne “NT AUTHORITY\LOCAL SERVICE”)}
Burada dikkatinizi çeken ifade $_ ifadesi olmuştur. $_ powershell script diline özel pipeline değişkenidir. O an üzerinde işlem yapılan nesneyi temsil eder. Örneğin bizim örneğimizde birinci pipe’ın solundan gelen o anki servisi temsil eder. Bu gelen servisin servis hesabı bilgisini de StartName isimli özellikten çekiyoruz. Yani $_.StartName ile o an üzerinde işlem yapılan servisin servis hesabı bilgisini alıp, -ne yani Not Equal (Eşit Değil) karşılaştırma operatörü ile belirttiğimiz değere eşit olmayan ya da belirttiğimiz değerin dışında kalan servis hesabını kullanan servislerin listesini alıyoruz. Sonuçta da aşağıdaki gibi daha kısa bir çıktı gelecektir:
Gelen listeyi tablo formatında ve aşağıdaki şekilde bir formatta almak için de yeni bir pipe daha açıp yukarıdaki çıktıdan servisin adı (Name), servisin başlangıç Modu (StartMode), kullanılan servis hesabı adı (StartName) ve servisin çalıştığı sunucunun adı (SystemName) parameterleri ile çektik. Bunların görünen isimleri olarak da yani kolon başlıklarındaki etiket isimleri olarak da sırayla Name, Startup Type, Service Account, System Name olarak düzenledik.
Get-WmiObject win32_service -ComputerName MSTDC01 | where-object {($_.StartName -ne “localSystem”) -and ($_.StartName -ne “NT Service\MsDtsServer110”) -and ($_.StartName -ne “NT Service\MSSQLFDLauncher”) -and ($_.StartName -ne “NT Service\ReportServer”) -and ($_.StartName -ne “NT Service\SQLSERVERAGENT”) -and ($_.StartName -ne “NT AUTHORITY\NetworkService”) -and ($_.StartName-ne “NT AUTHORITY\LocalService”) -and ($_.StartName -ne “NT AUTHORITY\NETWORK SERVICE”) -and ($_.StartName -ne “NT AUTHORITY\LOCAL SERVICE”)} | select Name,@{N=“Startup Type”;E={$_.StartMode}},@{N=“Service Account”;E={$_.StartName}}, @{N=“System Name”;E={$_.Systemname}}
Bu düzenlemeler sonrasında elde ettiğimiz çıktı aşağıdaki gibi olmuştur.
Şimdi bu gelen listeyi de servis adına göre A’dan Z’ye sıralayacak eklemeyi yapalım. Bunun için de bir pipe daha ilave edip Sort-Object “Name” komutunu kullanacağız. Script’in son hali aşağıdaki gibidir:
Get-WmiObject win32_service -ComputerName MSTDC01 | where-object {($_.StartName -ne “localSystem”) -and ($_.StartName -ne “NT Service\MsDtsServer110”) -and ($_.StartName -ne “NT Service\MSSQLFDLauncher”) -and ($_.StartName -ne “NT Service\ReportServer”) -and ($_.StartName -ne “NT Service\SQLSERVERAGENT”) -and ($_.StartName -ne “NT AUTHORITY\NetworkService”) -and ($_.StartName-ne “NT AUTHORITY\LocalService”) -and ($_.StartName -ne “NT AUTHORITY\NETWORK SERVICE”) -and ($_.StartName -ne “NT AUTHORITY\LOCAL SERVICE”)} | select Name,@{N=“Startup Type”;E={$_.StartMode}},@{N=“Service Account”;E={$_.StartName}}, @{N=“System Name”;E={$_.Systemname}} | Sort-Object “Name”
Bu haliyle tekrar çalıştırınca gelen çıktının son hali aşağıdaki gibidir:
Bu çıktıyı bir CSV dosyasına çıkarmak için de Export-CSV komutunu ilave ediyoruz. Önceki kayıtların üzerine yazmaması için de aşağıdaki gibi –Append modda dosyaya yazıyoruz:
export-csv C:\ServiceAccountList.csv” -Append
Bu eklemeyle beraber kodun son hali aşağıdaki gibidir:
Get-WmiObject win32_service -ComputerName MSTDC01 | where-object {($_.StartName -ne “localSystem”) -and ($_.StartName -ne “NT Service\MsDtsServer110”) -and ($_.StartName -ne “NT Service\MSSQLFDLauncher”) -and ($_.StartName -ne “NT Service\ReportServer”) -and ($_.StartName -ne “NT Service\SQLSERVERAGENT”) -and ($_.StartName -ne “NT AUTHORITY\NetworkService”) -and ($_.StartName-ne “NT AUTHORITY\LocalService”) -and ($_.StartName -ne “NT AUTHORITY\NETWORK SERVICE”) -and ($_.StartName -ne “NT AUTHORITY\LOCAL SERVICE”)} | select Name,@{N=“Startup Type”;E={$_.StartMode}},@{N=“Service Account”;E={$_.StartName}}, @{N=“System Name”;E={$_.Systemname}} | Sort-Object “Name” | export-csv “C:\ServiceAccountList.csv” -Append
Bu haliyle tekrar çalıştırınca sonuçların ekran yerine CSV dosyasına aktarıldığını ve aşağıdaki şekilde geldiğini göreceksiniz:
Biz bu aşamaya kadar tek sunucu için bu listeyi çıkarttık ve detaylarıyla anlattık. Bizim örneğimizdeki tüm sunucular için bunu döngü içerisinden çalıştırmak için kullanılacak kodun güncel hali de aşağıdadır:
$ServerArray1=gc “C:\ServersUP.txt”
cd $KayitYeri
Foreach ($item in $ServerArray1 )
{
Write-Host “Adi $item Olan Sunucuya Ait Servis Detayları alınıyor “
Get-WmiObject win32_service -ComputerName $item | where-object {($_.StartName -ne “localSystem”) -and ($_.StartName -ne “NT Service\MsDtsServer110”) -and ($_.StartName -ne “NT Service\MSSQLFDLauncher”) -and ($_.StartName -ne “NT Service\ReportServer”) -and ($_.StartName -ne “NT Service\SQLSERVERAGENT”) -and ($_.StartName -ne “NT AUTHORITY\NetworkService”) -and ($_.StartName-ne “NT AUTHORITY\LocalService”) -and ($_.StartName -ne “NT AUTHORITY\NETWORK SERVICE”) -and ($_.StartName -ne “NT AUTHORITY\LOCAL SERVICE”)} | select Name,@{N=“Startup Type”;E={$_.StartMode}},@{N=“Service Account”;E={$_.StartName}}, @{N=“System Name”;E={$_.Systemname}} | Sort-Object “Name” | export-csv “$KayitYeri\ServiceAccountList.csv“ -Append
}
Yukarıdaki kod bloğunda Write-Host komutu ile konsol ekranına mesaj görüntülemek için kullanıyoruz. Yaptığı işlev ekrana ilgili sunucuya ait servis hesabi detaylarının alınmaya başlayacağının mesajını göstermektir. Her sunucu için sırayla bu mesaj konsol ekranına görüntülenecek ve ilgili sunucu üzerindeki servis hesapları listelenerek çıktısı C:\ServiceAccountList.csv dosyasına kaydedilecektir.
Script kodumuzun son kısmında üretilen dosyaları e-posta yoluyla ilgili adreslere mail atma fonksiyonunu yerine getirecek kodlamayı da yapıp tamamlamış olalım.
PowerShell ile e-posta göndermeyi Send-MailMessage komutu ile gerçekleştiriyoruz.
Send-MailMessage komutunun söz dizimini aşağıda görmektesiniz:
Send-MailMessage [-To] <String[]> [-Subject] <String> [[-Body] <String> ] [[-SmtpServer] <String> ] -From <String> [-Attachments <String[]> ] [-Bcc <String[]> ] [-BodyAsHtml] [-Cc <String[]> ] [-Credential <PSCredential> ] [-DeliveryNotificationOption <DeliveryNotificationOptions> ] [-Encoding <Encoding> ] [-Port <Int32> ] [-Priority <MailPriority> ] [-UseSsl] [ <CommonParameters>]
From : E-posta mesajının gönderileceği kimden adresi.
To : E-posta mesajının gönderileceği birinci dereceden sorumlu adres ya da adresler.
Cc : E-posta mesajının bilgi amaçlı gönderileceği adres ya da adresler.
Bcc : E-posta mesajının gönderileceği gizli adres ya da adresler.
Subject : E-posta mesajının konusu.
Body : E-posta mesajının gövde kısmı.
BodyAsHtml : E-posta mesajının gövde kısmının HTML formatta gönderilmesini sağlar.
Credential : E-posta mesajını gönderirken kullanılacak kullanıcı hesabı bilgileri.
Priority : E-posta mesajının öncelik değeri.
Attachments : E-posta mesajının ekleri.
Port : E-posta mesajının gönderileceği alternatif SMTP port numarası.
SMTPServer : E-posta mesajının gönderileceği SMTP sunucu adı ya da ip adresi.
Bu tanımlamalardan sonra kodumuzun en alt kısmına aşağıdaki şekilde e-mail gönderme kodumuzu da ekliyoruz:
$output=“$KayitYeri\ServiceAccountList.csv”
$from = “bildiri@mesutaladag.com”
$to = “mesutaladag@hotmail.com”
$CCaddress1 = “mesut.aladag@cozumpark.com”,”mesut.aladag@kocsistem.com.tr”
$bccaddress1 = “mesutaladag@gmail.com”
$smtpserver = “mail.mesutaladag.com”
#### MAIL GONDERIMI ###############
Send-MailMessage -From $from -To $to -Cc $CCaddress1 -Bcc $bccaddress1 -Subject “[SERVICE ACCOUNTS ON SERVERS]” -SmtpServer $smtpserver -Attachments $success,$output
#################################
Son bir işlem olarak e-posta ile gönderilen dosyaları disk üzerinden silmek için de aşağıdaki komutları kullanabilirsiniz:
### DOSYALARIN SILINMESI########
Remove-Item “C:\Servers.txt”
Remove-Item “C:\ServersUP.txt”
Remove-Item “$KayitYeri\ServiceAccountList.csv”
Böylelikle mevcut active directory domain ortamında merkezi olarak sunucular üzerinde kurulu uygulamalardan servis hesabı envanterini toplayan script dosyamızın sonuna geldik.
Script kodumuzun konsolide halini aşağıda sizlerin kullanımına sunuyoruz:
<######################################################################
# Author : MESUT ALADAG
# Web : www.mesutaladag.com
# Date : 17th July 2014
# Version : 1.0
# Description : Bu PwShell script ile merkezden sunucular üzerindeki servis envanterini çekiyor olacağız.
######################################################################>
#Parametreler istege gore degistirilebilir.
Get-ADComputer -Filter { OperatingSystem -Like ‘Windows Server*’ } -Properties OperatingSystem |Select -ExpandProperty Name | Out-File “C:\Servers.txt”
############################## PING Durum Kodlari #####################
######### 0 {“Success”}
######### 11001 {“Buffer Too Small”}
######### 11002 {“Destination Net Unreachable”}
######### 11003 {“Destination Host Unreachable”}
######### 11004 {“Destination Protocol Unreachable”}
######### 11005 {“Destination Port Unreachable”}
######### 11006 {“No Resources”}
######### 11007 {“Bad Option”}
######### 11008 {“Hardware Error”}
######### 11009 {“Packet Too Big”}
######### 11010 {“Request Timed Out”}
######### 11011 {“Bad Request”}
######### 11012 {“Bad Route”}
######### 11013 {“TimeToLive Expired Transit”}
######### 11014 {“TimeToLive Expired Reassembly”}
######### 11015 {“Parameter Problem”}
######### 11016 {“Source Quench”}
######### 11017 {“Option Too Big”}
######### 11018 {“Bad Destination”}
######### 11032 {“Negotiating IPSEC”}
######### 11050 {“General Failure”}
######### default {“Failed”}
############################## PING Durum Kodlari #####################
$ServerArray=gc “C:\Servers.txt”
Foreach($Server in $ServerArray)
{
$pingsonuc = Get-WmiObject -Query “Select * from win32_PingStatus where Address=’$Server‘”
if($pingsonuc.StatusCode -eq 0) {$server | Out-File C:\ServersUP.txt –Append }
}
$ServerArray1=gc “C:\ServersUP.txt”
$KayitYeri=“C:\”
cd $KayitYeri
Foreach ($item in $ServerArray1 )
{
Write-Host “Adi $item Olan Sunucuya Ait Servis Detayları alınıyor “
Get-WmiObject win32_service -ComputerName $item | where-object {($_.StartName -ne “localSystem”) -and ($_.StartName -ne “NT Service\MsDtsServer110”) -and ($_.StartName -ne “NT Service\MSSQLFDLauncher”) -and ($_.StartName -ne “NT Service\ReportServer”) -and ($_.StartName -ne “NT Service\SQLSERVERAGENT”) -and ($_.StartName -ne “NT AUTHORITY\NetworkService”) -and ($_.StartName-ne “NT AUTHORITY\LocalService”) -and ($_.StartName -ne “NT AUTHORITY\NETWORK SERVICE”) -and ($_.StartName -ne “NT AUTHORITY\LOCAL SERVICE”)} | select Name,@{N=“Startup Type”;E={$_.StartMode}},@{N=“Service Account”;E={$_.StartName}}, @{N=“System Name”;E={$_.Systemname}} | Sort-Object “Name” | export-csv “$KayitYeri\ServiceAccountList.csv” -Append
}
$success=“C:\ServersUP.txt”
$output=“$KayitYeri\ServiceAccountList.csv”
$from = “bildiri@mesutaladag.com”
$to = “mesutaladag@hotmail.com”
$CCaddress1 = “mesut.aladag@cozumpark.com”,”mesut.aladag@kocsistem.com.tr”
$bccaddress1 = “mesutaladag@gmail.com”
$smtpserver = “mail.mesutaladag.com”
#### MAIL GONDERIMI ###############
Send-MailMessage -From $from -To $to -Cc $CCaddress1 -Bcc $bccaddress1 -Subject “[SERVICE ACCOUNTS ON SERVERS]” -SmtpServer $smtpserver -Attachments $success,$output
#################################
### DOSYALARIN SILINMESI########
Remove-Item “C:\Servers.txt”
Remove-Item “C:\ServersUP.txt”
Remove-Item “$KayitYeri\ServiceAccountList.csv”
SONUÇ
Bu makalemizde sizlere Windows Server 2012 R2 işletim sistemi üzerinde PowerShell ile Active Directory Domain ortamlarında merkezden istenilen sunucular üzerinde kullanılan servis hesabı envanteri çıkarmayı adım adım hem kodunu yazıp hem de detaylı anlatımını gerçekleştirdik. Umarım çoğunuzun bu konudaki ihtiyacını karşılayan bir script olacaktır.Önümüzdeki makalelerde Windows Server 2012 R2 ile gelen yeni özellikleri incelemeye devam ediyor olacağız. Görüşmek üzere, sağlıcakla kalın.