# Azure DevOps Setup - Workload Identity Federation

> **Scope:** blazor-azure
> **Layer:** 2
> **Keywords:** azure devops, workload identity, ci/cd, pipelines, service connection, federated credential
> **Load When:** CI/CD setup, Azure DevOps integration, pipeline configuration

> **MORPH-SPEC Framework**
> Configuração de CI/CD com autenticação moderna (sem secrets)

---

## 🚀 Quick Start

### 1. Configurar Workload Identity (10 min)

```bash
# Ver guia completo abaixo em "Configurar Workload Identity Federation"

# Criar App Registrations
az ad app create --display-name "myapp-staging-pipeline"
az ad app create --display-name "myapp-prod-pipeline"

# Configurar federated credentials
# (Ver guia detalhado)
```

### 2. Importar Pipelines no Azure DevOps

1. **Pipelines** → **New pipeline**
2. **Azure Repos Git** → Selecione repo
3. **Existing Azure Pipelines YAML file**
4. Selecione:
   - `.azure/pipelines/staging-pipeline.yml`
   - `.azure/pipelines/prod-pipeline.yml`

### 3. Configurar Variáveis

Para cada pipeline, adicione:
```
ACR_NAME: <seu-acr-name>
APP_NAME: <seu-app-name>
SUBSCRIPTION_ID: <subscription-id>
```

### 4. Criar Environments

1. **Pipelines** → **Environments** → **New environment**
2. Criar:
   - `staging` (sem aprovação - deploy rápido)
   - `production` (aprovação obrigatória)

### 5. Testar!

```bash
# Trigger staging pipeline
git checkout staging
git commit -m "test" --allow-empty
git push origin staging

# Trigger prod pipeline
git checkout main
git commit -m "test" --allow-empty
git push origin main
# ⏸️ Aprovar manualmente no Azure DevOps
```

---

## 📋 Índice

