---
title: Managed Identity Authentication with Azure REST APIs and Azure Container Apps
description: Learn how to authenticate with Azure REST APIs using Managed Identity on Azure Container Apps. Dive into the detailed steps and code examples in this informative guide by Matthew Nixon.
---

[Musings from the Scribes](https://blog.arkahna.io)

# [Managed Identity Authentication with Azure REST APIs and Azure Container Apps](https://blog.arkahna.io/managed-identity-authentication-with-azure-rest-apis-and-azure-container-apps)

 Written by [Matthew Nixon](https://blog.arkahna.io/author/matthew-nixon) | May 6, 2024 7:30:36 AM

As part of an engagement with a client, I had to write guidance around using Managed Identity when interacting directly with Azure REST APIs on Azure Container Apps. Turns out - this wasn’t as easy as I predicted. Nor was the Microsoft documentation for this as comprehensive as I would like.

> This process differs from Managed Identity authentication on an *Azure VM*. A garden path I went naively down…

Read this article if you want the background for what comes next: [Managed identities in Azure Container Apps](https://learn.microsoft.com/en-us/azure/container-apps/managed-identity?tabs=portal%2Chttp)

These steps pertain to testing code that has 2 flows:

1. When run locally, az cli commands are called to fetch an authentication token.
2. When run on an Azure Container App/Job instance, the local identity endpoint is invoked and a token is returned.

Pre-requisites for developer testing:

1. Create Azure Storage and ensure it is not restricted in network access. As this is Enabled from All Networks, take care with PIID or sensitive data as ordinarily you wouldn’t allow storage like this to accessed from any network.
2. Create a container and put a file in it.
3. Grant ***yourself*** Storage Blob Data Owner permissions
4. See the demo code at the bottom of this article.
5. Ensure *az cli* is installed on your machine.
   
     1. 1. Ensure you are logged in and the subscription is set to the same as the Storage above. e.g.  
               `az account set --subscription`
6. Update these 2 variables with the information from above
   
    
   
    
7. Run these commands in the console  
   `pnpm i`  
   `pnpm run build`  
   `pnpm start`
   
   This will connect to the store by fetching a token from this command:  
   `az account get-access-token --resource https://storage.azure.com/ --query accessToken -o tsv`
   
   It should list the contents of the container in raw XML, e.g:
   
   `Full Container Object: <?xml version="1.0" encoding="utf-8"?><EnumerationResults ServiceEndpoint="https://arkdemodemotest.blob.core.windows.net/" ContainerName="democontainer"><Blobs><Blob><Name>hi.txt</Name><Properties><Creation-Time>Fri, 12 Apr 2024 06:09:23 GMT</Creation-Time><Last-Modified>Fri, 12 Apr 2024 06:09:23 GMT</Last-Modified><Etag>0x8DC5AB7197BA857</Etag><Content-Length>0</Content-Length><Content-Type>text/plain</Content-Type><Content-Encoding /><Content-Language /><Content-CRC64 /><Content-MD5>1B2M2Y8AsgTpgAmY7PhCfg==</Content-MD5><Cache-Control /><Content-Disposition /><BlobType>BlockBlob</BlobType><AccessTier>Hot</AccessTier><AccessTierInferred>true</AccessTierInferred><LeaseStatus>unlocked</LeaseStatus><LeaseState>available</LeaseState><ServerEncrypted>true</ServerEncrypted></Properties><OrMetadata /></Blob></Blobs><NextMarker /></EnumerationResults>`
8. You’ll notice if NODE\_ENV is set to Production, it will attempt to fetch a token from an identity endpoint based on environment variables made available to Azure Container Apps. AZ CLI is of course not available in these containers.
9. Here is a Manually invoked Azure Container App Job with a managed identity assigned:
   
   Ensure your User Assigned Managed Identity has the appropriate permissions, same as step 3:
   
   Here you can see it fetching a token against calling: [http://localhost:42356/msi/token?api-version=2019-08-01&resource=https://storage.azure.com/&client\_id=07a42cba-009b-4bce-b895-4fd940e4643f](http://localhost:42356/msi/token?api-version=2019-08-01&resource=https://storage.azure.com/&client_id=07a42cba-009b-4bce-b895-4fd940e4643f)
   
   Take note we must pass in the client\_id of the generated Managed Identity, i.e.
   
   This example is from my Arkahna subscription:
   
    

## Appendix:

### index.ts:

import dotenv from "dotenv";  
import { exec } from 'child\_process';  
import { promisify } from 'util';  
import { parseStringPromise } from 'xml2js'; 

dotenv.config();

const execAsync = promisify(exec);

// Replace these with your Storage account details  
const accountName = 'your\_account\_here';  
const containerName = 'your\_container\_here';  
const managed\_identity\_client\_id = 'client\_id\_guid\_here'

const getAzCliBlobAccessToken = async () => {  
    try {  
      // Command to get the access token  
      const command = 'az account get-access-token --resource https://storage.azure.com/ --query accessToken -o tsv';  
        
      // Execute the command  
      const { stdout, stderr } = await execAsync(command);  
        
      if (stderr) {  
        console.error('Error:', stderr);  
        return null;  
      }  
        
      // Output the access token  
      if (stdout) {  
      const output = stdout as unknown as string;  
      const accessToken = output.trim();  
      console.log('Access Token:', accessToken);  
      return accessToken;  
    }  
    } catch (error) {  
      console.error('Execution error:', error);  
      return null;  
    }  
  };  
    
const getAuthToken = async () => {  
    // IDENTITY\_ENDPOINT and IDENTITY\_HEADER are supposed to be provided for us  
    // see: https://learn.microsoft.com/en-us/azure/container-apps/managed-identity?tabs=cli%2Chttp  
    const url = \`${process.env.IDENTITY\_ENDPOINT}?api-version=2019-08-01&resource=https://storage.azure.com/&client\_id=${managed\_identity\_client\_id}\`;  
    const identityHeader = process.env.IDENTITY\_HEADER || ''  
    const headers = {  
        'X-IDENTITY-HEADER': identityHeader          
    };

    try {  
        console.log(\`Calling ${url}\`)  
        console.log(\`X-IDENTITY-HEADER is ${identityHeader}\`)  
        const response = await fetch(url, { method: 'GET', headers: headers });  
        if (!response.ok) {  
            throw new Error(\`Failed to fetch token: ${response.statusText}\`);  
        }  
        const data = await response.json();  
        return data.access\_token;  
    } catch (error) {  
        console.error('Error obtaining auth token:', error);  
        return null;  
    }  
};

const listBlobs = async () => {  
    console.log('--------------- Azure REST Auth Demo --------------- ')

    var local = false  
    if (typeof process.env.NODE\_ENV === 'undefined' || process.env.NODE\_ENV !== 'production') {  
        local = true  
    }        

    var token

    if (!local) {  
        console.log('Running in Azure Container App');  
        token = await getAuthToken();  
    } else {  
        token = await getAzCliBlobAccessToken()  
    }  
    // obviously in a real app don't log tokens to console, this is visibility  
    console.log(\`token is ${token}\`)

    const url = \`https://${accountName}.blob.core.windows.net/${containerName}?restype=container&comp=list\`;

    try {  
        const response = await fetch(url, {  
            method: 'GET',  
            headers: {  
                'Authorization': \`Bearer ${token}\`,  
                'x-ms-version': '2020-02-10',  
            },  
        });

        if (!response.ok) {  
            throw new Error(\`Failed to list blobs: ${response.statusText}\`);  
        }

        // NOTE, this service is XML!  
        const xml = await response.text();

        try {  
            const result = await parseStringPromise(xml);

        if (result.EnumerationResults) {  
            const blobs = result.EnumerationResults.Blobs\[0\].Blob;  
            // Map through the array and extract blob names  
            const blobNames = blobs.map((blob: any) => blob.Name\[0\]);              
            console.log('Full Container Object:', result);  
            console.log('Blob Names:', blobNames);  
            console.log('Full Container raw xml:', xml);  
        } else {  
            console.log('No blobs found or unable to parse blobs.');  
        }  
        } catch (parseError) {  
            console.error('Failed to parse XML:', parseError);  
            console.log('Raw response:', xml.slice(0, 100));   
        }        

        console.log('--------------- End --------------- ')  
    } catch (error) {  
        console.error('Error listing blobs:', error);  
    }  
};

listBlobs();

### Docker file:

# Use an official Node.js runtime as a parent image  
FROM node:latest

# Set the working directory in the container to /usr/src/app  
WORKDIR /usr/src/app

# Install pnpm  
RUN npm install -g pnpm

# Copy the package.json and pnpm-lock.yaml (or pnpm-workspace.yaml) file  
COPY package.json pnpm-lock.yaml ./

# Install any needed packages specified in package.json  
RUN pnpm install

# Bundle the app source inside the Docker image  
COPY . .

# Build the app  
RUN pnpm run build

ENV NODE\_ENV production  
# Define the command to run the app using CMD which defines your runtime  
CMD \["pnpm", "start"\]

### .dockerignore:

\*\*/node\_modules  
\*\*/dist

### package.json:

{  
  "name": "restauthdemo",  
  "version": "1.0.0",  
  "description": "",  
  "main": "index.js",  
  "scripts": {  
    "build": "tsc",  
    "start": "node dist/index.js",  
    "dev": "ts-node src/index.ts"  
  },  
  "keywords": \[\],  
  "author": "",  
  "license": "ISC",  
  "dependencies": {  
    "child\_process": "^1.0.2",  
    "dotenv": "^16.4.5",  
    "util": "^0.12.5",  
    "xml2js": "^0.6.2"  
  },  
  "devDependencies": {  
    "@types/node": "^20.12.7",  
    "@types/xml2js": "^0.4.14",  
    "typescript": "^5.4.5"  
  }  
}

### tsconfig.json:

{  
  "compilerOptions": {  
    "target": "es6",  
    "module": "commonjs",  
    "rootDir": "./src",  
    "outDir": "./dist",  
    "esModuleInterop": true,  
    "strict": true  
  },  
  "include": \["src/\*\*/\*.ts"\],  
  "exclude": \["node\_modules"\]  
}

[View full post](https://blog.arkahna.io/managed-identity-authentication-with-azure-rest-apis-and-azure-container-apps)

```json
{
  "@context" : "http://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Matthew Nixon"
  },
  "dateModified" : "2024-05-06T07:30:36.742Z",
  "datePublished" : "2024-05-06T07:30:36Z",
  "headline" : "Managed Identity Authentication with Azure REST APIs and Azure Container Apps",
  "image" : {
    "@type" : "ImageObject",
    "height" : 370,
    "url" : "https://40094738.fs1.hubspotusercontent-na1.net/hubfs/40094738/blog-images/image-20240419-070430.png",
    "width" : 991
  },
  "mainEntityOfPage" : "https://blog.arkahna.io/managed-identity-authentication-with-azure-rest-apis-and-azure-container-apps",
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "height" : 60.0,
      "url" : "https://40094738.fs1.hubspotusercontent-na1.net/hubfs/40094738/logos/Arkahna_Brand%2BWord-Horizontal.png",
      "width" : 166.05327
    },
    "name" : "Musings from the Scribes"
  }
}
```