Azure Cloud Services: Building Enterprise Applications
An overview of key Azure services for enterprise development. App Service, Functions, Container Apps, and integration with Microsoft ecosystem.
Azure Cloud Services: Building Enterprise Applications
Microsoft Azure provides comprehensive cloud services with deep integration into the Microsoft ecosystem. For enterprises already using Microsoft technologies, Azure offers seamless connectivity with Active Directory, Office 365, and development tools like Visual Studio.
Azure Compute Options
Service Comparison
Azure Compute Services:
App Service (PaaS)
├── Best for: Web apps, APIs, mobile backends
├── Languages: .NET, Node.js, Python, Java, PHP
├── Scaling: Automatic or manual
└── Features: Deployment slots, custom domains, SSL
Azure Functions (Serverless)
├── Best for: Event-driven, short-running tasks
├── Triggers: HTTP, Timer, Queue, Blob, Event Grid
├── Scaling: Automatic, scale to zero
└── Features: Durable Functions, bindings
Container Apps (Serverless Containers)
├── Best for: Microservices, APIs, background jobs
├── Based on: Kubernetes (managed)
├── Scaling: KEDA-based auto-scaling
└── Features: Dapr integration, traffic splitting
Azure Kubernetes Service (AKS)
├── Best for: Complex containerised workloads
├── Control: Full Kubernetes access
├── Scaling: Cluster and pod auto-scaling
└── Features: Azure AD integration, Azure CNIWhen to Use Each
| Use Case | Recommended Service |
|---|---|
| Simple web app | App Service |
| Event processing | Azure Functions |
| Microservices | Container Apps |
| Complex orchestration | AKS |
| Background jobs | Container Apps or Functions |
| Legacy lift-and-shift | VMs or App Service |
App Service
Terraform Configuration
# app-service.tf
resource "azurerm_service_plan" "main" {
name = "${var.project}-plan"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
os_type = "Linux"
sku_name = "P1v3"
}
resource "azurerm_linux_web_app" "api" {
name = "${var.project}-api"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
service_plan_id = azurerm_service_plan.main.id
site_config {
application_stack {
node_version = "20-lts"
}
always_on = true
health_check_path = "/health"
cors {
allowed_origins = var.allowed_origins
}
}
app_settings = {
"NODE_ENV" = var.environment
"APPLICATIONINSIGHTS_CONNECTION_STRING" = azurerm_application_insights.main.connection_string
"DATABASE_URL" = "@Microsoft.KeyVault(VaultName=${azurerm_key_vault.main.name};SecretName=database-url)"
}
identity {
type = "SystemAssigned"
}
sticky_settings {
app_setting_names = ["NODE_ENV"]
}
}
# Deployment slots for blue-green deployment
resource "azurerm_linux_web_app_slot" "staging" {
name = "staging"
app_service_id = azurerm_linux_web_app.api.id
site_config {
application_stack {
node_version = "20-lts"
}
}
app_settings = {
"NODE_ENV" = "staging"
}
}
# Auto-scaling
resource "azurerm_monitor_autoscale_setting" "api" {
name = "${var.project}-autoscale"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
target_resource_id = azurerm_service_plan.main.id
profile {
name = "default"
capacity {
default = 2
minimum = 2
maximum = 10
}
rule {
metric_trigger {
metric_name = "CpuPercentage"
metric_resource_id = azurerm_service_plan.main.id
time_grain = "PT1M"
statistic = "Average"
time_window = "PT5M"
time_aggregation = "Average"
operator = "GreaterThan"
threshold = 75
}
scale_action {
direction = "Increase"
type = "ChangeCount"
value = "1"
cooldown = "PT5M"
}
}
rule {
metric_trigger {
metric_name = "CpuPercentage"
metric_resource_id = azurerm_service_plan.main.id
time_grain = "PT1M"
statistic = "Average"
time_window = "PT5M"
time_aggregation = "Average"
operator = "LessThan"
threshold = 25
}
scale_action {
direction = "Decrease"
type = "ChangeCount"
value = "1"
cooldown = "PT5M"
}
}
}
}Azure Functions
Function App Configuration
# functions.tf
resource "azurerm_storage_account" "functions" {
name = "${replace(var.project, "-", "")}func"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
account_tier = "Standard"
account_replication_type = "LRS"
}
resource "azurerm_service_plan" "functions" {
name = "${var.project}-functions-plan"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
os_type = "Linux"
sku_name = "Y1" # Consumption plan
}
resource "azurerm_linux_function_app" "main" {
name = "${var.project}-functions"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
storage_account_name = azurerm_storage_account.functions.name
storage_account_access_key = azurerm_storage_account.functions.primary_access_key
service_plan_id = azurerm_service_plan.functions.id
site_config {
application_stack {
node_version = "20"
}
application_insights_connection_string = azurerm_application_insights.main.connection_string
}
app_settings = {
"FUNCTIONS_WORKER_RUNTIME" = "node"
"WEBSITE_RUN_FROM_PACKAGE" = "1"
"ServiceBusConnection" = azurerm_servicebus_namespace.main.default_primary_connection_string
}
identity {
type = "SystemAssigned"
}
}Function Implementation
// functions/src/functions/processOrder.ts
import { app, InvocationContext } from '@azure/functions';
import { ServiceBusMessage } from '@azure/service-bus';
interface OrderMessage {
orderId: string;
customerId: string;
items: Array<{ productId: string; quantity: number }>;
total: number;
}
export async function processOrder(
message: ServiceBusMessage,
context: InvocationContext
): Promise<void> {
const order = message.body as OrderMessage;
context.log(`Processing order ${order.orderId}`);
try {
// Process the order
await validateOrder(order);
await reserveInventory(order);
await processPayment(order);
await sendConfirmation(order);
context.log(`Order ${order.orderId} processed successfully`);
} catch (error) {
context.error(`Failed to process order ${order.orderId}:`, error);
throw error; // Message will be retried or sent to DLQ
}
}
app.serviceBusQueue('processOrder', {
connection: 'ServiceBusConnection',
queueName: 'orders',
handler: processOrder
});
// HTTP trigger example
app.http('getOrder', {
methods: ['GET'],
authLevel: 'function',
route: 'orders/{orderId}',
handler: async (request, context) => {
const orderId = request.params.orderId;
const order = await getOrderFromDatabase(orderId);
if (!order) {
return {
status: 404,
jsonBody: { error: 'Order not found' }
};
}
return {
jsonBody: order
};
}
});Container Apps
Configuration
# container-apps.tf
resource "azurerm_container_app_environment" "main" {
name = "${var.project}-env"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
}
resource "azurerm_container_app" "api" {
name = "${var.project}-api"
container_app_environment_id = azurerm_container_app_environment.main.id
resource_group_name = azurerm_resource_group.main.name
revision_mode = "Multiple"
template {
container {
name = "api"
image = "${azurerm_container_registry.main.login_server}/${var.project}-api:${var.image_tag}"
cpu = 0.5
memory = "1Gi"
env {
name = "NODE_ENV"
value = var.environment
}
env {
name = "DATABASE_URL"
secret_name = "database-url"
}
liveness_probe {
path = "/health"
port = 8080
transport = "HTTP"
}
readiness_probe {
path = "/ready"
port = 8080
transport = "HTTP"
}
}
min_replicas = var.environment == "production" ? 2 : 0
max_replicas = 10
}
ingress {
external_enabled = true
target_port = 8080
traffic_weight {
percentage = 100
latest_revision = true
}
}
secret {
name = "database-url"
value = var.database_url
}
identity {
type = "SystemAssigned"
}
dapr {
app_id = "${var.project}-api"
app_port = 8080
app_protocol = "http"
}
}Azure AD Integration
Authentication Configuration
// auth/azure-ad.ts
import { ConfidentialClientApplication } from '@azure/msal-node';
const msalConfig = {
auth: {
clientId: process.env.AZURE_CLIENT_ID!,
authority: `https://login.microsoftonline.com/${process.env.AZURE_TENANT_ID}`,
clientSecret: process.env.AZURE_CLIENT_SECRET!
}
};
const cca = new ConfidentialClientApplication(msalConfig);
// Validate token in Express middleware
export const validateAzureToken = async (
req: Request,
res: Response,
next: NextFunction
) => {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Missing authorization header' });
}
const token = authHeader.substring(7);
try {
// Validate token using MSAL
const result = await cca.acquireTokenOnBehalfOf({
oboAssertion: token,
scopes: ['api://my-api/.default']
});
req.user = {
oid: result.account?.homeAccountId,
name: result.account?.name,
email: result.account?.username
};
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid token' });
}
};Key Takeaways
-
Microsoft integration: Azure shines when integrated with Microsoft ecosystem
-
App Service simplicity: PaaS for straightforward web applications
-
Container Apps: Modern alternative to AKS for simpler workloads
-
Deployment slots: Enable zero-downtime deployments
-
Azure AD: Native enterprise identity management
-
Key Vault: Centralised secrets management
-
Application Insights: Built-in observability
-
Hybrid capabilities: Strong on-premises integration options
Azure provides enterprise-grade services with seamless Microsoft integration. Choose services based on complexity requirements and team expertise.