Azure template export does not match deployed template - azure

I am deploying a Resource Group from an Azure DevOps Build pipeline so that the release pipeline can deploy the rest of the resources as needed. Yes, I am deploying at the subscription level, and the resource group creates with no problem. My issue is that when I look at the "Export Template" option in the Azure Resource Manager, my changes are not represented, most notably my contentVersion is not updated, however, I also have noticed that the $schema is not the same either. Here is my resource group template:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "[parameters('semVer')]",
"parameters": {
"semVer": {
"type": "string"
},
"resourceGroupName": {
"type": "string"
}
},
"resources": [
{
"name": "[parameters('resourceGroupName')]",
"type": "Microsoft.Resources/resourceGroups",
"apiVersion": "2019-05-01",
"location": "eastus",
"tags": {
},
"properties": {
}
}
],
"outputs": {
},
"functions": [
]
}
and here is my parameters file that I am using:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"semVer":{
"value": "0.0.0.1"
},
"resourceGroupName":{
"value": "rgName"
}
}
}
As I said, I want to update the content version, so I am using a transform in my build pipeline that should update the contentVersion to the Assembly Semantic Version. I seems like the transform is working, here is my azure-pipelines.yml:
name: $(date:yyyyMMdd)$(rev:.r)-$(Build.SourceBranchName)-$(GitVersion.SemVer)
trigger:
- master
- develop
stages:
- stage: DEV
displayName: 'DEV'
condition: and(always(), contains(variables['Build.SourceBranch'], 'develop'))
pool:
vmImage: 'ubuntu-latest'
variables:
contentVersion: $(GitVersion.AssemblySemVer)
parameters.semVer.value: $(GitVersion.AssemblySemVer)
parameters.resourceGroupName.value: 'rgName-DEV'
jobs:
- job: DevResourceGroup
steps:
- task: GitVersion#5
inputs:
preferBundledVersion: false
updateAssemblyInfo: true
configFilePath: './GitVersion.yml'
- script: echo %Action%%BuildVersion%
displayName: 'Set Build Number to Semantic Version'
env:
Action: '##vso[build.updatebuildnumber]'
BuildVersion: '$(GitVersion.SemVer)'
- task: FileTransform#2
inputs:
folderPath: '$(Build.SourcesDirectory)'
xmlTransformationRules:
jsonTargetFiles: './ResourceGroup/resourceGroup.parameters.json'
- task: AzureResourceManagerTemplateDeployment#3
inputs:
deploymentScope: 'Subscription'
azureResourceManagerConnection: 'ConnectionName'
subscriptionId: 'GUID'
location: 'East US'
templateLocation: 'Linked artifact'
csmFile: '$(Build.SourcesDirectory)/ResourceGroup/resourceGroup.json'
csmParametersFile: '$(Build.SourcesDirectory)/ResourceGroup/resourceGroup.parameters.json'
deploymentMode: 'Incremental'
- task: PublishBuildArtifacts#1
inputs:
PathtoPublish: '$(Build.SourcesDirectory)'
ArtifactName: 'develop'
publishLocation: 'Container'
- stage: PROD
displayName: 'PROD'
condition: and(always(), contains(variables['Build.SourceBranch'],'master'))
pool:
vmImage: 'ubuntu-latest'
variables:
contentVersion: $(GitVersion.AssemblySemVer)
parameters.semVer.value: $(GitVersion.AssemblySemVer)
jobs:
- job: ProdResourceGroup
steps:
- task: GitVersion#5
inputs:
preferBundledVersion: false
updateAssemblyInfo: true
configFilePath: './GitVersion.yml'
- script: echo %Action%%BuildVersion%
displayName: 'Set Build Number to Semantic Version'
env:
Action: '##vso[build.updatebuildnumber]'
BuildVersion: '$(GitVersion.SemVer)'
- task: FileTransform#2
inputs:
folderPath: '$(Build.SourcesDirectory)'
xmlTransformationRules:
jsonTargetFiles: './ResourceGroup/resourceGroup.parameters.json'
- task: AzureResourceManagerTemplateDeployment#3
inputs:
deploymentScope: 'Subscription'
azureResourceManagerConnection: 'ConnectionName'
subscriptionId: 'GUID'
location: 'East US'
templateLocation: 'Linked artifact'
csmFile: '$(Build.SourcesDirectory)/ResourceGroup/resourceGroup.json'
csmParametersFile: '$(Build.SourcesDirectory)/ResourceGroup/resourceGroup.parameters.json'
deploymentMode: 'Incremental'
- task: PublishBuildArtifacts#1
inputs:
PathtoPublish: '$(Build.SourcesDirectory)'
ArtifactName: 'master'
publishLocation: 'Container'
It appears that the transform works just fine because the resource group in DEV does have the DEV text appended. The step in the pipeline also outputs the following:
Applying JSON variable substitution for ./ResourceGroup/resourceGroup.parameters.json
Applying JSON variable substitution for /home/vsts/work/1/s/ResourceGroup/resourceGroup.parameters.json
Substituting value on key contentVersion with (string) value: 0.1.0.0
Substituting value on key value with (string) value: 0.1.0.0
Substituting value on key value with (string) value: rgName-DEV
JSON variable substitution applied successfully.
My concern is that when I go to Export the template from the Azure Resource Manager, the template looks as such:
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": []
}
Why is my contentVersion returned to the default at this point in time? How would I verify what version of my template was deployed to an environment? What is the point of the contentVersion variable if it is just going to be overwritten?

