Windows PowerShell우수한 인벤토리 도구 개발

Don Jones

목차

항상 동일한 개체
유연한 출력
유연한 입력
오류 관련 사항
효율성 개선

이 칼럼의 이전 호에서는 여러 원격 컴퓨터에서 서비스 팩 인벤토리 정보를 검색할 수 있는 함수를 만들어 보았습니다. 이 함수는 매우 간편하게 사용할 수 있는 유용한 도구이며, 이 도구를 개발하기 위해 사용한 절차도 중요합니다. 이번 호에서는 사용자가 자신에게 맞는 함수를 개발하는 데 참조할 수 있도록 필자가 사용하는 절차에 대해 설명하려고 합니다.

이전 호에서 작성한 함수는 다음과 같습니다.

Function Get-SPInventory {
  PROCESS {
    $wmi = Get-WmiObject Win32_OperatingSystem  
      –comp $_ | 
    Select CSName,BuildNumber,
      ServicePackMajorVersion
    Write-Output $wmi
  }
}

이 함수를 사용하는 방법은 다음과 같습니다.

Get-Content c:\computernames.txt | 
Get-SPInventory

이 함수는 텍스트 파일에서 한 줄에 컴퓨터 이름이 하나씩 있어야 제대로 실행됩니다.

이 함수는 하나의 WMI(Windows Management Instrumentation) 클래스에서만 데이터를 반환할 수 있다는 한 가지 단점이 있습니다. 따라서 BIOS 일련 번호도 함께 반환하려면 어떻게 해야 하는지 알아 보겠습니다.

항상 동일한 개체

문제는 이 함수가 Win32_OperatingSystem 클래스를 WMI에서 검색하여 해당 개체만 출력만 한다는 점입니다. 모든 개체와 마찬가지로 Win32_OperatingSystem 클래스는 포함된 데이터에 고정되어 있습니다. 즉, BIOS 일련 번호를 포함하지 않으며 포함할 수도 없습니다. Win32_OperatingSystem 개체만 출력할 경우 일련 번호를 출력에 포함시킬 수 없습니다.

WMI를 아무리 검색해봐도 BIOS 일련 번호와 서비스 팩 정보를 모두 포함하는 단일 개체 클래스는 없습니다. 따라서 이 함수로는 WMI 클래스를 출력할 수 없습니다. 대신 필요한 데이터를 모두 포함하는 사용자 지정 개체를 구성하여 출력해야 합니다.

속성이 없는 빈 개체를 새로 만들기 위해 다음과 같이 했습니다.

$obj = New-Object PSObject

새 개체는 $obj에 저장되며 원하는 모든 데이터를 추가할 수 있습니다. 이제 함수를 실행하면 추가한 모든 데이터 출력될 것입니다.

이 함수에서는 Win32_OperatingSystem 정보를 검색하여 $wmi 변수에 저장했습니다. 이 정보를 사용하는 것은 $wmi의 속성을 참조하는 것만큼 간단합니다. 예를 들어, BuildNumber 속성을 가져오려면 다음 함수를 사용합니다.

$wmi.BuildNumber

사용자 지정 개체에 속성을 추가하려면 $obj 변수에 있는 개체를 Add-Member에 연결해야 합니다. Add-Member에거 추가할 속성의 유형(항상 NoteProperty)과 이름, 원하는 속성 값 등을 지정해 줍니다.

$obj | Add-Member NoteProperty BuildNumber 
($wmi.BuildNumber)

컴퓨터 시스템 이름과 서비스 팩 버전에 대해서도 이렇게 할 수 있습니다.

$obj | Add-Member NotePropertyCSName 
  ($wmi.CSName)
$obj | Add-Member NotePropertySPVersion 
  ($wmi.ServicePackMajorVersion)

결과적으로 새로운 함수가 생성됩니다(그림 1 참조). 원래의 $wmi 개체 대신 사용자 지정 개체를 출력하도록 Write-Output 행을 변경했습니다.

그림 1 사용자 지정 개체

Function Get-SPInventory {
  PROCESS {
    $wmi = Get-WmiObject Win32_       OperatingSystem –comp $_ | 
       Select CSName,BuildNumber,ServicePack         MajorVersion
    $obj = New-Object PSObject
    $obj | Add-Member NoteProperty 
      BuildNumber ($wmi.BuildNumber)
    $obj | Add-Member NoteProperty CSName      ($wmi.CSName) 
    $obj | Add-Member NoteProperty SPVersion 
      ($wmi.ServicePackMajorVersion)
    Write-Output $obj
  }
}

이제 BIOS 일련 번호를 가져와야 합니다. 일부 검색에서 SerialNumber 속성을 가진 Win32_BIOS 클래스가 반환됩니다(그림 2에 표시된 온라인 설명서 페이지 부분 참조). 따라서 Win32_BIOS 클래스를 쿼리하고 SerialNumber 속성을 사용자 지정 개체에 추가하기만 하면 됩니다. 수정된 함수는 그림 3에 나와 있습니다.

