Skip to content

Deploy ThreatAtlas to Lista Pre-Prod Azure Environment #1

Description

@jeff-at-trimble

Deployment Plan: ThreatAtlas → Lista Pre-Prod (Azure)

Overview

Deploy the ThreatAtlas application (backend API, frontend SPA, PostgreSQL database) to the lista pre-prod environment in Azure using Container Apps, Azure Database for PostgreSQL Flexible Server, and Azure Container Registry, managed via reusable Bicep templates.


Architecture

                    ┌──────────────────────────────────────┐
                    │   Resource Group                      │
                    │   threatatlas-lista-preprod-rg         │
                    │                                       │
                    │  ┌──────────────────────────────┐     │
                    │  │ Container Apps Environment    │     │
                    │  │                               │     │
                    │  │  ┌────────────┐ ┌──────────┐  │     │
                    │  │  │  Backend   │ │ Frontend │  │     │
                    │  │  │ (FastAPI)  │ │ (Nginx)  │  │     │
                    │  │  │  :8000     │ │  :8080   │  │     │
                    │  │  └─────┬──────┘ └──────────┘  │     │
                    │  └────────┼───────────────────────┘     │
                    │           │                              │
                    │  ┌────────▼──────────┐                  │
                    │  │ PostgreSQL Flex   │                  │
                    │  │ Server (v16)      │                  │
                    │  └──────────────────┘                  │
                    │                                       │
                    │  ┌──────────┐  ┌───────────┐          │
                    │  │   ACR    │  │ Key Vault │          │
                    │  └──────────┘  └───────────┘          │
                    │                                       │
                    │  ┌─────────────────┐                  │
                    │  │ Managed Identity│                  │
                    │  │ (ACR pull + KV) │                  │
                    │  └─────────────────┘                  │
                    │                                       │
                    │  ┌──────────────────┐                 │
                    │  │ Log Analytics    │                 │
                    │  └──────────────────┘                 │
                    └──────────────────────────────────────┘

Azure Resources

Resource Service SKU / Tier Purpose
threatatlas-lista-preprod-pg Azure Database for PostgreSQL Flexible Server Standard_B1ms (Burstable) Application database
threatatlaslista-preprodacr Azure Container Registry Basic Docker image storage
threatatlas-lista-preprod-env Container Apps Environment Consumption Container orchestration
threatatlas-lista-preprod-backend Container App 0.5 CPU / 1Gi RAM FastAPI backend (1-3 replicas)
threatatlas-lista-preprod-frontend Container App 0.25 CPU / 0.5Gi RAM Nginx frontend (1-2 replicas)
ta-lista-preprod-kv Key Vault Standard Secrets management
threatatlas-lista-preprod-identity User-Assigned Managed Identity -- ACR pull + KV access
threatatlas-lista-preprod-logs Log Analytics Workspace PerGB2018 Centralized logging

Bicep Infrastructure Files

All Bicep files live under threatatlas-app/infra/:

infra/
├── main.bicep                              # Orchestration - wires all modules
├── deploy.sh                               # One-command deploy script
├── parameters/
│   └── lista-preprod.parameters.json       # Pre-prod parameter values
└── modules/
    ├── acr.bicep                           # Azure Container Registry
    ├── container-app.bicep                 # Reusable Container App
    ├── container-app-environment.bicep     # Container Apps Environment + Log Analytics
    ├── keyvault.bicep                      # Key Vault + RBAC role assignment
    ├── managed-identity.bicep              # User-Assigned Managed Identity
    └── postgresql.bicep                    # PostgreSQL Flexible Server + DB + firewall

Design principles:

  • Each module is self-contained and reusable across environments
  • Secrets are injected via Key Vault references in the parameter file (no plaintext)
  • Managed identity for ACR pull (no admin credentials on the registry)
  • Key Vault RBAC-based access (no access policies)

Deployment Steps

Prerequisites

  • Azure CLI (az) installed and logged in
  • Docker installed for image builds
  • gh CLI for issue management
  • Access to the target Azure subscription
  • A pre-existing Key Vault with secrets bootstrapped (or provide them at deploy time)

