Queue Azure devops YAML pipeline from code does not accept runtime parameters

2k Views Asked by At

YAML pipeline looks like below

parameters:
  - name: parameter1
    type: string
steps:
task: PowerShell@2
  inputs:
    targetType: 'inline'
    script: |
      # Write your PowerShell commands here.
      Write-Host ${{ parameters.parameter1 }}


C# code to queue build is like

var build = new Build()
            {
                Definition = definition,
                Project = project

            };
var dict = new Dictionary<string, string> { { "parameter1", "parametervalue" } };
build.Parameters = JsonSerializer.Serialize(dict);
buildClient.QueueBuildAsync(build).Wait();

I get exception Could not queue the build because there were validation errors or warnings. A value for the 'parameter1' parameter must be provided. Any idea to fix this issue would be helpful.

4

There are 4 best solutions below

2
Krzysztof Madej On BEST ANSWER

It looks that this is not possible at the moment to run pipeline with passing runtime parameters over C# SDK. You found workaround using REST API.

This is not an issue with SDK. They are developed in their own pace. So functionality which is available in REST API is not always available at the same moment in SDK. It could be available in the future, but for the moment if you want to run it programmatically you need to use REST API.

1
Rossco On

I managed to reverse engineer some client code which might help others. It may not be fully production-ready, but feel free to improve on it:

public class PipelineHttpClient : VssHttpClientBase
{
    public PipelineHttpClient(Uri baseUrl, VssCredentials credentials) 
        : base(baseUrl, credentials)
    {
    }

    public PipelineHttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings) 
        : base(baseUrl, credentials, settings)
    {
    }

    public PipelineHttpClient(Uri baseUrl, VssCredentials credentials, params DelegatingHandler[] handlers) 
        : base(baseUrl, credentials, handlers)
    {
    }

    public PipelineHttpClient(Uri baseUrl, VssCredentials credentials, VssHttpRequestSettings settings, params DelegatingHandler[] handlers) 
        : base(baseUrl, credentials, settings, handlers)
    {
    }

    public PipelineHttpClient(Uri baseUrl, HttpMessageHandler pipeline, bool disposeHandler) 
        : base(baseUrl, pipeline, disposeHandler)
    {
        
    }

    public async Task<PipelineRun> RunPipelineAsync(Guid project, int pipelineId, object parameters, string refName = "refs/heads/master")
    {
        var method = HttpMethod.Post;
        var locationId = Guid.Parse("7859261e-d2e9-4a68-b820-a5d84cc5bb3d");
        object routeValues = new
        {
            project,
            pipelineId,
            //pipelineVersion = ""
        };
        var version = new ApiResourceVersion("6.0");
        
        // ensure that the refName is prefixed correctly.
        refName = refName.StartsWith("refs/heads/", StringComparison.InvariantCultureIgnoreCase) 
            ? refName 
            : $"refs/heads/{refName}";

        var content = (HttpContent) new ObjectContent<object>(new
            {
                StagesToSkip = new object[0],
                TemplateParameters = parameters,
                Variables = new object(),
                Resources = new
                {
                    Repositories = new
                    {
                        Self = new
                        {
                            RefName = refName
                        }
                    }
                }
            },
            new VssJsonMediaTypeFormatter(true));
        var queryParameters = new Dictionary<string, string>();
        return await SendAsync<PipelineRun>(method, locationId, routeValues, version, content, queryParameters, cancellationToken: CancellationToken.None);
    }
}

public class Pipeline
{
    [JsonProperty("url")]
    public string Url { get; set; }

    [JsonProperty("id")]
    public int Id { get; set; }

    [JsonProperty("revision")]
    public int Revision { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("folder")]
    public string Folder { get; set; }
}

public class Repository
{
    [JsonProperty("id")]
    public string Id { get; set; }

    [JsonProperty("type")]
    public string Type { get; set; }
}

public class Self
{
    [JsonProperty("repository")]
    public Repository Repository { get; set; }

    [JsonProperty("refName")]
    public string RefName { get; set; }

    [JsonProperty("version")]
    public string Version { get; set; }
}

public class Repositories
{
    [JsonProperty("self")]
    public Self Self { get; set; }
}

public class Resources
{
    [JsonProperty("repositories")]
    public Repositories Repositories { get; set; }
}

public class PipelineRun
{
    [JsonProperty("pipeline")]
    public Pipeline Pipeline { get; set; }

    [JsonProperty("state")]
    public string State { get; set; }

    [JsonProperty("createdDate")]
    public DateTime CreatedDate { get; set; }

    [JsonProperty("url")]
    public string Url { get; set; }

    [JsonProperty("resources")]
    public Resources Resources { get; set; }

    [JsonProperty("id")]
    public int Id { get; set; }

    [JsonProperty("name")]
    public string Name { get; set; }
}

Usage:

PipelineHttpClient client = new PipelineHttpClient(...);
var project = Guid.Parse("TODO");
var pipelineId = 1234;
// pipeline parameters:
var parameters = new {meaningOfLife = 42};
var result = await client.RunPipelineAsync(project, pipelineId, parameters, "refs/heads/feature/my-branch");
if (result.State == "inProgress")
{
    // TODO the pipeline is running!
}
0
Mike D On

these using statements are required for the classes above:

using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
0
Stefan Wittmann On

I was able to fix the problem by passing templateparameters instead of parameters:

build.Parameters = JsonSerializer.Serialize(dict);


build.TemplateParameters = new Dictionary<string, string> { { "testkey", "testvalue" } };

the complete code:

 Build build = new Build()
  {
     Definition = definition,
     Project = project
  };
 build.TemplateParameters = new Dictionary<string, string> { { "testkey", "testvalue" } };
 Build queuedBuild = buildClient.QueueBuildAsync(build).GetAwaiter().GetResult();

used .net sdk lib:

Microsoft.TeamFoundationServer.Client version 19.219.0-preview