I'd say both the content version and schema do not matter at all when you are deploying the template. besides, exporting the template has nothing to do with verifying it works.
Exporting the template is a way to get your current resources configuration (kinda), if you just deployed a template it makes no sense to export the template, because if the template worked - your configuration was applied successfully.

Related

azure function pipeline automate setup

can we automatically create a pipeline for each new function thats created ie fully automate the lot? Im not sure if it possible and wondering if anyone has done or can point me to something that has a set by step pocess?
can we automatically create a pipeline for each new function thats
created ie fully automate the lot?
Yes, you can.
For the simplest situation, for example, this is the deployment pipeline on my side:
trigger:
- none
pool:
vmImage: ubuntu-latest
parameters:
- name: functionname
default: bowman0822
steps:
- task: DownloadBuildArtifacts#1
inputs:
buildType: 'specific'
project: 'xxx'
pipeline: 'xxx'
buildVersionToDownload: 'latest'
downloadType: 'single'
downloadPath: '$(System.ArtifactsDirectory)'
- script: |
dir
displayName: 'Run a multi-line script'
- task: AzureFunctionApp#1
inputs:
azureSubscription: 'testbowman_in_AAD'
appType: 'functionAppLinux'
appName: '${{parameters.functionname}}'
package: '$(System.DefaultWorkingDirectory)/azurefunction'
runtimeStack: 'PYTHON|3.9'
#key step see the below.
- task: PythonScript#0
inputs:
scriptSource: 'inline'
script: |
import requests
import json
org_name = "<The Organization Name>"
project_name = "<The Product Name>"
pipeline_name = "${{parameters.functionname}}"
pat = "<Your Personal Access Token>"
url = "https://dev.azure.com/"+org_name+"/"+project_name+"/_apis/build/definitions?api-version=5.0"
payload = json.dumps({
"name": ""+pipeline_name+"",
"Repository": {
"name": "functiontest",
"type": "TfsGit"
},
"Process": {
"phases": [
{
"name": "Agent job 1",
"refName": "Job_1",
"condition": "succeeded()",
"target": {
"executionOptions": {
"type": 0
},
"allowScriptsAuthAccessOption": False,
"type": 1
},
"jobAuthorizationScope": "project"
}
]
}
})
headers = {
'Authorization': 'Basic '+pat,
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
Successfully created on my side:
Please note that the above sample is the simplest situation, there is nothing in the created pipeline. If you want specific requirements in pipeline, I suggest you to first manually create a pipeline, and then use this API so that from the response you can know how the request structure of Create API should be.
The code above is based on this REST API:
https://learn.microsoft.com/en-us/rest/api/azure/devops/build/definitions/create?view=azure-devops-rest-5.0
By the way, if you want YAML definition, there should be additional step to push the YAML definition to your repo.

Azure DevOps Conditional String Variables

I've got a pipeline I've created that deploys some app settings to all our websites (multiple per file), and I'm trying to conditionally set a variable within the JSON structure without any sort of anti-patterns.
For conditional variables, all we have this syntax:
${{ if endsWith('EXISTING_STRING','MATCH_STRING') }}:
${{ else }}:
This syntax assumes that you're going to put the entirety of the string after condition, though. If my JSON structure is 50 lines long, I don't want to do that. Is there a way to do this without that cluttered code, whilst avoiding any sort of anti-patterns as well?
Here's the Azure Piplines Module:
- task: AzureAppServiceSettings#1
displayName: 'Deploy App Settings'
inputs:
azureSubscription: "${{ parameters.resource_group_name }}"
appName: "wz${{ parameters.default_environment }}"
resourceGroupName: "${{ parameters.resource_group_name }}"
appSettings: |
[
{
"name": "LaunchUrl",
"value": "False",
/* ^^^^
Value I want to be conditional based off:
if endsWith( parameters['default_environment'], 'matchString'
The resulting string also contains parameters.default_environment FYI
*/
"slotSetting": true
},
{
"name": "DisableFHIR",
"value": "False",
"slotSetting": true
},
...
Thanks
Putting conditionals inside a string literal is unfortunately not supported. However, one technique my team uses is define our JSON values as a string parameter and populate it with variables that are evaluated at runtime.
parameters:
- name: appSettings
type: string
default: |
'[
{
"name": "appSetting1",
"value": "$(appSetting1)",
"slotSetting": "true"
}
]'
Personally, I like to go one further and define them as a yaml parameter and then use the convertToJson expression. This avoids the potential for malformed JSON and I can also put comments in the YAML.
parameters:
- name: resource_group_name
type: string
- name: appSettings
type: object
default:
- name: 'LaunchUrl'
value: '$(launchUrl)'
slotSetting: true
- name: 'DisableFHIR'
value: 'false'
slotSetting: true
job:
variables:
${{ if endsWith('prd', parameters.resource_group_name) }}:
launchUrl: https://production.dot.com
${{ else }}:
launchUrl: http://somewhere.else.com
steps:
- task: AzureAppServiceSettings#1
inputs:
azureSubscription: "${{ parameters.resource_group_name }}"
appName: "wz${{ parameters.default_environment }}"
resourceGroupName: "${{ parameters.resource_group_name }}"
appSettings: ${{ convertToJson(parameters.appSettings) }}

Azure App Service Problem in DevOps Pipeline

Hi does anyone have any idea on why im getting this error:
Got service connection details for Azure App Service:'nsclassroomstudent-dgyn27h2dfoyo'
##[debug][POST]https://login.windows.net/***/oauth2/token/
##[debug][GET]https://management.azure.com/subscriptions/237bc9da-22ad-49ea-8411-6cf6a190c18f/resourceGroups/ClassroomInTheCloud/providers/Microsoft.Web/sites/nsclassroomstudent-dgyn27h2dfoyo/?api-version=2016-08-01
##[debug]Correlation ID from ARM api call response : c3eefa7c-9088-44f1-8dc8-dccd7a9f8c4c
##[debug]Processed: ##vso[task.logissue type=error;code=ResourceNotFound;]
##[debug]Deployment Failed with Error: Error: Failed to fetch App Service 'nsclassroomstudent-dgyn27h2dfoyo' details. Error: The Resource 'Microsoft.Web/sites/nsclassroomstudent-dgyn27h2dfoyo' under resource group 'ClassroomInTheCloud' was not found. For more details please go to https://aka.ms/ARMResourceNotFoundFix (CODE: 404)
##[debug]task result: Failed
My code is attached, I have been through the microsoft Site: https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/deploy/azure-app-service-settings?view=azure-devops
I have added the Connected Service Name to my YAML but I still get the error and im not sure what else it wants.
Thanks
#pool:
# vmImage: windows-latest
resources:
repositories:
- repository: Student
name: Classroom In The Cloud/Student
path:
- include: /Student/Student
type: git
ref: master #branch name
variables:
System.Debug: true
azureSubscription: 'azuresubscription'
RG: 'ClassroomInTheCloud'
Location: West Europe
containername: 'private'
appconnectionname: 'RunPipelinesInOtherSubscription'
jobs:
- job: job1
displayName: Create And Publish Artifact
pool:
vmImage: vs2017-win2016
steps:
- checkout: Student
clean: true
- task: DotNetCoreCLI#2
displayName: dotnet restore
inputs:
command: restore
projects: '**/*.csproj'
- task: DotNetCoreCLI#2
displayName: dotnet build
inputs:
projects: '**/*.csproj'
workingDirectory: Student
- task: DotNetCoreCLI#2
displayName: dotnet publish
inputs:
command: publish
projects: '**/*.csproj'
arguments: --output "$(Build.ArtifactStagingDirectory)"
zipAfterPublish: true
modifyOutputPath: false
workingDirectory: Student
- task: PublishPipelineArtifact#1
displayName: Publish Pipeline Artifact
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)'
artifact: 'Student'
publishLocation: 'pipeline'
- job: job2
displayName: 'Get Variable Value for Student Env'
dependsOn: job1
steps:
- task: AzureCLI#1
displayName: 'Azure CLI '
inputs:
azureSubscription: $(azureSubscription)
scriptLocation: inlineScript
inlineScript: |
mkdir $(Pipeline.Workspace)\BlobFile
az storage blob download --container-name $(containername) --file '$(Pipeline.Workspace)/s/student.json' --name 'student.json' --connection-string 'DefaultEndpointsProtocol=https;EndpointSuffix=core.windows.net;AccountName=devscriptstorage;AccountKeyMY VALUE HERE'
- pwsh: |
cd '/home/vsts/work/1/s/'
ls
$armOutput = Get-Content '/home/vsts/work/1/s/student.json' | convertfrom-json
$student = $armOutput.studentvalue #use student not studentvalue
$type = $armOutput.type
$appServicePlanName = $armOutput.appServiceValue
Write-Host "The value of [$student] is [$appServicePlanName]"
Write-Host "##vso[task.setvariable variable=studentvalue;isOutput=true]$student" #use studentvalue not $studentvalue
Write-Host "##vso[task.setvariable variable=appServiceValue;isOutput=true]$appServicePlanName" #use appServiceValue not $appServicePlanName
name: setvarStep
- script: echo $(setvarStep.studentvalue)
- script: echo $(sevarStep.appServiceValue)
name: echovar
- job: job3
displayName: Create Web App
dependsOn: job2
variables:
webappname: $[ dependencies.job2.outputs['setvarStep.studentvalue'] ]
appservice: $[ dependencies.job2.outputs['setvarStep.appServicevalue'] ]
# Download Artifact File
steps:
- download: none
- task: DownloadPipelineArtifact#2
displayName: 'Download Build Artifacts'
inputs:
patterns: '**/*.zip'
path: '$(Build.ArtifactStagingDirectory)'
# deploy to Azure Web App
- task: AzureWebApp#1
inputs:
package: $(Build.ArtifactStagingDirectory)/**/*.zip
azureSubscription: $(azureSubscription)
appName: '$(webappname)'
ResourceGroupName: $(RG)
- task: AzureAppServiceSettings#1
inputs:
azureSubscription: $(azureSubscription)
ConnectedServiceName: $(appconnectionname)
appName: '$(webappname)'
resourceGroupName: $(RG)
# To deploy the settings on a slot, provide slot name as below. By default, the settings would be applied to the actual Web App (Production slot)
# slotName: staging
appSettings: |
{
"name": "DIAGNOSTICS_AZUREBLOBCONTAINERSASURL",
"value": "VALUEINHERE",
"slotSetting": false
},
{
"name": "DIAGNOSTICS_AZUREBLOBRETENTIONINDAYS",
"value": "365",
"slotSetting": false
},
{
"name": "OEM",
"value": "netsupport",
"slotSetting": false
},
{
"name": "SCM_REPOSITORY_PATH",
"value": "d:\\home\\respository",
"slotSetting": false
},
{
"name": "VIDEO_CLIENT_URL",
"value": "https://signal-uks.classroom.cloud",
"slotSetting": false
},
{
"name": "WEBSITE_NODE_DEFAULT_VERSION",
"value": "10.15.2",
"slotSetting": false
}
I ended up fixing this, when changing app settings on an app you have to put in the app name in the displayname area in the code like so:
- task: AzureAppServiceSettings#1
displayName: 'Azure Web App Deploy: $(webappname)'
inputs:
azureSubscription: $(azureSubscription)
ConnectedServiceName: $(appconnectionname)
appName: '$(webappname)'
resourceGroupName: $(RG)
# To deploy the settings on a slot, provide slot name as below. By default, the settings would be applied to the actual Web App (Production slot)
# slotName: staging
appSettings: |
{
"name": "DIAGNOSTICS_AZUREBLOBCONTAINERSASURL",
"value": "VALUEINHERE",
"slotSetting": false
},
{
"name": "DIAGNOSTICS_AZUREBLOBRETENTIONINDAYS",
"value": "365",
"slotSetting": false
},
{
"name": "OEM",
"value": "netsupport",
"slotSetting": false
},
{
"name": "SCM_REPOSITORY_PATH",
"value": "d:\\home\\respository",
"slotSetting": false
},
{
"name": "VIDEO_CLIENT_URL",
"value": "https://signal-uks.classroom.cloud",
"slotSetting": false
},
{
"name": "WEBSITE_NODE_DEFAULT_VERSION",
"value": "10.15.2",
"slotSetting": false
}
I got to this conclusion after playing around with the classic pipeline editor and watching what it would do to the YAML code.

