Search code examples
azure-devopsazure-devops-rest-apiserviceconnection

Azure DevOps - Service Connections - REST API - An item with the same key has already been added


For some time now, then I create new Azure ARM Manuel Service Connections in Azure Devops, I cannot validate them. I get the error: An item with the same key has already been added.

If I from the Azure DevOps GUI, change the password for the service principal Service Connection to something else, I get a password error, and if I update the password to the exact same password that my REST API created the password with, the connections validates OK.

So something is happening then creating from REST API, that bugs the connection?

param(
    [Parameter(Mandatory)]
    [string]$SPKey,

    [Parameter(Mandatory)]
    [string]$PAT,

    [Parameter(Mandatory)]
    [string]$TenantID,

    [Parameter(Mandatory)]
    [string]$AADApplicationID,

    [Parameter(Mandatory)]
    [string]$subscriptionName,

    [Parameter(Mandatory)]
    [string]$SubscriptionId
)

#write-host "SPKEY is $SPKEY"
write-host "TenantID is $TenantID"
write-host "AADApplicationID is $AADApplicationID"
write-host "Subname is $subscriptionName"
write-host "SubID $SubscriptionId"

$subscriptionName = $subscriptionName.Replace('Ø','OE')
$subscriptionName = $subscriptionName.Replace('ø','oe')
$subscriptionName = $subscriptionName.Replace('Å','AA')
$subscriptionName = $subscriptionName.Replace('å','aa')
$subscriptionName = $subscriptionName.Replace('Æ','AE')
$subscriptionName = $subscriptionName.Replace('æ','ae')

write-host "Subname is now" $subscriptionName

$PATGetBytes = [System.Text.Encoding]::ASCII.GetBytes(":$PAT")
$Authentication = [System.Convert]::ToBase64String($PATGetBytes)
$Headers = @{Authorization = ("Basic {0}" -f $Authentication) }
$Uri = "https://dev.azure.com/ORG/PROJECT/_apis/serviceendpoint/endpoints?api-version=5.1-preview.2"
$Body = [pscustomobject]@{
    data = [pscustomobject]@{
        subscriptionId = "$SubscriptionId"
        subscriptionName = "$subscriptionName"
        CreationMode = 'Manual'
        scopeLevel = 'Subscription'
    }
    authorization = [pscustomobject]@{
            scheme = 'ServicePrincipal'
            parameters = [pscustomobject]@{
                tenantid            = "$TenantID"
                serviceprincipalid  = "$AADApplicationID"
                authenticationType  = "spnKey"
                serviceprincipalkey = "$SPKey"
        }
    }
    isShared                         = $true
    isReady                          = $True
    serviceEndpointProjectReferences = @(
        @{
            projectReference = @{
                id   = "9a4bfcab-c7b2-48fb-90c8-efb7461a962f"
                name = "CescomIaC"
            }
            name             = "$SubscriptionId"
        }
    )
    name = "$SubscriptionId"
    type = 'azurerm'
    url = 'https://management.azure.com/'
} | ConvertTo-Json -Depth 10
$serviceendpointAzure = Invoke-RestMethod -Uri $Uri -Method Post -Body $Body -Headers $Headers -ContentType 'application/json'
$serviceendpointAzureid = $serviceendpointAzure.id
$jsonpermitazure = @"
{
    "allPipelines": {
        "authorized": true,
        "authorizedBy": null,
        "authorizedOn": null
    },
    "pipelines": null,
    "resource": {
        "id": "$serviceendpointAzureid",
        "type": "endpoint"
    }
}
"@

$authhttpazure = "https://dev.azure.com/ORG/PROJECT/_apis/pipelines/pipelinePermissions/endpoint/" + $serviceendpointAzureid + "?api-version=5.1-preview.1"
Invoke-RestMethod -Method PATCH -Uri $authhttpazure -Headers $Headers -Body $jsonpermitazure -ContentType "application/json"

Solution

  • I tried to use your script to create a service connection. I found the same error message when I click the Verify button.

    Then I tried to replace the body with the following script, and it works with Verification Succeeded. Please refer the following script.

    $SPKey="" 
    $PAT=""
    $TenantID=""
    $AADApplicationID=""
    $subscriptionName=""
    $SubscriptionId=""
    $orgname=""
    
    #write-host "SPKEY is $SPKEY"
    write-host "TenantID is $TenantID"
    write-host "AADApplicationID is $AADApplicationID"
    write-host "Subname is $subscriptionName"
    write-host "SubID $SubscriptionId"
    
    $PATGetBytes = [System.Text.Encoding]::ASCII.GetBytes(":$PAT")
    $Authentication = [System.Convert]::ToBase64String($PATGetBytes)
    $Headers = @{Authorization = ("Basic {0}" -f $Authentication) }
    $Uri = "https://dev.azure.com/"+$orgname+"/_apis/serviceendpoint/endpoints?api-version=7.2-preview.4"
    $body = @"
    {
      `"data`": {
        `"subscriptionId`": `"$SubscriptionId`",
        `"subscriptionName`": `"$subscriptionName`",
        `"environment`": `"AzureCloud`",
        `"scopeLevel`": `"Subscription`",
        `"creationMode`": `"Manual`"
      },
      `"name`": `"MyServiceConnection`",
      `"type`": `"AzureRM`",
      `"url`": `"https://management.azure.com/`",
      `"authorization`": {
        `"parameters`": {
          `"tenantid`": `"$TenantID`",
          `"serviceprincipalid`": `"$AADApplicationID`",
          `"authenticationType`": `"spnKey`",
          `"serviceprincipalkey`": `"$SPKey`"
        },
        `"scheme`": `"ServicePrincipal`"
      },
      `"isShared`": false,
      `"isReady`": true,
      `"serviceEndpointProjectReferences`": [
        {
          `"projectReference`": {
            `"id`": `"17d78674-6c54-4723-9a07-30ceb77e7d84`",
            `"name`": `"PROJECT`"
          },
          `"name`": `"MyServiceConnection`"
        }
      ]
    }
    "@
    $serviceendpointAzure = Invoke-RestMethod -Uri $Uri -Method Post -Body $Body -Headers $Headers -ContentType 'application/json'
    $serviceendpointAzure | ConvertTo-Json
    

    Document reference: Endpoints - Create api-version=7.2-preview.4