Azure Pipelines - apply Build Number into App Settings - azure

I have a Build and Release pipelines setup for a project and I want to be able to get the Build Number that was used for the Release into the running application.
I've seen a number of explainations of how to use the Release.Artifacts.{alias}.BuildNumber variable for things like the artifact output, however I haven't been able to find how to use this to directly update the App Settings for the App Service.
I've found a few Tasks available to use in the Release process that allow changing of App Settings, but none of them supported using variables from the build process (just allow setting of static values)
I want this to be set at the App Service level so that the website and all the webjobs are able to access the variable (rather than having to update several app.config files).
Can anyone point me in the right direction for this?

Delay in updating this, however this is the Powershell based solution I arrived at and this has been working well so far.
Added this as a Powershell script within the solution and updated the Build process to copy this artifact
param (
$myResourceGroup,
$mySite,
$mySlot,
$buildNo
)
$webApp = Get-AzureRMWebAppSlot -ResourceGroupName $myResourceGroup -Name $mySite -Slot $mySlot
$appSettingList = $webApp.SiteConfig.AppSettings
$hash = #{}
ForEach ($kvp in $appSettingList) {
$hash[$kvp.Name] = $kvp.Value
}
$hash['BuildNumber'] = $buildNo
Set-AzureRMWebAppSlot -ResourceGroupName $myResourceGroup -Name $mySite -AppSettings $hash -Slot $mySlot
This takes the parameters provided and retreives the current App Settings from the slot and adds/updates the BuildNumber setting to the $buildNo parameter that is received.
During the Release process, I added in an Azure Powershell task to call this script and then set the "Script Argurements" field to the following
-myResourceGroup "WebResourceGroup" -mySite "myexamplservice" -mySlot "staging" -buildNo $(Build.BuildNumber)
Now I can read the "BuildNumber" app setting within my website and get the deployed build number.

If you want to set the AppsSettings directly against the website without using an XML transform on the web.config, then you would need to go down the route of applying an ARM template.
Here's how you can do that via Pipelines:
Grab the ARM template for your site from the portal
Edit the template to accept a new buildNumber variable, and set this in the app settings for the site e.g.
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"buildNumber": {
"type": "string"
}
},
...
"resources": [{
"type": "Microsoft.Web/sites",
...
"properties": {
"siteConfig": {
"appSettings": [
{
"name": "BUILD_NUMBER",
"value": "[parameters('buildNumber')]"
},
...
]
}
}
}]
}
Setup a Azure Resource Group Deployment task to consume your template
Set the buildNumber parameter
This should result in having a BUILD_NUMBER app setting in your Web App that reflects the current build in Azure Pipelines.

Related

ARM template error Bad JSON content found in the request

