Modules/Private/Reporting/Export-AZTIAsciiDocReport.ps1

<#
.Synopsis
Export inventory data as a structured AsciiDoc report
 
.DESCRIPTION
Reads all cache files produced by the processing phase and assembles them into
a single AsciiDoc document. Each category becomes a level-1 section and each
module's data is rendered as an AsciiDoc table. Includes AsciiDoc admonition
blocks ([TIP], [NOTE], [WARNING]) for recommendations. Suitable for Antora,
Confluence, and any AsciiDoc-capable documentation system.
 
.Link
https://github.com/thisismydemo/azure-scout/Modules/Private/Reporting/Export-AZSCAsciiDocReport.ps1
 
.COMPONENT
This PowerShell Module is part of Azure Scout (AZSC)
 
.NOTES
Version: 1.0.0
First Release Date: February 24, 2026
Authors: AzureScout Contributors
#>


function Export-AZSCAsciiDocReport {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$ReportCache,

        [Parameter(Mandatory)]
        [string]$File,

        [Parameter()]
        [string]$TenantID,

        [Parameter()]
        [object]$Subscriptions,

        [Parameter()]
        [ValidateSet('All', 'ArmOnly', 'EntraOnly')]
        [string]$Scope = 'All'
    )

    $AdocFile = [System.IO.Path]::ChangeExtension($File, '.adoc')
    Write-Debug ((Get-Date -Format 'yyyy-MM-dd_HH_mm_ss') + " - AsciiDoc output file: $AdocFile")

    $subCount = if ($Subscriptions) { @($Subscriptions).Count } else { 0 }
    $genDate  = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'

    $lines = [System.Collections.Generic.List[string]]::new()

    # ── Document header ──────────────────────────────────────────────────
    $lines.Add('= Azure Scout Report')
    $lines.Add('AzureScout')
    $lines.Add(":toc: left")
    $lines.Add(":toc-title: Contents")
    $lines.Add(":toclevels: 3")
    $lines.Add(":icons: font")
    $lines.Add(":source-highlighter: highlight.js")
    $lines.Add(":docdate: $genDate")
    $lines.Add('')
    $lines.Add('[NOTE]')
    $lines.Add('====')
    $lines.Add("This report was auto-generated by *AzureScout* on `$genDate`.")
    $lines.Add('')
    $lines.Add("[horizontal]")
    $lines.Add("Tenant ID:: $TenantID")
    $lines.Add("Subscriptions:: $subCount")
    $lines.Add("Scope:: $Scope")
    $lines.Add('====')
    $lines.Add('')

    # ── Discover module folders ───────────────────────────────────────────
    $ParentPath           = (Get-Item $PSScriptRoot).Parent.Parent
    $InventoryModulesPath = Join-Path $ParentPath 'Public' 'InventoryModules'
    $ModuleFolders        = Get-ChildItem -Path $InventoryModulesPath -Directory | Sort-Object Name
    $CacheFiles           = Get-ChildItem -Path $ReportCache -Recurse -Filter '*.json' -ErrorAction SilentlyContinue

    $totalResources = 0
    $sectionLines   = [System.Collections.Generic.List[string]]::new()

    foreach ($ModuleFolder in $ModuleFolders) {
        $FolderName   = $ModuleFolder.Name
        $JSONFileName = "$FolderName.json"
        $CacheFile    = $CacheFiles | Where-Object { $_.Name -eq $JSONFileName }
        if (-not $CacheFile) { continue }

        $RawJson = try { [System.IO.File]::ReadAllText($CacheFile.FullName) } catch { $null }
        if ([string]::IsNullOrWhiteSpace($RawJson)) { continue }

        $CacheData   = $RawJson | ConvertFrom-Json
        $ModuleFiles = Get-ChildItem -Path (Join-Path $ModuleFolder.FullName '*.ps1') -ErrorAction SilentlyContinue | Sort-Object BaseName

        $folderHasData = $false
        $folderSections = [System.Collections.Generic.List[string]]::new()

        foreach ($Module in $ModuleFiles) {
            $ModName      = $Module.BaseName
            $ModResources = $CacheData.$ModName
            if (-not $ModResources -or @($ModResources).Count -eq 0) { continue }

            $rows = @($ModResources)
            $totalResources += $rows.Count

            if (-not $folderHasData) {
                $folderSections.Add("== $FolderName")
                $folderSections.Add('')
                $folderHasData = $true
            }

            # Module sub-section
            $folderSections.Add("=== $ModName")
            $folderSections.Add('')
            $folderSections.Add("[TIP]")
            $folderSections.Add("====")
            $folderSections.Add("$($rows.Count) resource(s) found in this module.")
            $folderSections.Add("====")
            $folderSections.Add('')

            # Build AsciiDoc table
            $props = $rows[0].PSObject.Properties.Name | Where-Object { $_ -notmatch 'Tag (Name|Value)|Resource U' }
            if ($props.Count -gt 0) {
                $colSpec = ($props | ForEach-Object { '1' }) -join ','
                $folderSections.Add("[%header,cols=""$colSpec""]")
                $folderSections.Add('|===')
                # Header row
                $folderSections.Add(($props | ForEach-Object { "| $_" }) -join ' ')
                foreach ($row in $rows) {
                    $cells = $props | ForEach-Object {
                        $v = $row.$_
                        if ($null -eq $v) { '' }
                        else { [string]$v -replace '\|', '{vbar}' -replace '\r?\n', ' +' }
                    }
                    $folderSections.Add(($cells | ForEach-Object { "| $_" }) -join ' ')
                }
                $folderSections.Add('|===')
            }
            $folderSections.Add('')
        }

        if ($folderHasData) {
            foreach ($l in $folderSections) { $sectionLines.Add($l) }
        }
    }

    $lines.Add("_Total resources inventoried: *$totalResources*_")
    $lines.Add('')
    foreach ($l in $sectionLines) { $lines.Add($l) }

    # ── Footer ────────────────────────────────────────────────────────────
    $lines.Add("'''")
    $lines.Add('')
    $lines.Add("[NOTE]")
    $lines.Add('====')
    $lines.Add("Generated by link:https://github.com/thisismydemo/azure-scout[AzureScout] at $genDate")
    $lines.Add('====')

    try {
        $lines | Out-File -FilePath $AdocFile -Encoding UTF8 -Force
        Write-Host "AsciiDoc report saved to: " -ForegroundColor Green -NoNewline
        Write-Host $AdocFile -ForegroundColor Cyan
    }
    catch {
        Write-Warning "Failed to write AsciiDoc report to '$AdocFile': $_"
    }

    return $AdocFile
}