# Deploy to Azure

> Source: https://agilitycms.com/docs/dotNet/deploy-to-azure

This guide covers deploying your Agility CMS .NET application to Azure App Service, including one-click deployment, manual deployment, and CI/CD setup.

## One-Click Deployment

The fastest way to deploy is using our Azure Resource Manager (ARM) templates.

### Deploy Blazor Starter

[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fagility%2Fagilitycms-dotnet-starter%2Fmain%2FAgility.NET.Blazor.Starter%2Fazuredeploy.json)

### Deploy MVC Starter

[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fagility%2Fagilitycms-dotnet-starter%2Fmain%2FAgility.NET.MVC.Starter%2Fazuredeploy.json)

### Deployment Parameters

| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `webAppName` | Yes | - | Globally unique name (becomes `yourname.azurewebsites.net`) |
| `instanceGuid` | Yes | - | Agility CMS Instance GUID |
| `securityKey` | Yes | - | Security key for preview mode |
| `fetchAPIKey` | Yes | - | Live content API key |
| `previewAPIKey` | Yes | - | Preview content API key |
| `locales` | No | `en-us` | Comma-separated locale codes |
| `channelName` | No | `website` | Agility sitemap channel |
| `sku` | No | `B1` | App Service Plan SKU |

### SKU Requirements

| SKU | WebSockets | AlwaysOn | Recommended For |
|-----|------------|----------|-----------------|
| F1 (Free) | No | No | Not supported |
| D1 (Shared) | No | No | Not supported |
| **B1** | Yes | Yes | Development/Testing |
| **S1** | Yes | Yes | Production |
| **P1V2+** | Yes | Yes | High-traffic production |

> **Important**: The Blazor starter requires WebSockets for Interactive Server components. Choose B1 or higher.

### What Gets Deployed

The ARM template creates:

- **Linux App Service** running .NET 10
- **App Service Plan** with your chosen SKU
- **WebSockets enabled** (required for Blazor Interactive Server)
- **AlwaysOn enabled** (keeps the app warm)
- **HTTP/2 enabled** (better performance)
- **TLS 1.2 minimum** (security)
- **Git deployment** from the repository

## Manual Deployment

### Using Visual Studio Code

1. **Install Extensions**
   - Azure App Service extension
   - Azure Resources extension

2. **Build for Release**
   ```bash
   dotnet publish -c Release -o ./publish
   ```

3. **Deploy**
   - Right-click the `publish` folder
   - Select "Deploy to Web App..."
   - Choose your subscription and App Service
   - Confirm deployment

### Using Visual Studio

1. **Right-click** the project in Solution Explorer
2. Select **Publish**
3. Choose **Azure > Azure App Service (Linux)**
4. Select or create an App Service
5. Click **Publish**

### Using Azure CLI

```bash
# Login to Azure
az login

# Create resource group
az group create --name myResourceGroup --location eastus

# Create App Service Plan
az appservice plan create \
  --name myAppServicePlan \
  --resource-group myResourceGroup \
  --sku B1 \
  --is-linux

# Create Web App
az webapp create \
  --name myAgilityApp \
  --resource-group myResourceGroup \
  --plan myAppServicePlan \
  --runtime "DOTNET|10.0"

# Configure settings
az webapp config appsettings set \
  --name myAgilityApp \
  --resource-group myResourceGroup \
  --settings \
    AppSettings__InstanceGUID=your-guid \
    AppSettings__SecurityKey=your-key \
    AppSettings__FetchAPIKey=defaultlive.your-key \
    AppSettings__PreviewAPIKey=defaultpreview.your-key

# Enable WebSockets (required for Blazor)
az webapp config set \
  --name myAgilityApp \
  --resource-group myResourceGroup \
  --web-sockets-enabled true

# Deploy code
az webapp deployment source config-local-git \
  --name myAgilityApp \
  --resource-group myResourceGroup

# Push to deploy
git remote add azure <deployment-url>
git push azure main
```

### Using GitHub Actions

Create `.github/workflows/azure-deploy.yml`:

```yaml
name: Deploy to Azure

on:
  push:
    branches: [main]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '10.0.x'

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: |
          cd Agility.NET.Blazor.Starter
          dotnet restore
          npm install

      - name: Build
        run: |
          cd Agility.NET.Blazor.Starter
          dotnet publish -c Release -o ./publish

      - name: Deploy to Azure
        uses: azure/webapps-deploy@v3
        with:
          app-name: ${{ secrets.AZURE_APP_NAME }}
          publish-profile: ${{ secrets.AZURE_PUBLISH_PROFILE }}
          package: ./Agility.NET.Blazor.Starter/publish
```

**Setup:**
1. Go to Azure Portal > Your App Service > Deployment Center
2. Download the publish profile
3. Add it as a GitHub secret named `AZURE_PUBLISH_PROFILE`
4. Add your app name as `AZURE_APP_NAME`