I am trying to deploy an ARM template using the Azure DevOps release pipeline. Azure KeyVault is one of the resources in the template. the deployment is successful when I use the Powershell script. however, when Azure DevOps Release pipeline is used, deployment fails with error "Bad JSON content found in the request"
The key vault resource definition is as below.
{
"type": "Microsoft.KeyVault/vaults",
"apiVersion": "2018-02-14",
"name": "[parameters('keyVaultName')]",
"location": "[parameters('location')]",
"tags": {
"displayName": "KeyVault"
},
"properties": {
"enabledForDeployment": "[parameters('enabledForDeployment')]",
"enabledForTemplateDeployment": "[parameters('enabledForTemplateDeployment')]",
"enabledForDiskEncryption": "[parameters('enabledForDiskEncryption')]",
"tenantId": "[parameters('tenantId')]",
"accessPolicies": [],
"sku": {
"name": "[parameters('skuName')]",
"family": "A"
}
}
}
Update: I suspected that it could be because of tenant id and hardcoded the tenant id to test. But still no luck.
According to the log, you are specifying the override parameters in the task. That's why you are using the ARM template I provided, but still facing the Bad request error. Because in the task logic, the script which in ARM files is the request body of the API. And we use this API to create a resource you specified in azure. For detailed task logic described, you can refer my previous answer.
The parameter definition in the ARM template is correct, but now, the error caused by the override parameters specified:
More specifically, the error is because of the subscription().tenantId in your parameter override definition.
You can try to use Write-Host subscription().tenantId to get its value and print out by using Azure powershell task. You will see that it could not get any thing. One word, this can only used in Json file instead of used in task.
So now, because of no value get from this expression, also you have override the previous value which defined in the JSON file. It will lack the key parameter value(tenantId) in the request body when the task is going to create a azure resource with API.
There has 2 solution can solve it.
1. Do not try to override the parameters which its value is using expression.
Here I just mean the parameter that relevant with Azure subscription. Most of the expression could not be compiled in the Azure ARM deploy task.
2. If you still want to override these special parameters with the special expression in the task.
If this, you must add one task firstly, to get the tenantId from that. Then pass it into the ARM deploy task.
You can add Azure Powershell task by using the following sample script:
Write-Output "Getting tenantId using Get-AzureRmSubscription..."
$subscription = (Get-AzureRmSubscription -SubscriptionId $azureSubscriptionId)
Write-Output "Requested subscription: $azureSubscriptionId"
$subscriptionId = $subscription.Id
$subscriptionName = $subscription.Name
$tenantId = $subscription.tenantId
Write-Output "Subscription Id: $subscriptionId"
Write-Output "Subscription Name: $subscriptionName"
Write-Output "Tenant Id: $tenantId"
Write-Host "##vso[task.setvariable variable=TenantID;]$$tenantId"
Then in the next task, you can use $(TenantID) to get its value.
Here you can refer to this two excellent blog: Blog1 and Blog2
I still recommend you to use the first solution since the volume of the pipeline will increase and complicate if choosing the second solution.

Retrieve regional code for azure static website in ARM template

