diff --git a/docs/preview/03-Features/powershell/azure-devops.md b/docs/preview/03-Features/powershell/azure-devops.md index e8821856..bc8ce134 100644 --- a/docs/preview/03-Features/powershell/azure-devops.md +++ b/docs/preview/03-Features/powershell/azure-devops.md @@ -40,6 +40,22 @@ PS> Set-AzDevOpsVariable "my-variable" "my-variable-value" -AsSecret ##vso[task.setvariable variable=my-variable;issecret=true] *** ``` +## Setting a variable in an Azure DevOps variable group + +Assign a value to a DevOps variable group during the execution of a pipeline. + +| Parameter | Mandatory | Description | +| --------------------- | --------- | ------------------------------------------------------ | +| `VariableGroupName` | yes | The name of the remote variable group on Azure DevOps | +| `VariableName` | yes | The name of the variable to set in the variable group | +| `VariableValue` | yes | The value of the variable to set in the variable group | + +**Example** + +```powershell +PS> Set-AzDevOpsGroupVariable -VariableGroupName "product-v1-dev" -VariableName "Product.Api.Url" -VariableValue "https://product/api/" +``` + ## Setting ARM outputs to Azure DevOps variable group Stores the Azure Resource Management (ARM) outputs in a variable group on Azure DevOps. diff --git a/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.psd1 b/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.psd1 index 728c1b72..b7faba8c 100644 Binary files a/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.psd1 and b/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.psd1 differ diff --git a/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.psm1 b/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.psm1 index 87586675..c414814d 100644 --- a/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.psm1 +++ b/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.psm1 @@ -5,10 +5,10 @@ .Description Assign a value to a DevOps pipeline variable during the execution of this pipeline. - .Parameter VariableName + .Parameter Name The name of the variable to set in the pipeline. - .Parameter VariableValue + .Parameter Value The value of the variable to set in the pipeline. #> function Set-AzDevOpsVariable { @@ -27,6 +27,34 @@ function Set-AzDevOpsVariable { Export-ModuleMember -Function Set-AzDevOpsVariable +<# + .Synopsis + Set a variable in the Azure DevOps variable group. + + .Description + Assign a value to a DevOps variable group during the execution of an Azure DevOps pipeline. + + .Parameter VariableGroupName + The name of the Azure DevOps variable group to updat with a new variable. + + .Parameter VariableName + The name of the variable to set in the variable group. + + .Parameter VariableValue + The value of the variable to set in the variable group. +#> +function Set-AzDevOpsGroupVariable { + param( + [string][parameter(Mandatory = $true)]$VariableGroupName, + [string][parameter(Mandatory = $true)]$VariableName, + [string][parameter(Mandatory = $true)]$VariableValue + ) + + . $PSScriptRoot\Scripts\Set-AzDevOpsGroupVariable.ps1 -VariableGroupName $VariableGroupName -VariableName $VariableName -VariableValue $VariableValue +} + +Export-ModuleMember -Function Set-AzDevOpsGroupVariable + <# .Synopsis Sets the ARM outputs as a variable group on Azure DevOps. diff --git a/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.pssproj b/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.pssproj index 1ba027ce..4b1290c2 100644 --- a/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.pssproj +++ b/src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.pssproj @@ -34,6 +34,7 @@ + diff --git a/src/Arcus.Scripting.DevOps/Scripts/Set-AzDevOpsArmOutputs.ps1 b/src/Arcus.Scripting.DevOps/Scripts/Set-AzDevOpsArmOutputs.ps1 index f044a88d..462811a0 100644 --- a/src/Arcus.Scripting.DevOps/Scripts/Set-AzDevOpsArmOutputs.ps1 +++ b/src/Arcus.Scripting.DevOps/Scripts/Set-AzDevOpsArmOutputs.ps1 @@ -5,59 +5,6 @@ param( [switch][parameter(Mandatory = $false)] $UpdateVariablesForCurrentJob = $false ) -function Add-VariableGroupVariable() { - [CmdletBinding()] - param( - [string][parameter(Mandatory = $true)]$VariableGroupName, - [string][parameter(Mandatory = $true)]$variableName, - [string][parameter(Mandatory = $true)]$variableValue - ) - BEGIN { - Write-Verbose "Retrieving Azure DevOps project details for variable group '$VariableGroupName'..." - [String]$project = "$env:SYSTEM_TEAMPROJECT" - [String]$projectUri = "$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" - [String]$apiVersion = "4.1-preview.1" - Write-Debug "Using Azure DevOps project: $project, project URI: $projectUri" - - - Write-Verbose "Setting authorization headers to retrieve potential existing Azure DevOps variable group '$VariableGroupName'..." - if ([string]::IsNullOrEmpty($env:SYSTEM_ACCESSTOKEN)) { - Write-Error "The SYSTEM_ACCESSTOKEN environment variable is empty. Remember to explicitly allow the build job to access the OAuth Token!" - } - $headers = @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } - - - Write-Verbose "Getting Azure DevOps variable group '$VariableGroupName'..." - $getVariableGroupUrl = $projectUri + $project + "/_apis/distributedtask/variablegroups?api-version=" + $apiVersion + "&groupName=" + $VariableGroupName - $variableGroup = (Invoke-RestMethod -Uri $getVariableGroupUrl -Headers $headers -Verbose) - - $releaseName = $env:RELEASE_RELEASENAME - if ([string]::IsNullOrEmpty($releaseName)) { - $releaseName = $env:BUILD_DEFINITIONNAME + " " + $env:BUILD_BUILDNUMBER - } - - if ($variableGroup.value) { - Write-Host "Set properties for update of existing Azure DevOps variable group '$variableGroupName'" - $variableGroup = $variableGroup.value[0] - $variableGroup | Add-Member -Name "description" -MemberType NoteProperty -Value "Variable group that got auto-updated by release '$releaseName'." -Force - $method = "Put" - $upsertVariableGroupUrl = $projectUri + $project + "/_apis/distributedtask/variablegroups/" + $variableGroup.id + "?api-version=" + $apiVersion - } else { - Write-Host "Set properties for creation of new Azure DevOps variable group '$VariableGroupName'" - $variableGroup = @{name = $VariableGroupName; type = "Vsts"; description = "Variable group that got auto-updated by release '$releaseName'."; variables = New-Object PSObject; } - $method = "Post" - $upsertVariableGroupUrl = $projectUri + $project + "/_apis/distributedtask/variablegroups?api-version=" + $apiVersion - } - - $variableGroup.variables | Add-Member -Name $variableName -MemberType NoteProperty -Value @{value = $variableValue } -Force - - Write-Verbose "Upserting Azure DevOps variable group '$variableGroupName'..." - $body = $variableGroup | ConvertTo-Json -Depth 10 -Compress - Write-Debug $body - Invoke-RestMethod $upsertVariableGroupUrl -Method $method -Body $body -Headers $headers -ContentType 'application/json; charset=utf-8' -Verbose - } -} - Write-Verbose "Geting ARM outputs from '$ArmOutputsEnvironmentVariableName' environment variable..." $json = [System.Environment]::GetEnvironmentVariable($ArmOutputsEnvironmentVariableName) $armOutputs = ConvertFrom-Json $json @@ -68,7 +15,7 @@ foreach ($output in $armOutputs.PSObject.Properties) { if ($UpdateVariableGroup) { Write-Host Adding variable $output.Name with value $variableValue to variable group $VariableGroupName - Add-VariableGroupVariable -VariableGroupName $VariableGroupName -variableName $variableName -variableValue $variableValue + . $PSScriptRoot\Set-AzDevOpsGroupVariable.ps1 -VariableGroupName $VariableGroupName -VariableName $variableName -VariableValue $variableValue } if ($UpdateVariablesForCurrentJob) { diff --git a/src/Arcus.Scripting.DevOps/Scripts/Set-AzDevOpsGroupVariable.ps1 b/src/Arcus.Scripting.DevOps/Scripts/Set-AzDevOpsGroupVariable.ps1 new file mode 100644 index 00000000..4d11274a --- /dev/null +++ b/src/Arcus.Scripting.DevOps/Scripts/Set-AzDevOpsGroupVariable.ps1 @@ -0,0 +1,50 @@ +param( + [string][parameter(Mandatory = $true)]$VariableGroupName, + [string][parameter(Mandatory = $true)]$VariableName, + [string][parameter(Mandatory = $true)]$VariableValue +) + +Write-Verbose "Retrieving Azure DevOps project details for variable group '$VariableGroupName'..." +[String]$project = "$env:SYSTEM_TEAMPROJECT" +[String]$projectUri = "$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" +[String]$apiVersion = "7.1" +Write-Debug "Using Azure DevOps project: $project, project URI: $projectUri" + + +Write-Verbose "Setting authorization headers to retrieve potential existing Azure DevOps variable group '$VariableGroupName'..." +if ([string]::IsNullOrEmpty($env:SYSTEM_ACCESSTOKEN)) { + Write-Error "The SYSTEM_ACCESSTOKEN environment variable is empty. Remember to explicitly allow the build job to access the OAuth Token!" +} +$headers = @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } + + +Write-Verbose "Getting Azure DevOps variable group '$VariableGroupName'..." +$getVariableGroupUrl = $projectUri + $project + "/_apis/distributedtask/variablegroups?api-version=" + $apiVersion + "&groupName=" + $VariableGroupName +$variableGroup = (Invoke-RestMethod -Uri $getVariableGroupUrl -Headers $headers -Verbose) + +$releaseName = $env:RELEASE_RELEASENAME +if ([string]::IsNullOrEmpty($releaseName)) { + $releaseName = $env:BUILD_DEFINITIONNAME + " " + $env:BUILD_BUILDNUMBER +} + +if ($variableGroup.value) { + Write-Host "Set properties for update of existing Azure DevOps variable group '$variableGroupName'" + $variableGroup = $variableGroup.value[0] + $variableGroup | Add-Member -Name "description" -MemberType NoteProperty -Value "Variable group that got auto-updated by pipeline '$releaseName'." -Force + $method = "Put" + $upsertVariableGroupUrl = $projectUri + $project + "/_apis/distributedtask/variablegroups/" + $variableGroup.id + "?api-version=" + $apiVersion +} else { + Write-Host "Set properties for creation of new Azure DevOps variable group '$VariableGroupName'" + $variableGroup = @{name = $VariableGroupName; type = "Vsts"; description = "Variable group that got auto-updated by pipeline '$releaseName'."; variables = New-Object PSObject; } + $method = "Post" + $upsertVariableGroupUrl = $projectUri + $project + "/_apis/distributedtask/variablegroups?api-version=" + $apiVersion +} + +$variableGroup.variables | Add-Member -Name $VariableName -MemberType NoteProperty -Value @{value = $VariableValue } -Force + +Write-Verbose "Upserting Azure DevOps variable group '$variableGroupName'..." +$body = $variableGroup | ConvertTo-Json -Depth 10 -Compress +Write-Host $body + +Write-Host "$method -> $upsertVariableGroupUrl" +Invoke-RestMethod $upsertVariableGroupUrl -Method $method -Body $body -Headers $headers -ContentType 'application/json; charset=utf-8' -Verbose diff --git a/src/Arcus.Scripting.Tests.Integration/Arcus.Scripting.DevOps.tests.ps1 b/src/Arcus.Scripting.Tests.Integration/Arcus.Scripting.DevOps.tests.ps1 index 5ccb0192..4646a00c 100644 --- a/src/Arcus.Scripting.Tests.Integration/Arcus.Scripting.DevOps.tests.ps1 +++ b/src/Arcus.Scripting.Tests.Integration/Arcus.Scripting.DevOps.tests.ps1 @@ -1,5 +1,66 @@ Import-Module -Name $PSScriptRoot\..\Arcus.Scripting.DevOps -ErrorAction Stop +function global:Get-AzDevOpsGroup { + param($VariableGroupName) + + $VariableGroupName = $VariableGroupName -replace ' ', '%20' + $projectId = $env:SYSTEM_TEAMPROJECTID + $collectionUri = $env:SYSTEM_COLLECTIONURI + $getUri = "$collectionUri" + "$projectId/_apis/distributedtask/variablegroups?groupName=" + $VariableGroupName + "&api-version=7.1" + $headers = @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } + + Write-Host "GET -> $getUri" + $getResponse = Invoke-WebRequest -Uri $getUri -Method Get -Headers $headers + $json = ConvertFrom-Json $getResponse.Content + + $variableGroup = $json.value[0] + Write-Host "$($getResponse.StatusCode) $variableGroup <- $getUri" + + return $variableGroup +} + +function global:Remove-AzDevOpsVariableGroup { + param($VariableGroupName) + + $variableGroup = Get-AzDevOpsGroup -VariableGroupName $VariableGroupName + + $VariableGroupName = $VariableGroupName -replace ' ', '%20' + $projectId = $env:SYSTEM_TEAMPROJECTID + $collectionUri = $env:SYSTEM_COLLECTIONURI + $deleteUri = "$collectionUri" + "$projectId/_apis/distributedtask/variablegroups/" + $variableGroup.id + "?api-version=7.1" + $headers = @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } + + Write-Host "DELETE -> $deleteUri" + $deleteResponse = Invoke-WebRequest -Uri $deleteUri -Method Delete -Headers $headers + Write-Host "$($deleteResponse.StatusCode) <- $deleteUri" +} + +function global:Get-AzDevOpsGroupVariable { + param($VariableGroupName, $VariableName) + + $json = Get-AzDevOpsGroup -VariableGroupName $VariableGroupName + $variable = $json.variables.PSObject.Properties | where { $_.Name -eq $VariableName } + + return $variable +} + +function global:Remove-AzDevOpsGroupVariable { + param($VariableGroupName, $VariableName) + + $json = Get-AzDevOpsGroup -VariableGroupName $VariableGroupName + $json.variables.PSObject.Properties.Remove($VariableName) + + $project = "$env:SYSTEM_TEAMPROJECT" + $projectUri = "$env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" + $upsertVariableGroupUrl = $projectUri + $project + "/_apis/distributedtask/variablegroups/$($json.id)?api-version=7.1" + $headers = @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } + + $json = $json | ConvertTo-Json -Depth 10 -Compress + Write-Host "PUT $json -> $upsertVariableGroupUrl" + $putResponse = Invoke-WebRequest -Uri $upsertVariableGroupUrl -Method Put -Headers $headers -Body $json -ContentType 'application/json; charset=utf-8' + Write-Host "$($putResponse.StatusCode) <- $upsertVariableGroupUrl" +} + InModuleScope Arcus.Scripting.DevOps { Describe "Arcus Azure DevOps integration tests" { BeforeEach { @@ -43,6 +104,7 @@ InModuleScope Arcus.Scripting.DevOps { $collectionUri = $env:SYSTEM_COLLECTIONURI $requestUri = "$collectionUri" + "$projectId/_apis/build/builds/" + $buildId + "/leases?api-version=7.0" $headers = @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" } + try { # Act Save-AzDevOpsBuild -ProjectId $projectId -BuildId $buildId -DaysToKeep 10 @@ -66,6 +128,8 @@ InModuleScope Arcus.Scripting.DevOps { } } } + } + Context "Azure DevOps variable group" { It "Sets the DevOps variable group description with the release name" { # Arrange $variableGroupName = $config.Arcus.DevOps.VariableGroup.Name @@ -86,6 +150,40 @@ InModuleScope Arcus.Scripting.DevOps { $json = ConvertFrom-Json $getResponse.Content $json.value[0].description | Should -BeLike "*$env:Build_DefinitionName*$env:Build_BuildNumber*" } + It "Sets a new variable to an existing DevOps variable group" { + # Arrange + $variableGroupName = $config.Arcus.DevOps.VariableGroup.Name + $variableName = [System.Guid]::NewGuid().ToString() + $expectedValue = [System.Guid]::NewGuid().ToString() + try { + # Act + Set-AzDevOpsGroupVariable -VariableGroupName $variableGroupName -VariableName $variableName -VariableValue $expectedValue + + # Assert + $actualValue = Get-AzDevOpsGroupVariable -VariableGroupName $variableGroupName -VariableName $variableName + $actualValue | Should -Be $variableValue + + } finally { + Remove-AzDevOpsGroupVariable -VariableGroupName $variableGroupName -VariableName $variableName + } + } + It "Sets a new variable to a new DevOps variable group" { + # Arrange + $variableGroupName = [System.Guid]::NewGuid() + $variableName = [System.Guid]::NewGuid() + $variableValue = [System.Guid]::NewGuid() + try { + # Act + Set-AzDevOpsGroupVariable -VariableGroupName $variableGroupName -VariableName $variableName -VariableValue $variableValue + + # Assert + $actualValue = Get-AzDevOpsGroupVariable -VariableGroupName $variableGroupName -VariableName $variableName + $actualValue | Should -Be $variableValue + + } finally { + Remove-AzDevOpsVariableGroup -VariableGroupName $variableGroupName + } + } } } }