Writing an Azure Function using Azure Storage, Azure DevOps and Java

Updated: Sep 5, 2020

In this tutorial we want create a REST service without hosting or configuring any classic server in a fully automated environment with the help of a CI/CD job. In this session we will need to work with 2 components: azure portal and Azure DevOps. The azure portal will provide us the REST service and in Azure DevOps we will use GIT and the YAML build pipeline and the included azure build agent.

Azure Portal

First we need to go to portal.azure.com to create our REST service. Therefore, we click in the search bar and type „function app“. Another way is to click on the three dashes in the top left corner and click on „all services“ and then click on the category „compute“.

Select our subscription, a resource group where the function app should be organized and a service name. The service name will be a DNS entry and can be considered as the entry point of our rest service URI. Next we choose that we want to publish code only and not a container (because containers are very costly and we’re cheap asses). For the example project we’ve chosen Java with the version 8 (well there is no other version, 11 LTS is not supported yet). Now we need to select the region what is next to our physical location and click on „next“.

In the next step we have to assign a storage account to our function app. That’s where our Java code will be deployed and persisted. We’ve chosen Linux as the operating system, since it has a smaller overhead. Since Java is used, it shouldn’t make a difference which OS we pick. In the final step of the hosting part we choose the serverless plan type. This is very important, because we don’t want to pay for an extra service plan. Let’s click „next“.

In the monitoring section, we decide to enable application insights. Since we are using a serverless architecture, we need to store our logs somewhere else. Application insights can be compared with the ELK-stack (elastic search, logstash, kibana) which distributes and stores the data and visualizes it. Good for us that application insights can do all of this for us and it’s free. Let’s skip the tagging part and click on „review and create“.

We should wait for the message „Your deployment is complete“ to be sure that nothing went wrong. If we can see this text with a green check mark, then we are good for now in the azure portal. To be 140% sure, we can go to the resource group and verify that the following 4 resources are available.

Code

In the next step we want to check out the prepared code, so we can fully focus on the CI/CD components and their configurations. The project consists of a Java application managed with Apache Maven. The most important files in this repository are the pom.xml, azure-pipelines.yml and Function.java.

https://dev.azure.com/kmorbe/random/_git/vingenerator

Let’s have a little peek in the class de.klemensmorbe.vingenerator.function.Function to see what our REST service will be doing. The entire function provides one single endpoint with a fixed path and one optional query parameter. A legal GET call might look like that: https://vingeneratorservice.azurewebsites.net/api/vingenerator?level=3

public class Function {

    /**
     * Generates a random VIN based on the given level.
     *
     * @param request Containing the vinLevel to be applied. (1-3 or one to three or ONE to THREE)
     * @param context Used for logging and stuff.
     * @return Returns a valid vin based on the VIN level.
     */
    @FunctionName("vingenerator")
    public HttpResponseMessage run(
            @HttpTrigger(name = "req", methods = {HttpMethod.GET}, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {

        context.getLogger().info("Java HTTP trigger processed a request.");


        // Parse query parameter
        final String query = request.getQueryParameters().get("level");

        final String vin = new VinGenerator().withLevel(query).generate();

        return request.createResponseBuilder(HttpStatus.OK).body(vin).build();
    }
} 

The service accepts zero or exactly one string parameter, the query parameter. A VIN consists of 17 characters, which can be described by this simplified regular expression [A-Z0-9]{17} (level 1). A real VIN has a more complex regex as the one provided, with a higher level (1 to 3), the VIN looks more and more like a real vehicle identification number. Omitting this parameter will result in a level 1 VIN pattern.

Azure DevOps

Now that we have the code, we need to configure the build pipeline. In the already checked out project, let’s inspect the pom.xml in the root directory. The most important part of the content is the plugins section, specially the azure-functions-maven-plugin, where we link our artifact with the azure portal resource.

<plugin>
	<groupId>com.microsoft.azure</groupId>
	<artifactId>azure-functions-maven-plugin</artifactId>
	<configuration>
		<appName>${artifactName}</appName>
		<resourceGroup>temp</resourceGroup>
		<appServicePlanName>ASP-temp-a9c3</appServicePlanName>
		<region>northeurope</region>
		<runtime>
			<os>linux</os>
		</runtime>
		<appSettings>
			<property>
				<name>FUNCTIONS_EXTENSION_VERSION</name>
				<value>~3</value>
			</property>
		</appSettings>
	</configuration>
	<executions>
		<execution>
			<id>package-functions</id>
			<goals>
				<goal>package</goal>
			</goals>
		</execution>
	</executions>
</plugin> 

As we maybe already realized, this plugin contains information about our service plan resource.

Now let’s have a look at the azure-pipelines.yml file, which is also located in the root directory of the project. The file consists of three steps, build the maven project, archive the artifact and push it to our storage account.

trigger:
  - master

pool:
  vmImage: 'ubuntu-latest'

variables:
  artifactName: 'vingeneratorfunction'
  appName: 'vingeneratorservice'

steps:
  - task: Maven@3
    inputs:
      mavenPomFile: 'pom.xml'
      goals: 'clean package'
      options: ''
      publishJUnitResults: true
      testResultsFiles: '**/surefire-reports/TEST-*.xml'
      javaHomeOption: 'JDKVersion'
      jdkVersionOption: '1.8'
      mavenVersionOption: 'Default'
      mavenOptions: '-Xmx3072m'
      mavenAuthenticateFeed: false
      effectivePomSkip: false
      sonarQubeRunAnalysis: false
  - task: ArchiveFiles@2
    inputs:
      rootFolderOrFile: 'target/azure-functions/$(artifactName)'
      includeRootFolder: false
      archiveType: 'zip'
      archiveFile: '$(Build.ArtifactStagingDirectory)/$(artifactName)$(Build.BuildId).zip'
      replaceExistingArchive: true
  - task: AzureFunctionApp@1
    inputs:
      azureSubscription: 'myazuresubscription'
      appType: 'functionAppLinux'
      appName: '$(appName)'
      package: '$(Build.ArtifactStagingDirectory)/$(artifactName)$(Build.BuildId).zip' 

If we look back to the azure portal, we can see in the storage account, that our archived artifact is there. We can can see more than one archive there, because we pushed the artifact successfully more than once.

Now we check out the app service (function). We navigate to the resource „vingeneratorservice“ and click on functions. We should see the vingenerator.

Navigation even further we get basic monitoring for our endpoint and a visualized view of the REST service.

If we are not sure what our concatenated endpoint URL is, we can find it out by clicking on overview and then get function URL.

As mentioned earlier, the URI is https://vingeneratorservice.azurewebsites.net/api/vingenerator?level=3, let’s click on it and see how it works. That’s it, we are done. Have fun with your first REST service using Azure Functions and Azure DevOps.

Logging

Since we configured the application insights already, we can now check for any log entries if desired. Navigate to the azure portal and click on the application insights resource. Click on search, type in „traces“ and press „run“. Now we can see that the backend talks a lot, but our applications logs too, in the first entries we should see the occurrence of „Java HTTP trigger processed a request.“ That’s another proof, that our application is running properly.


Recent Posts

See All