Update the Azure DevOps service endpoint (connection) using REST API

%3CLINGO-SUB%20id%3D%22lingo-sub-2410285%22%20slang%3D%22en-US%22%3EUpdate%20the%20Azure%20DevOps%20service%20endpoint%20(connection)%20using%20REST%20API%3C%2FLINGO-SUB%3E%3CLINGO-BODY%20id%3D%22lingo-body-2410285%22%20slang%3D%22en-US%22%3E%3CP%3Ewe%20are%20using%20the%20REST%20API%20Method%20(%3CA%20href%3D%22https%3A%2F%2Fdocs.microsoft.com%2Fen-us%2Frest%2Fapi%2Fazure%2Fdevops%2Fserviceendpoint%2Fendpoints%2Fupdate%2520service%2520endpoint%3Fview%3Dazure-devops-rest-6.0%22%20rel%3D%22noreferrer%20noopener%22%20target%3D%22_blank%22%3EPUT%3C%2FA%3E)%20to%20update%20the%20existing%20AWS%20service%20connection%20in%20our%20ADO%20environment%20by%20assigning%20a%20minimum%20level%20of%20access%20(scopes)%20to%20the%20PAT.%3C%2FP%3E%3CP%3EThe%20access%20levels%20are%3C%2FP%3E%3COL%3E%3CLI%3EService%20Connections%20(Read%2C%20query%2C%20and%20manage)%3C%2FLI%3E%3CLI%3ETokens%20(Read%2C%20update%2C%20and%20revoke)%3C%2FLI%3E%3C%2FOL%3E%3CP%3EThere%20are%20no%20issues%20with%20this%20until%20past%20few%20days.%20We%20were%20able%20update%20the%20service%20connection%20on%20a%20scheduled%20basis%20using%20Azure%20Pipelines.%20But%20since%20past%20few%20days%20it%20started%20throwing%20401%20Unauthorized%20error.%20but%20it%20was%20working%20fine%20before.%20Not%20sure%20if%20they%20(Azure)%20have%20changed%20something%20in%20the%20latest%20release.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EI%20tried%20to%20troubleshoot%20the%20issue%20by%20increasing%20the%20level%20of%20access%20scope%20one%20by%20one%20(assigned%20all%20the%20scopes%20at%20some%20point)%2C%20but%20no%20luck%20with%20it.%20However%2C%20I%20was%20able%20to%20update%20the%20service%20connection%2C%20If%20I%20change%20the%20level%20of%20access%20to%20a%20full%20access%20instead%20of%20custom%20defined.%20But%20it%20is%20not%20a%20good%20idea%20to%20give%20the%20full%20level%20of%20access%20to%20a%20personal%20access%20token.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EAm%20I%20missing%20something%20here%20or%20Is%20there%20a%20way%20to%20update%20the%20service%20connection%20with%20a%20minimum%20level%20of%20access%20control%3F%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3EAlso%2C%20what%20is%20difference%20between%20full%20access%20vs%20custom%20defined%20with%20all%20the%20scopes%2C%20since%20the%20full%20access%20is%20working%20fine%2C%20but%20custom%20defined%20with%20all%20the%20scopes%20is%20not%20working.%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3Ebelow%20is%20the%20snippet%20of%20code%20for%20your%20reference%3C%2FP%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CPRE%20class%3D%22lia-code-sample%20language-powershell%22%3E%3CCODE%3E%24OrganizationName%20%3D%20%22%22%20%0A%24ProjectName%20%3D%20%22%22%0A%24PAT%20%3D%20%22%22%0A%24AccessKeyID%20%3D%20%22%22%0A%24SecretAccessKey%20%3D%20%22%22%20%0A%24Serviceconnectionname%20%3D%20%22%22%0A%0A%0Afunction%20Update-AWSServiceConnection%20%7B%0A%0A%20%20%20%20Write-Host%20%22Executing%20the%20Update%20Service%20Connection%20Script..%22%0A%0A%20%20%20%20%23%20Create%20the%20header%20to%20authenticate%20to%20Azure%20DevOps%0A%20%20%20%20Write-Host%20%22Create%20the%20header%20to%20authenticate%20to%20Azure%20DevOps%22%0A%20%20%20%20%24token%20%3D%20%5BSystem.Convert%5D%3A%3AToBase64String(%5BSystem.Text.Encoding%5D%3A%3AASCII.GetBytes(%22%3A%24(%24PAT)%22))%0A%0A%20%20%20%20%24Headers%20%3D%20%40%7B%0A%20%20%20%20%20%20%20%20Authorization%20%3D%20%22Basic%20%24token%22%0A%20%20%20%20%7D%0A%0A%20%20%20%20%23%20Get%20the%20Project%20ID%0A%20%20%20%20Write-Host%20%22Construct%20the%20API%20URL%20to%20get%20the%20project%20ID..%22%0A%20%20%20%20%24project%20%3D%20%22https%3A%2F%2Fdev.azure.com%2F%22%20%2B%20%22%24OrganizationName%2F_apis%2Fprojects%2F%24ProjectName%20%3Fapi-version%3D6.0%22%0A%20%20%20%20Write-Host%20%22Project%20API%20URL%20%3A%3A%20%24project%22%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20Write-Host%20%22Get%20the%20Project%20%5B%24ProjectName%5D%20ID..%22%0A%20%20%20%20%20%20%20%20%24response%20%3D%20Invoke-RestMethod%20-Uri%20%24project%20-Headers%20%24Headers%20-Method%20GET%0A%20%20%20%20%20%20%20%20%24ProjectID%20%3D%20%24response.id%0A%20%20%20%20%20%20%20%20if%20(!%24ProjectID)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20Write-Host%20%22ProjectID%20value%20is%20null%22%0A%20%20%20%20%20%20%20%20%20%20%20%20Write-Error%20%22Script%20step%20has%20been%20aborted.%22%20-ErrorAction%20stop%0A%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20Write-Host%20%22Project%20ID%20%3A%3A%20%24ProjectID%22%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20catch%20%7B%0A%20%20%20%20%20%20%20%20%24ErrorMessage%20%3D%20%24_%20%7C%20ConvertFrom-Json%0A%20%20%20%20%20%20%20%20throw%20%22Could%20not%20Get%20the%20project%20%5B%24ProjectName%5D%20ID%3A%20%24(%24ErrorMessage.message)%22%0A%20%20%20%20%7D%0A%0A%20%20%20%20%23%20Get%20Endpoint%20ID%20Details%0A%20%20%20%20%24endpoint%20%3D%20%22https%3A%2F%2Fdev.azure.com%2F%22%20%2B%20%22%24OrganizationName%2F%24ProjectName%2F_apis%2Fserviceendpoint%2Fendpoints%3FendpointNames%3D%24Serviceconnectionname%26amp%3Bapi-version%3D6.0-preview.4%22%0A%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20Write-Host%20%22Get%20the%20Service%20Connection%20%5B%24Serviceconnectionname%5D%20ID..%22%0A%20%20%20%20%20%20%20%20%24response%20%3D%20Invoke-RestMethod%20-Uri%20%24endpoint%20-Headers%20%24Headers%20-Method%20GET%0A%20%20%20%20%20%20%20%20%24endpointId%20%3D%20%24response.value.id%0A%20%20%20%20%20%20%20%20if%20(!%24endpointId)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20Write-Host%20%22Service%20Endpoint%20ID%20value%20is%20null%22%0A%20%20%20%20%20%20%20%20%20%20%20%20Write-Error%20%22Script%20step%20has%20been%20aborted.%22%20-ErrorAction%20stop%0A%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20Write-Host%20%22Service%20Endpoint%20ID%20%3A%3A%20%24endpointId%22%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%7D%0A%20%20%20%20catch%20%7B%0A%20%20%20%20%20%20%20%20%24ErrorMessage%20%3D%20%24_%20%7C%20ConvertFrom-Json%0A%20%20%20%20%20%20%20%20throw%20%22Could%20not%20Get%20the%20service%20connection%20%5B%24Serviceconnectionname%5D%20ID%3A%20%24(%24ErrorMessage.message)%22%0A%20%20%20%20%7D%0A%0A%20%20%20%20%23%20Create%20a%20body%20for%20the%20API%20call%0A%20%20%20%20%24url%20%3D%20%22https%3A%2F%2Fdev.azure.com%2F%22%20%2B%20%22%24OrganizationName%2F_apis%2Fserviceendpoint%2Fendpoints%2F%24endpointId%20%3Fapi-version%3D6.1-preview.4%22%0A%20%20%20%20%24body%20%3D%20%40%22%0A%7B%0A%20%20%20%20%22data%22%3A%20%7B%7D%2C%0A%20%20%20%20%22id%22%3A%20%22%24endpointId%22%2C%0A%20%20%20%20%22name%22%3A%20%22UpdatedServiceEndpoint%22%2C%0A%20%20%20%20%22type%22%3A%20%22AWS%22%2C%0A%20%20%20%20%22url%22%3A%20%22https%3A%2F%2Faws.amazon.com%2F%22%2C%0A%20%20%20%20%22description%22%3A%20null%2C%0A%20%20%20%20%22authorization%22%3A%20%7B%0A%20%20%20%20%20%20%22parameters%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22username%22%3A%20%22%24AccessKeyId%22%2C%0A%20%20%20%20%20%20%20%20%22password%22%3A%20%22%24SecretAccessKey%22%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%22scheme%22%3A%20%22UsernamePassword%22%2C%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22isShared%22%3A%20false%2C%0A%20%20%20%20%22isReady%22%3A%20true%2C%0A%20%20%20%20%22owner%22%3A%20%22Library%22%2C%0A%20%20%20%20%22serviceEndpointProjectReferences%22%3A%20%5B%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22name%22%3A%20%22%24Serviceconnectionname%22%2C%0A%20%20%20%20%20%20%20%20%22projectReference%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%22id%22%3A%20%22%24ProjectID%22%2C%0A%20%20%20%20%20%20%20%20%20%20%22name%22%3A%20%22%24ProjectName%22%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%0A%20%20%7D%0A%22%40%0A%0A%20%20%20%20try%20%7B%20%0A%20%20%20%20Write-Host%20%22Updating%20the%20Service%20Connection%20%5B%24Serviceconnectionname%5D%22%0A%20%20%20%20%24response%20%3D%20Invoke-RestMethod%20-Uri%20%24url%20-Headers%20%24Headers%20-Method%20PUT%20-Body%20%24body%20-ContentType%20application%2Fjson%0A%20%20%20%20Write-Host%20%22Connection%20Updated%22%0A%20%20%20%20%24response%0A%20%20%20%20%7D%0A%20%20%20%20catch%20%7B%0A%20%20%20%20%20%20Write-Host%20%22An%20error%20occurred%3A%22%0A%20%20%20%20%20%20Write-Host%20%24_%0A%20%20%20%20%7D%0A%20%20%20%20%0A%7D%0A%0A%0AUpdate-AWSServiceConnection%3C%2FCODE%3E%3C%2FPRE%3E%3CP%3E%26nbsp%3B%3C%2FP%3E%3CP%3E%3CSPAN%3EAny%20suggestions%20or%20comments%20would%20be%20greatly%20appreciated.%3C%2FSPAN%3E%3C%2FP%3E%3C%2FLINGO-BODY%3E
Occasional Visitor

