Az.Extensions.DynamicDeployment.psm1

function Get-ExpandedString {
    [cmdletbinding()]
    param($data, $prefix='$pwsh[', $lastChar=']')
    
    if($data -and $data.IndexOf($prefix) -gt -1 -and $data.EndsWith($lastChar)){
        Write-Verbose "Expanding $data"
        $data = $data.Replace($prefix,'');
        $data = $data.Substring(0, $data.length-1)
        $hasFunctionStart = $data.IndexOf('{');
        $hasFunctionEnd = $data.IndexOf('}');
        $expandedString = $ExecutionContext.InvokeCommand.ExpandString($data)
        # Allow expressions such as a($b) or a(-paramName '$b') translating into a -paramName 'value'
        if($hasFunctionStart -gt -1 -and $hasFunctionEnd -gt -1){
            $expandedString = $expandedString.Substring($expandedString.IndexOf('{')+1, $expandedString.IndexOf('}')-1)
            $expandedString = Invoke-Expression $expandedString
        }
        return $expandedString
    } else {
        Write-Verbose "Value does not have magic string format"
        return $data
    }    
}
function Get-FileContentFromUri {
    [cmdletbinding()]
    param (
        $uri
    )
    $tempFile = New-TemporaryFile;
    [void](Invoke-WebRequest -uri $uri -OutFile $tempFile.FullName)
    return (Get-Content $file.FullName)    
}

<#
.SYNOPSIS
    Parses a parameters file/uri and returns the expanded parameters in a hashtable to be used for deployment
.DESCRIPTION
    Returns a hashtable to be used for deployment, with values expanded - supporting Powershell variables and simple expressions
.EXAMPLE
    PS C:\> Add-AzAccount
    PS C:\> $myVar = 'The Variable Content
    PS C:\> $params = Expand-AZParameters -TemplateParameterUri 'https://www.github.com/haraldfianbakken/Az.Extensions.DynamicDeployment/Templates/Simple.Params.json
    PS C:\> $params
    PS C:\> New-AZResourceGroupDeployment -TemplateUri https://www.github.com/haraldfianbakken/Az.Extensions.DynamicDeployment/Templates/Simple.json -TemplateParameterObject $params
    Sets the variable found in the dynamics parameters and expands it.
    You can use the params in any deployment as fit.
.EXAMPLE
    PS C:\> Add-AzAccount
    PS C:\> function func($testParam){ "Hello $testParam"}
    PS C:\> $params = Expand-AZParameters -TemplateParameterUri 'https://www.github.com/haraldfianbakken/Az.Extensions.DynamicDeployment/Templates/UsingFunc.Params.json
    PS C:\> New-AZResourceGroupDeployment -TemplateObject $template -TemplateParameterObject $params
.EXAMPLE
    PS C:\> Add-AzAccount
    PS C:\> $params = Expand-AZParameters -TemplateParameterUri 'https://www.github.com/haraldfianbakken/Az.Extensions.DynamicDeployment/Templates/Complex.Params.json
    PS C:\> New-AZResourceGroupDeployment -TemplateObject $template -TemplateParameterObject $params
    This demonstates a complex template - which has an expression - calling out to ARM, selecting your Storage accounts (first one) and its name into the parameter.
.INPUTS
    -TemplateParameterUri
    Uri of the parameters file
    -TemplateParameterFile
    Local path of the parameters file
.OUTPUTS
    Hashtable of expanded variables
#>

function Expand-AzParameters {
    [cmdletbinding()]
    param(
        [Parameter(Position = 0, Mandatory=$true, ParameterSetName = 'UriSet')]
        [Uri]    
        $templateParameterUri,
        [Parameter(Position = 0, Mandatory=$true, ParameterSetName = 'FileSet')]
        [String]    
        $templateParameterFile
    )
    begin {
        $content = "";
        switch ($PSCmdlet.ParameterSetName) {
            'UriSet' {
                $content = Get-FileContentFromUri -uri $templateParameterUri;
            }
            'FileSet' {
                $content = Get-Content $templateParameterFile -Raw;
            }
        }
        
        $paramsJson = $content | ConvertFrom-Json 

        if($paramsJson.'$schema'.IndexOf('deploymentParameters.json') -eq -1){
            Write-Error 'Invalid parameters file passed'
            exit 1;
        }

        $paramsAsHashTable = $content | ConvertFrom-Json -AsHashtable
    } 
    process {
        $paramsAsHashTable.parameters.Keys|ForEach-Object {
            $paramsJson.parameters."$_".value = Get-ExpandedString -data $paramsJson.parameters."$_".value
        }
    }
    end {
        $paramsObject=$paramsJson.parameters;
        # Parmas Object needs to be simpler
        $o = @{}
        $paramsAsHashTable.parameters.keys|ForEach-Object {
            $o.Add($_, $paramsJson.parameters."$_".Value)
        }
        return $o;
        # $paramsJson|ConvertTo-Json
        # return $paramsObject|ConvertTo-Json|ConvertFrom-Json -AsHashtable;

    }
}

