Windows PowerShell가장 뛰어난 인벤토리 도구

Don Jones

목차

도구 자세히 알아보기
시험 사용
컴퓨터 집합
도구의 용도
도구 사용자 지정
강력한 DIY 도구

지난 몇 편의 이 칼럼 기사에 걸쳐 Windows PowerShell을 사용하여 컴퓨터 인벤토리 정보를 수집하는 다양한 기법을 소개했습니다. 이번 달 기사에서는 이러한 모든 기법을 취합하여 WMI(Windows Management Instrumentation)에서 원하는 정보를 수집할 수 있는 실행 가능한 도구를 작성해 보도록 하겠습니다.

이 도구는 컴퓨터 이름의 텍스트 목록이나 Active Directory에서 쿼리한 컴퓨터 이름을 사용하도록 설계됩니다.

도구 자세히 알아보기

스크립트 구조의 요소를 하나씩 살펴보고 몇 가지 원리를 알아보면서 스크립트 자체(그림 1)부터 설명하도록 하겠습니다. 여기서 설명하는 기법 중 일부는 다소 생소하거나 부적절해 보일 수도 있지만 다양한 기법을 소개하기 위해 모두 포함했습니다.

그림 1 스크립트

1. function Get-WmiInventory {
2.  param (
3.  $wmiclass = "Win32_OperatingSystem"
4.  )
5.  PROCESS {
6.   $ErrorActionPreference = "SilentlyContinue"
7.   $computer = $_
8.   trap {
9.    $computer | out-file c:\errors.txt -append
10.    set-variable skip ($true) -scope 1
11.    continue
12.   }
13.   $skip = $false
14.   $wmi = Get-WmiObject -class $wmiclass -computer $computer -ea stop
15.   if (-not $skip) {
16.    foreach ($obj in $wmi) {
17.     $obj | Add-Member NoteProperty ComputerName $computer
18.     write $obj
19.    }
20.   }
21.  }
22. }  

1번 줄에는 Get-WmiInventory라는 함수가 있습니다. 바로 아래에는 $wmiclass라는 입력 매개 변수를 정의하고 기본값을 "Win32_OperatingSystem"으로 할당했습니다. PROCESS 스크립트 블록은 이 함수가 파이프라인을 통해 컴퓨터 이름의 컬렉션을 받도록 설계된 필터링 함수임을 나타냅니다. 컴퓨터 이름 정보를 이 함수에 전달하는 방법은 잠시 후에 살펴보겠습니다.

고유한 오류 로깅을 제공할 생각이기 때문에 6번 줄에서 셸의 일반 오류 보고 동작을 해제합니다. 그리고 7번 줄에서는 파이프라인을 통해 함수로 전달된 현재 컴퓨터 이름을 $computer라는 변수로 가져옵니다.

Windows PowerShell Q&A

Q: Windows PowerShell을 사용하여 Windows Server 2008 Server Core를 관리할 수 있습니까?

A: 물론입니다. 셸에 필요한 .NET Framework가 현재 Server Core에서 지원되지 않아 Server Core에 Windows PowerShell을 설치할 수 없다는 이야기를 들으신 것 같군요. 하지만 문제될 것 없습니다. 실력 있는 관리자라면 서버에 설치하지 않고도 도구를 지원하는 경우가 많으며 Windows PowerShell도 예외가 아닙니다. 대신 Windows PowerShell을 클라이언트 컴퓨터에 설치한 후 Windows PowerShell을 통해 WMI(Windows Management Instrumentation), Active Directory 등의 기술을 활용하여 Server Core를 원격으로 관리할 수 있습니다. 예를 들어 Server Core 기반 도메인 컨트롤러에 있는 Active Directory의 거의 모든 측면을 제자리에서 편리하게 관리할 수 있습니다.