Can someone please tell me why Azure Pipelines does not like this yaml file?

---
variables:
azureSubscription: 'Visual Studio Professional Subscription (237bc9da-22ad-49ea-8411-6cf6a190c18f)'
RG: ClassroomInTheCloud
jobs:
- job: 1
steps:
- task: AzureCLI#1
dislayName: 'Azure CLI '
inputs:
azureSubscription: $(azureSubscription)
scriptLocation: inlineScript
inlineScript: |
mkdir $(Pipeline.Workspace)\BlobFile
az storage blob download --container-name $(containername) --file '$(Pipeline.Workspace)/s/student.json' --name 'student.json' --connection-string 'VALUE IN HERE'
- pwsh: |
cd '/home/vsts/work/1/s/'
ls
$armOutput = Get-Content '/home/vsts/work/1/s/student.json' | convertfrom-json
$studentvalue = $armOutput.studentvalue
$type = $armOutput.type
Write-Host "The value of [$studentvalue] is [$type]"
Write-Output "##vso[task.setvariable variable=$studentvalue;]$type"
Write-Output "##vso[task.setvariable variable=$studentvalue;isOutput=true]$studentvalue"
Write-Output "The Value of studentvalue is [$studentvalue]"
name: setvarStep
- script: |
echo $(studentvalue)
- job: 2
displayName: Create Web App
dependsOn: 1
variables:
webappname: $[studentvalue]
steps:
- task: AzureWebApp#1
inputs:
azureSubscription: $(azureSubscription)
resourceGroupName: $(RG)
appName: $(webappname)
package: $(System.DefaultWorkingDirectory)**/*.csproj
- task: AzureAppServiceSettings#1
displayName: Azure App Service Settings
inputs:
azureSubscription: $(azureSubscription)
appName: $(webappname)
# To deploy the settings on a slot, provide slot name as below. By default, the settings would be applied to the actual Web App (Production slot)
# slotName: staging
appSettings: |
[
{
"name": "DIAGNOSTICS_AZUREBLOBCONTAINERSASURL",
"value": "VALUEINHERE",
"slotSetting": false
},
{
"name": "DIAGNOSTICS_AZUREBLOBRETENTIONINDAYS",
"value": "365",
"slotSetting": false
},
{
"name": "OEM",
"value": "netsupport",
"slotSetting": false
},
{
"name": "SCM_REPOSITORY_PATH",
"value": "d:\\home\\respository",
"slotSetting": false
},
{
"name": "VIDEO_CLIENT_URL",
"value": "https://signal-uks.classroom.cloud",
"slotSetting": false
},
{
"name": "WEBSITE_NODE_DEFAULT_VERSION",
"value": "10.15.2",
"slotSetting": false
}
]
Can someone tell me why I am getting the following errors when putting this YAML file in the pipeline?
Line 14, Col 7: Unexpected Value Display Name
Line 45, Col 6: A sequence was not expected
Please firstly review this doc: YAML schema reference and Define variables, following is the sample yaml of setting a multi-job output variable.
jobs:
# Set an output variable from job A
- job: A
pool:
vmImage: 'vs2017-win2016'
steps:
- powershell: echo "##vso[task.setvariable variable=myOutputVar;isOutput=true]this is the value"
name: setvarStep
- script: echo $(setvarStep.myOutputVar)
name: echovar
# Map the variable into job B
- job: B
dependsOn: A
pool:
vmImage: 'ubuntu-18.04'
variables:
myVarFromJobA: $[ dependencies.A.outputs['setvarStep.myOutputVar'] ] # map in the variable
# remember, expressions require single quotes
steps:
- script: echo $(myVarFromJobA)
name: echovar
Thus the following snippet of yaml should be correct syntax for your reference.
pool:
vmImage: windows-latest
variables:
azureSubscription: 'subscription here'
RG: 'WClassroomInTheCloud'
jobs:
- job: job1
steps:
- task: AzureCLI#1
displayName: 'Azure CLI '
inputs:
azureSubscription: $(azureSubscription)
scriptLocation: inlineScript
inlineScript: |
mkdir $(Pipeline.Workspace)\BlobFile
az storage blob download --container-name $(containername) --file '$(Pipeline.Workspace)/s/student.json' --name 'student.json' --connection-string 'VALUE IN HERE'
- pwsh: |
cd '/home/vsts/work/1/s/'
ls
$armOutput = Get-Content '/home/vsts/work/1/s/student.json' | convertfrom-json
$student = $armOutput.studentvalue #use student not studentvalue
$type = $armOutput.type
Write-Host "The value of [$student] is [$type]"
Write-Host "##vso[task.setvariable variable=studentvalue;isOutput=true]$student" #use studentvalue not $studentvalue
name: setvarStep
- script: echo $(setvarStep.studentvalue)
name: echovar
- job: job2
displayName: Create Web App
dependsOn: job1
variables:
webappname: $[ dependencies.job1.outputs['setvarStep.studentvalue'] ]
steps:
- task: AzureWebApp#1
inputs:
azureSubscription: $(azureSubscription)
appType: 'webApp'
resourceGroupName: $(RG)
appName: $(webappname)
package: $(System.DefaultWorkingDirectory)**/*.csproj
- task: AzureAppServiceSettings#1
inputs:
azureSubscription: $(azureSubscription)
appName: $(webappname)
resourceGroupName: $(RG)
# To deploy the settings on a slot, provide slot name as below. By default, the settings would be applied to the actual Web App (Production slot)
# slotName: staging
appSettings: |
{
"name": "DIAGNOSTICS_AZUREBLOBCONTAINERSASURL",
"value": "VALUEINHERE",
"slotSetting": false
},
{
"name": "DIAGNOSTICS_AZUREBLOBRETENTIONINDAYS",
"value": "365",
"slotSetting": false
},
{
"name": "OEM",
"value": "netsupport",
"slotSetting": false
},
{
"name": "SCM_REPOSITORY_PATH",
"value": "d:\\home\\respository",
"slotSetting": false
},
{
"name": "VIDEO_CLIENT_URL",
"value": "https://signal-uks.classroom.cloud",
"slotSetting": false
},
{
"name": "WEBSITE_NODE_DEFAULT_VERSION",
"value": "10.15.2",
"slotSetting": false
}
The second error is because steps at line 40 is at the level of webappname when it should be at the level of variables. Generally you shouldn't use single-space indentation to avoid such errors.
I am not sure about the first error but try writing displayName instead of dislayName on line 10.

