Public/Template/Build-CardFromTemplate.ps1

<#
.SYNOPSIS
    Builds a complete Adaptive Card by replacing template tags with actual content.
 
.DESCRIPTION
    The Build-CardFromTemplate function takes a template-based Adaptive Card content structure
    and replaces all template tag placeholders with actual data. It supports both simple string/value
    replacements and complex ScriptBlock-based dynamic content generation, enabling the creation
    of reusable card templates that can be populated with different data sets.
 
.PARAMETER Content
    A hashtable representing the Adaptive Card template content structure containing template tags
    to be replaced. This is typically created using card creation functions with New-CardTemplateTag placeholders.
 
.PARAMETER Tags
    A hashtable where keys are template tag names (without the !{{ }} wrapper) and values are
    the replacement content. Values can be:
    - Strings, numbers, booleans: Replaced directly
    - ScriptBlocks: Executed to generate dynamic content
    - Objects/Arrays: Converted to JSON and inserted as structured content
 
.OUTPUTS
    System.Collections.Hashtable
        Returns a hashtable representing the completed Adaptive Card content with all template tags replaced.
 
.EXAMPLE
    $template = New-CardContainer -Content {
        New-CardTextBlock -Text (New-CardTemplateTag -TagName "Greeting")
        New-CardTextBlock -Text (New-CardTemplateTag -TagName "UserName")
    }
 
    $card = Build-CardFromTemplate -Content $template -Tags @{
        Greeting = "Welcome!"
        UserName = "John Doe"
    }
 
    Replaces the template tags with simple string values.
 
.EXAMPLE
    $template = New-CardContainer -Content {
        New-CardTextBlock -Text (New-CardTemplateTag -TagName "Message")
        New-CardTemplateTag -TagName "DynamicContent"
    }
 
    $card = Build-CardFromTemplate -Content $template -Tags @{
        Message = "Current Status:"
        DynamicContent = {
            New-CardFactSet -Facts @{
                "Time" = (Get-Date).ToString("HH:mm")
                "Status" = "Online"
            }
        }
    }
 
    Combines string replacement with dynamic content generation using ScriptBlocks.
 
.EXAMPLE
    $userTemplate = New-CardContainer -Content {
        New-CardTextBlock -Text "User: !{{Name}}"
        New-CardTemplateTag -TagName "UserDetails"
    } -Id (New-CardTemplateTag -TagName "ContainerID")
 
    $users = @("Alice", "Bob", "Charlie")
    $cards = foreach ($user in $users) {
        Build-CardFromTemplate -Content $userTemplate -Tags @{
            Name = $user
            UserDetails = { New-CardTextBlock -Text "Welcome, $user!" }
            ContainerID = "User_$user"
        }
    }
 
    Demonstrates creating multiple cards from a single template with different data.
 
.NOTES
    - Template tag names in the Tags hashtable should not include the !{{ }} wrapper syntax
    - ScriptBlocks in tag values are executed in the current scope and can access variables
    - The function performs deep replacement through the entire content structure
    - Warnings are generated for tags specified in the Tags parameter but not found in the template
    - String values are JSON-escaped to ensure proper integration into the card structure
    - Complex objects and arrays are serialized to JSON for structured content insertion
    - The function uses ConvertFrom-JsonAsHashtable for reliable JSON-to-hashtable conversion
 
.LINK
    New-CardTemplateTag
 
.LINK
    Find-CardTemplateTags
#>

function Build-CardFromTemplate {
    param (
        [hashtable]$Content,
        [hashtable]$Tags
    )

    $TemplateTags = Find-CardTemplateTag -Content $Content
    $ContentAsJson = $Content | ConvertTo-Json -Depth $_MaxDepth

    Write-Host $_MaxDepth
    Write-Host $ContentAsJson

    foreach ($Key in $Tags.Keys) {
        if ($TemplateTags -contains $Key) {
            $TagValue = $Tags[$Key]
            if ($TagValue -is [scriptblock]) {
                $ResolvedValue = Invoke-Command -ScriptBlock $TagValue
            }
            else {
                $ResolvedValue = $TagValue
            }
            $ResolvedValueIsString = $ResolvedValue -is [string] -or $ResolvedValue -is [int] -or $ResolvedValue -is [double] -or $ResolvedValue -is [bool]

            if ($ResolvedValueIsString) {
                $ReplaceValue = ($ResolvedValue | ConvertTo-Json -Depth $_MaxDepth -Compress).Trim('"')
                $TagPlaceholder = New-CardTemplateTag -TagName $Key
            }
            else {
                $ReplaceValue = $ResolvedValue | ConvertTo-Json -Depth $_MaxDepth -Compress
                $TagPlaceholder = New-CardTemplateTag -TagName $Key | ConvertTo-Json -Depth $_MaxDepth -Compress
            }

            $ContentAsJson = $ContentAsJson -replace [regex]::Escape($TagPlaceholder), $ReplaceValue
        }
        else {
            Write-Warning "Tag '$Key' not found in template."
        }
    }

    $UpdatedContent = ConvertFrom-JsonAsHashtable -InputObject $ContentAsJson

    return $UpdatedContent
}