다음으로, 13번 줄에서는 $skip이라는 변수를 만들어 부울 값 False로 설정합니다. 이 변수에 대한 자세한 내용은 잠시 후에 설명하겠습니다. 그리고 14번 줄에서 Get-WmiObject cmdlet를 사용하여 원하는 WMI 정보를 현재 컴퓨터에서 가져옵니다. WMI 검색이 실패할 경우 예외를 생성하도록 셸에 지시하는 –ErrorAction(또는 –EA) 매개 변수를 지정한 것을 알 수 있습니다.

15번 줄에서는 $skip 변수에 아직 False 값이 들어 있는지 확인합니다. False 값이 들어 있으면 16번 줄에서 WMI로부터 검색되는 값을 열거하고 17번 줄에서 각 WMI 개체에 ComputerName 속성을 추가합니다. 이렇게 하면 스크립트가 여러 컴퓨터에서 가져온 WMI 개체를 반환할 때 각 개체의 적절한 속성에 상위 컴퓨터 이름이 레이블로 지정됩니다. 18번 줄은 각 개체를 파이프라인에 출력하여 다른 cmdlet가 개체를 처리하도록 하거나 셸의 서식 지정 하위 시스템이 제어권을 넘겨 받아 개체의 속성 중 일부를 표시하도록 합니다.

하지만 문제가 발생한다면 어떨까요? –EA Stop을 지정했기 때문에 셸은 8번 줄의 트랩을 실행합니다. 먼저 인벤토리 오류를 발생시킨 컴퓨터 이름이 기록되도록 텍스트 파일에 컴퓨터 이름을 씁니다. 그런 다음 10번 줄에서 $skip 변수를 부울 값 True로 설정합니다. 따라서 스크립트가 15번 줄에서 아무 것도 출력하지 않습니다. 이렇게 하지 않으면 컴퓨터에서 인벤토리 오류가 발생할 때마다 이전 컴퓨터의 인벤토리 정보가 다시 출력됩니다.

10번 줄에서는 다른 기법을 사용하여 변수를 설정합니다. 즉, 트랩 자체는 전용(private) 변수 범위이고 $skip 변수를 포함하지 않습니다. 필요한 $skip 변수는 트랩의 상위 범위(한 수준 위)에서 가져옵니다. Set-Variable cmdlet에서 -scope 매개 변수를 사용하여 필요한 $skip 변수가 한 수준 위에 있음을 지정하고 이 변수를 수정할 수 있습니다. Set-Variable cmdlet를 사용하는 경우 변수 이름 앞에 $ 기호를 붙이지 않습니다.

시험 사용

이 스크립트를 간단히 테스트하려면 다음과 같이 파이프라인을 통해 컴퓨터 이름을 하나만 제공해 봅니다.

"localhost" | Get-WmiInventory 

WMI 클래스 이름을 지정하지 않았기 때문에 기본값인 Win32_OperatingSystem이 사용됩니다. 다른 클래스를 지정하려면 다음과 같이 합니다.

"localhost" | Get-WmiInventory "Win32_LogicalDisk"

그림 2에서는 실행 결과를 보여 줍니다. 컴퓨터 이름을 여러 개 제공하려면 다음과 같이 쉼표로 구분된 목록으로 만들면 됩니다.

"localhost","server2","client17" | 
Get-WmiInventory "Win32_LogicalDisk"

fig02.gif

그림 2 Get-WmiInventory 결과

컴퓨터 집합

컴퓨터 수가 많으면 컴퓨터 이름을 하나씩 지정하기가 번거롭습니다. 한 줄에 컴퓨터 이름이 하나씩 있는 텍스트 파일로 된 컴퓨터 이름 목록이 있으면 다음과 같이 파이프라인을 통해 목록의 컴퓨터 이름을 인벤토리 함수에 바로 제공할 수 있습니다.

Get-Content c:\names.txt | Get-WmiInventory "Win32_Service"