Phase 1: Bootstrap Secrets

# Create a bootstrap Key Vault (if not already existing) and populate secrets
az keyvault create --name ta-bootstrap-kv --resource-group bootstrap-rg --location norwayeast
az keyvault secret set --vault-name ta-bootstrap-kv --name db-admin-password --value "<STRONG_PASSWORD>"
az keyvault secret set --vault-name ta-bootstrap-kv --name app-secret-key --value "$(openssl rand -hex 32)"
az keyvault secret set --vault-name ta-bootstrap-kv --name smtp-password --value "<SMTP_APP_PASSWORD>"

Then update lista-preprod.parameters.json Key Vault references with the actual subscription/RG/vault IDs.

Phase 2: Deploy Infrastructure + App

cd threatatlas-app/infra
bash deploy.sh --env lista-preprod --tag v1.0.0

This single command will:

  1. Create/ensure the resource group threatatlas-lista-preprod-rg
  2. Deploy all Bicep infrastructure (idempotent)
  3. Build backend and frontend Docker images
  4. Push images to ACR
  5. Update Container Apps with the new images
  6. Resolve the circular FRONTEND_URL dependency

Phase 3: Post-Deployment Verification

# Check backend health
BACKEND_URL=$(az containerapp show --name threatatlas-lista-preprod-backend \
  --resource-group threatatlas-lista-preprod-rg \
  --query "properties.configuration.ingress.fqdn" -o tsv)
curl -s "https://${BACKEND_URL}/health"

# Check frontend is serving
FRONTEND_URL=$(az containerapp show --name threatatlas-lista-preprod-frontend \
  --resource-group threatatlas-lista-preprod-rg \
  --query "properties.configuration.ingress.fqdn" -o tsv)
curl -s -o /dev/null -w "%{http_code}" "https://${FRONTEND_URL}"

# View backend logs
az containerapp logs show --name threatatlas-lista-preprod-backend \
  --resource-group threatatlas-lista-preprod-rg --follow

# Verify database migrations ran
az containerapp logs show --name threatatlas-lista-preprod-backend \
  --resource-group threatatlas-lista-preprod-rg --tail 50 | grep -i "alembic"

Environment Variables and Secrets

Variable Source Notes
DATABASE_URL Container App secret (from Bicep) PostgreSQL connection string with ?sslmode=require
SECRET_KEY Container App secret (from Bicep) JWT signing key -- generate with openssl rand -hex 32
DEBUG Env var = False Must be False in pre-prod
FRONTEND_URL Env var (set post-deploy by script) Used for CORS and invitation emails
SMTP_* Env vars + secret for password Email delivery for invitations
VITE_API_URL Build arg at Docker image build time Baked into frontend JS bundle

Rollback Procedure

# Roll back to a previous image tag
az containerapp update --name threatatlas-lista-preprod-backend \
  --resource-group threatatlas-lista-preprod-rg \
  --image <ACR_LOGIN_SERVER>/threatatlas-backend:<PREVIOUS_TAG>

az containerapp update --name threatatlas-lista-preprod-frontend \
  --resource-group threatatlas-lista-preprod-rg \
  --image <ACR_LOGIN_SERVER>/threatatlas-frontend:<PREVIOUS_TAG>

Remaining Work / Open Questions

  • Update lista-preprod.parameters.json with actual subscription ID and Key Vault resource IDs
  • Decide on custom domain + TLS certificate for pre-prod (optional: Azure-managed or bring-your-own)
  • Determine SMTP provider credentials for pre-prod invitation emails
  • Set up CI/CD pipeline (GitHub Actions) to automate deploy.sh on merge to a release branch
  • Change the default admin password (Admin@1234) on first deployment -- seed data creates this automatically
  • Consider adding a WAF / Azure Front Door for production (not needed for pre-prod)
  • Review PostgreSQL backup/retention settings for compliance
  • Make deploy.sh executable: chmod +x threatatlas-app/infra/deploy.sh

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions