#!/bin/bash # Azure CDN Setup for Credential Seal Images # Creates storage account, container, and CDN profile/endpoint set -euo pipefail GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[1;33m' RED='\033[0;31m' NC='\033[0m' log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[✓]${NC} $1"; } log_warning() { echo -e "${YELLOW}[!]${NC} $1"; } log_error() { echo -e "${RED}[✗]${NC} $1"; } cd "$(dirname "$0")/../.." # Check if Azure CLI is installed if ! command -v az &> /dev/null; then log_error "Azure CLI is not installed" echo "Install from: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli" exit 1 fi # Check if logged in if ! az account show &> /dev/null; then log_warning "Not logged in to Azure. Please log in:" echo " az login" exit 1 fi # Get subscription info SUBSCRIPTION_ID=$(az account show --query id -o tsv) SUBSCRIPTION_NAME=$(az account show --query name -o tsv) TENANT_ID=$(az account show --query tenantId -o tsv) log_info "Azure Subscription: ${SUBSCRIPTION_NAME} (${SUBSCRIPTION_ID})" echo "" # Configuration RESOURCE_GROUP="${AZURE_RESOURCE_GROUP:-the-order-cdn-rg}" LOCATION="${AZURE_LOCATION:-westeurope}" STORAGE_ACCOUNT_NAME="${AZURE_STORAGE_ACCOUNT:-theordercdn$(date +%s | tail -c 6)}" CONTAINER_NAME="images" CDN_PROFILE_NAME="${AZURE_CDN_PROFILE:-theorder-cdn-profile}" CDN_ENDPOINT_NAME="${AZURE_CDN_ENDPOINT:-theorder-cdn-endpoint}" # Validate storage account name (must be lowercase alphanumeric, 3-24 chars) STORAGE_ACCOUNT_NAME=$(echo "${STORAGE_ACCOUNT_NAME}" | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9' | cut -c1-24) if [ ${#STORAGE_ACCOUNT_NAME} -lt 3 ]; then STORAGE_ACCOUNT_NAME="theordercdn$(date +%s | tail -c 6)" fi log_info "Configuration:" echo " Resource Group: ${RESOURCE_GROUP}" echo " Location: ${LOCATION}" echo " Storage Account: ${STORAGE_ACCOUNT_NAME}" echo " Container: ${CONTAINER_NAME}" echo " CDN Profile: ${CDN_PROFILE_NAME}" echo " CDN Endpoint: ${CDN_ENDPOINT_NAME}" echo "" # Step 1: Check quotas log_info "Step 1: Checking Azure quotas..." QUOTA_FILE="azure-cdn-quotas.txt" cat > "${QUOTA_FILE}" << EOF Azure Quota Check for CDN Setup Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC") Subscription: ${SUBSCRIPTION_NAME} (${SUBSCRIPTION_ID}) Location: ${LOCATION} EOF # Check storage account quota log_info " Checking storage account quota..." STORAGE_QUOTA=$(az storage account show-usage --location "${LOCATION}" -o json 2>/dev/null || echo "{}") STORAGE_CURRENT=$(echo "${STORAGE_QUOTA}" | jq -r '.currentValue // 0' 2>/dev/null || echo "0") STORAGE_LIMIT=$(echo "${STORAGE_QUOTA}" | jq -r '.limit // 250' 2>/dev/null || echo "250") echo "Storage Accounts:" >> "${QUOTA_FILE}" echo " Current: ${STORAGE_CURRENT}" >> "${QUOTA_FILE}" echo " Limit: ${STORAGE_LIMIT}" >> "${QUOTA_FILE}" if [ "${STORAGE_CURRENT}" -ge "${STORAGE_LIMIT}" ]; then log_error "Storage account quota exceeded (${STORAGE_CURRENT}/${STORAGE_LIMIT})" log_info "Request quota increase: https://portal.azure.com/#blade/Microsoft_Azure_Support/HelpAndSupportBlade" exit 1 else log_success "Storage account quota OK (${STORAGE_CURRENT}/${STORAGE_LIMIT})" fi # Check CDN profile quota log_info " Checking CDN profile quota..." CDN_QUOTA=$(az cdn profile list --query "[].{Name:name}" -o tsv 2>/dev/null | wc -l || echo "0") CDN_LIMIT=25 # Default Azure CDN limit echo "CDN Profiles:" >> "${QUOTA_FILE}" echo " Current: ${CDN_QUOTA}" >> "${QUOTA_FILE}" echo " Limit: ${CDN_LIMIT}" >> "${QUOTA_FILE}" if [ "${CDN_QUOTA}" -ge "${CDN_LIMIT}" ]; then log_warning "CDN profile quota may be exceeded (${CDN_QUOTA}/${CDN_LIMIT})" else log_success "CDN profile quota OK (${CDN_QUOTA}/${CDN_LIMIT})" fi log_success "Quota check complete. Report: ${QUOTA_FILE}" echo "" # Step 2: Create resource group log_info "Step 2: Creating resource group..." if az group show --name "${RESOURCE_GROUP}" &> /dev/null; then log_success "Resource group already exists: ${RESOURCE_GROUP}" else if az group create --name "${RESOURCE_GROUP}" --location "${LOCATION}" -o json &> /dev/null; then log_success "Resource group created: ${RESOURCE_GROUP}" else log_error "Failed to create resource group" exit 1 fi fi echo "" # Step 3: Create storage account log_info "Step 3: Creating storage account..." if az storage account show --name "${STORAGE_ACCOUNT_NAME}" --resource-group "${RESOURCE_GROUP}" &> /dev/null; then log_success "Storage account already exists: ${STORAGE_ACCOUNT_NAME}" else log_info " Creating storage account (this may take a few minutes)..." if az storage account create \ --name "${STORAGE_ACCOUNT_NAME}" \ --resource-group "${RESOURCE_GROUP}" \ --location "${LOCATION}" \ --sku Standard_LRS \ --kind StorageV2 \ --min-tls-version TLS1_2 \ --allow-blob-public-access true \ -o json &> /dev/null; then log_success "Storage account created: ${STORAGE_ACCOUNT_NAME}" else log_error "Failed to create storage account" exit 1 fi fi # Get storage account key STORAGE_KEY=$(az storage account keys list \ --resource-group "${RESOURCE_GROUP}" \ --account-name "${STORAGE_ACCOUNT_NAME}" \ --query "[0].value" -o tsv) echo "" # Step 4: Create container log_info "Step 4: Creating storage container..." if az storage container show \ --name "${CONTAINER_NAME}" \ --account-name "${STORAGE_ACCOUNT_NAME}" \ --account-key "${STORAGE_KEY}" \ &> /dev/null; then log_success "Container already exists: ${CONTAINER_NAME}" else if az storage container create \ --name "${CONTAINER_NAME}" \ --account-name "${STORAGE_ACCOUNT_NAME}" \ --account-key "${STORAGE_KEY}" \ --public-access blob \ -o json &> /dev/null; then log_success "Container created: ${CONTAINER_NAME} (public blob access)" else log_error "Failed to create container" exit 1 fi fi echo "" # Step 5: Create CDN profile log_info "Step 5: Creating CDN profile..." if az cdn profile show \ --name "${CDN_PROFILE_NAME}" \ --resource-group "${RESOURCE_GROUP}" \ &> /dev/null; then log_success "CDN profile already exists: ${CDN_PROFILE_NAME}" else log_info " Creating CDN profile (this may take a few minutes)..." if az cdn profile create \ --name "${CDN_PROFILE_NAME}" \ --resource-group "${RESOURCE_GROUP}" \ --sku Standard_Microsoft \ -o json &> /dev/null; then log_success "CDN profile created: ${CDN_PROFILE_NAME}" else log_warning "Failed to create CDN profile (may need manual creation)" log_info "Create manually: https://portal.azure.com" fi fi echo "" # Step 6: Create CDN endpoint log_info "Step 6: Creating CDN endpoint..." if az cdn endpoint show \ --name "${CDN_ENDPOINT_NAME}" \ --profile-name "${CDN_PROFILE_NAME}" \ --resource-group "${RESOURCE_GROUP}" \ &> /dev/null; then log_success "CDN endpoint already exists: ${CDN_ENDPOINT_NAME}" CDN_ENDPOINT_URL=$(az cdn endpoint show \ --name "${CDN_ENDPOINT_NAME}" \ --profile-name "${CDN_PROFILE_NAME}" \ --resource-group "${RESOURCE_GROUP}" \ --query "hostName" -o tsv) else ORIGIN_HOST="${STORAGE_ACCOUNT_NAME}.blob.core.windows.net" log_info " Creating CDN endpoint (this may take 10-15 minutes)..." if az cdn endpoint create \ --name "${CDN_ENDPOINT_NAME}" \ --profile-name "${CDN_PROFILE_NAME}" \ --resource-group "${RESOURCE_GROUP}" \ --origin "${ORIGIN_HOST}" \ --origin-host-header "${ORIGIN_HOST}" \ --enable-compression true \ -o json &> /dev/null; then log_success "CDN endpoint created: ${CDN_ENDPOINT_NAME}" # Wait for endpoint to be ready log_info " Waiting for CDN endpoint to be ready..." sleep 30 CDN_ENDPOINT_URL=$(az cdn endpoint show \ --name "${CDN_ENDPOINT_NAME}" \ --profile-name "${CDN_PROFILE_NAME}" \ --resource-group "${RESOURCE_GROUP}" \ --query "hostName" -o tsv 2>/dev/null || echo "") else log_warning "Failed to create CDN endpoint (may need manual creation)" CDN_ENDPOINT_URL="" fi fi echo "" # Step 7: Configure CORS (if needed) log_info "Step 7: Configuring CORS..." az storage cors add \ --services b \ --methods GET HEAD OPTIONS \ --origins "*" \ --allowed-headers "*" \ --exposed-headers "*" \ --max-age 3600 \ --account-name "${STORAGE_ACCOUNT_NAME}" \ --account-key "${STORAGE_KEY}" \ &> /dev/null && log_success "CORS configured" || log_warning "CORS configuration skipped" echo "" # Step 8: Generate configuration log_info "Step 8: Generating configuration..." CONFIG_FILE="azure-cdn-config.env" cat > "${CONFIG_FILE}" << EOF # Azure CDN Configuration # Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC") # Storage Account AZURE_STORAGE_ACCOUNT=${STORAGE_ACCOUNT_NAME} AZURE_STORAGE_KEY=${STORAGE_KEY} AZURE_STORAGE_CONTAINER=${CONTAINER_NAME} AZURE_RESOURCE_GROUP=${RESOURCE_GROUP} AZURE_LOCATION=${LOCATION} # CDN AZURE_CDN_PROFILE=${CDN_PROFILE_NAME} AZURE_CDN_ENDPOINT=${CDN_ENDPOINT_NAME} AZURE_CDN_ENDPOINT_URL=${CDN_ENDPOINT_URL:-} # CDN Base URLs # Direct Blob Storage URL CDN_BASE_URL_BLOB=https://${STORAGE_ACCOUNT_NAME}.blob.core.windows.net/${CONTAINER_NAME}/ # CDN URL (use this once CDN endpoint is ready) if [ -n "${CDN_ENDPOINT_URL}" ]; then CDN_BASE_URL_CDN=https://${CDN_ENDPOINT_URL}/${CONTAINER_NAME}/ else CDN_BASE_URL_CDN=https://${CDN_ENDPOINT_NAME}.azureedge.net/${CONTAINER_NAME}/ fi # Recommended (use CDN URL if available, otherwise blob URL) CDN_BASE_URL=\${CDN_BASE_URL_CDN:-${CDN_BASE_URL_BLOB}} EOF log_success "Configuration saved: ${CONFIG_FILE}" echo "" # Summary log_info "=== Setup Summary ===" echo "" log_success "Resource Group: ${RESOURCE_GROUP}" log_success "Storage Account: ${STORAGE_ACCOUNT_NAME}" log_success "Container: ${CONTAINER_NAME} (public blob access)" log_success "CDN Profile: ${CDN_PROFILE_NAME}" log_success "CDN Endpoint: ${CDN_ENDPOINT_NAME}" echo "" log_info "URLs:" echo " Blob Storage: https://${STORAGE_ACCOUNT_NAME}.blob.core.windows.net/${CONTAINER_NAME}/" if [ -n "${CDN_ENDPOINT_URL}" ]; then echo " CDN: https://${CDN_ENDPOINT_URL}/${CONTAINER_NAME}/" else echo " CDN: (endpoint may still be provisioning, check in Azure Portal)" fi echo "" log_info "Next Steps:" echo "1. Source configuration: source ${CONFIG_FILE}" echo "2. Upload PNG files: ./scripts/deploy/upload-seals-to-azure.sh" echo "3. Update manifest URLs: CDN_BASE_URL=\${CDN_BASE_URL_CDN} ./scripts/deploy/update-manifest-seal-urls.sh" echo "" log_success "Azure CDN setup complete!"