1. [Pré-requisitos](#pré-requisitos)
2. [Configurar Workload Identity Federation](#configurar-workload-identity-federation)
3. [Criar Service Connections](#criar-service-connections)
4. [Configurar Pipelines](#configurar-pipelines)
5. [Configurar Environments e Aprovações](#configurar-environments-e-aprovações)
6. [Troubleshooting](#troubleshooting)

---

## 🔑 Pré-requisitos

### Azure
- ✅ Subscription Azure ativa
- ✅ Permissões de Owner ou User Access Administrator na subscription
- ✅ Azure CLI instalado: https://aka.ms/azure-cli

### Azure DevOps
- ✅ Organização Azure DevOps criada
- ✅ Projeto criado
- ✅ Permissões de administrador do projeto

### Informações Necessárias
```bash
# Azure
SUBSCRIPTION_ID="<sua-subscription-id>"
TENANT_ID="<seu-tenant-id>"

# Azure DevOps
ADO_ORG="<sua-org>"          # Ex: polymorphismtech
ADO_PROJECT="<seu-projeto>"   # Ex: morph-app

# Application
APP_NAME="<nome-da-app>"      # Ex: myapp
```

---

## 🌐 Configurar Workload Identity Federation

### Passo 1: Criar App Registration

```bash
# Login no Azure
az login
az account set --subscription $SUBSCRIPTION_ID

# Criar App Registration para Dev
APP_DEV_NAME="${APP_NAME}-dev-pipeline"
APP_DEV_ID=$(az ad app create \
  --display-name "$APP_DEV_NAME" \
  --query appId -o tsv)

echo "Dev App ID: $APP_DEV_ID"

# Criar Service Principal
SP_DEV_ID=$(az ad sp create \
  --id $APP_DEV_ID \
  --query id -o tsv)

echo "Dev Service Principal ID: $SP_DEV_ID"

# Repetir para Staging e Prod
APP_STAGING_NAME="${APP_NAME}-staging-pipeline"
APP_STAGING_ID=$(az ad app create --display-name "$APP_STAGING_NAME" --query appId -o tsv)
SP_STAGING_ID=$(az ad sp create --id $APP_STAGING_ID --query id -o tsv)

APP_PROD_NAME="${APP_NAME}-prod-pipeline"
APP_PROD_ID=$(az ad app create --display-name "$APP_PROD_NAME" --query appId -o tsv)
SP_PROD_ID=$(az ad sp create --id $APP_PROD_ID --query id -o tsv)
```

### Passo 2: Configurar Federated Credentials

```bash
# DEV Environment
cat <<EOF > federated-credential-dev.json
{
  "name": "dev-pipeline-federated",
  "issuer": "https://vstoken.dev.azure.com/<ADO_ORG_ID>",
  "subject": "sc://$ADO_ORG/$ADO_PROJECT/Azure-Dev-Connection",
  "description": "Federated credential for dev pipeline",
  "audiences": [
    "api://AzureADTokenExchange"
  ]
}
EOF

az ad app federated-credential create \
  --id $APP_DEV_ID \
  --parameters federated-credential-dev.json

# STAGING Environment
cat <<EOF > federated-credential-staging.json
{
  "name": "staging-pipeline-federated",
  "issuer": "https://vstoken.dev.azure.com/<ADO_ORG_ID>",
  "subject": "sc://$ADO_ORG/$ADO_PROJECT/Azure-Staging-Connection",
  "description": "Federated credential for staging pipeline",
  "audiences": [
    "api://AzureADTokenExchange"
  ]
}
EOF

az ad app federated-credential create \
  --id $APP_STAGING_ID \
  --parameters federated-credential-staging.json

# PROD Environment
cat <<EOF > federated-credential-prod.json
{
  "name": "prod-pipeline-federated",
  "issuer": "https://vstoken.dev.azure.com/<ADO_ORG_ID>",
  "subject": "sc://$ADO_ORG/$ADO_PROJECT/Azure-Prod-Connection",
  "description": "Federated credential for prod pipeline",
  "audiences": [
    "api://AzureADTokenExchange"
  ]
}
EOF

az ad app federated-credential create \
  --id $APP_PROD_ID \
  --parameters federated-credential-prod.json
```

**📌 Como obter ADO_ORG_ID:**
```bash
# Via Azure DevOps UI
# Vá em: Organization Settings → Overview → Organization ID
# Ou via API:
curl -u ":${AZURE_DEVOPS_PAT}" \
  "https://dev.azure.com/${ADO_ORG}/_apis/connectionData"
```

### Passo 3: Atribuir Permissões Azure

```bash
# DEV - Contributor na resource group
RG_DEV="rg-${APP_NAME}-dev"
az role assignment create \
  --assignee $SP_DEV_ID \
  --role Contributor \
  --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_DEV"

# STAGING - Contributor na resource group
RG_STAGING="rg-${APP_NAME}-staging"
az role assignment create \
  --assignee $SP_STAGING_ID \
  --role Contributor \
  --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_STAGING"

# PROD - Contributor na resource group
RG_PROD="rg-${APP_NAME}-prod"
az role assignment create \
  --assignee $SP_PROD_ID \
  --role Contributor \
  --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_PROD"

# ACR - AcrPush para todos
ACR_ID="/subscriptions/$SUBSCRIPTION_ID/resourceGroups/<rg-acr>/providers/Microsoft.ContainerRegistry/registries/<acr-name>"

az role assignment create --assignee $SP_DEV_ID --role AcrPush --scope $ACR_ID
az role assignment create --assignee $SP_STAGING_ID --role AcrPush --scope $ACR_ID
az role assignment create --assignee $SP_PROD_ID --role AcrPush --scope $ACR_ID
```

---

## 🔗 Criar Service Connections

### Via Azure DevOps UI

#### 1. Service Connection para Azure (Dev)

1. Vá em: **Project Settings** → **Service connections** → **New service connection**
2. Selecione: **Azure Resource Manager**
3. Authentication method: **Workload Identity federation (automatic)**
4. Scope level: **Subscription**
5. Preencha:
   - **Subscription ID**: `<sua-subscription-id>`
   - **Service connection name**: `Azure-Dev-Connection`
   - **Service Principal ID**: `$APP_DEV_ID` (do Passo 1)
6. Marque: **Grant access permission to all pipelines** (ou configure por pipeline)
7. Click: **Save**

#### 2. Repetir para Staging e Prod

- **Staging**: Nome `Azure-Staging-Connection`, usar `$APP_STAGING_ID`
- **Prod**: Nome `Azure-Prod-Connection`, usar `$APP_PROD_ID`

#### 3. Service Connection para ACR

1. **New service connection** → **Docker Registry**
2. Registry type: **Azure Container Registry**
3. Authentication type: **Workload Identity federation**
4. Preencha:
   - **Azure subscription**: Selecione a subscription
   - **Azure container registry**: Selecione seu ACR
   - **Service connection name**: `ACR-Connection`
5. **Save**

### Via Azure CLI (Alternativa)

```bash
# Requer Azure DevOps extension
az extension add --name azure-devops

# Login
az devops configure --defaults organization=https://dev.azure.com/$ADO_ORG project=$ADO_PROJECT

# Criar service connection (exemplo simplificado)
# Nota: Workload Identity via CLI é complexo, recomenda-se usar UI
```

---

## ⚙️ Configurar Pipelines

### Passo 1: Importar Pipelines

1. Vá em: **Pipelines** → **New pipeline**
2. Selecione: **Azure Repos Git** (ou seu SCM)
3. Selecione seu repositório
4. **Existing Azure Pipelines YAML file**
5. Path: `.azure/pipelines/dev-pipeline.yml`
6. **Continue** → **Save** (não run ainda)

Repetir para:
- `.azure/pipelines/staging-pipeline.yml`
- `.azure/pipelines/prod-pipeline.yml`

### Passo 2: Configurar Variáveis

#### Variáveis no Pipeline Level

Para cada pipeline, adicione as variáveis:

**Dev Pipeline** → **Edit** → **Variables**:
```
ACR_NAME: <seu-acr-name>
APP_NAME: <seu-app-name>
SUBSCRIPTION_ID: <subscription-id>
```

**Staging Pipeline** - mesmas variáveis

**Prod Pipeline** - mesmas variáveis

#### Variáveis no Group Level (Opcional)

1. **Pipelines** → **Library** → **+ Variable group**
2. Nome: `morph-common-vars`
3. Adicionar:
   ```
   ACR_NAME: <seu-acr>
   APP_NAME: <seu-app>
   SUBSCRIPTION_ID: <subscription-id>
   ```
4. Linkar aos pipelines:
   ```yaml
   variables:
     - group: morph-common-vars
     - template: pipeline-variables.yml
   ```

---

## 🛡️ Configurar Environments e Aprovações

### Passo 1: Criar Environments

1. **Pipelines** → **Environments** → **New environment**
2. Criar 3 environments:

**Dev Environment:**
- Name: `dev`
- Resource: None
- Approvals: **Nenhuma** (deploy automático)

**Staging Environment:**
- Name: `staging`
- Resource: None
- Approvals: **Opcional** (recomendado nenhuma para deploy rápido)
  - Se desejar: Add approver selecione você mesmo
  - Timeout: 24 hours

**Production Environment:**
- Name: `production`
- Resource: None
- Approvals: **OBRIGATÓRIO**
  - Add approvers: Selecione você mesmo
  - Timeout: 48 hours
  - **Checks**: Adicionar "Invoke REST API" para verificações adicionais (opcional)

### Passo 2: Configurar Branch Policies (Opcional)

Para `main` branch:

1. **Repos** → **Branches** → `main` → **Branch policies**
2. Habilitar:
   - **Require a minimum number of reviewers**: 0 (self-review via approval gate)
   - **Check for linked work items**: Recommended
   - **Build validation**: Link prod pipeline

---

## 🧪 Testar Configuração

### Teste 1: Dev Pipeline

```bash
# Criar branch develop
git checkout -b develop
git push origin develop

# Fazer um commit qualquer
echo "test" > test.txt
git add test.txt
git commit -m "test: trigger dev pipeline"
git push origin develop
```

**Verificar:**
- Pipeline triggou automaticamente
- Build passou
- Deploy para App Service Free foi bem-sucedido
- Health check passou

### Teste 2: Staging Pipeline

```bash
# Merge develop em main
git checkout main
git merge develop
git push origin main
```

**Verificar:**
- Staging pipeline triggou
- Container foi buildado e pushed para ACR
- Deploy para Container Apps funcionou
- Integration tests passaram

### Teste 3: Prod Pipeline (Manual)

1. **Pipelines** → **prod-pipeline** → **Run pipeline**
2. Verificar aprovação manual aparece
3. Aprovar deploy
4. Verificar deployment bem-sucedido

---

## 🆘 Troubleshooting

### Erro: "Failed to get federated token"

**Causa:** Subject no federated credential não match com service connection.

**Solução:**
```bash
# Verificar subject correto
# Deve ser: sc://<ORG>/<PROJECT>/<SERVICE_CONNECTION_NAME>

# Recriar federated credential com subject correto
az ad app federated-credential delete \
  --id $APP_ID \
  --federated-credential-id <credential-id>

# Criar novamente com subject correto
az ad app federated-credential create \
  --id $APP_ID \
  --parameters federated-credential.json
```

### Erro: "Insufficient permissions"

**Causa:** Service Principal não tem permissões na subscription/resource group.

**Solução:**
```bash
# Verificar role assignments
az role assignment list \
  --assignee $SP_ID \
  --output table

# Adicionar Contributor se necessário
az role assignment create \
  --assignee $SP_ID \
  --role Contributor \
  --scope "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_NAME"
```

### Erro: "Container registry not found"

**Causa:** Service Principal não tem permissão no ACR.

**Solução:**
```bash
# Adicionar AcrPush role
az role assignment create \
  --assignee $SP_ID \
  --role AcrPush \
  --scope $ACR_ID
```

### Erro: "Pipeline not authorized to access service connection"

**Causa:** Pipeline não foi autorizado a usar a service connection.

**Solução:**
1. **Project Settings** → **Service connections**
2. Click na service connection
3. **Security** → Adicionar pipeline específico ou marcar "Grant access to all pipelines"

---

## 📚 Referências

- [Workload Identity Federation](https://learn.microsoft.com/azure/devops/pipelines/library/connect-to-azure)
- [Azure Pipelines YAML Schema](https://learn.microsoft.com/azure/devops/pipelines/yaml-schema)
- [Environments](https://learn.microsoft.com/azure/devops/pipelines/process/environments)
- [Service Connections](https://learn.microsoft.com/azure/devops/pipelines/library/service-endpoints)

---

## ✅ Checklist Final

Antes de ir para produção:

- [ ] Workload Identity configurada para dev/staging/prod
- [ ] Service connections criadas e testadas
- [ ] Variáveis configuradas (ACR_NAME, APP_NAME, SUBSCRIPTION_ID)
- [ ] Environments criados (dev, staging, production)
- [ ] Aprovações configuradas (production requer aprovação manual)
- [ ] Dev pipeline testado com sucesso
- [ ] Staging pipeline testado com sucesso
- [ ] Prod pipeline testado com aprovação
- [ ] Health checks funcionando
- [ ] Monitoring configurado (Application Insights)
- [ ] Rollback plan documentado

---

*MORPH-SPEC by Polymorphism Tech*
