Can I Develop Az Functions on a Mac?

I started writing this blog post a very long time ago when I switched for the first time in my life to a Mac as my main computer. I was super excited to have the shiniest laptop in town and I was sure I could do all my work on it since most of what I did was .NET Core which is cross platform and front end development. So happy days!?!?!? Right? Not really!!!

I hit a massive road block, I found out that the tooling I needed to develop Azure Functions wasn't available for Mac and that was a deal breaker. Without the Storage Emulator, I could only run locally Azure Functions with HTTP triggers, and well within my solution that was the trigger I used the least.

I WAS SAD; I stopped using the Mac for Azure Functions development but life goes on and there's no looking back.

Time has passed and by accident I found out a community maintained open source storage emulator called Azurite. I was pumped and decided to give another chance for the Mac to be my main work machine again. So let's see how it goes.

First I installed Azurite by running sudo npm install -g azurite, once it was installed I kicked it off by simply running azurite-blob.

If you haven't installed the Azure Functions Core Tools yet, do follow the steps from the Microsoft docs.

I put together a super simple code just to prove a point and to be able to share here and to create a Function App I went to the terminal and ran:

mkdir FunctionsOnMac
cd FunctionsOnMac
func init --worker-runtime dotnet

I also created two functions: a timer trigger and service bus trigger as below:

func new -t TimerTrigger -n TimerTrigger
func new -t ServiceBusQueueTrigger -n ServiceBusTrigger

Then I updated my local.settings.json as below to use the storage emulator just as you would in a Windows machine using the official Storage Emulator:

For this example to work, it's required that you have an Azure Service Bus in place and a queue named temp. Also make sure you copy the service bus connection string and update the file below

{
    "IsEncrypted": false,
    "Values": {
        "AzureWebJobsStorage": "UseDevelopmentStorage=true",
        "AzureWebJobsDashboard": "UseDevelopmentStorage=true",
        "FUNCTIONS_WORKER_RUNTIME": "dotnet",
        "ServiceBusConnString": "[SERVICE BUSS CONN STRING]"
    }
}

The idea of my sample app is for the timer to send a message to the service bus every 10 seconds and the service bus function to pick up that message and print in the screen, as simple as that.

My timer function will look like this:

using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Microsoft.Azure.WebJobs.ServiceBus;

namespace FunctionsOnMac
{
    public static class TimerTrigger
    {
        [FunctionName(nameof(RunTimer))]
        public static void RunTimer(
            [TimerTrigger("*/10 * * * * *")]TimerInfo myTimer, ILogger log,
            [ServiceBus("temp",EntityType.Queue,
            Connection = "ServiceBusConnString")] 
            ICollector<string> messages)
        {
            log.LogInformation($"C# Timer trigger function executed");
            messages.Add($"Message sent from timer at {DateTime.Now}");
        }
    }
}

And my Service Bus function will look like the below:

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;

namespace FunctionsOnMac
{
    public static class ServiceBusTrigger
    {
        [FunctionName(nameof(RunServiceBus))]
        public static void RunServiceBus(
            [ServiceBusTrigger("temp", Connection = "ServiceBusConnString")]
            string message, 
            ILogger log){
                log.LogInformation($"Message Received: '{message}'");
        }
    }
}

Now when I go back to the terminal and run func start that's what I get:

Btw, if you do forget to run azurite-blob, you should be getting the below instead

Hoorey!!!! Happy days again!!! Let's wait for my next roadblock :D

Cheers