Maintaining Application Settings for an Azure Functions Project

Before you get too excited by the title of this post, it’s not good news. Yet. I’ve been looking for a decent workflow to solve this problem and am turning up blanks, which means there’s only one thing left: I’ll have to define one.

Right off the bat, there seems to be a lot of confusion at the moment around application settings in Azure Functions, mostly due to the rate of change in the Azure ecosystem. Even more so because the configuration model for Azure Functions is handled rather differently to App Service applications. I haven’t dug deep enough in to the architecture to understand why, but am guessing there’s a fairly fundamental reason for this.

For clarification:

  • appsettings.json is not used for deployed functions. This was only ever used for local development and has since been renamed to local.settings.json
  • web.config is not used. Refer to the folder structure for a Functions application
  • host.json can not be used to store application settings

The problem I’m trying to solve here is to find a clean way of managing an Azure Functions app’s application settings over time. For an App Service application, you can simply add an entry to appsettings.json, configure any overrides for your deployment environments and off you go. With Functions there’s no such luck. On day one, you can create a neat little ARM template to handle your infrastructure setup, including app settings, but what happens when you want to add one later? I’ve seen a few different solutions which basically all come down to hacks.

As a last resort, tonight I did a final search in the Azure Functions GitHub repository tickets and found a ticket detailing the issue that I assume many people are having. Well by the looks of it we’ll have an answer tomorrow at 20:00 CEST! Here’s a direct link to the live stream.

Update: From the webcast, the bottom line is that there are plans to enhance the process, but for now keep using the CLI. Great.

Deploy an ASP.Net Core 2.0 Application to an Azure App Service With Bitbucket Pipelines

Recently I took another look at Bitbucket’s Pipelines offering, their successor to Bamboo cloud which was removed last year (but lives on as an on-premise solution). Pipelines had previously been uninteresting to me as there was no support for traditional .NET apps, but now that I’m dealing more with .NET Core, I figured it would be worth a look. As it turns out, I abandoned this approach almost as soon as I had things up and running, choosing to go with the built in support offered by Azure App Services. A guide on that will appear soon.

The premise is cool, builds are run in Docker containers, defined using YAML and you get 50 build minutes included in the free account, with all the triggers and notifications you’d expect, e.g. allowing you to fire off Slack notifications to keep your team informed of the build health. If your code is sitting in a Bitbucket repo, it makes sense to try and keep everything under one roof (always with an alternative solution of course, you never know when that roof will collapse!).

I started off by following the guide provided by Atlassian and the YAML from this repository which immediately resulted in errors. Getting the build definition in to shape took a bit of messing around so I decided to write this post in the hopes that a) you can laugh at the silly mistakes I made along the way and b) provide a more up to date and comprehensive guide to deploying an application to the Azure App Service using Bitbucket Pipelines.

Trials and Errors

Problem #1 – Builds not being triggered

Cause: Incorrect casing of the branch name in the build definition file, ‘Development’ vs. ‘development’ (who would name the development branch ‘Development’ anyway?!).

Solution: Correct the casing in the bitbucket-pipelines.yml file.

Problem #2 – Error building the solution

+ dotnet build $PROJECT_NAME
Couldn't find 'project.json' in 'PROJECT_NAME'

Cause: This was the result of project.json being a dotnet core 1.0 feature which is now deprecated in version 2.0.

Solution: As this is a dotnet core 2.0 project, I needed to use a different Docker image, and changed the Docker image from

image: microsoft/dotnet:onbuild

to

image: microsoft/dotnet:2.0-sdk

Problem #3 – Error restoring Nuget packages

  + dotnet restore
  MSBUILD : error MSB1003: Specify a project or solution file. The current working directory does not contain a project or solution file.

Cause: It would appear that at some point between version 1 and 2, an error is raised instead of a warning when the restore command is unable to find a project/solution file. The repository was structured such that the solution was in a folder off the root directory.

Solution: Pass in the path to the solution file to the restore command. E.g.

- dotnet restore FolderWithSolution/MySolution.sln

Problem #4 – Error pushing to the Azure App Service Git repository

  + git push https://$AZURE_LOGIN:$AZURE_PASSWORD@appservicename.scm.azurewebsites.net/appservicename.git master
  error: src refspec master does not match any.
  error: failed to push some refs to 'https://username:$AZURE_PASSWORD@appservicename.scm.azurewebsites.net/appservicename.git'

This tripped me up as I hadn’t taken the time to process what the error was telling me and ended up on a bit of a wild goose chase, resulting in me cloning the App Services Git repository locally and creating a master branch. Note: Do not do this, jump to #6 to see why.

Cause: The build was being executed on the Development branch, but I was trying to push code to the App Services’ master branch.

Solution: Change the Git push command, specifying the source and destination branch names:

git push https://$AZURE_LOGIN:$AZURE_PASSWORD@appservicename.scm.azurewebsites.net/appservicename.git Development:master

Problem #5 – Error pushing to the Azure App Service Git repository, take two

+ git push https://$AZURE_LOGIN:$AZURE_PASSWORD@appservicename.scm.azurewebsites.net/appservicename.git Development:master
  fatal: unable to access 'https://username:$AZURE_PASSWORD@appservicename.scm.azurewebsites.net/appservicename.git/': Illegal port number

Well that’s weird! HTTPS is pretty standard after all no? Just for good measure I went and changed the Git push command again, adding the port in:

git push https://$AZURE_LOGIN:$AZURE_PASSWORD@appservicename.scm.azurewebsites.net:443/appservicename.git Development:master

