pf-azFuncDeploy.ps1

function Get-ScriptTopCallerFolder() {
    $topCaller = Get-PSCallStack | Where-Object ScriptName | Select-Object -Last 1
    $result = Split-Path $topCaller.ScriptName -Parent
    return $result;
}

function New-TemporaryDirectory {
    $parent = [System.IO.Path]::GetTempPath()
    $name = [System.IO.Path]::GetRandomFileName()
    New-Item -ItemType Directory -Path (Join-Path $parent $name)
}


$azLocationAbbreviationMap = @{
    UKS = 'UK South'
    UKW = 'UK West'
}

function Get-AzLocationAbbreviation($location){
   $item = $azLocationAbbreviationMap.GetEnumerator() | 
    Where-Object { $_.Value -eq $location  } 
   return $item.Key
}
function Get-AzLocationAbbreviation:::Example{
   Get-AzLocationAbbreviation 'UK South'
}

function New-DeployManifest {
    $topCaller = Get-ScriptTopCallerFolder
    $gitInfo = Get-Git_Info -path $topCaller
    $content = Get-Content -Path "$($gitInfo.Folder)\deploy\deployManifest.json" -Raw
    $result = ConvertFrom-Json $content
    $result | Set-Member -Name ScriptFolder -value $topCaller
    $result | Set-Member -Name ServiceName -value $gitInfo.Name
    $result | Set-Member -Name Folder -value $gitInfo.Folder
    $result | Set-Member -Name ResourceGroup -value {
        $azLocationAbbreviation = Get-AzLocationAbbreviation $_.Location
        $ownerAndEnvironment = $gitInfo.Branch.Split('!\/')[0]
        return "$ownerAndEnvironment-$($_.ServiceName)-$azLocationAbbreviation"
    }
    $result | Set-Member -Name AppInsights -value { $_.ResourceGroup + "-Insights" }
    $result | Set-Member -Name TemplateFile -value  { $_.ScriptFolder + "\template.json" }

    return $result
}

function Initialize-AzFunc_Tools {
    npm install -g azure-functions-core-tools
}

function Initialize-Azcontext {
    $azSubscription = $deployManifest.Subscription
    $azContext = Get-AzContext
    if (-not $azContext) {
        Connect-AzAccount -Subscription $azSubscription | Out-Null
    }
    else {
        Set-AzContext -Subscription $azSubscription | Out-Null
    }
}

function Get-AzStorageRandomName {
    Get-RandomString -seed 'AnyObject' -validChars (Get-Chars -LowerCase -number )
}

