How to replace a token with different values for different machines? - azure-devops

I use TFS 2018 Update 2 as CI/CD server.
And I use Replace Token task as my token replacement solutions.
When I have a token A in my web.config file,
and it will be deployed to 2 VMs, VM B and VM C, by my tfs release definition.
If I need token A on VM B as Value V1 and token A on VM C as Value V2.
How to do this via Replace Tokens task?
My idea is using a powershell task to update variable A just prior to Replace Token task. Is this possible?
Please kindly provide or share your idea , it will helps me a lot.
thanks.

Option 1
Yes, you can use set a variable using this Powershell
Write-Host "##vso[task.setvariable variable=sauce]crushed tomatoes"
Write-Host "##vso[task.setvariable variable=secret.Sauce;issecret=true]crushed tomatoes with garlic"
see here.
But, you can find more convenient using Jesse's Variable Toolbox.
Option 2
I was thinking of another technique: have the per-machine value in a variable that includes the machine name and let VSTS resolve it for you before handing to the Task. Example
In file:
....#(tok)#....
Variables:
tok_vma = 'v1'
tok_vmb = 'v2'
tok = $(tok_$(Agent.MachineName))
Nested variables are supported as you can see here.
HTH

Related

Cannot read secret variable from AWS SSM Get Parameter task in Azure DevOps Pipeline

My Azure DevOps pipeline has a "AWS SSM Get Parameter" task that reads the parameters into build variables. I can reference these variables successfully using "$(BuildVariableName)" syntax, as long as they are not secret parameters, but for any parameter where "is secret = true", the value of the variable is "***". What do I need to do to be able to read the secret parameters as build variables?
This might not be the most desirable solution, but I was able to partially get this to work by using a Powershell task to copy the secure variable to a non-secure variable.
Write-Host "##vso[task.setvariable variable=NonSecureVariable;]$(SecureVariable)"
However, if $(SecureVariable) contains "$", then all of the characters after the "$" are truncated. Is there a way to get access to the secure variable other than Powershell, or is there a way to get Powershell to work with special characters?

Dynamic variable name in VSTS (Azure DevOps) pipeline

