Tuesday, September 2, 2025

Building Secure APIs with FastAPI and Azure AD Authentication

Building Secure APIs with FastAPI and Azure AD Authentication

Published on September 2, 2025

In today's world of microservices and API-first architecture, securing your endpoints is crucial. In this blog post, we'll explore how to create a simple yet secure FastAPI application that integrates with Azure Active Directory (Azure AD) for authentication using bearer tokens.

What We're Building

Our FastAPI application demonstrates:

  • Bearer token authentication using JWT tokens from Azure AD
  • Token validation without signature verification (for demonstration purposes)
  • User information extraction from the JWT payload
  • Protected endpoints that require valid authentication

The Architecture

The application consists of a single FastAPI server with:

  • A token verification function that validates Azure AD JWT tokens
  • A protected /hello endpoint that returns user information
  • Integration with Azure AD using tenant-specific configuration

Key Components

1. Azure AD Configuration

TENANT_ID = "6213dbca-6fe9-42b2-bdaa-ccf2fc2f6332"
AUTHORITY = f"https://login.microsoftonline.com/{TENANT_ID}"
APP_ID_URI = "b1c47286-f990-408a-8d6e-938377129947"
CLIENT_ID = "b1c47286-f990-408a-8d6e-938377129947"
SECRET_KEY = "APP Secret Key"

These configuration values connect your application to a specific Azure AD tenant and define the valid audiences for token validation.

2. Token Verification

The verify_token function is the heart of our authentication system:

  • Extracts bearer tokens from the Authorization header
  • Decodes JWT tokens without signature verification (⚠️ Note: In production, always verify signatures!)
  • Validates the audience to ensure tokens are intended for this application
  • Handles common JWT errors like expired or malformed tokens

3. Protected Endpoints

Our /hello endpoint demonstrates how to:

  • Require authentication using FastAPI's dependency injection
  • Extract user information from the token payload
  • Return personalized responses based on the authenticated user

What Makes This Special

  1. Simple Integration: Just a few lines of code to secure your endpoints
  2. Enterprise Ready: Uses Azure AD, which is widely adopted in corporate environments
  3. User Context: Extracts meaningful user information like username and employee ID
  4. Scope Awareness: Can handle OAuth scopes for fine-grained permissions

Running the Application

Getting started is straightforward:

# Install dependencies
pip install fastapi uvicorn python-jose[cryptography]

# Run the server
uvicorn main:app --reload --host 0.0.0.0 --port 8000
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import uvicorn
import jwt

app = FastAPI(title="FastAPI Authentication Example")
security = HTTPBearer()

TENANT_ID = "6213dbca-6fe9-42b2-bdaa-ccf2fc2f6332"
AUTHORITY = f"https://login.microsoftonline.com/{TENANT_ID}"
APP_ID_URI = "b1c47286-f990-408a-8d6e-938377129947"  
CLIENT_ID = "b1c47286-f990-408a-8d6e-938377129947"
SECRET_KEY = "APP Secret Key"
VALID_AUDIENCES = [APP_ID_URI, CLIENT_ID]


def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
    """Verify the bearer token using JWT validation"""
    try:
        decoded = jwt.decode(credentials.credentials, options={"verify_signature": False})
        audience = decoded.get("aud")
       
        if audience not in VALID_AUDIENCES:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Invalid token audience",
                headers={"WWW-Authenticate": "Bearer"},
            )
        return decoded
       
    except jwt.ExpiredSignatureError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token has expired",
            headers={"WWW-Authenticate": "Bearer"},
        )
    except jwt.InvalidTokenError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid token format",
            headers={"WWW-Authenticate": "Bearer"},
        )

@app.get("/hello")
async def hello_world(decoded_token: dict = Depends(verify_token)):
    """Protected hello world endpoint"""
    username = decoded_token.get("preferred_username", "Unknown User")
    employee_id = decoded_token.get("employeeid", "Unknown")
   
    return {
        "message": "Hello World! You are authenticated.",
        "user": username,
        "employee_id": employee_id,
        "scopes": decoded_token.get("scp", "").split(" ")
    }

if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8000)


The application will be available at http://localhost:8000, with automatic API documentation at http://localhost:8000/docs.

Testing the Authentication

To test the protected endpoint, you'll need a valid Azure AD token:

curl -H "Authorization: Bearer YOUR_AZURE_AD_TOKEN" \
     http://localhost:8000/hello

