Page Type | Languages | Key Services | Tools |
---|---|---|---|
Sample | C# (.NET Core 6) | Azure Functions Azure User-Assigned Managed Identity Azure DevOps Azure Pipelines |
Visual Studio Azure Functions Core Tools |
When calling the Azure DevOps REST API, a common approach is to generate a Personal Access Token (PAT) to authenticate requests, but managing PATs can expose you to security risks if not properly handled. This codebase demonstrates how to avoid managing PATs and Secrets entirely by providing an example of making authenticated requests to the Azure DevOps REST API with a Managed Identity via an Azure Function.
This codebase provides a modified and more detailed walkthrough of this sample GitHub project and this video guide. The Function in this guide runs an Azure Pipeline when a request is made.
The scenario presented in this codebase is simple and contrived, and it should be viewed as a foundation for modification and expansion into more complex applications.
-
An Azure Subscription - for hosting cloud infrastructure
-
Azure DevOps with an Organization, Project and a Pipeline
-
Visual Studio - for development and publishing to Azure
-
Azure Functions Core Tools - for local Function development
-
Add the code from
AzDOFunction/AzDOFunction/Function1.cs
to the.cs
file in your project. -
Open the Terminal in Visual Studio (View > Terminal),
cd
to your project directory, and run the following commands to install the required project dependencies (package versions current as of writing):dotnet add package Microsoft.VisualStudio.Services.InteractiveClient --version 19.232.0-preview dotnet add package Microsoft.TeamFoundationServer.Client --version 19.232.0-preview dotnet add package Azure.Identity --version 1.10.4 dotnet add package Microsoft.NET.Sdk.Functions --version 4.2.0
-
Ensure you're signed into Visual Studio with an account that has access to your Azure Tenant.
-
In Azure, create a user-assigned Managed Identity. It will be assigned to your Function later.
- Consider creating a resource group for the Managed Identity, which you can later publish the Function to.
-
In Azure DevOps, add the user-assigned Managed Identity to the AzDO Organization as a user with the appropriate permissions. When searching for a user, type in the name of the user-assigned Managed Identity that you created.
-
In Visual Studio in your
.cs
file, update the following string variables in the declarations section of the class:AdoOrgName
: This is the name of your Azure DevOps OrganizationAadTenantId
: You can get this from the Azure PortalAadUserAssignedManagedIdentityClientId
: This is the Client ID of the user-assigned Managed Identity you created. You can get this from the Azure Portal
-
Test your Function app locally. Consider invoking the local Function from a tool like Postman.
-
When calling the Function, two querysting parameters are required:
projectName
: Name of the Azure DevOps ProjectpipelineId
: ID of the Pipeline you want to run. You can get the Pipeline ID from the URL of the Pipeline in Azure DevOps.- The URL may look like:
http://localhost:<port>/api/<function-name>?pipelineId=<id>&projectName=<name>
- On success, you will see the message, "Pipeline run started". If you navigate to the Pipeline in Azure DevOps, you will see a new run.
-
-
Publish the Function to Azure.
- In addition to the Function resource in Azure, this deployment requires a Resource Group, Storage Account, and optionally an App Insights resource. If you don't already have these resources created, you can create them at this point.
- Consider publishing to a Windows plan, which provides a better testing experience in the Azure Portal. Using App Insights is also highly recommended to enable a better debugging experience.
-
In your Azure Function, navigate to the Identity blade and assign the user-assigned Managed Identity to the Function.
-
Test your Azure Function. You can use the Code + Test feature in the Azure Portal editor - you may need to enable CORS for https://portal.azure.com in the Function's CORS settings. Remember to include the two required querystring parameters.
- On success, you will see the message, "Pipeline run started". If you navigate to the Pipeline in Azure DevOps, you will see a new run.
-
You may now POST to the Function app URL from any other service to initiate pipeline runs without needing to manage PATs or Secrets.
If you want to be able to set variables, create a pipeline variable in Azure DevOps and select 'Settable at queue time' on the variables tab of the pipeline editor. Read more at https://aka.ms/AzPipelines/SettingVariablesAtQueueTime.
Ensure you also update the variable and set 'Let users override this value when running this pipeline'.
In your pipeline, you can now reference the variable as $(variableName)
(in the example, this would be $(branch)
).
To pass your variable to the pipeline when calling the Function, add a JSON object to the body, where the key matches the variable name and the value is the value you want to set. In your .cs
file, update the JsonKey
variable to the name of the key you want to use. For example, in the provided code, the key is branch
, so the JSON object passed into the REST API body would look like:
{
"branch": "your-branch-name"
}
Note that in this example, I hardcoded the JsonKey, but you can modify the code to more dynamically set the key.
If you do not want to use variables, you can comment out the block of code between the comments 'the below/above code is used to set a pipeline variable'.
This example sets a single variable, but you can modify the code to set multiple variables.
- Optionally consider configuring the Azure DevOps organization name, Tenant ID, and Managed Identity Client ID as settings in
local.settings.json
and in the Azure Function App configuration. In this sample, the settings are hardcoded in the code for simplicity. - Consider using Infrastructure as Code to create the Function App and assign the user-assigned Managed Identity to it.
- Consider building a CI/CD pipeline for deploying the Function App.