robconery.com

A Simpler Way to Azure

November 19, 2021 |
I have loved working with Heroku for years and have long wished Azure had a similar offering. Many people have tried this, here's my effort.
***

TL;DR: I made a fun CLI ā€œwrapperā€ for working with web apps and Azure in a very Heroku way. Itā€™s called ā€œAZXā€ and you can read a full walkthrough and installation instructions from the GitHub repo.

One of my jobs at Microsoft is to help product teams understand how developers work in other ecosystems. In other words: places other than the .NET/Azure world. Thatā€™s a pretty big place, so Iā€™ve decided to stay focused on an experience I love: Heroku.

Iā€™ve used Heroku for years and years and love the simplicity. You can do similar things nowadays with AWS, DigitalOcean, Netlify and yes, even Azure. The whole idea of git push deployment is pretty standard - in fact itā€™s kind of passĆ©.

Either way, the experience that Heroku pioneered is still compelling. Once you install the CLI and login you simply:

  • Create a project locally with heroku create
  • Work on your app and when youā€™re ready
  • Deploy your app using Git: git push heroku main

Thatā€™s it. Heroku will ā€œguessā€ what stack it needs to use, provision a set of free resources for it, and before you know it youā€™re up and running.

Scaling With You

Of course applications are much more complex than that. Youā€™d likely want a database, a cache of some kind (like Redis), more capable logging and so on. Youā€™re also going to quickly outgrow the free tier - and this, to me, is whatā€™s super groovy about Heroku: itā€™s there with you for all of this.

When you want to scale things up you can use heroku ps:scale web=1 from your local dev directory and boom, youā€™re scaled. To add better logging you simply heroku addons:create papertrail.

Adding a database, such as PostgreSQL (becauseā€¦ why wouldnā€™t you?) you need to look up the plan name you want and then heroku addons:create heroku-postgresql:<PLAN_NAME> --version=12.

The fun part is that everything is configured and wired for you on the back end - thereā€™s nothing you need to do aside from make sure you use ā€œconventionalā€ environment variables (like DATABASE_URL, REDIS_URL etc).

You can access all of these resources locally with the heroku tool. For instance, to read your logs you can heroku addons:open papertrail and there are your app logs!
Cool service - and it got me wondering how difficult would it be to do this with Azure?

A Rite of Passage

I told Burke Holland my idea and he started laughing and quipped:

I think thatā€™s a rite of passage here at Microsoft - everyone wants to create a Heroku clone!

Fair enough! Challenge accepted.

To be honest I never thought this would see the light of day but my fellow friends in Cloud Advocacy pushed me to open it up, so here goes.

A Localized Azure Experience

The idea with this project is straightforward: letā€™s bring Azure to you instead of you to Azure. The Azure CLI is outstanding but, if Iā€™m honest, itā€™s aimed at IT people and not developers. The good news is that, with a little love and imagination, you can leverage the CLI and have a little magic happen. Letā€™s seeā€¦

I have a local Python application, the Tailwind Starter site that Iā€™ve been working on and I want to get it up and live on Azure. Iā€™ve installed azx using NPM:

    npm install -g @robconery/azx

I then head into my project directory and run azx:

    cd ~/Tailwind
    azx

This will display the help screen:

AZX is seeing that thereā€™s no local settings file, which would be saved in .azure so itā€™s telling me what choices I have at this point and offering me a tip. My goal with this is to decrease the cognitive load that comes with something as complex as Azure (or any cloud service).

Here, we have 3 choices:

  • init which creates our project
  • get which will load an existing project (more on that in a minute) and
  • lookup which provides supporting information like regions, runtime names and SKUs.

Thatā€™s it - and I love the sparseness of it. Letā€™s keep rolling by creating our project.

Creating an Azure Project

An ā€œAzure projectā€ is something I made up - itā€™s not an Azure term. To create one, we just need to run azx init:

