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:
- Create/ensure the resource group
threatatlas-lista-preprod-rg
- Deploy all Bicep infrastructure (idempotent)
- Build backend and frontend Docker images
- Push images to ACR
- Update Container Apps with the new images
- 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
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
Azure Resources
threatatlas-lista-preprod-pgthreatatlaslista-preprodacrthreatatlas-lista-preprod-envthreatatlas-lista-preprod-backendthreatatlas-lista-preprod-frontendta-lista-preprod-kvthreatatlas-lista-preprod-identitythreatatlas-lista-preprod-logsBicep Infrastructure Files
All Bicep files live under
threatatlas-app/infra/:Design principles:
Deployment Steps
Prerequisites
az) installed and logged inghCLI for issue managementPhase 1: Bootstrap Secrets
Then update
lista-preprod.parameters.jsonKey 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.0This single command will:
threatatlas-lista-preprod-rgPhase 3: Post-Deployment Verification
Environment Variables and Secrets
DATABASE_URL?sslmode=requireSECRET_KEYopenssl rand -hex 32DEBUGFalseFRONTEND_URLSMTP_*VITE_API_URLRollback Procedure
Remaining Work / Open Questions
lista-preprod.parameters.jsonwith actual subscription ID and Key Vault resource IDsdeploy.shon merge to a release branchAdmin@1234) on first deployment -- seed data creates this automaticallydeploy.shexecutable:chmod +x threatatlas-app/infra/deploy.sh