Angular MFE Azure Static Web Apps Deployment | Complete Guide
This comprehensive guide walks you through deploying your Angular Micro Frontend applications to Azure Static Web Apps, including CI/CD setup, custom domains, and production optimization.
🌐 Azure Static Web Apps Overview
Section titled “🌐 Azure Static Web Apps Overview”Why Azure Static Web Apps?
Section titled “Why Azure Static Web Apps?”- Global CDN: Worldwide content distribution
- CI/CD Integration: GitHub Actions automation
- Custom Domains: Easy SSL/HTTPS setup
- Serverless APIs: Optional backend integration
- Cost-Effective: Pay-per-use pricing model
- Built-in Security: Authentication and authorization
Architecture Overview
Section titled “Architecture Overview”graph TB A[GitHub Repository] --> B[GitHub Actions] B --> C[Build Pipeline] C --> D[Azure Static Web Apps] D --> E[Global CDN] E --> F[Custom Domain]
subgraph "MFE Deployment" D1[Host App] D2[Loan Calculator MFE] D3[User Dashboard MFE] D4[Transaction History MFE] end
🚀 Step 1: Prepare Applications for Production
Section titled “🚀 Step 1: Prepare Applications for Production”1.1 Production Build Configuration
Section titled “1.1 Production Build Configuration”Update each application’s build configuration for production:
angular.json (for each MFE):
{ "projects": { "your-app": { "architect": { "build": { "builder": "@angular-builders/custom-webpack:browser", "options": { "customWebpackConfig": { "path": "./webpack.config.js" }, "outputPath": "dist/your-app", "index": "src/index.html", "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", "assets": [ "src/favicon.ico", "src/assets" ], "styles": [ "src/styles.scss" ], "scripts": [], "optimization": true, "outputHashing": "all", "sourceMap": false, "extractCss": true, "namedChunks": false, "aot": true, "extractLicenses": true, "vendorChunk": false, "buildOptimizer": true }, "configurations": { "production": { "fileReplacements": [ { "replace": "src/environments/environment.ts", "with": "src/environments/environment.prod.ts" } ], "budgets": [ { "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" }, { "type": "anyComponentStyle", "maximumWarning": "2kb", "maximumError": "4kb" } ] } } } } } }}
1.2 Production Environment Configuration
Section titled “1.2 Production Environment Configuration”environment.prod.ts (Host Application):
export const environment = { production: true, apiUrl: 'https://api.yourdomain.com', mfeConfig: { loanCalculator: 'https://loan-calculator.azurestaticapps.net/remoteEntry.js', userDashboard: 'https://user-dashboard.azurestaticapps.net/remoteEntry.js', transactionHistory: 'https://transaction-history.azurestaticapps.net/remoteEntry.js' }, appInsights: { instrumentationKey: 'your-app-insights-key' }, auth: { clientId: 'your-auth-client-id', authority: 'https://yourtenant.b2clogin.com', redirectUri: 'https://banking-platform.azurestaticapps.net' }};
environment.prod.ts (Each MFE):
export const environment = { production: true, apiUrl: 'https://api.yourdomain.com', hostUrl: 'https://banking-platform.azurestaticapps.net', appInsights: { instrumentationKey: 'your-app-insights-key' }};
1.3 Production Webpack Configuration
Section titled “1.3 Production Webpack Configuration”webpack.prod.config.js (Host Application):
const ModuleFederationPlugin = require("@module-federation/webpack");const CompressionPlugin = require("compression-webpack-plugin");
module.exports = { mode: "production", optimization: { splitChunks: { chunks: 'all', cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'all', }, }, }, }, plugins: [ new ModuleFederationPlugin({ name: "host", filename: "remoteEntry.js",
remotes: { "mfeLoanCalculator": "https://loan-calculator.azurestaticapps.net/remoteEntry.js", "mfeUserDashboard": "https://user-dashboard.azurestaticapps.net/remoteEntry.js", "mfeTransactionHistory": "https://transaction-history.azurestaticapps.net/remoteEntry.js", },
shared: { "@angular/core": { singleton: true, strictVersion: true, requiredVersion: "auto" }, "@angular/common": { singleton: true, strictVersion: true, requiredVersion: "auto" }, "@angular/router": { singleton: true, strictVersion: true, requiredVersion: "auto" }, }, }), new CompressionPlugin({ algorithm: 'gzip', test: /\.(js|css|html|svg)$/, threshold: 8192, minRatio: 0.8 }) ],};
☁️ Step 2: Create Azure Static Web Apps
Section titled “☁️ Step 2: Create Azure Static Web Apps”2.1 Azure Portal Setup
Section titled “2.1 Azure Portal Setup”-
Sign in to Azure Portal: https://portal.azure.com
-
Create Resource Group:
Unix/macOS/Windows:
Terminal window az group create --name rg-banking-mfe --location "East US" -
Create Static Web Apps for each application:
Host Application:
Unix/macOS/Windows:
az staticwebapp create \ --name banking-platform-host \ --resource-group rg-banking-mfe \ --source https://github.com/yourusername/host-banking-app \ --location "East US" \ --branch main \ --app-location "/" \ --output-location "dist/host-banking-app"
Loan Calculator MFE:
Unix/macOS/Windows:
az staticwebapp create \ --name loan-calculator-mfe \ --resource-group rg-banking-mfe \ --source https://github.com/yourusername/mfe-loan-calculator \ --location "East US" \ --branch main \ --app-location "/" \ --output-location "dist/mfe-loan-calculator"
Repeat for User Dashboard and Transaction History MFEs.
2.2 GitHub Repository Preparation
Section titled “2.2 GitHub Repository Preparation”Ensure each repository has the correct structure:
Repository Structure:├── .github/│ └── workflows/│ └── azure-static-web-apps-[name].yml├── src/├── angular.json├── package.json├── webpack.config.js├── webpack.prod.config.js└── staticwebapp.config.json
2.3 Static Web App Configuration
Section titled “2.3 Static Web App Configuration”staticwebapp.config.json (Host Application):
{ "routes": [ { "route": "/dashboard/*", "allowedRoles": ["authenticated"] }, { "route": "/loan-calculator", "allowedRoles": ["anonymous", "authenticated"] }, { "route": "/api/*", "allowedRoles": ["authenticated"] }, { "route": "/*", "serve": "/index.html", "statusCode": 200 } ], "navigationFallback": { "rewrite": "/index.html", "exclude": ["/assets/*", "/*.{js,css,png,jpg,jpeg,gif,svg,ico,woff,woff2,ttf,eot}"] }, "mimeTypes": { ".js": "application/javascript", ".css": "text/css" }, "globalHeaders": { "X-Content-Type-Options": "nosniff", "X-Frame-Options": "DENY", "X-XSS-Protection": "1; mode=block", "Strict-Transport-Security": "max-age=31536000; includeSubDomains", "Content-Security-Policy": "default-src 'self' *.azurestaticapps.net; script-src 'self' 'unsafe-inline' *.azurestaticapps.net; style-src 'self' 'unsafe-inline'" }}
staticwebapp.config.json (MFE Applications):
{ "routes": [ { "route": "/*", "serve": "/index.html", "statusCode": 200 } ], "navigationFallback": { "rewrite": "/index.html" }, "globalHeaders": { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization" }}
🔄 Step 3: GitHub Actions CI/CD Pipeline
Section titled “🔄 Step 3: GitHub Actions CI/CD Pipeline”3.1 Host Application Workflow
Section titled “3.1 Host Application Workflow”.github/workflows/azure-static-web-apps-host.yml:
name: Azure Static Web Apps CI/CD - Host
on: push: branches: - main - develop pull_request: types: [opened, synchronize, reopened, closed] branches: - main
jobs: build_and_deploy_job: if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') runs-on: ubuntu-latest name: Build and Deploy Job steps: - uses: actions/checkout@v3 with: submodules: true
- name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' cache: 'npm'
- name: Install dependencies run: npm ci
- name: Lint code run: npm run lint
- name: Run tests run: npm run test:ci
- name: Build for production run: npm run build:prod env: NODE_ENV: production
- name: Deploy to Azure Static Web Apps id: builddeploy uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_HOST }} repo_token: ${{ secrets.GITHUB_TOKEN }} action: "upload" app_location: "/" output_location: "dist/host-banking-app" config_file_location: "/"
close_pull_request_job: if: github.event_name == 'pull_request' && github.event.action == 'closed' runs-on: ubuntu-latest name: Close Pull Request Job steps: - name: Close Pull Request id: closepullrequest uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_HOST }} action: "close"
3.2 MFE Workflow Template
Section titled “3.2 MFE Workflow Template”.github/workflows/azure-static-web-apps-loan-calculator.yml:
name: Azure Static Web Apps CI/CD - Loan Calculator
on: push: branches: - main pull_request: types: [opened, synchronize, reopened, closed] branches: - main
jobs: build_and_deploy_job: if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') runs-on: ubuntu-latest name: Build and Deploy Job steps: - uses: actions/checkout@v3 with: submodules: true
- name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' cache: 'npm'
- name: Install dependencies run: npm ci
- name: Lint code run: npm run lint
- name: Run tests run: npm run test:ci
- name: Build for production run: npm run build:prod env: NODE_ENV: production
- name: Copy remoteEntry.js to root run: cp dist/mfe-loan-calculator/remoteEntry.js dist/mfe-loan-calculator/remoteEntry.js
- name: Deploy to Azure Static Web Apps id: builddeploy uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_LOAN_CALCULATOR }} repo_token: ${{ secrets.GITHUB_TOKEN }} action: "upload" app_location: "/" output_location: "dist/mfe-loan-calculator"
close_pull_request_job: if: github.event_name == 'pull_request' && github.event.action == 'closed' runs-on: ubuntu-latest name: Close Pull Request Job steps: - name: Close Pull Request id: closepullrequest uses: Azure/static-web-apps-deploy@v1 with: azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_LOAN_CALCULATOR }} action: "close"
3.3 Package.json Scripts for CI/CD
Section titled “3.3 Package.json Scripts for CI/CD”Add these scripts to each package.json
:
{ "scripts": { "build:prod": "ng build --configuration production --output-hashing all", "test:ci": "ng test --watch=false --browsers=ChromeHeadless --code-coverage", "lint": "ng lint", "e2e:ci": "ng e2e --watch=false", "analyze": "ng build --configuration production --stats-json && npx webpack-bundle-analyzer dist/*/stats.json" }}
🔧 Step 4: Custom Domains and SSL
Section titled “🔧 Step 4: Custom Domains and SSL”4.1 Configure Custom Domains
Section titled “4.1 Configure Custom Domains”- Purchase Domain: Buy your domain from a registrar
- Configure DNS in Azure Portal or your DNS provider:
DNS Records:├── banking.yourdomain.com (Host App)├── loan-calculator.yourdomain.com (Loan Calculator MFE)├── user-dashboard.yourdomain.com (User Dashboard MFE)└── transactions.yourdomain.com (Transaction History MFE)
- Add Custom Domain in Azure Portal:
Unix/macOS/Windows:
az staticwebapp hostname set \ --name banking-platform-host \ --hostname banking.yourdomain.com
4.2 Update MFE Configuration for Custom Domains
Section titled “4.2 Update MFE Configuration for Custom Domains”mf.manifest.json (Production):
{ "production": { "mfeLoanCalculator": "https://loan-calculator.yourdomain.com/remoteEntry.js", "mfeUserDashboard": "https://user-dashboard.yourdomain.com/remoteEntry.js", "mfeTransactionHistory": "https://transactions.yourdomain.com/remoteEntry.js" }}
📊 Step 5: Monitoring and Analytics
Section titled “📊 Step 5: Monitoring and Analytics”5.1 Azure Application Insights Integration
Section titled “5.1 Azure Application Insights Integration”app-insights.service.ts:
import { Injectable } from '@angular/core';import { ApplicationInsights } from '@microsoft/applicationinsights-web';import { environment } from '../environments/environment';
@Injectable({ providedIn: 'root'})export class AppInsightsService { private appInsights: ApplicationInsights;
constructor() { this.appInsights = new ApplicationInsights({ config: { instrumentationKey: environment.appInsights.instrumentationKey, enableAutoRouteTracking: true, enableRequestHeaderTracking: true, enableResponseHeaderTracking: true } });
this.appInsights.loadAppInsights(); this.appInsights.trackPageView(); }
trackEvent(name: string, properties?: any) { this.appInsights.trackEvent({ name }, properties); }
trackException(exception: Error, properties?: any) { this.appInsights.trackException({ exception }, properties); }
trackMfeLoad(mfeName: string, loadTime: number) { this.trackEvent('MFE_LOAD', { mfeName, loadTime, timestamp: new Date().toISOString() }); }}
5.2 Performance Monitoring
Section titled “5.2 Performance Monitoring”performance.service.ts:
import { Injectable } from '@angular/core';import { AppInsightsService } from './app-insights.service';
@Injectable({ providedIn: 'root'})export class PerformanceService {
constructor(private appInsights: AppInsightsService) {}
measureMfeLoadTime(mfeName: string): () => void { const startTime = performance.now();
return () => { const endTime = performance.now(); const loadTime = endTime - startTime;
this.appInsights.trackMfeLoad(mfeName, loadTime);
console.log(`${mfeName} loaded in ${loadTime.toFixed(2)}ms`); }; }
trackBundleSize() { if ('navigator' in window && 'connection' in navigator) { const connection = (navigator as any).connection;
this.appInsights.trackEvent('NETWORK_INFO', { effectiveType: connection.effectiveType, downlink: connection.downlink, rtt: connection.rtt }); } }}
🔒 Step 6: Security Configuration
Section titled “🔒 Step 6: Security Configuration”6.1 Content Security Policy
Section titled “6.1 Content Security Policy”Update staticwebapp.config.json
with strict CSP:
{ "globalHeaders": { "Content-Security-Policy": "default-src 'self'; script-src 'self' 'unsafe-inline' *.yourdomain.com *.azurestaticapps.net; style-src 'self' 'unsafe-inline'; img-src 'self' data: *.yourdomain.com; font-src 'self' fonts.googleapis.com fonts.gstatic.com; connect-src 'self' *.yourdomain.com api.yourdomain.com" }}
6.2 Authentication Integration
Section titled “6.2 Authentication Integration”auth.config.ts (Production):
export const authConfig = { instance: "https://login.microsoftonline.com/", tenantId: "your-tenant-id", clientId: "your-client-id", redirectUri: "https://banking.yourdomain.com", scopes: ["user.read", "banking.access"]};
🚀 Step 7: Deployment Process
Section titled “🚀 Step 7: Deployment Process”7.1 Deployment Order
Section titled “7.1 Deployment Order”Deploy in this specific order to avoid dependency issues:
-
Deploy MFEs First:
Unix/macOS:
Terminal window # Loan Calculatorcd mfe-loan-calculatorgit push origin main# User Dashboardcd ../mfe-user-dashboardgit push origin main# Transaction Historycd ../mfe-transaction-historygit push origin mainWindows:
Terminal window # Loan CalculatorSet-Location mfe-loan-calculatorgit push origin main# User DashboardSet-Location ..\mfe-user-dashboardgit push origin main# Transaction HistorySet-Location ..\mfe-transaction-historygit push origin main -
Deploy Host Application:
Unix/macOS:
Terminal window cd host-banking-appgit push origin mainWindows:
Terminal window Set-Location host-banking-appgit push origin main -
Update Host Configuration:
- Update
mf.manifest.json
with production URLs - Update webpack configuration
- Update
7.2 Verification Script
Section titled “7.2 Verification Script”Create verification scripts to test deployment:
Unix/macOS (verify-deployment.sh):
#!/bin/bash
echo "🔍 Verifying MFE Deployment"echo "==========================="
# URLs to checkHOST_URL="https://banking.yourdomain.com"LOAN_CALC_URL="https://loan-calculator.yourdomain.com"DASHBOARD_URL="https://user-dashboard.yourdomain.com"TRANSACTIONS_URL="https://transactions.yourdomain.com"
check_url() { local name=$1 local url=$2
echo -n "Checking $name... "
if curl -s -f "$url" > /dev/null; then echo "✅ Available" else echo "❌ Not available" return 1 fi}
# Check all URLscheck_url "Host Application" "$HOST_URL"check_url "Loan Calculator MFE" "$LOAN_CALC_URL"check_url "User Dashboard MFE" "$DASHBOARD_URL"check_url "Transaction History MFE" "$TRANSACTIONS_URL"
# Check remoteEntry.js filesecho ""echo "🔍 Checking Remote Entries..."check_url "Loan Calculator Remote" "$LOAN_CALC_URL/remoteEntry.js"check_url "Dashboard Remote" "$DASHBOARD_URL/remoteEntry.js"check_url "Transactions Remote" "$TRANSACTIONS_URL/remoteEntry.js"
echo ""echo "🎯 Testing MFE Integration..."
# Test if host can load MFEs (simplified check)if curl -s "$HOST_URL" | grep -q "banking"; then echo "✅ Host application is responding"else echo "❌ Host application issue detected"fi
echo ""echo "✅ Deployment verification complete!"
Windows (verify-deployment.ps1):
Write-Host "🔍 Verifying MFE Deployment" -ForegroundColor BlueWrite-Host "===========================" -ForegroundColor Blue
# URLs to check$HOST_URL = "https://banking.yourdomain.com"$LOAN_CALC_URL = "https://loan-calculator.yourdomain.com"$DASHBOARD_URL = "https://user-dashboard.yourdomain.com"$TRANSACTIONS_URL = "https://transactions.yourdomain.com"
function Check-Url { param( [string]$Name, [string]$Url )
Write-Host "Checking $Name... " -NoNewline
try { $response = Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 10 if ($response.StatusCode -eq 200) { Write-Host "✅ Available" -ForegroundColor Green return $true } else { Write-Host "❌ Not available (Status: $($response.StatusCode))" -ForegroundColor Red return $false } } catch { Write-Host "❌ Not available (Error: $($_.Exception.Message))" -ForegroundColor Red return $false }}
# Check all URLsCheck-Url "Host Application" $HOST_URLCheck-Url "Loan Calculator MFE" $LOAN_CALC_URLCheck-Url "User Dashboard MFE" $DASHBOARD_URLCheck-Url "Transaction History MFE" $TRANSACTIONS_URL
# Check remoteEntry.js filesWrite-Host ""Write-Host "🔍 Checking Remote Entries..." -ForegroundColor CyanCheck-Url "Loan Calculator Remote" "$LOAN_CALC_URL/remoteEntry.js"Check-Url "Dashboard Remote" "$DASHBOARD_URL/remoteEntry.js"Check-Url "Transactions Remote" "$TRANSACTIONS_URL/remoteEntry.js"
Write-Host ""Write-Host "🎯 Testing MFE Integration..." -ForegroundColor Yellow
# Test if host can load MFEs (simplified check)try { $hostContent = Invoke-WebRequest -Uri $HOST_URL -UseBasicParsing if ($hostContent.Content -match "banking") { Write-Host "✅ Host application is responding" -ForegroundColor Green } else { Write-Host "❌ Host application issue detected" -ForegroundColor Red }} catch { Write-Host "❌ Host application issue detected: $($_.Exception.Message)" -ForegroundColor Red}
Write-Host ""Write-Host "✅ Deployment verification complete!" -ForegroundColor Green
📈 Step 8: Performance Optimization
Section titled “📈 Step 8: Performance Optimization”8.1 Azure CDN Configuration
Section titled “8.1 Azure CDN Configuration”Configure Azure CDN for better global performance:
Unix/macOS/Windows:
az cdn profile create \ --name banking-mfe-cdn \ --resource-group rg-banking-mfe \ --sku Standard_Microsoft
az cdn endpoint create \ --name banking-platform \ --profile-name banking-mfe-cdn \ --resource-group rg-banking-mfe \ --origin banking.yourdomain.com
8.2 Caching Strategy
Section titled “8.2 Caching Strategy”Configure caching headers in staticwebapp.config.json
:
{ "routes": [ { "route": "/assets/*", "headers": { "Cache-Control": "public, max-age=31536000, immutable" } }, { "route": "/*.js", "headers": { "Cache-Control": "public, max-age=31536000, immutable" } }, { "route": "/*.css", "headers": { "Cache-Control": "public, max-age=31536000, immutable" } }, { "route": "/remoteEntry.js", "headers": { "Cache-Control": "public, max-age=300" } } ]}
✅ Azure Deployment Verification Checklist
Section titled “✅ Azure Deployment Verification Checklist”Complete Deployment Checklist
Section titled “Complete Deployment Checklist”- Azure Static Web Apps created for all applications
- GitHub Actions workflows configured and working
- Production builds optimized and compressed
- Custom domains configured with SSL
- DNS records pointing to correct endpoints
- CORS headers configured for MFE communication
- Security headers implemented (CSP, HSTS, etc.)
- Application Insights monitoring configured
- Performance metrics being tracked
- Error handling and fallbacks working
- Authentication integrated (if required)
- CDN configured for global distribution
- Caching strategy implemented
- Deployment verification script passing
Production Readiness Metrics
Section titled “Production Readiness Metrics”- ⚡ Load Time: < 3 seconds globally
- 📦 Bundle Size: Optimized and compressed
- 🔒 Security Score: A+ rating
- 🌍 Global Availability: 99.9% uptime
- 📊 Performance Score: > 90
- ♿ Accessibility: AA compliance
🚀 What’s Next?
Section titled “🚀 What’s Next?”Your Angular MFE application is now deployed to production! Next steps:
- ✅ Azure Deployment Complete
- ➡️ Continue to: Documentation Guide - Create comprehensive documentation
- 📚 Alternative: Advanced Concepts - Learn advanced MFE patterns
Your Angular Micro Frontend platform is now live and serving users globally! 🎉
📞 Support & Troubleshooting
Section titled “📞 Support & Troubleshooting”Common Issues
Section titled “Common Issues”- CORS Errors: Check
staticwebapp.config.json
headers - Module Not Found: Verify remoteEntry.js URLs
- Build Failures: Check GitHub Actions logs
- SSL Issues: Verify custom domain configuration
- Performance Issues: Review bundle sizes and CDN configuration
Monitoring Resources
Section titled “Monitoring Resources”- 📊 Azure Portal: Monitor application health
- 📈 Application Insights: Performance and error tracking
- 🔍 GitHub Actions: CI/CD pipeline monitoring
- 🌐 Azure CDN: Global distribution metrics