이 글에서는 Invoke-Command cmdlet을 사용하여 원격 컴퓨터에서 PowerShell 명령어나 스크립트를 실행하는 방법을 알아봅니다. PowerShell을 활용하면 네트워크상의 한 대 또는 여러 대의 컴퓨터에서 원격으로 명령을 실행할 수 있습니다. Invoke-Command는 PowerShell Remoting의 원격 관리 기능을 기반으로 작동하며, PowerShell Remoting은 WinRM(Windows Remote Management) 서비스와 WS-Management(Web Services for Management) 프로토콜을 통해 원격 컴퓨터의 PowerShell 세션에 연결하고 코드를 실행할 수 있게 해줍니다.
PowerShell Remoting을 위한 WinRM 구성
PowerShell Remoting은 컴퓨터 간 통신에 HTTP(TCP/5985 포트) 또는 HTTPS(TCP/5986 포트)를 사용합니다. 기본값은 HTTP이지만, 이 트래픽 역시 AES-256으로 암호화됩니다(다만 중간자 공격(man-in-the-middle)의 위험은 존재합니다). 인증에는 Kerberos 또는 NTLM 방식이 사용될 수 있습니다.
원격으로 연결하려는 컴퓨터에서는 WinRM 서비스가 반드시 실행 중이어야 합니다. 먼저 WinRM 서비스 상태를 확인해 보세요.
Get-Service -Name "*WinRM*" | fl
서비스가 실행 중이 아니라면 아래 명령으로 활성화합니다.
Enable-PSRemoting
WinRM has been updated to receive requests. WinRM service started. WinRM is already set up for remote management on this computer.
이 명령은 WinRM 서비스를 시작하고(자동 시작으로 설정), 기본 WinRM 설정을 적용하며, Windows 방화벽에 예외 규칙을 추가합니다. Enable-PSRemoting –Force처럼 –Force 옵션을 붙이면 사용자 확인 없이 바로 WinRM을 활성화할 수 있습니다.
설정이 완료되면 PowerShell Remoting을 통해 원격 컴퓨터에 접속할 수 있습니다.
단, 네트워크 유형이 공용(Public)으로 설정되어 있으면 PowerShell Remoting은 기본적으로 작동하지 않으며, 다음과 같은 오류가 반환됩니다.
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.
이 경우 네트워크 위치를 개인(Private) 또는 도메인(Domain)으로 변경하거나, 다음 명령을 사용하여 공용 네트워크에서도 Remoting을 허용할 수 있습니다.
Enable-PSRemoting –SkipNetworkProfileCheck
추가로, 공용 네트워크에서 WinRM 접근을 허용하는 Windows Defender 방화벽 규칙도 활성화해야 합니다. GPO(그룹 정책)나 PowerShell로 방화벽 규칙을 켤 수 있습니다.
Set-NetFirewallRule -Name 'WINRM-HTTP-In-TCP' -RemoteAddress Any
PowerShell Remoting을 통한 원격 컴퓨터 연결을 테스트하려면 다음 명령을 실행합니다.
Test-WsMan compname1
Active Directory 도메인 환경이 아니거나 IP 주소로 원격 컴퓨터에 접속하는 경우에는 NTLM 프로토콜이 인증에 사용됩니다. NTLM 환경에서 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
연결에 사용하는 컴퓨터에서 NTLM 인증이 정상적으로 작동하도록 하려면, WinRM용 SSL 인증서를 발급하거나 해당 호스트 이름/IP 주소를 신뢰할 수 있는 호스트(TrustedHosts) 목록에 추가해야 합니다.
Set-Item wsman:\localhost\Client\TrustedHosts -value 192.168.1.201
모든 컴퓨터로의 연결을 허용할 수도 있지만, NTLM은 상호 인증(mutual authentication)을 지원하지 않기 때문에 보안상 권장되지 않습니다.
Set-Item wsman:\localhost\Client\TrustedHosts -value *
동일한 설정을 원격 호스트에도 적용해야 합니다. 현재 등록된 신뢰 호스트 목록을 확인하려면 다음 명령을 사용하세요.
Get-Item WSMan:\localhost\Client\TrustedHosts
변경 사항을 적용하려면 WinRM 서비스를 재시작합니다.
Restart-Service WinRM
참고로 그룹 정책(GPO)을 통해서도 WinRM을 활성화하고 구성할 수 있습니다.
Invoke-Command로 원격에서 PowerShell 명령어 실행하기
Invoke-Command cmdlet을 사용하면 한 대 이상의 원격 컴퓨터에서 명령을 실행할 수 있습니다.
예를 들어 원격 컴퓨터에서 단일 명령을 실행하려면 다음과 같이 입력합니다.
Invoke-Command -ComputerName dc01 -ScriptBlock {$PSVersionTable.PSVersion}
이 명령은 -ComputerName 매개변수에 지정된 원격 컴퓨터에 설치된 PowerShell 버전을 표시합니다. 원격에서 실행할 명령은 -ScriptBlock {[cmdlet]} 블록 안에 작성합니다.
기본적으로 Invoke-Command로 전달된 명령은 원격 컴퓨터에서 현재 사용자 계정으로 실행됩니다. 다른 사용자 권한으로 실행하려면 자격 증명을 요청받아 변수에 저장한 뒤 사용하면 됩니다.
$cred = Get-Credential
Invoke-Command -ComputerName dc01 -Credential $cred -ScriptBlock {Get-NetAdapter}
위 명령은 원격 컴퓨터의 네트워크 어댑터 목록을 출력합니다.
ScriptBlock 안에는 세미콜론(;)으로 구분하여 여러 개의 명령을 넣을 수도 있습니다. 예를 들어 다음 명령은 현재 시간대를 확인한 후 다른 시간대로 변경합니다.
Invoke-Command -Computername dc01 -ScriptBlock {Get-TimeZone| select DisplayName;Set-TimeZone -Name "Central Europe Standard Time"}
Invoke-Command는 개별 명령뿐만 아니라 PowerShell 스크립트(.ps1 파일) 실행도 지원합니다. 이때는 –ScriptBlock 대신 -FilePath 인수를 사용하며, 로컬 컴퓨터에 있는 PS1 스크립트 파일의 경로를 지정합니다(스크립트 파일을 대상 원격 컴퓨터로 복사할 필요가 없습니다).
Invoke-Command -ComputerName DC01 -FilePath C:\PS\Scripts\CheckSMBversion.ps1
Invoke-Command로 여러 컴퓨터에서 동시에 명령 실행하기
Invoke-Command를 사용하면 여러 원격 컴퓨터에서 명령을 병렬(동시)로 실행할 수 있습니다.
가장 간단한 방법은 명령을 실행할 컴퓨터 이름을 쉼표로 나열하는 것입니다.
Invoke-Command server1, server2, server3 -ScriptBlock {get-date}
컴퓨터 목록을 변수(배열)에 담아 사용할 수도 있습니다.
$servers = @("server1","server2","server3")
Invoke-Command -ScriptBlock { get-date} -ComputerName $servers
텍스트 파일에서 목록을 가져오는 방법도 있습니다.
Invoke-Command -ScriptBlock {Restart-Service spooler} -ComputerName(Get-Content c:\ps\servers.txt)
AD for PowerShell 모듈의 Get-ADComputer cmdlet을 사용하면 Active Directory에서 컴퓨터 목록을 가져올 수 있습니다. 예를 들어 도메인 내 모든 Windows Server 호스트에서 명령을 실행하려면 다음 PowerShell 코드를 사용합니다.
$computers = (Get-ADComputer -Filter 'OperatingSystem -like "*Windows server*" -and Enabled -eq "true"').Name
Invoke-Command -ComputerName $computers -ScriptBlock {Get-Date} -ErrorAction SilentlyContinue
컴퓨터가 꺼져 있거나 접속할 수 없더라도 SilentlyContinue 매개변수 덕분에 스크립트가 중단되지 않고 나머지 컴퓨터에서 계속 실행됩니다.
결과가 어떤 컴퓨터에서 나왔는지 확인하려면 PSComputerName 변수를 활용하세요.
$results = Invoke-Command server1, server2, server3 -ScriptBlock {get-date}
$results | Select-Object PSComputerName, DateTime
여러 컴퓨터에서 Invoke-Command로 명령을 실행하면 동시에 처리됩니다. 다만 Invoke-Command에는 동시에 관리할 수 있는 최대 컴퓨터 수 제한(동시 PSSession 수 제한)이 있으며, 이 값은 ThrottleLimit 매개변수로 설정됩니다(기본값 32). 32대 이상, 예를 들어 128대의 컴퓨터에서 명령을 실행하려면 –ThrottleLimit 128을 지정하면 됩니다(단, 다수의 PSSession을 생성하므로 로컬 컴퓨터 부하가 커질 수 있습니다).
원격 컴퓨터에서 백그라운드로 명령을 실행하려면 특수 매개변수인 –AsJob을 사용합니다. 이 경우 명령 결과가 콘솔로 바로 반환되지 않으며, 결과를 확인하려면 Receive-Job cmdlet을 사용해야 합니다.