다음으로, Get-QADComputer cmdlet를 사용하는 기법이 있습니다. 이 cmdlet는 quest.com/powershell에 있는 무료 Active Directory cmdlet의 일부로 제공됩니다. Active Directory cmdlet를 설치한 후 Windows PowerShell을 열고 다음을 실행해야 합니다.

Add-PSSnapin Quest.ActiveRoles.ADManagement

Get-QADComputer를 실행하면 Active Directory에서 모든 컴퓨터를 검색합니다. 그러나 대규모 도메인에서는 이 작업에 시간이 많이 걸립니다. Help Get-QADComputer를 실행하면 컴퓨터 목록을 필터링하는 옵션을 확인할 수 있습니다. 이러한 옵션을 사용하여 특정 OU(조직 구성 단위)에 있는 컴퓨터만 가져오는 등, 검색 대상을 제한할 수 있습니다. 또한 스크립트를 약간 변경해야 합니다. 7번 줄을 다음과 같이 변경합니다.

$computer = $_

$computer = $_.Name

이렇게 변경하는 이유는 Get-QADComputer로 생성된 개체가 단순한 문자열 개체가 아니기 때문입니다. 컴퓨터 이름은 이 개체의 Name 속성에 저장됩니다.

도구의 용도

이 스크립트의 기본 출력에는 Windows PowerShell 서식 지정 하위 시스템에서 기본적으로 생성하는 모든 형식이 사용됩니다. 예를 들어 Win32_OperatingSystem 클래스의 경우 이 클래스에서 실제로 사용 가능한 정보 중 일부(6개 속성)만 출력됩니다. 이 스크립트의 출력은 파이프라인을 통해 모든 표준 셸 cmdlet에 전달하여 사용자 지정할 수 있습니다. 따라서 빌드 번호와 서비스 팩 정보를 컴퓨터 이름과 함께 표시하려면 다음을 실행합니다.

Get-Content c:\names.txt | Get-WmiInventory | 
Format-List BuildNumber,ServicePackMajor­Version,­ComputerName

설치된 서비스의 인벤토리로 HTML 표를 생성하려면 다음을 실행합니다.

Get-Content c:\names.txt | Get-WmiInventory "Win32_Service" | 
ConvertTo-HTML | Out-File c:\services_inventory.html

논리 디스크 정보의 전체 인벤토리를 CSV(쉼표로 구분된 값) 파일에 쓰려면 다음을 실행합니다.

Get-Content c:\names.txt | 
Get-WmiInventory "Win32_LogicalDisk" | Export-CSV c:\disks.csv

Get-WmiInventory 함수는 단순 텍스트가 아니라 개체를 생성하므로 셸에서 Get-WmiInventory 함수를 사용하면 통계적 경향을 분석하거나 관리 보고서를 작성하거나 중요 정보를 간단히 살펴보는 등의 용도에 맞는 형식으로 데이터를 가져오는 데 큰 도움이 될 수 있습니다.

도구 사용자 지정

현재 스크립트의 단점은 지정한 클래스에서 모든 WMI 개체를 검색한다는 점입니다. 때문에 클래스에 따라 데이터가 너무 방대해질 수 있습니다. 필자의 경우 보통 DriveType 속성이 3인 로컬 디스크만 검색하도록 Win32_LogicalDisk를 제한합니다. 그림 3과 같이 이러한 필터 조건을 포함하도록 스크립트를 쉽게 변경할 수 있습니다. 수정된 스크립트는 다음을 사용하여 호출합니다.

Get-Content c:\names.txt | 
Get-WmiInventory "Win32_LogicalDisk" "DriveType='3'"

그림 3 필터링을 지원하도록 수정된 스크립트

