Add Azure deployment automation and documentation

- Add separate deployment pipeline (azure-pipelines-deploy.yml) for App Service deployment
- Add advanced pipeline with deployment slots (azure-pipelines-slots.yml)
- Restore azure-pipelines.yml to build-only (no deployment)
- Add comprehensive Azure setup documentation:
  - AZURE-NEW-SUBSCRIPTION-SETUP.md: Complete step-by-step Azure resource setup
  - AZURE-RESOURCES-OVERVIEW.md: Quick reference for all Azure resources
  - AZURE-ACR-SHARED-SETUP.md: Guide for shared Container Registry
  - AZURE-ACR-NAMING-RECOMMENDATION.md: Naming recommendations for Zuyderland
  - AZURE-PIPELINE-DEPLOYMENT.md: Automated deployment setup guide
  - AZURE-PIPELINE-QUICK-REFERENCE.md: Quick reference for pipeline variables
  - AZURE-PIPELINES-USAGE.md: Guide for using build and deployment pipelines
- Add setup script (scripts/setup-azure-resources.sh) for automated resource creation
- Support for shared ACR across multiple applications
This commit is contained in:
2026-01-21 23:03:48 +01:00
parent 52f851c1f3
commit 42a04e6cb3
10 changed files with 3176 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
# Azure Container Registry Naming Recommendation
Recommendations for naming your Azure Container Registry for Zuyderland Application Services.
## 🎯 Requirements
Azure Container Registry names must:
- Be **globally unique** (across all Azure subscriptions)
- Be **5-50 characters** long
- Contain **only lowercase alphanumeric characters** (no hyphens, underscores, or special characters)
- Be **descriptive** but not too long
## 💡 Recommended Options
### Option 1: `zuyderlandacr` ⭐ **RECOMMENDED**
**Pros:**
- ✅ Clear company identification
- ✅ Short and memorable (15 characters)
- ✅ Generic enough for all Application Services apps
- ✅ Easy to type and remember
- ✅ Professional appearance
**Cons:**
- ⚠️ Might be taken if Zuyderland already has an ACR
**Usage:**
```bash
ACR_NAME="zuyderlandacr"
# Images: zuyderlandacr.azurecr.io/cmdb-insight/backend:latest
```
### Option 2: `zuyderlandsharedacr`
**Pros:**
- ✅ Clearly indicates it's a shared registry
- ✅ Company identification
- ✅ Good for documentation ("shared ACR")
**Cons:**
- ⚠️ Longer (20 characters)
- ⚠️ "shared" might be redundant (ACRs are typically shared)
**Usage:**
```bash
ACR_NAME="zuyderlandsharedacr"
# Images: zuyderlandsharedacr.azurecr.io/cmdb-insight/backend:latest
```
### Option 3: `zyldacr` (Abbreviated)
**Pros:**
- ✅ Very short (7 characters)
- ✅ Easy to type
- ✅ Less likely to be taken
**Cons:**
- ⚠️ Less clear what "zyld" means
- ⚠️ Might not be obvious it's Zuyderland
**Usage:**
```bash
ACR_NAME="zyldacr"
# Images: zyldacr.azurecr.io/cmdb-insight/backend:latest
```
### Option 4: `zuyderlandappsvcsacr`
**Pros:**
- ✅ Includes department name (Application Services)
- ✅ Very specific
**Cons:**
- ⚠️ Long (23 characters)
- ⚠️ Less flexible if other departments want to use it
- ⚠️ Harder to type
**Usage:**
```bash
ACR_NAME="zuyderlandappsvcsacr"
# Images: zuyderlandappsvcsacr.azurecr.io/cmdb-insight/backend:latest
```
## 🏆 Final Recommendation
**Use: `zuyderlandacr`**
**Reasoning:**
1. **Clear and professional**: Immediately identifies as Zuyderland
2. **Appropriate length**: Not too short (unclear) or too long (hard to type)
3. **Generic enough**: Can be used by all Application Services applications
4. **Future-proof**: Works for any department or team within Zuyderland
5. **Standard pattern**: Follows common naming convention (`companynameacr`)
## 🔍 Check Availability
Before creating, check if the name is available:
```bash
# Try to check if name exists (will fail if available, which is good)
az acr show --name zuyderlandacr --resource-group dummy-rg 2>&1 | grep -q "could not be found" && echo "Name available!" || echo "Name might be taken"
# Or try to create (will fail if taken)
az acr check-name --name zuyderlandacr
```
## 📝 Alternative if Name is Taken
If `zuyderlandacr` is already taken, try:
1. `zuyderlandacr01` - Add number suffix
2. `zuyderlandacrprod` - Add environment suffix
3. `zyldacr` - Use abbreviation
4. `zuyderlandregistry` - Use full word "registry"
5. `zuyderlandcontainers` - Use "containers" instead of "acr"
## 🎯 Naming Pattern
For consistency across Zuyderland, consider this pattern:
```
zuyderlandacr ← Shared ACR for all apps (recommended)
zuyderlandacrdev ← Development ACR (if needed)
zuyderlandacrprod ← Production ACR (if separate)
```
**But for most cases, one shared ACR (`zuyderlandacr`) is sufficient.**
## 📋 Update Your Configuration
Once you've chosen a name, update:
### 1. Setup Script
```bash
# In scripts/setup-azure-resources.sh
ACR_NAME="zuyderlandacr"
ACR_RESOURCE_GROUP="rg-shared-services" # Or rg-zuyderland-shared
```
### 2. Pipeline Variables
```yaml
# In azure-pipelines.yml
variables:
acrName: 'zuyderlandacr'
repositoryName: 'cmdb-insight'
```
### 3. Build Scripts
```bash
# In scripts/build-and-push-azure.sh
export ACR_NAME="zuyderlandacr"
```
## ✅ Checklist
- [ ] Choose ACR name: `zuyderlandacr` (recommended)
- [ ] Check name availability
- [ ] Create ACR with chosen name
- [ ] Update all configuration files
- [ ] Document name for team
- [ ] Share ACR name with other Application Services teams
---
**Recommended: `zuyderlandacr`** - Clear, professional, and reusable for all Zuyderland Application Services applications.

View File

