Blog Post

Microsoft Developer Community Blog
3 MIN READ

Azure Tips and Tricks - How to run Logic Apps in a Docker container

manoj-srinivas's avatar
Jun 23, 2022

How to run Logic Apps in a Docker container

 

 

Azure Logic Apps can run anywhere

 

You can run Azure Logic Apps anywhere, on the Azure Functions Runtime. This enables you to deploy and run Logic Apps on your local machine, in your own datacenter, or in another cloud. And you can also run Azure Logic Apps in a Docker container.

In this post, we'll run an Azure Logic App in a Docker container.

 

Prerequisites

 

If you want to follow along, you'll need the following:

 

Deploy a Logic App to a container

 

The Logic App is triggered by an HTTP trigger and creates a queue message in Azure Storage. Let's deploy the Logic App to a container.

  1. In VS Code, create a Dockerfile in the project of the Logic App. The file has no file extension, and is just called Dockerfile. It should have the following contents:

 

 

FROM mcr.microsoft.com/azure-functions/dotnet:3.0.14492-appservice

ENV AzureWebJobsStorage=<Your Azure Storage connection string>
ENV AZURE_FUNCTIONS_ENVIRONMENT Development
ENV AzureWebJobsScriptRoot=/home/site/wwwroot
ENV AzureFunctionsJobHost__Logging__Console__IsEnabled=true
ENV FUNCTIONS_V2_COMPATIBILITY_MODE=true

COPY ./bin/release/netcoreapp3.1/publish/ /home/site/wwwroot

 

 

 

Fill in your Azure Storage connection string for the AzureWebJobsStorage environment variable so that the Logic App in the container can use it.

 

Dockerfile in VS Code

2. Next, open the Terminal in VS Code and build and publish the project with these commands:

 

 

dotnet build -c release
dotnet publish -c release

 

 

 

  1. Now create the container image with this command:

 

 

docker build --tag local/workflowcontainer .

 

 

 

  1. Run the container with the following command. This makes the Logic App accessible through localhost:8080

 

 

docker run -e WEBSITE_HOSTNAME=localhost -p 8080:80 local/workflowcontainer

 

 

 

  1. Now for the most difficult part. We need to discover the URL for the HTTP trigger. To do that, we first need to get the masterKey to gain access to the Logic App. You can find this key in the Azure Storage Account that you've indicated in the Dockerfile. Go to the Azure Storage Account and navigate to Containers/azure-webjobs-secrets/{id} and open the host.json file.

Host.json file in Azure Storage

 

 

  "masterKey": {
    "name": "master",
    "value": "l6vZj8J3aLEZzOfTV7SiiP2H2eru96ajlzZNpoXm5WScABAoP1tlEg==",
    "encrypted": false
  }

 

 

 

Copy the value of the masterKey. We'll need that in the next step.

  1. Open Postman
  2. Create a new POST request for http://localhost:8080/runtime/webhooks/workflow/api/management/workflows/{your logic app workflow name}/triggers/manual/listCallbackUrl?api-version=2020-05-01-preview&code={masterKey value} and send it

Postman results

The results from the post contain the URL of the HTTP trigger to call and the query parameters to add to the URL.

  1. Open a browser and trigger the Logic App with a URL like this: http://localhost:8080/api/{your logic app workflow name}/triggers/manual/invoke?api-version=2020-05-01-preview&sp={value for sp}&sv={value for sv}&sig={value for sig}&myqueuetext

The final querystring is a text, which the Logic App takes and puts in a new queue message in Azure Storage

 

That's it! The Logic App is now triggered and should run successfully and place a message on the Azure Storage queue, like in the image below:

 

The Logic App created a queue message in the Azure Storage queue

Conclusion

Azure Logic Apps enable you to easily create complex workflows. Now, you can run Azure Logic Apps anywhere, including in a Docker container. Create a trial account today and go and check it out!

 

