From 42a04e6cb36b2bb14efa9189312f680aac637d0e Mon Sep 17 00:00:00 2001 From: Bert Hausmans Date: Wed, 21 Jan 2026 23:03:48 +0100 Subject: [PATCH] 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 --- azure-pipelines-deploy.yml | 133 ++++ azure-pipelines-slots.yml | 247 +++++++ docs/AZURE-ACR-NAMING-RECOMMENDATION.md | 165 +++++ docs/AZURE-ACR-SHARED-SETUP.md | 317 ++++++++ docs/AZURE-NEW-SUBSCRIPTION-SETUP.md | 943 ++++++++++++++++++++++++ docs/AZURE-PIPELINE-DEPLOYMENT.md | 337 +++++++++ docs/AZURE-PIPELINE-QUICK-REFERENCE.md | 152 ++++ docs/AZURE-PIPELINES-USAGE.md | 222 ++++++ docs/AZURE-RESOURCES-OVERVIEW.md | 242 ++++++ scripts/setup-azure-resources.sh | 418 +++++++++++ 10 files changed, 3176 insertions(+) create mode 100644 azure-pipelines-deploy.yml create mode 100644 azure-pipelines-slots.yml create mode 100644 docs/AZURE-ACR-NAMING-RECOMMENDATION.md create mode 100644 docs/AZURE-ACR-SHARED-SETUP.md create mode 100644 docs/AZURE-NEW-SUBSCRIPTION-SETUP.md create mode 100644 docs/AZURE-PIPELINE-DEPLOYMENT.md create mode 100644 docs/AZURE-PIPELINE-QUICK-REFERENCE.md create mode 100644 docs/AZURE-PIPELINES-USAGE.md create mode 100644 docs/AZURE-RESOURCES-OVERVIEW.md create mode 100755 scripts/setup-azure-resources.sh diff --git a/azure-pipelines-deploy.yml b/azure-pipelines-deploy.yml new file mode 100644 index 0000000..b7e4f7d --- /dev/null +++ b/azure-pipelines-deploy.yml @@ -0,0 +1,133 @@ +# Azure DevOps Pipeline - Deploy to Azure App Service +# Use this pipeline after images have been built and pushed to ACR +# +# To use this pipeline: +# 1. Make sure images exist in ACR (run azure-pipelines.yml first) +# 2. Update variables below with your Azure resource names +# 3. Create Azure service connection for App Service deployment +# 4. Create 'production' environment in Azure DevOps +# 5. Configure this pipeline in Azure DevOps + +trigger: + branches: + include: + - main + tags: + include: + - 'v*' + +pool: + vmImage: 'ubuntu-latest' + +variables: + # Azure Container Registry configuratie + acrName: 'zdlas' # Pas aan naar jouw ACR naam + repositoryName: 'cmdb-insight' + + # Azure App Service configuratie + resourceGroup: 'rg-cmdb-insight-prod' # Pas aan naar jouw resource group + backendAppName: 'cmdb-backend-prod' # Pas aan naar jouw backend app naam + frontendAppName: 'cmdb-frontend-prod' # Pas aan naar jouw frontend app naam + azureSubscription: 'zuyderland-cmdb-subscription' # Azure service connection voor App Service deployment + + # Deployment configuratie + imageTag: 'latest' # Use 'latest' or specific tag like 'v1.0.0' + +stages: +- stage: Deploy + displayName: 'Deploy to Azure App Service' + jobs: + - deployment: DeployBackend + displayName: 'Deploy Backend' + environment: 'production' + strategy: + runOnce: + deploy: + steps: + - task: AzureWebAppContainer@1 + displayName: 'Deploy Backend Container' + inputs: + azureSubscription: '$(azureSubscription)' + appName: '$(backendAppName)' + containers: '$(acrName).azurecr.io/$(repositoryName)/backend:$(imageTag)' + deployToSlotOrASE: false + + - task: AzureCLI@2 + displayName: 'Restart Backend App Service' + inputs: + azureSubscription: '$(azureSubscription)' + scriptType: 'bash' + scriptLocation: 'inlineScript' + inlineScript: | + echo "Restarting backend app service..." + az webapp restart \ + --name $(backendAppName) \ + --resource-group $(resourceGroup) + echo "Backend app service restarted successfully" + + - deployment: DeployFrontend + displayName: 'Deploy Frontend' + environment: 'production' + strategy: + runOnce: + deploy: + steps: + - task: AzureWebAppContainer@1 + displayName: 'Deploy Frontend Container' + inputs: + azureSubscription: '$(azureSubscription)' + appName: '$(frontendAppName)' + containers: '$(acrName).azurecr.io/$(repositoryName)/frontend:$(imageTag)' + deployToSlotOrASE: false + + - task: AzureCLI@2 + displayName: 'Restart Frontend App Service' + inputs: + azureSubscription: '$(azureSubscription)' + scriptType: 'bash' + scriptLocation: 'inlineScript' + inlineScript: | + echo "Restarting frontend app service..." + az webapp restart \ + --name $(frontendAppName) \ + --resource-group $(resourceGroup) + echo "Frontend app service restarted successfully" + + - job: VerifyDeployment + displayName: 'Verify Deployment' + dependsOn: + - DeployBackend + - DeployFrontend + steps: + - task: AzureCLI@2 + displayName: 'Health Check' + inputs: + azureSubscription: '$(azureSubscription)' + scriptType: 'bash' + scriptLocation: 'inlineScript' + inlineScript: | + echo "Checking backend health..." + BACKEND_URL="https://$(backendAppName).azurewebsites.net/api/health" + FRONTEND_URL="https://$(frontendAppName).azurewebsites.net" + + echo "Backend URL: $BACKEND_URL" + echo "Frontend URL: $FRONTEND_URL" + + # Wait a bit for apps to start + sleep 10 + + # Check backend health + BACKEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" $BACKEND_URL || echo "000") + if [ "$BACKEND_STATUS" = "200" ]; then + echo "✅ Backend health check passed" + else + echo "⚠️ Backend health check returned status: $BACKEND_STATUS" + fi + + # Check frontend + FRONTEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" $FRONTEND_URL || echo "000") + if [ "$FRONTEND_STATUS" = "200" ]; then + echo "✅ Frontend is accessible" + else + echo "⚠️ Frontend returned status: $FRONTEND_STATUS" + fi diff --git a/azure-pipelines-slots.yml b/azure-pipelines-slots.yml new file mode 100644 index 0000000..5dea2f1 --- /dev/null +++ b/azure-pipelines-slots.yml @@ -0,0 +1,247 @@ +# Azure DevOps Pipeline - Build, Push and Deploy with Deployment Slots +# Advanced version with zero-downtime deployment using staging slots + +trigger: + branches: + include: + - main + tags: + include: + - 'v*' + +pool: + vmImage: 'ubuntu-latest' + +variables: + # Azure Container Registry configuratie + acrName: 'zdlas' # Pas aan naar jouw ACR naam + repositoryName: 'cmdb-insight' + dockerRegistryServiceConnection: 'zuyderland-cmdb-acr-connection' + + # Azure App Service configuratie + resourceGroup: 'rg-cmdb-insight-prod' + backendAppName: 'cmdb-backend-prod' + frontendAppName: 'cmdb-frontend-prod' + azureSubscription: 'zuyderland-cmdb-subscription' + + # Deployment configuratie + imageTag: '$(Build.BuildId)' + deployToProduction: true + useDeploymentSlots: true # Enable deployment slots + stagingSlotName: 'staging' + +stages: +- stage: Build + displayName: 'Build and Push Docker Images' + jobs: + - job: BuildImages + displayName: 'Build Docker Images' + steps: + - task: Docker@2 + displayName: 'Build and Push Backend Image' + inputs: + command: buildAndPush + repository: '$(repositoryName)/backend' + dockerfile: 'backend/Dockerfile.prod' + containerRegistry: '$(dockerRegistryServiceConnection)' + tags: | + $(imageTag) + latest + + - task: Docker@2 + displayName: 'Build and Push Frontend Image' + inputs: + command: buildAndPush + repository: '$(repositoryName)/frontend' + dockerfile: 'frontend/Dockerfile.prod' + containerRegistry: '$(dockerRegistryServiceConnection)' + tags: | + $(imageTag) + latest + + - task: PowerShell@2 + displayName: 'Output Image URLs' + inputs: + targetType: 'inline' + script: | + $backendImage = "$(acrName).azurecr.io/$(repositoryName)/backend:$(imageTag)" + $frontendImage = "$(acrName).azurecr.io/$(repositoryName)/frontend:$(imageTag)" + Write-Host "##vso[task.setvariable variable=backendImage;isOutput=true]$backendImage" + Write-Host "##vso[task.setvariable variable=frontendImage;isOutput=true]$frontendImage" + Write-Host "Backend Image: $backendImage" + Write-Host "Frontend Image: $frontendImage" + +- stage: DeployToStaging + displayName: 'Deploy to Staging Slot' + dependsOn: Build + condition: and(succeeded(), eq(variables['deployToProduction'], true), eq(variables['useDeploymentSlots'], true)) + jobs: + - deployment: DeployBackendStaging + displayName: 'Deploy Backend to Staging' + environment: 'staging' + strategy: + runOnce: + deploy: + steps: + - task: AzureWebAppContainer@1 + displayName: 'Deploy Backend to Staging Slot' + inputs: + azureSubscription: '$(azureSubscription)' + appName: '$(backendAppName)' + deployToSlotOrASE: true + resourceGroupName: '$(resourceGroup)' + slotName: '$(stagingSlotName)' + containers: '$(acrName).azurecr.io/$(repositoryName)/backend:latest' + + - task: AzureCLI@2 + displayName: 'Wait for Backend Staging to be Ready' + inputs: + azureSubscription: '$(azureSubscription)' + scriptType: 'bash' + scriptLocation: 'inlineScript' + inlineScript: | + echo "Waiting for backend staging to be ready..." + sleep 30 + STAGING_URL="https://$(backendAppName)-$(stagingSlotName).azurewebsites.net/api/health" + for i in {1..10}; do + STATUS=$(curl -s -o /dev/null -w "%{http_code}" $STAGING_URL || echo "000") + if [ "$STATUS" = "200" ]; then + echo "✅ Backend staging is ready" + exit 0 + fi + echo "Waiting... ($i/10)" + sleep 10 + done + echo "⚠️ Backend staging health check timeout" + + - deployment: DeployFrontendStaging + displayName: 'Deploy Frontend to Staging' + environment: 'staging' + strategy: + runOnce: + deploy: + steps: + - task: AzureWebAppContainer@1 + displayName: 'Deploy Frontend to Staging Slot' + inputs: + azureSubscription: '$(azureSubscription)' + appName: '$(frontendAppName)' + deployToSlotOrASE: true + resourceGroupName: '$(resourceGroup)' + slotName: '$(stagingSlotName)' + containers: '$(acrName).azurecr.io/$(repositoryName)/frontend:latest' + + - task: AzureCLI@2 + displayName: 'Wait for Frontend Staging to be Ready' + inputs: + azureSubscription: '$(azureSubscription)' + scriptType: 'bash' + scriptLocation: 'inlineScript' + inlineScript: | + echo "Waiting for frontend staging to be ready..." + sleep 20 + STAGING_URL="https://$(frontendAppName)-$(stagingSlotName).azurewebsites.net" + for i in {1..10}; do + STATUS=$(curl -s -o /dev/null -w "%{http_code}" $STAGING_URL || echo "000") + if [ "$STATUS" = "200" ]; then + echo "✅ Frontend staging is ready" + exit 0 + fi + echo "Waiting... ($i/10)" + sleep 10 + done + echo "⚠️ Frontend staging health check timeout" + +- stage: SwapToProduction + displayName: 'Swap Staging to Production' + dependsOn: DeployToStaging + condition: and(succeeded(), eq(variables['deployToProduction'], true), eq(variables['useDeploymentSlots'], true)) + jobs: + - deployment: SwapBackend + displayName: 'Swap Backend to Production' + environment: 'production' + strategy: + runOnce: + deploy: + steps: + - task: AzureCLI@2 + displayName: 'Swap Backend Staging to Production' + inputs: + azureSubscription: '$(azureSubscription)' + scriptType: 'bash' + scriptLocation: 'inlineScript' + inlineScript: | + echo "Swapping backend staging to production..." + az webapp deployment slot swap \ + --name $(backendAppName) \ + --resource-group $(resourceGroup) \ + --slot $(stagingSlotName) \ + --target-slot production + echo "✅ Backend swapped to production" + + - deployment: SwapFrontend + displayName: 'Swap Frontend to Production' + environment: 'production' + strategy: + runOnce: + deploy: + steps: + - task: AzureCLI@2 + displayName: 'Swap Frontend Staging to Production' + inputs: + azureSubscription: '$(azureSubscription)' + scriptType: 'bash' + scriptLocation: 'inlineScript' + inlineScript: | + echo "Swapping frontend staging to production..." + az webapp deployment slot swap \ + --name $(frontendAppName) \ + --resource-group $(resourceGroup) \ + --slot $(stagingSlotName) \ + --target-slot production + echo "✅ Frontend swapped to production" + +- stage: VerifyProduction + displayName: 'Verify Production Deployment' + dependsOn: SwapToProduction + condition: and(succeeded(), eq(variables['deployToProduction'], true), eq(variables['useDeploymentSlots'], true)) + jobs: + - job: VerifyDeployment + displayName: 'Verify Production' + steps: + - task: AzureCLI@2 + displayName: 'Production Health Check' + inputs: + azureSubscription: '$(azureSubscription)' + scriptType: 'bash' + scriptLocation: 'inlineScript' + inlineScript: | + echo "Verifying production deployment..." + BACKEND_URL="https://$(backendAppName).azurewebsites.net/api/health" + FRONTEND_URL="https://$(frontendAppName).azurewebsites.net" + + echo "Backend URL: $BACKEND_URL" + echo "Frontend URL: $FRONTEND_URL" + + # Wait for swap to complete + sleep 15 + + # Check backend health + BACKEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" $BACKEND_URL || echo "000") + if [ "$BACKEND_STATUS" = "200" ]; then + echo "✅ Backend production health check passed" + else + echo "❌ Backend production health check failed: $BACKEND_STATUS" + exit 1 + fi + + # Check frontend + FRONTEND_STATUS=$(curl -s -o /dev/null -w "%{http_code}" $FRONTEND_URL || echo "000") + if [ "$FRONTEND_STATUS" = "200" ]; then + echo "✅ Frontend production is accessible" + else + echo "❌ Frontend production check failed: $FRONTEND_STATUS" + exit 1 + fi + + echo "🎉 Production deployment verified successfully!" diff --git a/docs/AZURE-ACR-NAMING-RECOMMENDATION.md b/docs/AZURE-ACR-NAMING-RECOMMENDATION.md new file mode 100644 index 0000000..a911afc --- /dev/null +++ b/docs/AZURE-ACR-NAMING-RECOMMENDATION.md @@ -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. diff --git a/docs/AZURE-ACR-SHARED-SETUP.md b/docs/AZURE-ACR-SHARED-SETUP.md new file mode 100644 index 0000000..d0bc5bd --- /dev/null +++ b/docs/AZURE-ACR-SHARED-SETUP.md @@ -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! diff --git a/docs/AZURE-NEW-SUBSCRIPTION-SETUP.md b/docs/AZURE-NEW-SUBSCRIPTION-SETUP.md new file mode 100644 index 0000000..ce1bae6 --- /dev/null +++ b/docs/AZURE-NEW-SUBSCRIPTION-SETUP.md @@ -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 "" +``` + +--- + +## 📦 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="" # 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 \ + --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 +``` + +**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//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 diff --git a/docs/AZURE-PIPELINE-DEPLOYMENT.md b/docs/AZURE-PIPELINE-DEPLOYMENT.md new file mode 100644 index 0000000..7bd1574 --- /dev/null +++ b/docs/AZURE-PIPELINE-DEPLOYMENT.md @@ -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 \ + --role "Website Contributor" \ + --scope /subscriptions//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 +``` + +## 🔄 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. diff --git a/docs/AZURE-PIPELINE-QUICK-REFERENCE.md b/docs/AZURE-PIPELINE-QUICK-REFERENCE.md new file mode 100644 index 0000000..b8eb129 --- /dev/null +++ b/docs/AZURE-PIPELINE-QUICK-REFERENCE.md @@ -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 --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! diff --git a/docs/AZURE-PIPELINES-USAGE.md b/docs/AZURE-PIPELINES-USAGE.md new file mode 100644 index 0000000..bf41c46 --- /dev/null +++ b/docs/AZURE-PIPELINES-USAGE.md @@ -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! diff --git a/docs/AZURE-RESOURCES-OVERVIEW.md b/docs/AZURE-RESOURCES-OVERVIEW.md new file mode 100644 index 0000000..d115bf8 --- /dev/null +++ b/docs/AZURE-RESOURCES-OVERVIEW.md @@ -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 diff --git a/scripts/setup-azure-resources.sh b/scripts/setup-azure-resources.sh new file mode 100755 index 0000000..af5b1fd --- /dev/null +++ b/scripts/setup-azure-resources.sh @@ -0,0 +1,418 @@ +#!/bin/bash + +# Azure Resources Setup Script for CMDB Insight +# This script helps automate the creation of Azure resources +# +# Usage: +# ./scripts/setup-azure-resources.sh +# +# Prerequisites: +# - Azure CLI installed and logged in (az login) +# - Appropriate permissions on Azure subscription +# - Jira credentials ready (OAuth or PAT) + +set -e # Exit on error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Configuration - Customize these values +RESOURCE_GROUP="rg-cmdb-insight-prod" +LOCATION="westeurope" + +# ACR Configuration - Can be shared across multiple applications +# Option 1: Use existing ACR (recommended if you have one) +ACR_NAME="" # Leave empty to use existing, or set to create new +ACR_RESOURCE_GROUP="" # Resource group where ACR exists (for existing) or will be created (for new) + +# Option 2: Create new ACR (only if ACR_NAME is set and doesn't exist) +# ACR_NAME="yourcompanyacr" # Must be globally unique, lowercase, 5-50 chars +# ACR_RESOURCE_GROUP="rg-shared-services" # Or use RESOURCE_GROUP + +DB_SERVER_NAME="cmdb-postgres-prod" # Must be globally unique +DB_ADMIN_USER="cmdbadmin" +DB_NAME="cmdb" +KEY_VAULT_NAME="kv-cmdb-insight-prod" # Must be globally unique +APP_INSIGHTS_NAME="appi-cmdb-insight-prod" # Must be globally unique +APP_SERVICE_PLAN_NAME="plan-cmdb-insight-prod" +BACKEND_APP_NAME="cmdb-backend-prod" # Must be globally unique +FRONTEND_APP_NAME="cmdb-frontend-prod" # Must be globally unique + +# SKU Configuration +ACR_SKU="Standard" # Options: Basic, Standard, Premium (Standard recommended for shared ACR) +APP_SERVICE_SKU="B1" # Options: B1, B2, S1 +POSTGRES_SKU="Standard_B1ms" # Options: Standard_B1ms, Standard_B2s + +echo -e "${GREEN}========================================${NC}" +echo -e "${GREEN}Azure Resources Setup for CMDB Insight${NC}" +echo -e "${GREEN}========================================${NC}" +echo "" + +# Check Azure CLI +if ! command -v az &> /dev/null; then + echo -e "${RED}Error: Azure CLI is not installed.${NC}" + echo "Install it from: https://docs.microsoft.com/cli/azure/install-azure-cli" + exit 1 +fi + +# Check if logged in +if ! az account show &> /dev/null; then + echo -e "${YELLOW}Not logged in to Azure. Please run: az login${NC}" + exit 1 +fi + +# Show current subscription +echo -e "${GREEN}Current Azure Subscription:${NC}" +az account show --query "{Name:name, SubscriptionId:id}" -o table +echo "" + +# Confirm before proceeding +read -p "Do you want to proceed with creating resources? (yes/no): " confirm +if [ "$confirm" != "yes" ]; then + echo "Aborted." + exit 0 +fi + +echo "" +echo -e "${GREEN}Step 1: Creating Resource Group...${NC}" +az group create \ + --name $RESOURCE_GROUP \ + --location $LOCATION \ + --output none +echo -e "${GREEN}✓ Resource Group created${NC}" + +echo "" +echo -e "${GREEN}Step 2: Setting up Azure Container Registry...${NC}" + +# Check if ACR_NAME is provided +if [ -z "$ACR_NAME" ]; then + echo -e "${YELLOW}⚠️ ACR_NAME not set. Please provide an existing ACR name or set ACR_NAME to create a new one.${NC}" + read -p "Enter existing ACR name (or press Enter to skip): " EXISTING_ACR_NAME + if [ -z "$EXISTING_ACR_NAME" ]; then + echo -e "${YELLOW}Skipping ACR setup. You'll need to configure ACR manually.${NC}" + ACR_NAME="" + ACR_LOGIN_SERVER="" + else + ACR_NAME=$EXISTING_ACR_NAME + if [ -z "$ACR_RESOURCE_GROUP" ]; then + read -p "Enter ACR resource group: " ACR_RESOURCE_GROUP + fi + # Verify ACR exists + if az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP &> /dev/null; then + echo -e "${GREEN}✓ Using existing Container Registry: $ACR_NAME${NC}" + 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" + else + echo -e "${RED}Error: ACR '$ACR_NAME' not found in resource group '$ACR_RESOURCE_GROUP'${NC}" + exit 1 + fi + fi +else + # Determine ACR resource group + if [ -z "$ACR_RESOURCE_GROUP" ]; then + ACR_RESOURCE_GROUP=$RESOURCE_GROUP + fi + + # Check if ACR already exists + if az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP &> /dev/null; then + echo -e "${GREEN}✓ Using existing Container Registry: $ACR_NAME${NC}" + 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" + else + # Create resource group for ACR if it doesn't exist + if ! az group show --name $ACR_RESOURCE_GROUP &> /dev/null; then + echo "Creating resource group for ACR: $ACR_RESOURCE_GROUP" + az group create --name $ACR_RESOURCE_GROUP --location $LOCATION --output none + fi + + # Create new ACR + echo "Creating new Container Registry: $ACR_NAME" + az acr create \ + --resource-group $ACR_RESOURCE_GROUP \ + --name $ACR_NAME \ + --sku $ACR_SKU \ + --admin-enabled true \ + --output none + echo -e "${GREEN}✓ Container Registry created${NC}" + + 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" + echo -e "${YELLOW}💡 This ACR can be shared with other applications. Use repository names to separate apps.${NC}" + fi +fi + +echo "" +echo -e "${GREEN}Step 3: Creating PostgreSQL Database...${NC}" +echo -e "${YELLOW}Generating secure database password...${NC}" +DB_ADMIN_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-25) +echo " Database Password: $DB_ADMIN_PASSWORD" +echo " ${YELLOW}⚠️ Save this password securely!${NC}" + +az postgres flexible-server create \ + --resource-group $RESOURCE_GROUP \ + --name $DB_SERVER_NAME \ + --location $LOCATION \ + --admin-user $DB_ADMIN_USER \ + --admin-password $DB_ADMIN_PASSWORD \ + --sku-name $POSTGRES_SKU \ + --tier Burstable \ + --storage-size 32 \ + --version 15 \ + --public-access 0.0.0.0 \ + --output none +echo -e "${GREEN}✓ PostgreSQL Server created${NC}" + +az postgres flexible-server db create \ + --resource-group $RESOURCE_GROUP \ + --server-name $DB_SERVER_NAME \ + --database-name $DB_NAME \ + --output none +echo -e "${GREEN}✓ Database created${NC}" + +DB_HOST="${DB_SERVER_NAME}.postgres.database.azure.com" +DB_CONNECTION_STRING="postgresql://${DB_ADMIN_USER}:${DB_ADMIN_PASSWORD}@${DB_HOST}:5432/${DB_NAME}?sslmode=require" +echo " Database Host: $DB_HOST" + +echo "" +echo -e "${GREEN}Step 4: Creating Key Vault...${NC}" +az keyvault create \ + --name $KEY_VAULT_NAME \ + --resource-group $RESOURCE_GROUP \ + --location $LOCATION \ + --sku standard \ + --output none +echo -e "${GREEN}✓ Key Vault created${NC}" + +# Add database password to Key Vault +az keyvault secret set \ + --vault-name $KEY_VAULT_NAME \ + --name "DatabasePassword" \ + --value "$DB_ADMIN_PASSWORD" \ + --output none +echo -e "${GREEN}✓ Database password stored in Key Vault${NC}" + +echo "" +echo -e "${YELLOW}⚠️ You need to add the following secrets to Key Vault manually:${NC}" +echo " - JiraPat (if using PAT authentication)" +echo " - SessionSecret (generate with: openssl rand -hex 32)" +echo " - JiraOAuthClientId (if using OAuth)" +echo " - JiraOAuthClientSecret (if using OAuth)" +echo " - JiraSchemaId" +echo "" +echo "Commands to add secrets:" +echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name SessionSecret --value \$(openssl rand -hex 32)" +echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name JiraOAuthClientId --value " +echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name JiraOAuthClientSecret --value " +echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name JiraSchemaId --value " + +echo "" +echo -e "${GREEN}Step 5: Creating Application Insights...${NC}" +az monitor app-insights component create \ + --app $APP_INSIGHTS_NAME \ + --location $LOCATION \ + --resource-group $RESOURCE_GROUP \ + --application-type web \ + --output none +echo -e "${GREEN}✓ Application Insights created${NC}" + +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" + +echo "" +echo -e "${GREEN}Step 6: Creating App Service Plan...${NC}" +az appservice plan create \ + --name $APP_SERVICE_PLAN_NAME \ + --resource-group $RESOURCE_GROUP \ + --sku $APP_SERVICE_SKU \ + --is-linux \ + --output none +echo -e "${GREEN}✓ App Service Plan created${NC}" + +echo "" +echo -e "${GREEN}Step 7: Creating Backend App Service...${NC}" +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" \ + --output none +echo -e "${GREEN}✓ Backend App Service created${NC}" + +# Enable Managed Identity +az webapp identity assign \ + --name $BACKEND_APP_NAME \ + --resource-group $RESOURCE_GROUP \ + --output none + +BACKEND_PRINCIPAL_ID=$(az webapp identity show \ + --name $BACKEND_APP_NAME \ + --resource-group $RESOURCE_GROUP \ + --query principalId --output tsv) + +# Grant ACR access (if ACR was configured) +if [ -n "$ACR_NAME" ] && [ -n "$ACR_RESOURCE_GROUP" ]; then + ACR_ID=$(az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP --query id --output tsv) + az role assignment create \ + --assignee $BACKEND_PRINCIPAL_ID \ + --role AcrPull \ + --scope $ACR_ID \ + --output none + echo -e "${GREEN}✓ ACR access granted to backend${NC}" +else + echo -e "${YELLOW}⚠️ ACR not configured. You'll need to grant ACR access manually.${NC}" +fi + +# Configure container (if ACR was configured) +if [ -n "$ACR_NAME" ]; then + 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" \ + --output none +else + echo -e "${YELLOW}⚠️ ACR not configured. You'll need to configure container settings manually.${NC}" +fi + +# Grant Key Vault access +az keyvault set-policy \ + --name $KEY_VAULT_NAME \ + --object-id $BACKEND_PRINCIPAL_ID \ + --secret-permissions get list \ + --output none +echo -e "${GREEN}✓ Key Vault access granted to backend${NC}" + +# Set environment variables (basic - you'll need to add Key Vault references manually) +az webapp config appsettings set \ + --name $BACKEND_APP_NAME \ + --resource-group $RESOURCE_GROUP \ + --settings \ + NODE_ENV=production \ + PORT=3001 \ + DATABASE_TYPE=postgres \ + DATABASE_URL="postgresql://${DB_ADMIN_USER}:${DB_ADMIN_PASSWORD}@${DB_HOST}:5432/${DB_NAME}?sslmode=require" \ + JIRA_HOST=https://jira.zuyderland.nl \ + JIRA_AUTH_METHOD=oauth \ + FRONTEND_URL="https://${FRONTEND_APP_NAME}.azurewebsites.net" \ + APPINSIGHTS_INSTRUMENTATIONKEY="${INSTRUMENTATION_KEY}" \ + --output none + +# Enable HTTPS only +az webapp update \ + --name $BACKEND_APP_NAME \ + --resource-group $RESOURCE_GROUP \ + --https-only true \ + --output none + +echo -e "${GREEN}✓ Backend configuration complete${NC}" + +echo "" +echo -e "${GREEN}Step 8: Creating Frontend App Service...${NC}" +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" \ + --output none +echo -e "${GREEN}✓ Frontend App Service created${NC}" + +# Enable Managed Identity +az webapp identity assign \ + --name $FRONTEND_APP_NAME \ + --resource-group $RESOURCE_GROUP \ + --output none + +FRONTEND_PRINCIPAL_ID=$(az webapp identity show \ + --name $FRONTEND_APP_NAME \ + --resource-group $RESOURCE_GROUP \ + --query principalId --output tsv) + +# Grant ACR access (if ACR was configured) +if [ -n "$ACR_NAME" ] && [ -n "$ACR_RESOURCE_GROUP" ]; then + ACR_ID=$(az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP --query id --output tsv) + az role assignment create \ + --assignee $FRONTEND_PRINCIPAL_ID \ + --role AcrPull \ + --scope $ACR_ID \ + --output none + echo -e "${GREEN}✓ ACR access granted to frontend${NC}" +fi + +# Configure container (if ACR was configured) +if [ -n "$ACR_NAME" ]; then + 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" \ + --output none +fi + +# 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" \ + --output none + +# Enable HTTPS only +az webapp update \ + --name $FRONTEND_APP_NAME \ + --resource-group $RESOURCE_GROUP \ + --https-only true \ + --output none + +echo -e "${GREEN}✓ Frontend configuration complete${NC}" + +echo "" +echo -e "${GREEN}========================================${NC}" +echo -e "${GREEN}Setup Complete!${NC}" +echo -e "${GREEN}========================================${NC}" +echo "" +echo -e "${YELLOW}Next Steps:${NC}" +echo "" +echo "1. Add secrets to Key Vault:" +echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name SessionSecret --value \$(openssl rand -hex 32)" +echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name JiraOAuthClientId --value " +echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name JiraOAuthClientSecret --value " +echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name JiraSchemaId --value " +echo "" +echo "2. Update backend app settings to use Key Vault references:" +echo " See AZURE-NEW-SUBSCRIPTION-SETUP.md for details" +echo "" +echo "3. Build and push Docker images:" +if [ -n "$ACR_NAME" ]; then + echo " export ACR_NAME=\"$ACR_NAME\"" + echo " ./scripts/build-and-push-azure.sh" +else + echo " export ACR_NAME=\"\"" + echo " ./scripts/build-and-push-azure.sh" +fi +echo "" +echo "4. Restart apps to pull images:" +echo " az webapp restart --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP" +echo " az webapp restart --name $FRONTEND_APP_NAME --resource-group $RESOURCE_GROUP" +echo "" +echo -e "${GREEN}Resource Information:${NC}" +echo " Resource Group: $RESOURCE_GROUP" +if [ -n "$ACR_LOGIN_SERVER" ]; then + echo " ACR: $ACR_LOGIN_SERVER" + echo " Repository: cmdb-insight (backend, frontend)" +else + echo " ACR: Not configured (configure manually)" +fi +echo " Database: $DB_HOST" +echo " Key Vault: $KEY_VAULT_NAME" +echo " Frontend URL: https://${FRONTEND_APP_NAME}.azurewebsites.net" +echo " Backend URL: https://${BACKEND_APP_NAME}.azurewebsites.net/api" +echo "" +echo -e "${YELLOW}⚠️ Important: Save the database password securely!${NC}" +echo " Password: $DB_ADMIN_PASSWORD" +echo ""