Skip to main content

Deployment Guide

This guide covers deploying the SWMM Feature Publisher Function to Azure.

Prerequisites

  • Azure subscription
  • Azure CLI installed and configured
  • Function App deployment permissions
  • ArcGIS Online account with API key

Deployment Options

Step 1: Create Storage Account

  1. Go to Azure Portal

  2. Create new Storage Account:

    • Resource Group: Create new or use existing
    • Storage Account Name: e.g., swmmpublisherstorage
    • Location: Choose region close to users
    • Performance: Standard
    • Redundancy: LRS (or GRS for production)
  3. After creation, create these resources:

    • Container: swmm-input (for INP file uploads - triggers blob function)
    • Tables: Projects, ProjectFeatureMetaData
  4. Important: Ensure the container swmm-input has appropriate permissions for blob trigger:

    • The function app's managed identity or connection string must have read access
    • Blob creation events will trigger the swmmBlobTriggerPublisher function

Step 2: Get Connection String/SAS Token

Option A: Connection String (Simpler)

  1. Go to Storage Account → Access keys
  2. Copy "Connection string" from key1 or key2

Option B: SAS Token (More Secure)

  1. Go to Storage Account → Shared access signature
  2. Configure:
    • Allowed services: Blob, Table
    • Allowed resource types: Service, Container, Object
    • Allowed permissions: Read, Write, Delete, List, Add, Create, Update
    • Start and expiry date: Set appropriate dates
  3. Click "Generate SAS and connection string"
  4. Copy the SAS token

Step 3: Create Application Insights

  1. Create new Application Insights resource
  2. Note the Connection String

Step 4: Create Function App

  1. Create new Function App:

    • Runtime: Python
    • Version: 3.11
    • Operating System: Linux
    • Plan: Consumption (Y1) or Premium
    • Storage Account: Use existing or create new
  2. Configure Application Settings (Environment Variables):

    Go to Function App → Configuration → Application settings:

    AzureWebJobsStorage = <storage-connection-string>
    0b61f1_STORAGE = <storage-connection-string>
    APPLICATIONINSIGHTS_CONNECTION_STRING = <app-insights-connection-string>

    AZURE_STORAGE_ACCOUNT_NAME = <account-name>
    AZURE_STORAGE_SAS_TOKEN = <sas-token-with-question-mark>

    SWMM_INP_CONTAINER = swmm-input
    PROJECT_TABLE_NAME = Projects
    FEATURES_TABLE_NAME = ProjectFeatureMetaData

    ARCGIS_URL = https://www.arcgis.com
    ARCGIS_API_KEY = <your-arcgis-api-key>

    MAX_ATTEMPTS = 3
    FUNCTIONS_WORKER_RUNTIME = python

    # Optional: fail publish if Projects row has no valid EPSG (Int32) and HTTP body omits "epsg"
    # REQUIRE_PROJECT_EPSG = true

Project CRS (EPSG): Layer publishing uses the project's EPSG authority code for hybrid reprojection and INP-only GeoJSON CRS metadata. Resolution order: optional epsg in the publish-project-layers JSON body → EPSG column on the Projects table row (Int32, e.g. 26917) → default 3089 (see publisher/constants.py) with a log warning. Set REQUIRE_PROJECT_EPSG=true to return an error instead of using the default when EPSG is missing.

Subcatchment outputs: Hybrid publishing writes two subcatchment layers and two metadata rows in ProjectFeatureMetaData:

  • Subcatchment (Polygon geometry from INP [Polygons] section; no centroid fallback)
  • Subcatchment_Centroid (Point geometry)

Step 5: Deploy Code

From VS Code:

  1. Install Azure Functions extension
  2. Right-click on function folder
  3. Select "Deploy to Function App"
  4. Choose your function app

From Command Line:

cd c:\Git\gqc\hydrotrek-ssc-suite\swmm-feature-publisher-function
func azure functionapp publish <your-function-app-name>

