PRODUCTION DEPLOYMENT BRIEF

ProjectForge

Enterprise project management with Okta SSO, SCIM provisioning, and Microsoft Teams integration — deployment-ready for Azure.

March 25, 2026 Prepared by Sean Brandom 2,000 Users
~$195
Monthly Run Cost
9 of 9
Security Controls Built In
2-3 Days
Time to Deploy
2
Pre-Launch Items (4 hrs)
1

Production Cost Analysis

Azure East US pricing for 2,000 licensed users. Infrastructure sized for ~600-800 concurrent sessions at peak (30-40% daily active).

What You Need to Launch
ComponentConfigurationMonthly
PostgreSQL Flexible ServerB_Standard_B2s (2 vCPU, 4GB RAM) 64GB storage$58
Container Apps1 vCPU / 2GB RAM 1-3 replicas, auto-scaling$86
Container RegistryBasic SKU 10GB included$5
Key VaultStandard tier Secrets access$1
Virtual NetworkVNet + private subnets$0
Log Analytics~5GB/mo ingestion Free tier$0
DNS / TLSCustom domain + managed cert$1
Bandwidth (egress)~500GB/mo at 40% daily active$43
Terraform StateBlob storage$1
Production Total~$195/mo
Internal network note: If this deploys within the organization's existing Azure tenant and users access via ExpressRoute or VPN, egress costs drop significantly — potentially to $0, bringing the total to ~$150/mo.
Okta SSO / SCIM
$0 — existing tenant
MS Teams Bot
$0 — free for Teams channel
SSL / TLS
$0 — Azure managed certs

Optional Security Hardening Add-Ons

Not required to launch. The application already ships with Okta SSO, RBAC, encrypted transport, security headers, private networking, Key Vault, and a full audit trail. These are additional layers for specific risk profiles.

A content delivery network that sits in front of the application and inspects every incoming request before it reaches the server. Includes a Web Application Firewall that blocks known attack patterns (SQL injection, cross-site scripting, bot abuse, DDoS floods).

You'd want this if...
The application is exposed to the public internet and receives traffic from untrusted networks — anyone on the internet can reach the login page.
You likely don't need this if...
Access is restricted to the corporate network (VPN or ExpressRoute). Users are all authenticated employees. Okta SSO already rejects unauthenticated requests at the login page before they touch any data. Azure Container Apps also includes built-in DDoS protection at the infrastructure layer.
Bottom line: If users access ProjectForge only from managed corporate devices on your network, this adds protection against a threat vector that is already limited by your network controls.

A small in-memory database used to coordinate rate limiting across multiple application replicas. Rate limiting prevents any single caller from flooding an API endpoint with requests.

You'd want this if...
You're running 3+ container replicas consistently, and the SCIM or Teams webhook endpoints are exposed to callers outside your trust boundary.
You likely don't need this if...
At 1-2 replicas (the expected baseline for 2,000 users), in-memory rate limiting works fine. The rate-limited endpoints (SCIM and Teams) are both authenticated with bearer tokens — an attacker would need a valid token to even reach the rate limiter. This is defense-in-depth, not an access control gap.
Bottom line: Only relevant if you scale beyond 2 replicas and your threat model includes authenticated callers abusing the SCIM or Teams APIs at high volume. For most deployments, this can wait.

Scans container images for known software vulnerabilities (CVEs) before and after deployment. Alerts you if a dependency has a published security issue.

You'd want this if...
Your compliance framework (SOC 2, FedRAMP, PCI-DSS) mandates automated container image scanning as a control.
You likely don't need this if...
You're running standard open-source dependencies (Node.js, PostgreSQL) and updating regularly. The risk is managed through your normal patching cycle. This provides visibility, not protection.
Bottom line: Nice to have for visibility into dependency vulnerabilities. Not blocking for launch.
Add-OnMonthlyRequired?
Azure Front Door + WAF$49No
Azure Cache for Redis$16No
Azure Defender for Cloud$15No
Production + all add-ons~$275/mo
Include all optional add-ons
Toggle to see total with hardening
~$195
per month

Cost Optimization Opportunities

$15-$20/mo
Reserved Instances
1-year commit on PostgreSQL saves 25-35% on database
Up to $43/mo
Internal Routing
ExpressRoute / VPN eliminates bandwidth egress costs
20-30%
Dev/Test Pricing
Azure Dev/Test subscription pricing if eligible
2

