CfnTemplate.ps1

<#
.SYNOPSIS
A template describes your AWS infrastructure.
 
.DESCRIPTION
Templates include several major sections. The Resources section is the only section that is required:
 * Parameters
 * Mappings
 * Conditions
 * Resources
 * Outputs
 
Some sections in a template can be in any order. However, as you build your template, it might be helpful to use the logical ordering of the previous example, as values in one section might refer to values from a previous section.
 
.LINK
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-anatomy.html
#>

function New-CfnTemplate {

    param(
        [Parameter(Mandatory,Position=0)]
        [scriptblock]$TemplateBlock,

        [string]$Version="2010-09-09",
        [string]$Description,
        [System.Collections.IDictionary]$Metadata,
        [switch]$JSON,
        [switch]$Compress
    )

    $tMetadata   = [ordered]@{}
    $tParameters = [ordered]@{}
    $tMappings   = [ordered]@{}
    $tConditions = [ordered]@{}
    $tResources  = [ordered]@{}
    $tOutputs    = [ordered]@{}

    ## Copy over any Metadata that's available as a Param
    if ($Metadata) {
        foreach ($k in $Metadata.Keys) {
            $tMetadata[$k] = $Metadata[$k]
        }
    }

    & $TemplateBlock

    $t = [ordered]@{ Body = [ordered]@{} }

    if ($Version    ) { $t.Body.AWSTemplateFormatVersion = $Version }
    if ($Description) { $t.Body.Description = $Description }

    if ($tMetadata   -and $tMetadata.Count  ) { $t.Body.Metadata   = $tMetadata   }
    if ($tParameters -and $tParameters.Count) { $t.Body.Parameters = $tParameters }
    if ($tMappings   -and $tMappings.Count  ) { $t.Body.Mappings   = $tMappings   }
    if ($tConditions -and $tConditions.Count) { $t.Body.Conditions = $tConditions }
    if ($tResources  -and $tResources.Count ) { $t.Body.Resources  = $tResources  }
    if ($tOutputs    -and $tOutputs.Count   ) { $t.Body.Outputs    = $tOutputs    }

    if ($JSON) {
        $convertParams = @{ Compress = $Compress }
        $t.Body | ConvertTo-Json -Depth 100 @convertParams
    }
    else {
        $t
    }
}
Set-Alias -Name Template -Value New-CfnTemplate