@@ -0,0 +1,317 @@
# Shared Azure Container Registry Setup
Guide for using a shared Azure Container Registry across multiple applications.
## 🎯 Why Share ACR?
**Benefits:**
-**Cost Savings**: One ACR for all applications (€5-20/month vs multiple ACRs)
-**Centralized Management**: All images in one place
-**Easier Collaboration**: Teams can share images
-**Better Resource Utilization**: More efficient use of storage
**How it works:**
- ACR is shared, but each application uses a **unique repository name**
- Repository name (`cmdb-insight`) separates your app from others
- Images are organized by application: `acr.azurecr.io/app-name/service:tag`
## 📦 ACR Structure
```
zuyderlandacr.azurecr.io/
├── cmdb-insight/ ← This application
│ ├── backend:latest
│ ├── backend:v1.0.0
│ ├── frontend:latest
│ └── frontend:v1.0.0
├── other-app/ ← Another application
│ ├── api:latest
│ └── web:latest
└── shared-services/ ← Shared base images
├── nginx:latest
└── node:20-alpine
```
## 🔧 Setup Options
### Option 1: Use Existing ACR (Recommended)
If you already have an ACR for other applications:
```bash
# Set your existing ACR details
ACR_NAME="your-existing-acr"
ACR_RESOURCE_GROUP="rg-shared-services" # Or wherever your ACR is
# Verify it exists
az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP
# Get login server
ACR_LOGIN_SERVER=$(az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP --query loginServer --output tsv)
echo "ACR Login Server: $ACR_LOGIN_SERVER"
```
**That's it!** Your images will be stored as:
- `your-existing-acr.azurecr.io/cmdb-insight/backend:latest`
- `your-existing-acr.azurecr.io/cmdb-insight/frontend:latest`
### Option 2: Create New Shared ACR
If you don't have an ACR yet, create one that can be shared:
```bash
# Set variables
ACR_NAME="zuyderlandacr" # Recommended: company name + "acr"
ACR_RESOURCE_GROUP="rg-shared-services" # Shared resource group
LOCATION="westeurope"
SKU="Standard" # Recommended for shared ACR
# Create resource group for shared services
az group create --name $ACR_RESOURCE_GROUP --location $LOCATION
# Create ACR
az acr create \
--resource-group $ACR_RESOURCE_GROUP \
--name $ACR_NAME \
--sku $SKU \
--admin-enabled true
# Verify
az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP
```
## 🚀 Using Shared ACR
### Build and Push Images
```bash
# Set ACR name
export ACR_NAME="zuyderlandacr"
export REPO_NAME="cmdb-insight" # This is your app identifier
# Build and push (repository name separates your app)
./scripts/build-and-push-azure.sh
# Images will be:
# - zuyderlandacr.azurecr.io/cmdb-insight/backend:latest
# - zuyderlandacr.azurecr.io/cmdb-insight/frontend:latest
```
### Configure App Services
```bash
# Backend App Service
az webapp config container set \
--name cmdb-backend-prod \
--resource-group rg-cmdb-insight-prod \
--docker-custom-image-name "zuyderlandacr.azurecr.io/cmdb-insight/backend:latest" \
--docker-registry-server-url "https://zuyderlandacr.azurecr.io"
# Frontend App Service
az webapp config container set \
--name cmdb-frontend-prod \
--resource-group rg-cmdb-insight-prod \
--docker-custom-image-name "zuyderlandacr.azurecr.io/cmdb-insight/frontend:latest" \
--docker-registry-server-url "https://zuyderlandacr.azurecr.io"
```
### Update Pipeline Variables
In `azure-pipelines.yml`:
```yaml
variables:
acrName: 'yourcompanyacr' # Shared ACR name
repositoryName: 'cmdb-insight' # Your app repository name
# ... other variables
```
## 🔐 Permissions
### Grant App Services Access to Shared ACR
```bash
# Get App Service Managed Identity
BACKEND_PRINCIPAL_ID=$(az webapp identity show \
--name cmdb-backend-prod \
--resource-group rg-cmdb-insight-prod \
--query principalId --output tsv)
# Get ACR Resource ID (from shared resource group)
ACR_ID=$(az acr show \
--name zuyderlandacr \
--resource-group rg-shared-services \
--query id --output tsv)
# Grant AcrPull permission
az role assignment create \
--assignee $BACKEND_PRINCIPAL_ID \
--role AcrPull \
--scope $ACR_ID
```
## 📊 Managing Multiple Applications
### List All Repositories
```bash
# See all applications in ACR
az acr repository list --name zuyderlandacr
# Output:
# cmdb-insight
# other-app
# shared-services
```
### List Images for This App
```bash
# Backend images
az acr repository show-tags \
--name zuyderlandacr \
--repository cmdb-insight/backend
# Frontend images
az acr repository show-tags \
--name zuyderlandacr \
--repository cmdb-insight/frontend
```
### Clean Up Old Images
```bash
# Delete old tags (keep last 10)
az acr repository show-tags \
--name zuyderlandacr \
--repository cmdb-insight/backend \
--orderby time_desc \
--query '[10:].name' \
--output tsv | \
xargs -I {} az acr repository delete \
--name zuyderlandacr \
--image cmdb-insight/backend:{} \
--yes
```
## 💰 Cost Optimization
### Shared ACR Costs
| SKU | Storage | Cost | Best For |
|-----|---------|------|----------|
| Basic | 10GB | €5/month | Small teams, few apps |
| Standard | 100GB | €20/month | **Recommended for shared ACR** |
| Premium | 500GB | €50/month | Large organizations |
**Recommendation**: Use **Standard** SKU for shared ACR:
- Enough storage for multiple applications
- Geo-replication available
- Good balance of cost and features
### Cost Savings Example
**Without sharing:**
- App 1 ACR: €20/month
- App 2 ACR: €20/month
- App 3 ACR: €20/month
- **Total: €60/month**
**With shared ACR:**
- Shared ACR (Standard): €20/month
- **Total: €20/month**
- **Savings: €40/month (67%)**
## 🎯 Best Practices
### 1. Naming Convention
Use consistent repository naming:
- `app-name/service:tag` (e.g., `cmdb-insight/backend:latest`)
- Avoid generic names like `backend`, `frontend`
- Include app identifier in repository name
### 2. Resource Group Organization
**Option A: Separate Resource Groups**
```
rg-shared-services/
└── ACR (shared)
rg-cmdb-insight-prod/
└── App-specific resources
```
**Option B: Single Resource Group**
```
rg-production/
├── ACR
├── App 1 resources
├── App 2 resources
└── App 3 resources
```
### 3. Access Control
- Use **Managed Identity** for App Services (recommended)
- Grant **AcrPull** role (not AcrPush) to App Services
- Use **Service Principals** for CI/CD pipelines
- Consider **Azure RBAC** for fine-grained access
### 4. Image Tagging Strategy
```bash
# Use semantic versioning
cmdb-insight/backend:v1.0.0
cmdb-insight/backend:v1.0.1
cmdb-insight/backend:latest
# Use build IDs for CI/CD
cmdb-insight/backend:12345
cmdb-insight/backend:latest
```
## 🔄 Migration from Dedicated ACR
If you have a dedicated ACR and want to migrate to shared:
```bash
# 1. Tag images with new repository name
docker tag oldacr.azurecr.io/backend:latest newacr.azurecr.io/cmdb-insight/backend:latest
docker tag oldacr.azurecr.io/frontend:latest newacr.azurecr.io/cmdb-insight/frontend:latest
# 2. Push to shared ACR
docker push newacr.azurecr.io/cmdb-insight/backend:latest
docker push newacr.azurecr.io/cmdb-insight/frontend:latest
# 3. Update App Services
az webapp config container set \
--name cmdb-backend-prod \
--resource-group rg-cmdb-insight-prod \
--docker-custom-image-name "newacr.azurecr.io/cmdb-insight/backend:latest"
# 4. Update pipeline variables
# 5. Test deployment
# 6. Delete old ACR (after verification)
```
## 📚 Related Documentation
- **`AZURE-NEW-SUBSCRIPTION-SETUP.md`** - Complete Azure setup guide
- **`AZURE-CONTAINER-REGISTRY.md`** - ACR setup and usage
- **`AZURE-PIPELINE-DEPLOYMENT.md`** - Automated deployment
## ✅ Checklist
- [ ] Decide: Use existing ACR or create new shared ACR
- [ ] Verify ACR exists or create new one
- [ ] Update pipeline variables with ACR name
- [ ] Grant App Services access to ACR
- [ ] Build and push images with repository name `cmdb-insight`
- [ ] Configure App Services to use shared ACR
- [ ] Test deployment
- [ ] Document ACR name for team
---
**💡 Remember**: The repository name (`cmdb-insight`) is what separates your application from others in the shared ACR!

View File

