Trong bài viết này, chúng ta sẽ học cách sử dụng cmdlet Invoke-Command để chạy lệnh hoặc kịch bản PowerShell từ xa. Bạn có thể dùng PowerShell để chạy lệnh từ xa trên một hoặc nhiều máy tính trong mạng. Cmdlet Invoke-Command sử dụng tính năng quản trị từ xa từ PowerShell Remoting. PowerShell Remoting cho phép bạn kết nối từ xa đến phiên PowerShell trên máy tính qua dịch vụ WinRM (Windows Remote Management) và giao thức Web Services for Management (WS-Management). Dịch vụ này cung cấp khả năng thiết lập phiên PowerShell từ xa và chạy mã của bạn.
Cấu hình WinRM cho PowerShell Remoting
PowerShell Remoting dùng HTTP (Cổng TCP/5985) hoặc HTTPS (Cổng TCP/5986) để giao tiếp giữa các máy tính. Mặc định dùng giao thức HTTP, nhưng ngay cả lưu lượng này cũng được mã hóa bằng AES-56 (tuy nhiên, vẫn có nguy cơ tấn công man-in-the-middle). Xác thực Kerberos hoặc NTLM cũng có thể dùng.
WinRM phải chạy trên máy tính từ xa bạn sẽ kết nối. Kiểm tra trạng thái dịch vụ WinRM:
Get-Service -Name "*WinRM*" | fl
Nếu dịch vụ không chạy, hãy khởi động nó:
Enable-PSRemoting
WinRM has been updated to receive requests. WinRM service started. WinRM is already set up for remote management on this computer.
Lệnh này sẽ khởi động dịch vụ WinRM (và đặt nó khởi động tự động), đặt cài đặt winrm mặc định, và thêm quy tắc ngoại lệ vào Windows Firewall. Lệnh Enable-PSRemoting –Force bật WinRM mà không nhắc người dùng.
Sau đó bạn có thể kết nối từ xa với máy tính bằng PowerShell Remoting.
Lưu ý PowerShell Remoting không hoạt động mặc định nếu loại mạng của bạn đặt là Public. Lúc đó lệnh trả về lỗi:
Set-WSManQuickConfig : ... WinRM firewall exception will not work since one of the network connection types on this machine is set to Public. Change the network connection type to either Domain or Private and try again.
Bạn phải thay đổi vị trí mạng thành Private hoặc dùng lệnh:
Enable-PSRemoting –SkipNetworkProfileCheck.
Cũng bật quy tắc Windows Defender Firewall cho phép truy cập WinRM trong mạng công cộng. Bạn có thể bật quy tắc tường lửa bằng GPO hoặc PowerShell:
Set-NetFirewallRule -Name 'WINRM-HTTP-In-TCP' -RemoteAddress Any
Để kiểm tra kết nối đến máy tính từ xa qua PowerShell Remoting, chạy lệnh sau:
Test-WsMan compname1
Nếu bạn không có miền Active Directory hoặc truy cập máy tính qua PowerShell Remoting bằng địa chỉ IP, trong trường hợp này giao thức NTLM dùng cho xác thực. Khi dùng NTLM, lỗi sau xuất hiện nếu bạn thử chạy Invoke-Command:
[192.168.1.201] Connecting to remote server 192.168.1.102 failed with the following error message: The WinRM client cannot process the request. Default authentication may be used with an IP address under the following conditions: the transport is HTTPS or the destination is in the TrustedHosts list, and explicit credentials are provided. Use winrm.cmd to configure TrustedHosts. Note that computers in the TrustedHosts list might not be authenticated. + FullyQualifiedErrorId: CannotUseIPAddress,PSSessionStateBroken
Để xác thực NTLM hoạt động đúng trên máy tính bạn dùng để kết nối, hãy làm thêm: cấp chứng chỉ SSL cho WinRM hoặc thêm tên máy/IP vào danh sách trusted hosts:
Set-Item wsman:\localhost\Client\TrustedHosts -value 192.168.1.201
Hoặc cho phép kết nối đến tất cả máy tính (không khuyến khích, vì đây là nhược điểm của NTLM — không hỗ trợ xác thực lẫn nhau).
Set-Item wsman:\localhost\Client\TrustedHosts -value *
Cùng cài đặt phải áp dụng trên máy chủ từ xa.
Để hiển thị danh sách trusted hosts, chạy lệnh:
Get-Item WSMan:\localhost\Client\TrustedHosts
Để áp dụng thay đổi, khởi động lại WinRM:
Restart-Service WinRM
Bạn cũng có thể bật và cấu hình WinRM bằng Group Policies.
Cách chạy lệnh PowerShell từ xa dùng Invoke-Command?
Cmdlet Invoke-Command cho phép chạy lệnh trên một hoặc nhiều máy tính từ xa.
Ví dụ, để chạy một lệnh đơn trên máy tính từ xa, dùng:
Invoke-Command -ComputerName dc01 -ScriptBlock {$PSVersionTable.PSVersion}
Lệnh này sẽ hiển thị phiên bản PowerShell cài trên máy tính từ xa, tên được chỉ định trong tham số -ComputerName. Nhập lệnh cần chạy trên máy tính từ xa trong khối -ScriptBlock {[cmdlet]}.
Mặc định, lệnh gửi qua Invoke-Command thực thi với tư cách người dùng hiện tại trên máy tính từ xa. Nếu muốn chạy với tư cách người dùng khác, yêu cầu thông tin đăng nhập và lưu vào biến:
$cred = Get-Credential
Invoke-Command -ComputerName dc01 -Credential $cred -ScriptBlock {Get-NetAdapter}
Lệnh PowerShell này hiển thị danh sách giao diện mạng trên máy tính từ xa:
Bạn có thể nhập nhiều hơn một lệnh trong ScriptBlock, phân cách bằng dấu chấm phẩy. Ví dụ, lệnh sau sẽ hiển thị múi giờ hiện tại và thay đổi sang múi giờ khác:
Invoke-Command -Computername dc01 -ScriptBlock {Get-TimeZone| select DisplayName;Set-TimeZone -Name "Central Europe Standard Time”}
Invoke-Command cho phép không chỉ chạy lệnh đơn lẻ, mà còn chạy kịch bản PowerShell. Để làm vậy, dùng đối số -FilePath (thay vì –ScriptBlock). Trong trường hợp này, bạn chỉ định đường dẫn đến tệp PS1 kịch bản cục bộ trên máy tính của bạn (bạn không cần sao chép tệp kịch bản sang máy tính đích từ xa):
Invoke-Command -ComputerName DC01 -FilePath C:\PS\Scripts\CheckSMBversion.ps1
Cách dùng Invoke-Command chạy lệnh trên nhiều máy tính cùng lúc?
Bạn có thể dùng Invoke-Command để chạy lệnh trên nhiều máy tính từ xa song song (đồng thời).
Trong trường hợp đơn giản nhất, tên máy tính chạy lệnh PowerShell được phân cách bằng dấu phẩy:
Invoke-Command server1, server2, server3 -ScriptBlock {get-date}
Bạn có thể đặt danh sách máy tính vào biến (mảng):
$servers = @("server1","server2","server3")
Invoke-Command -ScriptBlock { get-date} -ComputerName $servers
Hoặc lấy từ tệp văn bản:
Invoke-Command -ScriptBlock {Restart-Service spooler} -ComputerName(Get-Content c:\ps\servers.txt)
Bạn cũng có thể lấy danh sách máy tính trong AD dùng cmdlet Get-ADComputer từ module Active Directory cho PowerShell:
Để chạy lệnh trên tất cả máy chủ Windows Server trong miền, dùng mã PowerShell sau:
$computers = (Get-ADComputer -Filter 'OperatingSystem -like "*Windows server*" -and Enabled -eq "true"').Name
Invoke-Command -ComputerName $computers -ScriptBlock {Get-Date} -ErrorAction SilentlyContinue
Nếu máy tính tắt hoặc không khả dụng, kịch bản không dừng nhờ tham số SilentlyContinue và tiếp tục chạy trên máy tính khác.
Để hiểu kết quả đến từ máy tính nào, dùng biến môi trường PSComputerNamee.
$results = Invoke-Command server1, server2, server3 -ScriptBlock {get-date}
$results | Select-Object PSComputerName, DateTime
Khi chạy lệnh dùng Invoke-Command trên nhiều máy tính, nó chạy đồng thời. Invoke-Command có giới hạn tối đa số máy tính quản lý cùng lúc (số PSSessions đồng thời giới hạn). Giới hạn này đặt trong tham số ThrottleLimit (giá trị mặc định 32). Nếu muốn chạy lệnh trên nhiều hơn 32 máy tính (128, ví dụ), dùng –ThrottleLimit 128 (tuy nhiên, máy tính của bạn sẽ có tải cao hơn để thiết lập số lượng lớn PSSessions).
Để chạy lệnh trên máy tính từ xa qua Invoke-Command ở nền, thuộc tính đặc biệt –AsJob được dùng. Khi đó kết quả lệnh không trả về console. Để lấy kết quả, dùng cmdlet Receive-Job.