scripts/New-OsARMTemplate.ps1

Function New-OsARMParameterTemplate {
    Param(
        [string]$Environment,
        [array] $modules,
        [alias('Dir', 'Path')]
        [string] $ProjectDirectory = (Get-Location | Select-Object -ExpandProperty Path).ToString(),
        [string] $ResourceGroupLocation = 'eastus2',
        [string] $TemplateParametersFile,
        [string] $ResourceGroup
    )

    if ($ResourceGroup) {
        $Environment = $ResourceGroup
    }

    # Parameter Layering - Deserialize all ARM template JSON documents and setup variables.
    $JsonParameters = Get-Content "$ProjectDirectory\schema.parameters.json" -Raw | ConvertFrom-Json
    $GlobalParameters = Get-Content "$ProjectDirectory\global.parameters.json" -Raw | ConvertFrom-Json
    $EnvironmentParameters = Get-Content "$ProjectDirectory\env\$ResourceGroupLocation\$Environment\environment.parameters.json" -Raw | ConvertFrom-Json
    $modulesList = $EnvironmentParameters.parameters.modules.value | Get-Member -MemberType NoteProperty | Where-Object { $_.Definition -match "[1]" } | Select-Object -ExpandProperty Name

    # Begin layering Global parameters from the root "global" directory and the environment directory.
    $JsonParameters.parameters.Global | Add-Member -MemberType NoteProperty -Name value -Value $GlobalParameters.parameters.Global.value -Force
    $JsonParameters.parameters.modules.value = $EnvironmentParameters.parameters.modules.value
    $EnvironmentParameters.parameters.Global.value | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name | ForEach-Object {
        $JsonParameters.parameters.Global.value | Add-Member -MemberType NoteProperty -Name $_ -Value $EnvironmentParameters.parameters.Global.value.$_ -Force
    }

    # Determine which deployments and modules will be used
    if ($modules) {
        $modulesList = $modules
    }

    # This loops through every file in the module folder subdirectory of the environment and adds top level parameter blocks to $JsonParameters.
    $modulesList | ForEach-Object {
        Add-OsTemplatemoduleParameters -Environment $Environment -module $_ -JsonParameters $JsonParameters -ProjectDirectory $ProjectDirectory -ResourceGroupLocation $ResourceGroupLocation
    }

    #Cleanup template and check folders.
    if (-not (Test-Path -Path "$ProjectDirectory\temp")) {
        New-Item -Path "$ProjectDirectory\temp" -ItemType Directory
    }

    # The latest-template-deployment.json is used to create the deployment parameter file that is deployed.
    # Can also be used to review the last template generated for errors and/or troubleshooting deployments.
    $JsonParameters | ConvertTo-Json -Depth 100 | Out-File "$ProjectDirectory\temp\latest-template-deployment.json"
}

Function New-OsARMDeploymentTemplate {
    Param(
        [string]$Environment,
        [array] $modules,
        [alias('Dir', 'Path')]
        [string] $ProjectDirectory = (Get-Location | Select-Object -ExpandProperty Path).ToString(),
        [string] $ResourceGroupLocation = 'eastus2',
        [object] $JsonParameters,
        [string] $TemplateFile
    )

    # Parameter Layering - Deserialize all ARM template JSON documents and setup variables.
    $EnvironmentParameters = Get-Content "$ProjectDirectory\env\$ResourceGroupLocation\$Environment\environment.parameters.json" -Raw | ConvertFrom-Json
    $moduleDeployJson = Get-Content "$ProjectDirectory\schema.template.json" -Raw | ConvertFrom-Json
    $moduleDeployJson.resources = @()

    # Determine which deployments and modules will be used for either a full deployment or for manually selected modules.
    if ($modules) {
        $modulesList = $modules
    }
    else {
        $modulesList = $EnvironmentParameters.parameters.modules.value | Get-Member -MemberType NoteProperty | Where-Object { $_.Definition -match "[1]" } | Select-Object -ExpandProperty Name
    }
 
    # This loops through the folders of every module subdirectory in the environment and adds all the top level parameters to $moduleDeployJson.
    $AllModulesParamList = @()
    $script:AllModulesUsedList = @()
    $modulenames = @()
    $DefaultParameters = $moduleDeployJson.parameters | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name

    $modulesList | ForEach-Object {
        if (Test-Path -Path "$ProjectDirectory\modules\$($_)\metadata.json") {
            $moduleJson = Get-Content "$ProjectDirectory\modules\$($_)\metadata.json" -Raw | ConvertFrom-Json
            $modulenames += $moduleJson.modulenames 
        }
        else {
            $modulenames += $_
        }

        # Adds the current module name to the parameters so it can use a unique set of module parameters.
        # Will default to the module name if no valid module was found.
        $AllModulesParamList += $modulenames
        $script:AllModulesUsedList += $modulenames

        foreach ($modulename in $modulenames) {
            $ModuleParameters = Get-Content "$ProjectDirectory\modules\$modulename\module.json" -Raw | ConvertFrom-Json
            $ModuleParamList = $ModuleParameters.parameters | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
            $AllModulesParamList += $ModuleParamList
        }

        $moduleDeployJson.parameters | Add-Member -MemberType NoteProperty -Name $_ -Value (New-Object -TypeName PSCustomObject) -Force
        $moduleDeployJson.parameters.$_ | Add-Member -MemberType NoteProperty -Name 'type' -Value "Object" -Force
    }

    # Trim off deployment parameters not needed by the called modules.
    $AllModulesParamList = $AllModulesParamList | Select-Object -Unique
    $DeploymentParamList = $moduleDeployJson.parameters | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty Name
    $DeploymentParamList = $DeploymentParamList | Where-Object { $_ -notmatch "_artifactsLocation|EnvName" }

    # $DeploymentParamList | ForEach-Object {
    # if ($_ -eq "Global") {
    # $moduleDeployJson.parameters | Add-Member -MemberType NoteProperty -Name "Global" -Value (New-Object -TypeName PSCustomObject) -Force
    # $moduleDeployJson.parameters.$_ | Add-Member -MemberType NoteProperty -Name 'type' -Value "Object" -Force
    # }
    # elseif ($AllModulesParamList -notcontains $_) {
    # $moduleDeployJson.parameters = $moduleDeployJson.parameters | Select-Object -ExcludeProperty $_
    # }
    # }

    #Build out deployment resources to run each requested module in the template.
    foreach ($module in $modulesList) {
        try {
            Write-Verbose "module: $module"
            New-OsDeploymentResource -ProjectDirectory $ProjectDirectory -module $module -DefaultParameters $DefaultParameters -ResourceGroupLocation $ResourceGroupLocation -Environment $Environment -ErrorAction Stop
            $moduleDeployJson.resources += $DeploymentResourceJson
        }
        catch {
            Write-Warning "Could not create a deployment resource for module $module! Please check the modules metadata file!`n$_"
        }
    }

    if (-not (Test-Path -Path "$ProjectDirectory\temp")) {
        New-Item -Path "$ProjectDirectory\temp" -ItemType Directory
    }

    $moduleDeployJson | ConvertTo-Json -Depth 100 | Out-File "$ProjectDirectory\temp\latest-module-deploy.json"
    $script:AllModulesUsedList = $script:AllModulesUsedList | Select-Object -Unique | Sort-Object
    Write-Host "modules: $script:AllModulesUsedList" -ForegroundColor Green
    Write-Verbose "All module params: $AllModulesParamList"
}