We are developing an application which uses the following two resources:
Azure Functions for the backend
Azure Storage as a static website for the front end
This is being deployed automatically by our CI pipeline using ARM templates. However, for the application to work we need to set the CORS rules on the Azure function to allow the static website to perform the api calls.
This is now performed by the following resource configuration:
{
"type": "Microsoft.Web/sites/config",
"name": "[concat(variables('functionAppName'), '/web')]",
"apiVersion": "2016-08-01",
"location": "[parameters('location')]",
"properties": {
"cors": {
"allowedOrigins": [
"[concat('https://', variables('storageAccountName'),'.z21', '.web.core.windows.net']"
],
}
},
"dependsOn": [
"[resourceId('Microsoft.Web/sites/', variables('functionAppName'))]"
]
}
However we are hardcoding the .z21 parameter since we know that the location parameter will be South Central US. However, this is not something that we desire to hardcode since probably the application could be deployed to another location.
Reading upon the documentation for static website hosting it states that
The URL of your site contains a regional code. For example the URL https://contosoblobaccount.z22.web.core.windows.net/ contains regional code z22.
While that code must remain the URL, it is only for internal use, and you won't have to use that code in any other way.
However I couldnt find a reference to which regional codes are being used by Azure. Is there a way to know that so that we stop hardcoding this value into our ARM template?
Another approach would be to dynamically access the storage account properties on the ARM template through an ARM template function, however I am unsure of which function could help us retrieve the regional code for the storage account.
Thanks in advance!
Ugly solution to extract regional code with PowerShell.
$stgname = '0708static'
$start = "https://$stgname."
$end = (Get-AzStorageAccount -Name $stgname -ResourceGroupName 'deleteme').PrimaryEndpoints.Web.TrimEnd('.web.core.windows.net/')
$result = $end.TrimStart("$start")
$result
If you want the entire URI then you can use:
[reference(variables('storageAccountId'), '2019-04-01').primaryEndpoints.web
If you want just the region code you can use:
[split(reference(variables('storageAccountId'), '2019-04-01').primaryEndpoints.web, '.')[1]]

Why won't my app service clone properly on Azure

I'm attempting to clone my web app using azure's clone feature. It's found under app services -> Developer tools -> clone app. Every time I run the function it results in a "Deployment Failed" due to conflict.
I've tried changing the clone options: target App name, using new and existing resource groups, new and existing app service plans and with or without application insights.
This is the error message I get every time I try to run this:
{
"id": "/subscriptions/<My subscription>",
"operationId": "<opID>",
"properties": {
"provisioningOperation": "Create",
"provisioningState": "Failed",
"timestamp": "2019-07-22T13:59:57.4502596Z",
"duration": "PT3M32.7637096S",
"trackingId": "<trackID>",
"statusCode": "Conflict",
"statusMessage": {
"status": "Failed",
"error": {
"code": "ResourceDeploymentFailure",
"message": "The resource operation completed with terminal provisioning state 'Failed'.",
"details": [
{
"message": "Provisioning failed with errors: System.FormatException: Input string was not in a correct format.\r\n at System.Text.StringBuilder.FormatError()\r\n at System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args)\r\n at System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args)\r\n at System.String.Format(String format, Object[] args)\r\n at Microsoft.Web.Hosting.Administration.GeoScale.Sql.WebSiteCloneManager.LogCloneProgress(Operation cloneOperation, SiteClone cloneRequest, CloneStatus cloneStatus, String messageFormat, Object[] args)\r\n at Microsoft.Web.Hosting.Administration.GeoScale.Sql.WebSiteCloneManager.AssociateClonedSiteWithSourceControl(SiteClone cloneRequest, Operation cloneOperation, GeoWebClient destinationStampClient, Site clonedSite)\r\n at Microsoft.Web.Hosting.Administration.GeoScale.Sql.WebSiteCloneManager.RefreshRestoreProgressForClone(IRepositoryContext context, SiteClone clone, Operation cloneOperation)\r\n at Microsoft.Web.Hosting.Administration.GeoScale.Sql.WebSiteCloneManager.<>c__DisplayClass47_0.<ProcessClonesWithProvisioningInProgress>b__0(SiteClone clone)"
}
]
}
},
"targetResource": {
"id": "/subscriptions/<myresource>",
"resourceType": "Microsoft.Web/sites",
"resourceName": "<ResourceName>"
}
}
}
Update: The App was created but the deployment settings weren't transferred over. I'm using Kudu to deploy so that may have been what causes the issue.
From your "update" on the question, if I understand correctly - 'The app was created but the settings weren't transferred over.'
Here are the known restrictions of app cloning currently (As mentioned in the Azure doc below):
Auto scale settings are not cloned
Backup schedule settings are not cloned
VNET settings are not cloned
App Insights are not automatically set up on the destination app
Easy Auth settings are not cloned
Kudu Extension are not cloned
TiP rules are not cloned
Database content is not cloned
Outbound IP Addresses changes if cloning to a different scale unit
Not available for Linux Apps
Reference Azure Doc: https://learn.microsoft.com/azure/app-service/app-service-web-app-cloning#current-restrictions
To clone an existing app including all associated deployment slots, you need to use the IncludeSourceWebAppSlots parameter. The following PowerShell command demonstrates the use of that parameter with the New-AzWebApp command:
$srcappslot = Get-AzWebAppSlot -ResourceGroupName SourceAzureResourceGroup -Name source-app -Slot source-appslot
The following command demonstrates creating a clone of the source app to a new app:
$destapp = New-AzWebApp -ResourceGroupName DestinationAzureResourceGroup -Name dest-app -Location "North Central US" -AppServicePlan DestinationAppServicePlan -SourceWebApp $srcappslot
Additionally on Slots:
When you clone configuration from another deployment slot, the cloned configuration is editable. Some configuration elements follow the content across a swap (not slot specific), whereas other configuration elements stay in the same slot after a swap (slot specific). The following lists show the settings that change when you swap slots.

Azure Functions: how to set CORS via automation?

I have an azure function app that I would like to set up in repeatable (automated) manner so that I can duplicate it in different environments/resource groups. I'm able to create the function app via the azure cli, but I also need to configure the CORS options such that I can call it from a browser.
I've found where to do that in the azure portal web ui,
in the 'Platform Features' tab(https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings#cors), but I can't find anything about modifying that setting via azure cli, or by the VSTS deployment task that I've set up to do releases when I change the functions in the app.
It seems you can even specify the CORS setting for local development via the local.settisg.json, but that only applies locally (https://learn.microsoft.com/en-us/azure/azure-functions/functions-run-local#local-settings). Were I deploying the app via the azure function tools cli I could supposedly specify the --publish-local-settings flag when I deploy, but I'm not deploying that way.
It seems like there must be a way to modify the CORS configuration without using the web UI, am I just not finding it?
Fabio's answer is correct, Azure Resource Manager templates work for this. Since the example he linked to was about logic apps and not azure functions, the getting the template right required a few changes and I wanted to add some detail that may help others get there faster.
To craft the template I ended up downloading the automation template from the function app that I created manually, and then deleting stuff until I got to what I think is the minimum. Here's what I'm using:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"function_app_name": {
"defaultValue": "my-function-app",
"type": "string"
}
},
"variables": {},
"resources": [
{
"comments": "CORS allow origins *.",
"type": "Microsoft.Web/sites/config",
"name": "[concat(parameters('function_app_name'), '/web')]",
"apiVersion": "2016-08-01",
"properties": {
"cors": {
"allowedOrigins": [
"*"
]
}
},
"dependsOn": []
}
]
}
I also have a parameters file that goes with this that looks like this:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"function_app_name": {
"value": null
}
}
}
and then I have an Azure Resource Group Deployment step in my release definition that deploys this and substitutes the desired function app name depending on the environment I'm deploying to.
To set CORS settings programatically, you want to use ARM.
Here's an example you can follow: https://msftplayground.com/2016/08/setting-api-definition-url-cors-value-arm/
I tend to favour automating the fucntion CORS entries as part of the deployment (after function app resource has already been built with an ARM template earlier in the pipeline or another pipeline).
Since you can have multiple functions within a function app, I consider the CORS requirements specific to the function being deployed within a function app and I feel any CORS entries should be part of the actual function deployment process.
I use Azure CLI to automate the CORS setup. Please refer to How to set CORS via Automation for Azure Functions
az functionapp cors add --allowed-origins
[--ids]
[--name]
[--resource-group]
[--slot]
[--subscription]
You can also check/display existing entries like this:
az functionapp cors show --name MyFunctionApp --resource-group MyResourceGroup