Security Assessment

ProjectForge ships with a comprehensive security stack. These are built in — not add-ons.

Okta SAML 2.0 + SCIM

Users authenticate through your existing Okta tenant. No local passwords stored.
User lifecycle (join, leave, role change) is fully automated from Okta via SCIM provisioning.

Role-Based Access Control

Admin, Project Manager, Contributor, Read Only — scoped to Org, Team, Mission, Project.
Permissions enforced server-side on every API call, not just in the UI.

Transport Security

HSTS with 2-year preload. TLS enforced on all connections.
All data in transit is encrypted. Browsers are instructed to never connect over plain HTTP.

Security Headers

CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy.
Standard protections against XSS, clickjacking, and content sniffing on every response.

Secrets Management

Azure Key Vault for all sensitive configuration.
Credentials never in code, config files, or environment variables on disk.

Network Isolation

Private VNet with dedicated subnets. PostgreSQL on private endpoint.
Database is not reachable from the public internet. Only the application container connects.

Full Audit Trail

Every create, update, delete action is recorded with actor and timestamp.
Available for compliance review. Who did what and when.

Rate Limiting

SCIM: 100 req/min. Teams API: 60 req/min per user.
Provisioning and webhook endpoints throttled to prevent abuse.

Infrastructure as Code

Terraform — all infrastructure version-controlled and auditable.
No manual configuration drift. Changes reviewed in PRs before applied.

Pre-Launch Housekeeping

Two items to address before go-live. Neither requires additional Azure services or spend.

Remove Demo Login Route

The demo used a simplified login that bypasses Okta. The route exists in the codebase, gated by an environment variable (DEMO_MODE). We remove it entirely from the production build so it cannot be accidentally enabled.

~2 hours $0

Document Backup / Recovery

Azure PostgreSQL has 7-day automatic backups configured. We write a one-page runbook: how to restore, expected recovery time, who to contact. Optionally increase retention to 35 days at no extra cost.

~2 hours $0

Post-Launch Considerations

Longer-term items that do not affect launch readiness.

Azure Monitor Alerts

Free with existing Log Analytics. Configure after launch once baseline traffic patterns are established. Alerts on failed auth, anomalous access.

Nonce-Based CSP

Current CSP allows inline scripts/styles (standard). A nonce-based CSP is stricter. Only relevant for non-Teams routes — Teams requires inline for iframe compatibility.

Customer-Managed Encryption Keys

Azure encrypts database storage at rest by default. Customer-managed keys only required if your compliance policy explicitly mandates them.

Penetration Testing

Standard practice before broad exposure. Typically $5K-$15K one-time. Schedule based on your security team's cadence.

3

Azure Deployment Process

5 phases across 2-3 business days. All infrastructure provisioned via Terraform — no manual Azure Portal clicks.

Prerequisites: Azure subscription (Contributor role), Azure CLI v2.50+, Terraform v1.5+, Docker, Okta admin access, DNS access, Git repo access.
1
Azure Foundation
Day 1 - Morning
1
Create Terraform state storage

Resource group + storage account for Terraform remote state.

# Create resource group for Terraform state az group create --name projectforge-state --location eastus # Create storage account az storage account create \ --name projectforgetfstate \ --resource-group projectforge-state \ --sku Standard_LRS --encryption-services blob # Create blob container az storage container create \ --name tfstate --account-name projectforgetfstate
2
Configure Terraform variables

Copy terraform.tfvars.example and populate with production values:

project_name = "projectforge" resource_group_name = "projectforge-rg" location = "eastus" db_admin_password = "<generate-strong-password>" nextauth_secret = "<openssl rand -base64 32>" nextauth_url = "https://projectforge.yourdomain.com" okta_client_id = "<from-okta-setup>" okta_client_secret = "<from-okta-setup>" okta_issuer = "https://your-org.okta.com" scim_bearer_token = "<openssl rand -base64 32>" image_name = "projectforge:latest"
3
Deploy infrastructure

Provisions VNet, PostgreSQL, Container Registry, Key Vault, Container Apps, and Log Analytics. ~15 minutes.

terraform init terraform plan -out=tfplan terraform apply tfplan
2
Application Build & Push
Day 1 - Afternoon
4
Build & push Docker image

