Kotlin and Azure Functions - Automating the deployment
Tags: kotlin, azurefunctions, deploy, azure
Published: 2024-03-16 10:09:45

Recently I needed to write an Azure Function app that uses the [Apache POI](https://poi.apache.org) library for getting the text from Microsoft Word 94 documents (and yes, I am fully aware that the year is currently 2024, but some people still have 30 year old documents kicking around!).

Being somewhat allergic to coding in Java (this is a personal thing, if you like Java then good for you) I decided to try out writing the code using [Kotlin](https://kotlinlang.org) from [JetBrains](https://www.jetbrains.com) instead. I'm already using [IntelliJ](https://www.jetbrains.com/idea/) as I work with Apache Spark using Scala, so the tooling was already there and ready to go for this.

Microsoft, unexpectedly, have an [article](https://learn.microsoft.com/azure/azure-functions/functions-create-maven-kotlin-intellij) about creating Azure Function apps using Kotlin, so this was a great start and, pretty quickly, I had a version of my function app ready to try out.

## Debugging

This isn't as straight forward as just running a debug configuration unfortunately, it would be nice if it was, but it's honestly not too bad either. The Microsoft article talks about how to do it, and once you get into the habit of running the app and then running a configuration to attach the debugger, it just starts to flow.

So, running locally I have an Azure Function app, written in Kotlin, using the Apache POI library, extracting text from a Wore 94 document (it kills me a little bit every time I write that part). So I opened up [HTTPie](https://httpie.io/desktop) and started firing sample documents at it

![IntelliJ running and debugging the code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9s7352nmahdfdlt09dvg.png)


![API being called from HTTPie](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ug65rahebvebfr1fcquc.png)

It works great, so next I want to deploy this thing, and that's where the smooth ride hits a few bumps.

## Deployment

In the article it tells you to just run the `azure-functions:deploy` Maven target. It somewhat skips over the fact that you should probably first open up the `pom.xml` file and do things like change the resource group name, function app name, and the app region. These might seem trivial, but if you have issues with data leaving certain regions then they become really important.

So, first up, change the `pom.xml` file, specifically these values in the `properties` section.

* functionAppName
* functionAppRegion
* functionResourceGroup

Having done that I tried to run the maven target again and... failed.

But it failed because it was complaining that it couldn't read the `azureProfile.json` or `accessTokens.json` files and that I should check that I'm logged in using the Azure CLI.

This is weird because I had just created the resource group and a few other things for my deployment using the CLI. But I logged in again and tried again and still the same error!

A bit of searching around the web and it turns out that the Azure CLI had been updated a while ago and changed how it was handling tokens, but the Maven Archtype hasn't been updated to reflect this! So, what next?

Well, most deployments to Azure Functions work by packaging up the function app, producing a zip, and then pushing this up. There are other ways, but this works well and is pretty robust. But it's all hidden away in the target, so how can we replicate what it does. We can do this fairly easily it turns out.

There are a few steps we want to follow.

1. Clean everything up
1. Package the app
1. Create the zip
1. Deploy the zip

Cleaning things up is easy, we can just run `mvn clean` and we're done.

Packaging we need to run a couple of things. First is the `mvn package` target to build our application, and then the next is the `mvn azure-functions:package` target which creates our directory structure that we need to be able to publish the app (this is the really important step).

Once both of these have been executed, if we look under the `target` directory we can see a new `azure-functions` directory which contains everything we need, all the `jar` files, the `host.json` file and more.


![Contents of the target directory after packaging](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lm60yw0nq072wb2vltk9.png)

Now, we're just creating a zip to deploy, so in the command line we move to the `target/azure-functions/<function app name>` folder and run the following.

```zsh
> zip -r ../../../deploy.zip *
```

That will create a `deploy.zip` file in the root of the source directory with all the contents of the packaged function app directory within it.

Then, we publish the zip to our function app (you need to create that first by the way, as a Java 8 function app) with the following command.

```zsh
> az functionapp deployment source config-zip -g <resource group name> -n <function app name> --src ./deploy.zip
```

And that's it, the function app is deployed. You can then grab the URL from the [Azure Portal](https://portal.azure.com) and change the URL in HTTPie, send the document, and get the response.


![HTTPie showing response from deployed application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xtytt5x6ptwdbw0716cb.png)

What I ended up doing was putting all of the steps into a Makefile so I could just run `make deploy` whenever I wanted to push a new version

```makefile
clean:
	mvn clean

package: clean
	mvn package
	mvn azure-functions:package

deploy: package
	cd ./target/azure-functions/<app name>; zip -r ../../../deploy.zip *
	az functionapp deployment source config-zip -g <resource group name> -n <func app name> --src ./deploy.zip
	rm deploy.zip
```

Putting in the dependencies meant that I only needed to execute the deploy step, instead of remembering to clean and package as well.

## Wrapping up

Pretty quickly I ended up with a function app deployed, written in Kotlin, which could read and extract text from Word 94 (sob sob sob) file. Other than working out the deployment process it went really smoothly and it runs incredibly well.

I'm probably not quite ready to give up C# and dotnet to use this for everything, but this is a nice option when a Java based solution is the better option.