Azure Functions: Complete Serverless Computing Guide
Azure Functions enables serverless computing, allowing you to run event-driven code without managing infrastructure. This comprehensive guide covers function development, triggers, bindings, and deployment strategies for small business applications.
Understanding Azure Functions
Hosting Plans
- Consumption Plan: Pay-per-execution with automatic scaling
- Premium Plan: Enhanced performance with VNet connectivity
- App Service Plan: Dedicated resources for consistent performance
- Container Apps: Containerized function hosting
Runtime Versions
- Functions 4.x: Latest runtime with .NET 6+ support
- Functions 3.x: Previous stable version
- Functions 2.x: Legacy runtime (deprecated)
Supported Languages
- C#: .NET 6/7/8 support
- JavaScript/TypeScript: Node.js runtime
- Python: Python 3.7-3.11
- Java: Java 8/11/17
- PowerShell: PowerShell 7.x
Creating Function Apps
PowerShell Setup
# Install Azure PowerShell module
Install-Module -Name Az -Force
# Connect to Azure
Connect-AzAccount
# Create resource group
New-AzResourceGroup -Name "Functions-RG" -Location "East US"
# Create storage account (required for Functions)
$storageAccount = New-AzStorageAccount `
-ResourceGroupName "Functions-RG" `
-Name "businessfunctionsstore" `
-Location "East US" `
-SkuName "Standard_LRS" `
-Kind "StorageV2"
# Create Function App
$functionApp = New-AzFunctionApp `
-ResourceGroupName "Functions-RG" `
-Name "businessfunctions001" `
-Location "East US" `
-StorageAccountName "businessfunctionsstore" `
-Runtime "dotnet" `
-RuntimeVersion "6" `
-FunctionsVersion "4" `
-OSType "Windows"
Azure CLI Setup
# Login to Azure
az login
# Create resource group
az group create --name "Functions-RG" --location "eastus"
# Create storage account
az storage account create \
--name "businessfunctionsstore" \
--resource-group "Functions-RG" \
--location "eastus" \
--sku "Standard_LRS"
# Create Function App
az functionapp create \
--resource-group "Functions-RG" \
--consumption-plan-location "eastus" \
--runtime "dotnet" \
--runtime-version "6" \
--functions-version "4" \
--name "businessfunctions001" \
--storage-account "businessfunctionsstore" \
--os-type "Windows"
Function Development
HTTP Trigger Function (C#)
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
public static class HttpTriggerFunction
{
[FunctionName("ProcessOrder")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "orders")] HttpRequest req,
[CosmosDB(
databaseName: "BusinessDB",
collectionName: "Orders",
ConnectionStringSetting = "CosmosDBConnection")] IAsyncCollector<Order> ordersOut,
ILogger log)
{
log.LogInformation("Processing order request");
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
var order = JsonConvert.DeserializeObject<Order>(requestBody);
if (order == null)
{
return new BadRequestObjectResult("Invalid order data");
}
// Process order
order.Id = Guid.NewGuid().ToString();
order.ProcessedAt = DateTime.UtcNow;
order.Status = "Processing";
// Save to Cosmos DB
await ordersOut.AddAsync(order);
log.LogInformation($"Order {order.Id} processed successfully");
return new OkObjectResult(order);
}
}
public class Order
{
public string Id { get; set; }
public string CustomerName { get; set; }
public decimal Amount { get; set; }
public DateTime ProcessedAt { get; set; }
public string Status { get; set; }
}
Timer Trigger Function (C#)
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using System;
using System.Data.SqlClient;
using System.Threading.Tasks;
public static class TimerTriggerFunction
{
[FunctionName("DailyReportGeneration")]
public static async Task Run(
[TimerTrigger("0 0 6 * * *")] TimerInfo myTimer,
[SendGrid(ApiKey = "SendGridApiKey")] IAsyncCollector<SendGridMessage> messageCollector,
ILogger log)
{
log.LogInformation($"Daily report function executed at: {DateTime.Now}");
try
{
// Generate report data
var reportData = await GenerateReportData();
var reportHtml = GenerateReportHtml(reportData);
// Send email report
var message = new SendGridMessage
{
From = new EmailAddress("reports@company.com", "Business Reports"),
Subject = $"Daily Sales Report - {DateTime.Now:yyyy-MM-dd}",
HtmlContent = reportHtml
};
message.AddTo("admin@company.com");
await messageCollector.AddAsync(message);
log.LogInformation("Daily report sent successfully");
}
catch (Exception ex)
{
log.LogError(ex, "Error generating daily report");
throw;
}
}
private static async Task<ReportData> GenerateReportData()
{
var connectionString = Environment.GetEnvironmentVariable("SqlConnectionString");
using var connection = new SqlConnection(connectionString);
// Query database for report data
var command = new SqlCommand(@"
SELECT
COUNT(*) as TotalOrders,
SUM(Amount) as TotalSales,
AVG(Amount) as AverageOrderValue
FROM Orders
WHERE DATE(ProcessedAt) = @ReportDate", connection);
command.Parameters.AddWithValue("@ReportDate", DateTime.Today);
await connection.OpenAsync();
using var reader = await command.ExecuteReaderAsync();
if (await reader.ReadAsync())
{
return new ReportData
{
TotalOrders = reader.GetInt32("TotalOrders"),
TotalSales = reader.GetDecimal("TotalSales"),
AverageOrderValue = reader.GetDecimal("AverageOrderValue")
};
}
return new ReportData();
}
private static string GenerateReportHtml(ReportData data)
{
return $@"
<html>
<body>
<h2>Daily Sales Report - {DateTime.Now:yyyy-MM-dd}</h2>
<table border='1'>
<tr><td>Total Orders</td><td>{data.TotalOrders}</td></tr>
<tr><td>Total Sales</td><td>${data.TotalSales:F2}</td></tr>
<tr><td>Average Order Value</td><td>${data.AverageOrderValue:F2}</td></tr>
</table>
</body>
</html>";
}
}
public class ReportData
{
public int TotalOrders { get; set; }
public decimal TotalSales { get; set; }
public decimal AverageOrderValue { get; set; }
}
Blob Trigger Function (JavaScript)
const { app } = require('@azure/functions');
app.storageBlob('processBlobUpload', {
path: 'uploads/{name}',
connection: 'AzureWebJobsStorage',
handler: async (blob, context) => {
context.log(`Processing blob: ${context.triggerMetadata.name}`);
context.log(`Blob size: ${blob.length} bytes`);
try {
// Process blob content
const fileName = context.triggerMetadata.name;
const fileExtension = fileName.split('.').pop().toLowerCase();
if (fileExtension === 'csv') {
await processCsvFile(blob, context);
} else if (fileExtension === 'json') {
await processJsonFile(blob, context);
} else {
context.log(`Unsupported file type: ${fileExtension}`);
return;
}
context.log(`Successfully processed ${fileName}`);
} catch (error) {
context.log.error(`Error processing blob: ${error.message}`);
throw error;
}
}
});
async function processCsvFile(blob, context) {
const csv = require('csv-parser');
const { Readable } = require('stream');
const results = [];
const stream = Readable.from(blob.toString());
return new Promise((resolve, reject) => {
stream
.pipe(csv())
.on('data', (data) => results.push(data))
.on('end', async () => {
try {
// Process CSV data
context.log(`Processed ${results.length} CSV records`);
// Save to database or perform other operations
await saveToDatabase(results, context);
resolve();
} catch (error) {
reject(error);
}
})
.on('error', reject);
});
}
async function processJsonFile(blob, context) {
const data = JSON.parse(blob.toString());
context.log(`Processing JSON with ${data.length} items`);
// Process JSON data
await saveToDatabase(data, context);
}
async function saveToDatabase(data, context) {
// Implement database saving logic
context.log(`Saving ${data.length} records to database`);
// Example: Save to Cosmos DB
const { CosmosClient } = require('@azure/cosmos');
const client = new CosmosClient(process.env.CosmosDBConnection);
const container = client.database('BusinessDB').container('ProcessedData');
for (const item of data) {
await container.items.create({
id: require('uuid').v4(),
data: item,
processedAt: new Date().toISOString()
});
}
}
Triggers and Bindings
Service Bus Trigger
[FunctionName("ProcessServiceBusMessage")]
public static async Task Run(
[ServiceBusTrigger("orders-queue", Connection = "ServiceBusConnection")] string messageBody,
[CosmosDB(
databaseName: "BusinessDB",
collectionName: "ProcessedOrders",
ConnectionStringSetting = "CosmosDBConnection")] IAsyncCollector<ProcessedOrder> processedOrdersOut,
ILogger log)
{
log.LogInformation($"Processing Service Bus message: {messageBody}");
try
{
var order = JsonConvert.DeserializeObject<Order>(messageBody);
// Process the order
var processedOrder = new ProcessedOrder
{
Id = Guid.NewGuid().ToString(),
OriginalOrderId = order.Id,
ProcessedAt = DateTime.UtcNow,
Status = "Completed",
ProcessingDetails = "Processed by Service Bus trigger"
};
await processedOrdersOut.AddAsync(processedOrder);
log.LogInformation($"Order {order.Id} processed successfully");
}
catch (Exception ex)
{
log.LogError(ex, "Error processing Service Bus message");
throw;
}
}
public class ProcessedOrder
{
public string Id { get; set; }
public string OriginalOrderId { get; set; }
public DateTime ProcessedAt { get; set; }
public string Status { get; set; }
public string ProcessingDetails { get; set; }
}
Event Grid Trigger
[FunctionName("ProcessEventGridEvent")]
public static async Task Run(
[EventGridTrigger] EventGridEvent eventGridEvent,
[Table("EventLog", Connection = "AzureWebJobsStorage")] IAsyncCollector<EventLogEntry> eventLogOut,
ILogger log)
{
log.LogInformation($"Event Grid event received: {eventGridEvent.EventType}");
try
{
// Log event details
var logEntry = new EventLogEntry
{
PartitionKey = eventGridEvent.EventType,
RowKey = Guid.NewGuid().ToString(),
EventId = eventGridEvent.Id,
EventType = eventGridEvent.EventType,
Subject = eventGridEvent.Subject,
EventTime = eventGridEvent.EventTime,
Data = JsonConvert.SerializeObject(eventGridEvent.Data)
};
await eventLogOut.AddAsync(logEntry);
// Process different event types
switch (eventGridEvent.EventType)
{
case "Microsoft.Storage.BlobCreated":
await ProcessBlobCreatedEvent(eventGridEvent, log);
break;
case "Microsoft.Resources.ResourceWriteSuccess":
await ProcessResourceChangeEvent(eventGridEvent, log);
break;
default:
log.LogInformation($"Unhandled event type: {eventGridEvent.EventType}");
break;
}
}
catch (Exception ex)
{
log.LogError(ex, "Error processing Event Grid event");
throw;
}
}
private static async Task ProcessBlobCreatedEvent(EventGridEvent eventGridEvent, ILogger log)
{
log.LogInformation($"Processing blob created event: {eventGridEvent.Subject}");
// Implement blob processing logic
}
private static async Task ProcessResourceChangeEvent(EventGridEvent eventGridEvent, ILogger log)
{
log.LogInformation($"Processing resource change event: {eventGridEvent.Subject}");
// Implement resource change processing logic
}
public class EventLogEntry : ITableEntity
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string EventId { get; set; }
public string EventType { get; set; }
public string Subject { get; set; }
public DateTime EventTime { get; set; }
public string Data { get; set; }
public DateTimeOffset? Timestamp { get; set; }
public ETag ETag { get; set; }
}
Configuration and Settings
Application Settings
# Set application settings
$appSettings = @{
"SqlConnectionString" = "Server=businesssqlserver001.database.windows.net;Database=BusinessDB;User Id=sqladmin;Password=SecurePassword123!;"
"CosmosDBConnection" = "AccountEndpoint=https://businesscosmosdb.documents.azure.com:443/;AccountKey=your-key;"
"ServiceBusConnection" = "Endpoint=sb://businessservicebus.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=your-key"
"SendGridApiKey" = "SG.your-sendgrid-api-key"
"StorageAccountConnection" = "DefaultEndpointsProtocol=https;AccountName=businessfunctionsstore;AccountKey=your-key;"
}
# Update function app settings
Update-AzFunctionAppSetting -ResourceGroupName "Functions-RG" -Name "businessfunctions001" -AppSetting $appSettings
Environment-Specific Configuration
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"SqlConnectionString": "Server=localhost;Database=BusinessDB;Integrated Security=true;",
"CosmosDBConnection": "AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==;",
"APPINSIGHTS_INSTRUMENTATIONKEY": "your-app-insights-key"
},
"ConnectionStrings": {}
}
Deployment Strategies
Visual Studio Deployment
<!-- Add to .csproj file -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.10.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.0.13" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.CosmosDB" Version="4.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.ServiceBus" Version="5.9.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.EventGrid" Version="3.2.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.7.0" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
Azure DevOps CI/CD Pipeline
trigger:
- main
pool:
vmImage: 'windows-latest'
variables:
buildConfiguration: 'Release'
azureSubscription: 'BusinessSubscription'
functionAppName: 'businessfunctions001'
resourceGroupName: 'Functions-RG'
stages:
- stage: Build
jobs:
- job: Build
steps:
- task: DotNetCoreCLI@2
displayName: 'Restore NuGet packages'
inputs:
command: 'restore'
projects: '**/*.csproj'
- task: DotNetCoreCLI@2
displayName: 'Build solution'
inputs:
command: 'build'
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration)'
- task: DotNetCoreCLI@2
displayName: 'Run unit tests'
inputs:
command: 'test'
projects: '**/*Tests*.csproj'
arguments: '--configuration $(buildConfiguration) --collect:"XPlat Code Coverage"'
- task: DotNetCoreCLI@2
displayName: 'Publish application'
inputs:
command: 'publish'
publishWebProjects: false
projects: '**/*.csproj'
arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'
zipAfterPublish: false
- task: PublishBuildArtifacts@1
displayName: 'Publish build artifacts'
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
- stage: Deploy
dependsOn: Build
condition: succeeded()
jobs:
- deployment: Deploy
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- task: AzureFunctionApp@1
displayName: 'Deploy Azure Function App'
inputs:
azureSubscription: '$(azureSubscription)'
appType: 'functionApp'
appName: '$(functionAppName)'
resourceGroupName: '$(resourceGroupName)'
package: '$(Pipeline.Workspace)/drop'
deploymentMethod: 'zipDeploy'
GitHub Actions Deployment
name: Deploy Azure Functions
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
AZURE_FUNCTIONAPP_NAME: 'businessfunctions001'
AZURE_FUNCTIONAPP_PACKAGE_PATH: '.'
DOTNET_VERSION: '6.0.x'
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v3
- name: Setup DotNet ${{ env.DOTNET_VERSION }} Environment
uses: actions/setup-dotnet@v1
with:
dotnet-version: ${{ env.DOTNET_VERSION }}
- name: 'Resolve Project Dependencies Using Dotnet'
shell: bash
run: |
pushd './${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}'
dotnet build --configuration Release --output ./output
popd
- name: 'Run Azure Functions Action'
uses: Azure/functions-action@v1
id: fa
with:
app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
package: '${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}/output'
publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}
Monitoring and Diagnostics
Application Insights Integration
// Program.cs for .NET 6 isolated worker
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.ApplicationInsights.WorkerService;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
services.AddApplicationInsightsTelemetryWorkerService();
services.ConfigureFunctionsApplicationInsights();
})
.Build();
host.Run();
Custom Metrics and Logging
[FunctionName("ProcessOrderWithMetrics")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "orders")] HttpRequest req,
ILogger log)
{
var telemetryClient = new TelemetryClient();
using var operation = telemetryClient.StartOperation<RequestTelemetry>("ProcessOrder");
try
{
// Custom metrics
telemetryClient.TrackMetric("OrderProcessingStarted", 1);
var stopwatch = Stopwatch.StartNew();
// Process order logic
var order = await ProcessOrder(req, log);
stopwatch.Stop();
// Track processing time
telemetryClient.TrackMetric("OrderProcessingTime", stopwatch.ElapsedMilliseconds);
telemetryClient.TrackMetric("OrderProcessingCompleted", 1);
// Custom event
telemetryClient.TrackEvent("OrderProcessed", new Dictionary<string, string>
{
["OrderId"] = order.Id,
["CustomerName"] = order.CustomerName,
["Amount"] = order.Amount.ToString()
});
return new OkObjectResult(order);
}
catch (Exception ex)
{
telemetryClient.TrackException(ex);
telemetryClient.TrackMetric("OrderProcessingFailed", 1);
throw;
}
}
Security Best Practices
Authentication and Authorization
[FunctionName("SecureFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "secure")] HttpRequest req,
ILogger log)
{
// Validate JWT token
var authHeader = req.Headers["Authorization"].FirstOrDefault();
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer "))
{
return new UnauthorizedResult();
}
var token = authHeader.Substring(7);
var isValid = await ValidateJwtToken(token);
if (!isValid)
{
return new UnauthorizedResult();
}
// Function logic here
return new OkResult();
}
private static async Task<bool> ValidateJwtToken(string token)
{
// Implement JWT validation logic
try
{
var tokenHandler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://your-issuer.com",
ValidateAudience = true,
ValidAudience = "your-audience",
ValidateLifetime = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("your-secret-key"))
};
var principal = tokenHandler.ValidateToken(token, validationParameters, out var validatedToken);
return true;
}
catch
{
return false;
}
}
Key Vault Integration
[FunctionName("SecureConfigFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
ILogger log)
{
var keyVaultClient = new SecretClient(
new Uri("https://businesskeyvault001.vault.azure.net/"),
new DefaultAzureCredential());
try
{
var secret = await keyVaultClient.GetSecretAsync("DatabaseConnectionString");
var connectionString = secret.Value.Value;
// Use connection string securely
// Don't log or return the actual secret value
return new OkObjectResult(new { status = "success", message = "Configuration retrieved" });
}
catch (Exception ex)
{
log.LogError(ex, "Failed to retrieve secret from Key Vault");
return new StatusCodeResult(500);
}
}
Performance Optimization
Function Configuration
{
"version": "2.0",
"functionTimeout": "00:05:00",
"extensions": {
"http": {
"routePrefix": "api",
"maxConcurrentRequests": 200,
"maxOutstandingRequests": 600
}
},
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"maxTelemetryItemsPerSecond": 20
}
}
}
}
Connection Pooling
public static class ConnectionManager
{
private static readonly Lazy<SqlConnection> _connection = new Lazy<SqlConnection>(() =>
{
var connectionString = Environment.GetEnvironmentVariable("SqlConnectionString");
return new SqlConnection(connectionString);
});
public static SqlConnection GetConnection() => _connection.Value;
}
[FunctionName("OptimizedDatabaseFunction")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
ILogger log)
{
using var connection = ConnectionManager.GetConnection();
if (connection.State != ConnectionState.Open)
{
await connection.OpenAsync();
}
// Use connection for database operations
var command = new SqlCommand("SELECT * FROM Orders", connection);
var results = await command.ExecuteReaderAsync();
var orders = new List<Order>();
while (await results.ReadAsync())
{
orders.Add(new Order
{
Id = results.GetString("Id"),
CustomerName = results.GetString("CustomerName"),
Amount = results.GetDecimal("Amount")
});
}
return new OkObjectResult(orders);
}
Cost Management
Consumption Plan Optimization
# Monitor function execution metrics
$resourceId = "/subscriptions/subscription-id/resourceGroups/Functions-RG/providers/Microsoft.Web/sites/businessfunctions001"
# Get execution count
$executionCount = Get-AzMetric -ResourceId $resourceId -MetricName "FunctionExecutionCount" -TimeGrain 01:00:00 -StartTime (Get-Date).AddDays(-30)
# Get execution units
$executionUnits = Get-AzMetric -ResourceId $resourceId -MetricName "FunctionExecutionUnits" -TimeGrain 01:00:00 -StartTime (Get-Date).AddDays(-30)
# Calculate estimated cost
$totalExecutions = ($executionCount.Data | Measure-Object -Property Average -Sum).Sum
$totalExecutionUnits = ($executionUnits.Data | Measure-Object -Property Average -Sum).Sum
$executionCost = [math]::Max(0, ($totalExecutions - 1000000) * 0.0000002) # First 1M executions free
$gbSecondCost = [math]::Max(0, ($totalExecutionUnits - 400000) * 0.000016) # First 400K GB-s free
$totalCost = $executionCost + $gbSecondCost
Write-Host "Estimated monthly cost: $${totalCost:F2}"
Best Practices
Development
- Use dependency injection for better testability
- Implement proper error handling and logging
- Follow single responsibility principle for functions
- Use async/await for I/O operations
Performance
- Minimize cold starts with appropriate hosting plans
- Use connection pooling for database connections
- Implement caching for frequently accessed data
- Optimize function memory allocation
Security
- Use managed identities for Azure service authentication
- Implement proper input validation
- Store secrets in Key Vault
- Enable HTTPS for HTTP triggers
Troubleshooting
Common Issues
# Check function app status
Get-AzFunctionApp -ResourceGroupName "Functions-RG" -Name "businessfunctions001"
# View function logs
Get-AzFunctionAppLog -ResourceGroupName "Functions-RG" -Name "businessfunctions001"
# Check function execution history
$functions = Get-AzFunctionAppFunction -ResourceGroupName "Functions-RG" -Name "businessfunctions001"
foreach ($function in $functions) {
Get-AzFunctionAppFunctionLog -ResourceGroupName "Functions-RG" -Name "businessfunctions001" -FunctionName $function.Name
}
Performance Monitoring
// Application Insights query for function performance
requests
| where timestamp > ago(24h)
| where name startswith "ProcessOrder"
| summarize
avg(duration),
min(duration),
max(duration),
percentile(duration, 95)
by bin(timestamp, 1h)
| render timechart
Conclusion
Azure Functions provides a powerful serverless computing platform that enables event-driven applications with automatic scaling and cost-effective pricing. Proper implementation of triggers, bindings, and security practices ensures robust and maintainable serverless solutions.
For professional Azure Functions development and serverless architecture consulting services in Louisville, contact Tyler on Tech Louisville for expert assistance with your serverless computing needs.