we are using the REST API Method (PUT) to update the existing AWS service connection in our ADO environment by assigning a minimum level of access (scopes) to the PAT.

The access levels are

  1. Service Connections (Read, query, and manage)
  2. Tokens (Read, update, and revoke)

There are no issues with this until past few days. We were able update the service connection on a scheduled basis using Azure Pipelines. But since past few days it started throwing 401 Unauthorized error. but it was working fine before. Not sure if they (Azure) have changed something in the latest release.

 

I tried to troubleshoot the issue by increasing the level of access scope one by one (assigned all the scopes at some point), but no luck with it. However, I was able to update the service connection, If I change the level of access to a full access instead of custom defined. But it is not a good idea to give the full level of access to a personal access token.

 

Am I missing something here or Is there a way to update the service connection with a minimum level of access control?

 

Also, what is difference between full access vs custom defined with all the scopes, since the full access is working fine, but custom defined with all the scopes is not working.

 

below is the snippet of code for your reference

 

$OrganizationName = "" 
$ProjectName = ""
$PAT = ""
$AccessKeyID = ""
$SecretAccessKey = "" 
$Serviceconnectionname = ""


function Update-AWSServiceConnection {

    Write-Host "Executing the Update Service Connection Script.."

    # Create the header to authenticate to Azure DevOps
    Write-Host "Create the header to authenticate to Azure DevOps"
    $token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($PAT)"))

    $Headers = @{
        Authorization = "Basic $token"
    }

    # Get the Project ID
    Write-Host "Construct the API URL to get the project ID.."
    $project = "https://dev.azure.com/" + "$OrganizationName/_apis/projects/$ProjectName ?api-version=6.0"
    Write-Host "Project API URL :: $project"
    try {
        Write-Host "Get the Project [$ProjectName] ID.."
        $response = Invoke-RestMethod -Uri $project -Headers $Headers -Method GET
        $ProjectID = $response.id
        if (!$ProjectID) {
            Write-Host "ProjectID value is null"
            Write-Error "Script step has been aborted." -ErrorAction stop
        } else {
            Write-Host "Project ID :: $ProjectID"
        }
    }
    catch {
        $ErrorMessage = $_ | ConvertFrom-Json
        throw "Could not Get the project [$ProjectName] ID: $($ErrorMessage.message)"
    }

    # Get Endpoint ID Details
    $endpoint = "https://dev.azure.com/" + "$OrganizationName/$ProjectName/_apis/serviceendpoint/endpoints?endpointNames=$Serviceconnectionname&api-version=6.0-preview.4"

    try {
        Write-Host "Get the Service Connection [$Serviceconnectionname] ID.."
        $response = Invoke-RestMethod -Uri $endpoint -Headers $Headers -Method GET
        $endpointId = $response.value.id
        if (!$endpointId) {
            Write-Host "Service Endpoint ID value is null"
            Write-Error "Script step has been aborted." -ErrorAction stop
        } else {
            Write-Host "Service Endpoint ID :: $endpointId"
        }

    }
    catch {
        $ErrorMessage = $_ | ConvertFrom-Json
        throw "Could not Get the service connection [$Serviceconnectionname] ID: $($ErrorMessage.message)"
    }

    # Create a body for the API call
    $url = "https://dev.azure.com/" + "$OrganizationName/_apis/serviceendpoint/endpoints/$endpointId ?api-version=6.1-preview.4"
    $body = @"
{
    "data": {},
    "id": "$endpointId",
    "name": "UpdatedServiceEndpoint",
    "type": "AWS",
    "url": "https://aws.amazon.com/",
    "description": null,
    "authorization": {
      "parameters": {
        "username": "$AccessKeyId",
        "password": "$SecretAccessKey"
        },
        "scheme": "UsernamePassword",
    },
    "isShared": false,
    "isReady": true,
    "owner": "Library",
    "serviceEndpointProjectReferences": [
      {
        "name": "$Serviceconnectionname",
        "projectReference": {
          "id": "$ProjectID",
          "name": "$ProjectName"
        }
      }
    ]
  }
"@

    try { 
    Write-Host "Updating the Service Connection [$Serviceconnectionname]"
    $response = Invoke-RestMethod -Uri $url -Headers $Headers -Method PUT -Body $body -ContentType application/json
    Write-Host "Connection Updated"
    $response
    }
    catch {
      Write-Host "An error occurred:"
      Write-Host $_
    }
    
}


Update-AWSServiceConnection

 

Any suggestions or comments would be greatly appreciated.

0 Replies