function Deploy-AzService {
    # Install-Module -Name Az -AllowClobber -Scope CurrentUser
    Initialize-Azcontext

    $azFunctionApp = $deployManifest.ResourceGroup

    function Get-AzDeploymentMode {
        $resourceGroup = Get-AzResourceGroup -Name $deployManifest.ResourceGroup -ErrorAction SilentlyContinue
        if(!$resourceGroup) {
            $resourceGroup = New-AzResourceGroup -Name $deployManifest.ResourceGroup -Location $deployManifest.Location
            return 'Complete'
        }
        return 'Incremental'
    }

    $DeploymentMode = Get-AzDeploymentMode

    if ($deployManifest.TemplateFile) {
        if (Test-Path ($deployManifest.TemplateFile)) {
            New-AzResourceGroupDeployment -Name $deployManifest.TemplateName `
                -Mode $DeploymentMode -Force -Confirm:$false `
                -ResourceGroupName $deployManifest.ResourceGroup `
                -TemplateFile $deployManifest.TemplateFile #`
             # -TemplateParameterFile $deployManifest.ParametersFile;

            $pv = Get-AzResourceGroupDeployment -Name $deployManifest.TemplateName `
                -ResourceGroupName $deployManifest.ResourceGroup

            write-host $pv.ProvisioningState
        }
    }

    function Get-AzStorageName([string]$sourceName) {
        [string]$result = $sourceName.ToLowerInvariant()
        $result = Get-RandomString -seed $sourceName  -length 21 -validChars (Get-Chars -LowerCase -number )
        return $result + "st"
    }

    $azFuncStorage = Get-AzStorageName -sourceName $azFunctionApp

    function Deploy-AzApplicationInsights {
        $applicationInsights = Get-AzApplicationInsights -Name $deployManifest.AppInsights `
            -ResourceGroupName $deployManifest.ResourceGroup -ErrorAction SilentlyContinue
        if (!$applicationInsights) {
            $applicationInsights = New-AzApplicationInsights -Name $deployManifest.AppInsights `
                -ResourceGroupName $deployManifest.ResourceGroup `
                -location $deployManifest.Location
        }
    }

    Deploy-AzApplicationInsights

    $azWebApp = Get-AzWebApp -Name $azFunctionApp -ResourceGroupName `
        $deployManifest.ResourceGroup -ErrorAction SilentlyContinue
    if (!$azWebApp) {

        $storage = Get-AzStorageAccount -Name $azFuncStorage `
            -ResourceGroupName $deployManifest.ResourceGroup -ErrorAction SilentlyContinue
        if (!$storage) {
            $storage = New-AzStorageAccount -Name $azFuncStorage -Kind StorageV2 -AccessTier Hot `
                -ResourceGroupName $deployManifest.ResourceGroup -AssignIdentity `
                -SkuName Standard_LRS -Location $deployManifest.Location
        } 

        az functionapp create --name $azFunctionApp -g $deployManifest.ResourceGroup `
          -s $storage.StorageAccountName `
          --disable-app-insights `
          --consumption-plan-location uksouth | Out-Null
    } 

# az functionapp delete --name $azFunctionApp -g $deployManifest.ResourceGroup

    az functionapp config appsettings set --name $azFunctionApp `
        --resource-group $deployManifest.ResourceGroup `
        --settings FUNCTIONS_EXTENSION_VERSION=~2 | Out-Null

        function Set-AzFunctionAppInsightKey {
        $appInsights = Get-AzApplicationInsights `
                        -ResourceGroupName $deployManifest.ResourceGroup `
                        -Name $deployManifest.AppInsights

        $appInsightKey = $appInsights.InstrumentationKey
        if ($appInsightKey) {
            az functionapp config appsettings set --name $azFunctionApp `
                --resource-group $deployManifest.ResourceGroup `
                --settings APPINSIGHTS_INSTRUMENTATIONKEY=$appInsightKey | Out-Null
        }
    }

    Set-AzFunctionAppInsightKey

    Publish-AzFunction
    Deploy-AzKeyVault
}

function Get-AzFunctionProjects($path) {
    $funcProjectList = Get-ChildItem -Path $path -Filter *.csproj -Recurse |
        Select-String -SimpleMatch '<AzureFunctionsVersion>'
    $funcProjectList.Path | ForEach-Object { Split-Path $_ -Parent }
}


function Publish-AzFunction($projectPath)  {
    if (!$projectPath) {
        $projectPath = Get-AzFunctionProjects -path $deployManifest.Folder
    }

    try {

        $tmpFolder = New-TemporaryDirectory

        $tmpFolderPublish = $tmpFolder.FullName + "\publish"
        $zipPath = $tmpFolder.FullName + "\deploy.zip"

        mkdir -Path "$tmpFolderPublish\bin" -Force | Out-Host
        dotnet publish $projectPath --configuration debug -o $tmpFolderPublish | Out-Host

        Compress-Archive -Path "$tmpFolderPublish\*" -DestinationPath $zipPath `
            -CompressionLevel Optimal -Force -Confirm:$false | Out-Host

<#
        az functionapp deployment source config-zip `
            -g $deployManifest.ResourceGroup -n $azFunctionApp `
            --src $zipPath
 
        az functionapp deployment source config-zip `
            -g djl02_drawioAzureBridge -n PBITOpsDrawIO20191108073052 `
            --src $zipPath
#>


        Publish-AzWebapp -ArchivePath $zipPath -ResourceGroupName $deployManifest.ResourceGroup `
            -Name $azFunctionApp -Force -Confirm:$false
    }
    finally {
        if ($tmpFolder) {
            $tmpFolder | Remove-Item -Recurse -Force -Confirm:$false -ErrorAction SilentlyContinue
        }
    }
}

function Deploy-AzKeyVault {
#https://gist.github.com/pascalnaber/75412a97a0d0b059314d193c3ab37c4c
    Set-AzWebApp -AssignIdentity $true -Name $azFunctionApp -ResourceGroupName $deployManifest.ResourceGroup
    $app = Get-AzWebApp -ResourceGroupName $deployManifest.ResourceGroup -Name $azFunctionApp

    $azVaultName = $deployManifest.ServiceName + "-kv"
    $azVault = Get-AzKeyVault -Name $azVaultName 
    if (-not $azVault) {
        $azVault = Get-AzKeyVault -Name $azVaultName -InRemovedState -Location $deployManifest.Location
        if (-not $azVault) {
            $azVault = New-AzKeyVault -Name $azVaultName -ResourceGroupName $deployManifest.ResourceGroup `
                -Location $deployManifest.Location -EnablePurgeProtection -EnableSoftDelete
        }
        else {
            Undo-AzKeyVaultRemoval -VaultName $azVaultName -ResourceGroupName $deployManifest.ResourceGroup `
                -Location $deployManifest.Location
        }
    }

    az keyvault set-policy --secret-permissions get -n $azVaultName -g $deployManifest.ResourceGroup `
        --object-id $app.Identity.PrincipalId | Out-Host

    $localSettingsFile = Get-ChildItem -Path $deployManifest.Folder -Filter local.settings.json -Recurse

    $settingsPath = $localSettingsFile[0].FullName

    function Get-AppLocalSettings($path, $name) {
        $settings = Get-Content -path $path -Raw | ConvertFrom-Json 
        $SecretSettings = $settings.Values | Get-Member -MemberType NoteProperty | 
            Where-Object Name -Like $name
        $result = @{}
        if ($SecretSettings) {
            $SecretSettings.Name | ForEach-Object { $result.Add($_ , $settings.Values.$_ ) }
        }
        return $result
    }

    $vaultSecrets = Get-AppLocalSettings -path $settingsPath -name 'Secret-*'
    $vaultSecrets.GetEnumerator()  | Set-AzSecretSetting -VaultName $azVaultName
}

function Set-AzSecretSetting {
    Param (
        $VaultName, 
        [parameter(ValueFromPipelineByPropertyName=$true,Position=0)]
        [Alias('Name')]
        [String]$SecretName,
        [parameter(ValueFromPipelineByPropertyName=$true,Position=1)]
        [Alias('Value')]
        [String]$SecretValue
    )

    begin {
        $app = Get-AzWebApp -ResourceGroupName $deployManifest.ResourceGroup -Name $azFunctionApp
        $appSettings = @{}
        foreach($setting in $app.SiteConfig.AppSettings) {
            $appSettings[$setting.Name] = $setting.Value 
        }
    }
    Process {
        $SecureValue = ConvertTo-SecureString -String $SecretValue -AsPlainText -Force
        $azVaultSecret = Set-AzKeyVaultSecret -VaultName $VaultName -Name $SecretName `
                                -SecretValue $SecureValue
        $settingValue = "@Microsoft.KeyVault(SecretUri=$($azVaultSecret.Id))"
        $appSettings[$SecretName] = $settingValue

<#
        # The following does not work as it can truncate the last character
        az functionapp config appsettings set --name $azFunctionApp `
            --resource-group $deployManifest.ResourceGroup --settings $settingValue
#>


    }
    end {
        Set-AzWebApp -ResourceGroupName $deployManifest.ResourceGroup -Name $azFunctionApp `
            -AppSettings $appSettings
    }
}

function Export-AzResources {

    Export-AzResourceGroup -ResourceGroupName $deployManifest.ResourceGroup -Path $deployManifest.TemplateFile `
        -IncludeParameterDefaultValue  -Force -Confirm:$false

    $armContent = Get-Content $deployManifest.TemplateFile -Raw | ConvertFrom-Json

    $armResourcesTypesToRemove = @(
        # Ignored in order to support SoftDelete
        "Microsoft.KeyVault/vaults", 
        # Ignored as require values that are not obtained during Export
        "Microsoft.KeyVault/vaults/secrets",
        # Previous deployments are not relevant
        "Microsoft.Web/sites/deployments"
         )

    $armContent.resources = $armContent.resources | Where-Object { $_.type -notin $armResourcesTypesToRemove }

    $jsonContent = $armContent | ConvertTo-Json -Depth 100 
    $jsonContent = Format-Json -Json $jsonContent -Indentation 2
    $jsonContent | Out-File -FilePath $deployManifest.TemplateFile -Encoding ascii
}

function Remove-AzService {
    Remove-AzResourceGroup $deployManifest.ResourceGroup -Force -Confirm:$false
}

function Deploy-Planuml_Container {

    # docker run -d --name plantuml -p 8080:8080 plantuml/plantuml-server:tomcat

    $containerName = "$azEnv-plantuml"
    $containerGroup = New-AzureRmContainerGroup -ResourceGroupName $deployManifest.ResourceGroup `
        -Name $containerName `
        -Image 'plantuml/plantuml-server:tomcat' -DnsNameLabel plantuml -Port 8080

    $basepath = "http://" +  $containerGroup.Fqdn + ":8080"
    $puml =  "https://raw.githubusercontent.com/anoff/devradar/master/assets/editor-build.puml"

    Start-Process "$basepath/proxy?src=$puml"

    # $containerGroup | Remove-AzureRmContainerGroup
}