fig02.gif

그림 2 Win32_BIOS 클래스 설명서 페이지(더 크게 보려면 이미지를 클릭하십시오.)

그림 3 SerialNumber 속성 포함

Function Get-SPInventory {
  PROCESS {
    $wmi = Get-WmiObject Win32_      OperatingSystem –comp $_ |    
      Select CSName,BuildNumber,ServicePack        MajorVersion
    $obj = New-Object PSObject
    $obj | Add-Member NoteProperty 
      BuildNumber ($wmi.BuildNumber)
    $obj | Add-Member NoteProperty CSName      ($wmi.CSName)
    $obj | Add-Member NoteProperty SPVersion 
      ($wmi.ServicePackMajorVersion)

    $wmi = Get-WmiObject Win32_BIOS –comp $_ | 
      Select SerialNumber
    $obj | Add-Member NoteProperty BIOSSerial 
      ($wmi.SerialNumber)
    Write-Output $obj
  }
}

유연한 출력

사용자 지정 개체를 출력에 사용하여 다양한 용도로 이 함수를 만들어 보았습니다. 예를 들어, Windows Server 2008 컴퓨터만 포함시키려면 다음 함수를 사용할 수 있습니다.

Gc c:\computernames.txt | Get-SPInventory | 
Where { $_.BuildNumber –eq 6001 }

서비스 팩 1이 설치되지 않은 모든 Windows Vista 컴퓨터를 나열하는 HTML 파일을 만들려면 다음 함수를 사용할 수 있습니다.

Gc c:\computernames.txt | Get-SPInventory | 
Where { $_.BuildNumber –eq 6000 } | 
ConvertTo-HTML | Out-File c:\VistaInventory.html

XML, CSV, 테이블, 목록, HTML, 정렬, 필터링, 그룹화 등 수많은 형식으로 출력할 수 있습니다. Windows PowerShell의 기본 cmdlet는 개체에 사용하기 위해 고안된 것입니다. 출력할 개체를 만듦으로써 별도의 작업 없이 Windows PowerShell이 수행할 수 있는 모든 기능을 활용할 수 있습니다.

유연한 입력

안타깝게도 이 함수는 입력에 있어서는 유연성이 없습니다. 이로 인해 Windows PowerShell 버전 1의 함수 설계 방법이 부분적으로 제한됩니다. 버전 2에서는 cmdlet 스크립트를 사용하여 유연성을 크게 향상시켰습니다.

이 함수는 파이프라인 입력으로 간단한 문자열 개체를 사용합니다. Active Directory에서 모든 컴퓨터 이름을 검색하기 위해 Get-Content 명령을 cmdlet로 대체할 경우 함수가 제대로 실행되지 않습니다.

Get-QADComputer | Get-SPInventory

이 경우 Get-QADComputer 명령(quest.com/powershell에서 가져올 수 있는 Active Directory 관리 cmdlet 무료 세트의 일부)은 단순 문자열 개체를 반환하는 대신 Name 속성을 가진 개체를 반환합니다. 이 명령을 실행하려면 함수를 그림 4와 같이 조정해야 합니다. 변경 사항은 빨간색으로 강조 표시됩니다.

그림 4 최종 결과

Function Get-SPInventory {
  PROCESS {
    $wmi = Get-WmiObject Win32_OperatingSystem –<span class="clsRed" xmlns="http://www.w3.org/1999/xhtml">comp 
    $_.Name</span> | Select 
      CSName,BuildNumber,
        ServicePackMajorVersion
    $obj = New-Object PSObject
    $obj | Add-Member NoteProperty 
      BuildNumber ($wmi.BuildNumber)
    $obj | Add-Member NoteProperty CSName 
      ($wmi.CSName)
    $obj | Add-Member NoteProperty SPVersion  
      ($wmi.ServicePackMajorVersion)

    $wmi = Get-WmiObject Win32_BIOS –comp <span class="clsRed" xmlns="http://www.w3.org/1999/xhtml">$_.Name |</span> 
      Select SerialNumber
    $obj | Add-Member NoteProperty BIOSSerial 
      ($wmi.SerialNumber)
    Write-Output $obj
  }
}

전체 파이프라인 개체를 –computerName 매개 변수에 전달하는 대신 이제 이 함수는 파이프라인 개체의 Name 속성에 영향을 줍니다. 이러한 간단한 변경으로 유연성이 크게 향상되었습니다. 예를 들어, Get-QADComputer를 사용하여 입력을 특정 조직 단위의 컴퓨터 또는 Active Directory 특성 기반의 기타 기준을 충족하는 컴퓨터로 제한할 수 있습니다.

오류 관련 사항

불가피하게 이 함수에서 연결할 수 없는 컴퓨터나 연결할 권한이 없는 컴퓨터 등을 찾는 경우가 있습니다. 이 경우 함수는 Windows PowerShell 콘솔 창에 오류 메시지를 빨간색 텍스트로 표시한 후 다음 컴퓨터로 계속 진행합니다.

