Quản Trị Mạng - Trong bài viết trước, chúng tôi đã giới thiệu với các bạn một số chức năng và thủ thuật cơ bản có thể thực hiện với PowerShell trong Windows Server 2008. Và lần này, chúng ta sẽ tiếp tục với phần 2, cũng là phần cuối trong loạt bài về PowerShell trong môi trường Windows Server:
Thủ thuật với PowerShell
6. Thu thập thông tin của 10 lỗi gần nhất
Hàng ngày, có thể bạn sẽ phải duyệt từng file log trong hệ thống để tìm thông tin chi tiết những sự kiện lỗi phát sinh gần đây nhất trên 1 hoặc nhiều nhiều máy tính. Tuy nhiên, chúng ta hoàn toàn có thể đơn giản hóa quá trình này bằng cách sử dụng lệnh cmdlet Get-EventLog trong PowerShell.
Tất cả những gì các bạn cần làm ở đây là xác định rõ tên của file log và thành phần tương ứng. Cấu trúc lệnh thông thường sẽ có dạng như sau:
Trong ảnh chụp màn hình trên thì tên của file log là system, thành phần tham gia là Error. Và như vậy, PowerShell sẽ thu thập toàn bộ thông tin có liên quan tới 10 lỗi xảy ra gần đây nhất từ log của hệ thống. Lệnh này được thực hiện trên bất kỳ máy tính local nào, do vậy các bạn sẽ không cần phải chỉ định rõ tên máy tính. Lưu ý rằng nếu câu lệnh trên không hiển thị kết quả theo đúng yêu cầu, chúng ta có thể chỉnh sửa lại một chút thông tin theo cú pháp dưới đây:
Chúng ta đơn giản hóa quá trình bằng cách chuyển thông tin output của câu lệnh trước đó thành ft, với alias là Format-Table, và yêu cầu hiển thị các thuộc tính sau: Timewritten, Source, EventID, và Message. Đồng thời, chúng tôi thêm -wrap và -auto để kết quả “dễ nhìn” hơn. Cụ thể, -wrap kích hoạt chế độ wrap đối với thông tin dạng text, và -auto là tính năng tự động thay đổi kích thước.
Và kết quả của chúng ta sẽ như sau:
Tiếp theo, chúng ta sẽ tạo 1 câu lệnh khác, và lần này yêu cầu đặt ra là sắp xếp thuộc tính theo trường Source, sau đó gộp chúng lại. Kết quả cuối cùng sẽ được gán thêm tùy chọn more để hiển thị theo từng kích thước màn hình riêng, người dùng sẽ không phải kéo chuột xuống dưới để xem tất cả:
Và kết quả:
Bây giờ là thời điểm cần phải gộp các thành phần theo source. Nhóm thông tin đầu tiên đều có EventLog tương tự là source, còn nhóm thứ 2 là Microsoft-Windows-GroupPolicy. Các bạn cần để ý rằng -- More -- xuất hiện ở phía cuối màn hình, có nhiệm vụ thông báo với người sử dụng nhấn 1 phím bất kỳ để xem thêm thông tin.
Tất cả các câu lệnh Get-EventLog đều được thực hiện trên máy tính local, còn nếu muốn thực hiện trên máy tính remote thì sao?
Ví dụ, chúng ta muốn xem kết quả báo cáo về tình trạng lỗi 5 mới nhất xảy ra trên một số máy tính thuộc hệ thống domain controller tại chi nhánh ở Chicago. Các máy tính có tên lần lượt là chi-dc01 và chi-dc02, giả sử rằng người quản trị muốn sắp xếp kết quả theo Machine Name. Để thực hiện, chúng ta sẽ cho hiển thị những thuộc tính như sau: Timewritten, Source, EventID, và Message. Một lần nữa, các bạn nên sử dụng tham số -wrap, -auto, và more tại đây:
Và đây là kết quả:
7. Reset quyền điều khiển trên thư mục
Trên thực tế, có khá nhiều trường hợp mức phân quyền NTFS của 1 thư mục không được thiết lập đúng cách. Và để khắc phục thì chúng ta chỉ cần reset lại quyền điều khiển tương ứng với cú pháp lệnh cmdlet Set-Acl (Set-ACL) trên PowerShell.
Cách thực hiện đơn giản nhất tại đây có lẽ là sử dụng Get-Acl để thu thập thông tin ACL từ 1 thư mục hoàn toàn bình thường, sau đó gán vào thư mục đang gặp vấn đề. Giả sử rằng chúng ta có những file chia sẻ có tên là sales trên máy tính CHI-FP01, và file đó có ACL khá ổn định. Nếu muốn copy dữ liệu ACL của sales sau đó lưu vào biến $acl thì các bạn hãy dùng câu lệnh sau:
Và đây là phần thông tin bên trong ACL:
Các bạn có nhìn thấy thuộc tính Access ở phía góc phải? Đó thực ra chỉ là 1 đối tượng khác, và để biết nội dung bên trong đó thì chúng ta dùng lệnh:
Kết quả hiển thị:
Chúng ta có thể dễ dàng thấy rằng đây chỉ là 1 collection của nhiều thành phần Access Control khác. Nếu chỉ muốn xem những thông tin có liên quan tới sales thì sử dụng cú pháp:
Tiếp theo, dùng lệnh tương tự như vậy để kiểm tra thông tin bên trong thuộc tính Access, thuộc về file chia sẻ mới được tạo có tên là chicagosales thì chúng ta sẽ không nhận được gì.
Lý do tại sao hệ thống không hiển thị bất kỳ thông tin nào có thể là do chế độ chia sẻ đã được thiết lập nhưng mức phân quyền NTFS không được áp dụng đúng cách. Phương án khắc phục với tỉ lệ thành công cao nhất là copy thông tin ACL từ file chia sẻ ổn định sang file có vấn đề. Nhưng trước tiên, chúng ta cần phải lấy mức phân quyền NTFS hiện thời của chicagosales và lưu thành file XML. Với cách làm này, chúng ta có thể dễ dàng khôi phục nếu xảy ra lỗi trong quá trình thực hiện:
Sau đó, các bạn tiếp tục thực hiện và áp dụng lệnh Set-Acl trên chicagosales sử dụng thuộc tính $acl:
Để kiểm tra quá trình thực hiện của chúng ta có thành công hay không, sử dụng câu lệnh trước đó để kiểm tra các thông tin có liên quan tới Sales như hình dưới:
Và giờ đây mức phân quyền NTFS trên thư mục chicagosales đã tương tự với sales.
8. Kiểm tra giờ hoạt động của server
Để thực hiện yêu cầu này, chúng ta chỉ cần áp dụng class WMI Win32_OperatingSystem trong PowerShell, có thể đáp ứng được nhu cầu của bạn, hoạt động trực tiếp trên máy local hoặc remote – điều khiển từ xa. Thuộc tính kỹ thuật cần tìm kiếm ở đây chính là LastBootUpTime, tuy nhiên định dạng nguyên gốc lại là WMI, do vậy chúng ta sẽ phải chuyển đổi định dạng – convert thành đối tượng khác dễ xử lý hơn.
Chúng ta hãy bắt đầu với ví dụ sử dụng máy tính local với hệ điều hành Windows 7. Trước tiên, hãy lưu kết quả của cú pháp GetWmiObject thành 1 biến khác có tên là $wmi:
Và từ bây giờ, chúng ta có thể làm việc trực tiếp trên $wmi, cụ thể là thuộc tính CSName - Computer Name và LastBootUpTime:
Như đã đề cập ở phía trên, thuộc tính LastBootUpTime là WMI timestamp, không thực sự hữu ích trong trường hợp này. Do vậy, chúng ta sẽ phải convert sang định dạng khác, cụ thể ở đây là 1 biến khác có tên là $boot:
Trong trường hợp này, các bạn nên áp dụng hàm ConverToDateTime, đã được bao gồm trong đối tượng WMI khi sử dụng GetWmiObject. Tham số cần truyền vào đây chính là thuộc tính LastBootUpTime của đối tượng $wmi của WMI. Nếu chúng ta cho hiển thị giá trị của $boot thì hệ thống sẽ hiển thị như sau:
Rõ ràng những thông tin này có ích hơn nhiều so với thuộc tính nguyên bản LastBootUpTime. Để tìm ra khoảng thời gian hệ thống đã hoạt động, chúng ta chỉ cần trừ giá trị $boot từ thời điểm date/time hiện tại – bằng cách dùng hàm Get-Date:
Kết quả ở đây lại là 1 đối tượng khác dạng TimeSpan, nếu muốn phần thông tin này “nhỏ gọn” hơn thì thì các bạn chỉ việc áp dụng công thức convert thành string với hàm ToString():
Hệ thống của chúng ta trong trường hợp này đã hoạt động được 2 ngày, 5 giờ, 46 phút. Việc cần làm tiếp theo của chúng ta tại đây đặt toàn bộ công thức tính toán thành 1 function duy nhất có tên là get-boot:
Function này sẽ có tham số để lấy tên của máy tính, giá trị mặc định sẽ là máy local:
Bên cạnh đó là script Process để chặn giá trị Computer Name, về cơ bản khi bất kỳ giá trị nào được truyền vào đây, biến $computername sẽ tiếp tục truyền giá trị tương ứng vào đó. Bên cạnh đó, những thành phần khác sẽ được sử dụng nếu không có giá trị hoặc tương đương với computername:
Bên trong đoạn script Process là công thức GetWmiObject – được áp dụng để xác định tên của máy tính remote:
Mặt khác, chúng ta còn có 1 vài cặp hashtable tại đây. Thuộc tính CSName không dễ sử dụng, nên cần phải thay thế bằng giá trị mới tạo có tên là Computername. Và bên cạnh đó là 1 thuộc tính khác – LastBoot, có chức năng lưu trữ giá trị LastBootUpTime qua hàm ConvertToDateTime(). Cuối cùng là thuộc tính Uptime – dưới dạng đối tượng TimeSpan, tương ứng với khoảng thời gian hệ thống đã hoạt động:
Nếu chúng ta thử nghiệm trên local (ví dụ: không chỉ định rõ giá trị tên máy tính), thì hệ thống sẽ áp dụng trực tiếp đối với giá trị tên máy tính local ở chế độ mặc định. Ví dụ như sau:
Với những gì chúng tôi đã trình bày trong phần 1 của bài viết, tại mục Tắt hoặc khởi động lại server, các bạn có thể lưu lại tên của server trong 1 file text, áp dụng đối với những thành phần đã được ping, sau đó truyền những giá trị tên đó vào function get-boot:
Ảnh chụp màn hình trên đã chỉ ra danh sách các máy tính remote, tổng thời gian hoạt động cũng như thời điểm khởi động lại gần đây nhất.
9. Thu thập thông tin Service Pack:
Trên thực tế, có khá nhiều lý do chúng ta cần phải biết về thông tin service pack của server. Và để làm việc này, các bạn sẽ áp dụng WMI và class Win32_Operating System, các thuộc tính được dùng ở đây bao gồm: ServicePackMajorVersion (với các giá trị chính là 0, 1 hoặc 2), ServicePackMinorVersion, CSDVersion (thường hiển thị dưới dạng Service Pack 1)...
Cụ thể, chúng ta sẽ bắt đầu bằng việc dùng Get-WmiObject và class Win32_operatingsystem để thu thập thông tin trong hệ thống từ PowerShell. Những thuộc tính cần quan tâm nhất ở đây là: CSName (tên máy tính), Caption (hệ điều hành), CSDversion và ServicePackMajorVersion.
Cú pháp chính được sử dụng ở đây có dạng:
Nhìn vào ảnh chụp màn hình trên, chúng ta có thể thấy rằng hệ điều hành Windows 7 này không sử dụng bất kỳ phiên bản Service Pack nào, do vậy giá trị ServicePackMajorVersion là 0, còn phần CSDVersion thì để trống. Nhưng việc chính của chúng ta tại đây là tạo 1 function mới có tên là Get-SP, việc đầu tiên cần làm là lấy giá trị về tên máy tính, mặc định là máy local:
Tiếp theo là đoạn script Process, nếu 1 giá trị tên máy tính được truyền vào thì biến $computername sẽ khởi tạo giá trị cho đối tương đó. Phần chính của cú pháp này là class Get-Wmiobject/Win32_operatingsystem.
Sau đó, các bạn cần triển khai 1 cặp Hashtable, ở đây chúng tôi chọn thuộc tính CSName và gán tới thuộc tính khác có tên là ComputerName. Tương tự như vậy là thuộc tính Caption - Operating System. Thay vì việc dùng CSDVersion thì chúng ta sử dụng SPName, cuối cùng là Version thay cho ServicePackMajorVersion.
Và đây là function đầy đủ của chúng ta khi hoạt động trên local:
Khi hoàn tất, chúng ta đã sẵn sàng cho việc lọc danh sách tên máy tính từ file text, việc còn lại của chúng ta chỉ là vấn đề ping và truyền tham số tên máy tính tương ứng vào function get-sp vừa tạo. Kết quả của chúng ta tại đây sẽ trông giống như sau:
Các bạn có thể thấy rằng máy tính CHI-DC02 thiếu mất thông tin Service Pack 1, được phát hành cho bản Server 2008 R2.
10. Xóa file không cần thiết trong hệ thống
Để thực hiện việc này, chúng ta sẽ sử dụng lệnh cmdlet Get-ChildItem, với alias tương ứng là dir. Xét về mặt kỹ thuật. Cách tốt nhất là so sánh với thuộc tính LastWriteTime của file hoặc thư mục trong quãng thời gian nhất định. Nếu LastWriteTime vượt quá khoảng thời gian nhất định, thì đó chính là file cần xóa.
Trong khi LastWriteTime chỉ định rõ thời gian chỉnh sửa file lần cuối cùng, và LastAccessTime có thể được xác định hoặc thay đổi bằng ứng dụng trong hệ thống, chẳng hạn như phần mềm bảo mật hoặc chương trình chống phân mảnh – Defragment. Do vậy, LastAccessTime không thực sự chính xác đối với thời điểm sử dụng file và được xác định hành động đó được thực hiện bởi người dùng hay phần mềm.
Sau khi đã tìm kiếm được những file cần xóa bỏ, các bạn có thể chuyển hết tất cả thành Remove-Item. Bởi vì cú pháp Remove-Item hỗ trợ -WhatIf và -Confirm, cho nên chúng ta sẽ thực thi lệnh với những tham số đó, lưu kết quả thành file text, và cuối cùng là quyết định áp dụng lệnh xóa với danh sách file.
Tuy nhiên, cách tối ưu hơn vẫn là việc sử dụng đoạn mã script. Với cách làm này, người dùng có thể gán thêm chức năng lưu file log và hỗ trợ whatif để xác nhận có thực sự muốn xóa file hay không. Dưới đây là ví dụ về 1 function mẫu hoàn thiện có tên là Remove-OldFiles.ps1. Và đây là 1 phần của đoạn mã đó:
Đây có thể coi là 1 đoạn mã nâng cao, với cấu trúc cmdlet binding được thiết lập thành SupportShouldProcess=$True. Có nghĩa rằng, nếu người dùng chỉ định tham số -whatif khi chạy script thì -whatif sẽ được lựa chọn bởi Remove-Item (vì có cơ chế hỗ trợ -whatif).
Các bạn cần lưu ý rằng tham số có tên là $Cutoff – với giá trị mặc định ban đầu là 180 ngày kể từ thời điểm hiện tại, nhưng người dùng vẫn có thể thay đổi và được coi là 1 đối tượng có dạng date – time khác trong hệ thống.
Phần thân của đoạn mã:
Cấu trúc này sẽ thực hiện việc liệt kê danh sách đệ quy của đường dẫn được chỉ định, bên cạnh đó chức năng này cũng sẽ tìm tất cả các file với LastWriteTime ngắn hơn $cutoff, sau đó sẽ lưu danh sách này thành biến có tên là files sử dụng tham số chung - outvariable. Và cuối cùng, bất kỳ file nào với đối tượng thuộc tính where được chọn sẽ được đẩy tới Remove-Item.
Giả sử rằng hệ thống đã tìm được các file đáp ứng điều kiện xóa bỏ, file XML sẽ được tạo và lưu trữ tại thư mục hiện thời, và file đó sẽ có dạng Delete-yyyyMMddhhmm.xml, với yyyyMMddhhmm là timestamp hiện thời, sắp xếp theo định dạng: year – month – day – hour – minute. Và đây là phần mã chính của chức năng trên:
Tiếp theo, chúng ta sẽ cho chạy đoạn mã đó, giả sử rằng người sử dụng muốn xóa thư mục public của file server trên máy tính remote Chicago – có tên là chi-fp01, 1 yêu cầu khác là xóa tất cả các file có khoảng thời gian lâu lơn ngày cutoff. Và đây là câu lệnh chính, đi kèm với thông báo về kết quả:
Lưu ý rằng chúng ta có thể thấy đường dẫn của file XML đã được tạo, ví dụ tại đây đã có 4 file được xóa. Và để thực hiện, các bạn hãy import nội dung file XML vào biến $data và xem 4 thành phần đầu tiên trong $data:
Chúc các bạn thành công!