A successful response will look like:

{
  "message": "Hello World! You are authenticated.",
  "user": "john.doe@company.com",
  "employee_id": "EMP123",
  "scopes": ["read", "write"]
}

Security Considerations

While this example demonstrates the basics, remember these important points for production use:

  1. Always verify token signatures - Don't set verify_signature: False in production
  2. Use HTTPS - Never transmit tokens over unencrypted connections
  3. Validate all claims - Check issuer, expiration, and other relevant claims
  4. Implement rate limiting - Protect against abuse and brute force attacks
  5. Log security events - Monitor authentication failures and suspicious activity

Conclusion

FastAPI's elegant dependency injection system makes it incredibly easy to add authentication to your APIs. Combined with Azure AD's robust identity platform, you can quickly build secure, enterprise-ready applications.

This example provides a solid foundation that you can extend with additional features like:

  • Role-based authorization
  • API key authentication
  • Multi-tenant support
  • Advanced logging and monitoring

The complete code is available in this repository, ready for you to experiment with and adapt to your specific needs.


Ready to secure your APIs? Start with this example and build from there. FastAPI and Azure AD make a powerful combination for modern web applications.

Technologies Used

  • FastAPI - Modern, fast web framework for building APIs
  • Azure Active Directory - Microsoft's cloud-based identity service
  • JWT - JSON Web Tokens for secure information transmission
  • Python-JOSE - JWT library for Python
  • Uvicorn - Lightning-fast ASGI server

Resources

Friday, August 29, 2025

Building a Read-Only Microsoft Information Protection Sensitivity Label Reader in C#

Building a Read-Only Microsoft Information Protection Sensitivity Label Reader in C#
https://learn.microsoft.com/en-us/information-protection/develop/quick-app-initialization-csharp

Introduction

In today's data-driven world, protecting sensitive information is paramount. Microsoft Information Protection (MIP) provides robust capabilities for classifying, labeling, and protecting documents across your organization. While many solutions focus on applying and modifying sensitivity labels, there's often a need for read-only operations—such as auditing, compliance checking, or simply understanding what labels are applied to existing documents.

In this article, we'll explore how to build a simple yet powerful C# console application that reads Microsoft Information Protection sensitivity labels from files without modifying them. This solution is perfect for compliance officers, IT administrators, or developers who need to audit document classifications safely.

What We'll Build

Our application will:

  • Connect to Microsoft Information Protection services
  • List all available sensitivity labels in your organization
  • Read existing sensitivity labels from specific files
  • Display detailed label information including protection status
  • Operate in read-only mode, ensuring no modifications to files

Prerequisites

Before we start, ensure you have:

  1. Azure AD App Registration with appropriate MIP permissions
  2. Visual Studio or .NET Framework 4.8 development environment
  3. Microsoft Information Protection SDK (via NuGet)
  4. Microsoft Authentication Library (MSAL) for authentication
  5. Files with sensitivity labels applied for testing

Azure AD App Registration Setup

First, you'll need to register an application in Azure Active Directory:

  1. Navigate to the Azure Portal → Azure Active Directory → App registrations
  2. Click "New registration"
  3. Provide a name (e.g., "Sensitivity Label Reader")
  4. Set redirect URI to http://localhost (for desktop app)
  5. Under API permissions, add:
    • UnifiedPolicy.User.Read (for reading label policies)
    • InformationProtectionPolicy.Read (for accessing protection policies)

Project Setup and Dependencies

Create a new console application and install the required NuGet packages:

<PackageReference Include="Microsoft.InformationProtection.File" Version="1.17.158" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.76.0" />

The Complete Implementation

Here's our streamlined, single-file implementation:

using Microsoft.InformationProtection;
using Microsoft.InformationProtection.File;
using Microsoft.Identity.Client;
using System;
using System.Linq;
using System.Threading.Tasks;

namespace ConsoleApp5
{
    internal class Program
    {
        private const string clientId = "clientId";
        private const string tenantId = "tenantId";
        private const string appName = "SensitivityLabelReader";
        private const string userEmail = "user1@demain.com";
        private const string filePath = "C:\\Users\\TestdDoc.docx";

