반응형

Ansible의 Windows 모듈 중에 win_package에서는 특정 URL에서 msi 파일이나 exe 파일을 다운받아서 설치하는 과정까지 한 번에 해주는 기능을 제공한다.

 

아래와 같이 작성하면 일반적으로 문제가 없을 거라고 생각했다.

...
 - name: win_package test
   win_package:
     path: MSI파일을 받을 URL 입력
     arguments:
       - /quiet
...

 

실행해보면 아래와 같은 Fatal 에러가 발생한다.

fatal: [x.x.x.x]: FAILED! => {"changed": false, "msg": "product_id is required when the path is not an MSI or the path is an MSI but not local", "reboot_required": false, "restart_required": false}

 

MSI파일이 로컬 내에 위치해 있지 않은 경우 product_id를 넣으라는 의미인데..

Windows를 잘 모르는 내 입장에서 product_id를 어떻게 찾아야 할 지 막막해서 구글링을 해본 결과 다음과 같은 링크를 찾게 됨.

powershell을 이용해서 msi 파일의 정보를 뽑아내는데, 그 중에 ProductCode (ansible에서 이야기하는 product_id)를 뽑아낼 수 있다는 내용이다.

https://www.scconfigmgr.com/2014/08/22/how-to-get-msi-file-information-with-powershell/ 

 

How to get MSI file information with PowerShell

If you’re an ConfigMgr administrator like me, you will often find yourself in the situation where you may want to get the Product Code from a MSI file. Or perhaps you’re interested in the Product Version or simply just the Product Name. A while back I wrot

www.scconfigmgr.com

 

혹시 위 링크의 정보가 사라질 수 있으므로, Powershell 스크립트와 실행법에 대해 이 글에 복붙을 해보기로 한다.

param(
    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [System.IO.FileInfo]$Path,
 
    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [ValidateSet("ProductCode", "ProductVersion", "ProductName", "Manufacturer", "ProductLanguage", "FullVersion")]
    [string]$Property
)
Process {
    try {
        # Read property from MSI database
        $WindowsInstaller = New-Object -ComObject WindowsInstaller.Installer
        $MSIDatabase = $WindowsInstaller.GetType().InvokeMember("OpenDatabase", "InvokeMethod", $null, $WindowsInstaller, @($Path.FullName, 0))
        $Query = "SELECT Value FROM Property WHERE Property = '$($Property)'"
        $View = $MSIDatabase.GetType().InvokeMember("OpenView", "InvokeMethod", $null, $MSIDatabase, ($Query))
        $View.GetType().InvokeMember("Execute", "InvokeMethod", $null, $View, $null)
        $Record = $View.GetType().InvokeMember("Fetch", "InvokeMethod", $null, $View, $null)
        $Value = $Record.GetType().InvokeMember("StringData", "GetProperty", $null, $Record, 1)
 
        # Commit database and close view
        $MSIDatabase.GetType().InvokeMember("Commit", "InvokeMethod", $null, $MSIDatabase, $null)
        $View.GetType().InvokeMember("Close", "InvokeMethod", $null, $View, $null)           
        $MSIDatabase = $null
        $View = $null
 
        # Return the value
        return $Value
    } 
    catch {
        Write-Warning -Message $_.Exception.Message ; break
    }
}
End {
    # Run garbage collection and release ComObject
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($WindowsInstaller) | Out-Null
    [System.GC]::Collect()
}

 

실행법은 파워쉘을 연 후에 다음과 같은 커맨드를 입력하면 된다.


.\파일명.ps1 -Path "MSI파일의 경로" -Property ProductCode

위의 powershell 스크립트를 이용해서 win_package 모듈 사용 시 아래와 같이 product_id를 추가해주면 에러가 사라지게 된다.

...
 - name: win_package test
   win_package:
     path: MSI파일을 받을 URL 입력
     product_id: Powershell 스크립트를 이용해 얻은 product_id (single quote를 붙여주자.)
     arguments:
       - /quiet
...

 

반응형
,