Public/Resource Helpers/Add-UserData.ps1

function Add-UserData {
    <#
    .SYNOPSIS
        Adds UserData to a resource on the template. For single values (i.e. in AutoScaling Launch Configurations), it adds the single For multiple values, it automatically adds it as {"Fn::Base64": {"Fn::Join": ["",[VALUES...] ] } } to reduce the amount of scripting needed.
    
    .PARAMETER String
        An array of strings and/or Instrinsic Functions.

        IMPORTANT: You must specify new lines in Powershell syntax so it identifies it as a new line when converting to JSON via Export-Vaporshell. This will convert `n [backtick n] into \n [backslash n] in the resulting JSON template.
    
    .PARAMETER File
        The path of the script file to convert to UserData. This cannot contain any Intrinsic functions such as Ref in it. Use the String parameter if you'd like to include functions in the array.

    .EXAMPLE
        $EC2 =

    .FUNCTIONALITY
        Vaporshell
    #>

    [OutputType('Vaporshell.Resource.UserData')]
    [cmdletbinding(DefaultParameterSetName="String")]
    Param
    (
        [parameter(Mandatory = $true,Position = 0,ParameterSetName="String")]
        [ValidateScript( {
                $allowedTypes = "System.String","Vaporshell.Function"
                if ([string]$($_.PSTypeNames) -match "($(($allowedTypes|ForEach-Object{[RegEx]::Escape($_)}) -join '|'))") {
                    $true
                }
                else {
                    throw "The Properties parameter only accepts the following types: $($allowedTypes -join ", "). The current types of the value are: $($_.PSTypeNames -join ", ")."
                }
            })]
        $String,
        [parameter(Mandatory = $true,Position = 0,ParameterSetName="File")]
        [ValidateScript( {
                if (Test-Path $_) {
                    $true
                }
                else {
                    throw "You must specify a valid file path -- unable to find the path $_"
                }
            })]
        [System.String]
        $File
    )
    Begin {
        $Values = @()
        $Path = (Resolve-Path -Path $File).Path
    }
    Process {
        switch ($PSBoundParameters.Keys) {
            'String' {
                $Values = $String
            }
            'File' {
                $Values = @()
                if ($Path -like "*.ps1") {
                    $Windows = $true
                    $tag = "powershell"
                }
                elseif ($Path -like "*.bat" -or $Path -like "*.cmd") {
                    $Windows = $true
                    $tag = "script"
                }
                else {
                    $Windows = $false
                }
                [System.Collections.ArrayList]$fileContents = Get-Content $Path
                do {
                    if ([string]::IsNullOrWhiteSpace($fileContents[0])){
                        $fileContents.RemoveAt(0)
                    }
                } until (!([string]::IsNullOrWhiteSpace($fileContents[0])))
                do {
                    $lastIndex = $fileContents.Count - 1
                    if ([string]::IsNullOrWhiteSpace($fileContents[$lastIndex])){
                        $fileContents.RemoveAt($lastIndex)
                    }
                } until (!([string]::IsNullOrWhiteSpace($fileContents[$lastIndex - 1])))
                if ($Windows) {
                    if ($fileContents[0] -notlike "<$($tag)>*") {
                        $Values += "<$($tag)>"
                    }
                }
                $fileContents | ForEach-Object {
                    $Values += "$($_)`n"
                }
                if ($Windows) {
                    if ($fileContents[$fileContents.Count - 1] -notlike "</$($tag)>*") {
                        $Values += "</$($tag)>"
                    }
                }
            }
        }
    }
    End {
        if ($Values.Count -gt 1) {
            $obj = Add-FnBase64 -ValueToEncode (Add-FnJoin -ListOfValues $Values)
        }
        else {
            $obj = Add-FnBase64 -ValueToEncode ($Values | Select-Object -First 1)
        }
        $obj | Add-ObjectDetail -TypeName 'Vaporshell.Resource.UserData'
        Write-Verbose "Resulting JSON from $($MyInvocation.MyCommand): `n`n`t$($obj | ConvertTo-Json -Depth 5)`n"
    }
}