<#
.SYNOPSIS
    Parses a parameters file/uri and returns the expanded template in a hashtable to be used for deployment
.DESCRIPTION
    Returns a hashtable to be used for deployment - with default values (parameters) expanded - supporting Powershell variables and simple expressions
.EXAMPLE
    PS C:\> Add-AZAccount
    PS C:\> $myVar = 'The Variable Content
    PS C:\> $template = Expand-AzTemplate -TemplateParameterUri 'https://www.github.com/haraldfianbakken/Az.Extensions.DynamicDeployment/Templates/Simple.json
    PS C:\> $template
    PS C:\> New-AZResourceGroupDeployment -TemplateObject $template -TemplateParameterUri $paramsUri
     
    Expanding variables from Powershell into the default value params.
.EXAMPLE
    PS C:\> Add-AzAccount
    PS C:\> function func($testParam){ "Hello $testParam"}
    PS C:\> $template = Expand-AzTemplate -TemplateParameterUri 'https://www.github.com/haraldfianbakken/Az.Extensions.DynamicDeployment/Templates/UsingFunc.json
    PS C:\> $template
    PS C:\> New-AZResourceGroupDeployment -TemplateObject $template -TemplateParameterUri $paramsUri
     
    Showing how to create a function returning a value that is expanded into the template before deployment
         
.EXAMPLE
    PS C:\> Add-AzAccount
    PS C:\> $params = Expand-AzTemplate -TemplateParameterUri 'https://www.github.com/haraldfianbakken/Az.Extensions.DynamicDeployment/Templates/Complex.json
    PS C:\> $template
    PS C:\> New-AZResourceGroupDeployment -TemplateObject $template -TemplateParameterUri $paramsUri
     
    This demonstates a complex template - which has an expression - calling out to ARM, selecting your Storage accounts (first one) and its name into the parameter.
 
.INPUTS
    -TemplateUri
    Uri of the template file
    -TemplateFile
    Local path of the template file
.OUTPUTS
    Hashtable of expanded variables
#>

function Expand-AzTemplate {
    [cmdletbinding()]
    param(
        [Parameter(Position = 0, Mandatory=$true, ParameterSetName = 'UriSet')]
        [Uri]    
        $templateUri,
        [Parameter(Position = 0, Mandatory=$true, ParameterSetName = 'FileSet')]
        [String]    
        $templateFile
    )
    begin {
        $content = "";
        switch ($PSCmdlet.ParameterSetName) {
            'UriSet' {
                $content = Get-FileContentFromUri -uri $templateUri;
            }
            'FileSet' {
                $content = Get-Content $templateFile -Raw;
            }
        }
        
        $templateJson = $content | ConvertFrom-Json 

        if($templateJson.'$schema'.IndexOf('deploymentTemplate.json') -eq -1){
            Write-Error 'Invalid parameters file passed'
            exit 1;
        }

        $templateAsHashTable = $content | ConvertFrom-Json -AsHashtable
    } 
    process {
        $templateAsHashTable.parameters.Keys|ForEach-Object {
            if($templateJson.parameters."$_".defaultvalue){
                $templateJson.parameters."$_".defaultvalue = Get-ExpandedString -data $templateJson.parameters."$_".defaultvalue
            } else {
                Write-Verbose "$_ does not have a default value"
            }            
        }
    }
    end {
        return $templateJson|ConvertTo-Json|ConvertFrom-Json -AsHashtable;    
    }
}