Add Azure deployment automation and documentation
- Add separate deployment pipeline (azure-pipelines-deploy.yml) for App Service deployment - Add advanced pipeline with deployment slots (azure-pipelines-slots.yml) - Restore azure-pipelines.yml to build-only (no deployment) - Add comprehensive Azure setup documentation: - AZURE-NEW-SUBSCRIPTION-SETUP.md: Complete step-by-step Azure resource setup - AZURE-RESOURCES-OVERVIEW.md: Quick reference for all Azure resources - AZURE-ACR-SHARED-SETUP.md: Guide for shared Container Registry - AZURE-ACR-NAMING-RECOMMENDATION.md: Naming recommendations for Zuyderland - AZURE-PIPELINE-DEPLOYMENT.md: Automated deployment setup guide - AZURE-PIPELINE-QUICK-REFERENCE.md: Quick reference for pipeline variables - AZURE-PIPELINES-USAGE.md: Guide for using build and deployment pipelines - Add setup script (scripts/setup-azure-resources.sh) for automated resource creation - Support for shared ACR across multiple applications
This commit is contained in:
133
azure-pipelines-deploy.yml
Normal file
133
azure-pipelines-deploy.yml
Normal file
@@ -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
|
||||||
247
azure-pipelines-slots.yml
Normal file
247
azure-pipelines-slots.yml
Normal file
@@ -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!"
|
||||||
165
docs/AZURE-ACR-NAMING-RECOMMENDATION.md
Normal file
165
docs/AZURE-ACR-NAMING-RECOMMENDATION.md
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
# Azure Container Registry Naming Recommendation
|
||||||
|
|
||||||
|
Recommendations for naming your Azure Container Registry for Zuyderland Application Services.
|
||||||
|
|
||||||
|
## 🎯 Requirements
|
||||||
|
|
||||||
|
Azure Container Registry names must:
|
||||||
|
- Be **globally unique** (across all Azure subscriptions)
|
||||||
|
- Be **5-50 characters** long
|
||||||
|
- Contain **only lowercase alphanumeric characters** (no hyphens, underscores, or special characters)
|
||||||
|
- Be **descriptive** but not too long
|
||||||
|
|
||||||
|
## 💡 Recommended Options
|
||||||
|
|
||||||
|
### Option 1: `zuyderlandacr` ⭐ **RECOMMENDED**
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- ✅ Clear company identification
|
||||||
|
- ✅ Short and memorable (15 characters)
|
||||||
|
- ✅ Generic enough for all Application Services apps
|
||||||
|
- ✅ Easy to type and remember
|
||||||
|
- ✅ Professional appearance
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- ⚠️ Might be taken if Zuyderland already has an ACR
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
ACR_NAME="zuyderlandacr"
|
||||||
|
# Images: zuyderlandacr.azurecr.io/cmdb-insight/backend:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: `zuyderlandsharedacr`
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- ✅ Clearly indicates it's a shared registry
|
||||||
|
- ✅ Company identification
|
||||||
|
- ✅ Good for documentation ("shared ACR")
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- ⚠️ Longer (20 characters)
|
||||||
|
- ⚠️ "shared" might be redundant (ACRs are typically shared)
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
ACR_NAME="zuyderlandsharedacr"
|
||||||
|
# Images: zuyderlandsharedacr.azurecr.io/cmdb-insight/backend:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: `zyldacr` (Abbreviated)
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- ✅ Very short (7 characters)
|
||||||
|
- ✅ Easy to type
|
||||||
|
- ✅ Less likely to be taken
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- ⚠️ Less clear what "zyld" means
|
||||||
|
- ⚠️ Might not be obvious it's Zuyderland
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
ACR_NAME="zyldacr"
|
||||||
|
# Images: zyldacr.azurecr.io/cmdb-insight/backend:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 4: `zuyderlandappsvcsacr`
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- ✅ Includes department name (Application Services)
|
||||||
|
- ✅ Very specific
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- ⚠️ Long (23 characters)
|
||||||
|
- ⚠️ Less flexible if other departments want to use it
|
||||||
|
- ⚠️ Harder to type
|
||||||
|
|
||||||
|
**Usage:**
|
||||||
|
```bash
|
||||||
|
ACR_NAME="zuyderlandappsvcsacr"
|
||||||
|
# Images: zuyderlandappsvcsacr.azurecr.io/cmdb-insight/backend:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🏆 Final Recommendation
|
||||||
|
|
||||||
|
**Use: `zuyderlandacr`**
|
||||||
|
|
||||||
|
**Reasoning:**
|
||||||
|
1. **Clear and professional**: Immediately identifies as Zuyderland
|
||||||
|
2. **Appropriate length**: Not too short (unclear) or too long (hard to type)
|
||||||
|
3. **Generic enough**: Can be used by all Application Services applications
|
||||||
|
4. **Future-proof**: Works for any department or team within Zuyderland
|
||||||
|
5. **Standard pattern**: Follows common naming convention (`companynameacr`)
|
||||||
|
|
||||||
|
## 🔍 Check Availability
|
||||||
|
|
||||||
|
Before creating, check if the name is available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Try to check if name exists (will fail if available, which is good)
|
||||||
|
az acr show --name zuyderlandacr --resource-group dummy-rg 2>&1 | grep -q "could not be found" && echo "Name available!" || echo "Name might be taken"
|
||||||
|
|
||||||
|
# Or try to create (will fail if taken)
|
||||||
|
az acr check-name --name zuyderlandacr
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 Alternative if Name is Taken
|
||||||
|
|
||||||
|
If `zuyderlandacr` is already taken, try:
|
||||||
|
|
||||||
|
1. `zuyderlandacr01` - Add number suffix
|
||||||
|
2. `zuyderlandacrprod` - Add environment suffix
|
||||||
|
3. `zyldacr` - Use abbreviation
|
||||||
|
4. `zuyderlandregistry` - Use full word "registry"
|
||||||
|
5. `zuyderlandcontainers` - Use "containers" instead of "acr"
|
||||||
|
|
||||||
|
## 🎯 Naming Pattern
|
||||||
|
|
||||||
|
For consistency across Zuyderland, consider this pattern:
|
||||||
|
|
||||||
|
```
|
||||||
|
zuyderlandacr ← Shared ACR for all apps (recommended)
|
||||||
|
zuyderlandacrdev ← Development ACR (if needed)
|
||||||
|
zuyderlandacrprod ← Production ACR (if separate)
|
||||||
|
```
|
||||||
|
|
||||||
|
**But for most cases, one shared ACR (`zuyderlandacr`) is sufficient.**
|
||||||
|
|
||||||
|
## 📋 Update Your Configuration
|
||||||
|
|
||||||
|
Once you've chosen a name, update:
|
||||||
|
|
||||||
|
### 1. Setup Script
|
||||||
|
```bash
|
||||||
|
# In scripts/setup-azure-resources.sh
|
||||||
|
ACR_NAME="zuyderlandacr"
|
||||||
|
ACR_RESOURCE_GROUP="rg-shared-services" # Or rg-zuyderland-shared
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Pipeline Variables
|
||||||
|
```yaml
|
||||||
|
# In azure-pipelines.yml
|
||||||
|
variables:
|
||||||
|
acrName: 'zuyderlandacr'
|
||||||
|
repositoryName: 'cmdb-insight'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Build Scripts
|
||||||
|
```bash
|
||||||
|
# In scripts/build-and-push-azure.sh
|
||||||
|
export ACR_NAME="zuyderlandacr"
|
||||||
|
```
|
||||||
|
|
||||||
|
## ✅ Checklist
|
||||||
|
|
||||||
|
- [ ] Choose ACR name: `zuyderlandacr` (recommended)
|
||||||
|
- [ ] Check name availability
|
||||||
|
- [ ] Create ACR with chosen name
|
||||||
|
- [ ] Update all configuration files
|
||||||
|
- [ ] Document name for team
|
||||||
|
- [ ] Share ACR name with other Application Services teams
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Recommended: `zuyderlandacr`** - Clear, professional, and reusable for all Zuyderland Application Services applications.
|
||||||
317
docs/AZURE-ACR-SHARED-SETUP.md
Normal file
317
docs/AZURE-ACR-SHARED-SETUP.md
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
# Shared Azure Container Registry Setup
|
||||||
|
|
||||||
|
Guide for using a shared Azure Container Registry across multiple applications.
|
||||||
|
|
||||||
|
## 🎯 Why Share ACR?
|
||||||
|
|
||||||
|
**Benefits:**
|
||||||
|
- ✅ **Cost Savings**: One ACR for all applications (€5-20/month vs multiple ACRs)
|
||||||
|
- ✅ **Centralized Management**: All images in one place
|
||||||
|
- ✅ **Easier Collaboration**: Teams can share images
|
||||||
|
- ✅ **Better Resource Utilization**: More efficient use of storage
|
||||||
|
|
||||||
|
**How it works:**
|
||||||
|
- ACR is shared, but each application uses a **unique repository name**
|
||||||
|
- Repository name (`cmdb-insight`) separates your app from others
|
||||||
|
- Images are organized by application: `acr.azurecr.io/app-name/service:tag`
|
||||||
|
|
||||||
|
## 📦 ACR Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
zuyderlandacr.azurecr.io/
|
||||||
|
├── cmdb-insight/ ← This application
|
||||||
|
│ ├── backend:latest
|
||||||
|
│ ├── backend:v1.0.0
|
||||||
|
│ ├── frontend:latest
|
||||||
|
│ └── frontend:v1.0.0
|
||||||
|
├── other-app/ ← Another application
|
||||||
|
│ ├── api:latest
|
||||||
|
│ └── web:latest
|
||||||
|
└── shared-services/ ← Shared base images
|
||||||
|
├── nginx:latest
|
||||||
|
└── node:20-alpine
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Setup Options
|
||||||
|
|
||||||
|
### Option 1: Use Existing ACR (Recommended)
|
||||||
|
|
||||||
|
If you already have an ACR for other applications:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set your existing ACR details
|
||||||
|
ACR_NAME="your-existing-acr"
|
||||||
|
ACR_RESOURCE_GROUP="rg-shared-services" # Or wherever your ACR is
|
||||||
|
|
||||||
|
# Verify it exists
|
||||||
|
az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP
|
||||||
|
|
||||||
|
# Get login server
|
||||||
|
ACR_LOGIN_SERVER=$(az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP --query loginServer --output tsv)
|
||||||
|
echo "ACR Login Server: $ACR_LOGIN_SERVER"
|
||||||
|
```
|
||||||
|
|
||||||
|
**That's it!** Your images will be stored as:
|
||||||
|
- `your-existing-acr.azurecr.io/cmdb-insight/backend:latest`
|
||||||
|
- `your-existing-acr.azurecr.io/cmdb-insight/frontend:latest`
|
||||||
|
|
||||||
|
### Option 2: Create New Shared ACR
|
||||||
|
|
||||||
|
If you don't have an ACR yet, create one that can be shared:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
ACR_NAME="zuyderlandacr" # Recommended: company name + "acr"
|
||||||
|
ACR_RESOURCE_GROUP="rg-shared-services" # Shared resource group
|
||||||
|
LOCATION="westeurope"
|
||||||
|
SKU="Standard" # Recommended for shared ACR
|
||||||
|
|
||||||
|
# Create resource group for shared services
|
||||||
|
az group create --name $ACR_RESOURCE_GROUP --location $LOCATION
|
||||||
|
|
||||||
|
# Create ACR
|
||||||
|
az acr create \
|
||||||
|
--resource-group $ACR_RESOURCE_GROUP \
|
||||||
|
--name $ACR_NAME \
|
||||||
|
--sku $SKU \
|
||||||
|
--admin-enabled true
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Using Shared ACR
|
||||||
|
|
||||||
|
### Build and Push Images
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set ACR name
|
||||||
|
export ACR_NAME="zuyderlandacr"
|
||||||
|
export REPO_NAME="cmdb-insight" # This is your app identifier
|
||||||
|
|
||||||
|
# Build and push (repository name separates your app)
|
||||||
|
./scripts/build-and-push-azure.sh
|
||||||
|
|
||||||
|
# Images will be:
|
||||||
|
# - zuyderlandacr.azurecr.io/cmdb-insight/backend:latest
|
||||||
|
# - zuyderlandacr.azurecr.io/cmdb-insight/frontend:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configure App Services
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend App Service
|
||||||
|
az webapp config container set \
|
||||||
|
--name cmdb-backend-prod \
|
||||||
|
--resource-group rg-cmdb-insight-prod \
|
||||||
|
--docker-custom-image-name "zuyderlandacr.azurecr.io/cmdb-insight/backend:latest" \
|
||||||
|
--docker-registry-server-url "https://zuyderlandacr.azurecr.io"
|
||||||
|
|
||||||
|
# Frontend App Service
|
||||||
|
az webapp config container set \
|
||||||
|
--name cmdb-frontend-prod \
|
||||||
|
--resource-group rg-cmdb-insight-prod \
|
||||||
|
--docker-custom-image-name "zuyderlandacr.azurecr.io/cmdb-insight/frontend:latest" \
|
||||||
|
--docker-registry-server-url "https://zuyderlandacr.azurecr.io"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update Pipeline Variables
|
||||||
|
|
||||||
|
In `azure-pipelines.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
variables:
|
||||||
|
acrName: 'yourcompanyacr' # Shared ACR name
|
||||||
|
repositoryName: 'cmdb-insight' # Your app repository name
|
||||||
|
# ... other variables
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔐 Permissions
|
||||||
|
|
||||||
|
### Grant App Services Access to Shared ACR
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get App Service Managed Identity
|
||||||
|
BACKEND_PRINCIPAL_ID=$(az webapp identity show \
|
||||||
|
--name cmdb-backend-prod \
|
||||||
|
--resource-group rg-cmdb-insight-prod \
|
||||||
|
--query principalId --output tsv)
|
||||||
|
|
||||||
|
# Get ACR Resource ID (from shared resource group)
|
||||||
|
ACR_ID=$(az acr show \
|
||||||
|
--name zuyderlandacr \
|
||||||
|
--resource-group rg-shared-services \
|
||||||
|
--query id --output tsv)
|
||||||
|
|
||||||
|
# Grant AcrPull permission
|
||||||
|
az role assignment create \
|
||||||
|
--assignee $BACKEND_PRINCIPAL_ID \
|
||||||
|
--role AcrPull \
|
||||||
|
--scope $ACR_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 Managing Multiple Applications
|
||||||
|
|
||||||
|
### List All Repositories
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# See all applications in ACR
|
||||||
|
az acr repository list --name zuyderlandacr
|
||||||
|
|
||||||
|
# Output:
|
||||||
|
# cmdb-insight
|
||||||
|
# other-app
|
||||||
|
# shared-services
|
||||||
|
```
|
||||||
|
|
||||||
|
### List Images for This App
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend images
|
||||||
|
az acr repository show-tags \
|
||||||
|
--name zuyderlandacr \
|
||||||
|
--repository cmdb-insight/backend
|
||||||
|
|
||||||
|
# Frontend images
|
||||||
|
az acr repository show-tags \
|
||||||
|
--name zuyderlandacr \
|
||||||
|
--repository cmdb-insight/frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Clean Up Old Images
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Delete old tags (keep last 10)
|
||||||
|
az acr repository show-tags \
|
||||||
|
--name zuyderlandacr \
|
||||||
|
--repository cmdb-insight/backend \
|
||||||
|
--orderby time_desc \
|
||||||
|
--query '[10:].name' \
|
||||||
|
--output tsv | \
|
||||||
|
xargs -I {} az acr repository delete \
|
||||||
|
--name zuyderlandacr \
|
||||||
|
--image cmdb-insight/backend:{} \
|
||||||
|
--yes
|
||||||
|
```
|
||||||
|
|
||||||
|
## 💰 Cost Optimization
|
||||||
|
|
||||||
|
### Shared ACR Costs
|
||||||
|
|
||||||
|
| SKU | Storage | Cost | Best For |
|
||||||
|
|-----|---------|------|----------|
|
||||||
|
| Basic | 10GB | €5/month | Small teams, few apps |
|
||||||
|
| Standard | 100GB | €20/month | **Recommended for shared ACR** |
|
||||||
|
| Premium | 500GB | €50/month | Large organizations |
|
||||||
|
|
||||||
|
**Recommendation**: Use **Standard** SKU for shared ACR:
|
||||||
|
- Enough storage for multiple applications
|
||||||
|
- Geo-replication available
|
||||||
|
- Good balance of cost and features
|
||||||
|
|
||||||
|
### Cost Savings Example
|
||||||
|
|
||||||
|
**Without sharing:**
|
||||||
|
- App 1 ACR: €20/month
|
||||||
|
- App 2 ACR: €20/month
|
||||||
|
- App 3 ACR: €20/month
|
||||||
|
- **Total: €60/month**
|
||||||
|
|
||||||
|
**With shared ACR:**
|
||||||
|
- Shared ACR (Standard): €20/month
|
||||||
|
- **Total: €20/month**
|
||||||
|
- **Savings: €40/month (67%)**
|
||||||
|
|
||||||
|
## 🎯 Best Practices
|
||||||
|
|
||||||
|
### 1. Naming Convention
|
||||||
|
|
||||||
|
Use consistent repository naming:
|
||||||
|
- `app-name/service:tag` (e.g., `cmdb-insight/backend:latest`)
|
||||||
|
- Avoid generic names like `backend`, `frontend`
|
||||||
|
- Include app identifier in repository name
|
||||||
|
|
||||||
|
### 2. Resource Group Organization
|
||||||
|
|
||||||
|
**Option A: Separate Resource Groups**
|
||||||
|
```
|
||||||
|
rg-shared-services/
|
||||||
|
└── ACR (shared)
|
||||||
|
|
||||||
|
rg-cmdb-insight-prod/
|
||||||
|
└── App-specific resources
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option B: Single Resource Group**
|
||||||
|
```
|
||||||
|
rg-production/
|
||||||
|
├── ACR
|
||||||
|
├── App 1 resources
|
||||||
|
├── App 2 resources
|
||||||
|
└── App 3 resources
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Access Control
|
||||||
|
|
||||||
|
- Use **Managed Identity** for App Services (recommended)
|
||||||
|
- Grant **AcrPull** role (not AcrPush) to App Services
|
||||||
|
- Use **Service Principals** for CI/CD pipelines
|
||||||
|
- Consider **Azure RBAC** for fine-grained access
|
||||||
|
|
||||||
|
### 4. Image Tagging Strategy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Use semantic versioning
|
||||||
|
cmdb-insight/backend:v1.0.0
|
||||||
|
cmdb-insight/backend:v1.0.1
|
||||||
|
cmdb-insight/backend:latest
|
||||||
|
|
||||||
|
# Use build IDs for CI/CD
|
||||||
|
cmdb-insight/backend:12345
|
||||||
|
cmdb-insight/backend:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 Migration from Dedicated ACR
|
||||||
|
|
||||||
|
If you have a dedicated ACR and want to migrate to shared:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Tag images with new repository name
|
||||||
|
docker tag oldacr.azurecr.io/backend:latest newacr.azurecr.io/cmdb-insight/backend:latest
|
||||||
|
docker tag oldacr.azurecr.io/frontend:latest newacr.azurecr.io/cmdb-insight/frontend:latest
|
||||||
|
|
||||||
|
# 2. Push to shared ACR
|
||||||
|
docker push newacr.azurecr.io/cmdb-insight/backend:latest
|
||||||
|
docker push newacr.azurecr.io/cmdb-insight/frontend:latest
|
||||||
|
|
||||||
|
# 3. Update App Services
|
||||||
|
az webapp config container set \
|
||||||
|
--name cmdb-backend-prod \
|
||||||
|
--resource-group rg-cmdb-insight-prod \
|
||||||
|
--docker-custom-image-name "newacr.azurecr.io/cmdb-insight/backend:latest"
|
||||||
|
|
||||||
|
# 4. Update pipeline variables
|
||||||
|
# 5. Test deployment
|
||||||
|
# 6. Delete old ACR (after verification)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 Related Documentation
|
||||||
|
|
||||||
|
- **`AZURE-NEW-SUBSCRIPTION-SETUP.md`** - Complete Azure setup guide
|
||||||
|
- **`AZURE-CONTAINER-REGISTRY.md`** - ACR setup and usage
|
||||||
|
- **`AZURE-PIPELINE-DEPLOYMENT.md`** - Automated deployment
|
||||||
|
|
||||||
|
## ✅ Checklist
|
||||||
|
|
||||||
|
- [ ] Decide: Use existing ACR or create new shared ACR
|
||||||
|
- [ ] Verify ACR exists or create new one
|
||||||
|
- [ ] Update pipeline variables with ACR name
|
||||||
|
- [ ] Grant App Services access to ACR
|
||||||
|
- [ ] Build and push images with repository name `cmdb-insight`
|
||||||
|
- [ ] Configure App Services to use shared ACR
|
||||||
|
- [ ] Test deployment
|
||||||
|
- [ ] Document ACR name for team
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**💡 Remember**: The repository name (`cmdb-insight`) is what separates your application from others in the shared ACR!
|
||||||
943
docs/AZURE-NEW-SUBSCRIPTION-SETUP.md
Normal file
943
docs/AZURE-NEW-SUBSCRIPTION-SETUP.md
Normal file
@@ -0,0 +1,943 @@
|
|||||||
|
# Azure New Subscription Setup Guide
|
||||||
|
|
||||||
|
Complete guide for setting up all required Azure resources for CMDB Insight in a new Azure subscription.
|
||||||
|
|
||||||
|
## 📋 Overview
|
||||||
|
|
||||||
|
This guide will help you create and configure all necessary Azure resources to deploy the CMDB Insight application. The setup includes:
|
||||||
|
|
||||||
|
### Required Resources
|
||||||
|
|
||||||
|
1. **Resource Group** - Container for all resources
|
||||||
|
2. **Azure Container Registry (ACR)** - Store Docker images
|
||||||
|
3. **Azure Database for PostgreSQL** - Production database (recommended)
|
||||||
|
4. **Azure Key Vault** - Secure storage for secrets
|
||||||
|
5. **Azure App Service Plan** - Hosting plan for web apps
|
||||||
|
6. **Azure App Service (Backend)** - Backend API service
|
||||||
|
7. **Azure App Service (Frontend)** - Frontend web application
|
||||||
|
8. **Application Insights** - Monitoring and logging
|
||||||
|
9. **DNS & SSL** - Custom domain and HTTPS certificate
|
||||||
|
|
||||||
|
### Estimated Costs
|
||||||
|
|
||||||
|
- **Basic Setup (SQLite)**: €17-35/month
|
||||||
|
- **Recommended Setup (PostgreSQL)**: €36-62/month
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Prerequisites
|
||||||
|
|
||||||
|
Before starting, ensure you have:
|
||||||
|
|
||||||
|
- [ ] Azure CLI installed (`az --version`)
|
||||||
|
- [ ] Azure subscription with appropriate permissions
|
||||||
|
- [ ] Docker installed (for local testing)
|
||||||
|
- [ ] Access to Azure Portal
|
||||||
|
- [ ] Jira credentials (OAuth client ID/secret or Personal Access Token)
|
||||||
|
|
||||||
|
### Install Azure CLI (if needed)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# macOS
|
||||||
|
brew install azure-cli
|
||||||
|
|
||||||
|
# Linux
|
||||||
|
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
|
||||||
|
|
||||||
|
# Windows
|
||||||
|
# Download from: https://aka.ms/installazurecliwindows
|
||||||
|
```
|
||||||
|
|
||||||
|
### Login to Azure
|
||||||
|
|
||||||
|
```bash
|
||||||
|
az login
|
||||||
|
az account list --output table
|
||||||
|
az account set --subscription "<subscription-id-or-name>"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Step 1: Create Resource Group
|
||||||
|
|
||||||
|
Create a resource group to organize all resources:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables (customize as needed)
|
||||||
|
RESOURCE_GROUP="rg-cmdb-insight-prod"
|
||||||
|
LOCATION="westeurope" # or your preferred region
|
||||||
|
|
||||||
|
# Create resource group
|
||||||
|
az group create \
|
||||||
|
--name $RESOURCE_GROUP \
|
||||||
|
--location $LOCATION
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
az group show --name $RESOURCE_GROUP
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: Choose a location close to your users. Common options:
|
||||||
|
- `westeurope` (Netherlands, Germany)
|
||||||
|
- `northeurope` (Ireland, UK)
|
||||||
|
- `eastus` (US East)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐳 Step 2: Create or Use Existing Azure Container Registry (ACR)
|
||||||
|
|
||||||
|
**Important**: Azure Container Registry can be **shared across multiple applications**. The repository name (`cmdb-insight`) is what separates this application from others in the same ACR.
|
||||||
|
|
||||||
|
### Option A: Use Existing ACR (Recommended if you have one)
|
||||||
|
|
||||||
|
If you already have an ACR for other applications, you can reuse it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables - use your existing ACR name
|
||||||
|
ACR_NAME="your-existing-acr" # Your existing ACR name
|
||||||
|
ACR_RESOURCE_GROUP="rg-shared-services" # Resource group where ACR exists
|
||||||
|
|
||||||
|
# Verify ACR exists
|
||||||
|
az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP
|
||||||
|
|
||||||
|
# Get ACR login server
|
||||||
|
ACR_LOGIN_SERVER=$(az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP --query loginServer --output tsv)
|
||||||
|
echo "ACR Login Server: $ACR_LOGIN_SERVER"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Benefits of reusing ACR**:
|
||||||
|
- ✅ Cost savings (one ACR for all apps)
|
||||||
|
- ✅ Centralized image management
|
||||||
|
- ✅ Easier to share images across teams
|
||||||
|
- ✅ Better resource utilization
|
||||||
|
|
||||||
|
### Option B: Create New ACR
|
||||||
|
|
||||||
|
If you don't have an ACR yet, create one:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
ACR_NAME="yourcompanyacr" # Must be globally unique, lowercase, 5-50 chars, alphanumeric only
|
||||||
|
ACR_RESOURCE_GROUP="rg-shared-services" # Or use your app resource group
|
||||||
|
SKU="Standard" # Options: Basic, Standard, Premium
|
||||||
|
|
||||||
|
# Create resource group for shared services (if needed)
|
||||||
|
az group create --name $ACR_RESOURCE_GROUP --location westeurope
|
||||||
|
|
||||||
|
# Create ACR
|
||||||
|
az acr create \
|
||||||
|
--resource-group $ACR_RESOURCE_GROUP \
|
||||||
|
--name $ACR_NAME \
|
||||||
|
--sku $SKU \
|
||||||
|
--admin-enabled true
|
||||||
|
|
||||||
|
# Get ACR login server
|
||||||
|
ACR_LOGIN_SERVER=$(az acr show --name $ACR_NAME --resource-group $ACR_RESOURCE_GROUP --query loginServer --output tsv)
|
||||||
|
echo "ACR Login Server: $ACR_LOGIN_SERVER"
|
||||||
|
|
||||||
|
# Get admin credentials (save these securely)
|
||||||
|
az acr credential show --name $ACR_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
**ACR SKU Comparison**:
|
||||||
|
- **Basic**: €5/month - Development/test, 10GB storage
|
||||||
|
- **Standard**: €20/month - Production, 100GB storage, geo-replication (recommended)
|
||||||
|
- **Premium**: €50/month - Enterprise, 500GB storage, advanced security
|
||||||
|
|
||||||
|
**Repository Structure in ACR**:
|
||||||
|
```
|
||||||
|
yourcompanyacr.azurecr.io/
|
||||||
|
├── cmdb-insight/ ← This application
|
||||||
|
│ ├── backend:latest
|
||||||
|
│ └── frontend:latest
|
||||||
|
├── other-app/ ← Other applications
|
||||||
|
│ ├── api:latest
|
||||||
|
│ └── web:latest
|
||||||
|
└── shared-services/ ← Shared images
|
||||||
|
└── nginx:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test ACR Connection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Login to ACR
|
||||||
|
az acr login --name $ACR_NAME
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
az acr repository list --name $ACR_NAME
|
||||||
|
|
||||||
|
# List repositories (you'll see cmdb-insight after first push)
|
||||||
|
az acr repository list --name $ACR_NAME --output table
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗄️ Step 3: Create Azure Database for PostgreSQL
|
||||||
|
|
||||||
|
PostgreSQL is recommended for production. Alternatively, you can use SQLite with Azure Storage (see Step 3B).
|
||||||
|
|
||||||
|
### Step 3A: PostgreSQL (Recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
DB_SERVER_NAME="cmdb-postgres-prod" # Must be globally unique
|
||||||
|
RESOURCE_GROUP="rg-cmdb-insight-prod"
|
||||||
|
DB_ADMIN_USER="cmdbadmin"
|
||||||
|
DB_ADMIN_PASSWORD="<generate-secure-password>" # Use a strong password!
|
||||||
|
DB_NAME="cmdb"
|
||||||
|
SKU="Standard_B1ms" # Burstable tier, 1 vCore, 2GB RAM
|
||||||
|
|
||||||
|
# Generate secure password (save this!)
|
||||||
|
DB_ADMIN_PASSWORD=$(openssl rand -base64 32)
|
||||||
|
echo "Database Password: $DB_ADMIN_PASSWORD"
|
||||||
|
|
||||||
|
# Create PostgreSQL Flexible Server
|
||||||
|
az postgres flexible-server create \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--name $DB_SERVER_NAME \
|
||||||
|
--location westeurope \
|
||||||
|
--admin-user $DB_ADMIN_USER \
|
||||||
|
--admin-password $DB_ADMIN_PASSWORD \
|
||||||
|
--sku-name $SKU \
|
||||||
|
--tier Burstable \
|
||||||
|
--storage-size 32 \
|
||||||
|
--version 15 \
|
||||||
|
--public-access 0.0.0.0 # Allow Azure services (restrict later if needed)
|
||||||
|
|
||||||
|
# Create database
|
||||||
|
az postgres flexible-server db create \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--server-name $DB_SERVER_NAME \
|
||||||
|
--database-name $DB_NAME
|
||||||
|
|
||||||
|
# Get connection string
|
||||||
|
DB_CONNECTION_STRING="postgresql://${DB_ADMIN_USER}:${DB_ADMIN_PASSWORD}@${DB_SERVER_NAME}.postgres.database.azure.com:5432/${DB_NAME}?sslmode=require"
|
||||||
|
echo "Database Connection String: $DB_CONNECTION_STRING"
|
||||||
|
|
||||||
|
# Save connection details securely
|
||||||
|
echo "DB_HOST=${DB_SERVER_NAME}.postgres.database.azure.com" > .env.azure
|
||||||
|
echo "DB_USER=${DB_ADMIN_USER}" >> .env.azure
|
||||||
|
echo "DB_PASSWORD=${DB_ADMIN_PASSWORD}" >> .env.azure
|
||||||
|
echo "DB_NAME=${DB_NAME}" >> .env.azure
|
||||||
|
```
|
||||||
|
|
||||||
|
**PostgreSQL SKU Options**:
|
||||||
|
- **Standard_B1ms**: €20-30/month - 1 vCore, 2GB RAM (recommended for 20 users)
|
||||||
|
- **Standard_B2s**: €40-50/month - 2 vCores, 4GB RAM (for growth)
|
||||||
|
- **Standard_D2s_v3**: €100+/month - 2 vCores, 8GB RAM (high performance)
|
||||||
|
|
||||||
|
### Step 3B: SQLite with Azure Storage (Alternative)
|
||||||
|
|
||||||
|
If you prefer to use SQLite (simpler, but less scalable):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
STORAGE_ACCOUNT_NAME="cmdbstorage$(openssl rand -hex 4)" # Must be globally unique, lowercase
|
||||||
|
RESOURCE_GROUP="rg-cmdb-insight-prod"
|
||||||
|
|
||||||
|
# Create storage account
|
||||||
|
az storage account create \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--name $STORAGE_ACCOUNT_NAME \
|
||||||
|
--location westeurope \
|
||||||
|
--sku Standard_LRS
|
||||||
|
|
||||||
|
# Get storage account key
|
||||||
|
STORAGE_KEY=$(az storage account keys list \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--account-name $STORAGE_ACCOUNT_NAME \
|
||||||
|
--query "[0].value" --output tsv)
|
||||||
|
|
||||||
|
echo "Storage Account: $STORAGE_ACCOUNT_NAME"
|
||||||
|
echo "Storage Key: $STORAGE_KEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: SQLite works but has limitations with concurrent users. PostgreSQL is recommended for production.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Step 4: Create Azure Key Vault
|
||||||
|
|
||||||
|
Key Vault securely stores secrets like API keys, passwords, and tokens.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
KEY_VAULT_NAME="kv-cmdb-insight-prod" # Must be globally unique
|
||||||
|
RESOURCE_GROUP="rg-cmdb-insight-prod"
|
||||||
|
|
||||||
|
# Create Key Vault
|
||||||
|
az keyvault create \
|
||||||
|
--name $KEY_VAULT_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--location westeurope \
|
||||||
|
--sku standard
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
az keyvault show --name $KEY_VAULT_NAME --resource-group $RESOURCE_GROUP
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add Secrets to Key Vault
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set your actual values
|
||||||
|
JIRA_PAT="your-jira-personal-access-token"
|
||||||
|
SESSION_SECRET=$(openssl rand -hex 32)
|
||||||
|
JIRA_OAUTH_CLIENT_ID="your-oauth-client-id"
|
||||||
|
JIRA_OAUTH_CLIENT_SECRET="your-oauth-client-secret"
|
||||||
|
JIRA_SCHEMA_ID="your-schema-id"
|
||||||
|
|
||||||
|
# Add secrets
|
||||||
|
az keyvault secret set \
|
||||||
|
--vault-name $KEY_VAULT_NAME \
|
||||||
|
--name "JiraPat" \
|
||||||
|
--value "$JIRA_PAT"
|
||||||
|
|
||||||
|
az keyvault secret set \
|
||||||
|
--vault-name $KEY_VAULT_NAME \
|
||||||
|
--name "SessionSecret" \
|
||||||
|
--value "$SESSION_SECRET"
|
||||||
|
|
||||||
|
az keyvault secret set \
|
||||||
|
--vault-name $KEY_VAULT_NAME \
|
||||||
|
--name "JiraOAuthClientId" \
|
||||||
|
--value "$JIRA_OAUTH_CLIENT_ID"
|
||||||
|
|
||||||
|
az keyvault secret set \
|
||||||
|
--vault-name $KEY_VAULT_NAME \
|
||||||
|
--name "JiraOAuthClientSecret" \
|
||||||
|
--value "$JIRA_OAUTH_CLIENT_SECRET"
|
||||||
|
|
||||||
|
az keyvault secret set \
|
||||||
|
--vault-name $KEY_VAULT_NAME \
|
||||||
|
--name "JiraSchemaId" \
|
||||||
|
--value "$JIRA_SCHEMA_ID"
|
||||||
|
|
||||||
|
# If using PostgreSQL, add database password
|
||||||
|
az keyvault secret set \
|
||||||
|
--vault-name $KEY_VAULT_NAME \
|
||||||
|
--name "DatabasePassword" \
|
||||||
|
--value "$DB_ADMIN_PASSWORD"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Step 5: Create Application Insights
|
||||||
|
|
||||||
|
Application Insights provides monitoring, logging, and performance metrics.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
APP_INSIGHTS_NAME="appi-cmdb-insight-prod"
|
||||||
|
RESOURCE_GROUP="rg-cmdb-insight-prod"
|
||||||
|
|
||||||
|
# Create Application Insights
|
||||||
|
az monitor app-insights component create \
|
||||||
|
--app $APP_INSIGHTS_NAME \
|
||||||
|
--location westeurope \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--application-type web
|
||||||
|
|
||||||
|
# Get Instrumentation Key
|
||||||
|
INSTRUMENTATION_KEY=$(az monitor app-insights component show \
|
||||||
|
--app $APP_INSIGHTS_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--query instrumentationKey --output tsv)
|
||||||
|
|
||||||
|
echo "Instrumentation Key: $INSTRUMENTATION_KEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: Application Insights Basic tier is free up to 5GB/month, which is sufficient for most small applications.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🖥️ Step 6: Create App Service Plan
|
||||||
|
|
||||||
|
App Service Plan defines the compute resources for your web apps.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
APP_SERVICE_PLAN_NAME="plan-cmdb-insight-prod"
|
||||||
|
RESOURCE_GROUP="rg-cmdb-insight-prod"
|
||||||
|
SKU="B1" # Basic tier, 1 vCore, 1.75GB RAM
|
||||||
|
|
||||||
|
# Create App Service Plan (Linux)
|
||||||
|
az appservice plan create \
|
||||||
|
--name $APP_SERVICE_PLAN_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--sku $SKU \
|
||||||
|
--is-linux
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
az appservice plan show --name $APP_SERVICE_PLAN_NAME --resource-group $RESOURCE_GROUP
|
||||||
|
```
|
||||||
|
|
||||||
|
**App Service Plan SKU Options**:
|
||||||
|
- **B1**: €15-25/month - 1 vCore, 1.75GB RAM (recommended for 20 users)
|
||||||
|
- **B2**: €30-40/month - 2 vCores, 3.5GB RAM
|
||||||
|
- **S1**: €50-70/month - 1 vCore, 1.75GB RAM (Standard tier, better performance)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Step 7: Create App Services (Backend & Frontend)
|
||||||
|
|
||||||
|
Create two web apps: one for backend API and one for frontend.
|
||||||
|
|
||||||
|
### Step 7A: Create Backend App Service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
BACKEND_APP_NAME="cmdb-backend-prod" # Must be globally unique
|
||||||
|
RESOURCE_GROUP="rg-cmdb-insight-prod"
|
||||||
|
APP_SERVICE_PLAN_NAME="plan-cmdb-insight-prod"
|
||||||
|
ACR_NAME="cmdbinsightacr" # From Step 2
|
||||||
|
|
||||||
|
# Create backend web app
|
||||||
|
az webapp create \
|
||||||
|
--name $BACKEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--plan $APP_SERVICE_PLAN_NAME \
|
||||||
|
--deployment-container-image-name "${ACR_NAME}.azurecr.io/cmdb-insight/backend:latest"
|
||||||
|
|
||||||
|
# Enable Managed Identity (for ACR access)
|
||||||
|
az webapp identity assign \
|
||||||
|
--name $BACKEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP
|
||||||
|
|
||||||
|
# Get Managed Identity Principal ID
|
||||||
|
BACKEND_PRINCIPAL_ID=$(az webapp identity show \
|
||||||
|
--name $BACKEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--query principalId --output tsv)
|
||||||
|
|
||||||
|
# Grant ACR pull permissions
|
||||||
|
ACR_ID=$(az acr show --name $ACR_NAME --resource-group $RESOURCE_GROUP --query id --output tsv)
|
||||||
|
az role assignment create \
|
||||||
|
--assignee $BACKEND_PRINCIPAL_ID \
|
||||||
|
--role AcrPull \
|
||||||
|
--scope $ACR_ID
|
||||||
|
|
||||||
|
# Configure container settings
|
||||||
|
az webapp config container set \
|
||||||
|
--name $BACKEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--docker-custom-image-name "${ACR_NAME}.azurecr.io/cmdb-insight/backend:latest" \
|
||||||
|
--docker-registry-server-url "https://${ACR_NAME}.azurecr.io"
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
az webapp config appsettings set \
|
||||||
|
--name $BACKEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--settings \
|
||||||
|
NODE_ENV=production \
|
||||||
|
PORT=3001 \
|
||||||
|
DATABASE_TYPE=postgres \
|
||||||
|
DATABASE_URL="@Microsoft.KeyVault(SecretUri=https://${KEY_VAULT_NAME}.vault.azure.net/secrets/DatabasePassword/)" \
|
||||||
|
JIRA_HOST=https://jira.zuyderland.nl \
|
||||||
|
JIRA_AUTH_METHOD=oauth \
|
||||||
|
JIRA_OAUTH_CLIENT_ID="@Microsoft.KeyVault(SecretUri=https://${KEY_VAULT_NAME}.vault.azure.net/secrets/JiraOAuthClientId/)" \
|
||||||
|
JIRA_OAUTH_CLIENT_SECRET="@Microsoft.KeyVault(SecretUri=https://${KEY_VAULT_NAME}.vault.azure.net/secrets/JiraOAuthClientSecret/)" \
|
||||||
|
JIRA_OAUTH_CALLBACK_URL="https://${BACKEND_APP_NAME}.azurewebsites.net/api/auth/callback" \
|
||||||
|
JIRA_SCHEMA_ID="@Microsoft.KeyVault(SecretUri=https://${KEY_VAULT_NAME}.vault.azure.net/secrets/JiraSchemaId/)" \
|
||||||
|
SESSION_SECRET="@Microsoft.KeyVault(SecretUri=https://${KEY_VAULT_NAME}.vault.azure.net/secrets/SessionSecret/)" \
|
||||||
|
FRONTEND_URL="https://${FRONTEND_APP_NAME}.azurewebsites.net" \
|
||||||
|
APPINSIGHTS_INSTRUMENTATIONKEY="${INSTRUMENTATION_KEY}"
|
||||||
|
|
||||||
|
# Grant Key Vault access to backend
|
||||||
|
az keyvault set-policy \
|
||||||
|
--name $KEY_VAULT_NAME \
|
||||||
|
--object-id $BACKEND_PRINCIPAL_ID \
|
||||||
|
--secret-permissions get list
|
||||||
|
|
||||||
|
# Enable HTTPS only
|
||||||
|
az webapp update \
|
||||||
|
--name $BACKEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--https-only true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 7B: Create Frontend App Service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
FRONTEND_APP_NAME="cmdb-frontend-prod" # Must be globally unique
|
||||||
|
RESOURCE_GROUP="rg-cmdb-insight-prod"
|
||||||
|
APP_SERVICE_PLAN_NAME="plan-cmdb-insight-prod"
|
||||||
|
ACR_NAME="cmdbinsightacr" # From Step 2
|
||||||
|
|
||||||
|
# Create frontend web app
|
||||||
|
az webapp create \
|
||||||
|
--name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--plan $APP_SERVICE_PLAN_NAME \
|
||||||
|
--deployment-container-image-name "${ACR_NAME}.azurecr.io/cmdb-insight/frontend:latest"
|
||||||
|
|
||||||
|
# Enable Managed Identity
|
||||||
|
az webapp identity assign \
|
||||||
|
--name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP
|
||||||
|
|
||||||
|
# Get Managed Identity Principal ID
|
||||||
|
FRONTEND_PRINCIPAL_ID=$(az webapp identity show \
|
||||||
|
--name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--query principalId --output tsv)
|
||||||
|
|
||||||
|
# Grant ACR pull permissions
|
||||||
|
az role assignment create \
|
||||||
|
--assignee $FRONTEND_PRINCIPAL_ID \
|
||||||
|
--role AcrPull \
|
||||||
|
--scope $ACR_ID
|
||||||
|
|
||||||
|
# Configure container settings
|
||||||
|
az webapp config container set \
|
||||||
|
--name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--docker-custom-image-name "${ACR_NAME}.azurecr.io/cmdb-insight/frontend:latest" \
|
||||||
|
--docker-registry-server-url "https://${ACR_NAME}.azurecr.io"
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
az webapp config appsettings set \
|
||||||
|
--name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--settings \
|
||||||
|
VITE_API_URL="https://${BACKEND_APP_NAME}.azurewebsites.net/api"
|
||||||
|
|
||||||
|
# Enable HTTPS only
|
||||||
|
az webapp update \
|
||||||
|
--name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--https-only true
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Step 8: Build and Push Docker Images
|
||||||
|
|
||||||
|
Before the apps can run, you need to build and push Docker images to ACR.
|
||||||
|
|
||||||
|
### Option A: Using Script (Recommended)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Navigate to project root
|
||||||
|
cd /path/to/cmdb-insight
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
export ACR_NAME="cmdbinsightacr" # Your ACR name
|
||||||
|
export REPO_NAME="cmdb-insight"
|
||||||
|
|
||||||
|
# Build and push
|
||||||
|
./scripts/build-and-push-azure.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B: Manual Build and Push
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
ACR_NAME="cmdbinsightacr"
|
||||||
|
REGISTRY="${ACR_NAME}.azurecr.io"
|
||||||
|
REPO_NAME="cmdb-insight"
|
||||||
|
|
||||||
|
# Login to ACR
|
||||||
|
az acr login --name $ACR_NAME
|
||||||
|
|
||||||
|
# Build backend
|
||||||
|
docker build -t ${REGISTRY}/${REPO_NAME}/backend:latest \
|
||||||
|
-f backend/Dockerfile.prod ./backend
|
||||||
|
|
||||||
|
# Build frontend
|
||||||
|
docker build -t ${REGISTRY}/${REPO_NAME}/frontend:latest \
|
||||||
|
-f frontend/Dockerfile.prod ./frontend
|
||||||
|
|
||||||
|
# Push images
|
||||||
|
docker push ${REGISTRY}/${REPO_NAME}/backend:latest
|
||||||
|
docker push ${REGISTRY}/${REPO_NAME}/frontend:latest
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
az acr repository list --name $ACR_NAME
|
||||||
|
az acr repository show-tags --name $ACR_NAME --repository ${REPO_NAME}/backend
|
||||||
|
az acr repository show-tags --name $ACR_NAME --repository ${REPO_NAME}/frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌐 Step 9: Configure Custom Domain and SSL (Optional)
|
||||||
|
|
||||||
|
If you have a custom domain (e.g., `cmdb.yourcompany.com`):
|
||||||
|
|
||||||
|
### Step 9A: Add Custom Domain
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
FRONTEND_APP_NAME="cmdb-frontend-prod"
|
||||||
|
BACKEND_APP_NAME="cmdb-backend-prod"
|
||||||
|
CUSTOM_DOMAIN="cmdb.yourcompany.com"
|
||||||
|
RESOURCE_GROUP="rg-cmdb-insight-prod"
|
||||||
|
|
||||||
|
# Add custom domain to frontend
|
||||||
|
az webapp config hostname add \
|
||||||
|
--webapp-name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--hostname $CUSTOM_DOMAIN
|
||||||
|
|
||||||
|
# Add custom domain to backend (if needed)
|
||||||
|
az webapp config hostname add \
|
||||||
|
--webapp-name $BACKEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--hostname "api.${CUSTOM_DOMAIN}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 9B: Configure SSL Certificate
|
||||||
|
|
||||||
|
**Option 1: App Service Managed Certificate (Free, Recommended)**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create managed certificate for frontend
|
||||||
|
az webapp config ssl create \
|
||||||
|
--name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--hostname $CUSTOM_DOMAIN
|
||||||
|
|
||||||
|
# Bind certificate
|
||||||
|
az webapp config ssl bind \
|
||||||
|
--name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--certificate-thumbprint <thumbprint> \
|
||||||
|
--ssl-type SNI
|
||||||
|
```
|
||||||
|
|
||||||
|
**Option 2: Import Existing Certificate**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Upload certificate
|
||||||
|
az webapp config ssl upload \
|
||||||
|
--name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--certificate-file /path/to/certificate.pfx \
|
||||||
|
--certificate-password <password>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: You'll need to update DNS records to point to your App Service. Get the IP address:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
az webapp show --name $FRONTEND_APP_NAME --resource-group $RESOURCE_GROUP --query defaultHostName
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Step 10: Verify Deployment
|
||||||
|
|
||||||
|
### Check App Status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check backend
|
||||||
|
az webapp show --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP --query state
|
||||||
|
|
||||||
|
# Check frontend
|
||||||
|
az webapp show --name $FRONTEND_APP_NAME --resource-group $RESOURCE_GROUP --query state
|
||||||
|
|
||||||
|
# Start apps if needed
|
||||||
|
az webapp start --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP
|
||||||
|
az webapp start --name $FRONTEND_APP_NAME --resource-group $RESOURCE_GROUP
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Health Endpoints
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend health check
|
||||||
|
curl https://${BACKEND_APP_NAME}.azurewebsites.net/api/health
|
||||||
|
|
||||||
|
# Frontend
|
||||||
|
curl https://${FRONTEND_APP_NAME}.azurewebsites.net
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend logs
|
||||||
|
az webapp log tail --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP
|
||||||
|
|
||||||
|
# Frontend logs
|
||||||
|
az webapp log tail --name $FRONTEND_APP_NAME --resource-group $RESOURCE_GROUP
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Step 11: Configure Database Schema
|
||||||
|
|
||||||
|
If using PostgreSQL, you need to initialize the database schema:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Connect to database and run schema initialization
|
||||||
|
# Option 1: Using psql
|
||||||
|
psql "postgresql://${DB_ADMIN_USER}:${DB_ADMIN_PASSWORD}@${DB_SERVER_NAME}.postgres.database.azure.com:5432/${DB_NAME}?sslmode=require"
|
||||||
|
|
||||||
|
# Option 2: Using Azure Cloud Shell or local script
|
||||||
|
# The application will create tables automatically on first run
|
||||||
|
# Or use the migration scripts in backend/scripts/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: The application will automatically create the required database schema on first startup if it doesn't exist.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 Step 12: Update Environment Variables (If Needed)
|
||||||
|
|
||||||
|
If you need to update any configuration:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update backend settings
|
||||||
|
az webapp config appsettings set \
|
||||||
|
--name $BACKEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--settings \
|
||||||
|
NEW_SETTING=value
|
||||||
|
|
||||||
|
# Update frontend settings
|
||||||
|
az webapp config appsettings set \
|
||||||
|
--name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--settings \
|
||||||
|
VITE_API_URL="https://${BACKEND_APP_NAME}.azurewebsites.net/api"
|
||||||
|
|
||||||
|
# Restart apps to apply changes
|
||||||
|
az webapp restart --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP
|
||||||
|
az webapp restart --name $FRONTEND_APP_NAME --resource-group $RESOURCE_GROUP
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Step 13: Set Up CI/CD with Automated Deployment
|
||||||
|
|
||||||
|
### Azure DevOps Pipeline Setup
|
||||||
|
|
||||||
|
The project includes an automated pipeline that builds, pushes, and deploys your application.
|
||||||
|
|
||||||
|
1. **Create Service Connections**:
|
||||||
|
|
||||||
|
**A) Docker Registry Connection (for building images)**:
|
||||||
|
- Go to Azure DevOps → Project Settings → Service connections
|
||||||
|
- Create new **Docker Registry** connection
|
||||||
|
- Select **Azure Container Registry**
|
||||||
|
- Choose your subscription and ACR
|
||||||
|
- Name: `zuyderland-cmdb-acr-connection` (or match your variable)
|
||||||
|
|
||||||
|
**B) Azure Resource Manager Connection (for deployment)**:
|
||||||
|
- Create new **Azure Resource Manager** connection
|
||||||
|
- Select your subscription
|
||||||
|
- Name: `zuyderland-cmdb-subscription` (or match your variable)
|
||||||
|
|
||||||
|
2. **Configure Pipeline Variables**:
|
||||||
|
|
||||||
|
Update `azure-pipelines.yml` with your values:
|
||||||
|
```yaml
|
||||||
|
variables:
|
||||||
|
acrName: 'cmdbinsightacr' # Your ACR name
|
||||||
|
resourceGroup: 'rg-cmdb-insight-prod' # Your resource group
|
||||||
|
backendAppName: 'cmdb-backend-prod' # Your backend app name
|
||||||
|
frontendAppName: 'cmdb-frontend-prod' # Your frontend app name
|
||||||
|
azureSubscription: 'zuyderland-cmdb-subscription' # Azure service connection
|
||||||
|
dockerRegistryServiceConnection: 'zuyderland-cmdb-acr-connection'
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Create Environment**:
|
||||||
|
- Go to **Pipelines** → **Environments**
|
||||||
|
- Create environment: `production`
|
||||||
|
- (Optional) Add approvals for manual deployment control
|
||||||
|
|
||||||
|
4. **Run Pipeline**:
|
||||||
|
- Push to `main` branch → **Automatically builds AND deploys**
|
||||||
|
- Pipeline will:
|
||||||
|
- Build Docker images
|
||||||
|
- Push to ACR
|
||||||
|
- Deploy to App Services
|
||||||
|
- Verify deployment
|
||||||
|
|
||||||
|
### Zero-Downtime Deployment (Optional)
|
||||||
|
|
||||||
|
For production with zero downtime, use deployment slots:
|
||||||
|
|
||||||
|
1. **Create Staging Slots**:
|
||||||
|
```bash
|
||||||
|
az webapp deployment slot create \
|
||||||
|
--name $BACKEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--slot staging
|
||||||
|
|
||||||
|
az webapp deployment slot create \
|
||||||
|
--name $FRONTEND_APP_NAME \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--slot staging
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Use Advanced Pipeline**:
|
||||||
|
- Use `azure-pipelines-slots.yml` instead
|
||||||
|
- Deploys to staging first
|
||||||
|
- Swaps to production after verification
|
||||||
|
|
||||||
|
**See `docs/AZURE-PIPELINE-DEPLOYMENT.md` for complete setup guide.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Troubleshooting
|
||||||
|
|
||||||
|
### App Service Won't Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
az webapp log tail --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP
|
||||||
|
|
||||||
|
# Check container logs
|
||||||
|
az webapp log show --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP
|
||||||
|
|
||||||
|
# Check app status
|
||||||
|
az webapp show --name $BACKEND_APP_NAME --resource-group $RESOURCE_GROUP --query state
|
||||||
|
```
|
||||||
|
|
||||||
|
### ACR Authentication Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Re-authenticate
|
||||||
|
az acr login --name $ACR_NAME
|
||||||
|
|
||||||
|
# Check Managed Identity permissions
|
||||||
|
az role assignment list --assignee $BACKEND_PRINCIPAL_ID --scope $ACR_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Connection Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test database connection
|
||||||
|
psql "postgresql://${DB_ADMIN_USER}:${DB_ADMIN_PASSWORD}@${DB_SERVER_NAME}.postgres.database.azure.com:5432/${DB_NAME}?sslmode=require"
|
||||||
|
|
||||||
|
# Check firewall rules
|
||||||
|
az postgres flexible-server firewall-rule list \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--name $DB_SERVER_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Vault Access Issues
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check Key Vault policies
|
||||||
|
az keyvault show --name $KEY_VAULT_NAME --resource-group $RESOURCE_GROUP
|
||||||
|
|
||||||
|
# Verify Managed Identity has access
|
||||||
|
az keyvault show-policy --name $KEY_VAULT_NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Monitoring
|
||||||
|
|
||||||
|
### Application Insights
|
||||||
|
|
||||||
|
1. Go to Azure Portal → Application Insights → Your app
|
||||||
|
2. View:
|
||||||
|
- **Live Metrics**: Real-time performance
|
||||||
|
- **Application Map**: Service dependencies
|
||||||
|
- **Logs**: Query application logs
|
||||||
|
- **Metrics**: Performance metrics
|
||||||
|
|
||||||
|
### Set Up Alerts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create alert for app downtime
|
||||||
|
az monitor metrics alert create \
|
||||||
|
--name "Backend-Down" \
|
||||||
|
--resource-group $RESOURCE_GROUP \
|
||||||
|
--scopes "/subscriptions/<subscription-id>/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Web/sites/${BACKEND_APP_NAME}" \
|
||||||
|
--condition "avg AvailabilityPercentage < 99" \
|
||||||
|
--window-size 5m \
|
||||||
|
--evaluation-frequency 1m
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💰 Cost Optimization
|
||||||
|
|
||||||
|
### Current Setup Costs
|
||||||
|
|
||||||
|
| Resource | SKU | Estimated Monthly Cost |
|
||||||
|
|----------|-----|------------------------|
|
||||||
|
| App Service Plan | B1 | €15-25 |
|
||||||
|
| PostgreSQL | Standard_B1ms | €20-30 |
|
||||||
|
| Container Registry | Basic | €5 |
|
||||||
|
| Key Vault | Standard | €1-2 |
|
||||||
|
| Application Insights | Basic | €0-5 (free tier) |
|
||||||
|
| **Total** | | **€41-67/month** |
|
||||||
|
|
||||||
|
### Cost Saving Tips
|
||||||
|
|
||||||
|
1. **Use Basic tier ACR** for development (€5 vs €20)
|
||||||
|
2. **Application Insights Basic** is free up to 5GB/month
|
||||||
|
3. **Stop App Services** when not in use (dev/test environments)
|
||||||
|
4. **Use SQLite** instead of PostgreSQL (saves €20-30/month, but less scalable)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Next Steps
|
||||||
|
|
||||||
|
1. **Configure DNS**: Point your domain to App Service
|
||||||
|
2. **Set up SSL**: Configure HTTPS certificate
|
||||||
|
3. **Test Application**: Verify all features work
|
||||||
|
4. **Set up Monitoring**: Configure alerts
|
||||||
|
5. **Document Access**: Share URLs and credentials with team
|
||||||
|
6. **Backup Strategy**: Plan for database backups (if needed)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔗 Useful Commands Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List all resources
|
||||||
|
az resource list --resource-group $RESOURCE_GROUP --output table
|
||||||
|
|
||||||
|
# Get resource IDs
|
||||||
|
az acr show --name $ACR_NAME --resource-group $RESOURCE_GROUP --query id
|
||||||
|
az postgres flexible-server show --name $DB_SERVER_NAME --resource-group $RESOURCE_GROUP --query id
|
||||||
|
|
||||||
|
# Export resource configuration
|
||||||
|
az group export --name $RESOURCE_GROUP --output-file resources.json
|
||||||
|
|
||||||
|
# Delete all resources (careful!)
|
||||||
|
az group delete --name $RESOURCE_GROUP --yes --no-wait
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📖 Additional Resources
|
||||||
|
|
||||||
|
- [Azure App Service Documentation](https://docs.microsoft.com/azure/app-service/)
|
||||||
|
- [Azure Container Registry Documentation](https://docs.microsoft.com/azure/container-registry/)
|
||||||
|
- [Azure Database for PostgreSQL Documentation](https://docs.microsoft.com/azure/postgresql/)
|
||||||
|
- [Azure Key Vault Documentation](https://docs.microsoft.com/azure/key-vault/)
|
||||||
|
- [Application Insights Documentation](https://docs.microsoft.com/azure/azure-monitor/app/app-insights-overview)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Deployment Checklist
|
||||||
|
|
||||||
|
- [ ] Resource Group created
|
||||||
|
- [ ] Azure Container Registry created and accessible
|
||||||
|
- [ ] PostgreSQL database created (or SQLite storage configured)
|
||||||
|
- [ ] Key Vault created with all secrets
|
||||||
|
- [ ] Application Insights created
|
||||||
|
- [ ] App Service Plan created
|
||||||
|
- [ ] Backend App Service created and configured
|
||||||
|
- [ ] Frontend App Service created and configured
|
||||||
|
- [ ] Docker images built and pushed to ACR
|
||||||
|
- [ ] Apps started and running
|
||||||
|
- [ ] Health checks passing
|
||||||
|
- [ ] Custom domain configured (if applicable)
|
||||||
|
- [ ] SSL certificate configured (if applicable)
|
||||||
|
- [ ] Monitoring and alerts configured
|
||||||
|
- [ ] Team access configured
|
||||||
|
- [ ] Documentation updated
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**🎉 Congratulations! Your CMDB Insight application is now deployed to Azure!**
|
||||||
|
|
||||||
|
For questions or issues, refer to:
|
||||||
|
- `AZURE-APP-SERVICE-DEPLOYMENT.md` - Detailed App Service deployment guide
|
||||||
|
- `AZURE-CONTAINER-REGISTRY.md` - ACR setup and usage
|
||||||
|
- `PRODUCTION-DEPLOYMENT.md` - General production deployment guide
|
||||||
337
docs/AZURE-PIPELINE-DEPLOYMENT.md
Normal file
337
docs/AZURE-PIPELINE-DEPLOYMENT.md
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
# Azure Pipeline Automated Deployment Guide
|
||||||
|
|
||||||
|
Complete guide for setting up automated deployment from Azure DevOps Pipeline to Azure App Services.
|
||||||
|
|
||||||
|
## 📋 Overview
|
||||||
|
|
||||||
|
The enhanced `azure-pipelines.yml` now includes:
|
||||||
|
- ✅ **Build Stage**: Builds and pushes Docker images to ACR
|
||||||
|
- ✅ **Deploy Stage**: Automatically deploys to Azure App Services
|
||||||
|
- ✅ **Verification**: Health checks after deployment
|
||||||
|
|
||||||
|
## 🚀 Quick Setup
|
||||||
|
|
||||||
|
### Step 1: Configure Pipeline Variables
|
||||||
|
|
||||||
|
Update the variables in `azure-pipelines.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
variables:
|
||||||
|
# Azure Container Registry
|
||||||
|
acrName: 'zdlas' # Your ACR name
|
||||||
|
repositoryName: 'cmdb-insight'
|
||||||
|
dockerRegistryServiceConnection: 'zuyderland-cmdb-acr-connection'
|
||||||
|
|
||||||
|
# Azure App Service
|
||||||
|
resourceGroup: 'rg-cmdb-insight-prod' # Your resource group
|
||||||
|
backendAppName: 'cmdb-backend-prod' # Your backend app name
|
||||||
|
frontendAppName: 'cmdb-frontend-prod' # Your frontend app name
|
||||||
|
azureSubscription: 'zuyderland-cmdb-subscription' # Azure service connection
|
||||||
|
|
||||||
|
# Deployment
|
||||||
|
deployToProduction: true # Set false to skip deployment
|
||||||
|
useDeploymentSlots: false # Set true for zero-downtime deployment
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Create Azure Service Connection
|
||||||
|
|
||||||
|
You need an Azure service connection for App Service deployment:
|
||||||
|
|
||||||
|
1. **Go to Azure DevOps** → Your Project
|
||||||
|
2. **Project Settings** → **Service connections** → **New service connection**
|
||||||
|
3. Choose **Azure Resource Manager**
|
||||||
|
4. Select:
|
||||||
|
- **Authentication method**: Managed identity (recommended) or Service principal
|
||||||
|
- **Azure subscription**: Your subscription
|
||||||
|
- **Resource group**: Your resource group (optional)
|
||||||
|
5. **Service connection name**: `zuyderland-cmdb-subscription` (match the variable name)
|
||||||
|
6. Click **Save**
|
||||||
|
|
||||||
|
### Step 3: Configure Environment
|
||||||
|
|
||||||
|
The pipeline uses an `environment` called `production`:
|
||||||
|
|
||||||
|
1. **Go to Pipelines** → **Environments**
|
||||||
|
2. Click **Create environment**
|
||||||
|
3. Name: `production`
|
||||||
|
4. Add **Approvals and checks** (optional):
|
||||||
|
- **Approvals**: Require manual approval before deployment
|
||||||
|
- **Gate**: Health checks before deployment
|
||||||
|
|
||||||
|
### Step 4: Run Pipeline
|
||||||
|
|
||||||
|
The pipeline will automatically:
|
||||||
|
1. Build Docker images
|
||||||
|
2. Push to ACR
|
||||||
|
3. Deploy to App Services
|
||||||
|
4. Verify deployment
|
||||||
|
|
||||||
|
**Trigger automatically on:**
|
||||||
|
- Push to `main` branch
|
||||||
|
- Git tags starting with `v*`
|
||||||
|
|
||||||
|
## 🔧 Configuration Options
|
||||||
|
|
||||||
|
### Enable/Disable Deployment
|
||||||
|
|
||||||
|
To skip deployment (only build images):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
variables:
|
||||||
|
deployToProduction: false
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use pipeline variables in Azure DevOps:
|
||||||
|
1. Go to **Pipelines** → Your pipeline → **Edit**
|
||||||
|
2. Click **Variables**
|
||||||
|
3. Add variable: `deployToProduction` = `false`
|
||||||
|
|
||||||
|
### Use Specific Image Tag
|
||||||
|
|
||||||
|
By default, the pipeline deploys the `latest` tag. To deploy a specific version:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# In the Deploy stage, change:
|
||||||
|
containers: '$(acrName).azurecr.io/$(repositoryName)/backend:$(imageTag)'
|
||||||
|
```
|
||||||
|
|
||||||
|
This will deploy the specific build ID instead of `latest`.
|
||||||
|
|
||||||
|
## 🎯 Zero-Downtime Deployment (Deployment Slots)
|
||||||
|
|
||||||
|
For production deployments without downtime, use deployment slots:
|
||||||
|
|
||||||
|
### Step 1: Create Deployment Slots
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create staging slot for backend
|
||||||
|
az webapp deployment slot create \
|
||||||
|
--name cmdb-backend-prod \
|
||||||
|
--resource-group rg-cmdb-insight-prod \
|
||||||
|
--slot staging
|
||||||
|
|
||||||
|
# Create staging slot for frontend
|
||||||
|
az webapp deployment slot create \
|
||||||
|
--name cmdb-frontend-prod \
|
||||||
|
--resource-group rg-cmdb-insight-prod \
|
||||||
|
--slot staging
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Update Pipeline for Slots
|
||||||
|
|
||||||
|
Create `azure-pipelines-slots.yml` (see advanced example below) or modify the existing pipeline:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- task: AzureWebAppContainer@1
|
||||||
|
displayName: 'Deploy to Staging Slot'
|
||||||
|
inputs:
|
||||||
|
azureSubscription: '$(azureSubscription)'
|
||||||
|
appName: '$(backendAppName)'
|
||||||
|
deployToSlotOrASE: true
|
||||||
|
resourceGroupName: '$(resourceGroup)'
|
||||||
|
slotName: 'staging'
|
||||||
|
containers: '$(acrName).azurecr.io/$(repositoryName)/backend:latest'
|
||||||
|
|
||||||
|
- task: AzureCLI@2
|
||||||
|
displayName: 'Swap Staging to Production'
|
||||||
|
inputs:
|
||||||
|
azureSubscription: '$(azureSubscription)'
|
||||||
|
scriptType: 'bash'
|
||||||
|
scriptLocation: 'inlineScript'
|
||||||
|
inlineScript: |
|
||||||
|
az webapp deployment slot swap \
|
||||||
|
--name $(backendAppName) \
|
||||||
|
--resource-group $(resourceGroup) \
|
||||||
|
--slot staging \
|
||||||
|
--target-slot production
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 Pipeline Stages
|
||||||
|
|
||||||
|
### Stage 1: Build
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Builds backend Docker image
|
||||||
|
- Builds frontend Docker image
|
||||||
|
- Pushes both to ACR with tags: `$(Build.BuildId)` and `latest`
|
||||||
|
|
||||||
|
**Output:**
|
||||||
|
- `backendImage`: Full image URL for backend
|
||||||
|
- `frontendImage`: Full image URL for frontend
|
||||||
|
|
||||||
|
### Stage 2: Deploy
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Deploys backend container to App Service
|
||||||
|
- Deploys frontend container to App Service
|
||||||
|
- Restarts both App Services
|
||||||
|
- Verifies deployment with health checks
|
||||||
|
|
||||||
|
**Conditions:**
|
||||||
|
- Only runs if `deployToProduction = true`
|
||||||
|
- Only runs if Build stage succeeded
|
||||||
|
|
||||||
|
### Stage 3: Verify
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- Checks backend health endpoint (`/api/health`)
|
||||||
|
- Checks frontend accessibility
|
||||||
|
- Reports status
|
||||||
|
|
||||||
|
## 🔐 Permissions Required
|
||||||
|
|
||||||
|
The Azure service connection needs:
|
||||||
|
|
||||||
|
1. **App Service Contributor** role on:
|
||||||
|
- Backend App Service
|
||||||
|
- Frontend App Service
|
||||||
|
- App Service Plan
|
||||||
|
|
||||||
|
2. **ACR Pull** permissions (if using Managed Identity):
|
||||||
|
- Already configured via Managed Identity on App Services
|
||||||
|
|
||||||
|
### Grant Permissions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get service principal ID from Azure DevOps service connection
|
||||||
|
# Then grant permissions:
|
||||||
|
|
||||||
|
az role assignment create \
|
||||||
|
--assignee <service-principal-id> \
|
||||||
|
--role "Website Contributor" \
|
||||||
|
--scope /subscriptions/<subscription-id>/resourceGroups/rg-cmdb-insight-prod
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🛠️ Troubleshooting
|
||||||
|
|
||||||
|
### Deployment Fails: "Service connection not found"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
- Verify service connection name matches `azureSubscription` variable
|
||||||
|
- Check service connection exists in Project Settings → Service connections
|
||||||
|
- Verify service connection has correct permissions
|
||||||
|
|
||||||
|
### Deployment Fails: "App Service not found"
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
- Verify `backendAppName` and `frontendAppName` variables are correct
|
||||||
|
- Check `resourceGroup` variable matches your resource group
|
||||||
|
- Verify App Services exist in Azure
|
||||||
|
|
||||||
|
### Images Not Updating
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
- Check if images were pushed to ACR successfully
|
||||||
|
- Verify App Service is pulling from correct ACR
|
||||||
|
- Check container settings in App Service configuration
|
||||||
|
- Ensure Managed Identity has ACR pull permissions
|
||||||
|
|
||||||
|
### Health Check Fails
|
||||||
|
|
||||||
|
**Solution:**
|
||||||
|
- Wait longer (apps may need time to start)
|
||||||
|
- Check App Service logs: `az webapp log tail`
|
||||||
|
- Verify health endpoint exists: `/api/health`
|
||||||
|
- Check environment variables are configured correctly
|
||||||
|
|
||||||
|
## 📝 Manual Deployment (Alternative)
|
||||||
|
|
||||||
|
If you prefer manual deployment after pipeline builds:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# After pipeline builds images, manually deploy:
|
||||||
|
|
||||||
|
# Restart backend to pull latest image
|
||||||
|
az webapp restart \
|
||||||
|
--name cmdb-backend-prod \
|
||||||
|
--resource-group rg-cmdb-insight-prod
|
||||||
|
|
||||||
|
# Restart frontend to pull latest image
|
||||||
|
az webapp restart \
|
||||||
|
--name cmdb-frontend-prod \
|
||||||
|
--resource-group rg-cmdb-insight-prod
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Best Practices
|
||||||
|
|
||||||
|
### 1. Use Deployment Slots for Production
|
||||||
|
|
||||||
|
- Deploy to staging slot first
|
||||||
|
- Test in staging
|
||||||
|
- Swap to production when ready
|
||||||
|
|
||||||
|
### 2. Use Specific Tags for Production
|
||||||
|
|
||||||
|
Instead of `latest`, use version tags:
|
||||||
|
```yaml
|
||||||
|
containers: '$(acrName).azurecr.io/$(repositoryName)/backend:v1.0.0'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Add Approvals for Production
|
||||||
|
|
||||||
|
Configure environment approvals:
|
||||||
|
- Go to **Pipelines** → **Environments** → **production**
|
||||||
|
- Add **Approvals** check
|
||||||
|
- Require manual approval before deployment
|
||||||
|
|
||||||
|
### 4. Monitor Deployments
|
||||||
|
|
||||||
|
- Set up alerts in Application Insights
|
||||||
|
- Monitor pipeline runs
|
||||||
|
- Check deployment logs regularly
|
||||||
|
|
||||||
|
### 5. Rollback Strategy
|
||||||
|
|
||||||
|
If deployment fails:
|
||||||
|
```bash
|
||||||
|
# Rollback to previous image
|
||||||
|
az webapp config container set \
|
||||||
|
--name cmdb-backend-prod \
|
||||||
|
--resource-group rg-cmdb-insight-prod \
|
||||||
|
--docker-custom-image-name <previous-image-tag>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 Workflow Example
|
||||||
|
|
||||||
|
### Typical Development Workflow
|
||||||
|
|
||||||
|
1. **Developer pushes code** to `main` branch
|
||||||
|
2. **Pipeline triggers automatically**
|
||||||
|
3. **Build stage**: Builds and pushes images
|
||||||
|
4. **Deploy stage**: Deploys to App Services
|
||||||
|
5. **Verify stage**: Checks health
|
||||||
|
6. **Application updated** - ready to use!
|
||||||
|
|
||||||
|
### Release Workflow
|
||||||
|
|
||||||
|
1. **Create release tag**: `git tag v1.0.0 && git push origin v1.0.0`
|
||||||
|
2. **Pipeline triggers** with tag
|
||||||
|
3. **Build stage**: Builds versioned images (`v1.0.0`)
|
||||||
|
4. **Deploy stage**: Deploys to staging slot
|
||||||
|
5. **Manual approval** (if configured)
|
||||||
|
6. **Swap to production**: Zero-downtime deployment
|
||||||
|
7. **Verify**: Health checks confirm success
|
||||||
|
|
||||||
|
## 📚 Related Documentation
|
||||||
|
|
||||||
|
- **`AZURE-NEW-SUBSCRIPTION-SETUP.md`** - Initial Azure setup
|
||||||
|
- **`AZURE-APP-SERVICE-DEPLOYMENT.md`** - Manual deployment guide
|
||||||
|
- **`AZURE-CONTAINER-REGISTRY.md`** - ACR setup
|
||||||
|
- **`AZURE-DEVOPS-SETUP.md`** - Pipeline setup basics
|
||||||
|
|
||||||
|
## ✅ Checklist
|
||||||
|
|
||||||
|
- [ ] Azure service connection created
|
||||||
|
- [ ] Pipeline variables configured
|
||||||
|
- [ ] Environment `production` created
|
||||||
|
- [ ] App Services exist in Azure
|
||||||
|
- [ ] Permissions configured
|
||||||
|
- [ ] Pipeline tested successfully
|
||||||
|
- [ ] Deployment verified
|
||||||
|
- [ ] Health checks passing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**🎉 Your automated deployment pipeline is ready!**
|
||||||
|
|
||||||
|
Every push to `main` will now automatically build and deploy your application.
|
||||||
152
docs/AZURE-PIPELINE-QUICK-REFERENCE.md
Normal file
152
docs/AZURE-PIPELINE-QUICK-REFERENCE.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# Azure Pipeline Quick Reference
|
||||||
|
|
||||||
|
Quick reference for configuring and using the automated deployment pipeline.
|
||||||
|
|
||||||
|
## 📋 Pipeline Variables
|
||||||
|
|
||||||
|
Update these in `azure-pipelines.yml`:
|
||||||
|
|
||||||
|
| Variable | Description | Example |
|
||||||
|
|----------|-------------|---------|
|
||||||
|
| `acrName` | Azure Container Registry name | `cmdbinsightacr` |
|
||||||
|
| `repositoryName` | Docker repository name | `cmdb-insight` |
|
||||||
|
| `dockerRegistryServiceConnection` | ACR service connection name | `zuyderland-cmdb-acr-connection` |
|
||||||
|
| `resourceGroup` | Azure resource group | `rg-cmdb-insight-prod` |
|
||||||
|
| `backendAppName` | Backend App Service name | `cmdb-backend-prod` |
|
||||||
|
| `frontendAppName` | Frontend App Service name | `cmdb-frontend-prod` |
|
||||||
|
| `azureSubscription` | Azure service connection for deployment | `zuyderland-cmdb-subscription` |
|
||||||
|
| `deployToProduction` | Enable/disable deployment | `true` or `false` |
|
||||||
|
| `useDeploymentSlots` | Use staging slots for zero-downtime | `true` or `false` |
|
||||||
|
|
||||||
|
## 🔧 Required Service Connections
|
||||||
|
|
||||||
|
### 1. Docker Registry Connection
|
||||||
|
|
||||||
|
**Purpose**: Push Docker images to ACR
|
||||||
|
|
||||||
|
**Setup**:
|
||||||
|
- Type: Docker Registry → Azure Container Registry
|
||||||
|
- Name: Match `dockerRegistryServiceConnection` variable
|
||||||
|
- Subscription: Your Azure subscription
|
||||||
|
- Registry: Your ACR
|
||||||
|
|
||||||
|
### 2. Azure Resource Manager Connection
|
||||||
|
|
||||||
|
**Purpose**: Deploy to App Services
|
||||||
|
|
||||||
|
**Setup**:
|
||||||
|
- Type: Azure Resource Manager
|
||||||
|
- Name: Match `azureSubscription` variable
|
||||||
|
- Subscription: Your Azure subscription
|
||||||
|
- Authentication: Managed Identity (recommended) or Service Principal
|
||||||
|
|
||||||
|
## 🚀 Pipeline Stages
|
||||||
|
|
||||||
|
### 1. Build Stage
|
||||||
|
- Builds backend Docker image
|
||||||
|
- Builds frontend Docker image
|
||||||
|
- Pushes both to ACR with tags: `$(Build.BuildId)` and `latest`
|
||||||
|
|
||||||
|
### 2. Deploy Stage
|
||||||
|
- Deploys backend to App Service
|
||||||
|
- Deploys frontend to App Service
|
||||||
|
- Restarts both services
|
||||||
|
- Verifies deployment
|
||||||
|
|
||||||
|
### 3. Verify Stage
|
||||||
|
- Health check on backend (`/api/health`)
|
||||||
|
- Accessibility check on frontend
|
||||||
|
- Reports status
|
||||||
|
|
||||||
|
## 🎯 Triggers
|
||||||
|
|
||||||
|
**Automatic triggers:**
|
||||||
|
- Push to `main` branch
|
||||||
|
- Git tags starting with `v*` (e.g., `v1.0.0`)
|
||||||
|
|
||||||
|
**Manual trigger:**
|
||||||
|
- Go to Pipelines → Your pipeline → Run pipeline
|
||||||
|
|
||||||
|
## 📝 Common Commands
|
||||||
|
|
||||||
|
### Check Pipeline Status
|
||||||
|
```bash
|
||||||
|
# View in Azure DevOps Portal
|
||||||
|
# Or use Azure CLI (if configured)
|
||||||
|
az pipelines runs list --organization <org> --project <project>
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Pipeline Logs
|
||||||
|
- Go to Azure DevOps → Pipelines → Select run → View logs
|
||||||
|
|
||||||
|
### Cancel Running Pipeline
|
||||||
|
- Go to Azure DevOps → Pipelines → Select run → Cancel
|
||||||
|
|
||||||
|
## 🔄 Deployment Flow
|
||||||
|
|
||||||
|
```
|
||||||
|
Code Push → Build Images → Push to ACR → Deploy to App Services → Verify
|
||||||
|
```
|
||||||
|
|
||||||
|
**With Slots:**
|
||||||
|
```
|
||||||
|
Code Push → Build Images → Push to ACR → Deploy to Staging → Swap to Production → Verify
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Configuration Examples
|
||||||
|
|
||||||
|
### Basic Deployment (Current)
|
||||||
|
```yaml
|
||||||
|
deployToProduction: true
|
||||||
|
useDeploymentSlots: false
|
||||||
|
```
|
||||||
|
→ Direct deployment to production
|
||||||
|
|
||||||
|
### Zero-Downtime Deployment
|
||||||
|
```yaml
|
||||||
|
deployToProduction: true
|
||||||
|
useDeploymentSlots: true
|
||||||
|
```
|
||||||
|
→ Deploy to staging, then swap to production
|
||||||
|
|
||||||
|
### Build Only (No Deployment)
|
||||||
|
```yaml
|
||||||
|
deployToProduction: false
|
||||||
|
```
|
||||||
|
→ Only build and push images, don't deploy
|
||||||
|
|
||||||
|
## 🛠️ Troubleshooting
|
||||||
|
|
||||||
|
### Pipeline Fails: "Service connection not found"
|
||||||
|
- Check service connection name matches variable
|
||||||
|
- Verify connection exists in Project Settings
|
||||||
|
|
||||||
|
### Deployment Fails: "App Service not found"
|
||||||
|
- Verify app names match your Azure resources
|
||||||
|
- Check resource group name is correct
|
||||||
|
|
||||||
|
### Images Not Updating
|
||||||
|
- Check ACR has new images
|
||||||
|
- Verify App Service container settings
|
||||||
|
- Check Managed Identity has ACR pull permissions
|
||||||
|
|
||||||
|
## 📚 Related Files
|
||||||
|
|
||||||
|
- **`azure-pipelines.yml`** - Main pipeline (basic deployment)
|
||||||
|
- **`azure-pipelines-slots.yml`** - Advanced pipeline (with slots)
|
||||||
|
- **`docs/AZURE-PIPELINE-DEPLOYMENT.md`** - Complete setup guide
|
||||||
|
- **`docs/AZURE-NEW-SUBSCRIPTION-SETUP.md`** - Initial Azure setup
|
||||||
|
|
||||||
|
## ✅ Checklist
|
||||||
|
|
||||||
|
- [ ] Service connections created
|
||||||
|
- [ ] Pipeline variables configured
|
||||||
|
- [ ] Environment `production` created
|
||||||
|
- [ ] App Services exist in Azure
|
||||||
|
- [ ] Pipeline tested successfully
|
||||||
|
- [ ] Deployment verified
|
||||||
|
- [ ] Health checks passing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Quick Start**: Update variables in `azure-pipelines.yml` and push to `main` branch!
|
||||||
222
docs/AZURE-PIPELINES-USAGE.md
Normal file
222
docs/AZURE-PIPELINES-USAGE.md
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
# Azure Pipelines Usage Guide
|
||||||
|
|
||||||
|
Guide for using the separate build and deployment pipelines.
|
||||||
|
|
||||||
|
## 📋 Pipeline Files
|
||||||
|
|
||||||
|
### 1. `azure-pipelines.yml` - Build and Push Images
|
||||||
|
|
||||||
|
**Purpose**: Builds Docker images and pushes them to Azure Container Registry.
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- ✅ Builds backend Docker image
|
||||||
|
- ✅ Builds frontend Docker image
|
||||||
|
- ✅ Pushes both to ACR with tags: `$(Build.BuildId)` and `latest`
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- First time setup (to test image building)
|
||||||
|
- After code changes (to build new images)
|
||||||
|
- Before deployment (to ensure images are in ACR)
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```yaml
|
||||||
|
variables:
|
||||||
|
acrName: 'zdlas' # Your ACR name
|
||||||
|
repositoryName: 'cmdb-insight'
|
||||||
|
dockerRegistryServiceConnection: 'zuyderland-cmdb-acr-connection'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. `azure-pipelines-deploy.yml` - Deploy to App Service
|
||||||
|
|
||||||
|
**Purpose**: Deploys existing images from ACR to Azure App Services.
|
||||||
|
|
||||||
|
**What it does:**
|
||||||
|
- ✅ Deploys backend container to App Service
|
||||||
|
- ✅ Deploys frontend container to App Service
|
||||||
|
- ✅ Restarts both App Services
|
||||||
|
- ✅ Verifies deployment with health checks
|
||||||
|
|
||||||
|
**When to use:**
|
||||||
|
- After images are built and pushed to ACR
|
||||||
|
- When you want to deploy/update the application
|
||||||
|
- For production deployments
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
```yaml
|
||||||
|
variables:
|
||||||
|
acrName: 'zdlas' # Your ACR name
|
||||||
|
resourceGroup: 'rg-cmdb-insight-prod' # Your resource group
|
||||||
|
backendAppName: 'cmdb-backend-prod' # Your backend app name
|
||||||
|
frontendAppName: 'cmdb-frontend-prod' # Your frontend app name
|
||||||
|
azureSubscription: 'zuyderland-cmdb-subscription' # Azure service connection
|
||||||
|
imageTag: 'latest' # Image tag to deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Workflow
|
||||||
|
|
||||||
|
### Step 1: Build and Push Images
|
||||||
|
|
||||||
|
1. **Configure `azure-pipelines.yml`**:
|
||||||
|
- Update `acrName` with your ACR name
|
||||||
|
- Update `dockerRegistryServiceConnection` with your service connection name
|
||||||
|
|
||||||
|
2. **Create Pipeline in Azure DevOps**:
|
||||||
|
- Go to **Pipelines** → **New pipeline**
|
||||||
|
- Select **Existing Azure Pipelines YAML file**
|
||||||
|
- Choose `azure-pipelines.yml`
|
||||||
|
- Run the pipeline
|
||||||
|
|
||||||
|
3. **Verify Images in ACR**:
|
||||||
|
```bash
|
||||||
|
az acr repository list --name zdlas
|
||||||
|
az acr repository show-tags --name zdlas --repository cmdb-insight/backend
|
||||||
|
az acr repository show-tags --name zdlas --repository cmdb-insight/frontend
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Deploy Application
|
||||||
|
|
||||||
|
1. **Ensure App Services exist**:
|
||||||
|
- Backend App Service: `cmdb-backend-prod`
|
||||||
|
- Frontend App Service: `cmdb-frontend-prod`
|
||||||
|
- See `AZURE-NEW-SUBSCRIPTION-SETUP.md` for setup instructions
|
||||||
|
|
||||||
|
2. **Configure `azure-pipelines-deploy.yml`**:
|
||||||
|
- Update all variables with your Azure resource names
|
||||||
|
- Create Azure service connection for App Service deployment
|
||||||
|
- Create `production` environment in Azure DevOps
|
||||||
|
|
||||||
|
3. **Create Deployment Pipeline**:
|
||||||
|
- Go to **Pipelines** → **New pipeline**
|
||||||
|
- Select **Existing Azure Pipelines YAML file**
|
||||||
|
- Choose `azure-pipelines-deploy.yml`
|
||||||
|
- Run the pipeline
|
||||||
|
|
||||||
|
4. **Verify Deployment**:
|
||||||
|
- Check backend: `https://cmdb-backend-prod.azurewebsites.net/api/health`
|
||||||
|
- Check frontend: `https://cmdb-frontend-prod.azurewebsites.net`
|
||||||
|
|
||||||
|
## 🔧 Setup Requirements
|
||||||
|
|
||||||
|
### For Build Pipeline (`azure-pipelines.yml`)
|
||||||
|
|
||||||
|
**Required:**
|
||||||
|
- ✅ Docker Registry service connection (for ACR)
|
||||||
|
- ✅ ACR exists and is accessible
|
||||||
|
- ✅ Service connection has push permissions
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
1. Create Docker Registry service connection:
|
||||||
|
- **Project Settings** → **Service connections** → **New service connection**
|
||||||
|
- Choose **Docker Registry** → **Azure Container Registry**
|
||||||
|
- Select your ACR
|
||||||
|
- Name: `zuyderland-cmdb-acr-connection`
|
||||||
|
|
||||||
|
### For Deployment Pipeline (`azure-pipelines-deploy.yml`)
|
||||||
|
|
||||||
|
**Required:**
|
||||||
|
- ✅ Azure Resource Manager service connection
|
||||||
|
- ✅ App Services exist in Azure
|
||||||
|
- ✅ `production` environment created in Azure DevOps
|
||||||
|
- ✅ Images exist in ACR
|
||||||
|
|
||||||
|
**Setup:**
|
||||||
|
1. Create Azure service connection:
|
||||||
|
- **Project Settings** → **Service connections** → **New service connection**
|
||||||
|
- Choose **Azure Resource Manager**
|
||||||
|
- Select your subscription
|
||||||
|
- Name: `zuyderland-cmdb-subscription`
|
||||||
|
|
||||||
|
2. Create environment:
|
||||||
|
- **Pipelines** → **Environments** → **Create environment**
|
||||||
|
- Name: `production`
|
||||||
|
- (Optional) Add approvals for manual control
|
||||||
|
|
||||||
|
## 📝 Typical Usage Scenarios
|
||||||
|
|
||||||
|
### Scenario 1: First Time Setup
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Build and push images
|
||||||
|
# Run azure-pipelines.yml → Images in ACR
|
||||||
|
|
||||||
|
# 2. Create App Services (manual or via script)
|
||||||
|
# See AZURE-NEW-SUBSCRIPTION-SETUP.md
|
||||||
|
|
||||||
|
# 3. Deploy application
|
||||||
|
# Run azure-pipelines-deploy.yml → App deployed
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 2: Code Update
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Push code to main branch
|
||||||
|
git push origin main
|
||||||
|
|
||||||
|
# 2. Build pipeline runs automatically
|
||||||
|
# azure-pipelines.yml → New images in ACR
|
||||||
|
|
||||||
|
# 3. Deploy new version
|
||||||
|
# Run azure-pipelines-deploy.yml → App updated
|
||||||
|
```
|
||||||
|
|
||||||
|
### Scenario 3: Deploy Specific Version
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Update azure-pipelines-deploy.yml
|
||||||
|
imageTag: 'v1.0.0' # Or specific build ID
|
||||||
|
|
||||||
|
# 2. Run deployment pipeline
|
||||||
|
# Deploys specific version
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 Combining Pipelines (Future)
|
||||||
|
|
||||||
|
Once you're comfortable with both pipelines, you can:
|
||||||
|
|
||||||
|
1. **Combine them** into one pipeline with conditional deployment
|
||||||
|
2. **Use deployment slots** for zero-downtime updates
|
||||||
|
3. **Add approval gates** for production deployments
|
||||||
|
|
||||||
|
See `azure-pipelines-slots.yml` for an advanced example with deployment slots.
|
||||||
|
|
||||||
|
## 🛠️ Troubleshooting
|
||||||
|
|
||||||
|
### Build Pipeline Fails
|
||||||
|
|
||||||
|
**Issue**: "Service connection not found"
|
||||||
|
- **Solution**: Verify service connection name matches `dockerRegistryServiceConnection` variable
|
||||||
|
|
||||||
|
**Issue**: "ACR not found"
|
||||||
|
- **Solution**: Check `acrName` variable matches your ACR name
|
||||||
|
|
||||||
|
### Deployment Pipeline Fails
|
||||||
|
|
||||||
|
**Issue**: "App Service not found"
|
||||||
|
- **Solution**: Verify app names match your Azure resources
|
||||||
|
|
||||||
|
**Issue**: "Environment not found"
|
||||||
|
- **Solution**: Create `production` environment in Azure DevOps
|
||||||
|
|
||||||
|
**Issue**: "Image not found in ACR"
|
||||||
|
- **Solution**: Run build pipeline first to push images to ACR
|
||||||
|
|
||||||
|
## ✅ Checklist
|
||||||
|
|
||||||
|
### Build Pipeline Setup
|
||||||
|
- [ ] Docker Registry service connection created
|
||||||
|
- [ ] `azure-pipelines.yml` variables configured
|
||||||
|
- [ ] Pipeline created in Azure DevOps
|
||||||
|
- [ ] Test run successful
|
||||||
|
- [ ] Images visible in ACR
|
||||||
|
|
||||||
|
### Deployment Pipeline Setup
|
||||||
|
- [ ] Azure Resource Manager service connection created
|
||||||
|
- [ ] `production` environment created
|
||||||
|
- [ ] App Services exist in Azure
|
||||||
|
- [ ] `azure-pipelines-deploy.yml` variables configured
|
||||||
|
- [ ] Deployment pipeline created in Azure DevOps
|
||||||
|
- [ ] Test deployment successful
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Workflow**: Build first → Deploy second → Verify success!
|
||||||
242
docs/AZURE-RESOURCES-OVERVIEW.md
Normal file
242
docs/AZURE-RESOURCES-OVERVIEW.md
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
# Azure Resources Overview
|
||||||
|
|
||||||
|
Quick reference of all Azure resources needed for CMDB Insight deployment.
|
||||||
|
|
||||||
|
## 📋 Resources Summary
|
||||||
|
|
||||||
|
| Resource Type | Resource Name | Purpose | SKU/Tier | Estimated Cost | Shared? |
|
||||||
|
|--------------|---------------|---------|----------|----------------|--------|
|
||||||
|
| **Resource Group** | `rg-cmdb-insight-prod` | Container for all resources | - | Free | No |
|
||||||
|
| **Container Registry** | `yourcompanyacr` | Store Docker images (can be shared) | Basic/Standard | €5-20/month | ✅ Yes |
|
||||||
|
| **PostgreSQL Database** | `cmdb-postgres-prod` | Production database | Standard_B1ms | €20-30/month | No |
|
||||||
|
| **Key Vault** | `kv-cmdb-insight-prod` | Store secrets securely | Standard | €1-2/month | No |
|
||||||
|
| **App Service Plan** | `plan-cmdb-insight-prod` | Hosting plan | B1 | €15-25/month | No |
|
||||||
|
| **App Service (Backend)** | `cmdb-backend-prod` | Backend API | - | Included in plan | No |
|
||||||
|
| **App Service (Frontend)** | `cmdb-frontend-prod` | Frontend web app | - | Included in plan | No |
|
||||||
|
| **Application Insights** | `appi-cmdb-insight-prod` | Monitoring & logging | Basic | €0-5/month | No |
|
||||||
|
|
||||||
|
**Total Estimated Cost: €41-82/month** (depending on ACR tier and usage)
|
||||||
|
|
||||||
|
**💡 Note**: Container Registry can be **shared across multiple applications**. The repository name (`cmdb-insight`) separates this app from others. If you already have an ACR, reuse it to save costs!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔗 Resource Dependencies
|
||||||
|
|
||||||
|
```
|
||||||
|
Resource Group (App-specific)
|
||||||
|
├── PostgreSQL Database
|
||||||
|
│ └── Stores: Application data
|
||||||
|
├── Key Vault
|
||||||
|
│ └── Stores: Secrets (JIRA tokens, passwords, etc.)
|
||||||
|
├── Application Insights
|
||||||
|
│ └── Monitors: Backend & Frontend apps
|
||||||
|
└── App Service Plan
|
||||||
|
├── Backend App Service
|
||||||
|
│ ├── Pulls from: Shared ACR (cmdb-insight/backend:latest)
|
||||||
|
│ ├── Connects to: PostgreSQL
|
||||||
|
│ ├── Reads from: Key Vault
|
||||||
|
│ └── Sends logs to: Application Insights
|
||||||
|
└── Frontend App Service
|
||||||
|
├── Pulls from: Shared ACR (cmdb-insight/frontend:latest)
|
||||||
|
└── Connects to: Backend App Service
|
||||||
|
|
||||||
|
Shared Resources (can be in separate resource group)
|
||||||
|
└── Container Registry (ACR) ← Shared across multiple applications
|
||||||
|
├── cmdb-insight/ ← This application
|
||||||
|
│ ├── backend:latest
|
||||||
|
│ └── frontend:latest
|
||||||
|
├── other-app/ ← Other applications
|
||||||
|
│ └── api:latest
|
||||||
|
└── shared-services/ ← Shared images
|
||||||
|
└── nginx:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌐 Endpoints
|
||||||
|
|
||||||
|
After deployment, your application will be available at:
|
||||||
|
|
||||||
|
- **Frontend**: `https://cmdb-frontend-prod.azurewebsites.net`
|
||||||
|
- **Backend API**: `https://cmdb-backend-prod.azurewebsites.net/api`
|
||||||
|
- **Health Check**: `https://cmdb-backend-prod.azurewebsites.net/api/health`
|
||||||
|
|
||||||
|
If custom domain is configured:
|
||||||
|
- **Frontend**: `https://cmdb.yourcompany.com`
|
||||||
|
- **Backend API**: `https://api.cmdb.yourcompany.com` (or subdomain of your choice)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Required Secrets
|
||||||
|
|
||||||
|
These secrets should be stored in Azure Key Vault:
|
||||||
|
|
||||||
|
| Secret Name | Description | Example |
|
||||||
|
|-------------|-------------|---------|
|
||||||
|
| `JiraPat` | Jira Personal Access Token (if using PAT auth) | `ATATT3xFfGF0...` |
|
||||||
|
| `SessionSecret` | Session encryption secret | `a1b2c3d4e5f6...` (32+ chars) |
|
||||||
|
| `JiraOAuthClientId` | Jira OAuth Client ID | `OAuthClientId123` |
|
||||||
|
| `JiraOAuthClientSecret` | Jira OAuth Client Secret | `OAuthSecret456` |
|
||||||
|
| `JiraSchemaId` | Jira Assets Schema ID | `schema-123` |
|
||||||
|
| `DatabasePassword` | PostgreSQL admin password | `SecurePassword123!` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Resource Sizing Recommendations
|
||||||
|
|
||||||
|
### For 20 Users (Current)
|
||||||
|
|
||||||
|
| Resource | Recommended SKU | Alternative |
|
||||||
|
|----------|----------------|-------------|
|
||||||
|
| App Service Plan | B1 (1 vCore, 1.75GB RAM) | B2 if experiencing slowness |
|
||||||
|
| PostgreSQL | Standard_B1ms (1 vCore, 2GB RAM) | Standard_B2s for growth |
|
||||||
|
| Container Registry | Basic (10GB) | Standard for production |
|
||||||
|
| Key Vault | Standard | Standard (only option) |
|
||||||
|
|
||||||
|
### For 50+ Users (Future Growth)
|
||||||
|
|
||||||
|
| Resource | Recommended SKU | Notes |
|
||||||
|
|----------|----------------|-------|
|
||||||
|
| App Service Plan | B2 or S1 | Better performance |
|
||||||
|
| PostgreSQL | Standard_B2s (2 vCores, 4GB RAM) | More concurrent connections |
|
||||||
|
| Container Registry | Standard (100GB) | More storage, geo-replication |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Update/Deployment Flow
|
||||||
|
|
||||||
|
1. **Code Changes** → Push to repository
|
||||||
|
2. **CI/CD Pipeline** → Builds Docker images
|
||||||
|
3. **Push to ACR** → Images stored in Container Registry
|
||||||
|
4. **Restart App Services** → Pulls new images from ACR
|
||||||
|
5. **Application Updates** → New version live
|
||||||
|
|
||||||
|
### Manual Deployment
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Restart apps to pull latest images
|
||||||
|
az webapp restart --name cmdb-backend-prod --resource-group rg-cmdb-insight-prod
|
||||||
|
az webapp restart --name cmdb-frontend-prod --resource-group rg-cmdb-insight-prod
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛡️ Security Configuration
|
||||||
|
|
||||||
|
### Network Security
|
||||||
|
|
||||||
|
- **HTTPS Only**: Enabled on both App Services
|
||||||
|
- **Database Firewall**: Restricted to Azure services (can be further restricted)
|
||||||
|
- **Key Vault Access**: Managed Identity only (no shared keys)
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
- **App Services**: Managed Identity for ACR and Key Vault access
|
||||||
|
- **Database**: Username/password (stored in Key Vault)
|
||||||
|
- **Application**: Jira OAuth 2.0 or Personal Access Token
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📈 Monitoring & Logging
|
||||||
|
|
||||||
|
### Application Insights
|
||||||
|
|
||||||
|
- **Metrics**: Response times, request rates, errors
|
||||||
|
- **Logs**: Application logs, exceptions, traces
|
||||||
|
- **Alerts**: Configured for downtime, errors, performance issues
|
||||||
|
|
||||||
|
### Access Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Backend logs
|
||||||
|
az webapp log tail --name cmdb-backend-prod --resource-group rg-cmdb-insight-prod
|
||||||
|
|
||||||
|
# Frontend logs
|
||||||
|
az webapp log tail --name cmdb-frontend-prod --resource-group rg-cmdb-insight-prod
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Configuration Files
|
||||||
|
|
||||||
|
### Environment Variables (Backend)
|
||||||
|
|
||||||
|
- `NODE_ENV=production`
|
||||||
|
- `PORT=3001`
|
||||||
|
- `DATABASE_TYPE=postgres`
|
||||||
|
- `DATABASE_URL` (from Key Vault)
|
||||||
|
- `JIRA_HOST=https://jira.zuyderland.nl`
|
||||||
|
- `JIRA_AUTH_METHOD=oauth`
|
||||||
|
- `JIRA_OAUTH_CLIENT_ID` (from Key Vault)
|
||||||
|
- `JIRA_OAUTH_CLIENT_SECRET` (from Key Vault)
|
||||||
|
- `JIRA_OAUTH_CALLBACK_URL`
|
||||||
|
- `JIRA_SCHEMA_ID` (from Key Vault)
|
||||||
|
- `SESSION_SECRET` (from Key Vault)
|
||||||
|
- `FRONTEND_URL`
|
||||||
|
- `APPINSIGHTS_INSTRUMENTATIONKEY`
|
||||||
|
|
||||||
|
### Environment Variables (Frontend)
|
||||||
|
|
||||||
|
- `VITE_API_URL` (points to backend API)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗑️ Cleanup (If Needed)
|
||||||
|
|
||||||
|
To delete all resources:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Delete entire resource group (deletes all resources)
|
||||||
|
az group delete --name rg-cmdb-insight-prod --yes --no-wait
|
||||||
|
|
||||||
|
# Or delete individual resources
|
||||||
|
az acr delete --name cmdbinsightacr --resource-group rg-cmdb-insight-prod
|
||||||
|
az postgres flexible-server delete --name cmdb-postgres-prod --resource-group rg-cmdb-insight-prod
|
||||||
|
az keyvault delete --name kv-cmdb-insight-prod --resource-group rg-cmdb-insight-prod
|
||||||
|
az appservice plan delete --name plan-cmdb-insight-prod --resource-group rg-cmdb-insight-prod
|
||||||
|
```
|
||||||
|
|
||||||
|
**⚠️ Warning**: This will permanently delete all resources and data. Make sure you have backups if needed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📞 Quick Commands Reference
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set variables
|
||||||
|
RESOURCE_GROUP="rg-cmdb-insight-prod"
|
||||||
|
BACKEND_APP="cmdb-backend-prod"
|
||||||
|
FRONTEND_APP="cmdb-frontend-prod"
|
||||||
|
|
||||||
|
# Check app status
|
||||||
|
az webapp show --name $BACKEND_APP --resource-group $RESOURCE_GROUP --query state
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
az webapp log tail --name $BACKEND_APP --resource-group $RESOURCE_GROUP
|
||||||
|
|
||||||
|
# Restart apps
|
||||||
|
az webapp restart --name $BACKEND_APP --resource-group $RESOURCE_GROUP
|
||||||
|
az webapp restart --name $FRONTEND_APP --resource-group $RESOURCE_GROUP
|
||||||
|
|
||||||
|
# List all resources
|
||||||
|
az resource list --resource-group $RESOURCE_GROUP --output table
|
||||||
|
|
||||||
|
# Get app URLs
|
||||||
|
echo "Frontend: https://${FRONTEND_APP}.azurewebsites.net"
|
||||||
|
echo "Backend: https://${BACKEND_APP}.azurewebsites.net/api"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Related Documentation
|
||||||
|
|
||||||
|
- **`AZURE-NEW-SUBSCRIPTION-SETUP.md`** - Complete step-by-step setup guide
|
||||||
|
- **`AZURE-APP-SERVICE-DEPLOYMENT.md`** - Detailed App Service deployment
|
||||||
|
- **`AZURE-CONTAINER-REGISTRY.md`** - ACR setup and usage
|
||||||
|
- **`AZURE-QUICK-REFERENCE.md`** - Quick reference guide
|
||||||
|
- **`PRODUCTION-DEPLOYMENT.md`** - General production deployment
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated**: 2025-01-21
|
||||||
418
scripts/setup-azure-resources.sh
Executable file
418
scripts/setup-azure-resources.sh
Executable file
@@ -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 <your-client-id>"
|
||||||
|
echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name JiraOAuthClientSecret --value <your-client-secret>"
|
||||||
|
echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name JiraSchemaId --value <your-schema-id>"
|
||||||
|
|
||||||
|
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 <your-client-id>"
|
||||||
|
echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name JiraOAuthClientSecret --value <your-client-secret>"
|
||||||
|
echo " az keyvault secret set --vault-name $KEY_VAULT_NAME --name JiraSchemaId --value <your-schema-id>"
|
||||||
|
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=\"<your-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 ""
|
||||||
Reference in New Issue
Block a user