Cloud deployment becomes more and more popular. I’ve been curious about it for a few years now, but have very few hands-on experience. After a first integration of some Azure services in my graduation project, about one year ago, and passing the Microsoft Azure Developer Associate certification, I wanted to give it a concreter try.
I’ve explained the application structure I would like to put in place in a first introductory article about this Cloud project. My goal is to try to deploy the same application on different Cloud providers (Azure, AWS, GCP and OCI) and compare the difficulty, costs, time spent… This article is my first shot with Azure. I started with Azure because that’s the Cloud provider I know the most about.
Also, I used an application that was not specifically developed for Cloud deployment, because otherwise it wouldn’t be fun, would it?
- The effort and the time
- Deploying the backend API
- Deploying the database server
- Deploying the frontend web application
- Securing the communication between backend and frontend apps with APIM (API Management)
- Deploying a message queue service and functions
- Where I failed
- The cost
- Azure ARM templates
The effort and the time
I may have a good theoretical overview of the Microsoft Azure services offer, but it was my first time actually creating app and services, configuring and deploying them. I had already noticed that the documentation by Microsoft Azure was well done only when it’s to their advantage, this experience reinforced that feeling. To make it clear: they give a great documentation to explain how to use the services they want you to use…
At the same time, I really feel a difference between the amount of documentation and answers you may find about open-source/free solutions, and proprietary/billable ones. It’s way easier to get community help and knowledge sharing about deployment on an Ubuntu server than on Azure. This can probably also be explained by the age of both technologies, Cloud as a service still being quite new and less widely used.
In this post, I’m documenting how it went during my first attempt, the time spent, effort, documentation troubles… per service. Just keep in mind that it was my first attempt (including looking for solutions, trying stuff, failing…), that a lot of things can be automated in a professional environment with heavier workloads, and that I’m still a junior-ish dev. Also, there would be a lot more to do for a production-size app. But in the end, it will still allow to compare the various providers based on a similar deployment.
For this attempt, I’ve spent about 14 hours deploying the whole solution, including time spent reading documentation, searching for solution, debugging, starting the wrong way and having to start again…
Deploying the backend API
Azure App service is ready to use with Java, among other SDKs like node.js, C#, Ruby… I made the mistake to start with the backend instead of with the database, which made me waste a little time. Even with that, the deployment (creation of the service, clone of the code, build, failure to deploy because of the wrong database connection string, changes in the code to set the new database connection string, deployment, and including the time waiting for builds and deployments…) of the backend took less than one hour.
- Service used: Microsoft Azure App Service
- Total time: 45 minutes
- Quality of documentation: pretty decent as the service is really easy to use, but the fact that I’ve been developing in Java for a few years did influence on my ease to do it…
- Difficulty of the operation: Easy Peazy
Deploying the database server
The deployment of the database wasn’t as easy. Fun fact (or is it?) is that I faced exactly the same problem when I integrated a Postgres database in my graduation work, about a year ago… Lucky enough, I kept my notes from that time (and the student I collaborated with on that project also did). Anyway, the fact that it caused so much pain twice makes me think that something’s wrong either with the process or with the documentation from Azure…
So, basically, creating the database server (including parameterizing and deployment) takes 10 minutes. Then I struggled to create the database itself, because that’s not something you can do directly from the Azure Portal (at least for non-proprietary databases…) and there’s no mention about the how-to in the documentation of Azure. After digging into the pages of StackOverflow I kept from last time, and into our notes, I managed to find my way. Now that I’ve saved everything, it should take minutes if I have to do it again. I’ve shared the information just below in case you face the same issue.
- Service used: Microsoft Azure SQL server for PostgreSQL
- Total time: 1 hour, including 45 minutes looking for the right way of working and commands…
- Quality of documentation: Meh-ish
- Difficulty of the operation: Easy if you know how to do it, headachy when you have to figure it out
How to create your database on the Azure database for PostgreSQL server
From your local Windows computer, you can connect in command lines to the master database on the server (that’s called “postgres”). You’ll find the connection information to your database server on the Azure portal. You have to make sure you allowed public connections to the server, and installed the SSL certificate on your machine.
psql “host=YOURSERVERNAME.postgres.database.azure.com port=5432 dbname=postgres user=YOURUSERNAME password=YOURUSERPASSWORD sslmode=require”
From there, you can create you database(s) as you would usually do. You may also connect to the server with a GUI like PGadmin.
To import data (restore a dump) from your local database to the database on Azure, you have to make a pg_dump of the local database, then:
psql.exe -U YOURUSERNAME -h YOURSERVERNAME.postgres.database.azure.com -p 5432 -d YOURTARGETDATABASEONTHESERVER -f YOUR\LOCAL\PATH\TO\DUMP\FILE\YOURLOCALDUMPFILE.sql
- How to backup and restore PostgreSQL databases in Windows 7? (StackOverflow)
- Restore a Postgre backup file using the command line? (StackOverflow)
Deploying the frontend web application
I expected the deployment of the Angular frontend app to be as easy as the deployment of the backend app. To deploy an app with Java or node.js, I just figured out Azure is a plain remote server on which you have to clone your code, build and run your app. But when I tried installing Angular, it just didn’t work so easily.
I couldn’t find a straightforward tutorial in the Azure documentation. After some research on blogs, I found two options: deploying with GitHub actions, which was going out of the scope I wanted to explore for this test, or deploying via Visual Studio Code and the Azure App service plugin. I chose the second option, which looked promising: the connection to Azure was smooth, so was the launch of the deployment of my code to the previously created app service. Everything was great until it got stuck on “Generating ES5 bundles…” message in the logs for 15 minutes. The wheel was still turning, the status was still “deploying”, but I couldn’t see anything happen. I finally tried to stop the deployment and restart from the beginning, because I had no clue what just happened, I could only see that the app wasn’t deployed on Azure.
After 1 hour 15 minutes I had created my service but still hadn’t been able to deploy my Angular app. And I was facing the issue I already had when I first integrated Azure services in an app: WHERE ARE THE EFFING LOGS? Azure adds an extra layer of abstraction that’s supposed to ease your tasks, but when your case doesn’t work perfectly as described in the tutorial (does it, ever?), you have to spend time finding logs where you can. That means, digging into the logging and monitoring system of Azure and gathering information from various places. I mean, they made a deployment plugin for Visual Studio Code, why wouldn’t they just show the error logs directly in VSC at failure?
As Ed Sheeran would sing it, My, my, my, my, oh give me looooogs, my, my, my, my…
After a few attempts at reproducing the same steps with minor changes, I got an error message, something like “request to (app variables)VS%20Code failed, reason: read ECONNRESET“. I tried elaborated solutions (like this and this) to solve the issue, first with Visual Studio Code. Note to self and to you: disable auto-update on Visual Studio Code, always. Downgrading to version 1.65.2 indeed took me one step further, just enough to encounter the next issue (so, in fact, one bug further…). Still without logs.
I was clueless, I decided to give a try to the automated deployment from GitHub Actions. It was out of my initial scope, but it was also the only way of deploying Angular on Azure that was more or less referenced by Microsoft. I faced a problem during karma tests automatically ran by GitHub. Again, I could see something failed in the logs but GitHub seemed like it was keeping up with the job.
After almost half an hour I cancelled the workflow, investigated the error (“Chrome failed 2 times (cannot start). Giving up.”), found information about karma tests failing during GitHub Actions build, pushed the changes (see below, in karma.conf.js), relaunched the workflow…
And I could finally see real application logs! And I discovered that, for some reason, GitHub Actions had created a deployment manifest to deploy with Docker, which was none of my intent.
I could have changed the manifest, but as I was unhappy feeling forced to use the GitHub Actions method, I went back to my research to manage the deployment from Visual Studio Code. Reading this tutorial, I found out the automatically generated deployment file from the Azure Web App plugin for Visual Studio Code (both products of Microsoft…) doesn’t auto-generate a correct path for the “package” parameter in the settings.json file.
I got a new error, read that tutorial, and caught that sentence:
Having no other idea, I tried creating a Windows Web App service instead of a Linux one. Then, finally, in less than 5 minutes from creation to deployment, my frontend app was live!
So, great lesson learned: when you build your Angular app, it creates files that are dependent of your OS, so you have to deploy on the same OS as the one used to make your build.
I didn’t have that problem with the Java app, because Java runs everywhere.
Just had to change the CORS policy in my backend app to the URL of my newly deployed frontend app, restart the service (it takes less than a second), and tadaaa! So much easier when you know what to do and how…
- Service used: Microsoft Azure App Service
- Total time: 3 to 4 hours including searching for the solution and trying fixes to deploy directly on the remote server or from Visual Studio Code + 1h30 trying the GitHub Actions solution and debugging + 30 minutes actually debugging things I understood with real clues + 5 minutes for actual deployment once everything was set up
- Quality of documentation: There is just none about Angular in Azure official doc
- Difficulty of the operation: Might make you want to change job the first time you try it
Securing the communication between backend and frontend apps with APIM (API Management)
One of the tools I was most excited about is APIM, because it’s said to offer a solution to handle many security issues without code. Among others, it should allow you to easily set authentication and authorization to the backend endpoints. The security layer is still one of my weakest points and, although I’m a fan of Java and Spring Boot, I still hate the Spring Security API as I don’t find it friendly to use.
I spent about an hour trying to create an APIM service: first directly through my backend app, where it went super fast but I couldn’t make it work, then via the APIM service menu, but the deployment was taking so long (15 minutes seemed pretty long to me) that I thought the service crashed, and finally, third time’s the charm, I created it again via the APIM service and let it deploy during about 30 minutes (it was not bugged, it’s just actually pretty long). I managed to curl the API via the APIM url. It then took me 5 minutes to deny all accesses to my backend app except through the APIM (the Azure documentation was pretty simple and it worked at first try).
At that stage, the frontend app couldn’t communicate directly with the backend anymore, but that was exactly the point.
I wanted the frontend app to communicate with the backend through the APIM, so I changed the backend URL to use the one from the APIM (the backend app wasn’t reachable otherwise now anyway). But the point was to add some security in here. First step would be to use the “subscription key”, a key that’s necessary to make request to the APIM. Naïve me thought it would be easy to retrieve a secret from the Key Vault, on my Azure subscription, and use it in my frontend app, on my Azure subscription, in order to dynamically call my API with the necessary headers…
Naïve of me indeed, because it seems no such thing exists, and if securing the backend app behind an APIM is pretty easy without even changing the code, the frontend-to-API part is far from being codeless. And again, it’s not documented as is, you have to dig in the doc in hope to find anything that would put you on the right path, read blog articles from 3 years ago when the Azure interface was completely different, and so on. In the end, I didn’t find a satisfying solution for the frontend part, I had to use the key in the header in the code, which is a disgusting and unsecure solution.
My goal at first was to use Azure AD, but as I was already stuck on the supposed-to-be-easy solution, and running out of time before the end of my trial period, I didn’t give it a try.
- Service used: Microsoft Azure API Management service
- Total time: about 2 hours to create and deploy the APIM and have it route requests to the backend app, about 2 hours to search and try solutions to establish the communication between frontend and API (but with a far-from-ideal solution)
- Quality of documentation: Decent
- Difficulty of the operation: Pretty easy for the backend/APIM, a failure for the front-to-API communication
Deploying a message queue service and functions
I decided to add a message queue service to the whole thing. It just didn’t appear to me that I would have to code an app to send the messages to the queue and wouldn’t be able to just send messages from my local Postman*… so I came with a slightly changed structure for my app: a function receives the incoming http calls, sends the content to a message queue storage, that triggers another function to make the http call to my app.
As is, it doesn’t serve any concrete purpose, that’s purely for practice. It would make sense if the data needed transformation or validation (use of a function), or for batch executions or high number of concurrent or async requests (use of a queue), for example. Those things can also be done programmatically, but using Azure services allows not to modify an existing code base to add those behaviors.
Creating the queue storage was simple and fast, as it’s just a storage type inside your storage account. Creating the functions seemed pretty simple on paper (I mean, in the Azure documentation), but I faced a few surprises, like the fact that there is no actual “HTTP output binding” (it only exists to answer an HTTP triggered function).
In the end, including the JS struggles, it took me 2 hours to create the queue storage and the two functions and manage to make an end-to-end flow: my http-triggered function was callable from anywhere (like, with Postman on my local machine), it created a new message in the queue, which itself triggered another function which would make an http request to my backend app, via the APIM.
- Services used: Queue storage and Function app
- Total time: 10 minutes to create the queue storage (including the time to remember in which service it was) and a little less than 2 hours to create the 2 functions and manage to make them work end-to-end.
- Quality of documentation: Queue storage is the most basic queuing service of Azure, so documentation is not necessary for creation and parameterization. About functions, meh… it seems really clear on paper, in the facts I had to pick a lot of pieces of information in a lot of documentations, and search for more on StackOverflow.
- Difficulty of the operation: Pretty easy still.
* well, after all I found some documentation and it seems it’s possible to post messages with a http request, but I was already done with my solution and the direct calls from Postman didn’t seem to work anyway…
Where I failed
I was really hoping to find in Azure an easy solution to implement security between services. I thought it would be easy to handle secrets and keys and manage accesses of services to each other. In the facts, it only seems to be the case with APIM (which is an expensive service). I couldn’t find possibilities to directly connect other services with Azure KeyVault for example.
If I had to implement a solid security layer between my applications and services, I think I could easily multiply the time spent by 2. The problem is a mix of incomplete or inconsistant documentation, and my limited knowledge in security and infrastructure. As a conclusion, it’s thus interesting to note that deploying an app on Azure is not just a point-and-click matter, it still requires some skills and knowledge (might seem silly to write that, but I often feel like cloud providers make it sound like anyone can do anything because it’s soooo easy with cloud services).
It’s important to take into account that I chose development* options most of the time (because I had a limited budget allowed by Azure in their try-for-free offer and I had no idea how much I would spend for my tests), and that my application wasn’t used by tens, hundreds or thousands of users and all services were not running 24/7. In production environments, higher costs are to be expected. As you can see below, the difference is pretty big.
* development tier is a cheaper tier for development/test environments, thus cheaper (sometimes free) than production environments, but without SLAs, with limited options and so on
My current solution has a cost of approximately €42 / month, but the forecast is about €264 / month (forecast is based on a projection, I guess it takes into account the typical usage for this kind of solution). For my current trial usage, out of €42.02, €41.85 are linked to the usage of APIM! The remaining cents are from Azure App Service.
Azure ARM templates
The ARM templates of the implemented solution are available on my GiHub account for reference.