I have successfully created a parameterized linked service for my Azure SQL DB in Azure Data Factory. However, I am facing problems when I try to deploy it across environments. When I publish my data factory, the ARM Template Parameters file that gets generated only contains a single parameter for my linked service, and it is not populated
"Generic_Databases_connectionString": {
"value": ""
},
Following the documentation the microsoft website, I figure I could overwrite this in my deployment with the correct parameterised value. Something like:
"Server=tcp:myserver.database.windows.net,1433;Database=#{linkedService().DBName};User ID=user;Password=fake;Trusted_Connection=False;Encrypt=True;Connection Timeout=30"
However, since my password is stored in Key Vault, i cannot simply include it here. I think the issue is that my ARM template parameters file is simply not being created properly? Has someone faced this issue?
There are multiple ways to approach this. I would assume you use Azure Pipelines to deploy your ARM Templates. Overall you didn't give any specifics about how you deploy your ARM templates.
For both examples to work below I would create an ARM parameters.json file and keep it in the repository.
You can use PowerShell scripts to change files. Here is an example:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: |
$filePath = 'arm.parameters.json'
$tempFilePath = "temp.arm.parameters.json"
$find = 'dbSecret'
$replace = 'VariableNameFromKV'
(Get-Content -Path $filePath) -replace $find, $replace | Add-Content -Path $tempFilePath
Remove-Item -Path $filePath
Move-Item -Path $tempFilePath -Destination $filePath
We personally use something like this: https://marketplace.visualstudio.com/items?itemName=qetza.replacetokens
Then you would tokenize the value in the parameters.json
"Generic_Databases_connectionString": {
"value": "#{NameOfKVSecret}#"
},
Then in your Pipelines per environment you would run this task that replaces the tokenized value :
#YAML file
- task: a8515ec8-7254-4ffd-912c-86772e2b5962#3
displayName: 'Replace Tokens in ARM parameters'
inputs:
rootDirectory: '$(Pipeline.Workspace)/drop'
targetFiles: '**/fileToReplace.json'
encoding: 'auto'
writeBOM: true
keepToken: false
tokenPrefix: '#{'
tokenSuffix: '}#'
enableTelemetry: false
actionOnMissing: 'continue'
verbosity: detailed
#Rest of deployment tasks using the replaced parameters.json file
You would need to import variables into the pipeline. There are multiple ways to do that depending if you use classic pipelines or yamls. If you for example have seperate Key Vaults for your environments you could store those values there and use:
#YAML file
- task: AzureKeyVault#1
displayName: Get KeyVault variables
inputs:
azureSubscription: '${{ parameters.KV_APP_NAME }}'
KeyVaultName: '$(vg.keyVaultName)'
This task reads the values from the KV into the Pipelines as variables that you can use then in the deployment. In your case, you would have a stage per environment.
Both the above tasks can also be used in the classic pipelines.
Overall for this to work you should design the deployment pipelines in a way, that every environment has its own stage and the configuration values can be replaced per environment like so:
Documentation for classic pipelines:
Documentation for yaml pipelines variables
Related
Different variable group for different schedule trigger in Azure devops
Do you mean you would like to use variable in different variable groups in different schedule triggered pipelines? What about go to UI->library->choose the specific variable group then choose pipeline permission and add the corresponding schedule pipelines.
Then add the variable group name in the schedule pipelines yml and you could output the value of variables in the group, for example if there is a variable named 'SomeNumber' in the variable group.
variables:
- group: MyVarGroup
steps:
- task: PowerShell#2
inputs:
targetType: 'inline'
script: Write-Host "$(SomeNumber)"
But if you mean to add variable into the schedule trigger syntax, you can't use pipeline variables when specifying schedules in yml. You could see the official doc for more details.
If it is not what you want, please kindly specific your issue with the sample yaml, then I will try to do further investigation.
I have been writing some terraform and using Azure Devops to deploy the pipeline. However if I use a variable $(serviceconnection) for the service connection it fails with the following error:
There was a resource authorization issue: "The pipeline is not valid. Job DeployDev: Step TerraformCLI1 input backendServiceArm references service connection $(serviceconnection) which could not be found. The service connection does not exist or has not been authorized for use.
I Have tried authorising it but no luck. Is there any workaround?
The task is a YAML task to use terraform as below :
- task: charleszipp.azure-pipelines-tasks-terraform.azure-pipelines-tasks-terraform-cli.TerraformCLI#0
displayName: 'Terraform Init'
inputs:
command: init
workingDirectory: $(Agent.BuildDirectory)/a/azuredirectory/terraform
backendType: azurerm
backendServiceArm: $(serviceconnection)
backendAzureRmResourceGroupName: $(ResourceGroupName)
backendAzureRmStorageAccountName: $(StorageAccountName)
backendAzureRmContainerName: $(ContainerName)
backendAzureRmKey: $(AzureRmKey)
You need to use a Template expression syntax for the service connection variable:
backendServiceArm: ${{ variables.serviceconnection }}
I imagine it's because the service connection needs to be known before the pipeline runs.
Sample use case. Using a variable file called variable.dev.yaml:
variables:
serviceconnection: my-dev-service-connection-name
...
You could then reference that in your pipeline:
jobs:
- job: myJob
...
variables:
- template: ./variable.dev.yaml
steps:
- task: AzureCLI#2
inputs:
azureSubscription: ${{ variables.serviceconnection }}
...
If you want to use runtime variable like $(serviceconnection), it is not supported now.
You can use ${{ variables.serviceconnection }} as Thomas recommended. But this practice means that you have to specify variables in advance(Before you run the pipeline).
For service connections, you can specify a value directly or use the ’compile-time variable‘ ${{xxx}}, which will expand and then populate the service connection section with values before running. In this usage of $(xxx), the service connection of the task cannot be obtained, because this is a runtime value.
The service connection needs to be specified before running. The changes (runtime changes) of the variables during the pipeline run will not be acquired by the service connection part of the subsequent task.
You are using a runtime variable.
But run time variables aren't supported for service connection OR azure subscription. The variable will get initialized at the run time.
https://github.com/microsoft/azure-pipelines-tasks/issues/10376#issuecomment-514477023
You can follow below method to use different service connection:
https://stackoverflow.com/a/57520153/6261890
But still need point that, parameters are expanded just before the pipeline runs, hardcode the specific service connection is unavoidable, this is by design.
Also clearly in this official document:
https://learn.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#use-a-service-connection
I am looking at deploying a .Net Core WebApi service to an Azure App Service and as part of the deployment I am keen to update the connection string in the appsettings.json with the CosmosDb connection string. I have a Azure KeyVault which has the connection string stored in there as a secret.
Using the YAML build pipeline for CI/CD I have the following (snippet) from my pipeline
- task: AzureKeyVault#1
inputs:
azureSubscription: '<service-principal>'
KeyVaultName: '<keyvault-name>'
SecretsFilter: '*'
RunAsPreJob: true
- task: AzureRmWebAppDeployment#4
inputs:
ConnectionType: 'AzureRM'
azureSubscription: '<service-principal>'
appType: 'webApp'
WebAppName: '<ci-resource-group>'
VirtualApplication: '/'
packageForLinux: '$(System.DefaultWorkingDirectory)/**/*.zip'
JSONFiles: '**/appsettings.json'
These two tasks are in a stage which starts with downloading the published artifact from a previous stage.
So the Azure App Service Deploy task can do JSON transformation but I need to define a variable in the format ConnectionStrings:CosmosDb with the value from the secret stored in the keyvault and that I am not certain of how to do!
Firstly, is this the correct way? I have seen articles about using a reference to the secret in the keyvault, is that the correct way?
The keyvault secrets are available to the pipeline using $(secret), how can I create a variable for the AzureRmWebAppDeployment#4 task as above?
Everything I have found so far points to the Classic release pipelines, and using variable groups but this needs to be part of the YAML pipeline.
Following the docs
To substitute values in nested levels of the file, concatenate the names with a period (.) in hierarchical order.
But your secrets cannot have dots in name so you must do a small rewrite between those two steps:
- powershell: |
echo "##vso[task.setvariable variable=ConnectionStrings.CosmosDb;]$(ConnectionStrings-CosmosDb)"
where your secret is ConnectionStrings-CosmosDb
I am trying to invoke rest api from within Azure DevOps build agent. For that I need bearertoken which I can get in my local machine with:
$accessToken = ((Get-AzContext).TokenCache.ReadItems() | Where { $_.TenantId -eq (Get-AzContext).Tenant } | Sort-Object -Property ExpiresOn -Descending)[0].AccessToken
However, on the build agent this never returns any tokens. Is there some other way I should try to get that token?
Have you enabled the 'Allow scripts to access the OAuth token' setting in the Agents settings?
This would be for builds and releases using the visual designer.
For YAML builds see the predefined variable documentation. There are notes about using the "System.AccessToken" variable in a script. Essentially, you must explicitly map System.AccessToken into the pipeline using a variable. You can do this at the step or task level:
steps:
- bash: echo This is a script that could use $SYSTEM_ACCESSTOKEN
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- powershell: Write-Host "This is a script that could use $env:SYSTEM_ACCESSTOKEN"
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
Thanks for the suggestions and ideas. Answer was too simple to figure it out. I ripped most of the oneliner and now it works by only using:
$accessToken = ((Get-AzContext).TokenCache.ReadItems()
May you please suggest how to lowercase the environment name in Azure DevOps pipelines.
When environment name is "Test" I want account name to be myprefixtest.
I don't think there is an option to do to lower when you use the task (like in your Azure CLI task), but you can add a small PowerShell script that does it before the Azure CLI task:
$envName = "$(Release.EnvironmentName)"
$lower = $envName.ToLower()
Write-Host "##vso[task.setvariable variable=Release.EnvironmentName;]$lower"
Have you seen lower? This might help with what you are trying to do.