Back to Blog
AzureLogic AppsContainer AppsEvent-DrivenSecurity

Master Blueprint: Event-Driven Automation with Logic Apps & Container App Jobs

8 min read
Master Blueprint: Event-Driven Automation with Logic Apps & Container App Jobs

A step-by-step guide for building an event-driven security scanner. We'll build and test each component before moving to the next.

The Architecture

[Logic App] → [Service Bus Queue] → [Container App Job]
  (trigger)      (message queue)        (worker)

How it works:

  • 1.Logic App runs on a schedule and sends a message to Service Bus
  • 2.KEDA (built into Container Apps) polls the queue and sees a message
  • 3.KEDA spins up a Container App Job instance to process it
  • 4.Job completes, container shuts down - you only pay when work is happening

  • Step 1: Create Service Bus & Logic App

    First, we set up the message queue and the trigger.

    1.1 Create the Service Bus

  • 1.In Azure Portal, search Service Bus → Create a Namespace
  • 2.Go into your namespace → Queues → Create a queue named scan-queue
  • 3.Go to Shared access policiesRootManageSharedAccessKey → Copy the Primary Connection String (save this for later)
  • 1.2 Create the Logic App & Workflow

  • 1.Search Logic Apps → Create a Logic App (Consumption)
  • 2.Once created, go to Logic App Designer
  • 3.Add trigger: Recurrence (e.g., every day at 2 AM)
  • 4.Add action: Service Bus - Send message
  • - First time: it asks for a connection - paste your connection string from step 1.1

    - Queue name: scan-queue

    - Content: {"target": "https://example.com", "scanType": "full"}

  • 5.Save the workflow
  • 1.3 Test It

  • 1.Click Run TriggerRun
  • 2.Go to your Service Bus namespace → Queuesscan-queueService Bus Explorer
  • 3.Click Peek from start - you should see your message
  • Message in the queue? Step 1 complete.


    Step 2: Build the Worker & Push to ACR

    Now we create the container that processes messages.

    2.1 Create Azure Container Registry

  • 1.In Azure Portal, search Container registries → Create one
  • 2.Once created, go to Access keys → Enable Admin user
  • 3.Note down the Login server, Username, and Password
  • 2.2 The Worker Code (Node.js)

    Create a file index.js:

    const { ServiceBusClient } = require("@azure/service-bus");
    
    async function main() {
        const sbClient = new ServiceBusClient(process.env.SB_CONNECTION);
        const receiver = sbClient.createReceiver("scan-queue", {
            receiveMode: "peekLock"
        });
    
        const messages = await receiver.receiveMessages(1, { maxWaitTimeInMs: 5000 });
    
        if (messages.length > 0) {
            const message = messages[0];
            console.log("Processing:", message.body);
    
            // Start a lock renewal loop for long-running jobs
            const lockRenewalInterval = setInterval(async () => {
                try {
                    await receiver.renewMessageLock(message);
                    console.log("Lock renewed");
                } catch (err) {
                    console.error("Lock renewal failed:", err);
                }
            }, 60000); // Renew every 60 seconds
    
            try {
                // Your scan logic here
                await runSecurityScan(message.body.target);
    
                clearInterval(lockRenewalInterval);
                await receiver.completeMessage(message);
                console.log("Done - message completed");
            } catch (err) {
                clearInterval(lockRenewalInterval);
                await receiver.abandonMessage(message);
                console.error("Failed - message abandoned:", err);
            }
        } else {
            console.log("No messages in queue");
        }
    
        await sbClient.close();
    }
    
    main();

    2.3 Create package.json

    {
      "name": "security-scanner",
      "version": "1.0.0",
      "main": "index.js",
      "dependencies": {
        "@azure/service-bus": "^7.9.0"
      }
    }

    2.4 Create Dockerfile

    FROM node:18-alpine
    WORKDIR /app
    COPY package*.json ./
    RUN npm install
    COPY . .
    CMD ["node", "index.js"]

    2.5 Build & Push to ACR

    # Login to your registry
    az acr login --name [acr-name]
    
    # Build and push
    docker build -t [acr-name].azurecr.io/security-scanner:v1 .
    docker push [acr-name].azurecr.io/security-scanner:v1

    Step 3: Create the Container App Job

    Now we create the job that KEDA will trigger when messages arrive.

    3.1 Create Container Apps Environment

  • 1.Search Container Apps Environments → Create one
  • 2.Pick your resource group and region → Create
  • 3.2 Create the Job

  • 1.Search Container App Jobs → Create
  • 2.Select your environment from 3.1
  • 3.Job type: Event-driven
  • 4.Image source: Azure Container Registry
  • - Registry: select yours

    - Image: security-scanner

    - Tag: v1

    - Authentication: Admin credentials

    3.3 Configure Event Trigger & Secrets (during creation)

    In the Event trigger section:

  • Replica timeout: 1800 (30 mins, adjust based on your scan time)
  • Polling interval: 60 seconds
  • Min executions: 0
  • Max executions: 10
  • Scale rule:

  • Name: service-bus-trigger
  • Type: Azure Service Bus Queue
  • Queue name: scan-queue
  • Message count: 1
  • Authentication: Connection string
  • Connection string value: paste your Service Bus connection string
  • 3.4 Add Environment Variable

    In the Container section → Environment variables:

  • Name: SB_CONNECTION
  • Source: Manual entry
  • Value: paste your Service Bus connection string
  • Create the job.


    Step 4: Test the Pipeline

    4.1 First Test

  • 1.Run your Logic App again (to put a fresh message in the queue)
  • 2.Go to your Container App Job → Execution history
  • 3.Within 60 seconds (polling interval), you should see a new execution
  • 4.Click on it → Console logs to see your worker output
  • 4.2 What to Check in Logs

  • "Processing: {...}" - job received the message
  • "Lock renewed" - lock renewal is working (for long jobs)
  • "Done - message completed" - success, message removed from queue
  • If you see these, your event-driven pipeline is working.


    Summary

    What we built:

  • 1.Service Bus - holds messages in a queue
  • 2.Logic App - sends scan requests on a schedule
  • 3.Container App Job - KEDA polls the queue, spins up a container when messages arrive, shuts down when done
  • You only pay for compute while scans are running.

    For production: Switch to Managed Identity instead of connection strings (no credentials to manage).

    AJ

    Aziz Jarrar

    Full Stack Engineer

    Share this article