Running azx init does a couple of things, most notably:

  • It creates a ā€œResource Groupā€ on Azure for us, which is a virtual ā€œbucketā€ for all the Azure stuff weā€™ll be creating for this project.
  • It decides a name for us because naming is hard. You can override this if you want, but I like Herokuā€™s way of doing this - it keeps things simple.
  • Our local project settings directory, .azure is created and in it is a JSON file with our project settings. Weā€™ll see that in a minute.

Notice also that weā€™re given a tip about what to do next: azx app create. That wasnā€™t a choice before, but it is now:

Another example of keeping the cognitive load to a minimum - it just doesnā€™t make sense to show a command choice if you canā€™t run that command, which we couldnā€™t before. Now that we have a project, we can setup our application on Azure.

Setting Up Our App Service

Each subcommand, just like the Azure CLI, has a help screen dedicated to it. If we run azx app create --``help weā€™ll see that we have only one command available to us: azx app create <runtime>. Once again, this is by design.

Following our tip, we can now create setup where our app is going to live on Azure. If we donā€™t know the runtime choice to enter we can ask the CLI by using azx lookup runtimes and weā€™ll see a list. In my case I need azx app create python because Iā€™m using Django:

A bunch of goodness happened here:

  • An App Service ā€œPlanā€ was created and youā€™re told that this is basically a VM. It tried to create a free one as a logical first step but couldnā€™t because my account is limited to only 1 free (F1) App Service. You donā€™t care about any of that - so AZX just set it up for you. All of this can be overridden by specifying --sku.
  • The location of the App Service, something you normally need to specify, was set to West US by default. You can override that too - but if youā€™re kicking the tires on Azure ā€¦ why think about this stuff?
  • A web application, on Azure, was created with local Git deployment - meaning you can git push just like with Heroku. This can be changed, easily, later on.
  • Comprehensive logging was set up, which is not turned on by default.
  • Deployment credentials were created for you using a random name and a GUID for a password. These credentials were added to your local Git repository under the azure remote.

Thatā€™s a lot of stuff you would have needed to understand before deploying your application. Itā€™s a good idea to understand it at some point, but often if youā€™re coming to a service for the first time, itā€™s kind of nice if you donā€™t have to fiddle with every knob. In our case, a lot of this stuff is basic (like logging) and can just be turned on.
Notice that weā€™re given another tip - how to set up our database. We didnā€™t have an azx db choice before, but now we do because we have an application!

Setting Up the Database

We have three choices (as of now) for databases: PostgreSQL (the correct choice), MySQL or Mongo DB (which is actually Cosmos DB using the Mongo DB driver). As you might expect, my database will be PostgreSQL.

To set that up, we can follow the tip that you see there at the top of the help screen. Notice that, just above the tip, we can see the basic description of our application. We see the name (cold-dust-76) and also that itā€™s a web application.

Letā€™s push on and create the database using azx db create postgres. If we wanted to know more about our choices we could use azx db create --``help and see that the only choice we have is to create a database with the specified engine.
Off we goā€¦

This takes a few minutes to run and youā€™re told that straight off. On average, provisioning a PostgreSQL server takes about 3-4 minutes. Cosmos DB takes a bit longer - up to 6 minutes - but the nice thing is what comes next: all the minutiae is handled for you. You can, if you want, get up and go for a walk or write in your bullet journal - this time is given back to you!

So, what happened here? A lot of things that would normally take you 20 clicks in the portal, or more:

  • A PostgreSQL server was created for you with a conventional name that you can override (a ā€œ-dbā€ was added to the end of your project name).
  • A firewall rule was added so your web app can see your database.
  • Another firewall rule was added using your local IP address so that you could see your database.
  • Admin credentials were created for you, which is the way (in my mind) it should be. Once again, a semi-randomized name (like admin-451) was used with a GUID password. These credentials were then used to construct access credentials for your application (a DATABASE_URL) and those credentials were added to your Azure web app configuration settings for you.
  • Those same credentials were added to a local .env file inside of the .azure directory.

We can now access our database using azx db connect:

