# Federated Credentials with User-Managed Identity to Access Azure Services through GitHub actions

The tittle sounds confusing right ?

So how to access Azure services with federated credentials that are tied with User Managed Identities ? Even I was skeptical if it is even possible because its more common and widely known that federated identity credentials (OIDC federation) are tied with service principal and not managed identities.

In this article I am going to delve into details on how to achieve this. But before that I strongly recommend reading the following article

[https://www.azureguru.net/configuring-federated-credentials-in-microsoft-azure-using-github-actions](https://www.azureguru.net/configuring-federated-credentials-in-microsoft-azure-using-github-actions)

that I published last year, which provides a detailed walkthrough of configuring federated credentials using GitHub actions as IdP (Identity Provider) through service principal.

Before we set up and test GitHub actions lets first ensure that a defined **User Managed Identity** can access the **Azure ADLS GEN2** storage. For that lets create an **Azure Function** and use an user managed identity in the Azure Function to access Azure storage.

### User Managed Identity

Create a new user managed identity or use an existing one. I created one with name **Federated\_Credentials\_Function\_Identity** .

In the next step grant role access **Storage Blob Data Owner** to the created Managed Identity that needs access to the Azure storage.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/af087e1f-5953-48a6-8205-61d5edb22727.png align="center")

You could also grant **Storage Blob Data Contributor** role but the **Storage Blob Data Owner** role has POSIX access control (ACL access) that auto grants all the (r-w-x) privileges to the owner for all the underlying objects under the container.

For instance in the following screenshot we can see that the owner i.e. the **Storage Blob Data Owner** was auto assigned the (r-w-x) access .

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/029f995d-6a2d-4686-a067-881e34696c04.png align="center")

Note down the **ClientID** of the created User Managed Identity. We will need it to reference in our Azure Function.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/c5e69c12-0e3a-41af-a138-c52131779381.png align="center")

### Azure Function Code

Create a new Azure function and add the following code.

> Before that ensure that you have added all the required project references.

```csharp
using Azure.Identity;
using Azure.Storage.Files.DataLake;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

namespace AzureFunction;

public class Function1
{
    private readonly ILogger<Function1> _logger;
    public static List<string> listoutput = new();

    public Function1(ILogger<Function1> logger)
    {
        _logger = logger;
    }

    [Function("Function1")]
    public async Task<OkObjectResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequest req)
    {
        {
            string storageAccount = "Storage Account";
            string fileSystemName = "Storage Container";           
            string userAssignedClientId = "Managed Identity ClientID";
            DataLakeServiceClient datalake_Service_Client;
            DataLakeFileSystemClient dataLake_FileSystem_Client;
            try
            {
                string dfsUri = $"https://{storageAccount}.dfs.core.windows.net";

                var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
                {
                    ManagedIdentityClientId = userAssignedClientId,
                    
                });

                datalake_Service_Client = new DataLakeServiceClient(new Uri(dfsUri), credential);
                dataLake_FileSystem_Client = datalake_Service_Client.GetFileSystemClient(fileSystemName);
                DataLakeDirectoryClient rootDirectory_ = dataLake_FileSystem_Client.GetDirectoryClient("");
                listoutput.Clear();

                return new OkObjectResult(new
                {
                    Output = TraverseDirectory(rootDirectory_)
                });
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error accessing ADLS Gen2");

            }

            return new OkObjectResult("");

        }

    }

    public static async Task<List<string>> TraverseDirectory(DataLakeDirectoryClient directoryClient)
    {

        await foreach (var item in directoryClient.GetPathsAsync())
        {
            if (item.IsDirectory == true)
            {
                listoutput.Add(item.Name);
                string[] split = item.Name.Split("/");
                var subDir = directoryClient.GetSubDirectoryClient(split.Length == 1 ? split[0] : split[split.Length - 1]);
                await TraverseDirectory(subDir);
            }
        }

        return listoutput;
    }
}
```

> The above code returns only the top level directories of the given container. It is a simplified version of the code from the article [here](https://www.azureguru.net/retrieve-the-hierarchical-directory-structure-from-azure-adls-gen2-storage) that recursively returns hierarchical directory structure for a given Azure storage container.

Deploy the above code to Azure, execute it and verify that it returns all the subdirectories within the specified container.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/d44941f3-5a56-4906-8ea2-ac7e861ae68a.gif align="center")

Following is the output from the execution of the Azure function.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/af4c59db-d15a-48af-aadc-6fec72781157.png align="center")