Step 6: Verify Deployment

  1. Check Function App logs:

    • Go to Function App → Log stream in Azure Portal
    • Or use Azure CLI: az webapp log tail --name <function-app-name> --resource-group <resource-group>
  2. Test health endpoint:

    curl https://<function-app-name>.azurewebsites.net/api/health

    Expected response: {"status": "healthy", "service": "swmm-feature-publisher"}

  3. Test blob trigger:

    # Upload test INP file to trigger automatic publishing
    python scripts/test_blob_upload.py \
    --project-id test-deployment \
    --inp-file scripts/rivershore.inp

    This tests the automatic publishing workflow:

    • ✅ Blob trigger activates on upload
    • ✅ Status updates: "Uploaded input file" → "Processing" → "Complete"
    • ✅ Layers published to ArcGIS with prefix SD1_test-deployment
  4. Monitor first upload:

    • Check Function App Logs for blob trigger activation
    • Verify Projects Table status transitions
    • Check ArcGIS Online for published layers
    • View Application Insights telemetry

Option 2: Azure CLI (Automated)

#!/bin/bash

# Configuration
RESOURCE_GROUP="rg-swmm-publisher"
LOCATION="eastus"
STORAGE_ACCOUNT="swmmpublisherstorage"
FUNCTION_APP="swmm-feature-publisher"
APP_INSIGHTS="swmm-publisher-insights"

# Create resource group
az group create \
--name $RESOURCE_GROUP \
--location $LOCATION

# Create storage account
az storage account create \
--name $STORAGE_ACCOUNT \
--resource-group $RESOURCE_GROUP \
--location $LOCATION \
--sku Standard_LRS

# Get storage connection string
STORAGE_CONN=$(az storage account show-connection-string \
--name $STORAGE_ACCOUNT \
--resource-group $RESOURCE_GROUP \
--query connectionString \
--output tsv)

# Create container
az storage container create \
--name swmm-input \
--connection-string "$STORAGE_CONN"

# Create tables
az storage table create \
--name Projects \
--connection-string "$STORAGE_CONN"

az storage table create \
--name ProjectFeatureMetaData \
--connection-string "$STORAGE_CONN"

# Create Application Insights
az monitor app-insights component create \
--app $APP_INSIGHTS \
--location $LOCATION \
--resource-group $RESOURCE_GROUP

# Get App Insights connection string
APP_INSIGHTS_CONN=$(az monitor app-insights component show \
--app $APP_INSIGHTS \
--resource-group $RESOURCE_GROUP \
--query connectionString \
--output tsv)

# Create Function App
az functionapp create \
--name $FUNCTION_APP \
--resource-group $RESOURCE_GROUP \
--storage-account $STORAGE_ACCOUNT \
--runtime python \
--runtime-version 3.11 \
--os-type Linux \
--functions-version 4 \
--consumption-plan-location $LOCATION

# Configure app settings
az functionapp config appsettings set \
--name $FUNCTION_APP \
--resource-group $RESOURCE_GROUP \
--settings \
"0b61f1_STORAGE=$STORAGE_CONN" \
"APPLICATIONINSIGHTS_CONNECTION_STRING=$APP_INSIGHTS_CONN" \
"AZURE_STORAGE_ACCOUNT_NAME=$STORAGE_ACCOUNT" \
"SWMM_INP_CONTAINER=swmm-input" \
"PROJECT_TABLE_NAME=Projects" \
"FEATURES_TABLE_NAME=ProjectFeatureMetaData" \
"ARCGIS_URL=https://www.arcgis.com" \
"ARCGIS_API_KEY=<YOUR_API_KEY>" \
"MAX_ATTEMPTS=3"

# Deploy function code
func azure functionapp publish $FUNCTION_APP

echo "Deployment complete!"
echo "Function App: https://$FUNCTION_APP.azurewebsites.net"
echo "Health Check: https://$FUNCTION_APP.azurewebsites.net/api/health"

Option 3: Using Managed Identity (Production Best Practice)

For production, use Managed Identity instead of connection strings:

Step 1: Enable Managed Identity

az functionapp identity assign \
--name $FUNCTION_APP \
--resource-group $RESOURCE_GROUP