        static void Main(string[] args)
        {
            try
            {
                // Initialize MIP SDK
                MIP.Initialize(MipComponent.File);

                // Create application info
                var appInfo = new ApplicationInfo()
                {
                    ApplicationId = clientId,
                    ApplicationName = appName,
                    ApplicationVersion = "1.0.0"
                };

                // Create delegates
                var authDelegate = new AuthDelegateImplementation(appInfo, tenantId);
                var consentDelegate = new ConsentDelegateImplementation();

                // Setup MIP context and profile
                var mipConfiguration = new MipConfiguration(appInfo, "mip_data", Microsoft.InformationProtection.LogLevel.Error, false, CacheStorageType.OnDiskEncrypted);
                var mipContext = MIP.CreateMipContext(mipConfiguration);
                var profileSettings = new FileProfileSettings(mipContext, CacheStorageType.OnDiskEncrypted, consentDelegate);
                var fileProfile = Task.Run(async () => await MIP.LoadFileProfileAsync(profileSettings)).Result;

                // Setup engine
                var engineSettings = new FileEngineSettings(userEmail, authDelegate, "", "en-US")
                {
                    Identity = new Identity(userEmail)
                };
                var fileEngine = Task.Run(async () => await fileProfile.AddEngineAsync(engineSettings)).Result;

                // Display available labels
                Console.WriteLine("Available Sensitivity Labels:");
                Console.WriteLine("==============================");
                foreach (var label in fileEngine.SensitivityLabels)
                {
                    Console.WriteLine($"{label.Name} : {label.Id}");
                    foreach (var child in label.Children)
                    {
                        Console.WriteLine($"\t{child.Name} : {child.Id}");
                    }
                }
                Console.WriteLine();

                // Read label from file
               
                Console.WriteLine($"Reading sensitivity label from: {filePath}");
                Console.WriteLine("===============================================");

                var handler = Task.Run(async () => await fileEngine.CreateFileHandlerAsync(filePath, filePath, true)).Result;
                var contentLabel = handler.Label;

                if (contentLabel?.Label != null)
                {
                    Console.WriteLine($"Label Name: {contentLabel.Label.Name}");
                    Console.WriteLine($"Label ID: {contentLabel.Label.Id}");
                    Console.WriteLine($"Is Protected: {contentLabel.IsProtectionAppliedFromLabel}");
                    if (!string.IsNullOrEmpty(contentLabel.Label.Description))
                        Console.WriteLine($"Description: {contentLabel.Label.Description}");
                    if (contentLabel.Label.Parent != null)
                        Console.WriteLine($"Parent Label: {contentLabel.Label.Parent.Name}");
                }
                else
                {
                    Console.WriteLine("No sensitivity label found on this file.");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }

            Console.WriteLine("\nPress any key to exit...");
            Console.ReadKey();
        }
    }

    // Authentication delegate for MIP SDK
    public class AuthDelegateImplementation : IAuthDelegate
    {
        private readonly ApplicationInfo _appInfo;
        private readonly string _tenantId;

        public AuthDelegateImplementation(ApplicationInfo appInfo, string tenantId)
        {
            _appInfo = appInfo;
            _tenantId = tenantId;
        }

        public string AcquireToken(Identity identity, string authority, string resource, string claims)
        {
            var authorityUri = new Uri(authority);
            authority = $"https://{authorityUri.Host}/{_tenantId}";

            var app = PublicClientApplicationBuilder
                .Create(_appInfo.ApplicationId)
                .WithAuthority(authority)
                .WithDefaultRedirectUri()
                .Build();

            var accounts = app.GetAccountsAsync().GetAwaiter().GetResult();
            var scopes = new[] { resource.TrimEnd('/') + "/.default" };

            var result = app.AcquireTokenInteractive(scopes)
                .WithAccount(accounts.FirstOrDefault())
                .WithPrompt(Prompt.SelectAccount)
                .ExecuteAsync()
                .GetAwaiter()
                .GetResult();

            return result.AccessToken;
        }
    }

    // Consent delegate for MIP SDK
    public class ConsentDelegateImplementation : IConsentDelegate
    {
        public Consent GetUserConsent(string url) => Consent.Accept;
    }
}


Monday, August 11, 2025

Real-Time Progress Updates with Azure Functions and SignalR Using Python

Real-Time Progress Updates with Azure Functions and SignalR Using Python

https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-quickstart-azure-functions-python?pivots=python-mode-decorators

Real-Time Progress Updates with Azure Functions and SignalR Using Python

This article demonstrates how to build a serverless Python application using Azure Functions and Azure SignalR Service to deliver real-time progress updates to clients. The solution includes HTTP endpoints, negotiation for SignalR connections, and timer triggers that broadcast messages and simulate progress updates for long-running operations.

Project File Structure

├── function_app.py
├── host.json
├── local.settings.json
├── requirements.txt
├── content/
│   └── index.html

Required Files

1. function_app.py

Main Azure Functions app with SignalR integration and timer triggers.

2. host.json

Azure Functions host configuration.

{
    "version": "2.0"
}

3. local.settings.json

Local development settings (update AzureSignalRConnectionString with your value).

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "python",
        "AzureSignalRConnectionString": "<your-signalr-connection-string>"
    }
}