이렇게 할 수도 있지만, 함수 실행 시 오류 메시지가 표시되지 않게 할 수도 있습니다. 이렇게 하려면 함수의 PROCESS 스크립트 블록의 시작 부분에 다음과 같이 한 줄만 추가하면 됩니다.

$ErrorActionPreference = "SilentlyContinue"

다소 복잡한 방법을 사용하여 연결할 수 없는 컴퓨터의 이름을 나열하는 오류 로그를 만들 수도 있습니다. 이 경우 Windows PowerShell을 사용하면 되지만, 그 방법은 다음 호에서 설명할 예정입니다.

이달의 cmdlet: Export-Alias 및 Import-Alias

다음은 여러분이 만든 사용자 지정 별칭을 공유하거나 셸이 시작될 때마다 사용자 지정 별칭을 불러오는 유용한 방법입니다. 원하는 모든 별칭을 작성한 후 다음과 같이 해당 별칭을 파일로 내보냅니다.

Export-Alias c:\aliases.xml

별칭을 셸로 다시 로드하려면 다음 명령을 실행합니다.

Import-Alias c:\aliases.xml

Windows PowerShell 프로필 스크립트에 두 번째 명령을 추가하여 셸이 시작될 때마다 실행되도록 할 수 있고, 함께 작업하는 다른 관리자가 쉽게 사용할 수 있도록 네트워크 공유에 파일을 저장할 수도 있습니다.

효율성 개선

지금까지는 이 함수를 .ps1 파일에 입력하고 스크립트를 실행했습니다. 그렇게 하는 것도 좋지만 효율성과 관련하여 몇 가지 문제가 있습니다. 우선 함수를 사용하려면 해당 스크립트 파일을 열고, 함수를 호출하는 행을 수정하고(경우에 따라 필요한 출력, 정렬 또는 기타 명령 추가), 스크립트를 저장한 다음 실행해야 합니다. cmdlet처럼 함수를 셸의 콘솔 창에서 바로 사용할 수 있다면 훨씬 더 편리할 것입니다.

그렇게 하기 위해 두 가지 방법을 사용할 수 있습니다. 첫 번째 방법은 시작될 때마다 셸이 자동으로 실행되는 4개의 스크립트 파일(있는 경우) 중 하나인 Windows PowerShell 프로필 스크립트에 함수를 복사하는 것입니다. 4개 스크립트의 위치는 Windows PowerShell과 함께 설치되는 빠른 시작 설명서에 나와 있습니다.

필자는 일반적으로 profile.ps1 파일을 사용합니다. 이 파일은 "Documents" 폴더 또는 "내 문서" 폴더(Windows XP 및 Windows Server 2003 컴퓨터)의 Windows­PowerShell(공백 없음) 폴더에 있어야 합니다. 프로필에 함수를 추가하면 셸을 실행한 셸의 명령 프롬프트에서 언제든지 바로 사용할 수 있습니다.

두 번째 방법은 스크립트에 다른 코드 없이 함수만 포함시킨 다음 셸에서 마침표를 입력하고 한 칸 띈 후 해당 스크립트의 경로와 이름을 입력하는 것입니다. 이를 도트 소싱이라고 합니다. 스크립트를 실행하면 일반적으로 Windows PowerShell이 스크립트에 대해 새 범위를 만들고, 스크립트에서 실행되는 모든 작업(예: 함수 정의)은 이 범위 내에서 수행됩니다. 스크립트 실행이 끝나면 이 범위가 삭제되고 해당 범위 내에서 실행된 모든 작업이 손실됩니다.

따라서 Get-SP­Inventory 함수만 포함된 스크립트 파일의 경우 이 스크립트는 새 범위를 만들고 함수를 정의한 후 범위를 삭제합니다. 그런 다음 함수가 종료됩니다. 이 옵션은 그렇게 유용하지는 않습니다.

반면에 도트 소싱은 새 범위를 만들지 않고 스크립트를 실행합니다. Get-SPInventory를 MyFunction.ps1 파일에 입력한 경우 다음과 같이 도트 소싱으로 실행할 수 있습니다.

PS C:\> . c:\functions\myfunction

마침표를 입력하고 한 칸 띈 후 스크립트 경로와 파일 이름을 차례로 입력합니다. 이는 스크립트를 현재 범위 내에서 실행하도록 셸에 지시하는 것입니다. 즉, 스크립트에서 발생하는 모든 작업이 스크립트 실행이 완료된 후에도 그대로 유지됩니다. 그 결과 Get-SPInventory 스크립트가 셸에 정의되어 cmdlet와 마찬가지로 명령줄에서 스크립트를 바로 사용할 수 있습니다.

Don Jones는 Concentrated Technology의 공동 설립자이며 많은 IT 서적을 저술했습니다. 매주 발행되는 Windows PowerShell 팁을 참조하거나 ConcentratedTech.com을 방문하여 Don Jones에게 질문할 수 있습니다.