Azure Arm template deployment of a web app zip package programmatically using build artifact

Short question:
I was wondering if packageUri parameter in MSDeploy resource (Deploy azure app service) below can point to the location of VSTS build Server drop location that is used by Release pipeline.
How to programmatically get acccess to build drop location?
To use vsts api? Which ones?
Maybe this link can shed some lights:
understanding artifacts
grab-your-build-artifacts-from-visual-studio-online
Basic authentication for the REST APIs
"resources": [
{
"name": "MSDeploy",
"type": "extensions",
"location": "[parameters('location')]",
"apiVersion": "2014-06-01",
"dependsOn": [
"[concat('Microsoft.Web/sites/', parameters('siteName'))]"
],
"tags": {
"displayName": "ContentDeploy"
},
"properties": {
"packageUri": "[concat(parameters('packageUrl'), parameters('sasToken'))]",
"dbType": "None",
"connectionString": "",
"setParameters": {
"IIS Web Application Name": "[parameters('siteName')]"
}
}
}
]
Longer question and explanation:
I am using VSTS release definition to deploy my web app infrastructure during development.
The web app will be build and packaged by my VSTS build and uploaded to Server drop location. I am using on-premise (my PC) agent for both my build and release pipeline.
While I can do web app deployments using vsts Deploy Azure App Service release task (that knows how to find build artifacts), I want to do an automated deployment of multiple web app programmatically from a web app I intend to build.
I want to know if this .zip web app package can be somehow made available to my ARM deployment template. Everybody recommends copying .zip package to a storage blob with SAS token and that way Release "Deploy azure app service" can find it.
I want to avoid this copying. Actually I want at least to programmatically download this package and eventually copy it it a storage blob container if I cannot manage to do it as asked.
Here is my little research I did:
I turned on agent logging for both build and release
Some Build variables:
BUILD_SOURCESDIRECTORY=F:\agent\_work\1\s
BUILD_STAGINGDIRECTORY=F:\agent\_work\1\a
Some release variables:
[SYSTEM_ARTIFACTSDIRECTORY] --> [F:\agent\_work\r3\a]
[RELEASE_ARTIFACTS_ECOMMERCESITECI_SOURCEVERSION] --> [91]
[BUILD_BUILDNUMBER] --> [20161230.12]
[BUILD_BUILDID] --> [85]
[SYSTEM_TEAMPROJECTID] --> [7ab6c2a4-208d-411d-81e5-5e88b26f2252]
[RELEASE_RELEASEWEBURL] --> [https://vstsaccount.visualstudio.com/7ab6c2a4-208d-411d-81e5-5e88b26f2252/_apps/hub/ms.vss-releaseManagement-web.hub-explorer?releaseId=103&_a=release-summary]
RELEASE_RELEASEWEBURL is an interesting page where I can press Artifacts which show all linked sources (in my case I have one Primary source coming from my CI build.). I can then follow the link and reach a specific build EcommerceSiteCI / Build 20161230.12 and I can press download button (from the link below) to download .zip package which I am after. I would like to find out about it but programmatically from my Arm template. I would like to create a Web app or Api app that can programmatically discover this .zip package and using it to deploy the app using "Web Deploy for Web Apps" resource.
https://vstsaccount.visualstudio.com/vstsproject/_build/index?buildId=85&_a=summary&tab=artifacts
Looking at fiddler I managed to see what is the url of asset download:
GET https://vstsaccount.visualstudio.com/7ab6c2a4-208d-411d-81e5-5e88b26f2252/_apis/build/builds/85/artifacts?artifactName=drop&%24format=zip
So I can see based on [BUILD_BUILDID] --> [85] and [SYSTEM_TEAMPROJECTID] --> [7ab6c2a4-208d-411d-81e5-5e88b26f2252] I can assemble this URL.
The only thing is how to authenticate this request. When I entered above address I was prompted to sign in using Microsoft account and I saw it is oauth 2.0 authentication. The url is something like this:
https://login.live.com/oauth20_authorize.srf?response_type=code&client_id=51483342-012c-4d86-bf138-cf50c7252078&scope=openid+profile+email+offline_access&response_mode=form_pos
etc...(some encrypted string - probably representing .zip package location is omitted).
Even if I cannot make this download url available to MSDeploy arm resource
it would be great if I could programmatically download it and upload to a Storage blob container and generate SAS token for MSDeploy to find it.
So how to programmatically get acccess to build drop location?
Thanks
Rad
I recently blogged about using PowerShell to queue a build and pull the artifacts using the the TFS API. Works great for us.
$artifacts = Invoke-RestMethod -Method Get -Credential $creds -ContentType application/json -Uri "$TFSUri/$project/_apis/build/builds/$buildId/artifacts?api-version=2.0"
foreach($artifact in $artifacts.value)
{
$filePath = $artifact.resource.downloadUrl.Replace("file:", "").Replace("/", "\").Replace("%20", " ");
$path = $filePath + "\*Package\*"
if (Test-Path $path)
{
$packagePath = "MSDeployPackages\$project-$buildName\";
if ((Test-Path $packagePath) -eq $false) {
New-Item $packagePath -ItemType directory
}
Copy-Item -Path $path -Destination $packagePath -Recurse -Force
}
}
http://www.dotnetcatch.com/2016/10/14/queue-tfsvsts-builds-via-powershell/

Resources