An Ultimate Guide To The Azure Deployment Process

Photo of Adam Kielar

Adam Kielar

Updated Oct 17, 2023 • 17 min read
Woman sending text message on cellphone-1

As a software developer, you may often wonder how you can show your mobile or web application to the whole world.

There is no easy answer to how you can do that but in this article I will provide guidance and direction that you can follow to achieve this goal with Microsoft Azure cloud.

There are many solutions available on the market, so you can easily get lost when trying to find the best one. To be honest, the perfect solution does not exist – there are always pros and cons. In this guide, I focus on Azure cloud platform and services where you can host your existing applications or develop new ones.

What are the major Azure services ?

You need to ask yourself: “Do I need to manage the whole infrastructure or can I use the platform management solutions that Azure provides? Maybe a serverless framework would be enough?” Let’s investigate what Azure can offer.

Azure Virtual Machines (VMs)

Azure VMs are easy to set up and use, and they offer a number of advantages over traditional on-premises virtualization solutions. For example, Azure VMs can be provisioned quickly and easily, and they can be scaled up or down as needed to meet changing workload demands.

VMs also benefit from the Azure cloud environment and global infrastructure, which provides high availability and durability.

Azure VMs can be used for various purposes:

  • Building Infrastructure as a Service solution (IaaS)
  • Deploying your software to either Windows or Linux machines
  • Having total control over the configuration of a VM
  • Migrating an on-premises application, often without making many changes
  • You are responsible for maintenance, patching, updates, security, and more

Azure App Service

Azure App Service is a cloud computing service created by Microsoft for building, deploying, and scaling web applications. It is available in three tiers: Basic, Standard, and Premium. The first provides a basic set of features at a lower cost, while Standard and Premium provide more features and performance at a higher cost.

Azure App Service is typically used by developers who want to focus on coding rather than infrastructure. It provides:

  • Fully managed Platform as a Service (PaaS).
  • Support for multiple languages and frameworks.
  • Service automatically maintains and patches the system and language framework.
  • Scale your application up or out, either manually or automatically.
  • App Service can handle most common needs and is a good starting point.

Azure Functions

Azure Functions is a serverless Azure compute service that enables you to run code on-demand without having to explicitly provision or manage infrastructure. Azure Functions lets you write code in a variety of languages and also provides built-in bindings for Azure services, allowing your code to access storage, databases, messaging, and more:

  • Serverless solution.
  • Azure takes care of infrastructure and most management tasks.
  • On the consumption plan, you only pay while your function is running.
  • If your application can work in response to events from a customer or other cloud service or on a schedule, this service is worth your consideration.

Azure Static Web Apps

Azure Static Web Apps is an Azure PaaS offering that automates the build and deployment of statically generated apps and sites, making it easy to get up and running with minimal configuration. The service handles all the Azure infrastructure so you can focus on building your app:

  • Ideal for applications that include HTML, CSS, JavaScript and image assets.
  • Static assets are globally distributed.
  • Integrate your software with Azure Functions.
  • Supports popular frameworks such as React, Angular, and Vue.js, as well as static site generators such as Gatsby, Hugo, and Jekyll.

Azure Container Instances

Azure Container Instances (ACI) offers a simple way to create and manage containers on Azure. You can quickly launch containers from the Azure portal or Azure CLI, and Azure takes care of all the underlying infrastructure. ACI is ideal for situations where you need to deploy containers quickly and without hassle, such as development or testing environments.

  • Ideal for simple containerized applications that run in isolated environments.
  • Faster startup time compared to Virtual Machines.
  • Secure your instance by deploying it to a virtual network and expose it through a VPN gateway.

Azure Kubernetes Service

Azure Kubernetes Service (AKS) is a managed Azure service that makes it easy to deploy and manage a cluster of Azure VMs that are running the open-source container orchestration system, Kubernetes. Azure handles the heavy lifting of provisioning and managing the underlying infrastructure, so you can focus on deploying and managing your applications:

  • Hosted Kubernetes service
  • Ideal for applications based on microservice architecture
  • Scale and manage containerized software

Azure Container Apps

Azure Container Apps (ACA) provides a managed container runtime environment that is purpose-built for Azure cloud. Simply put, ACA provides all the benefits of containers while abstracting away many of the complexities involved in setting up and managing a containerized environment.

This makes ACA an ideal solution for those who want to take advantage of the benefits of containers without having to deal with the underlying infrastructure.

You can use Azure Container Services to deploy popular open source frameworks such as Apache Mesos, DC/OS, Kubernetes, or Docker Swarm.

  • Run containerized applications on a serverless platform
  • Azure takes care of infrastructure and orchestration of containers
  • Ideal for applications based on microservice architecture without access to the underlying Kubernetes APIs

