Guía completa para desplegar Azure Serverless Product API en Microsoft Azure desde cero.
- Prerrequisitos
- Configuración Inicial
- Deploy Backend (Azure Functions)
- Deploy Frontend (Static Web App)
- Configuración CORS
- CI/CD con GitHub Actions
- Verificación del Deployment
- Troubleshooting
- Rollback y Gestión de Versiones
| Herramienta | Versión Mínima | Instalación |
|---|---|---|
| Azure CLI | 2.50+ | Descargar |
| Azure Functions Core Tools | 4.0+ | Descargar |
| Python | 3.12 | Descargar |
| Git | 2.0+ | Descargar |
| Node.js | 18+ (opcional) | Descargar |
# Azure CLI
az --version
# Azure Functions Core Tools
func --version
# Python
python --version
# Git
git --version- Cuenta activa de Azure
- Suscripción válida (Free Tier disponible)
- Permisos de Contributor en la suscripción
Crear cuenta gratuita: azure.microsoft.com/free
# Iniciar sesión
az login
# Verificar cuenta
az account show
# Listar suscripciones
az account list --output table
# Seleccionar suscripción específica (si tienes múltiples)
az account set --subscription "Suscripción de Azure 1"# Clonar proyecto
git clone https://github.com/AndresRJ18/AZURE-Serverless-API.git
cd AZURE-Serverless-API
# Verificar estructura
ls -laCrea un archivo .env en la raíz del proyecto:
# .env
RESOURCE_GROUP=rg-products-api
LOCATION=eastus
FUNCTION_APP_NAME=fnapi6794
STORAGE_ACCOUNT_NAME=stapi2025andres
STATIC_WEB_APP_NAME=products-dashboard
PYTHON_VERSION=3.12# Crear grupo de recursos
az group create \
--name rg-products-api \
--location eastus
# Verificar creación
az group show --name rg-products-api# Crear cuenta de almacenamiento
az storage account create \
--name stapi2025andres \
--resource-group rg-products-api \
--location eastus \
--sku Standard_LRS \
--kind StorageV2
# Verificar
az storage account show \
--name stapi2025andres \
--resource-group rg-products-api
⚠️ Importante: El nombre de la storage account debe ser único globalmente (solo minúsculas y números).
# Crear Azure Function App
az functionapp create \
--resource-group rg-products-api \
--consumption-plan-location eastus \
--runtime python \
--runtime-version 3.12 \
--functions-version 4 \
--name fnapi6794 \
--storage-account stapi2025andres \
--os-type Linux \
--disable-app-insights false
# Esperar a que termine (puede tardar 2-3 minutos)Parámetros explicados:
--consumption-plan-location: Región del plan serverless--runtime python: Runtime de Python--runtime-version 3.12: Versión de Python--functions-version 4: Azure Functions v4--os-type Linux: Sistema operativo (requerido para Python)--disable-app-insights false: Habilita Application Insights
# Configurar variables de entorno
az functionapp config appsettings set \
--name fnapi6794 \
--resource-group rg-products-api \
--settings \
"PYTHON_VERSION=3.12" \
"ENABLE_ORYX_BUILD=true" \
"SCM_DO_BUILD_DURING_DEPLOYMENT=true"
# Verificar configuración
az functionapp config appsettings list \
--name fnapi6794 \
--resource-group rg-products-api \
--output table# Navegar a la carpeta backend
cd backend
# Instalar dependencias localmente (opcional, para verificar)
pip install -r requirements.txt
# Deploy con Azure Functions Core Tools
func azure functionapp publish fnapi6794 --python
# Alternativa: Deploy con Azure CLI
# az functionapp deployment source config-zip \
# --resource-group rg-products-api \
# --name fnapi6794 \
# --src backend.zipOutput esperado:
Getting site publishing info...
Creating archive for current directory...
Uploading 2.34 MB [####################]
Upload completed successfully.
Deployment completed successfully.
Syncing triggers...
Functions in fnapi6794:
CreateProduct - [httpTrigger]
Invoke url: https://fnapi6794.azurewebsites.net/api/products
DeleteProduct - [httpTrigger]
Invoke url: https://fnapi6794.azurewebsites.net/api/products/{id}
GetProduct - [httpTrigger]
Invoke url: https://fnapi6794.azurewebsites.net/api/products/{id}
GetProducts - [httpTrigger]
Invoke url: https://fnapi6794.azurewebsites.net/api/products
HealthCheck - [httpTrigger]
Invoke url: https://fnapi6794.azurewebsites.net/api/health
UpdateProduct - [httpTrigger]
Invoke url: https://fnapi6794.azurewebsites.net/api/products/{id}
# Health check
curl https://fnapi6794.azurewebsites.net/api/health
# Listar productos
curl https://fnapi6794.azurewebsites.net/api/productsRespuesta esperada:
{
"status": "healthy",
"timestamp": "2025-02-10T...",
"service": "Products API"
}# Crear Static Web App
az staticwebapp create \
--name products-dashboard \
--resource-group rg-products-api \
--source https://github.com/AndresRJ18/AZURE-Serverless-API \
--location eastus \
--branch main \
--app-location "/frontend" \
--login-with-githubNota: Deberás autorizar Azure a acceder a tu repositorio de GitHub.
El comando anterior genera un workflow de GitHub Actions automáticamente. Verifica que se haya creado:
# Ver el archivo generado
cat .github/workflows/azure-static-web-apps-*.yml-
Ir a Azure Portal:
- Navega a portal.azure.com
- Click en "Create a resource"
- Busca "Static Web App"
-
Configuración básica:
- Subscription: Suscripción de Azure 1
- Resource Group: rg-products-api
- Name: products-dashboard
- Region: East US
- Plan type: Free
-
Deployment:
- Source: GitHub
- Organization: AndresRJ18
- Repository: AZURE-Serverless-API
- Branch: main
-
Build Details:
- App location:
/frontend - API location: (dejar vacío)
- Output location:
/
- App location:
-
Review + Create
- Click en "Create"
- Espera 2-3 minutos
# Listar Static Web Apps
az staticwebapp list \
--resource-group rg-products-api \
--output table
# Obtener URL específica
az staticwebapp show \
--name products-dashboard \
--resource-group rg-products-api \
--query "defaultHostname" \
--output tsvOutput:
nice-beach-0a1b2c3d4.azurestaticapps.net
Edita frontend/js/app.js:
// Cambiar esta línea:
const API_BASE_URL = 'http://localhost:7071';
// Por:
const API_BASE_URL = 'https://fnapi6794.azurewebsites.net';Commit y push:
git add frontend/js/app.js
git commit -m "Update API URL for production"
git push origin mainEl deployment se hará automáticamente con GitHub Actions.
Para permitir que el frontend acceda al backend, configura CORS:
# Agregar origen del frontend
az functionapp cors add \
--name fnapi6794 \
--resource-group rg-products-api \
--allowed-origins https://nice-beach-0a1b2c3d4.azurestaticapps.net
# Verificar configuración CORS
az functionapp cors show \
--name fnapi6794 \
--resource-group rg-products-api- Ve a Function App → fnapi6794
- En el menú izquierdo: API → CORS
- Agrega:
https://nice-beach-0a1b2c3d4.azurestaticapps.net - Click en Save
Edita backend/host.json:
{
"version": "2.0",
"extensions": {
"http": {
"routePrefix": "api",
"cors": {
"allowedOrigins": [
"https://nice-beach-0a1b2c3d4.azurestaticapps.net"
],
"supportCredentials": false
}
}
}
}Luego re-deploy:
cd backend
func azure functionapp publish fnapi6794 --pythonCrea .github/workflows/backend-deploy.yml:
name: Deploy Azure Functions Backend
on:
push:
branches:
- main
paths:
- 'backend/**'
workflow_dispatch:
env:
AZURE_FUNCTIONAPP_NAME: fnapi6794
AZURE_FUNCTIONAPP_PACKAGE_PATH: './backend'
PYTHON_VERSION: '3.12'
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: 'Checkout GitHub Action'
uses: actions/checkout@v4
- name: Setup Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: 'Install dependencies'
shell: bash
run: |
cd ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}
python -m pip install --upgrade pip
pip install -r requirements.txt --target=".python_packages/lib/site-packages"
- name: 'Run Azure Functions Action'
uses: Azure/functions-action@v1
with:
app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}
publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}
scm-do-build-during-deployment: true
enable-oryx-build: true# Descargar publish profile
az functionapp deployment list-publishing-profiles \
--name fnapi6794 \
--resource-group rg-products-api \
--xml > publish-profile.xml
# Ver contenido
cat publish-profile.xml- Ve a GitHub → Repositorio → Settings → Secrets and variables → Actions
- Click en New repository secret
- Name:
AZURE_FUNCTIONAPP_PUBLISH_PROFILE - Value: Pega el contenido de
publish-profile.xml - Click en Add secret
El workflow del frontend se crea automáticamente al crear la Static Web App. Verifica:
cat .github/workflows/azure-static-web-apps-*.yml# 1. Backend Health Check
curl https://fnapi6794.azurewebsites.net/api/health
# 2. Backend API Endpoints
curl https://fnapi6794.azurewebsites.net/api/products
# 3. Frontend accesible
curl https://nice-beach-0a1b2c3d4.azurestaticapps.net
# 4. CORS funcionando (desde el navegador)
# Abre DevTools → Network → verifica que no haya errores CORS
# 5. Logs del backend
az functionapp log tail \
--name fnapi6794 \
--resource-group rg-products-api
# 6. Estado de deployments
az functionapp deployment list \
--name fnapi6794 \
--resource-group rg-products-api \
--output table# Test completo de flujo CRUD
# 1. Crear producto
curl -X POST https://fnapi6794.azurewebsites.net/api/products \
-H "Content-Type: application/json" \
-d '{"name": "Test Product", "price": 99.99, "stock": 10}'
# 2. Listar productos (debe incluir el creado)
curl https://fnapi6794.azurewebsites.net/api/products
# 3. Actualizar producto (usa el ID del paso 1)
curl -X PUT https://fnapi6794.azurewebsites.net/api/products/{ID} \
-H "Content-Type: application/json" \
-d '{"price": 89.99}'
# 4. Eliminar producto
curl -X DELETE https://fnapi6794.azurewebsites.net/api/products/{ID}Error:
The subscription is not registered to use namespace 'Microsoft.Web'
Solución:
az provider register --namespace Microsoft.Web
az provider show --namespace Microsoft.Web --query "registrationState"
# Espera hasta que diga "Registered"Error:
Deployment timed out after 600 seconds
Solución:
# Aumentar timeout
func azure functionapp publish fnapi6794 --python --timeout 1200
# O usar deployment por zip
cd backend
zip -r ../backend.zip .
cd ..
az functionapp deployment source config-zip \
--resource-group rg-products-api \
--name fnapi6794 \
--src backend.zipError (en navegador):
Access to fetch at '...' has been blocked by CORS policy
Solución:
# 1. Verificar CORS actual
az functionapp cors show \
--name fnapi6794 \
--resource-group rg-products-api
# 2. Agregar origen correcto
az functionapp cors add \
--name fnapi6794 \
--resource-group rg-products-api \
--allowed-origins https://tu-frontend-url.azurestaticapps.net
# 3. Para desarrollo local temporal (NO usar en producción)
az functionapp cors add \
--name fnapi6794 \
--resource-group rg-products-api \
--allowed-origins "*"Error:
The Python version is not supported
Solución:
# Verificar versión configurada
az functionapp config show \
--name fnapi6794 \
--resource-group rg-products-api \
--query "linuxFxVersion"
# Configurar Python 3.12
az functionapp config set \
--name fnapi6794 \
--resource-group rg-products-api \
--linux-fx-version "PYTHON|3.12"Error:
ModuleNotFoundError: No module named 'pydantic'
Solución:
# 1. Verificar requirements.txt
cat backend/requirements.txt
# 2. Habilitar remote build
az functionapp config appsettings set \
--name fnapi6794 \
--resource-group rg-products-api \
--settings \
"ENABLE_ORYX_BUILD=true" \
"SCM_DO_BUILD_DURING_DEPLOYMENT=true"
# 3. Re-deploy
cd backend
func azure functionapp publish fnapi6794 --python --build remoteSolución:
# 1. Ver logs en tiempo real
az functionapp log tail \
--name fnapi6794 \
--resource-group rg-products-api
# 2. Ver logs históricos
az monitor app-insights query \
--app fnapi6794 \
--analytics-query "requests | where timestamp > ago(1h) | order by timestamp desc"
# 3. Test local para debug
cd backend
func startaz functionapp deployment list \
--name fnapi6794 \
--resource-group rg-products-api \
--output table# Método 1: Slot swap (requiere Standard tier)
# No disponible en Consumption Plan
# Método 2: Re-deploy desde Git tag
git checkout v1.0.0
cd backend
func azure functionapp publish fnapi6794 --python
git checkout main
# Método 3: Deploy desde backup local
cd backup/backend-v1.0.0
func azure functionapp publish fnapi6794 --python# Backup del código
git tag -a v1.0.1 -m "Production deployment 2025-02-10"
git push origin v1.0.1
# Backup de configuración
az functionapp config appsettings list \
--name fnapi6794 \
--resource-group rg-products-api \
> config-backup-$(date +%Y%m%d).json# Ver métricas en tiempo real
az monitor app-insights metrics show \
--app fnapi6794 \
--metric requests/count \
--interval PT1M
# Query logs
az monitor app-insights query \
--app fnapi6794 \
--analytics-query "
requests
| where timestamp > ago(24h)
| summarize count() by resultCode
| order by count_ desc
"# Crear alerta para errores 500
az monitor metrics alert create \
--name "high-error-rate" \
--resource-group rg-products-api \
--scopes /subscriptions/{sub-id}/resourceGroups/rg-products-api/providers/Microsoft.Web/sites/fnapi6794 \
--condition "count requests/failed > 10" \
--window-size 5m \
--evaluation-frequency 1m \
--action email your@email.com# Ver costos acumulados
az consumption usage list \
--start-date 2025-02-01 \
--end-date 2025-02-28 \
--output tableCostos estimados mensuales (Free Tier):
- Azure Functions: $0 (hasta 1M ejecuciones)
- Static Web App: $0 (hasta 100GB bandwidth)
- Storage Account: ~$0.50 (uso mínimo)
- Application Insights: $0 (hasta 5GB/mes)
Total: < $1/mes 🎉
- Configurar custom domain
- Agregar SSL certificate (Let's Encrypt)
- Implementar Azure AD B2C para autenticación
- Configurar Azure SQL Database
- Habilitar Application Insights dashboards
- Implementar rate limiting
- Configurar alertas de monitoreo
- Azure Functions Documentation
- Static Web Apps Documentation
- Azure CLI Reference
- GitHub Actions for Azure