Private/Get-M365AppConfigurations.ps1

#Requires -Version 5.1
<#
.SYNOPSIS
    Reads Microsoft 365 Apps Office Deployment Tool XML configuration files from a directory.
 
.DESCRIPTION
    Finds all *.xml files in the specified root directory (non-recursive), excluding
    Uninstall-Microsoft365Apps.xml, and parses each for configuration details including
    the PSPackageFactory GUID (Configuration/@ID), channel, products, and architecture.
 
.PARAMETER DefinitionsRoot
    Path to the directory containing the M365 Apps XML configuration files.
 
.OUTPUTS
    PSCustomObject[] - one row per XML file (valid or invalid).
#>

function Get-M365AppConfigurations {
    [CmdletBinding()]
    [OutputType([PSCustomObject[]])]
    param(
        [Parameter(Mandatory)]
        [string]$DefinitionsRoot
    )

    # Product ID to display name mapping
    $productDisplayNames = @{
        'O365ProPlusRetail'    = 'Microsoft 365 Apps for enterprise'
        'O365BusinessRetail'   = 'Microsoft 365 Apps for business'
        'VisioProRetail'       = 'Visio'
        'ProjectProRetail'     = 'Project'
        'AccessRuntimeRetail'  = 'Access Runtime'
    }


    $xmlFiles = @(Get-ChildItem -LiteralPath $DefinitionsRoot -File -Filter '*.xml' -ErrorAction Stop |
        Where-Object { $_.Name -ine 'Uninstall-Microsoft365Apps.xml' })

    $rows = foreach ($file in $xmlFiles) {
        try {
            [xml]$xml = Get-Content -LiteralPath $file.FullName -Raw -ErrorAction Stop

            $configId = [string]$xml.Configuration.ID
            $addNode  = $xml.Configuration.Add
            $channel  = [string]$addNode.Channel
            $arch     = [string]$addNode.OfficeClientEdition
            $archText = if ($arch -eq '32') { 'x86' } else { 'x64' }

            # Collect product IDs
            $productNodes = @($addNode.Product)
            $productIds   = ($productNodes | ForEach-Object { [string]$_.ID }) -join ', '

            # Determine primary product for display name
            $displayProducts = foreach ($p in $productNodes) {
                $id = [string]$p.ID
                if ($productDisplayNames.ContainsKey($id)) { $productDisplayNames[$id] } else { $id }
            }
            $displayProductsText = $displayProducts -join ', '

            # VDI / Desktop
            $sclProp = $xml.Configuration.Property | Where-Object { $_.Name -eq 'SharedComputerLicensing' } |
                Select-Object -First 1
            $isVdi   = [string]$sclProp.Value -eq '1'
            $envText = if ($isVdi) { 'VDI' } else { 'Desktop' }

            # Compose full display name: "Products: Environment, Architecture"
            # Channel is a placeholder in the XML (#Channel) and comes from the user's
            # dropdown selection at packaging time, so it is excluded from the display name.
            $displayName = if ($displayProductsText) {
                "$displayProductsText`: $envText, $archText"
            } else {
                $file.BaseName
            }

            # Validate GUID
            $guid   = [System.Guid]::Empty
            $status = if ([string]::IsNullOrWhiteSpace($configId)) {
                'No ID'
            } elseif (-not [System.Guid]::TryParse($configId, [ref]$guid)) {
                'Invalid ID'
            } else {
                'Valid'
            }

            $description = [string]$xml.Configuration.Info.Description

            [PSCustomObject]@{
                FileName         = $file.Name
                FilePath         = $file.FullName
                DisplayName      = $displayName
                Channel          = $channel
                Architecture     = $archText
                Products         = $productIds
                IsVdi            = $isVdi
                Description      = $description
                ConfigId         = $configId
                EvergreenVersion = ''
                Status           = $status
            }
        }
        catch {
            [PSCustomObject]@{
                FileName         = $file.Name
                FilePath         = $file.FullName
                DisplayName      = $file.BaseName
                Channel          = ''
                Architecture     = ''
                Products         = ''
                IsVdi            = $false
                Description      = ''
                ConfigId         = ''
                EvergreenVersion = ''
                Status           = 'Invalid XML'
            }
        }
    }

    return @($rows)
}