Stack Exchange
Queue Azure devops YAML pipeline from code does not accept runtime parameters
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.

Parameters here are not runtime parameters but variables. I'm not sure if this is exposed to SDK to pass runtime parameters. You may try to define default value if this is an option for you.

@ShaykiAbramczyk I'm not sure if this is a duplicate. Pankaj wants to pass runtime parameetrs programaticallu which haven't been available at time when question pointed by you was asked.

@KrzysztofMadej agree, but I think it should work. Pankaj can you test it?

I tested it and I got exception that parameter1 is required. So clearly sth is wrong here. But I assume that this is not exposed via SDK or there is magic way to pass it. I also tried to pass it as parameters.paramter1 but with the same result.

I re-opened the question :)

atlease it works with with REST api "https://{{your_tfs_server}}/{project}/_apis/pipelines/{pipelineId}/runs?api-version=6.0-preview.1  with body {"definition":{"id":2},"templateParameters":{"parameter1":"parameterValue"}}

looks like some issue wit nuget package "Microsoft.VisualStudio.Services.Client" Version="16.174.0-preview"

Hi, any plans to make these parameters work with SDK? I need that feature as well.

Well, no answer is also an 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.

One year later, is this still the case?  Also, is the C# client code open source - I can't seem to find it?

Yes. It's still the case. Having the same issue here.

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!
}


I'm pretty sad that a whole year later the SDK still doesn't support this. Although your code just worked fine, thank you very much!

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;


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
