Get-AppInfo.psm1

function Get-MsiVersion {
    <#
    .SYNOPSIS
    Gets the ProductVersion property from MSI files.
 
    .DESCRIPTION
    This function retrieves the ProductVersion property from MSI files located in the specified folder. It returns
 
    .PARAMETER MsiFolder
    The folder path where the MSI files are located. If not specified, it defaults to the script's root directory.
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$MsiFolder
    )

    if (-not (Test-Path -LiteralPath $MsiFolder -PathType Container)) {
        throw "MSI folder was not found: $MsiFolder"
    }

    $msiFiles = Get-ChildItem -LiteralPath $MsiFolder -Filter '*.msi' -File
    if (-not $msiFiles) {
        throw "No MSI files were found in: $MsiFolder"
    }

    $installer = New-Object -ComObject WindowsInstaller.Installer

    try {
        foreach ($msiFile in $msiFiles) {
            $database = $installer.OpenDatabase($msiFile.FullName, 0)
            $view = $database.OpenView("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductVersion'")
            $view.Execute()
            $record = $view.Fetch()

            if ($null -eq $record) {
                Write-Warning "ProductVersion was not found in $($msiFile.Name)"
                continue
            }

            [pscustomobject]@{
                FileName       = $msiFile.Name
                ProductVersion = $record.StringData(1)
                FullPath       = $msiFile.FullName
            }
        }
    }
    finally {
        if ($null -ne $installer) {
            [void][Runtime.InteropServices.Marshal]::ReleaseComObject($installer)
        }
    }
}

function Get-ExeVersion {
    
<#
.SYNOPSIS
Gets the ProductVersion property from EXE files.
 
.PARAMETER ExeFolder
Folder containing EXE files. Defaults to the script directory.
#>

[CmdletBinding()]
param(
    [Parameter()]
    [ValidateNotNullOrEmpty()]
    [string]$ExeFolder = $PSScriptRoot
)

if (-not (Test-Path -LiteralPath $ExeFolder -PathType Container)) {
    throw "EXE folder was not found: $ExeFolder"
}

$exeFiles = Get-ChildItem -LiteralPath $ExeFolder -Filter '*.exe' -File
if (-not $exeFiles) {
    throw "No EXE files were found in: $ExeFolder"
}

foreach ($exeFile in $exeFiles) {
    $versionInfo = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($exeFile.FullName)

    if ([string]::IsNullOrWhiteSpace($versionInfo.ProductVersion)) {
        Write-Warning "ProductVersion was not found in $($exeFile.Name)"
        continue
    }

    [pscustomobject]@{
        FileName       = $exeFile.Name
        ProductVersion = $versionInfo.ProductVersion
        FullPath       = $exeFile.FullName
        }
    }
}

function Get-AppInfo {
    <#
    .SYNOPSIS
    Gets installed-application registry entries matching an application name.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$AppName
    )

    $paths = @(
        'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
        'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
    )

    foreach ($path in $paths) {
        Write-Host "Searching for $AppName in $path"
        $AppInfo = Get-ItemProperty -Path $path -ErrorAction SilentlyContinue | Where-Object { $_.DisplayName -Match "$AppName" }
        if ($AppInfo) {
            Write-Host "Found $AppName in $path"
            return $AppInfo
        }
    }
}

function Find-IntuneWinAppUtil {

    Write-Log "InTuneWinAppUtil Location not set. Searching common locations..."

    $Paths = @(
        "$env:USERPROFILE\Downloads",
        "$env:USERPROFILE\Desktop",
        "$env:USERPROFILE\Documents",
        $PSScriptRoot,
        "C:\Temp",
        "C:\Tools"
    )

    foreach ($Path in $Paths) {
        if (Test-Path $Path){
            Write-Host $Path
            $IntuneWinAppUtil = Get-ChildItem -Path $Path -Filter "IntuneWinAppUtil.exe" -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty FullName
            Write-Output $IntuneWinAppUtil
            if ($IntuneWinAppUtil) {
                Write-Log "IntuneWinAppUtil found at $($IntuneWinAppUtil)"
                return $IntuneWinAppUtil
            }
        }
    }

    if (-not $IntuneWinAppUtil) {
        Write-Log "IntuneWinAppUtil not found. Downloading..."
        Invoke-WebRequest -Uri "https://github.com/microsoft/Microsoft-Win32-Content-Prep-Tool/raw/refs/heads/master/IntuneWinAppUtil.exe" -UseBasicParsing -OutFile "$env:USERPROFILE\Downloads\IntuneWinAppUtil.exe" | Select-Object -ExpandProperty StatusCode
        if (-not (Test-Path "$env:USERPROFILE\Downloads\IntuneWinAppUtil.exe")) {  
            throw "Download Failed. Please download IntuneWinAppUtil.exe from https://github.com/microsoft/Microsoft-Win32-Content-Prep-Tool/raw/refs/heads/master/IntuneWinAppUtil.exe"
        }
        Write-Log "IntuneWinAppUtil downloaded to $env:USERPROFILE\Downloads\IntuneWinAppUtil.exe"
        $IntuneWinAppUtil  = "$env:USERPROFILE\Downloads\IntuneWinAppUtil.exe"
        return $IntuneWinAppUtil
    }
}

function Write-Log {
    param([string]$Message)
    $DateTime = Get-Date -Format "dd-MM-yyyy hh:mm:ss"
    Write-Host "$DateTime - $Message"
}

Export-ModuleMember -Function Get-MsiVersion, Get-AppInfo, Find-IntuneWinAppUtil, Write-Log, Get-ExeVersion