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.json2.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.com4.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.jsonwith 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.com8.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.jsonheaders - 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