## Post-Deployment Configuration

### 1. Configure Environment Variables

In Azure Portal:

1. Go to your App Service
2. Navigate to **Settings > Environment variables**
3. Add application settings:

| Name | Value |
|------|-------|
| `AppSettings__InstanceGUID` | Your instance GUID |
| `AppSettings__SecurityKey` | Your security key |
| `AppSettings__FetchAPIKey` | Your live API key |
| `AppSettings__PreviewAPIKey` | Your preview API key |
| `AppSettings__Locales` | `en-us` (or your locales) |
| `AppSettings__ChannelName` | `website` |
| `AppSettings__CacheInMinutes` | `5` |

4. Click **Save** and confirm restart

### 2. Configure Webhooks

Set up cache invalidation in Agility CMS:

1. Go to **Settings > Webhooks** in Agility CMS
2. Click **Add Webhook**
3. Configure:
   - **Name**: Azure Cache Invalidation
   - **URL**: `https://your-app.azurewebsites.net/api/revalidate`
   - **Events**: Content Published, Page Published
4. Save the webhook

### 3. Configure Preview URLs

Enable content preview:

1. Go to **Settings > Preview URLs** in Agility CMS
2. Add a preview URL:
   - **Name**: Azure Preview
   - **URL**: `https://your-app.azurewebsites.net{path}?agilitypreviewkey={previewkey}`
3. Save

### 4. Custom Domain (Optional)

1. Go to **Settings > Custom domains** in Azure Portal
2. Click **Add custom domain**
3. Enter your domain name
4. Add the required DNS records
5. Add an SSL certificate (free managed certificate available)

## Monitoring and Troubleshooting

### Enable Application Insights

```bash
az webapp config appsettings set \
  --name myAgilityApp \
  --resource-group myResourceGroup \
  --settings APPLICATIONINSIGHTS_CONNECTION_STRING=your-connection-string
```

### View Logs

**Log Stream (real-time):**
```bash
az webapp log tail --name myAgilityApp --resource-group myResourceGroup
```

**In Azure Portal:**
1. Go to your App Service
2. Navigate to **Monitoring > Log stream**

### Common Issues

#### 502 Bad Gateway

- Check application logs for startup errors
- Verify .NET version matches your app
- Ensure all environment variables are set

#### SignalR/WebSocket Errors (Blazor)

1. Verify WebSockets are enabled:
   ```bash
   az webapp config show --name myAgilityApp --resource-group myResourceGroup \
     --query webSocketsEnabled
   ```

2. Enable if needed:
   ```bash
   az webapp config set --name myAgilityApp --resource-group myResourceGroup \
     --web-sockets-enabled true
   ```

#### Content Not Loading

1. Verify API keys are correct
2. Check Agility CMS has published content
3. Verify `ChannelName` matches your sitemap

#### Slow Cold Starts

Enable AlwaysOn to prevent cold starts:
```bash
az webapp config set --name myAgilityApp --resource-group myResourceGroup \
  --always-on true
```

## Scaling

### Vertical Scaling (Scale Up)

Change to a larger SKU:

```bash
az appservice plan update --name myAppServicePlan \
  --resource-group myResourceGroup \
  --sku S1
```

### Horizontal Scaling (Scale Out)

Add more instances:

```bash
az appservice plan update --name myAppServicePlan \
  --resource-group myResourceGroup \
  --number-of-workers 3
```

### Auto-Scaling

Configure auto-scale rules in Azure Portal:

1. Go to your App Service Plan
2. Navigate to **Settings > Scale out**
3. Configure rules based on CPU, memory, or HTTP queue length

## Cost Optimization

### Development/Testing

- Use **B1** SKU (~$13/month)
- Scale down when not in use
- Use deployment slots for staging

### Production

- Use **S1** or higher for production workloads
- Enable CDN for static assets
- Use Azure Front Door for global distribution

### Free Tier Limitations

The F1 (Free) tier does **not** support:
- WebSockets (required for Blazor Interactive Server)
- AlwaysOn (causes cold starts)
- Custom domains with SSL

## Security Checklist

- [ ] Environment variables set (not in code)
- [ ] HTTPS enforced
- [ ] TLS 1.2 minimum
- [ ] API keys are not exposed
- [ ] Webhook endpoint is secured
- [ ] Application Insights enabled for monitoring

## Next Steps

- [Configuration](/docs/dotNet/configuration) - All configuration options
- [Migration Guide](/docs/dotNet/migration-guide) - Upgrading from older versions
- [Blazor Starter](/docs/dotNet/blazor-starter) - Blazor-specific deployment notes
- [MVC Starter](/docs/dotNet/mvc-starter) - MVC-specific deployment notes