Now that **Federated\_Credentials\_Function\_Identity** which is an User Managed Identity can access the Azure storage , in the next step we can go ahead and create federated credentials tied to this specific User Managed Identity.

Go back to the created User Managed Identity on the Azure portal and click **Federated Credentials option** followed by **Add Credentials** .

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/4e4e8a4e-3d49-4162-b1d1-94973b2c7b83.png align="center")

In the next window, select **GitHub Actions deploying Azure resources** option under **Federated credential scenarios**.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/cb27e8c9-2420-4e79-89a5-3f0d327ce743.png align="center")

In the next step, enter the following details on **Edit Federated Credential** page

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/7f0cf3a6-6853-4cfb-9f01-e521c83f0737.png align="center")

*   **Organization** is the username or organization name of the repo
    
*   **Repository** is the name of the repository that you want to use. By default it also will be the name of the Federated Credentials.
    
*   **Entity** is the type of entity. Following are the available options .
    

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/f3c84986-516c-48cd-8909-4a6b3eb3cc10.png align="center")

*   **Branch** specifies the repo branch through which the request would be trusted by federated credentials.
    
*   **Audience** keep it to default
    

Click Update and double check if the Federated Credential is created.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/6361aaa0-95c7-48cc-82d8-82512e96a83b.png align="center")

In this example the Federated Credentials is named **GitHubActions**

We now need to set the environment variables for the GitHub repository.

Go to the GitHub repository and under **Secrets and variables**

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/43d1364b-bbf5-40aa-8238-35c944cdae39.png align="center")

and under **Actions** declare the following Repository secrets that you see in the screenshot below and enter their corresponding values

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/af0faa5a-0754-41d4-bfa4-383ab9e4c612.png align="center")

Fetch the values for **Azure\_Client\_ID** and **Azure\_Subscription\_ID** from the User Managed Identity that was set for GitHub actions. The value for secret **Azure\_Tenant\_Id** will be the Azure Tenant Id.

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/ae66db8f-f7ea-49f6-9c5c-4e4b7a5c90f9.png align="center")

The **Azure\_Storage\_Account** value should be the ADLS Gen2 storage account from which you want to retrieve the directory structure.

In next step , Go to the GitHub repo and under **Actions** select **New WorkFlow**

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/6e6234ae-19f5-4358-9bf3-1a841b833736.png align="center")

and then under **Simple Workflow** click **Configure**

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/f0987a51-bbec-45b5-9f6c-829375444c7b.png align="center")

and add the following code to the **\*. yml** file. In this example the file is named **Workflow.yml**

```yaml
name: GitHubActions Azure Federated Credentials Demo

on:
  push:
    branches:
      - main

jobs:
  auth-azure:
    runs-on: ubuntu-latest

    permissions:
      id-token: write    
      contents: read

    steps:
      - name: 'Login to Azure with OIDC federated credentials'
        uses: azure/login@v2
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

      - name: Show success message
        run: echo "Successfully authenticated for Azure using federated credentials!"

      - name: Get List of Folders
        uses: azure/cli@v2
        with:
          azcliversion: latest
          inlineScript: |
            echo "Fetching all directories..."
            
              FOLDERS=$(az storage fs file list \
            --account-name "${{ secrets.AZURE_STORAGE_ACCOUNT }}" \
            --file-system customers \
            --auth-mode login \
            --query "[?isDirectory].name" \
            --output table)
              
            echo "Available folders:"
            echo "$FOLDERS"
```

The code above logins into the Azure tenant based on the Environment variables set for the repository and lists recursively outputs all the directories for the given container (**customers** container in this example).

Commit the changes

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/d78146c0-6c2d-4dd7-8629-0b4cedf47396.png align="center")

As expected, the output contains all the underlying directories of container **customers.**

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/4e6197dd-dea4-4a28-8984-d96a63037dfe.png align="center")

### Execution>>

![](https://cdn.hashnode.com/uploads/covers/6693c62c166ee9c594cffda0/38c3ff5b-b266-4c42-b9ea-001e5f6b65fa.gif align="center")

### Conclusion

In this article, we explored how to configure Federated Credentials for a User Managed Identity, enabling password less authentication to Azure services for external Identity providers. By leveraging workload identity federation, you can grant external workloads (like GitHub Actions, Kubernetes workloads) access to Azure resources using short-lived tokens through federated credentials.

I hope this guide helps you to get started on implementation of federated credentials with User Managed Identities in your Azure environments.

Thanks for reading !!!