4. requirements.txt

Python dependencies for Azure Functions and HTTP requests.

azure-functions
requests

5. content/index.html

Sample HTML page for the index endpoint.

<html>

<body>
  <h1>Azure SignalR Serverless Sample</h1>
  <div id="messages"></div>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7
    /signalr.min.js"></script>
  <script>
    let messages = document.querySelector('#messages');
    const apiBaseUrl = window.location.origin;
    const connection = new signalR.HubConnectionBuilder()
        .withUrl(apiBaseUrl + '/api')
        .configureLogging(signalR.LogLevel.Information)
        .build();
      connection.on('newMessage', (message) => {
        document.getElementById("messages").innerHTML = message;
      });

      // Display progress updates from the InsertTables timer
      connection.on('ProgressUpdate', (percent, message) => {
        const div = document.createElement('div');
        div.textContent = `${percent}% - ${message}`;
        messages.appendChild(div);
      });

      connection.start()
        .catch(console.error);
  </script>
</body>

</html>

Overview

  • Azure Functions: Used to create serverless endpoints and timer triggers.
  • Azure SignalR Service: Enables real-time communication between server and clients.
  • Python: Implements the logic for HTTP triggers, negotiation, and progress simulation.

Key Features

  1. HTTP Trigger: Responds to HTTP requests with a personalized message.
  2. Index Endpoint: Serves a static HTML page.
  3. SignalR Negotiation: Provides connection info for SignalR clients.
  4. Broadcast Timer: Periodically fetches GitHub star count and broadcasts it.
  5. Insert Tables Timer: Simulates inserting tables and sends progress updates to SignalR clients.

Complete Code

import azure.functions as func
import logging
import azure.functions as func
import os
import requests
import json 
import time

app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)

etag = ''
start_count = 0

