Public/New-PSUADOVariable.ps1
|
function New-PSUADOVariable { <# .SYNOPSIS Adds a variable to an existing Azure DevOps Variable Group. .DESCRIPTION Adds or updates a variable in an existing variable group. Supports both regular and secret variables. If the variable already exists, it will be updated with the new value. .PARAMETER VariableGroupName (Mandatory) The name of the variable group to add the variable to. Use either VariableGroupName or VariableGroupId. .PARAMETER VariableGroupId (Mandatory) The ID of the variable group to add the variable to. Use either VariableGroupName or VariableGroupId. .PARAMETER VariableName (Mandatory) The name of the variable to add or update. .PARAMETER VariableValue (Mandatory) The value of the variable. .PARAMETER IsSecret (Optional) Switch parameter. If specified, the variable will be marked as secret and its value will be masked. .PARAMETER Project (Mandatory) The Azure DevOps project name containing the variable group. .PARAMETER Organization (Optional) The Azure DevOps organization name under which the project resides. Default value is $env:ORGANIZATION. Set using: Set-PSUUserEnvironmentVariable -Name "ORGANIZATION" -Value "your_org_name" .PARAMETER PAT (Optional) Personal Access Token for Azure DevOps authentication with Variable Groups (read, create, & manage) permissions. Default value is $env:PAT. Set using: Set-PSUUserEnvironmentVariable -Name "PAT" -Value "your_pat_token" .EXAMPLE New-PSUADOVariable -Organization "omg" -Project "psutilities" -VariableGroupName "MyVarGroup" -VariableName "Environment" -VariableValue "Production" Adds a regular variable named "Environment" with value "Production" to the variable group. .EXAMPLE New-PSUADOVariable -Organization "omg" -Project "psutilities" -VariableGroupName "MyVarGroup" -VariableName "ApiKey" -VariableValue "secret123" -IsSecret Adds a secret variable named "ApiKey" to the variable group. The value will be masked. .EXAMPLE New-PSUADOVariable -Organization "omg" -Project "psutilities" -VariableGroupName "MyVarGroup" -VariableName "Region" -VariableValue "East-US" Adds a variable to the psutilities project's variable group. .OUTPUTS [PSCustomObject] .NOTES Author: Lakshmanachari Panuganti Date: 15th October 2025 .LINK https://github.com/lakshmanachari-panuganti/OMG.PSUtilities/tree/main/OMG.PSUtilities.AzureDevOps https://www.linkedin.com/in/lakshmanachari-panuganti/ https://www.powershellgallery.com/packages/OMG.PSUtilities.AzureDevOps https://learn.microsoft.com/en-us/rest/api/azure/devops/distributedtask/variablegroups #> [CmdletBinding(DefaultParameterSetName = 'ByName')] param( [Parameter(Mandatory, ParameterSetName = 'ByName')] [ValidateNotNullOrEmpty()] [string]$VariableGroupName, [Parameter(Mandatory, ParameterSetName = 'ById')] [ValidateRange(1, [int]::MaxValue)] [int]$VariableGroupId, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$VariableName, [Parameter(Mandatory)] [AllowEmptyString()] [string]$VariableValue, [Parameter()] [switch]$IsSecret, [Parameter(Mandatory)] [ValidateNotNullOrEmpty()] [string]$Project, [Parameter()] [ValidateNotNullOrEmpty()] [string]$Organization = $env:ORGANIZATION, [Parameter()] [ValidateNotNullOrEmpty()] [string]$PAT = $env:PAT ) begin { # Display parameters Write-Verbose "[$($MyInvocation.MyCommand.Name)] Parameters:" foreach ($param in $PSBoundParameters.GetEnumerator()) { if ($param.Key -eq 'PAT') { $maskedPAT = if ($param.Value -and $param.Value.Length -ge 3) { $param.Value.Substring(0, 3) + "********" } else { "***" } Write-Verbose " $($param.Key): $maskedPAT" } else { Write-Verbose " $($param.Key): $($param.Value)" } } # Validate Organization (required because ValidateNotNullOrEmpty doesn't check default values from environment variables) if (-not $Organization) { throw "The default value for the 'ORGANIZATION' environment variable is not set.`nSet it using: Set-PSUUserEnvironmentVariable -Name 'ORGANIZATION' -Value '<org>' or provide via -Organization parameter." } # Validate PAT (required because ValidateNotNullOrEmpty doesn't check default values from environment variables) if (-not $PAT) { throw "The default value for the 'PAT' environment variable is not set.`nSet it using: Set-PSUUserEnvironmentVariable -Name 'PAT' -Value '<pat>' or provide via -PAT parameter." } $headers = Get-PSUAdoAuthHeader -PAT $PAT } process { try { # Get variable group by ID or Name if ($PSCmdlet.ParameterSetName -eq 'ById') { Write-Verbose "Retrieving variable group by ID: $VariableGroupId" $getUrl = "https://dev.azure.com/$Organization/$([uri]::EscapeDataString($Project))/_apis/distributedtask/variablegroups/$($VariableGroupId)?api-version=7.1-preview.2" $variableGroup = Invoke-RestMethod -Uri $getUrl -Method Get -Headers $headers -ErrorAction Stop } else { Write-Verbose "Retrieving variable group by name: $VariableGroupName" # Get all variable groups for the project $listUrl = "https://dev.azure.com/$Organization/$([uri]::EscapeDataString($Project))/_apis/distributedtask/variablegroups?api-version=7.1-preview.2" $allGroups = Invoke-RestMethod -Uri $listUrl -Method Get -Headers $headers -ErrorAction Stop $variableGroup = $allGroups.value | Where-Object { $_.name -eq $VariableGroupName } if (-not $variableGroup) { throw "Variable group '$VariableGroupName' not found in project '$Project'" } } Write-Verbose "Found variable group with ID: $($variableGroup.id)" Write-Verbose "Adding/updating variable: $VariableName" # Initialize variables hashtable if it doesn't exist if (-not $variableGroup.variables) { $variableGroup.variables = @{} } # Remove _placeholder if this is the first real variable if ($variableGroup.variables.PSObject.Properties.Name -contains '_placeholder' -and $variableGroup.variables.PSObject.Properties.Count -eq 1) { Write-Verbose "Removing placeholder variable" $variableGroup.variables.PSObject.Properties.Remove('_placeholder') } # Add or update the variable if ($variableGroup.variables.PSObject.Properties.Name -contains $VariableName) { Write-Verbose "Updating existing variable: $VariableName" $variableGroup.variables.$VariableName.value = $VariableValue if ($IsSecret) { $variableGroup.variables.$VariableName | Add-Member -MemberType NoteProperty -Name "isSecret" -Value $true -Force } } else { Write-Verbose "Adding new variable: $VariableName" $newVariable = @{ value = $VariableValue } if ($IsSecret) { $newVariable.isSecret = $true } $variableGroup.variables | Add-Member -MemberType NoteProperty -Name $VariableName -Value $newVariable -Force } # Get project ID for proper reference $projectUrl = "https://dev.azure.com/$Organization/_apis/projects/$([uri]::EscapeDataString($Project))?api-version=7.1" $projectInfo = Invoke-RestMethod -Uri $projectUrl -Method Get -Headers $headers -ErrorAction Stop $projectId = $projectInfo.id # Build clean update payload $updatePayload = @{ id = $variableGroup.id name = $variableGroup.name description = $variableGroup.description type = "Vsts" variables = $variableGroup.variables variableGroupProjectReferences = @( @{ projectReference = @{ id = $projectId } name = $variableGroup.name description = $variableGroup.description } ) } # Update the variable group $updateBody = $updatePayload | ConvertTo-Json -Depth 10 $updateUrl = "https://dev.azure.com/$Organization/$([uri]::EscapeDataString($Project))/_apis/distributedtask/variablegroups/$($variableGroup.id)?api-version=7.1-preview.2" $updatedGroup = Invoke-RestMethod -Uri $updateUrl -Method Put -Headers $headers -Body $updateBody -ErrorAction Stop $displayValue = if ($IsSecret) { "***" } else { $VariableValue } $secretLabel = if ($IsSecret) { " (Secret)" } else { "" } Write-Verbose "Variable '$VariableName' added/updated successfully: $displayValue$secretLabel" return [PSCustomObject]@{ VariableGroupId = $updatedGroup.id VariableGroupName = $updatedGroup.name VariableName = $VariableName VariableValue = $displayValue IsSecret = $IsSecret.IsPresent PSTypeName = 'PSU.ADO.Variable' } } catch { $PSCmdlet.ThrowTerminatingError($_) } } } |