Alas the error persisted.

Cause: Embarassingly, the value for the $AZURE_PASSWORD environment variable contained a colon. Oh dear, schoolboy error! That’s what you get for trying to generate a super secure password.

Solution: Change the Azure App Services deployment password such that it doesn’t contain any characters that will break the URL.

Problem #6 – Error pushing to the Azure App Service Git repository, take three

  + git push https://$AZURE_LOGIN:$AZURE_PASSWORD@appservicename.scm.azurewebsites.net:443/appservicename.git Development:master
  To https://appservicename.scm.azurewebsites.net:443/appservicename.git
   ! [rejected]        Development -> master (fetch first)
  error: failed to push some refs to 'https://username:$AZURE_PASSWORD@appservicename.scm.azurewebsites.net:443/appservicename.git'
  hint: Updates were rejected because the remote contains work that you do
  hint: not have locally. This is usually caused by another repository pushing
  hint: to the same ref. You may want to first integrate the remote changes
  hint: (e.g., 'git pull ...') before pushing again.
  hint: See the 'Note about fast-forwards' in 'git push --help' for details.

Cause: Sigh. In order to create the master branch in problem #4, I had to push a commit, which meant that the repository had changes that would need to be merged. Something felt very wrong here so I decided to reset the Azure App Service Git repository, which is easier said than done.

Solution: I ended up deleting and re-creating the App Serivce, using the same details as before so that I wouldn’t have to go through and update my build definition and environment variables.

Success

At last, I saw a nice green build indicator. Finally! I’d eaten through 13 of those precious 50 build minutes by this point, but it was working and the builds were relatively quick, taking around 3 minutes apiece. Here’s what the bitbucket-pipelines.yml looked like at this point:

image: microsoft/dotnet:2.0-sdk

pipelines:
  branches:
    Development:
      - step:
          caches:
            - dotnetcore
          script:
            - dotnet restore SolutionName/SolutionName.sln
            - dotnet build SolutionName/ProjectName
            - git push https://$AZURE_LOGIN:$AZURE_PASSWORD@appservicename.scm.azurewebsites.net:443/appservicename.git Development:master

Once the code was being deployed, I hit the next issue. This repository contained a solution with two ASP.NET Core web apps and I couldn’t easily figure out how to specify which project to deploy. Deploying the entire build output would always result in the first website being active. At this point, due to other checkins, all of those 50 build minutes were used up! Towards the end, when building two projects, the builds were hitting 9 minutes. Rather than pony up the $10 for another 1000 minutes, I took my search elsewhere.

The Recipe for Success

If you want to give Bitbucket Pipelines a spin and would like to avoid my mistakes, follow this guide. These steps assume three things:

  1. You already have your app code sitting in a Bitbucket repository.
  2. You have an active Azure subscription and have already created your App Service.
  3. Your .NET Core solution has a single project you wish to deploy.

This guide will bypass the Bitbucket Wizard for adding the bitbucket-pipelines.yml file to your repository as it only allows you to commit this directly to your master branch. If that’s ok with you, then follow the wizard (just access the Pipelines for your repository and follow the steps), however the steps below will result in the same outcome.

Please refer to the Atlassian documentation for the bitbucket-pipelines.yml file specification.

Note: Pipelines will only run for branches in which the bitbucket-pipelines.yml file exists and are defined in the build file (unless you only use a default configuration). Depending on your Git workflow, you may want to add this file to a development branch first, then propagate it to other branches by merging.

  1. Create a new text file, in the root of your repository on the desired branch, with the name bitbucket-pipelines.yml
  2. Paste in the following content:
    image: microsoft/dotnet:2.0-sdk
    
    pipelines:
      branches:
        BRANCH_NAME:
          - step:
              caches:
                - dotnetcore
              script:
                - dotnet restore SOLUTION_FOLDER/SOLUTION_NAME.sln
                - dotnet build SOLUTION_FOLDER/PROJECT_NAME
                - git push https://$AZURE_LOGIN:$AZURE_PASSWORD@APP_SERVICE_NAME.scm.azurewebsites.net:443/APP_SERVICE_NAME.git BRANCH_NAME:master
    
  3. Go through and replace the values for the following to match your setup:
    • BRANCH_NAME
    • SOLUTION_FOLDER
    • SOLUTION_NAME
    • PROJECT_NAME
    • APP_SERVICE_NAME
  4. If you don’t have multiple branches to deal with, you can omit the branches: and branch name (BRANCH_NAME:) lines, replacing them with default:
  5. Commit the file and push to your remote
  6. If you did everything right so far, you should be able to navigate to the Pipelines section of your Bitbucket repository, scroll down and see the contents of your bitbucket-pipelines.yml file in the validator. If there are any validation errors, correct them now
  7. Click the Enable button Enable Bitbucket Pipelines
  8. To define the username and password that will be used to push to your App Service’s Git repository, you have two options. Follow this guide to set those credentials.
  9. With the credentials in place, open the repository in Bitbucket and navigate to Settings -> Pipelines -> Environment Variables
  10. Add a new variable called AZURE_LOGIN and set the value to your deployment username. Mark this as secured if you like
  11. Add a second variable called AZURE_PASSWORD, setting the value to your deployment password. It’s recommended that you mark this as secured to prevent it appearing in logs
  12. Commit a change to the branch you added the bitbucket-pipelines.yml file to and you should be all set! If you hit any errors, check out the list of problems I ran in to and see if you’re experiencing the same. Otherwise, hit up Google, or ask a question in the comments below