@@ -0,0 +1,943 @@
# Azure New Subscription Setup Guide
Complete guide for setting up all required Azure resources for CMDB Insight in a new Azure subscription.
## 📋 Overview
This guide will help you create and configure all necessary Azure resources to deploy the CMDB Insight application. The setup includes:
### Required Resources
1. **Resource Group** - Container for all resources
2. **Azure Container Registry (ACR)** - Store Docker images
3. **Azure Database for PostgreSQL** - Production database (recommended)
4. **Azure Key Vault** - Secure storage for secrets
5. **Azure App Service Plan** - Hosting plan for web apps
6. **Azure App Service (Backend)** - Backend API service
7. **Azure App Service (Frontend)** - Frontend web application
8. **Application Insights** - Monitoring and logging
9. **DNS & SSL** - Custom domain and HTTPS certificate
### Estimated Costs
- **Basic Setup (SQLite)**: €17-35/month
- **Recommended Setup (PostgreSQL)**: €36-62/month
---
## 🚀 Prerequisites
Before starting, ensure you have:
- [ ] Azure CLI installed (`az --version`)
- [ ] Azure subscription with appropriate permissions
- [ ] Docker installed (for local testing)
- [ ] Access to Azure Portal
- [ ] Jira credentials (OAuth client ID/secret or Personal Access Token)
### Install Azure CLI (if needed)
```bash
# macOS
brew install azure-cli
# Linux
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
# Windows
# Download from: https://aka.ms/installazurecliwindows
```
### Login to Azure
```bash
az login
az account list --output table
az account set --subscription "<subscription-id-or-name>"
```
---
## 📦 Step 1: Create Resource Group
Create a resource group to organize all resources:
```bash
# Set variables (customize as needed)
RESOURCE_GROUP="rg-cmdb-insight-prod"
LOCATION="westeurope" # or your preferred region
# Create resource group
az group create \
--name $RESOURCE_GROUP \
--location $LOCATION
# Verify
az group show --name $RESOURCE_GROUP
```
**Note**: Choose a location close to your users. Common options:
- `westeurope` (Netherlands, Germany)
- `northeurope` (Ireland, UK)
- `eastus` (US East)
---
## 🐳 Step 2: Create or Use Existing Azure Container Registry (ACR)
**Important**: Azure Container Registry can be **shared across multiple applications**. The repository name (`cmdb-insight`) is what separates this application from others in the same ACR.
### Option A: Use Existing ACR (Recommended if you have one)
If you already have an ACR for other applications, you can reuse it:
```bash
# Set variables - use your existing ACR name
ACR_NAME="your-existing-acr" # Your existing ACR name
ACR_RESOURCE_GROUP="rg-shared-services" # Resource group where ACR exists
# Verify ACR exists
az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP
# Get ACR login server
ACR_LOGIN_SERVER=$(az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP --query loginServer --output tsv)
echo "ACR Login Server: $ACR_LOGIN_SERVER"
```
**Benefits of reusing ACR**:
- ✅ Cost savings (one ACR for all apps)
- ✅ Centralized image management
- ✅ Easier to share images across teams
- ✅ Better resource utilization
### Option B: Create New ACR
If you don't have an ACR yet, create one:
```bash
# Set variables
ACR_NAME="yourcompanyacr" # Must be globally unique, lowercase, 5-50 chars, alphanumeric only
ACR_RESOURCE_GROUP="rg-shared-services" # Or use your app resource group
SKU="Standard" # Options: Basic, Standard, Premium
# Create resource group for shared services (if needed)
az group create --name $ACR_RESOURCE_GROUP --location westeurope
# Create ACR
az acr create \
--resource-group $ACR_RESOURCE_GROUP \
--name $ACR_NAME \
--sku $SKU \
--admin-enabled true
# Get ACR login server
ACR_LOGIN_SERVER=$(az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP --query loginServer --output tsv)
echo "ACR Login Server: $ACR_LOGIN_SERVER"
# Get admin credentials (save these securely)
az acr credential show --name $ACR_NAME
```
**ACR SKU Comparison**:
- **Basic**: €5/month - Development/test, 10GB storage
- **Standard**: €20/month - Production, 100GB storage, geo-replication (recommended)
- **Premium**: €50/month - Enterprise, 500GB storage, advanced security
**Repository Structure in ACR**:
```
yourcompanyacr.azurecr.io/
├── cmdb-insight/ ← This application
│ ├── backend:latest
│ └── frontend:latest
├── other-app/ ← Other applications
│ ├── api:latest
│ └── web:latest
└── shared-services/ ← Shared images
└── nginx:latest
```
### Test ACR Connection
```bash
# Login to ACR
az acr login --name $ACR_NAME
# Verify
az acr repository list --name $ACR_NAME
# List repositories (you'll see cmdb-insight after first push)
az acr repository list --name $ACR_NAME --output table
```
---
## 🗄️ Step 3: Create Azure Database for PostgreSQL
PostgreSQL is recommended for production. Alternatively, you can use SQLite with Azure Storage (see Step 3B).
### Step 3A: PostgreSQL (Recommended)
```bash
# Set variables
DB_SERVER_NAME="cmdb-postgres-prod" # Must be globally unique
RESOURCE_GROUP="rg-cmdb-insight-prod"
DB_ADMIN_USER="cmdbadmin"
DB_ADMIN_PASSWORD="<generate-secure-password>" # Use a strong password!
DB_NAME="cmdb"
SKU="Standard_B1ms" # Burstable tier, 1 vCore, 2GB RAM
# Generate secure password (save this!)
DB_ADMIN_PASSWORD=$(openssl rand -base64 32)
echo "Database Password: $DB_ADMIN_PASSWORD"
# Create PostgreSQL Flexible Server
az postgres flexible-server create \
--resource-group $RESOURCE_GROUP \
--name $DB_SERVER_NAME \
--location westeurope \
--admin-user $DB_ADMIN_USER \
--admin-password $DB_ADMIN_PASSWORD \
--sku-name $SKU \
--tier Burstable \
--storage-size 32 \
--version 15 \
--public-access 0.0.0.0 # Allow Azure services (restrict later if needed)
# Create database
az postgres flexible-server db create \
--resource-group $RESOURCE_GROUP \
--server-name $DB_SERVER_NAME \
--database-name $DB_NAME
# Get connection string
DB_CONNECTION_STRING="postgresql://${DB_ADMIN_USER}:${DB_ADMIN_PASSWORD}@${DB_SERVER_NAME}.postgres.database.azure.com:5432/${DB_NAME}?sslmode=require"
echo "Database Connection String: $DB_CONNECTION_STRING"
# Save connection details securely
echo "DB_HOST=${DB_SERVER_NAME}.postgres.database.azure.com" > .env.azure
echo "DB_USER=${DB_ADMIN_USER}" >> .env.azure
echo "DB_PASSWORD=${DB_ADMIN_PASSWORD}" >> .env.azure
echo "DB_NAME=${DB_NAME}" >> .env.azure
```
**PostgreSQL SKU Options**:
- **Standard_B1ms**: €20-30/month - 1 vCore, 2GB RAM (recommended for 20 users)
- **Standard_B2s**: €40-50/month - 2 vCores, 4GB RAM (for growth)
- **Standard_D2s_v3**: €100+/month - 2 vCores, 8GB RAM (high performance)
### Step 3B: SQLite with Azure Storage (Alternative)
If you prefer to use SQLite (simpler, but less scalable):
```bash
# Set variables
STORAGE_ACCOUNT_NAME="cmdbstorage$(openssl rand -hex 4)" # Must be globally unique, lowercase
RESOURCE_GROUP="rg-cmdb-insight-prod"
# Create storage account
az storage account create \
--resource-group $RESOURCE_GROUP \
--name $STORAGE_ACCOUNT_NAME \
--location westeurope \
--sku Standard_LRS
# Get storage account key
STORAGE_KEY=$(az storage account keys list \
--resource-group $RESOURCE_GROUP \
--account-name $STORAGE_ACCOUNT_NAME \
--query "[0].value" --output tsv)
echo "Storage Account: $STORAGE_ACCOUNT_NAME"
echo "Storage Key: $STORAGE_KEY"
```
**Note**: SQLite works but has limitations with concurrent users. PostgreSQL is recommended for production.
---
## 🔐 Step 4: Create Azure Key Vault
Key Vault securely stores secrets like API keys, passwords, and tokens.
```bash
# Set variables
KEY_VAULT_NAME="kv-cmdb-insight-prod" # Must be globally unique
RESOURCE_GROUP="rg-cmdb-insight-prod"
# Create Key Vault
az keyvault create \
--name $KEY_VAULT_NAME \
--resource-group $RESOURCE_GROUP \
--location westeurope \
--sku standard
# Verify
az keyvault show --name $KEY_VAULT_NAME --resource-group $RESOURCE_GROUP
```
### Add Secrets to Key Vault
```bash
# Set your actual values
JIRA_PAT="your-jira-personal-access-token"
SESSION_SECRET=$(openssl rand -hex 32)
JIRA_OAUTH_CLIENT_ID="your-oauth-client-id"
JIRA_OAUTH_CLIENT_SECRET="your-oauth-client-secret"
JIRA_SCHEMA_ID="your-schema-id"
# Add secrets
az keyvault secret set \
--vault-name $KEY_VAULT_NAME \
--name "JiraPat" \
--value "$JIRA_PAT"
az keyvault secret set \
--vault-name $KEY_VAULT_NAME \
--name "SessionSecret" \
--value "$SESSION_SECRET"
az keyvault secret set \
--vault-name $KEY_VAULT_NAME \
--name "JiraOAuthClientId" \
--value "$JIRA_OAUTH_CLIENT_ID"
az keyvault secret set \
--vault-name $KEY_VAULT_NAME \
--name "JiraOAuthClientSecret" \
--value "$JIRA_OAUTH_CLIENT_SECRET"
az keyvault secret set \
--vault-name $KEY_VAULT_NAME \
--name "JiraSchemaId" \
--value "$JIRA_SCHEMA_ID"
# If using PostgreSQL, add database password
az keyvault secret set \
--vault-name $KEY_VAULT_NAME \
--name "DatabasePassword" \
--value "$DB_ADMIN_PASSWORD"
```
---
## 📊 Step 5: Create Application Insights
Application Insights provides monitoring, logging, and performance metrics.
```bash
# Set variables
APP_INSIGHTS_NAME="appi-cmdb-insight-prod"
RESOURCE_GROUP="rg-cmdb-insight-prod"
# Create Application Insights
az monitor app-insights component create \
--app $APP_INSIGHTS_NAME \
--location westeurope \
--resource-group $RESOURCE_GROUP \
--application-type web
# Get Instrumentation Key
INSTRUMENTATION_KEY=$(az monitor app-insights component show \
--app $APP_INSIGHTS_NAME \
--resource-group $RESOURCE_GROUP \
--query instrumentationKey --output tsv)
echo "Instrumentation Key: $INSTRUMENTATION_KEY"
```
**Note**: Application Insights Basic tier is free up to 5GB/month, which is sufficient for most small applications.
---
## 🖥️ Step 6: Create App Service Plan
App Service Plan defines the compute resources for your web apps.
```bash
# Set variables
APP_SERVICE_PLAN_NAME="plan-cmdb-insight-prod"
RESOURCE_GROUP="rg-cmdb-insight-prod"
SKU="B1" # Basic tier, 1 vCore, 1.75GB RAM
# Create App Service Plan (Linux)
az appservice plan create \
--name $APP_SERVICE_PLAN_NAME \
--resource-group $RESOURCE_GROUP \
--sku $SKU \
--is-linux
# Verify
az appservice plan show --name $APP_SERVICE_PLAN_NAME --resource-group $RESOURCE_GROUP
```
**App Service Plan SKU Options**:
- **B1**: €15-25/month - 1 vCore, 1.75GB RAM (recommended for 20 users)
- **B2**: €30-40/month - 2 vCores, 3.5GB RAM
- **S1**: €50-70/month - 1 vCore, 1.75GB RAM (Standard tier, better performance)
---
## 🚀 Step 7: Create App Services (Backend & Frontend)
Create two web apps: one for backend API and one for frontend.
### Step 7A: Create Backend App Service
```bash
# Set variables
BACKEND_APP_NAME="cmdb-backend-prod" # Must be globally unique
RESOURCE_GROUP="rg-cmdb-insight-prod"
APP_SERVICE_PLAN_NAME="plan-cmdb-insight-prod"
ACR_NAME="cmdbinsightacr" # From Step 2
# Create backend web app
az webapp create \
--name $BACKEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--plan $APP_SERVICE_PLAN_NAME \
--deployment-container-image-name "${ACR_NAME}.azurecr.io/cmdb-insight/backend:latest"
# Enable Managed Identity (for ACR access)
az webapp identity assign \
--name $BACKEND_APP_NAME \
--resource-group $RESOURCE_GROUP
# Get Managed Identity Principal ID
BACKEND_PRINCIPAL_ID=$(az webapp identity show \
--name $BACKEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--query principalId --output tsv)
# Grant ACR pull permissions
ACR_ID=$(az acr show --name $ACR_NAME --resource-group $RESOURCE_GROUP --query id --output tsv)
az role assignment create \
--assignee $BACKEND_PRINCIPAL_ID \
--role AcrPull \
--scope $ACR_ID
# Configure container settings
az webapp config container set \
--name $BACKEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--docker-custom-image-name "${ACR_NAME}.azurecr.io/cmdb-insight/backend:latest" \
--docker-registry-server-url "https://${ACR_NAME}.azurecr.io"
# Set environment variables
az webapp config appsettings set \
--name $BACKEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--settings \
NODE_ENV=production \
PORT=3001 \
DATABASE_TYPE=postgres \
DATABASE_URL="@Microsoft.KeyVault(SecretUri=https://${KEY_VAULT_NAME}.vault.azure.net/secrets/DatabasePassword/)" \
JIRA_HOST=https://jira.zuyderland.nl \
JIRA_AUTH_METHOD=oauth \
JIRA_OAUTH_CLIENT_ID="@Microsoft.KeyVault(SecretUri=https://${KEY_VAULT_NAME}.vault.azure.net/secrets/JiraOAuthClientId/)" \
JIRA_OAUTH_CLIENT_SECRET="@Microsoft.KeyVault(SecretUri=https://${KEY_VAULT_NAME}.vault.azure.net/secrets/JiraOAuthClientSecret/)" \
JIRA_OAUTH_CALLBACK_URL="https://${BACKEND_APP_NAME}.azurewebsites.net/api/auth/callback" \
JIRA_SCHEMA_ID="@Microsoft.KeyVault(SecretUri=https://${KEY_VAULT_NAME}.vault.azure.net/secrets/JiraSchemaId/)" \
SESSION_SECRET="@Microsoft.KeyVault(SecretUri=https://${KEY_VAULT_NAME}.vault.azure.net/secrets/SessionSecret/)" \
FRONTEND_URL="https://${FRONTEND_APP_NAME}.azurewebsites.net" \
APPINSIGHTS_INSTRUMENTATIONKEY="${INSTRUMENTATION_KEY}"
# Grant Key Vault access to backend
az keyvault set-policy \
--name $KEY_VAULT_NAME \
--object-id $BACKEND_PRINCIPAL_ID \
--secret-permissions get list
# Enable HTTPS only
az webapp update \
--name $BACKEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--https-only true
```
### Step 7B: Create Frontend App Service
```bash
# Set variables
FRONTEND_APP_NAME="cmdb-frontend-prod" # Must be globally unique
RESOURCE_GROUP="rg-cmdb-insight-prod"
APP_SERVICE_PLAN_NAME="plan-cmdb-insight-prod"
ACR_NAME="cmdbinsightacr" # From Step 2
# Create frontend web app
az webapp create \
--name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--plan $APP_SERVICE_PLAN_NAME \
--deployment-container-image-name "${ACR_NAME}.azurecr.io/cmdb-insight/frontend:latest"
# Enable Managed Identity
az webapp identity assign \
--name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP
# Get Managed Identity Principal ID
FRONTEND_PRINCIPAL_ID=$(az webapp identity show \
--name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--query principalId --output tsv)
# Grant ACR pull permissions
az role assignment create \
--assignee $FRONTEND_PRINCIPAL_ID \
--role AcrPull \
--scope $ACR_ID
# Configure container settings
az webapp config container set \
--name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--docker-custom-image-name "${ACR_NAME}.azurecr.io/cmdb-insight/frontend:latest" \
--docker-registry-server-url "https://${ACR_NAME}.azurecr.io"
# Set environment variables
az webapp config appsettings set \
--name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--settings \
VITE_API_URL="https://${BACKEND_APP_NAME}.azurewebsites.net/api"
# Enable HTTPS only
az webapp update \
--name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--https-only true
```
---
## 🔄 Step 8: Build and Push Docker Images
Before the apps can run, you need to build and push Docker images to ACR.
### Option A: Using Script (Recommended)
```bash
# Navigate to project root
cd /path/to/cmdb-insight
# Set environment variables
export ACR_NAME="cmdbinsightacr" # Your ACR name
export REPO_NAME="cmdb-insight"
# Build and push
./scripts/build-and-push-azure.sh
```
### Option B: Manual Build and Push
```bash
# Set variables
ACR_NAME="cmdbinsightacr"
REGISTRY="${ACR_NAME}.azurecr.io"
REPO_NAME="cmdb-insight"
# Login to ACR
az acr login --name $ACR_NAME
# Build backend
docker build -t ${REGISTRY}/${REPO_NAME}/backend:latest \
-f backend/Dockerfile.prod ./backend
# Build frontend
docker build -t ${REGISTRY}/${REPO_NAME}/frontend:latest \
-f frontend/Dockerfile.prod ./frontend
# Push images
docker push ${REGISTRY}/${REPO_NAME}/backend:latest
docker push ${REGISTRY}/${REPO_NAME}/frontend:latest
# Verify
az acr repository list --name $ACR_NAME
az acr repository show-tags --name $ACR_NAME --repository ${REPO_NAME}/backend
az acr repository show-tags --name $ACR_NAME --repository ${REPO_NAME}/frontend
```
---
## 🌐 Step 9: Configure Custom Domain and SSL (Optional)
If you have a custom domain (e.g., `cmdb.yourcompany.com`):
### Step 9A: Add Custom Domain
```bash
# Set variables
FRONTEND_APP_NAME="cmdb-frontend-prod"
BACKEND_APP_NAME="cmdb-backend-prod"
CUSTOM_DOMAIN="cmdb.yourcompany.com"
RESOURCE_GROUP="rg-cmdb-insight-prod"
# Add custom domain to frontend
az webapp config hostname add \
--webapp-name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--hostname $CUSTOM_DOMAIN
# Add custom domain to backend (if needed)
az webapp config hostname add \
--webapp-name $BACKEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--hostname "api.${CUSTOM_DOMAIN}"
```
### Step 9B: Configure SSL Certificate
**Option 1: App Service Managed Certificate (Free, Recommended)**
```bash
# Create managed certificate for frontend
az webapp config ssl create \
--name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--hostname $CUSTOM_DOMAIN
# Bind certificate
az webapp config ssl bind \
--name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--certificate-thumbprint <thumbprint> \
--ssl-type SNI
```
**Option 2: Import Existing Certificate**
```bash
# Upload certificate
az webapp config ssl upload \
--name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--certificate-file /path/to/certificate.pfx \
--certificate-password <password>
```
**Note**: You'll need to update DNS records to point to your App Service. Get the IP address:
```bash
az webapp show --name $FRONTEND_APP_NAME --resource-group $RESOURCE_GROUP --query defaultHostName
```
---
## ✅ Step 10: Verify Deployment
### Check App Status
```bash
# Check backend
az webapp show --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP --query state
# Check frontend
az webapp show --name $FRONTEND_APP_NAME --resource-group $RESOURCE_GROUP --query state
# Start apps if needed
az webapp start --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP
az webapp start --name $FRONTEND_APP_NAME --resource-group $RESOURCE_GROUP
```
### Test Health Endpoints
```bash
# Backend health check
curl https://${BACKEND_APP_NAME}.azurewebsites.net/api/health
# Frontend
curl https://${FRONTEND_APP_NAME}.azurewebsites.net
```
### View Logs
```bash
# Backend logs
az webapp log tail --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP
# Frontend logs
az webapp log tail --name $FRONTEND_APP_NAME --resource-group $RESOURCE_GROUP
```
---
## 🔧 Step 11: Configure Database Schema
If using PostgreSQL, you need to initialize the database schema:
```bash
# Connect to database and run schema initialization
# Option 1: Using psql
psql "postgresql://${DB_ADMIN_USER}:${DB_ADMIN_PASSWORD}@${DB_SERVER_NAME}.postgres.database.azure.com:5432/${DB_NAME}?sslmode=require"
# Option 2: Using Azure Cloud Shell or local script
# The application will create tables automatically on first run
# Or use the migration scripts in backend/scripts/
```
**Note**: The application will automatically create the required database schema on first startup if it doesn't exist.
---
## 📝 Step 12: Update Environment Variables (If Needed)
If you need to update any configuration:
```bash
# Update backend settings
az webapp config appsettings set \
--name $BACKEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--settings \
NEW_SETTING=value
# Update frontend settings
az webapp config appsettings set \
--name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--settings \
VITE_API_URL="https://${BACKEND_APP_NAME}.azurewebsites.net/api"
# Restart apps to apply changes
az webapp restart --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP
az webapp restart --name $FRONTEND_APP_NAME --resource-group $RESOURCE_GROUP
```
---
## 🔄 Step 13: Set Up CI/CD with Automated Deployment
### Azure DevOps Pipeline Setup
The project includes an automated pipeline that builds, pushes, and deploys your application.
1. **Create Service Connections**:
**A) Docker Registry Connection (for building images)**:
- Go to Azure DevOps → Project Settings → Service connections
- Create new **Docker Registry** connection
- Select **Azure Container Registry**
- Choose your subscription and ACR
- Name: `zuyderland-cmdb-acr-connection` (or match your variable)
**B) Azure Resource Manager Connection (for deployment)**:
- Create new **Azure Resource Manager** connection
- Select your subscription
- Name: `zuyderland-cmdb-subscription` (or match your variable)
2. **Configure Pipeline Variables**:
Update `azure-pipelines.yml` with your values:
```yaml
variables:
acrName: 'cmdbinsightacr' # Your ACR name
resourceGroup: 'rg-cmdb-insight-prod' # Your resource group
backendAppName: 'cmdb-backend-prod' # Your backend app name
frontendAppName: 'cmdb-frontend-prod' # Your frontend app name
azureSubscription: 'zuyderland-cmdb-subscription' # Azure service connection
dockerRegistryServiceConnection: 'zuyderland-cmdb-acr-connection'
```
3. **Create Environment**:
- Go to **Pipelines** → **Environments**
- Create environment: `production`
- (Optional) Add approvals for manual deployment control
4. **Run Pipeline**:
- Push to `main` branch → **Automatically builds AND deploys**
- Pipeline will:
- Build Docker images
- Push to ACR
- Deploy to App Services
- Verify deployment
### Zero-Downtime Deployment (Optional)
For production with zero downtime, use deployment slots:
1. **Create Staging Slots**:
```bash
az webapp deployment slot create \
--name $BACKEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--slot staging
az webapp deployment slot create \
--name $FRONTEND_APP_NAME \
--resource-group $RESOURCE_GROUP \
--slot staging
```
2. **Use Advanced Pipeline**:
- Use `azure-pipelines-slots.yml` instead
- Deploys to staging first
- Swaps to production after verification
**See `docs/AZURE-PIPELINE-DEPLOYMENT.md` for complete setup guide.**
---
## 🛠️ Troubleshooting
### App Service Won't Start
```bash
# Check logs
az webapp log tail --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP
# Check container logs
az webapp log show --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP
# Check app status
az webapp show --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP --query state
```
### ACR Authentication Issues
```bash
# Re-authenticate
az acr login --name $ACR_NAME
# Check Managed Identity permissions
az role assignment list --assignee $BACKEND_PRINCIPAL_ID --scope $ACR_ID
```
### Database Connection Issues
```bash
# Test database connection
psql "postgresql://${DB_ADMIN_USER}:${DB_ADMIN_PASSWORD}@${DB_SERVER_NAME}.postgres.database.azure.com:5432/${DB_NAME}?sslmode=require"
# Check firewall rules
az postgres flexible-server firewall-rule list \
--resource-group $RESOURCE_GROUP \
--name $DB_SERVER_NAME
```
### Key Vault Access Issues
```bash
# Check Key Vault policies
az keyvault show --name $KEY_VAULT_NAME --resource-group $RESOURCE_GROUP
# Verify Managed Identity has access
az keyvault show-policy --name $KEY_VAULT_NAME
```
---
## 📊 Monitoring
### Application Insights
1. Go to Azure Portal → Application Insights → Your app
2. View:
- **Live Metrics**: Real-time performance
- **Application Map**: Service dependencies
- **Logs**: Query application logs
- **Metrics**: Performance metrics
### Set Up Alerts
```bash
# Create alert for app downtime
az monitor metrics alert create \
--name "Backend-Down" \
--resource-group $RESOURCE_GROUP \
--scopes "/subscriptions/<subscription-id>/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Web/sites/${BACKEND_APP_NAME}" \
--condition "avg AvailabilityPercentage < 99" \
--window-size 5m \
--evaluation-frequency 1m
```
---
## 💰 Cost Optimization
### Current Setup Costs
| Resource | SKU | Estimated Monthly Cost |
|----------|-----|------------------------|
| App Service Plan | B1 | €15-25 |
| PostgreSQL | Standard_B1ms | €20-30 |
| Container Registry | Basic | €5 |
| Key Vault | Standard | €1-2 |
| Application Insights | Basic | €0-5 (free tier) |
| **Total** | | **€41-67/month** |
### Cost Saving Tips
1. **Use Basic tier ACR** for development (€5 vs €20)
2. **Application Insights Basic** is free up to 5GB/month
3. **Stop App Services** when not in use (dev/test environments)
4. **Use SQLite** instead of PostgreSQL (saves €20-30/month, but less scalable)
---
## 📚 Next Steps
1. **Configure DNS**: Point your domain to App Service
2. **Set up SSL**: Configure HTTPS certificate
3. **Test Application**: Verify all features work
4. **Set up Monitoring**: Configure alerts
5. **Document Access**: Share URLs and credentials with team
6. **Backup Strategy**: Plan for database backups (if needed)
---
## 🔗 Useful Commands Reference
```bash
# List all resources
az resource list --resource-group $RESOURCE_GROUP --output table
# Get resource IDs
az acr show --name $ACR_NAME --resource-group $RESOURCE_GROUP --query id
az postgres flexible-server show --name $DB_SERVER_NAME --resource-group $RESOURCE_GROUP --query id
# Export resource configuration
az group export --name $RESOURCE_GROUP --output-file resources.json
# Delete all resources (careful!)
az group delete --name $RESOURCE_GROUP --yes --no-wait
```
---
## 📖 Additional Resources
- [Azure App Service Documentation](https://docs.microsoft.com/azure/app-service/)
- [Azure Container Registry Documentation](https://docs.microsoft.com/azure/container-registry/)
- [Azure Database for PostgreSQL Documentation](https://docs.microsoft.com/azure/postgresql/)
- [Azure Key Vault Documentation](https://docs.microsoft.com/azure/key-vault/)
- [Application Insights Documentation](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview)
---
## ✅ Deployment Checklist
- [ ] Resource Group created
- [ ] Azure Container Registry created and accessible
- [ ] PostgreSQL database created (or SQLite storage configured)
- [ ] Key Vault created with all secrets
- [ ] Application Insights created
- [ ] App Service Plan created
- [ ] Backend App Service created and configured
- [ ] Frontend App Service created and configured
- [ ] Docker images built and pushed to ACR
- [ ] Apps started and running
- [ ] Health checks passing
- [ ] Custom domain configured (if applicable)
- [ ] SSL certificate configured (if applicable)
- [ ] Monitoring and alerts configured
- [ ] Team access configured
- [ ] Documentation updated
---
**🎉 Congratulations! Your CMDB Insight application is now deployed to Azure!**
For questions or issues, refer to:
- `AZURE-APP-SERVICE-DEPLOYMENT.md` - Detailed App Service deployment guide
- `AZURE-CONTAINER-REGISTRY.md` - ACR setup and usage
- `PRODUCTION-DEPLOYMENT.md` - General production deployment guide

View File

@@ -0,0 +1,337 @@
# Azure Pipeline Automated Deployment Guide
Complete guide for setting up automated deployment from Azure DevOps Pipeline to Azure App Services.
## 📋 Overview
The enhanced `azure-pipelines.yml` now includes:
-**Build Stage**: Builds and pushes Docker images to ACR
-**Deploy Stage**: Automatically deploys to Azure App Services
-**Verification**: Health checks after deployment
## 🚀 Quick Setup
### Step 1: Configure Pipeline Variables
Update the variables in `azure-pipelines.yml`:
```yaml
variables:
# Azure Container Registry
acrName: 'zdlas' # Your ACR name
repositoryName: 'cmdb-insight'
dockerRegistryServiceConnection: 'zuyderland-cmdb-acr-connection'
# Azure App Service
resourceGroup: 'rg-cmdb-insight-prod' # Your resource group
backendAppName: 'cmdb-backend-prod' # Your backend app name
frontendAppName: 'cmdb-frontend-prod' # Your frontend app name
azureSubscription: 'zuyderland-cmdb-subscription' # Azure service connection
# Deployment
deployToProduction: true # Set false to skip deployment
useDeploymentSlots: false # Set true for zero-downtime deployment
```
### Step 2: Create Azure Service Connection
You need an Azure service connection for App Service deployment:
1. **Go to Azure DevOps** → Your Project
2. **Project Settings****Service connections****New service connection**
3. Choose **Azure Resource Manager**
4. Select:
- **Authentication method**: Managed identity (recommended) or Service principal
- **Azure subscription**: Your subscription
- **Resource group**: Your resource group (optional)
5. **Service connection name**: `zuyderland-cmdb-subscription` (match the variable name)
6. Click **Save**
### Step 3: Configure Environment
The pipeline uses an `environment` called `production`:
1. **Go to Pipelines****Environments**
2. Click **Create environment**
3. Name: `production`
4. Add **Approvals and checks** (optional):
- **Approvals**: Require manual approval before deployment
- **Gate**: Health checks before deployment
### Step 4: Run Pipeline
The pipeline will automatically:
1. Build Docker images
2. Push to ACR
3. Deploy to App Services
4. Verify deployment
**Trigger automatically on:**
- Push to `main` branch
- Git tags starting with `v*`
## 🔧 Configuration Options
### Enable/Disable Deployment
To skip deployment (only build images):
```yaml
variables:
deployToProduction: false
```
Or use pipeline variables in Azure DevOps:
1. Go to **Pipelines** → Your pipeline → **Edit**
2. Click **Variables**
3. Add variable: `deployToProduction` = `false`
### Use Specific Image Tag
By default, the pipeline deploys the `latest` tag. To deploy a specific version:
```yaml
# In the Deploy stage, change:
containers: '$(acrName).azurecr.io/$(repositoryName)/backend:$(imageTag)'
```
This will deploy the specific build ID instead of `latest`.
## 🎯 Zero-Downtime Deployment (Deployment Slots)
For production deployments without downtime, use deployment slots:
### Step 1: Create Deployment Slots
```bash
# Create staging slot for backend
az webapp deployment slot create \
--name cmdb-backend-prod \
--resource-group rg-cmdb-insight-prod \
--slot staging
# Create staging slot for frontend
az webapp deployment slot create \
--name cmdb-frontend-prod \
--resource-group rg-cmdb-insight-prod \
--slot staging
```
### Step 2: Update Pipeline for Slots
Create `azure-pipelines-slots.yml` (see advanced example below) or modify the existing pipeline:
```yaml
- task: AzureWebAppContainer@1
displayName: 'Deploy to Staging Slot'
inputs:
azureSubscription: '$(azureSubscription)'
appName: '$(backendAppName)'
deployToSlotOrASE: true
resourceGroupName: '$(resourceGroup)'
slotName: 'staging'
containers: '$(acrName).azurecr.io/$(repositoryName)/backend:latest'
- task: AzureCLI@2
displayName: 'Swap Staging to Production'
inputs:
azureSubscription: '$(azureSubscription)'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
az webapp deployment slot swap \
--name $(backendAppName) \
--resource-group $(resourceGroup) \
--slot staging \
--target-slot production
```
## 📊 Pipeline Stages
### Stage 1: Build
**What it does:**
- Builds backend Docker image
- Builds frontend Docker image
- Pushes both to ACR with tags: `$(Build.BuildId)` and `latest`
**Output:**
- `backendImage`: Full image URL for backend
- `frontendImage`: Full image URL for frontend
### Stage 2: Deploy
**What it does:**
- Deploys backend container to App Service
- Deploys frontend container to App Service
- Restarts both App Services
- Verifies deployment with health checks
**Conditions:**
- Only runs if `deployToProduction = true`
- Only runs if Build stage succeeded
### Stage 3: Verify
**What it does:**
- Checks backend health endpoint (`/api/health`)
- Checks frontend accessibility
- Reports status
## 🔐 Permissions Required
The Azure service connection needs:
1. **App Service Contributor** role on:
- Backend App Service
- Frontend App Service
- App Service Plan
2. **ACR Pull** permissions (if using Managed Identity):
- Already configured via Managed Identity on App Services
### Grant Permissions
```bash
# Get service principal ID from Azure DevOps service connection
# Then grant permissions:
az role assignment create \
--assignee <service-principal-id> \
--role "Website Contributor" \
--scope /subscriptions/<subscription-id>/resourceGroups/rg-cmdb-insight-prod
```
## 🛠️ Troubleshooting
### Deployment Fails: "Service connection not found"
**Solution:**
- Verify service connection name matches `azureSubscription` variable
- Check service connection exists in Project Settings → Service connections
- Verify service connection has correct permissions
### Deployment Fails: "App Service not found"
**Solution:**
- Verify `backendAppName` and `frontendAppName` variables are correct
- Check `resourceGroup` variable matches your resource group
- Verify App Services exist in Azure
### Images Not Updating
**Solution:**
- Check if images were pushed to ACR successfully
- Verify App Service is pulling from correct ACR
- Check container settings in App Service configuration
- Ensure Managed Identity has ACR pull permissions
### Health Check Fails
**Solution:**
- Wait longer (apps may need time to start)
- Check App Service logs: `az webapp log tail`
- Verify health endpoint exists: `/api/health`
- Check environment variables are configured correctly
## 📝 Manual Deployment (Alternative)
If you prefer manual deployment after pipeline builds:
```bash
# After pipeline builds images, manually deploy:
# Restart backend to pull latest image
az webapp restart \
--name cmdb-backend-prod \
--resource-group rg-cmdb-insight-prod
# Restart frontend to pull latest image
az webapp restart \
--name cmdb-frontend-prod \
--resource-group rg-cmdb-insight-prod
```
## 🎯 Best Practices
### 1. Use Deployment Slots for Production
- Deploy to staging slot first
- Test in staging
- Swap to production when ready
### 2. Use Specific Tags for Production
Instead of `latest`, use version tags:
```yaml
containers: '$(acrName).azurecr.io/$(repositoryName)/backend:v1.0.0'
```
### 3. Add Approvals for Production
Configure environment approvals:
- Go to **Pipelines****Environments****production**
- Add **Approvals** check
- Require manual approval before deployment
### 4. Monitor Deployments
- Set up alerts in Application Insights
- Monitor pipeline runs
- Check deployment logs regularly
### 5. Rollback Strategy
If deployment fails:
```bash
# Rollback to previous image
az webapp config container set \
--name cmdb-backend-prod \
--resource-group rg-cmdb-insight-prod \
--docker-custom-image-name <previous-image-tag>
```
## 🔄 Workflow Example
### Typical Development Workflow
1. **Developer pushes code** to `main` branch
2. **Pipeline triggers automatically**
3. **Build stage**: Builds and pushes images
4. **Deploy stage**: Deploys to App Services
5. **Verify stage**: Checks health
6. **Application updated** - ready to use!
### Release Workflow
1. **Create release tag**: `git tag v1.0.0 && git push origin v1.0.0`
2. **Pipeline triggers** with tag
3. **Build stage**: Builds versioned images (`v1.0.0`)
4. **Deploy stage**: Deploys to staging slot
5. **Manual approval** (if configured)
6. **Swap to production**: Zero-downtime deployment
7. **Verify**: Health checks confirm success
## 📚 Related Documentation
- **`AZURE-NEW-SUBSCRIPTION-SETUP.md`** - Initial Azure setup
- **`AZURE-APP-SERVICE-DEPLOYMENT.md`** - Manual deployment guide
- **`AZURE-CONTAINER-REGISTRY.md`** - ACR setup
- **`AZURE-DEVOPS-SETUP.md`** - Pipeline setup basics
## ✅ Checklist
- [ ] Azure service connection created
- [ ] Pipeline variables configured
- [ ] Environment `production` created
- [ ] App Services exist in Azure
- [ ] Permissions configured
- [ ] Pipeline tested successfully
- [ ] Deployment verified
- [ ] Health checks passing
---
**🎉 Your automated deployment pipeline is ready!**
Every push to `main` will now automatically build and deploy your application.

View File

@@ -0,0 +1,152 @@
# Azure Pipeline Quick Reference
Quick reference for configuring and using the automated deployment pipeline.
## 📋 Pipeline Variables
Update these in `azure-pipelines.yml`:
| Variable | Description | Example |
|----------|-------------|---------|
| `acrName` | Azure Container Registry name | `cmdbinsightacr` |
| `repositoryName` | Docker repository name | `cmdb-insight` |
| `dockerRegistryServiceConnection` | ACR service connection name | `zuyderland-cmdb-acr-connection` |
| `resourceGroup` | Azure resource group | `rg-cmdb-insight-prod` |
| `backendAppName` | Backend App Service name | `cmdb-backend-prod` |
| `frontendAppName` | Frontend App Service name | `cmdb-frontend-prod` |
| `azureSubscription` | Azure service connection for deployment | `zuyderland-cmdb-subscription` |
| `deployToProduction` | Enable/disable deployment | `true` or `false` |
| `useDeploymentSlots` | Use staging slots for zero-downtime | `true` or `false` |
## 🔧 Required Service Connections
### 1. Docker Registry Connection
**Purpose**: Push Docker images to ACR
**Setup**:
- Type: Docker Registry → Azure Container Registry
- Name: Match `dockerRegistryServiceConnection` variable
- Subscription: Your Azure subscription
- Registry: Your ACR
### 2. Azure Resource Manager Connection
**Purpose**: Deploy to App Services
**Setup**:
- Type: Azure Resource Manager
- Name: Match `azureSubscription` variable
- Subscription: Your Azure subscription
- Authentication: Managed Identity (recommended) or Service Principal
## 🚀 Pipeline Stages
### 1. Build Stage
- Builds backend Docker image
- Builds frontend Docker image
- Pushes both to ACR with tags: `$(Build.BuildId)` and `latest`
### 2. Deploy Stage
- Deploys backend to App Service
- Deploys frontend to App Service
- Restarts both services
- Verifies deployment
### 3. Verify Stage
- Health check on backend (`/api/health`)
- Accessibility check on frontend
- Reports status
## 🎯 Triggers
**Automatic triggers:**
- Push to `main` branch
- Git tags starting with `v*` (e.g., `v1.0.0`)
**Manual trigger:**
- Go to Pipelines → Your pipeline → Run pipeline
## 📝 Common Commands
### Check Pipeline Status
```bash
# View in Azure DevOps Portal
# Or use Azure CLI (if configured)
az pipelines runs list --organization <org> --project <project>
```
### View Pipeline Logs
- Go to Azure DevOps → Pipelines → Select run → View logs
### Cancel Running Pipeline
- Go to Azure DevOps → Pipelines → Select run → Cancel
## 🔄 Deployment Flow
```
Code Push → Build Images → Push to ACR → Deploy to App Services → Verify
```
**With Slots:**
```
Code Push → Build Images → Push to ACR → Deploy to Staging → Swap to Production → Verify
```
## ⚙️ Configuration Examples
### Basic Deployment (Current)
```yaml
deployToProduction: true
useDeploymentSlots: false
```
→ Direct deployment to production
### Zero-Downtime Deployment
```yaml
deployToProduction: true
useDeploymentSlots: true
```
→ Deploy to staging, then swap to production
### Build Only (No Deployment)
```yaml
deployToProduction: false
```
→ Only build and push images, don't deploy
## 🛠️ Troubleshooting
### Pipeline Fails: "Service connection not found"
- Check service connection name matches variable
- Verify connection exists in Project Settings
### Deployment Fails: "App Service not found"
- Verify app names match your Azure resources
- Check resource group name is correct
### Images Not Updating
- Check ACR has new images
- Verify App Service container settings
- Check Managed Identity has ACR pull permissions
## 📚 Related Files
- **`azure-pipelines.yml`** - Main pipeline (basic deployment)
- **`azure-pipelines-slots.yml`** - Advanced pipeline (with slots)
- **`docs/AZURE-PIPELINE-DEPLOYMENT.md`** - Complete setup guide
- **`docs/AZURE-NEW-SUBSCRIPTION-SETUP.md`** - Initial Azure setup
## ✅ Checklist
- [ ] Service connections created
- [ ] Pipeline variables configured
- [ ] Environment `production` created
- [ ] App Services exist in Azure
- [ ] Pipeline tested successfully
- [ ] Deployment verified
- [ ] Health checks passing
---
**Quick Start**: Update variables in `azure-pipelines.yml` and push to `main` branch!

View File

@@ -0,0 +1,222 @@
# Azure Pipelines Usage Guide
Guide for using the separate build and deployment pipelines.
## 📋 Pipeline Files
### 1. `azure-pipelines.yml` - Build and Push Images
**Purpose**: Builds Docker images and pushes them to Azure Container Registry.
**What it does:**
- ✅ Builds backend Docker image
- ✅ Builds frontend Docker image
- ✅ Pushes both to ACR with tags: `$(Build.BuildId)` and `latest`
**When to use:**
- First time setup (to test image building)
- After code changes (to build new images)
- Before deployment (to ensure images are in ACR)
**Configuration:**
```yaml
variables:
acrName: 'zdlas' # Your ACR name
repositoryName: 'cmdb-insight'
dockerRegistryServiceConnection: 'zuyderland-cmdb-acr-connection'
```
### 2. `azure-pipelines-deploy.yml` - Deploy to App Service
**Purpose**: Deploys existing images from ACR to Azure App Services.
**What it does:**
- ✅ Deploys backend container to App Service
- ✅ Deploys frontend container to App Service
- ✅ Restarts both App Services
- ✅ Verifies deployment with health checks
**When to use:**
- After images are built and pushed to ACR
- When you want to deploy/update the application
- For production deployments
**Configuration:**
```yaml
variables:
acrName: 'zdlas' # Your ACR name
resourceGroup: 'rg-cmdb-insight-prod' # Your resource group
backendAppName: 'cmdb-backend-prod' # Your backend app name
frontendAppName: 'cmdb-frontend-prod' # Your frontend app name
azureSubscription: 'zuyderland-cmdb-subscription' # Azure service connection
imageTag: 'latest' # Image tag to deploy
```
## 🚀 Workflow
### Step 1: Build and Push Images
1. **Configure `azure-pipelines.yml`**:
- Update `acrName` with your ACR name
- Update `dockerRegistryServiceConnection` with your service connection name
2. **Create Pipeline in Azure DevOps**:
- Go to **Pipelines****New pipeline**
- Select **Existing Azure Pipelines YAML file**
- Choose `azure-pipelines.yml`
- Run the pipeline
3. **Verify Images in ACR**:
```bash
az acr repository list --name zdlas
az acr repository show-tags --name zdlas --repository cmdb-insight/backend
az acr repository show-tags --name zdlas --repository cmdb-insight/frontend
```
### Step 2: Deploy Application
1. **Ensure App Services exist**:
- Backend App Service: `cmdb-backend-prod`
- Frontend App Service: `cmdb-frontend-prod`
- See `AZURE-NEW-SUBSCRIPTION-SETUP.md` for setup instructions
2. **Configure `azure-pipelines-deploy.yml`**:
- Update all variables with your Azure resource names
- Create Azure service connection for App Service deployment
- Create `production` environment in Azure DevOps
3. **Create Deployment Pipeline**:
- Go to **Pipelines** → **New pipeline**
- Select **Existing Azure Pipelines YAML file**
- Choose `azure-pipelines-deploy.yml`
- Run the pipeline
4. **Verify Deployment**:
- Check backend: `https://cmdb-backend-prod.azurewebsites.net/api/health`
- Check frontend: `https://cmdb-frontend-prod.azurewebsites.net`
## 🔧 Setup Requirements
### For Build Pipeline (`azure-pipelines.yml`)
**Required:**
- ✅ Docker Registry service connection (for ACR)
- ✅ ACR exists and is accessible
- ✅ Service connection has push permissions
**Setup:**
1. Create Docker Registry service connection:
- **Project Settings** → **Service connections** → **New service connection**
- Choose **Docker Registry** → **Azure Container Registry**
- Select your ACR
- Name: `zuyderland-cmdb-acr-connection`
### For Deployment Pipeline (`azure-pipelines-deploy.yml`)
**Required:**
- ✅ Azure Resource Manager service connection
- ✅ App Services exist in Azure
- ✅ `production` environment created in Azure DevOps
- ✅ Images exist in ACR
**Setup:**
1. Create Azure service connection:
- **Project Settings** → **Service connections** → **New service connection**
- Choose **Azure Resource Manager**
- Select your subscription
- Name: `zuyderland-cmdb-subscription`
2. Create environment:
- **Pipelines** → **Environments** → **Create environment**
- Name: `production`
- (Optional) Add approvals for manual control
## 📝 Typical Usage Scenarios
### Scenario 1: First Time Setup
```bash
# 1. Build and push images
# Run azure-pipelines.yml → Images in ACR
# 2. Create App Services (manual or via script)
# See AZURE-NEW-SUBSCRIPTION-SETUP.md
# 3. Deploy application
# Run azure-pipelines-deploy.yml → App deployed
```
### Scenario 2: Code Update
```bash
# 1. Push code to main branch
git push origin main
# 2. Build pipeline runs automatically
# azure-pipelines.yml → New images in ACR
# 3. Deploy new version
# Run azure-pipelines-deploy.yml → App updated
```
### Scenario 3: Deploy Specific Version
```bash
# 1. Update azure-pipelines-deploy.yml
imageTag: 'v1.0.0' # Or specific build ID
# 2. Run deployment pipeline
# Deploys specific version
```
## 🔄 Combining Pipelines (Future)
Once you're comfortable with both pipelines, you can:
1. **Combine them** into one pipeline with conditional deployment
2. **Use deployment slots** for zero-downtime updates
3. **Add approval gates** for production deployments
See `azure-pipelines-slots.yml` for an advanced example with deployment slots.
## 🛠️ Troubleshooting
### Build Pipeline Fails
**Issue**: "Service connection not found"
- **Solution**: Verify service connection name matches `dockerRegistryServiceConnection` variable
**Issue**: "ACR not found"
- **Solution**: Check `acrName` variable matches your ACR name
### Deployment Pipeline Fails
**Issue**: "App Service not found"
- **Solution**: Verify app names match your Azure resources
**Issue**: "Environment not found"
- **Solution**: Create `production` environment in Azure DevOps
**Issue**: "Image not found in ACR"
- **Solution**: Run build pipeline first to push images to ACR
## ✅ Checklist
### Build Pipeline Setup
- [ ] Docker Registry service connection created
- [ ] `azure-pipelines.yml` variables configured
- [ ] Pipeline created in Azure DevOps
- [ ] Test run successful
- [ ] Images visible in ACR
### Deployment Pipeline Setup
- [ ] Azure Resource Manager service connection created
- [ ] `production` environment created
- [ ] App Services exist in Azure
- [ ] `azure-pipelines-deploy.yml` variables configured
- [ ] Deployment pipeline created in Azure DevOps
- [ ] Test deployment successful
---
**Workflow**: Build first → Deploy second → Verify success!

View File

@@ -0,0 +1,242 @@
# Azure Resources Overview
Quick reference of all Azure resources needed for CMDB Insight deployment.
## 📋 Resources Summary
| Resource Type | Resource Name | Purpose | SKU/Tier | Estimated Cost | Shared? |
|--------------|---------------|---------|----------|----------------|--------|
| **Resource Group** | `rg-cmdb-insight-prod` | Container for all resources | - | Free | No |
| **Container Registry** | `yourcompanyacr` | Store Docker images (can be shared) | Basic/Standard | €5-20/month | ✅ Yes |
| **PostgreSQL Database** | `cmdb-postgres-prod` | Production database | Standard_B1ms | €20-30/month | No |
| **Key Vault** | `kv-cmdb-insight-prod` | Store secrets securely | Standard | €1-2/month | No |
| **App Service Plan** | `plan-cmdb-insight-prod` | Hosting plan | B1 | €15-25/month | No |
| **App Service (Backend)** | `cmdb-backend-prod` | Backend API | - | Included in plan | No |
| **App Service (Frontend)** | `cmdb-frontend-prod` | Frontend web app | - | Included in plan | No |
| **Application Insights** | `appi-cmdb-insight-prod` | Monitoring & logging | Basic | €0-5/month | No |
**Total Estimated Cost: €41-82/month** (depending on ACR tier and usage)
**💡 Note**: Container Registry can be **shared across multiple applications**. The repository name (`cmdb-insight`) separates this app from others. If you already have an ACR, reuse it to save costs!
---
## 🔗 Resource Dependencies
```
Resource Group (App-specific)
├── PostgreSQL Database
│ └── Stores: Application data
├── Key Vault
│ └── Stores: Secrets (JIRA tokens, passwords, etc.)
├── Application Insights
│ └── Monitors: Backend & Frontend apps
└── App Service Plan
├── Backend App Service
│ ├── Pulls from: Shared ACR (cmdb-insight/backend:latest)
│ ├── Connects to: PostgreSQL
│ ├── Reads from: Key Vault
│ └── Sends logs to: Application Insights
└── Frontend App Service
├── Pulls from: Shared ACR (cmdb-insight/frontend:latest)
└── Connects to: Backend App Service
Shared Resources (can be in separate resource group)
└── Container Registry (ACR) ← Shared across multiple applications
├── cmdb-insight/ ← This application
│ ├── backend:latest
│ └── frontend:latest
├── other-app/ ← Other applications
│ └── api:latest
└── shared-services/ ← Shared images
└── nginx:latest
```
---
## 🌐 Endpoints
After deployment, your application will be available at:
- **Frontend**: `https://cmdb-frontend-prod.azurewebsites.net`
- **Backend API**: `https://cmdb-backend-prod.azurewebsites.net/api`
- **Health Check**: `https://cmdb-backend-prod.azurewebsites.net/api/health`
If custom domain is configured:
- **Frontend**: `https://cmdb.yourcompany.com`
- **Backend API**: `https://api.cmdb.yourcompany.com` (or subdomain of your choice)
---
## 🔐 Required Secrets
These secrets should be stored in Azure Key Vault:
| Secret Name | Description | Example |
|-------------|-------------|---------|
| `JiraPat` | Jira Personal Access Token (if using PAT auth) | `ATATT3xFfGF0...` |
| `SessionSecret` | Session encryption secret | `a1b2c3d4e5f6...` (32+ chars) |
| `JiraOAuthClientId` | Jira OAuth Client ID | `OAuthClientId123` |
| `JiraOAuthClientSecret` | Jira OAuth Client Secret | `OAuthSecret456` |
| `JiraSchemaId` | Jira Assets Schema ID | `schema-123` |
| `DatabasePassword` | PostgreSQL admin password | `SecurePassword123!` |
---
## 📊 Resource Sizing Recommendations
### For 20 Users (Current)
| Resource | Recommended SKU | Alternative |
|----------|----------------|-------------|
| App Service Plan | B1 (1 vCore, 1.75GB RAM) | B2 if experiencing slowness |
| PostgreSQL | Standard_B1ms (1 vCore, 2GB RAM) | Standard_B2s for growth |
| Container Registry | Basic (10GB) | Standard for production |
| Key Vault | Standard | Standard (only option) |
### For 50+ Users (Future Growth)
| Resource | Recommended SKU | Notes |
|----------|----------------|-------|
| App Service Plan | B2 or S1 | Better performance |
| PostgreSQL | Standard_B2s (2 vCores, 4GB RAM) | More concurrent connections |
| Container Registry | Standard (100GB) | More storage, geo-replication |
---
## 🔄 Update/Deployment Flow
1. **Code Changes** → Push to repository
2. **CI/CD Pipeline** → Builds Docker images
3. **Push to ACR** → Images stored in Container Registry
4. **Restart App Services** → Pulls new images from ACR
5. **Application Updates** → New version live
### Manual Deployment
```bash
# Restart apps to pull latest images
az webapp restart --name cmdb-backend-prod --resource-group rg-cmdb-insight-prod
az webapp restart --name cmdb-frontend-prod --resource-group rg-cmdb-insight-prod
```
---
## 🛡️ Security Configuration
### Network Security
- **HTTPS Only**: Enabled on both App Services
- **Database Firewall**: Restricted to Azure services (can be further restricted)
- **Key Vault Access**: Managed Identity only (no shared keys)
### Authentication
- **App Services**: Managed Identity for ACR and Key Vault access
- **Database**: Username/password (stored in Key Vault)
- **Application**: Jira OAuth 2.0 or Personal Access Token
---
## 📈 Monitoring & Logging
### Application Insights
- **Metrics**: Response times, request rates, errors
- **Logs**: Application logs, exceptions, traces
- **Alerts**: Configured for downtime, errors, performance issues
### Access Logs
```bash
# Backend logs
az webapp log tail --name cmdb-backend-prod --resource-group rg-cmdb-insight-prod
# Frontend logs
az webapp log tail --name cmdb-frontend-prod --resource-group rg-cmdb-insight-prod
```
---
## 🔧 Configuration Files
### Environment Variables (Backend)
- `NODE_ENV=production`
- `PORT=3001`
- `DATABASE_TYPE=postgres`
- `DATABASE_URL` (from Key Vault)
- `JIRA_HOST=https://jira.zuyderland.nl`
- `JIRA_AUTH_METHOD=oauth`
- `JIRA_OAUTH_CLIENT_ID` (from Key Vault)
- `JIRA_OAUTH_CLIENT_SECRET` (from Key Vault)
- `JIRA_OAUTH_CALLBACK_URL`
- `JIRA_SCHEMA_ID` (from Key Vault)
- `SESSION_SECRET` (from Key Vault)
- `FRONTEND_URL`
- `APPINSIGHTS_INSTRUMENTATIONKEY`
### Environment Variables (Frontend)
- `VITE_API_URL` (points to backend API)
---
## 🗑️ Cleanup (If Needed)
To delete all resources:
```bash
# Delete entire resource group (deletes all resources)
az group delete --name rg-cmdb-insight-prod --yes --no-wait
# Or delete individual resources
az acr delete --name cmdbinsightacr --resource-group rg-cmdb-insight-prod
az postgres flexible-server delete --name cmdb-postgres-prod --resource-group rg-cmdb-insight-prod
az keyvault delete --name kv-cmdb-insight-prod --resource-group rg-cmdb-insight-prod
az appservice plan delete --name plan-cmdb-insight-prod --resource-group rg-cmdb-insight-prod
```
**⚠️ Warning**: This will permanently delete all resources and data. Make sure you have backups if needed.
---
## 📞 Quick Commands Reference
```bash
# Set variables
RESOURCE_GROUP="rg-cmdb-insight-prod"
BACKEND_APP="cmdb-backend-prod"
FRONTEND_APP="cmdb-frontend-prod"
# Check app status
az webapp show --name $BACKEND_APP --resource-group $RESOURCE_GROUP --query state
# View logs
az webapp log tail --name $BACKEND_APP --resource-group $RESOURCE_GROUP
# Restart apps
az webapp restart --name $BACKEND_APP --resource-group $RESOURCE_GROUP
az webapp restart --name $FRONTEND_APP --resource-group $RESOURCE_GROUP
# List all resources
az resource list --resource-group $RESOURCE_GROUP --output table
# Get app URLs
echo "Frontend: https://${FRONTEND_APP}.azurewebsites.net"
echo "Backend: https://${BACKEND_APP}.azurewebsites.net/api"
```
---
## 📚 Related Documentation
- **`AZURE-NEW-SUBSCRIPTION-SETUP.md`** - Complete step-by-step setup guide
- **`AZURE-APP-SERVICE-DEPLOYMENT.md`** - Detailed App Service deployment
- **`AZURE-CONTAINER-REGISTRY.md`** - ACR setup and usage
- **`AZURE-QUICK-REFERENCE.md`** - Quick reference guide
- **`PRODUCTION-DEPLOYMENT.md`** - General production deployment
---
**Last Updated**: 2025-01-21