Which Azure service is the best for me?

There are many things to consider when looking for the right resources for your application, but it is very important to not rush this step. Below you can see a decision tree that might help you during the process.

graph cloud deployment process

How do I deploy an application on Azure?

For the purpose of this guide, let me assume that the best option is to deploy software to Azure Container Apps. I want to create architecture as in the picture below.

graph example resource architecture on Azure

First I need to provision this resource before I can successfully deliver my application. There are five ways in which I can deploy Azure resources:

  • Azure Portal
  • Azure CLI
  • Azure PowerShell
  • Azure Resource Manager template
  • Azure Bicep

Let me describe two of them in more detail.

Azure CLI

This is a dedicated command-line interface (CLI) tool to connect to Azure. I can interact with Azure API by executing commands or by running my scripts. This is a fast and useful tool to quickly set up the resources I need. Let's see it in action below.

1. Install extension for Container Apps

Input:

az extension add --name containerapp --upgrade

Output:

The installed extension 'containerapp' is in preview.
2. Register Microsoft.App namespace

I can see above that Container Apps are currently in public preview as of August 31, 2022, so let's check if it’s registered in my subscription.

Check status:

az provider list \
--query "[?namespace=='Microsoft.App'].{Status:registrationState}" \
--output table

Output:

Status
-------------
NotRegistered

Register:

az provider register --namespace Microsoft.App

Check status:

az provider show -n Microsoft.App --query "{Status:registrationState}" --output table

Output:

Status
----------
Registered

3. Set environment variables that I am going to use in this tutorial
RESOURCE_GROUP="hello-world-rg"
LOCATION="westeurope"
CONTAINER_ENV="hello-world-env"
CONTAINER_APP="hello-world-app"
REGISTRY="acrhelloworld$RANDOM"

4. Create resource group
az group create --name $RESOURCE_GROUP --location=$LOCATION

5. Create private Container Registry
az acr create --name $REGISTRY --resource-group $RESOURCE_GROUP --sku Basic

6. Import image to Container Registry
az acr import --name $REGISTRY --source docker.io/library/nginx:1.23.1

I use an official Docker nginx image to test deployment:

I could, of course, use a custom image. I only have to create a Dockerfile and use the az acr build command to push this image to the container registry.

7. Create Container Apps environment
az containerapp env create \
--name $CONTAINER_ENV \
--resource-group $RESOURCE_GROUP \
--location $LOCATION

8. Deploy application

I use an image from a private container registry. I enable managed identity to give Container App access to ACR, while AcrPull role is assigned automatically:

az containerapp create \
--name $CONTAINER_APP \
--resource-group $RESOURCE_GROUP \
--environment $CONTAINER_ENV \
--image $REGISTRY.azurecr.io/library/nginx:1.23.1 \
--target-port 80 \
--ingress 'external' \
--registry-server $REGISTRY.azurecr.io \
--registry-identity 'system' \
--query properties.configuration.ingress.fqdn

Output: (it will be different for your application if you follow this tutorial in your subscription):

"hello-world-app.politegrass-4e04a625.westeurope.azurecontainerapps.io"

Check this address in a browser to confirm if the application is deployed successfully:

website screenshot - 'welcome to nginx!'

Azure Bicep

What if I want to provision your resources using infrastructure as a code solution? Azure has a dedicated tool for that. Mikołaj Maćkowiak introduces Azure Bicep concepts in his article, so I present only an example of a Bicep file that I can use to deploy simple infrastructure.

I divided the main.bicep file into logical parts for readability. As a prerequisite, I delete the previous resource group and create a new one:

"hello-world-app.politegrass-4e04a625.westeurope.azurecontainerapps.io"
1. Define parameters and variables

Since I deleted previous resources, I can use the same names for my resources:


// ========== //
// Parameters //
// ========== //
param location string = resourceGroup().location
param acrName string = 'acrhelloworld'
param envName string = 'hello-world-env'
param appName string = 'hello-world-app'
param logName string = 'hello-world-log'
param appIdentityName string = 'hello-world-id'
param scriptIdentityName string = 'hello-world-script-id'
param imageTag string = '1.23.1'
 
 
// ========== //
// Variables  //
// ========== //
var _resourceName =  uniqueString(resourceGroup().id)
2. Create Azure Container Registry

I will store my Docker images here:


// ============== //
// Azure Container Registry
// ============== //
 
resource demoACR 'Microsoft.ContainerRegistry/registries@2019-05-01' = {
 name: '${acrName}${_resourceName}'
 location: location
 sku: {
   name: 'Basic'
 }
 properties: {
   adminUserEnabled: false
 }
}
3. Upload Docker image to ACR