Multi-stage build (Node 20 Alpine), pushed to Azure Container Registry.

REGISTRY=$(cd terraform && terraform output -raw registry_login_server) docker build -t $REGISTRY/projectforge:latest . az acr login --name projectforgeacr docker push $REGISTRY/projectforge:latest
5
Run database migrations

Prisma migrations against the Azure PostgreSQL instance.

DB_URL=$(cd terraform && terraform output -raw database_connection_string) DATABASE_URL=$DB_URL npx prisma migrate deploy
6
Verify container is running
az containerapp show \ --name projectforge \ --resource-group projectforge-rg \ --query "properties.runningStatus"
3
Okta Configuration
Day 2 - Morning
7
Configure SAML application in Okta
  • Applications → Create App Integration → SAML 2.0
  • SSO URL: https://projectforge.yourdomain.com/api/saml/callback
  • Audience URI: https://projectforge.yourdomain.com
  • Attribute Statements: firstName, lastName, email, department
  • Download X.509 Certificate, note IdP SSO URL and Issuer URI
8
Configure SCIM provisioning
  • Provisioning → Configure API Integration
  • SCIM base URL: https://projectforge.yourdomain.com/api/scim/v2
  • Auth: HTTP Header → Bearer Token
  • Enable: Create Users, Update Attributes, Deactivate Users
  • Push Groups to map Okta groups → ProjectForge roles
4
Teams Integration
Day 2 - Afternoon (Optional)
9
Register Azure Bot
  • Azure Portal → Bot Services → Create Azure Bot
  • Note App ID, generate Client Secret
  • Messaging endpoint: https://projectforge.yourdomain.com/api/teams/messages
10
Deploy Teams manifest

Update teams-manifest/manifest.json with production URLs and Bot App ID. Upload to Teams Admin Center or sideload for testing.

5
DNS, Validation & Go-Live
Day 2-3
11
Configure custom domain & TLS

Add hostname to Container App, create CNAME record, bind managed certificate.

az containerapp hostname add \ --name projectforge \ --resource-group projectforge-rg \ --hostname projectforge.yourdomain.com az containerapp hostname bind \ --name projectforge \ --resource-group projectforge-rg \ --hostname projectforge.yourdomain.com \ --environment projectforge-env \ --validation-method CNAME
12
Smoke test checklist
Login page loads — no demo accounts visible
Okta SSO authenticates and returns to dashboard
Create a test project successfully
Assign task → notification created
SCIM sync provisions new Okta user
Teams notification appears (if enabled)
Audit log records all actions with actor
HTTPS valid, no mixed content warnings
Unauthenticated requests redirect to login
DEMO_MODE env var is NOT set
4

Architecture Overview

Full technology stack and Azure infrastructure layout.

Technology Stack

Frontend
Next.js 16, React 19, TypeScript, Tailwind, shadcn/ui
Backend
Next.js API Routes, Prisma ORM
Database
PostgreSQL 16 (Azure Flexible Server)
Authentication
Okta SAML 2.0, SCIM 2.0
Authorization
RBAC — 4 roles, 4 scopes
Collaboration
MS Teams Bot, Adaptive Cards
Infrastructure
Azure Container Apps, Key Vault, VNet, ACR
IaC
Terraform (all infra codified)
Monitoring
Azure Log Analytics (30-day retention)

Azure Infrastructure

Azure Resource Group Container Apps Environment ProjectForge 1 vCPU / 2GB RAM • 1-3 replicas Auto-scaling • Port 3000 Key Vault Secrets Management PostgreSQL Flexible Server B_Standard_B2s • 64GB • Private Endpoint 7-day auto backup • TLS required Private Network Container Registry Basic SKU • 10GB Log Analytics 30-day retention 2,000 Users HTTPS Okta SSO SAML MS Teams Bot Bot Framework

Data Flow

1

Authentication: User → Okta SAML → ProjectForge validates assertion → JWT session created

2

Provisioning: Okta SCIM → automatic account creation and role assignment

3

Application: Container Apps (auto-scaling 1-3 replicas) → PostgreSQL over private VNet

4

Notifications: Bot Framework pushes Adaptive Cards to linked Teams users

5

Secrets: All credentials stored in Azure Key Vault, injected at container runtime