Azure DevOps Pipelines: CI/CD na Microsoft Cloud
Azure DevOps é o guarda-chuva da Microsoft com 5 produtos: Boards (kanban/scrum), Repos (Git hosting), Pipelines (CI/CD), Test Plans (QA), Artifacts (package registry). Este módulo foca em Azure Pipelines — o motor de CI/CD, que evoluiu de “classic pipelines” (UI clicada) para YAML pipelines versionados no repo. Se seu stack é Microsoft (Azure AD, AKS, App Service, SQL Server), ou seu time já usa Azure Boards/Repos, é o caminho de menor atrito. A Microsoft também tem GitHub (compram em 2018), então GH Actions é a aposta estratégica de longo prazo — mas Azure Pipelines continua tendo diferenciais fortes em gate de aprovação, governança e integração com Azure.
Azure DevOps em 30 segundos
Estrutura de um pipeline YAML
# azure-pipelines.yml — pipeline real de uma API Node
trigger:
branches:
include: [main, release/*]
paths:
exclude: [docs/*, README.md]
pr:
branches:
include: [main]
variables:
- group: 'shared-config' # Variable Group da Library
- name: NODE_VERSION
value: '20.x'
pool:
vmImage: 'ubuntu-latest'
stages:
- stage: Build
jobs:
- job: build_test
displayName: 'Build & Test'
steps:
- task: NodeTool@0
inputs: { versionSpec: $(NODE_VERSION) }
- task: Cache@2
inputs:
key: 'npm | "$(Agent.OS)" | package-lock.json'
restoreKeys: |
npm | "$(Agent.OS)"
path: $(Pipeline.Workspace)/.npm
- script: npm ci --cache $(Pipeline.Workspace)/.npm
displayName: 'Install deps'
- script: npm run lint
displayName: 'Lint'
- script: npm test -- --ci --coverage
displayName: 'Test'
- task: PublishTestResults@2
condition: succeededOrFailed()
inputs: { testResultsFiles: 'junit.xml' }
- task: PublishCodeCoverageResults@2
inputs:
summaryFileLocation: 'coverage/cobertura-coverage.xml'
- script: npm run build
- publish: dist
artifact: appDeploy em AKS com stage + approval
- stage: Deploy_Staging
dependsOn: Build
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
variables:
- group: aks-staging # conn string, KV secrets
jobs:
- deployment: deploy_aks
environment: 'staging.aks' # environment com approval/checks
strategy:
runOnce:
deploy:
steps:
- download: current
artifact: app
- task: KubernetesManifest@1
displayName: 'Deploy to AKS'
inputs:
action: 'deploy'
kubernetesServiceConnection: 'aks-staging-connection'
namespace: 'api'
manifests: |
k8s/deployment.yaml
k8s/service.yaml
containers: |
acrmeusite.azurecr.io/api:$(Build.BuildId)
- stage: Deploy_Prod
dependsOn: Deploy_Staging
jobs:
- deployment: deploy_prod
environment: 'production.aks' # environment exige 2 approvers
strategy:
canary:
increments: [10, 50, 100]
preDeploy:
steps:
- script: echo "Starting canary rollout"
deploy:
steps:
- task: KubernetesManifest@1
inputs:
action: 'deploy'
kubernetesServiceConnection: 'aks-prod-connection'
namespace: 'api'
manifests: k8s/deployment.yaml
containers: acrmeusite.azurecr.io/api:$(Build.BuildId)
postRouteTraffic:
steps:
- script: ./scripts/smoke-test.sh $(canary.percentage)
on:
failure:
steps:
- script: echo "Rollback automático"
success:
steps:
- script: echo "Canary $(canary.percentage)% OK"Canary nativo: o tipo da strategy faz rollout por porcentagem, executa smoke test entre incrementos, e reverte se falhar. GH Actions e Jenkins precisam de plugin ou script caseiro pra mesma coisa.
Service Connections e Workload Identity Federation
| Modo | Como funciona | Recomendação |
|---|---|---|
| Azure Resource Manager (auto) | Azure DevOps cria Service Principal automaticamente | Evite — secret estático de 2 anos |
| Service Principal manual | Você cria SP no Azure AD, coloca client secret no ADO | Legado — rotaciona à mão |
| Managed Identity | Pipeline usa identidade do agente self-hosted em Azure VM | Ótimo para self-hosted em Azure |
| Workload Identity Federation | OIDC: ADO emite token por run; Azure AD valida trust (org/project/pipeline) e dá STS | 🏆 Padrão moderno — zero secret |
# Com WIF, o YAML não muda muito — a mágica é na Service Connection
- task: AzureCLI@2
inputs:
azureSubscription: 'prod-wif' # Service Connection configurada como WIF
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
az aks get-credentials -n prod-cluster -g prod-rg
kubectl get pods -AVariable Groups + Key Vault
Nunca use . ADO masca variáveis declaradas secret, mas derivados (base64, JSON) escapam. Em logs de falha, erros de ferramentas podem despejar env — use com parcimônia e configure retention curto pra logs sensíveis.
Templates — reuso em escala
# templates/node-build.yml (no repo pipelines-templates)
parameters:
- name: nodeVersion
type: string
default: '20.x'
- name: runCoverage
type: boolean
default: true
steps:
- task: NodeTool@0
inputs: { versionSpec: ${'$'}{{ parameters.nodeVersion }} }
- script: npm ci
- script: npm run lint
- script: npm test -- --ci
displayName: 'Test'
- ${'$'}{{ if eq(parameters.runCoverage, true) }}:
- task: PublishCodeCoverageResults@2
inputs: { summaryFileLocation: 'coverage/cobertura-coverage.xml' }# pipeline consumidor
resources:
repositories:
- repository: templates
type: git
name: infra/pipelines-templates
ref: refs/tags/v2.1.0 # pin de versão
jobs:
- job: build
steps:
- template: templates/node-build.yml@templates
parameters:
nodeVersion: '22.x'
runCoverage: trueEnvironments, checks e approvals
Agents: Microsoft-hosted vs Self-hosted
| Dimensão | Microsoft-hosted | Self-hosted |
|---|---|---|
| Setup | Zero — usa vmImage: ubuntu-latest | Instala agent em VM/container/K8s |
| Custo | Free tier limitado; paralelismo comprado | Sua infra |
| Performance | VM compartilhada, sem estado | Disco persistente, cache local acelera |
| Rede | Público | Acessa VPN/VNet — necessário pra recursos privados |
| Ferramentas | Mudam por imagem (upgrades anuais) | Você escolhe |
| Quando usar | Pipelines públicos ou sem rede privada | On-prem, integração com Azure private link |
Monorepo, path filters e matriz
trigger:
branches: { include: [main] }
paths:
include:
- apps/api/**
- libs/**
- azure-pipelines-api.yml
jobs:
- job: test_matrix
strategy:
matrix:
node18_linux:
NODE: '18.x'
VMIMG: 'ubuntu-latest'
node20_linux:
NODE: '20.x'
VMIMG: 'ubuntu-latest'
node20_windows:
NODE: '20.x'
VMIMG: 'windows-latest'
pool:
vmImage: $(VMIMG)
steps:
- task: NodeTool@0
inputs: { versionSpec: $(NODE) }
- script: npm ci
- script: npm testSegurança e governança
Azure DevOps vs GitHub Actions — o dilema Microsoft
| Aspecto | Azure DevOps | GitHub Actions |
|---|---|---|
| Foco | Suíte ALM completa (Boards, Repos, Pipelines, Test, Artifacts) | CI/CD dentro do GitHub |
| Governança | Environments, approvals, checks, área/iteration paths | Environments + protection rules (mais simples) |
| Gate de aprovação | 🏆 Flexível (REST check, business hours, exclusive lock) | Básico (required reviewers, wait) |
| Integração Azure | 🏆 Nativa, WIF, tasks prontas | Boa (actions oficiais), mas menos profunda |
| YAML | Mais verboso, stages explícitos | Mais enxuto, eventos amplos |
| Estratégia Microsoft | Maintenance mode — evolui pouco | 🏆 Roadmap ativo (AI, Copilot) |
| Startup/greenfield | Raro hoje | Default |
| Enterprise Microsoft | 🏆 Onde está investimento existente | Alvo de migração futura |
A Microsoft tem declarado que GitHub Actions é a plataforma estratégica e Azure DevOps está em modo de manutenção (sem novas features grandes). Para projetos novos, GH Actions com Azure OIDC é o caminho. Para times já investidos em ADO, continua valendo a pena — migração é trabalho de meses.
Decisões
📋 Empresa Microsoft-shop com AD, AKS, App Service e 40 pipelines classic
Classic → YAML é conversão direta com Export to YAML. Introduzir WIF zera secrets. Templates centralizam padrão. Ganho sem mudar de plataforma.
Alt: Migrar pra GH Actions —
📋 Startup usando Azure mas começando do zero
Ir direto pro futuro. GH Actions + OIDC para AKS/ACR é setup de 1 dia, e você não cria dívida de Azure Boards que ninguém mais usa.
Alt: Azure DevOps completo —
📋 Pipeline com aprovação rigorosa (Sarbanes-Oxley), múltiplos gates
Checks como Business Hours, Invoke REST, Exclusive Lock, e Required Approvers com grupos AAD atendem compliance sem script caseiro.
Alt: GH Actions + action custom —
Perguntas típicas
❓ Classic pipeline ou YAML?
❓ Posso disparar um pipeline a partir de outro?
❓ Como compartilhar artefato entre stages?
❓ Dá para rodar pipeline só em PR que muda uma pasta?
❓ Como fazer IaC (Terraform/Bicep) no Azure Pipelines?
Take-aways. (1) Azure DevOps = 5 produtos; Pipelines é o CI/CD. (2) YAML versionado > classic clicado. (3) Stages representam fases de entrega com environments, approvals e histórico — diferencial forte vs GH Actions. (4) Workload Identity Federation zera secrets estáticos — migre hoje. (5) Variable Group + Key Vault centraliza config e secret com rotação automática. (6) Templates + repos externos = reuso DRY com pin de versão. (7) Microsoft sinalizou GH Actions como futuro estratégico — planeje migração em horizonte de 1-3 anos se for greenfield, mantenha e modernize se for legado.
Discussão
Carregando…