1. function Get-WmiInventory {
2.  param (
3.   $wmiclass = "Win32_OperatingSystem"
4.   $filter = ""
5.  )
6.  PROCESS {
7.   $ErrorActionPreference = "SilentlyContinue"
8.   $computer = $_
9.   trap {
10.    $computer | out-file c:\errors.txt -append
11.    set-variable skip ($true) -scope 1
12.    continue
13.  }
14.  $skip = $false
15.  $wmi = Get-WmiObject -class $wmiclass -computer $computer -ea stop –filter $filter
16.  if (-not $skip) {
17.    foreach ($obj in $wmi) {
18.     $obj | Add-Member NoteProperty ComputerName $computer
19.     write $obj
20.    }
21.   }
22.  }
23. }

변경된 스크립트는 4번 줄에서 $filter라는 새 매개 변수를 추가하여 15번 줄에서 Get-WmiObject의 –filter 매개 변수에 전달합니다. 이 함수를 호출할 때 $filter는 두 번째 매개 변수로 사용되므로 WMI 클래스 이름 뒤에 두 번째 값으로 전달됩니다. 이 함수를 환경에 맞게 사용자 지정하는 방법은 이외에도 많지만 다음에 주의해야 합니다.

  • 수정된 스크립트의 19번 줄과 같이 출력을 파이프라인으로만 보내도록 합니다. 다시 말하면 함수가 정보를 파일에 출력하지 않도록 해야 합니다. 대신 함수의 파이프라인 출력을 받아 Out-File과 같은 cmdlet로 보냅니다. 이러한 cmdlet는 파일 또는 기타 미디어로 출력을 옮깁니다. 이렇게 하면 함수의 유연성을 유지할 수 있습니다.
  • WMI 개체 속성을 제거하거나 속성 중 일부만 출력하는 등, 함수 내에서 정보를 필터링하지 않도록 합니다. 이 역시 향후 시나리오에서 함수의 유연성을 떨어뜨립니다. 대신 출력되는 정보 중 일부만 필요하다면 파이프라인을 통해 해당 출력을 Format cmdlet나 Select-Object로 전달하여 작업에 필요한 정보만 얻습니다.
  • 함수에 다른 작업을 추가해서는 안 됩니다. 많은 작업을 수행하는 만능 함수를 작성하지 말고 각 함수가 한 가지 작업만 수행하도록 합니다. 프로세스가 복잡한 경우 각 작업을 독립 실행형 함수로 작성한 다음 함수 간에 정보를 전달합니다. 이렇게 하면 함수의 유연성이 유지될 뿐만 아니라 관리 및 디버깅이 수월합니다.

강력한 DIY 도구

이러한 스크립트가 System Center Configuration Manager 같은 강력한 구성 관리 시스템을 대체할 수는 없지만 일련의 원격 컴퓨터에서 원하는 정보를 검색하여 손쉽게 목록으로 작성하는 기능을 제공합니다. 이 스크립트는 Microsoft가 Windows나 기타 제품을 개발할 때 예상하지 못한 문제에 대한 빠른 임시 해결책을 제공한다는 Windows PowerShell의 용도를 잘 보여 주는 예입니다.

이 스크립트는 WMI를 사용하기 때문에 Windows PowerShell 자체를 설치할 수 없는 이전 버전의 Windows(Windows NT 4.0 이상) 컴퓨터까지 지원합니다. 또한 Microsoft 제품 중에는 WMI를 통해 관리 정보를 제공하는 제품이 많기 때문에 다양한 제품에서 정보를 수집할 수 있습니다. 필자는 컴퓨터에서 사용 가능한 WMI 정보를 검색하는 데 WMI Explorer 도구(예: scriptinganswers.com의 "Tools Zone"에서 제공되는 무료 도구)를 즐겨 사용합니다. 그런 다음 여기서 소개한 것과 같은 스크립트를 사용하여 여러 컴퓨터에서 해당 정보를 수집합니다.

Don Jones는 Concentrated Technology에서 일하는 파트너로, Windows PowerShell: TFM(SAPIEN Press)의 공동 저자이기도 합니다. 매주 발행되는 Windows PowerShell 팁을 참조하거나 ConcentratedTech.com을 방문하여 Don Jones에게 질문할 수 있습니다.