Pass tags (with spaces) to an ARM template using Azure Devops Pipeline

I am using Azure DevOps Pipelines to deploy an ARM template. My template has a tags parameter that I pass into the pipeline using AzureResourceManagerTemplateDeployment#3.
My ARM template has a value in parameters section as an object. tags is an object, which is what many of the sample templates show:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceName": {
"type": "string",
"metadata": {
"description": "Specifies the name of the resource, including its prefix."
}
},
"tags": {
"type": "object",
"defaultValue": {
"Cost Center": "Admin"
}
}
},
"resources": [
{
"apiVersion": "2019-06-01",
"kind": "StorageV2",
"location": "[resourceGroup().location]",
"name": "[parameters('resourceName')]",
"properties": {
"supportsHttpsTrafficOnly": true
},
"sku": {
"name": "Standard_LRS"
},
"type": "Microsoft.Storage/storageAccounts",
"tags": "[parameters('tags')]"
}
]
}
[Edited to match the thread that follows]
I am using ubuntu-latest for my pool. Tags may have spaces.
In my pipeline for simplicity, I set the tags to a variable.
pool:
vmImage: 'ubuntu-latest'
variables:
- name: tags
value: ("Location Region=West US 2" "Environment=${{ parameters.environment }}")
When I call the template deployment, I am passing in the tags as an overrideParameters
- task: AzureResourceManagerTemplateDeployment#3
displayName: "Deploy my templateaccount"
inputs:
deploymentScope: 'Resource Group'
azureResourceManagerConnection: 'ResourceManager-connection'
subscriptionId: ${{ parameters.subscriptionid }}
action: 'Create Or Update Resource Group'
resourceGroupName: '$(resourceGroupName)'
location: '${{ parameters.location }}'
templateLocation: 'Linked artifact'
csmFile: 'mytemplatelocation/azuredeploy.json'
overrideParameters: -resourceName abcdefg76534 -tags "$(tags)"
deploymentMode: 'Incremental'
deploymentOutputs: resourceOutput
- pwsh: Write-Output '$(resourceOutput)'
So far I have not understood how AzureResourceManagerTemplateDeployment#3 on Ubuntu is expecting the tags to be sent.
In each case, the template fails to deploy.
Is this scenario supported in Azure DevOps Pipeline?
Anyone with a suggestion?
The format for the tags that worked in Azure DevOps pipeline AzureResourceManagerTemplateDeployment#3 is to use JSON for your ARM Template objects, such as tags.
Left curly bracket, colon separating the key value pairs, and comma separating the tags.
Each key and the value in my case worked with quotes.
A right curly bracket.
But following templates works by passing in a JSON object: {"Cost Center":"DevTest","Location":"West US"} as your template parameter. In context this looks like:
- task: AzureResourceManagerTemplateDeployment#3
inputs:
deploymentScope: 'Resource Group'
azureResourceManagerConnection: 'ResourceManager-connection'
subscriptionId: 'XXXXX'
action: 'Create Or Update Resource Group'
resourceGroupName: 'rg-wus2-exampletest'
location: 'West US 2'
templateLocation: 'Linked artifact'
csmFile: 'storageaccount/example.azuredeploy.json'
csmParametersFile: 'storageaccount/azuredeploy.parameters.json'
overrideParameters: '-resourceName oweruhsow -resourceTags {"Cost Center":"DevTest","Location":"West US"}'
deploymentMode: 'Complete'
This Pipeline module is expecting JSON objects, not the same format used elsewhere with PowerShell (https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/tag-resource) to deploy from the command line.
Also as a side note, other posts suggested that you name use something other than tags for your tags parameter. The one that worked for me is resourceTags. Here is my ARM template:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceName": {
"type": "string",
"metadata": {
"description": "Specifies the name of the resource"
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for the resources."
}
},
"resourceTags": {
"type": "object",
"defaultValue": {
"Cost Center": "Admin"
}
}
},
"resources": [
{
"apiVersion": "2019-06-01",
"kind": "StorageV2",
"location": "[parameters('location')]",
"name": "[parameters('resourceName')]",
"properties": {
"supportsHttpsTrafficOnly": true
},
"sku": {
"name": "Standard_LRS"
},
"type": "Microsoft.Storage/storageAccounts",
"tags": "[parameters('resourceTags')]"
}
]
}
If you want to set your template object as a the variable, you then can pass it in using the DevOps variable such as $(tags):
variables:
tags: '{"Cost Center":"DevTest","Location":"West US"}'
steps:
- task: AzureResourceManagerTemplateDeployment#3
inputs:
deploymentScope: 'Resource Group'
azureResourceManagerConnection: 'ResourceManager-connection'
subscriptionId: '9f241d6e-16e2-4b2b-a485-cc546f04799b'
action: 'Create Or Update Resource Group'
resourceGroupName: 'rg-wus2-exampletest'
location: 'West US 2'
templateLocation: 'Linked artifact'
csmFile: 'storageaccount/example.azuredeploy.json'
csmParametersFile: 'storageaccount/azuredeploy.parameters.json'
overrideParameters: '-resourceName oweruhso11w -resourceTags $(tags)'
deploymentMode: 'Complete'
Also (as a side note), for some reason, the module needed to have a csmParametersFile or it would just fail with something about failing with RESOURCEGROUP in all caps. The param file is not needed to deploy from the command line, but Pipelines modules does seem to require it.
A csmParamters file that can have just about nothing in it, but it seems to be needed.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": { }
}
This also works using the pool image: windows-latest
Many thanks to ToMakesSense in the post at https://github.com/MicrosoftDocs/azure-devops-docs/issues/9051
If the parameter value you're using has multiple words, enclose them in quotes, even if you're passing them using variables.
For example, -name "parameter value" -name2 "$(var)".
Try to use below format:
overrideParameters: -location "${{ parameters.location }}" -tags "$(tags)"
More details please refer our official doc here-- Azure Resource Group Deployment task

Resources