As in the Azure CLI example, I am using a Docker nginx image. To upload this image, I am going to use a

deployment script.

There is no Azure defined role to complete that task, so I have to create a custom role:


// ============== //
// Deployment Script User-assigned Identity
// ============== //
 
resource scriptIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
 name: scriptIdentityName
 location: location
}
 
// ============== //
// Create Custom Role for Deployment Script
// ============== //
 
var _azImportActions = [
 'Microsoft.ContainerRegistry/registries/push/write'
 'Microsoft.ContainerRegistry/registries/pull/read'
 'Microsoft.ContainerRegistry/registries/read'
 'Microsoft.ContainerRegistry/registries/importImage/action'
]
 
resource roleDefinition 'Microsoft.Authorization/roleDefinitions@2022-04-01' = {
 name: guid(subscription().id, string(_azImportActions))
 properties: {
   roleName: 'Custom Role - AZ IMPORT'
   description: 'Can import images to registry'
   type: 'customRole'
   permissions: [
     {
       actions: _azImportActions
       notActions: []
     }
   ]
   assignableScopes: [
     resourceGroup().id
   ]
 }
}
 
 
// ============== //
// Assign Role for Deployment Script
// ============== //
 
resource contributorRole 'Microsoft.Authorization/roleAssignments@2020-08-01-preview' = {
 name: guid(demoACR.name, 'Contributor', scriptIdentity.id)
 scope: demoACR
 properties: {
   roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleDefinition.name)
   principalId: scriptIdentity.properties.principalId
   principalType: 'ServicePrincipal'
 }
}
 
// ============== //
// Import Image to ACR
// ============== //
 
resource importImage 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
 name: 'acr-import-${demoACR.name}'
 location: location
 identity: {
   type: 'UserAssigned'
   userAssignedIdentities: {
     '${scriptIdentity.id}': {}
   }
 }
 kind: 'AzureCLI'
 properties: {
   azCliVersion: '2.38.0'
   timeout: 'PT5M'
   retentionInterval: 'P1D'
   environmentVariables: [
     {
       name: 'acrName'
       value: demoACR.name
     }
     {
       name: 'imageName'
       value: 'docker.io/library/nginx:${imageTag}'
     }
   ]
   scriptContent: 'az acr import --name $acrName --source $imageName –force'
 }
}
4. Create Container App Environment

Before I can create a container app, I need to create an environment for this app. Container environment also requires a log analytics workspace:


// ============== //
// Log Analytics
// ============== //
 
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-12-01-preview' = {
 name: logName
 location: location
}
 
// ============== //
// Container App Environment
// ============== //
 
resource demoENV 'Microsoft.App/managedEnvironments@2022-01-01-preview' = {
 name: envName
 location: location
 properties: {
   appLogsConfiguration: {
     destination: 'log-analytics'
     logAnalyticsConfiguration: {
       customerId: logAnalyticsWorkspace.properties.customerId
       sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey
     }
   }
 }
}
5. Create Container App

I create a dedicated Service Principal for the Container App and assign an AcrPull role so that the Container App is authorized to access the container registry:


// ============== //
// User-assigned Identity For Container App
// ============== //
 
resource appIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2018-11-30' = {
 name: appIdentityName
 location: location
}
 
// ============== //
// Assign AcrPull Role to appIdentity
// ============== //
 
resource acrPullRole 'Microsoft.Authorization/roleAssignments@2020-08-01-preview' = {
 name: guid(demoACR.name, 'AcrPull', appIdentity.id)
 scope: demoACR
 properties: {
   roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')
   principalId: appIdentity.properties.principalId
   principalType: 'ServicePrincipal'
 }
}
 
 
// ============== //
// Container App Resource
// ============== //
 
resource demoAPP 'Microsoft.App/containerApps@2022-03-01' = {
 dependsOn: [
   importImage
 ]
 name: appName
 location: location
 identity: {
   type: 'UserAssigned'
   userAssignedIdentities: {
     '${appIdentity.id}': {}
   }
 }
 properties: {
   configuration: {
     ingress: {
       external: true
       targetPort: 80
       allowInsecure: false
       traffic: [
         {
           latestRevision: true
           weight: 100
         }
       ]
     }
     registries: [
       {
         server: '${demoACR.name}.azurecr.io'
         identity: appIdentity.id
       }
     ]
   }
   managedEnvironmentId: demoENV.id
   template: {
     containers: [
       {
         name: appName
         image: '${demoACR.name}.azurecr.io/library/nginx:${imageTag}'
       }
     ]
   }
 }
}
6. Deploy main.bicep file

Let’s put it all together and deploy the Bicep file:

az deployment group create \
--name azure-demo \
--resource-group $RESOURCE_GROUP \
--template-file main.bicep

After successful deployment, I can verify that my new service exists. I can retrieve the application url by executing the following command:

az containerapp show \
--name $CONTAINER_APP \
--resource-group $RESOURCE_GROUP \
--query "properties.latestRevisionFqdn" \
--output tsv

Can I automate this process?

Of course you can. Automated deployments with CI/CD (continuous integration/continuous deployment) pipelines is the way to go. I touched this topic briefly in my article about CI/CD pipelines. Here I want to show you how you can use Github Actions to trigger a new deployment of your application. Azure Container Apps has a feature to connect seamlessly with Github. After I push a commit to a specific branch, Github Actions is triggered to deploy a new container image to the Azure Container Registry. Then my application is updated with a new revision of an image.

1. Prerequisites
    • Create sample Github repository
    • Create sample Dockerfile in Github repository:
FROM nginx:1.23.1
 
RUN echo "CONTAINER APPS DEMO" > /usr/share/nginx/html/index.html
    • Create Service Principal for deployment:
SUBSCRIPTION_ID=$(az account show --query id --output tsv)
az ad sp create-for-rbac \
--name hello-world-deploy-id \
--role "b24988ac-6180-42a0-ab88-20f7382dd24c" \ 
--scopes /subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP
    • As an output I receive Service Principal credentials in the format:
{
  "appId": ,
  "displayName": "hello-world-deploy-id",
  "password": ,
  "tenant": 
}
    • I save this output as HELLOWORLDAPP_AZURE_CREDENTIALS in Actions secrets in my repository.
    • Retrieve Azure Container Registry hostname:
az containerapp registry list -n $CONTAINER_APP -g $RESOURCE_GROUP --query "[].server" --output tsv)
  • I save this output as HELLOWORLDAPP_ACR_SERVER in Actions secrets in my repository.

2. Create Github Actions workflow


name: Trigger auto deployment for hello-world-app
 
# When this action will be executed
on:
 # Automatically trigger it when detected changes in repo
 push:
   branches:
     [ main ]
   paths:
   - '**'
   - '.github/workflows/hello-world-app.yml'
 
 # Allow manually trigger
 workflow_dispatch:     
 
jobs:
 build:
   runs-on: ubuntu-latest
 
   steps:
     - name: Checkout to the branch
       uses: actions/checkout@v2
 
     - name: Set up Docker Buildx
       uses: docker/setup-buildx-action@v1
 
     - name: Azure Login
       uses: azure/login@v1
       with:
         creds: $
 
     - name: Deploy to containerapp
       uses: azure/CLI@v1
       with:
         inlineScript: |
           az acr build -t hello-world-app:$ -r $ .
 
 deploy:
   runs-on: ubuntu-latest
   needs: build
  
   steps:
     - name: Azure Login
       uses: azure/login@v1
       with:
         creds: $
 
 
     - name: Deploy to containerapp
       uses: azure/CLI@v1
       with:
         inlineScript: |
           az config set extension.use_dynamic_install=yes_without_prompt
           az containerapp registry set -n hello-world-app -g hello-world-rg --server $ --identity  $
           az containerapp update -n hello-world-app -g hello-world-rg --image $/hello-world-app:$
 
     - name: Azure CLI script
       uses: azure/CLI@v1
       with:
         inlineScript: |
           az logout
           az cache purge
           az account clear

Now I can commit my changes to the Github repository. Pipeline is triggered automatically after changes are pushed to the main branch. If the pipeline runs with no errors, the application is updated.

screenshot

I can confirm the status of deployment in the revision management section in Azure portal or by running the command:

az containerapp revision list -name $CONTAINER_APP -resource-group $RESOURCE_GROUP

I can also check if a correct application image is in use. This is a good starting point to further improve this workflow.

Take it easy with Azure deployment

Choosing the right resources for your next application is a complex process. In this article, I presented Azure Container Apps as a possible solution, but it might be a different case for you.

Do not rush it. ACA will determine how your software will work. If you notice that your current solution is not the right one, do not hesitate to change your assumptions. Experiment but do not search for perfect solutions. Remember that your application is going to evolve, so try to improve your architecture little by little. If in doubt, you can always consult Azure engineers and architects that will help you arrange Azure deployment.

Photo of Adam Kielar

More posts by this author

Adam Kielar

Cloud Engineer in Netguru
Fuel your digital growth with cloud solutions  Discover powerful tools to drive revenue in the cloud  Learn more!

Read more on our Blog

Check out the knowledge base collected and distilled by experienced professionals.

We're Netguru

At Netguru we specialize in designing, building, shipping and scaling beautiful, usable products with blazing-fast efficiency.

Let's talk business