Note: this only works if you have the PostgreSQL binaries installed locally.
From here we can, once again, follow the tip and set up our application database by sending in a SQL file. Given that weā€™re working with the local client binaries, we can redirect STDIN in the same way we might with psql:

Weā€™re ready to go! At this point we have about 7 total minutes elapsed. But what do we do now?

One of my main goals with this tool is to have it help you as much as possible, without doing too much or overloading you with concepts. The only thing you need to remember is to run azx when you donā€™t know what to do next:

Things have changed once again, and this is because we have a database to go with our application. We have a few tips at the top - the first of which is telling us how we can deploy our application using Git. Weā€™ll talk about scaling and other things in just a minute.

Deployment

We have Git deployment set up and an azure remote has been added for us with our deployment credentials embedded in the remote URL, all thatā€™s left to do is git push azure master (or main):

Off we go. What you see here is Oryx, the build tool used by Azureā€™s App Services. Itā€™s received our code and, using a post-receive hook, is setting things up for us. In this case itā€™s a Python environment but this also works with Node and Rails, etc.

It takes a minute - for Django it takes about 4 minutes for a full deployment to happen. This is due to the project settings and packages being setup completely on the first run.
After a few minutes weā€™re done and, hopefully, ready to go!

Ready to goā€¦ where exactly? What do we do now? Hopefully at this point you get the idea: ask AZX:

Our app menu has a bunch of new choices. We can change configuration settings on Azure, scale things, look at the logs (which weā€™ll do in a minute) and finally open it! Thatā€™s what I want to doā€¦

Note: if youā€™re running this inside WSL youā€™ll get an error due to access permissions to Windows executables. If that happens, you can use *azx app get_settings* to see your appā€™s URL.

It takes a few seconds to load up on first run, but here we go!

Weā€™re live and our site is happily pulling data from our database. We have a problem, however, in that our images arenā€™t showing up for some reason. Letā€™s troubleshoot.

Troubleshooting and Moving On

This CLI has gone through a few iterations and I showed it once to executive types at Microsoft, which went pretty well, but there was one very important bit of feedback:

Helping people use Azure is great, but we need to be sure we support them into the future and not back them into the corner

Makes perfect sense. To that end, I added as much as I could to help you out as you work with your application, specifically:

  • The ability to scale your App Service up or down.
  • The ability to scale your Database Server up or down.
  • Rotating your database password (which also updates your web app).
  • Viewing your application logs.

That last is what we need now because our images arenā€™t showing up. Letā€™s see what happened using azx app logs:

Doing this will tail the logs so you can see whatā€™s happening realtime and, digging in, I shortly find out that I didnā€™t set my STATIC_ROOT properly so my images arenā€™t showing. I need to have that set for productionā€¦ but how?

Letā€™s take a look at our web app configuration settings using azx app get_settings:

What we need to do is update these settings with a STATIC_URL that points to a CDN somewhere that stores our images. How do we write these settings? Letā€™s ask AZX!

As I mentioned (briefly) before, a .env file containing all of our application secrets lives inside of our .azure directory. We can update that file and then save it up to Azure. Hereā€™s what it looks like:

All of these settings were created when our database was created and, yes, thereā€™s a .gitignore file that was created as well to ensure that we donā€™t commit this to source control. All we need to do now is to add a setting in here for STATIC_ROOT:

Now we just need to use azx app write_settings:

Heading to the Azure portal to confirm:

Great! We just saved ourselves another 10 or so clicks, including restarting our App Service so these changes are picked up.

Thereā€™s More To Do!

Iā€™ve had tons of fun putting this project together. It was supposed to be a quick prototype but I figured it could also help someone too. There are a few more things Iā€™d like to get done, including caching, deployment slots and more.

The main thing is that you can get to a solid point with your application and then jump over to the regular CLI or to the portalā€¦ the hard part being behind you.

Join over 15,000 programmers just like you and me

I have a problem when it comes to trying new things and learning about computer science stuff. I'm self-taught, so it's imperative I keep up with what's going on. I love sharing, so sign up and I'll send along what I've learned right to your inbox.