An Ultimate Guide To The Azure Deployment Process
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.
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.
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 AppsInput:
az extension add --name containerapp --upgrade
Output:
The installed extension 'containerapp' is in preview.
2. Register Microsoft.App namespaceI 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 environmentaz 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:
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 variablesSince 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 RegistryI 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 ACRAs 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 EnvironmentBefore 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 AppI 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 fileLet’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.
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.