@app.route(route="http_trigger")
def http_trigger(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

    if name:
        return func.HttpResponse(f"Hello, {name}. This HTTP triggered function executed successfully.")
    else:
        return func.HttpResponse(
             "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response.",
             status_code=200
        )
    

@app.route(route="index", auth_level=func.AuthLevel.ANONYMOUS)
def index(req: func.HttpRequest) -> func.HttpResponse:
    f = open(os.path.dirname(os.path.realpath(__file__)) + '/content/index.html')
    return func.HttpResponse(f.read(), mimetype='text/html')


@app.route(route="negotiate", auth_level=func.AuthLevel.ANONYMOUS, methods=["POST"])
@app.generic_input_binding(arg_name="connectionInfo", type="signalRConnectionInfo", hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
def negotiate(req: func.HttpRequest, connectionInfo) -> func.HttpResponse:
    return func.HttpResponse(connectionInfo)


@app.timer_trigger(schedule="*/1 * * * *", arg_name="myTimer",
              run_on_startup=False,
              use_monitor=False)
@app.generic_output_binding(arg_name="signalRMessages", type="signalR", hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
def broadcast(myTimer: func.TimerRequest, signalRMessages: func.Out[str]) -> None:
    global etag
    global start_count
    headers = {'User-Agent': 'serverless', 'If-None-Match': etag}
    res = requests.get('https://api.github.com/repos/azure/azure-functions-python-worker', headers=headers)
    if res.headers.get('ETag'):
        etag = res.headers.get('ETag')

    if res.status_code == 200:
        jres = res.json()
        start_count = jres['stargazers_count']

    signalRMessages.set(json.dumps({
        'target': 'newMessage',
        'arguments': [ 'Current star count of https://api.github.com/repos/azure/azure-functions-python-worker is: ' + str(start_count) ]
    }))


# New timer trigger: simulate inserting 10 tables and emit progress updates to SignalR
@app.timer_trigger(schedule="*/1 * * * *", arg_name="tablesTimer",
              run_on_startup=False,
              use_monitor=False)
@app.generic_output_binding(arg_name="signalRMessages", type="signalR", hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
def insert_tables_timer(tablesTimer: func.TimerRequest, signalRMessages: func.Out[str]) -> None:
    logging.info('InsertTables timer started.')

    total_tables = 10
    messages = []

    for i in range(1, total_tables + 1):
        # Simulate time-consuming insert
        time.sleep(1)

        percent = (i * 100) // total_tables
        msg = f"Table {i} of {total_tables} inserted."
        messages.append({
            'target': 'ProgressUpdate',
            'arguments': [percent, msg]
        })

    # Final success message
    messages.append({
        'target': 'ProgressUpdate',
        'arguments': [100, 'All tables inserted successfully.']
    })

    # Note: SignalR output binding in Python sends messages when the function completes.
    # We therefore batch all progress updates in a single payload.
    signalRMessages.set(json.dumps(messages))
    logging.info('InsertTables timer completed and progress sent to SignalR.')

How It Works

  • The broadcast function fetches the latest star count from GitHub and sends it to all connected SignalR clients every minute.
  • The insert_tables_timer function simulates a long-running operation (inserting tables) and sends progress updates to SignalR clients in real time.
  • All SignalR messages are sent when the function completes, so progress is batched and delivered together.

Next Steps

  • Deploy the function app to Azure.
  • Configure the Azure SignalR Service and connection string.
  • Build a client (e.g., web app) to receive and display real-time updates.

This approach is ideal for scenarios where you need to inform users about the progress of background tasks, such as data imports or batch processing, using serverless technologies and real-time messaging.

OutPut:
Run Function App: func host start
Open this url: http://localhost:7071/api/index


----------------------------------------------------------------------

index.html
<html>

<body>
  <h1>Azure SignalR Serverless Sample</h1>
  <div id="messages"></div>
  <button id="startProgress">Start Progress</button>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/3.1.7
    /signalr.min.js"></script>
  <script>
    let messages = document.querySelector('#messages');
    const apiBaseUrl = window.location.origin;
   
    // Generate a new GUID when page loads
    function generateGUID() {
      return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
      });
    }
   
    const sessionGuid = generateGUID();
    console.log('Generated GUID:', sessionGuid);
   
    const connection = new signalR.HubConnectionBuilder()
        .withUrl(apiBaseUrl + '/api')
        .configureLogging(signalR.LogLevel.Information)
        .build();
      connection.on('newMessage', (message) => {
        document.getElementById("messages").innerHTML = message;
      });

      // Display progress updates using the generated GUID
      connection.on(sessionGuid, (percent, message) => {
        const div = document.createElement('div');
        div.textContent = `[Manual] ${percent}% - ${message}`;
        div.style.color = 'blue';
        messages.appendChild(div);
      });

      // Display timer progress updates (broadcasted to all clients)
      // connection.on('TimerProgress', (percent, message, sessionId) => {
      //   const div = document.createElement('div');
      //   div.textContent = `[Auto] ${percent}% - ${message}`;
      //   div.style.color = 'green';
      //   messages.appendChild(div);
      // });

      connection.start()
        .then(() => {
          console.log('SignalR connection established');
         
          // Add event listener for the start progress button
          document.getElementById('startProgress').addEventListener('click', () => {
            // Clear previous messages
            messages.innerHTML = '';
           
            // Start the progress
            fetch(apiBaseUrl + '/api/startProgress', {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json',
              },
              body: JSON.stringify({ guid: sessionGuid })
            }).catch(console.error);
          });
        })
        .catch(console.error);
  </script>
</body>

</html>


function_app.py
import azure.functions as func
import logging
import azure.functions as func
import os
import requests
import json
import time

app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)

etag = ''
start_count = 0

