azurepowershellazure-devopsazure-vm-scale-set

Cannot create an Azure Devops pool via API, null paramter


I'm trying to create an Azure Devops Self-Hosted Pool from the CLI using powershell...

The target pool is supposed to be used for an already provisioned VM Scale Set

Trying to follow the docs here: https://learn.microsoft.com/en-us/rest/api/azure/devops/distributedtask/pools/add?view=azure-devops-rest-7.1

so far I have

$org = "myorg"
$pat = "MY_PAT"
$headers = @{
    Authorization = "Basic " + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$($pat)"))
}
$url="https://dev.azure.com/$org/_apis/distributedtask/pools?api-version=7.1-preview.1"

$body = @{
    autoProvision = $false  
    autoSize      = $true   
    autoUpdate    = $true   

    options       = "elasticPool, singleUseAgents"

    owner         = @{
        displayName = "My Name"  # Example value, replace with your actual value
        id          = "MY_ID"      # Example value, replace with your actual value
        uniqueName  = "my_name@gmail.com"  # Example value, replace with your actual value
    }
    isHosted =  $false
    isLegacy = $false
    pool = "myPool"

    name          = "NewAgentPool"  # Example value, replace with your actual value
    poolType      = "automation"  # Example value, replace with your actual value
    scope = "my-scope"  # of the form XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXX

    size          = 1  # Example value, replace with your actual value
}

when I run it I get the error

 Invoke-RestMethod -Uri $url -Headers $headers -Method Post  `
                             -Body ($body | ConvertTo-Json) `
                             -ContentType "application/json"
Invoke-RestMethod:
{
  "$id": "1",
  "innerException": null,
  "message": "Value cannot be null.\r\nParameter name: pool",
  "typeName": "System.ArgumentNullException, mscorlib",
  "typeKey": "ArgumentNullException",
  "errorCode": 0,
  "eventId": 0
}

Curling with same args shows a response status code of 400.

I don't see any mention of the paramter 'Pool' in the documentation, so I'm a bit at a loss from where to go from here. There is 'poolType' and 'name'... but no "pool"

Edit:

after some debugging with curl, a simple json body like the following goes through, but does not associate itself to any project, just the organization

{
  "autoProvision": false,
  "name": "myvmsspool5",
  "poolType": "automation",
  "options": "elasticPool"
}

its when I specify "scope" does that error start showing up, not sure what value to pass into 'scope' now...

Edit 2: It seems the API does not expose the ability to configure the settings of the poolenter image description here

I can create the pool, but I cannot assign it a service connection, nor a virtual machine scale set via the API

Edit 4: It seems the correct abstraction to configure these are the "Elastic Pool" as documented here: https://registry.terraform.io/providers/microsoft/azuredevops/latest/docs/resources/elastic_pool

Then I needed to attach it to my project with an Agent_Queue object


Solution

  • Based on the requirement to setup a VMSS self-hosted agent pool, we should use this API instead. This operation requires to leverage the Azure Resource Manager service connection to authenticate access to the VMSS resource in Azure subscription.

    enter image description here

    Here is a sample for your reference assuming you have created the ARM service connection and the VMSS resource in Azure. Please note that $serviceEndpointId is the resource Id of the ARM service connection, against which we authenticate access to the VMSS resource in Azure subscription and $serviceEndpointScope is typically the Id of the project where the ARM service connection is generated.

    #Elasticpools - Create POST https://dev.azure.com/{organization}/_apis/distributedtask/pools?api-version=7.1-preview.1
    
    $organization = "TheAzureDevOpsOrgName"
    $MyPat = 'xxxxxx'
    $B64Pat = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(":$MyPat"))
    $poolName= "AZVMSSPool"
    $serviceEndpointId = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee' # The resource Id of the ARM service connection, against which we authenticate access to the VMSS resource in Azure subscription
    $serviceEndpointScope = 'pppppppp-pppp-pppp-pppp-pppppppppppp' # Typically the Id of the project where the ARM service connection is generated
    
    $azureSubId = "ssssssss-ssss-ssss-ssss-ssssssssssss"
    $vmssResourceId = "/subscriptions/$azureSubId/resourceGroups/RG-AZVM-LINUX/providers/Microsoft.Compute/virtualMachineScaleSets/azvmss-ubuntu-22.04"
    
    $URL = "https://dev.azure.com/$organization/_apis/distributedtask/elasticpools?poolName=$poolName&authorizeAllPipelines=true&autoProvisionProjectPools=true&api-version=7.1-preview.1"
    
    $headers = @{
        'Authorization' = 'Basic ' + $B64Pat
        'Content-Type' = 'application/json'
    }
    
    $body = @{
        agentInteractiveUI = $false
        azureId = $vmssResourceId
        desiredIdle = 0
        maxCapacity = 1
        osType = 1
        maxSavedNodeCount = 0
        recycleAfterEachUse = $false
        serviceEndpointId = $serviceEndpointId
        serviceEndpointScope = $serviceEndpointScope
        timeToLiveMinutes = 30
    } | ConvertTo-Json
    
    $response = Invoke-RestMethod -Method Post -Uri $URL -Headers $headers -Body $body
    $response | ConvertTo-Json -Depth 10
    
    # Projects - List GET https://dev.azure.com/{organization}/_apis/projects?api-version=7.1-preview.4
    $projectsURL = "https://dev.azure.com/$organization/_apis/projects?api-version=7.1-preview.4"
    $projects = Invoke-RestMethod -Method Get -Uri $projectsURL -Headers $headers
    $projects | ConvertTo-Json -Depth 10
    
    # Endpoints - Get GET https://dev.azure.com/{organization}/{project}/_apis/serviceendpoint/endpoints/{endpointId}?api-version=7.1-preview.4
    $endpointURL = "https://dev.azure.com/$organization/$project/_apis/serviceendpoint/endpoints/$serviceEndpointId?api-version=7.1-preview.4"
    $endpoint = Invoke-RestMethod -Method Get -Uri $endpointURL -Headers $headers
    $endpoint | ConvertTo-Json -Depth 10