Deploy Azure Functions with Maven

Azure Functions is a serverless compute service provided by Microsoft Azure, designed to help developers run event-driven code without the need to manage infrastructure. Whether you’re automating workflows, building APIs, or processing data streams, Azure Functions offers a scalable, cost-effective solution. By abstracting away the complexities of server management, it allows developers to focus on writing the logic that drives their applications.

For Java developers, Azure Functions offers first-class support, making it easier than ever to integrate Java-based solutions into the Azure ecosystem. This support includes a robust set of tools and libraries specifically designed to streamline the development and deployment process for Java applications. If you’re eager to learn more, Microsoft has prepared a decent documentation covering this topic. You’ll find there information about the programming framework, how to implement a Function, and how to deploy it. In this post I’d like to take a look at the latter and present it to you in a slightly different light than usual.

Traditionally, deploying Azure Functions involves tools like Azure CLI, Bicep, or Terraform. These tools are robust and widely used, offering powerful features for managing Azure resources. However, Java developers also have a more seamless and better integrated option to choose from – Maven.

Maven, a build automation tool primarily used for Java projects, can be an excellent choice for deploying Azure Functions. The Azure Functions Maven Plugin allows developers to define their infrastructure as code, simplifying the deployment process and ensuring consistency across environments. It’s a great choice if you’re starting your journey with Azure Functions or have no other infrastructure in the cloud. It’s easy to use and allows you to keep your infrastructure definition as close to your build definition as possible.

The Azure Functions Maven Plugin allows you to use an infrastructure-as-code approach similar to Bicep or Terraform. You can declare the desired infrastructure, and the plugin handles provisioning and state management. This means your Function App won’t be reprovisioned every time you build your application, saving time and resources. While this might seem abstract, seeing it in action will clarify its efficiency and ease of use. Let’s dive into the practical aspects of deploying your Azure Functions with Maven and see how this approach can streamline your development process.

I’m using Maven v3.9.6 in this tutorial. You can find a working example of this configuration here.

First, let’s open the pom.xml file in the azure-funcitons-sdk module. Within this file, you’ll find the declaration of the azure-functions-maven-plugin. At the core of this declaration is the <plugin> element. In Maven, a plugin is a collection of goals, which are specific tasks or actions. In our case, we’re using the azure-functions-maven-plugin to facilitate the deployment of Azure Functions. Let’s take a look at the definition. 

<plugin>
    <groupId>com.microsoft.azure</groupId>
    <artifactId>azure-functions-maven-plugin</artifactId>
    <version>${azure.functions.maven.plugin.version}</version>
    <configuration>
        <appName>azure-functions-sdk</appName>
        <resourceGroup>azure-functions-sdk-rg</resourceGroup>
        <appServicePlanName>azure-functions-sdk-sp</appServicePlanName>
        <pricingTier>Consumption</pricingTier>
        <region>norwayeast</region>
        <runtime>
            <os>linux</os>
            <javaVersion>17</javaVersion>
        </runtime>
        <appSettings>
            <property>
                <name>FUNCTIONS_EXTENSION_VERSION</name>
                <value>~3</value>
            </property>
        </appSettings>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>package</goal>
            </goals>
        </execution>
    </executions>
</plugin>

There’s a lot of things you might be unfamiliar with in this declaration, so let’s crack them one by one.

Plugin Coordinates

(This is pretty standard, but let’s quickly go through them, especially if you’re new to Maven.)

  • <groupId>: Specifies the group identifier for the plugin. In this case, it’s com.microsoft.azure.
  • <artifactId>: Identifies the plugin itself. We’re using the azure-functions-maven-plugin.
  • <version>: Specifies the version of the plugin. The ${azure.functions.maven.plugin.version} variable points to a specific version, such as 1.24.0 in our example.

Configuration Section

  • <appName>: The name of your Azure Functions application, set to azure-functions-sdk.
  • <resourceGroup>: Defines the Azure resource group where your functions will be deployed, here named azure-functions-sdk-rg.
  • <appServicePlanName>: The Azure App Service Plan where your functions will run, set to azure-functions-sdk-sp.
  • <pricingTier>: Specifies the pricing tier for your functions. It’s set to Consumption, since it’s the cheapest and most popular option. You can also choose Premium or Dedicated tier here.
  • <region>: Determines the Azure region for deployment, set to norwayeast in our case. Note that free trial subscriptions may have limitations on available regions. Check Azure’s documentation for your specific options.
  • <runtime>: Configures the runtime settings:
    • <os>: Specifies the operating system, which is linux.
    • <javaVersion>: Sets the Java version used for the functions runtime, set to 17.
  • <appSettings>: Defines application settings specific to your functions. They’re populated as environment variables to your Function runtime. Here we only specify the Functions version itself, but you can put active profiles or other environment-dependent key-value pairs here.

Executions Section

  • <execution>: Defines when and how the plugin’s goals should be executed. The package goal is responsible for packaging your Azure Functions for deployment. Note that this phase does not deploy anything to Azure; it merely prepares the package. The actual deployment requires invoking a plugin-specific goal, which we will cover later.


Great! Now that we have our configuration in place, the next step is to run the build and deploy our Azure Functions. First, we need to prepare the artefact by executing the clean package command. You can do this from the right-hand side menu in IntelliJ IDEA. This step takes a few seconds to complete on my machine.

Once the packaging is done, the next step is to execute a plugin-specific goal: deploy. It’s essential to use the correct deployment goal to ensure our code gets deployed to Azure. By default, Maven’s deploy goal sends artefacts to the artefact repository specified in the <distributionManagement> section of your POM file, such as Nexus. However, we want to deploy our code directly to Azure. To do this, we need to specify the deployment goal from the Azure Functions Maven Plugin.

Before proceeding, ensure you have Azure CLI installed on your machine. The installation steps vary depending on your operating system, and you can find detailed instructions in the Azure CLI installation guide. I used Homebrew to install it on my machine, but you can choose any way that suits you best.

Once you have Azure CLI installed, open the terminal and type az login. This command initiates a session from your local machine to Azure’s REST API, which is necessary for the plugin to function correctly.

Now, let’s start the deployment process. Issue the following command from the directory where the POM file is located:

mvn azure-functions:deploy

As the deployment progresses, you’ll see logs indicating that all the necessary infrastructure is being provisioned, including the Resource Group, Function App, Application Insights, Storage Account, and App Service Plan. Once the deployment is complete, you can navigate to the Azure Portal to verify that everything is correctly set up and running. This is a great feature itself, because using generic tools like Bicep forces you to define related resources independently. Moreover, all necessary secrets (like connection string to the Storage Account) are added to your App Settings.

And that’s it! By following these steps, you have successfully deployed your Azure Functions using Maven. It may feel a bit odd in the beginning, especially if you’re already experienced with Bicep or Terraform, but I find this approach quite useful in scenarios when I don’t have a lot of infrastructure in Azure, or it’s provided by an external team. In this case an ability to define my Function App in the POM file and use the same tool for build, test, and deployment contributes to a great developer experience. Moreover, automatic creation of all related resources and configuration allows me to get the function up and running in a couple of minutes, making the Maven-based deployment a great option for learning and prototyping.