@app.route(route="http_trigger")
def http_trigger(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

    if name:
        return func.HttpResponse(f"Hello, {name}.
            This HTTP triggered function executed successfully.")
    else:
        return func.HttpResponse(
             "This HTTP triggered function executed successfully. Pass a name in the
    query string or in the request body for a personalized response.",
             status_code=200
        )
   

@app.route(route="index", auth_level=func.AuthLevel.ANONYMOUS)
def index(req: func.HttpRequest) -> func.HttpResponse:
    f = open(os.path.dirname(os.path.realpath(__file__)) + '/content/index.html')
    return func.HttpResponse(f.read(), mimetype='text/html')


@app.route(route="negotiate", auth_level=func.AuthLevel.ANONYMOUS, methods=["POST"])
@app.generic_input_binding(arg_name="connectionInfo", type="signalRConnectionInfo",
    hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
def negotiate(req: func.HttpRequest, connectionInfo) -> func.HttpResponse:
    return func.HttpResponse(connectionInfo)


@app.route(route="startProgress", auth_level=func.AuthLevel.ANONYMOUS,
    methods=["POST"])
@app.generic_output_binding(arg_name="signalRMessages", type="signalR",
    hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
def start_progress(req: func.HttpRequest, signalRMessages: func.Out[str]) ->
    func.HttpResponse:
    try:
        req_body = req.get_json()
        if req_body and req_body.get('guid'):
            client_guid = req_body.get('guid')
            logging.info(f'Starting progress for client GUID: {client_guid}')
           
            total_tables = 10
            messages = []

            for i in range(1, total_tables + 1):
                # Simulate time-consuming insert
                time.sleep(1)

                percent = (i * 100) // total_tables
                msg = f"Table {i} of {total_tables} inserted. {client_guid}"
                messages.append({
                    'target': client_guid,
                    'arguments': [percent, msg]
                })

            # Final success message
            messages.append({
                'target': client_guid,
                'arguments': [100, 'All tables inserted successfully.']
            })

            signalRMessages.set(json.dumps(messages))
            return func.HttpResponse(f"Progress started for GUID: {client_guid}",
    status_code=200)
        else:
            return func.HttpResponse("No GUID provided", status_code=400)
    except Exception as e:
        logging.error(f'Error starting progress: {str(e)}')
        return func.HttpResponse("Error starting progress", status_code=500)


# @app.timer_trigger(schedule="*/10 * * * * *", arg_name="myTimer",
run_on_startup=False, use_monitor=False)
# @app.generic_output_binding(arg_name="signalRMessages1", type="signalR",
hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
# def broadcast(myTimer: func.TimerRequest, signalRMessages1: func.Out[str]) -> None:
#     global etag
#     global start_count
#     headers = {'User-Agent': 'serverless', 'If-None-Match': etag}
#     res = requests.get('https://api.github.com/repos/azure
/azure-functions-python-worker', headers=headers)
#     if res.headers.get('ETag'):
#         etag = res.headers.get('ETag')

#     if res.status_code == 200:
#         jres = res.json()
#         start_count = jres['stargazers_count']

#     signalRMessages1.set(json.dumps({
#         'target': 'newMessage',
#         'arguments': [ 'Current star count of https://api.github.com/repos
/azure/azure-functions-python-worker is: ' + str(start_count) ]
#     }))


# # Timer trigger: simulate inserting 10 tables and emit progress updates to SignalR
# # This version broadcasts to all clients - each client can choose to display or
ignore
# @app.timer_trigger(schedule="*/15 * * * * *", arg_name="tablesTimer",
run_on_startup=False, use_monitor=False)
# @app.generic_output_binding(arg_name="signalRMessages2", type="signalR",
hubName="serverless", connectionStringSetting="AzureSignalRConnectionString")
# def insert_tables_timer(tablesTimer: func.TimerRequest, signalRMessages2:
func.Out[str]) -> None:
#     logging.info('InsertTables timer started.')

#     # Generate a unique session ID for this timer run
#     import uuid
#     timer_session_id = str(uuid.uuid4())
   
#     total_tables = 10
#     messages = []

#     for i in range(1, total_tables + 1):
#         # Simulate time-consuming insert
#         time.sleep(1)

#         percent = (i * 100) // total_tables
#         msg = f"Timer Session: Table {i} of {total_tables} inserted."
#         messages.append({
#             'target': 'TimerProgress',
#             'arguments': [percent, msg, timer_session_id]
#         })

#     # Final success message
#     messages.append({
#         'target': 'TimerProgress',
#         'arguments': [100, 'Timer Session: All tables inserted successfully.',
timer_session_id]
#     })

#     signalRMessages2.set(json.dumps(messages))
#     logging.info(f'InsertTables timer completed. Session ID: {timer_session_id}')



OutPut:
Run Function App: func host start
Open this url: http://localhost:7071/api/index




Featured Post

Building Secure APIs with FastAPI and Azure AD Authentication

Building Secure APIs with FastAPI and Azure AD Authentication Published on September 2, 2025 In today's world of microservices and API-f...

Popular posts