Step 2: Grant Permissions

# Get Function App's principal ID
PRINCIPAL_ID=$(az functionapp identity show \
--name $FUNCTION_APP \
--resource-group $RESOURCE_GROUP \
--query principalId \
--output tsv)

# Grant Storage Blob Data Contributor
az role assignment create \
--assignee $PRINCIPAL_ID \
--role "Storage Blob Data Contributor" \
--scope "/subscriptions/<subscription-id>/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Storage/storageAccounts/$STORAGE_ACCOUNT"

# Grant Storage Table Data Contributor
az role assignment create \
--assignee $PRINCIPAL_ID \
--role "Storage Table Data Contributor" \
--scope "/subscriptions/<subscription-id>/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Storage/storageAccounts/$STORAGE_ACCOUNT"

Step 3: Update App Settings

Remove keys, use identity-based connection:

az functionapp config appsettings set \
--name $FUNCTION_APP \
--resource-group $RESOURCE_GROUP \
--settings \
"AzureWebJobsStorage__accountName=$STORAGE_ACCOUNT" \
"0b61f1_STORAGE__accountName=$STORAGE_ACCOUNT" \
"AZURE_STORAGE_ACCOUNT_NAME=$STORAGE_ACCOUNT"

Post-Deployment Configuration

1. Configure Monitoring Alerts

# Alert on function failures
az monitor metrics alert create \
--name "swmm-function-failures" \
--resource-group $RESOURCE_GROUP \
--scopes "/subscriptions/<subscription-id>/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Web/sites/$FUNCTION_APP" \
--condition "count FunctionExecutionCount < 1" \
--window-size 5m \
--evaluation-frequency 1m

2. Configure Auto-scaling (Premium Plan)

az functionapp plan update \
--name <plan-name> \
--resource-group $RESOURCE_GROUP \
--max-burst 20 \
--min-instances 1

3. Enable Diagnostic Logs

az monitor diagnostic-settings create \
--name function-diagnostics \
--resource "/subscriptions/<subscription-id>/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Web/sites/$FUNCTION_APP" \
--logs '[{"category": "FunctionAppLogs","enabled": true}]' \
--workspace <log-analytics-workspace-id>

Continuous Deployment

Deployment is manual for now (GitHub Actions deploy to Flex Consumption can fail; use one of the methods in Step 5 above: VS Code "Deploy to Function App" or func azure functionapp publish <your-function-app-name> from the command line).


Rollback Procedure

If deployment fails:

Option 1: Azure Portal

  1. Go to Function App → Deployment Center
  2. Click on previous deployment
  3. Click "Redeploy"

Option 2: Azure CLI

# List deployment IDs
az functionapp deployment list \
--name $FUNCTION_APP \
--resource-group $RESOURCE_GROUP

# Redeploy specific version
az functionapp deployment source sync \
--name $FUNCTION_APP \
--resource-group $RESOURCE_GROUP

Deployment Verification Checklist

After deployment, verify:

  • Health endpoint returns 200 OK
  • Application Insights shows telemetry
  • Blob container swmm-input exists
  • Tables Projects and ProjectFeatureMetaData exist
  • Test blob upload processes successfully
  • Project status updates correctly
  • ArcGIS layers publish successfully
  • Error logging works

Troubleshooting Deployment

Function won't start

  • Check Application Insights logs
  • Verify all required app settings configured
  • Ensure Python runtime version matches (3.11)
  • Check storage account connection

Permission errors

  • Verify SAS token permissions (rwdxftlacup)
  • Check Managed Identity role assignments
  • Ensure ArcGIS API key is valid

Performance issues

  • Increase Function App plan tier
  • Adjust batchSize in host.json
  • Enable Application Insights profiling

Security Checklist

  • Use Managed Identity in production
  • Store secrets in Azure Key Vault
  • Enable HTTPS only
  • Configure IP restrictions if needed
  • Rotate SAS tokens regularly
  • Use least-privilege permissions
  • Enable Azure Defender for Storage
  • Configure alert rules