IT 전문가들은 주로 로컬 컴퓨터에서 작업하는 것이 아닙니다. PowerShell의 Invoke-Command cmdlet을 사용하면 그렇게 할 필요가 없습니다! 이 cmdlet을 사용하면 로컬 컴퓨터에서 작업하는 것처럼 코드를 작성할 수 있습니다.
PowerShell Remoting 기능을 사용하여 Invoke-Command
cmdlet은 PSSession 내에서 코드를 실행할 수 있는 일반적으로 사용되는 PowerShell cmdlet입니다. 이 PSSession은 이전에 New-PSSession
cmdlet로 생성된 것이거나 임시 세션을 빠르게 생성하고 제거할 수도 있습니다.
관련: PowerShell Remoting: 최종 가이드
Invoke-Command을 PowerShell의 psexec로 생각해보세요. 구현 방식은 다르지만 개념은 동일합니다. 조금의 코드나 명령을 가져와서 원격 컴퓨터에서 “로컬”로 실행합니다.
하지만 Invoke-Command
를 사용하려면 원격 컴퓨터에서 PowerShell Remoting이 활성화되어 있고 사용 가능해야 합니다. 기본적으로 Windows Server 2012 R2 이상의 모든 기계에는 이 기능이 활성화되어 있으며 적절한 방화벽 예외도 제공됩니다. 아직 Server 2008 기계를 사용하고 있다면 Remoting을 설정하는 여러 가지 방법이 있지만, 원격 컴퓨터에서 winrm quickconfig
또는 Enable-PSRemoting
을 실행하는 것이 간편합니다.
“ad-hoc command”라는 의미는 새로운 PSSession을 만들 필요가 없는 명령을 Invoke-Command와 함께 사용하는 방법을 보여주기 위해 원격 Windows Server 2012 R2 이상의 도메인에 가입된 컴퓨터가 있다고 가정해 보겠습니다. 워크그룹 컴퓨터에서 작업하는 경우에는 약간 복잡해집니다. PowerShell 콘솔을 열어서 Invoke-Command
를 입력한 다음 Enter 키를 누르겠습니다.
I’m immediately asked to provide a scriptblock. The scriptblock is the code that we’re going to run on the remote computer.
스크립트 블록 내부의 코드가 원격 컴퓨터에서 실행되는 것을 증명하기 위해 단순히 hostname
명령을 실행해 보겠습니다. 이 명령은 실행되는 컴퓨터의 호스트 이름을 반환합니다. 로컬 컴퓨터에서 hostname
을 실행하면, 이름이 반환됩니다.
이제 동일한 코드를 포함한 스크립트 블록을 Invoke-Command
에 전달해 보겠습니다. 하지만 그 전에 필요한 매개변수인 ComputerName
을 빼먹었습니다. Invoke-Command
에게 어떤 원격 컴퓨터에서 이 명령을 실행할지 알려줘야 합니다.
hostname
의 출력이 이제 원격 컴퓨터의 이름 WEBSRV1
이 되었습니다. WEBSRV1에서 코드를 실행했습니다. 단일 원격 컴퓨터에 코드를 스크립트 블록 내부에서 실행하고 전달하는 것은 Invoke-Command
의 가장 간단한 응용 프로그램이지만, 더 많은 작업을 수행할 수 있습니다.
로컬 변수를 원격 스크립트 블록에 전달하기
스크립트 내에서는 단일 Invoke-Command 참조를 사용하지 않을 것입니다. 스크립트는 수십 줄로 구성되어 있으며 변수가 정의되어 있고, 모듈에서 함수가 정의되어 있습니다. 중괄호로 코드를 감싸는 것만으로는 무해해 보일 수 있지만, 실제로는 해당 코드가 실행되는 범위 전체가 변경됩니다. 결국, 해당 코드를 원격 컴퓨터로 보내고 있습니다. 원격 컴퓨터는 스크립트 블록에 있는 것 이외에 로컬 컴퓨터에 있는 모든 코드에 대해 알지 못합니다.
예를 들어, 컴퓨터 이름과 파일 경로 매개변수를 갖는 함수가 있다고 가정해 봅시다. 이 함수의 목적은 원격 컴퓨터에 있는 소프트웨어 설치 프로그램을 실행하는 것입니다. 컴퓨터 이름과 “로컬” 파일 경로를 이미 원격 컴퓨터에 위치한 설치 프로그램에 전달할 수 있습니다.
아래 함수는 합리적인 것처럼 보입니다. 실행해 보겠습니다.
하지만 앰퍼샌드 연산자를 사용했기 때문에 애매한 오류 메시지로 실패합니다. 코드 자체에는 문제가 없지만, 함수 매개변수로 값을 전달해도 $InstallerFilePath
가 비어 있어 실패합니다. 이를 확인하기 위해 앰퍼샌드를 Write-Host
로 대체하여 테스트할 수 있습니다.
새 함수 라인: Invoke-Command -ComputerName $ComputerName -ScriptBlock { Write-Host "Installer path is: $InstallerFilePath" }
$InstallerFilePath
의 값은 없습니다. 변수가 원격 컴퓨터로 전달되지 않았기 때문에 변수가 확장되지 않았습니다. 로컬로 정의된 변수를 원격 스크립트 블록에 전달하는 두 가지 옵션이 있습니다. 스크립트 블록 내에서 변수 이름 앞에 $using:
을 붙이거나 Invoke-Command
매개변수 ArgumentList
를 사용할 수 있습니다. 둘 다 살펴보겠습니다.
ArgumentList 매개변수
로컬 변수를 원격 스크립트 블록에 전달하는 한 가지 방법은 Invoke-Command
의 ArgumentList
매개변수를 사용하는 것입니다. 이 매개변수를 사용하면 로컬 변수를 매개변수에 전달하고 스크립트 블록에서 로컬 변수 참조를 플레이스홀더로 대체할 수 있습니다.
ArgumentList
매개변수에 로컬 변수를 전달하는 것은 쉽습니다.
문제는 스크립트 블록 내에서 변수를 어떻게 구조화해야 하는지입니다. { & $InstallerPath }
대신에 { & $args[0] }
또는 {param($foo) & $foo }
로 변경해야 합니다. 두 가지 방법은 동일하게 작동하지만 어떤 것을 사용해야 할까요?
ArgumentList
매개변수는 개체 컬렉션입니다. 개체 컬렉션을 사용하면 한 번에 하나 이상의 개체를 전달할 수 있습니다. 이 경우에는 하나만 전달하고 있습니다.
Invoke-Command cmdlet을 실행하면 해당 컬렉션을 가져와 스크립트 블록에 주입하여 사실상 $args
라는 배열로 변환합니다. $args -eq ArgumentList
를 기억하세요. 이 시점에서 컬렉션의 각 인덱스를 배열처럼 참조할 수 있습니다. 우리의 경우, 컬렉션에는 하나의 요소만 있었기 때문에($InstallerFilePath
) 이는 $args[0]
으로 “변환”되어 첫 번째 인덱스를 의미합니다. 그러나 더 많은 요소가 있는 경우 $args[1]
, $args[2]
와 같이 참조해야 합니다.
게다가, 스크립트 블록 변수에 더 좋은 변수 이름을 할당하고 싶다면, 함수처럼 스크립트 블록에 매개변수를 추가할 수도 있습니다. 결국 스크립트 블록은 익명 함수입니다. 스크립트 블록 매개변수를 생성하려면 매개변수의 이름으로 param 블록을 만듭니다. 생성한 후에는 아래와 같이 스크립트 블록에서 해당 매개변수를 참조합니다.
이 경우, ArgumentList
컬렉션의 요소들은 정의된 매개변수와 일치하는 순서로 “매핑”됩니다. 매개변수 이름은 중요하지 않으며, 순서가 중요합니다. Invoke-Command
는 ArgumentList
컬렉션의 첫 번째 요소를 가져와 첫 번째 매개변수와 값을 매핑하고, 두 번째, 세 번째 요소에 대해서도 동일한 작업을 수행합니다.
$Using 구문
$using
구문은 로컬 변수를 원격 스크립트 블록에 전달하는 또 다른 인기있는 방법입니다. 이 구문을 사용하면 기존의 로컬 변수를 재사용할 수 있으며 변수 이름 앞에 $using:
을 붙이기만 하면 됩니다. $args
컬렉션을 걱정할 필요가 없으며 매개변수 블록을 추가할 필요도 없습니다.
PowerShell의 $using
구문은 훨씬 간단하지만 Pester 학습을 하게되면 ArgumentList
가 도움이 될 것입니다.
Invoke-Command 및 New-PSSession
기술적으로 이 게시물은 Invoke-Command에 대해서만 다룹니다. 그러나 유용성을 보여주기 위해 간단히 New-PSSession
명령에 대해서도 언급해야 합니다. 이전에 언급했듯이 Invoke-Command
는 “임시” 명령을 사용하거나 기존 세션을 사용할 수 있습니다.
이 게시물에서는 원격 컴퓨터에서 “임시” 명령만 실행했습니다. 새 세션을 생성하고 코드를 실행한 다음 해체하는 방식입니다. 이는 일회성 상황에는 좋지만 동일한 컴퓨터에서 수십 개의 명령을 수행하는 경우에는 그렇지 않습니다. 이 경우에는 New-PSSession
으로 미리 PSSession을 생성하여 기존 세션을 재사용하는 것이 좋습니다.
어떤 명령을 실행하기 전에 먼저 New-PSSession
으로 PSSession을 생성해야합니다. 이를 위해 $session = New-PSSession -ComputerName WEBSRV1
을 실행하면 됩니다. 이는 서버에서 원격 세션을 생성하고 로컬 머신에서 해당 세션에 대한 참조를 만듭니다. 이 시점에서 ComputerName
참조를 Session
으로 대체하고 Session
을 저장된 $session
변수로 지정할 수 있습니다.
실행하면 세션이 이미 구축되었기 때문에 성능이 더 빠르다는 것을 알 수 있습니다. 그러나 작업이 완료되면 Remove-PSSession
으로 열린 세션을 제거하는 것이 중요합니다.
요약
Invoke-Command PowerShell cmdlet은 가장 일반적이고 강력한 cmdlet 중 하나입니다. 개인적으로 거의 모든 cmdlet 중에서 가장 많이 사용하는 것입니다. 사용하기 쉽고 원격 컴퓨터에서 모든 코드를 실행할 수 있는 능력은 굉장히 강력하며, 맨 위에서부터 아래로 배우는 것을 권장하는 명령입니다!