Updated Aug 16, 2022
Version 2.0
  • J-Cat's avatar
    J-Cat
    Copper Contributor

    We are running Logic Apps in Docker containers in our Kubernetes environment (using the NodeJS version, doing development in VS Code), and it works great; has full access to your on-premise resources, if you have an on-premise Kubernetes environment this is a no-cost (or minimal) approach to deploying your Logic Apps.

     

    I created the following shell script that I use inside the Docker container to inspect the worfklow runs/logs (generates a menu based system using Linux "dialog" and JQ for JSON formatting/parsing).  This is the one thing that I was struggling with was inspecting the logs of the runs when they failed, and this 100% solves it as you can use it in a console window in the Kubernetes Dashboards in your browser:

    #!/bin/bash
    
    # while-menu-dialog: a menu driven system information program
    
    DIALOG_CANCEL=1
    DIALOG_ESC=255
    HEIGHT=0
    WIDTH=0
    
    POD_NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
    KUBE_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
    APICODE=$(curl -sSk -H "Authorization: Bearer $KUBE_TOKEN" "https://kubernetes.default:443/api/v1/namespaces/$POD_NAMESPACE/$AzureWebJobsKubernetesSecretName" | jq -cr .data.\"host.master\" | base64 --decode)
    
    WORKFLOWS=($(curl -s "http://localhost/runtime/webhooks/workflow/api/management/workflows?code=$APICODE" | jq -r '.[]|.name'))
    
    workflowRunActionMenu() {
        clear
        ACTION_PROPERTIES=$(curl -s "http://localhost/runtime/webhooks/workflow/api/management/workflows/$1/runs/$2/actions/$3?code=$APICODE")
        ACTION_INPUTURL=$(echo "$ACTION_PROPERTIES" | jq -r .properties.inputsLink.uri | sed 's/https:\/\/localhost\:443/http:\/\/localhost/')
        ACTION_OUTPUTURL=$(echo "$ACTION_PROPERTIES" | jq -r .properties.outputsLink.uri | sed 's/https:\/\/localhost\:443/http:\/\/localhost/')
        ACTION_MENU_ITEMS=("P" "Properties")
        ACTION_COUNT=1
        if [ "$ACTION_INPUTURL" != "null" ]; then
            ACTION_MENU_ITEMS+=("I" "Action_Input")
            ACTION_COUNT=$(($ACTION_COUNT+1))
        fi
        if [ "$ACTION_OUTPUTURL" != "null" ]; then
            ACTION_MENU_ITEMS+=("O" "Action_Output")
            ACTION_COUNT=$(($ACTION_COUNT+1))
        fi
        while true; do
            exec 3>&1
            ACTION_MENU_ID=$(dialog --backtitle "Logic App Workflow Log Viewer" --menu "Actions ($2)" $HEIGHT $WIDTH $ACTION_COUNT ${ACTION_MENU_ITEMS[@]} 2>&1 1>&3)
            exit_status=$?
            exec 3>&-
    
            case $exit_status in
            $DIALOG_CANCEL | $DIALOG_ESC)
                clear
                return
                ;;
            esac
    
            case $ACTION_MENU_ID in
            P)
                echo "$ACTION_PROPERTIES" | jq -C . | less -R
                ;;
            I)
                curl -s $ACTION_INPUTURL | jq -C . | less -R
                ;;
            O)
                curl -s $ACTION_OUTPUTURL | jq -C . | less -R
                ;;
            esac
        done
    }
    
    workflowRunActionsMenu() {
        clear
        RUN_ACTIONS=($(curl -s "http://localhost/runtime/webhooks/workflow/api/management/workflows/$1/runs/$2/actions?code=$APICODE" | jq -r '.value|sort_by(.properties.startTime)|map(.name,"\(.properties.startTime),(\(.properties.status))")|.[]'))
    
        while true; do
            exec 3>&1
            ACTION_NAME=$(dialog --backtitle "Logic App Workflow Log Viewer" --menu "Actions ($2)" $HEIGHT $WIDTH $(((${#RUN_ACTIONS[@]} + 1) / 2)) ${RUN_ACTIONS[@]} 2>&1 1>&3)
            exit_status=$?
            exec 3>&-
    
            case $exit_status in
            $DIALOG_CANCEL | $DIALOG_ESC)
                clear
                return
                ;;
            esac
    
            workflowRunActionMenu $1 $2 $ACTION_NAME
        done
    }
    
    workflowRunMenu() {
        clear
        RUN_MENU_OPTIONS=("P" "Properties/JSON")
    
        WFPROPERTIES=$(curl -s "http://localhost/runtime/webhooks/workflow/api/management/workflows/$1/runs/$2?code=$APICODE")
        WFINPUTURL=$(echo "$WFPROPERTIES" | jq -r .properties.trigger.inputsLink.uri | sed 's/https:\/\/localhost\:443/http:\/\/localhost/')
        WFOUTPUTURL=$(echo "$WFPROPERTIES" | jq -r .properties.trigger.outputsLink.uri | sed 's/https:\/\/localhost\:443/http:\/\/localhost/')
        WFRESPONSEURL=$(echo "$WFPROPERTIES" | jq -r .properties.response.outputsLink.uri | sed 's/https:\/\/localhost\:443/http:\/\/localhost/')
    
        WFCOUNT=2
        if [ "$WFINPUTURL" != "null" ]; then
             RUN_MENU_OPTIONS+=("I" "Trigger_Input")
             WFCOUNT=$(($WFCOUNT+1))
        fi
        if [ "$WFOUTPUTURL" != "null" ]; then
             RUN_MENU_OPTIONS+=("O" "Trigger_Output")
             WFCOUNT=$(($WFCOUNT+1))
        fi
        if [ "$WFRESPONSEURL" != "null" ]; then
             RUN_MENU_OPTIONS+=("R" "Response")
             WFCOUNT=$(($WFCOUNT+1))
        fi
        RUN_MENU_OPTIONS+=("A" "Actions")
    
        while true; do
            exec 3>&1
            RUN_MENU_ACTION=$(dialog --backtitle "Logic App Workflow Log Viewer" --menu "Run $2" $HEIGHT $WIDTH $WFCOUNT ${RUN_MENU_OPTIONS[@]} 2>&1 1>&3)
            exit_status=$?
            exec 3>&-
    
            case $exit_status in
            $DIALOG_CANCEL | $DIALOG_ESC)
                clear
                return
                ;;
            esac
    
            case $RUN_MENU_ACTION in
            P)
                echo "$WFPROPERTIES" | jq -C . | less -R
                ;;
            I)
                curl -s $WFINPUTURL | jq -C . | less -R
                ;;
            O)
                curl -s $WFOUTPUTURL | jq -C . | less -R
                ;;
            R)
                curl -s $WFRESPONSEURL | jq -C . | less -R
                ;;
            A)
                workflowRunActionsMenu $1 $2
                ;;
            esac
        done
    }
    
    workflowMenu() {
        clear
        WORKFLOW_RUNS=($(curl -s "http://localhost/runtime/webhooks/workflow/api/management/workflows/${WORKFLOWS[($1 - 1)]}/runs?code=$APICODE" | jq -r '.value|map(.name,"\(.properties.startTime),(\(.properties.status))")|.[]'))
    
        while true; do
            exec 3>&1
            RUNID=$(dialog --backtitle "Logic App Workflow Log Viewer" --menu "${WORKFLOWS[($1 - 1)]}" $HEIGHT $WIDTH $(((${#WORKFLOW_RUNS[@]} + 1) / 2)) ${WORKFLOW_RUNS[@]} 2>&1 1>&3)
            exit_status=$?
            exec 3>&-
    
            case $exit_status in
            $DIALOG_CANCEL | $DIALOG_ESC)
                clear
                return
                ;;
            esac
    
            workflowRunMenu ${WORKFLOWS[($1 - 1)]} $RUNID
        done
    }
    
    while true; do
        exec 3>&1
        selection=$(dialog --cancel-label "Exit" --backtitle "Logic App Workflow Log Viewer" --menu "Workflows" $HEIGHT $WIDTH ${#WORKFLOWS[@]} $( (for ((i = 0; i < ${#WORKFLOWS[@]}; i++)); do echo -n "$(($i + 1)) ${WORKFLOWS[$i]} "; done)) 2>&1 1>&3)
        exit_status=$?
        exec 3>&-
    
        case $exit_status in
        $DIALOG_CANCEL)
            clear
            echo "Program terminated."
            exit
            ;;
        $DIALOG_ESC)
            clear
            echo "Program aborted." >&2
            exit 1
            ;;
        esac
    
        workflowMenu $selection
    done

     

  • subhankars's avatar
    subhankars
    Copper Contributor

    This is a great post. Just wondering what could be some potential usecases to run logic apps in a Docker container as opposed to running it natively? Will we be able to see the workflow in the workflow editor/ designer? Any limitations with respect to using all available connectors? Are we saying we can now deploy a whole (complex in nature with connectivity with SAP ERP for example) workflow in a container (if so then it’s a game changer)? How the pricing would differ in these kind of scenario?

  • Stan Spotts's avatar
    Stan Spotts
    Copper Contributor

    It would be interesting to dig into the security/hardening aspects of this approach, too. And yes, pricing/cost comparison with native use wou;d be very interesting.