I have a pipeline variable called TestVariable.
I can easily access this variable from a PS script like so:
write-host $(TestVariable)
But if the name of that variable was dynamic, is there any way I can access the variable value from PS?
For example, the name of the variable would go into a string variable. I've tried these combinations as experiments...they just return the variable name, not the value (not surprisingly):
$varname="TestVariable"
write-host $($varname)
write-host $("$varname")
write-host $"($varname)"
write-host $("($varname)")
I think the answer is no, but I want to be sure. Any help much appreciated!
Edit - note
Both answers answer the question but don't solve my problem. After trying the solutions I realized I missed an additional complication which the answers don't help with unfortunately. Am noting here in case someone tries to do something similar.
The extra complication is, the value of the variable is set during the release (I'm trying to access ARM template output variables).
I thought I may be able to hit the API and get the 'live' variable value but unfortunately the release data does not exist (from the API) until the release completes.
So when I call this during a release:
https://vsrm.dev.azure.com/{company}/{project}/_apis/release/releases/$($releaseId)?api-version=5.0
I get "Release with ID 38 does not exist".
Late to the party, but figure I'd share.
As mentioned in the Defined Variables doc, pipeline variables are accessible through
the environment variables. While $(varname) gets processed before the task starts, $env:varname can be invoked mid-run. So you can cheat by using:
Write-Host ('$env:'+"$(varname)" | Invoke-Expression)
The task will resolve $(varname) into its value before the task begins. So the script reads as
Write-Host ("$env:TestVariable" | Invoke-Expression)
And it'll spit out the same as calling $(TestVariable).
Though you do need to respect the rules, such as " " and "." -> "_".
Dynamic variable name in VSTS (Azure DevOps) pipeline
Agree with Krzysztof Madej. There is no out of box way to achieve this.
That because the nested variables (like $($varname) are not yet supported in the build pipelines.
To resolve this issue, you could use the Definitions - Get to get the value of Dynamic variable:
GET https://dev.azure.com/{organization}/{project}/_apis/build/definitions/{definitionId}?api-version=5.1
Below is my test powershell script:
$varname="TestVariable"
$url = "https://dev.azure.com/YourOrganizationName/YourtProjectName/_apis/build/definitions/<definitionsId>?api-version=5.0"
Write-Host "URL: $url"
$pipeline = Invoke-RestMethod -Uri $url -Headers #{
Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
}
$VFDV= $pipeline.variables.$varname.value
Write-Host This is Value For Dynamic Variable: $VFDV
The output:
Hope this helps.
This is not possible directly in YAML but you can use for instance az cli. With this you can set programatically a variable name a get value of it
$variableName = "some"
az pipelines variable list --org "https://dev.azure.com/thecodemanual" --project "DevOps Manual" --pipeline-name "DevOps Manual-CI" --query ($variableName + '.value')
$variableName = "test"
az pipelines variable list --org "https://dev.azure.com/thecodemanual" --project "DevOps Manual" --pipeline-name "DevOps Manual-CI" --query ($variableName + '.value')
Now you can use this code in a powershell task to fetch value of variable.
Here you have info how to install extension.
I know its too late but I can tell you a perfect solution. as we know all the pipeline variables are available as environment variables so we can access the values as below
var=$(TestVariable)
upper= ${var^^} //convert the variable to uppercase as env variables are upper
echo ${!upper}
Please note the above solution is tested and works in bash only. I haven't written for PS

Azure DevOps Release Pipelines - Using env parms with a period . in

I am finding using AZDO Release pipeline variables maddening in Powershell steps.
I am running an Azure PowerShell step to return a primary key value. It is 2 lines…
$primarykey = (Get-AzRelayKey -ResourceGroupName ${env:az-resourcegroupname} -Namespace ${env:az-relaynamespace} -HybridConnection ${env:serviceBus.primaryRelay.ConnectionName} -Name ${env:serviceBus.primaryRelay.KeyName} | Select-Object -ExpandProperty PrimaryKey)
Write-Host "##vso[task.setvariable variable=serviceBus.primaryRelay.Key]$primarykey"
In my pipeline I have a mix of variable names, some I have complete control over (the az- prefixed ones) and others I don’t (the ones starting serviceBus.)
The reason I have no control over the latter is that they are used for a later File Transform step that navigates an appsettings.json file to find/replace values, and its unable to be changed (for example serviceBus.primaryRelay.ConnectionName is a value that is changed in the JSON and the file transform step specifies to navigate the JSON structure, it has to be separated with a period . )
When this script runs it always complains about the -HybridConnection value being empty. This is because the variable has a period in it.
I’ve tried everything I can think of to retrieve that value in the code.
Are they suggesting here that a variable with a period isn’t workable in Powershell in AZDO release pipelines? I’m completely lost.
I have found the answer by looking under the Release Pipelines "Initialize Job" log. It appears to substitute the period . with a dash -
The log revealed this...
[SERVICEBUS_PRIMARYRELAY_CONNECTIONNAME] --> [dev-sbrelay]

Enumerate secret variables in Azure Pipelines

I have a build step in Azure Pipelines that takes the variables from Azure Pipelines and uploads them somewhere equally secret. Currently I have about 50 builds, and each build has anywhere between 5-20 variables.
Some are secret and some are not. So for non secret ones I enumerate all the set ones and off i go; but for secret ones I need to add them to the build step manually; further, because I am writing them with the same keys i need to:
Declare variable in the group e.g. MyPrefix.MyVar
Edit the build step to say /specialtool --vars=MyPrefix.MyVar=$(MyPrefix.MyVar) which is rather mundane.
I found that I can get a list of variables using the Azure DevOps api, so i thought i could just modify the next build step as the build is running.
However, if I update the same build definition that is currently running (to dynamically write the command), it is not sent to the agent (rather, it feels like all arguments for tasks are captured when the whole build is triggered). Any thoughts on how i can dynamically enumerate secret vars to feed to my tool?
You can use VSTS Logging Commands to update variable value during the build. This will make the updated variable to be available in next build task.
Write-Host "##vso[task.setvariable variable=testvar;]testvalue"
When you create a Typescript custom task (NodeJS based), you can access all the build variables that are available to the build at that point in time through the getVariable api.
This function returns an array of VariableInfo:
/** Snapshot of a variable at the time when getVariables was called. */
export interface VariableInfo {
name: string;
value: string;
secret: boolean;
}
When you create a PowerShell3 custom task, you can access all the build variables that are available to the build at that point in time through the Get-VstsTaskVariable function.
Which returns a similar object structure as the Node version:
New-Object -TypeName psobject -Property #{
Name = $info.Name
Value = Get-TaskVariable -Name $info.Name
Secret = $info.Secret
}
If you need to support TFS 2015 and the 1.x build agents as well, you can use (now deprecated) PowerShell handler and enumerate the secrets using a custom powershell function I describe here.
Each task SDK (Typescript and Powershell), supports a function to set variables as well. Here is an example of setting the variable value in Typescript:
tl.setVariable(variable, value, isSecret);
And on PowerShell3:
Set-VstsTaskVariable -name $VariableName -value $Value -Secret $IsSecret
And on PowerShell (deprecated):
Write-Host "##vso[task.setvariable variable=$($VariableName);issecret=$($IsSecret)]$Value"
My suspicion is that you'd want to create a single task that reads the variables and invokes the command you mentioned in your original post to then post these variables to the other secret store. It's not recommended to read all the secrets and either store them in non-secret variables or to somehow pass them along to the next task.
So I have been looking at a solution for this too. It appears the only way to do this at the moment is to write a custom task. Within a custom task you can get hold of secret values dynamically.
An example is the 'vsts-replacetokens-task' (https://github.com/qetza/vsts-replacetokens-task/blob/master/task/index.ts)
Internally it uses the vsts task library (vsts-task-lib/task)
(https://github.com/Microsoft/azure-pipelines-task-lib/blob/master/node/task.ts)
This vsts task library exposes methods like GetVariables() and GetVariable() etc. which can provide what you need. Unfortunately bit long winded, but the only way that I can see.

Unable to read VSTS Online release variables

I'm working with VSTS environment variables and stuck with variables of a secret type.
I'm using POSH script (file) to generate a variable (in fact, to obtain the value from Azure Key Vault and the set this value to the variable):
# Add as a script parameter during the release step
-ResourceGroupNameArg "$(ResourceGroupName)" -KeyVaultNameArg "$(KeyVaultName)" -KeyVaultSecretNameArg "$(KeyVaultSecretName)"
# The script itself
Param(
[string]$ResourceGroupNameArg,
[string]$KeyVaultNameArg,
[string]$KeyVaultSecretNameArg
)
<...>
$secret = Get-AzureKeyVaultSecret -VaultName $KeyVaultNameArg -Name $KeyVaultSecretNameArg
$secretValue = $secret.SecretValueText
Write-Host "##vso[task.setvariable variable=SQLAdministratorPassword;issecret=true]$secretValue"
Here I can pass to the script different KeyVault names (according to my needs) - by substituting the $KeyVaultNameArg and $KeyVaultSecretNameArg variables.
For any other variables configured using ##vso[task.setvariable variable= I am able to retrieve them using the construction $env:DatabaseName (for example in another POSH script) or $(DatabaseName) in agent phase step (using Hosted 2017 agent).
However, for the issecret=true variable or even for a manually created variable I'm unable to retrieve its values during the release deployment process.
According to this article,
The values of hidden (secret) variables are stored securely on the
server and cannot be viewed by users after they are saved. During a
deployment, the Release Management service decrypts these values when
referenced by the tasks and passes them to the agent over a secure
HTTPS channel.
So IMO the variables should be accessible for the script (or even agent phase step) despite they are secret.
Refer to these steps to do it:
Click Library tab
Click + Variable group
Specify variable group name
Enable Link secrets for an Azure Key vault as variables and link Azure key vault
Click +Add to add necessary secret(s)
Edit release definition
Choose Variables tab
Select Variable groups
Click Link variable group to link that variable group
Using the related variable directly in release task ($(variable name))