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 } |