Az.DevOps.Blueprint.psm1
param ( [Parameter(Position=1, Mandatory=$false)] [ValidateNotNull()] $DevOpsUri = $env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI, [Parameter(Position=2, Mandatory=$false)] [ValidateNotNull()] $DevOpsProject = $env:SYSTEM_TEAMPROJECT, [Parameter(Position=7, Mandatory=$false)] [ValidateNotNull()] $context = (Get-AzContext) ) function Get-AzDevOpsBlueprintParameters { <# .SYNOPSIS Determines the Blueprint Parameters and creates two Variable Groups (required/not-required) containing the Variables required to match Parameters in the Blueprint. .DESCRIPTION The parameters in the Blueprint, follow a pattern where the first part of the parameter matches a Blueprint artifact. For example: keyvault_ad-domain-admin-user-password artifact | separator | parameter keyvault | _ | ad-domain-admin-user-password .PARAMETER InputPath A string containing the path to the Blueprint JSON File e.g. 'C:\Repos\Blueprints\Small_ISO27001_Shared-Services' .EXAMPLE Get-AzDevOpsBlueprintParameters ` -InputPath 'C:\Repos\Blueprints\Small_ISO27001_Shared-Services' Result: 3 x Variable Groups: - BLUEPRINT_Parameters_Required - BLUEPRINT_Parameters_Not_Required - BLUEPRINT_Resource_Groups #> [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [ValidateNotNull()] [string]$InputPath, [Parameter(Mandatory=$true)] $DevOpsPAT, [Parameter(Mandatory=$false)] $DevOpsApiVersion = "5.0-preview.1" ) try { # variables $DevOpsHeader = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$DevOpsPAT"))} $getVarGrpsUri = "{0}{1}/_apis/distributedtask/variablegroups?api-version={2}" -f $DevOpsUri, $DevOpsProject, $DevOpsApiVersion $VariableGroups = (Invoke-RestMethod -Uri $getVarGrpsUri -Method GET -Header $DevOpsHeader).value $rawBlueprint = Get-Content -Path "$($InputPath)\Blueprint.json" | ConvertFrom-Json #region Resource Group Parameters $json = @{ variables = @{} type = "Vsts" name = "BLUEPRINT_Resource_Groups" description = "These Variables in Azure DevOps map to Resource Group Parameters in the Blueprint" } Write-Output "Create these Variables in Azure DevOps for Resource Groups and assign values:" foreach ($param in $rawBlueprint.properties.resourceGroups.PSObject.Properties) { if ([string]::IsNullOrWhitespace($param.Value.Name)) { $key = "RG_$($param.Value.metadata.DisplayName -Replace ' ','')" $value = "REPLACE THIS TEXT WITH A NAME FOR A RESOURCE GROUP" Write-Output "`tName: $($key)" Write-Output "`tBlueprint Resource Group: $($param.Name)`r`n" $json.variables.Add($key, $value) } } New-AzDevOpsBlueprintVariableGroup -Json $json -VariableGroups $VariableGroups -DevOpsPAT $DevOpsPAT #endregion #region Parameters that need Values $json = @{ variables = @{} type = "Vsts" name = "BLUEPRINT_Parameters_Required" description = "These Variables in Azure DevOps map to Parameters in the Blueprint that need values" } Write-Output "Create these Variables in Azure DevOps and assign values:" foreach ($param in $rawBlueprint.properties.parameters.PSObject.Properties) { if ([string]::IsNullOrWhitespace($param.Value.defaultValue)) { $key = "BP_$($param.Name -Replace '-','')" $value = Test-ParameterValue -Value $param.Value Write-Output "`tName: $($key)" Write-Output "`tType: $($param.Value.Type)`r`n" $json.variables.Add($key, $value) } } New-AzDevOpsBlueprintVariableGroup -Json $json -VariableGroups $VariableGroups -DevOpsPAT $DevOpsPAT #endregion #region Parameters that have Default Values and can be overridden $json = @{ variables = @{} type = "Vsts" name = "BLUEPRINT_Parameters_Not_Required" description = "These Variables in Azure DevOps map to Parameters in the Blueprint that have default values and can be overridden." } Write-Output "These Variables have Default Values. Create any of these Variable, assign a value to override the Default Value:" foreach ($param in $rawBlueprint.properties.parameters.PSObject.Properties) { if (![string]::IsNullOrWhitespace($param.Value.defaultValue)) { $key = "BP_$($param.Name -Replace '-','')" $value = Test-ParameterDefaultValue -Value $param.Value Write-Output "`tName: BP_$($param.Name -Replace '-','')" Write-Output "`tValue: $($value)" Write-Output "`tType: $($param.Value.Type)`r`n" $json.variables.Add($key, $value) } } New-AzDevOpsBlueprintVariableGroup -Json $json -VariableGroups $VariableGroups -DevOpsPAT $DevOpsPAT } catch { if ($_.ErrorDetails.Message) {$ErrDetails = $_.ErrorDetails.Message } else {$ErrDetails = $_} Get-StandardError -Exception $($ErrDetails) } } function Get-AzDevOpsBlueprintVariableGroups { <# .SYNOPSIS Get Azure DevOps Variable Group or Groups .PARAMETER Name A string containing the name of a variable group to target. By default all Variable Groups are returned unless the a Variable Group is named .EXAMPLE Get all Variable Groups for an Azure DevOps Project Get-AzDevOpsBlueprintVariableGroups .EXAMPLE Get a Variable Group for an Azure DevOps Project Get-AzDevOpsBlueprintVariableGroups -Name TEST-Variable-Group #> [CmdletBinding(DefaultParameterSetName = 'AllScope')] param ( [Parameter(Mandatory=$false, ParameterSetName = 'ByName')] [ValidateNotNull()] [string]$Name, [Parameter(Mandatory=$true)] $DevOpsPAT, [Parameter(Mandatory=$false)] $DevOpsApiVersion = "5.0-preview.1" ) try { # variables $DevOpsHeader = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$DevOpsPAT"))} $getVarGrpsUri = "{0}{1}/_apis/distributedtask/variablegroups?api-version={2}" -f $DevOpsUri, $DevOpsProject, $DevOpsApiVersion $results = (Invoke-RestMethod -Uri $getVarGrpsUri -Method GET -Headers $DevOpsHeader).value if ($Name) { $results = $results | Where-Object Name -eq $Name } return $results } catch { if ($_.ErrorDetails.Message) {$ErrDetails = $_.ErrorDetails.Message } else {$ErrDetails = $_} Get-StandardError -Exception $($ErrDetails) } } function Find-AzDevOpsBlueprintParameters { <# .SYNOPSIS Matches different types of Blueprint parameters with Variables in Azure DevOps .DESCRIPTION Finds Blueprint Parameters in a Raw Blueprint and matches Resource Group, Parameters and Secure Parameters with Variables already defined in Azure DevOps .PARAMETER Type A string containing the Type of Parameter to match. There are two options 1. ResourceGroup 2. Parameters .PARAMETER RawBlueprint A PSCustomObject containing the raw data for a Blueprint. .PARAMETER Location A String containing the Location if a Resource Group parameter is required .EXAMPLE Match Blueprint Resource Group parameters Find-AzDevOpsBlueprintParameters ` -Type ResourceGroups ` -RawBlueprint (Get-Content -Raw '/Repos/Blueprints/Small_ISO27001_Shared-Services/Small_ISO27001_Shared-Services.json' | ConvertFrom-Json) .EXAMPLE Match Blueprint Parameters parameters Find-AzDevOpsBlueprintParameters ` -Type Parameters ` -RawBlueprint (Get-Content -Raw '/Repos/Blueprints/Small_ISO27001_Shared-Services/Small_ISO27001_Shared-Services.json' | ConvertFrom-Json) #> param ( [Parameter(Mandatory=$True)] [ValidateNotNull()] [string]$Type, [Parameter(Mandatory=$True)] [ValidateNotNull()] [PSCustomObject]$RawBlueprint, [Parameter(Mandatory=$false)] [ValidateNotNull()] [string]$Location ) switch ($Type) { "ResourceGroups" { $resourceGroups = @{} foreach ($rg in $rawBlueprint.properties.resourceGroups.PSObject.Properties) { foreach ($envVariable in (Get-ChildItem env: | Where-Object Name -like "RG_*")) { $tmp = $rg.Value.metadata.DisplayName -replace " ", "" $tmpEnv = $envVariable.Name -replace "RG_", "" $tmpValue = $null if ($tmp -like "*$($tmpEnv)*") { # Adding to resourceGroups $resourceGroups.Add($rg.Name, @{name = $envVariable.value; location = $Location}) } } } if ($resourceGroups) { return $resourceGroups } } "Parameters" { $params = @{} foreach ($param in $rawBlueprint.properties.parameters.PSObject.Properties) { foreach ($envVariable in (Get-ChildItem env: | Where-Object Name -like "BP_*")) { $tmp = $param.Name -replace "-", "" $tmpEnv = $envVariable.Name -replace "BP_", "" $tmpValue = $null if ($tmp -like "*$($tmpEnv)*") { if ([string]::IsNullOrWhitespace($param.Value.defaultValue)) { if ($param.Value.type -eq "array" -and [string]::IsNullOrWhitespace($envVariable.value)) { $tmpValue = @() $params.Add($param.Name, $tmpValue) } elseif ($param.Value.type -eq "object" -and [string]::IsNullOrWhitespace($envVariable.value)) { $tmpValue = @() $params.Add($param.Name, $tmpValue) } elseif ($param.Value.type -eq "object" -and $envVariable.value.getType().Name -eq "String") { $tmpObject = @{} (ConvertFrom-Json $envVariable.value -ErrorAction Stop).psobject.properties | Foreach-Object { $tmpObject[$_.Name] = $_.Value } $params.Add($param.Name, $tmpObject) } elseif ($param.Value.type -eq "int" -and $envVariable.value.getType().Name -eq "String") { $params.Add($param.Name, [int]$envVariable.value) } elseif ($param.Value.type -eq "string" -and [string]::IsNullOrWhitespace($envVariable.value)) { $tmpValue = "" $params.Add($param.Name, $tmpValue) } elseif ($envVariable.Value.Contains('Microsoft.KeyVault/vaults')) { continue } else { $params.Add($param.Name, $envVariable.value) } } else { $params.Add($param.Name, $envVariable.value) } } } # Checking for Variables that require a secret. These variables are defined as an Enviromment variable on the PowerShell Task and use a Azure DevOps Secret Variable foreach ($envVariable in (Get-ChildItem env: | Where-Object Name -like "BPS_*")) { $tmp = $param.Name -replace "-", "" $tmpEnv = $envVariable.Name -replace "BPS_", "" $tmpValue = $null if ($tmp -like "*$($tmpEnv)*") { if ([string]::IsNullOrWhitespace($param.Value.defaultValue)) { # Adding to Params $params.Add($param.Name, $envVariable.value) } } } } if ($params) { return $params } } "SecureParameters" { $secureParams = @{} foreach ($param in $rawBlueprint.properties.parameters.PSObject.Properties) { foreach ($envVariable in (Get-ChildItem env: | Where-Object Name -like "BP_*")) { $tmp = $param.Name -replace "-", "" $tmpEnv = $envVariable.Name -replace "BP_", "" $tmpValue = $null if ($tmp -like "*$($tmpEnv)*") { if ([string]::IsNullOrWhitespace($param.Value.defaultValue)) { if ($envVariable.Value.Contains('Microsoft.KeyVault/vaults')) { $tmpValue = $envVariable.Value -split "," $tmpValue = @{keyVaultId=$tmpValue[0];secretName=$tmpValue[1]} $secureParams.Add($param.Name, $tmpValue) } } } } } if ($secureParams) { return $secureParams } } } } function New-AzDevOpsBlueprintAssignment { <# .SYNOPSIS Assigns a Blueprint in Azure .DESCRIPTION Assigns a version of a Blueprint that was published to a Management Group / Subscription .PARAMETER Blueprint An Object containing the Blueprint information. .PARAMETER AssignmentName A string containing the Assignment Name of the Blueprint .PARAMETER InputPath A string containing the path to the Blueprint JSON File e.g. 'C:\Repos\Blueprints\Small_ISO27001_Shared-Services' .PARAMETER Location A string containing the azure region name to assign/create the resources for the Blueprint e.g. australiasoutheast .PARAMETER Version A string containing the Version of the Blueprint to be assigned. This value should be the Release Number generated by the Release pipeline e.g. 20190701.1 YYYYMMDD.R (YYYY - Year, MM = Month, DD = Day, R = Release number) .PARAMETER SubscriptionId A string containing the Subscription ID of the Subscription to import the Blueprint into. This parameter must be specified. .EXAMPLE Assign Blueprint to Management Group $Blueprint = Get-AzBlueprintAssignment -Name "Assignment-Small_ISO27001_Shared-Services" -Subscription "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" New-AzDevOpsBlueprintAssignment ` -Blueprint $Blueprint ` -AssignmentName "Assignment-Small_ISO27001_Shared-Services" ` -InputPath "C:\Repos\Blueprints\Small_ISO27001_Shared-Services" ` -Location "australiasoutheast" ` -Version "20190901.001" #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'None')] param ( [parameter(Mandatory=$true)] [object]$Blueprint, [parameter(Mandatory=$true)] [string]$AssignmentName, [Parameter(Mandatory=$True)] [ValidateNotNull()] [ValidateScript({Get-ChildItem -Path $_})] [string]$InputPath, [Parameter(Mandatory=$true)] [string]$Location, [Parameter(Mandatory=$true)] [string]$Version, [Parameter(Mandatory=$false)] [string]$SubscriptionId = $context.Subscription.Id ) try { # Variables $rawBlueprint = Get-Content "$($InputPath)\Blueprint.json" | ConvertFrom-Json -ErrorAction Stop # Get Any old Assignments $oldAssignment = Get-AzBlueprintAssignment -Name $AssignmentName -Subscription $SubscriptionId -ErrorAction SilentlyContinue # Assign Blueprint Write-Output "`r`nAssigning Blueprint '$Blueprint.Name' using Version '$($Version)'....." Write-Output "Matching Variables to Blueprint Resource Groups and configuring Values....." $resourceGroups = Find-AzDevOpsBlueprintParameters -Type ResourceGroups -RawBlueprint $rawBlueprint -Location $Location if ($resourceGroups.count -ne 0) { Write-Output " Found Resource Group Parameters!" foreach ($resourceGroup in $resourceGroups) { Write-Output " Setting Resource Group Name to: $($resourceGroup.Values.Name)" Write-Output " Setting Resource location to: $($resourceGroup.Values.Location)`r`n" } } Write-Output "Matching Variables to Blueprint Parameters and configuring Values....." $params = Find-AzDevOpsBlueprintParameters -Type Parameters -RawBlueprint $rawBlueprint if ($params.count -ne 0) { Write-Output " Found Parameters!" foreach ($param in $params.Keys) { Write-Output " Setting Parameter: $($param)" } } Write-Output "Matching Variables to Blueprint Secure Parameters and configuring Values....." $secureParams = Find-AzDevOpsBlueprintParameters -Type SecureParameters -RawBlueprint $rawBlueprint if ($secureParams.count -ne 0) { Write-Output " Found Secure Parameters!" foreach ($param in $secureParams.Keys) { Write-Output " Setting Parameter: $($param)" } } if ($oldAssignment -and $PSCmdlet.ShouldProcess($AssignmentName, 'Proceed.')) { # if yes, *update* the assignment Write-Output "Updating existing assignment....." if ($resourceGroups.Count -ge 1) { if ($secureParams.Count -ge 1) { Set-AzBlueprintAssignment -Blueprint $Blueprint -Location $Location -SubscriptionId $SubscriptionId -Name $AssignmentName -ResourceGroupParameter $resourceGroups -Parameter $params -SecureStringParameter $secureParams -ErrorAction Stop } else { Set-AzBlueprintAssignment -Blueprint $Blueprint -Location $Location -SubscriptionId $SubscriptionId -Name $AssignmentName -ResourceGroupParameter $resourceGroups -Parameter $params -ErrorAction Stop } } else { if ($secureParams.Count -ge 1) { Set-AzBlueprintAssignment -Blueprint $Blueprint -Location $Location -SubscriptionId $SubscriptionId -Name $AssignmentName -Parameter $params -SecureStringParameter $secureParams -ErrorAction Stop } else { Set-AzBlueprintAssignment -Blueprint $Blueprint -Location $Location -SubscriptionId $SubscriptionId -Name $AssignmentName -Parameter $params -ErrorAction Stop } } } else { # if no assignment, create one Write-Output "Creating new assignment....." if ($resourceGroups.Count -ge 1) { if ($secureParams.Count -ge 1) { New-AzBlueprintAssignment -Blueprint $Blueprint -Location $Location -SubscriptionId $SubscriptionId -Name $AssignmentName -ResourceGroupParameter $resourceGroups -Parameter $params -SecureStringParameter $secureParams -ErrorAction Stop } else { New-AzBlueprintAssignment -Blueprint $Blueprint -Location $Location -SubscriptionId $SubscriptionId -Name $AssignmentName -ResourceGroupParameter $resourceGroups -Parameter $params -ErrorAction Stop } } else { if ($secureParams.Count -ge 1) { New-AzBlueprintAssignment -Blueprint $Blueprint -Location $Location -SubscriptionId $SubscriptionId -Name $AssignmentName -Parameter $params -SecureStringParameter $secureParams -ErrorAction Stop } else { New-AzBlueprintAssignment -Blueprint $Blueprint -Location $Location -SubscriptionId $SubscriptionId -Name $AssignmentName -Parameter $params -ErrorAction Stop } } } $assignment = Get-AzBlueprintAssignment -Name $AssignmentName -Subscription $SubscriptionId $counter = 0 while (($assignment.ProvisioningState -ne "Succeeded") -and ($assignment.ProvisioningState -ne "Failed")) { Write-Output $assignment.ProvisioningState Start-Sleep -Seconds 5 $assignment = Get-AzBlueprintAssignment -Name $AssignmentName -Subscription $SubscriptionId $counter++ } if ($assignment.ProvisioningState -eq "Succeeded") { Write-Output "SUCCESS! Blueprint '$($Blueprint.Name) Version '$($Version)' has been Assigned" } elseif ($assignment.provisioningState -eq "Failed") { throw (Resolve-AzError -Last) } else { throw "Unhandled terminal state for assignment: {0}" -f $assignment.ProvisioningState } } catch { if ($_.ErrorDetails.Message) {$ErrDetails = $_.ErrorDetails.Message } else {$ErrDetails = $_} if ($_.Message) {$ErrDetails = $_.Message } else {$ErrDetails = $_} Get-StandardError -Exception $($ErrDetails) } } function Remove-AzDevOpsBlueprintAssignment { <# .SYNOPSIS Removes a Blueprint Assignment in Azure .DESCRIPTION Removes a Blueprint that was Assigned to a Management Group / Subscription .PARAMETER AssignmentName A string containing the Assignment Name of the Blueprint .PARAMETER SubscriptionId A string containing the Subscription ID of the Subscription to import the Blueprint into. This parameter must be specified. .EXAMPLE Remove Assignment from a Subscription New-AzDevOpsBlueprintAssignment ` -AssignmentName 'Assignment-Small_ISO27001_Shared-Services.json' #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'None')] param ( [parameter(Mandatory=$true)] [string]$AssignmentName, [Parameter(Mandatory=$false)] [string]$SubscriptionId = $context.Subscription.Id, [Parameter(Mandatory=$false)] [bool]$Test = $false ) try { # Get Assignment $Assignment = Get-AzBlueprintAssignment -Name $AssignmentName -Subscription $SubscriptionId -ErrorAction SilentlyContinue if ($Assignment -and $PSCmdlet.ShouldProcess($AssignmentName, 'Proceed.')) { $ResourceGroups = $Assignment.ResourceGroups Remove-AzBlueprintAssignment -Name $AssignmentName -Subscription $SubscriptionId -ErrorAction SilentlyContinue $Assignment = Get-AzBlueprintAssignment -Name $AssignmentName -Subscription $SubscriptionId -ErrorAction SilentlyContinue if ($Test -eq $true) { Write-Output "Removing Blueprint Assignment Resources" foreach ($item in $ResourceGroups) { $null = Remove-AzResourceGroup -Name $item.ResourceGroup.Name -Force Write-Output "SUCCESS! Removed Resource Group '$($item.ResourceGroup.Name)'" } } if (!$Assignment) { Write-Output "SUCCESS! Assignment '$($AssignmentName)' has been removed" } else { throw (Resolve-AzError -Last) } } else { Write-Output "Assignment '$($AssignmentName)' was not found." } } catch { if ($_.ErrorDetails.Message) {$ErrDetails = $_.ErrorDetails.Message } else {$ErrDetails = $_} if ($_.Message) {$ErrDetails = $_.Message } else {$ErrDetails = $_} Get-StandardError -Exception $($ErrDetails) } } function Test-ParameterValue { <# .SYNOPSIS Checks the type of Blueprint Parameter .DESCRIPTION Determines the type of Blueprint Parameter and returns a description of what the Azure DevOps variable requires as a value. .PARAMETER Value A PSCustomObject containing the Blueprint Parameter information .EXAMPLE Test-ParameterValue ` -Value [PSCustomObject]@{ type = "string" metadata = [PSCustomObject]@{displayName = "app_tshirt-size (WebApp Template)"; description = "What T-Shirt size is required for the Web App"} defaultValue = "Medium" allowedValues = @("Small", "Medium", "Large") } #> param ( [Parameter(Mandatory=$true)] [PSCustomObject]$Value ) if ($Value.type -eq "array") { $tmpValue = "Needs to be an Array e.g. value1, value2, etc." } elseif ($Value.type -eq "object") { $tmpValue = "Needs to be an Object, but as JSON e.g. '{`"key1`": `"value1`", `"key2`": `"value2`"}'" } elseif ($Value.type -eq "int") { $tmpValue = "Needs to be an Int i.e. Integer/Number e.g. 123 etc." } elseif ($Value.type -eq "secureString") { $tmpValue = "Needs to be ReferenceId to a password in a Key Vault e.g. /subscriptions/`$(subscriptionId)/resourceGroups/`$(keyVault.ResourceGroup)/providers/Microsoft.KeyVault/vaults/`$(keyVault),`$(BP_activedirectorydomainservices_addomainadminusername)." } elseif ($Value.type -eq "string") { $tmpValue = "Needs to be an String or Text e.g. This is Text etc." } else { $tmpValue = "" } return $tmpValue } function Test-ParameterDefaultValue { <# .SYNOPSIS Checks the Default Value of Blueprint Parameter .DESCRIPTION Depending on the type of Blueprint Parameter, the Default Value is modified and returned as a string. .PARAMETER Value A PSCustomObject containing the Blueprint Parameter information .EXAMPLE Test-ParameterDefaultValue ` -Value [PSCustomObject]@{ type = "string" metadata = [PSCustomObject]@{displayName = "app_tshirt-size (WebApp Template)"; description = "What T-Shirt size is required for the Web App"} defaultValue = "Medium" allowedValues = @("Small", "Medium", "Large") } #> param ( [Parameter(Mandatory=$true)] $Value ) if ($Value.type -eq "array") { $tmpValue = $Value.defaultValue -join "`n" $tmpValue = $tmpValue -replace "`n", "," } elseif ($Value.type -eq "object") { $tmpValue = ($Value.defaultValue | ConvertTo-Json) -replace "`n", "" $tmpValue = $tmpValue -replace " ", "" } else { $tmpValue = $Value.defaultValue } return $tmpValue.toString() } function New-AzDevOpsBlueprintVariableGroup { <# .SYNOPSIS Creates a Variable Group in a nominated Azure DevOps Project .DESCRIPTION The Variable Group created, contains the a group of variables detected from the Blueprint Parameters .PARAMETER Json A Hashtable containing Json information used to create the Variable Group .PARAMETER VariableGroups An Object containing all the Variable Groups in the Azure DevOps Project #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'None')] param ( [Parameter(Mandatory=$true)] [hashtable]$Json, [Parameter(Mandatory=$true)] [object]$VariableGroups, [Parameter(Mandatory=$true)] $DevOpsPAT, [Parameter(Mandatory=$false)] $DevOpsApiVersion = "5.0-preview.1" ) # Variables $DevOpsHeader = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$DevOpsPAT"))} if ($json.variables.count -ge 1 -and $PSCmdlet.ShouldProcess($json, 'Proceed.')) { if ($Id = ($VariableGroups | Where-Object Name -eq $json.Name).id) { $uri = "{0}{1}/_apis/distributedtask/variablegroups/{2}?api-version={3}" -f $DevOpsUri, $DevOpsProject, $id, $DevOpsApiVersion $body = $json | ConvertTo-Json $null = Invoke-RestMethod -Uri $uri -Method PUT -Headers $DevOpsHeader -Body $body -ContentType "application/json" -ErrorAction Stop Write-Output "SUCCESS! Variable Group '$($json.Name)' has been updated in Azure DevOps" } else { $uri = "{0}{1}/_apis/distributedtask/variablegroups?api-version={2}" -f $DevOpsUri, $DevOpsProject, $DevOpsApiVersion $body = $json | ConvertTo-Json $null = Invoke-RestMethod -Uri $uri -Method POST -Headers $DevOpsHeader -Body $body -ContentType "application/json" -ErrorAction Stop Write-Output "SUCCESS! Variable Group '$($json.Name)' has been created in Azure DevOps" } } } function Get-StandardError { <# .SYNOPSIS Error Message .DESCRIPTION Generates a standard error response .PARAMETER Exception Error Exception Object #> param ( [Parameter(Mandatory=$true)] $Exception ) Write-Output "An error occurred - please check rights or parameters for proper configuration and try again" Write-Output "=======================================================================" Write-Output "Specific Error is: " Write-Output "$($Exception)" #Exit 1 } |