Por que criei este script de auditoria para o Azure DevOps?
Em meu trabalho diário com equipes que utilizam o Azure DevOps, percebi que muitos gestores e administradores enfrentam um desafio comum: controlar quem realmente está usando o ambiente, qual tipo de licença está ativa e, principalmente, quando foi a última vez que cada usuário acessou a plataforma.
Em ambientes com múltiplas organizações e centenas de usuários, controlar quem realmente utiliza repositórios, pipelines e boards pode gerar uma economia significativa.
Tudo isso de forma segura, sem modificar nada no ambiente, garantindo que a auditoria seja apenas informativa.
Essa auditoria é essencial para manter a segurança, controlar custos e garantir conformidade.
Pensando nisso, criei um script de auditoria que:
O que o script verifica?
Para cada usuário com licença Basic, o script checa:
-
Commits em repositórios Git
-
Execução de builds no Azure Pipelines
-
Atividades nos Boards (work items modificados)
Se o usuário não tiver usado nenhum desses recursos nos últimos 30 dias, o script sugere o rebaixamento para a licença gratuita Stakeholder.
Pré-requisitos
-
Permissões adequadas para gerar Personal Access Token (PAT) com escopo de leitura em User Entitlements
-
PowerShell instalado na máquina
-
Módulo ImportExcel instalado (para exportação em Excel)
1. Gerar o Personal Access Token (PAT)
Para que o script funcione, você precisa de um Personal Access Token (PAT) com permissão de leitura para acessar as informações dos usuários da sua organização no Azure DevOps.Em “Personal Access Tokens”, clique em “+ New Token” (Novo Token).
Name: Auditoria DevOps
Organization: Selecione a organização desejada
Expiration: Escolha um prazo seguro (ex: 30 ou 90 dias)
Scopes (Escopos):
Marque apenas:
Read & execute
em "User Entitlements"(Dentro da opção "Organization")
Clique em “Create”.
⚠️ Copie o PAT imediatamente e armazene em local seguro. Você não poderá visualizá-lo novamente.
Recomendações de segurança:
-
Use tokens com o menor escopo necessário.
-
Nunca compartilhe seu PAT.
-
Se possível, crie um token apenas para auditorias, com expiração curta.
-
Você pode gerar um token para cada organização se não tiver acesso global com o mesmo PAT.
2. Script PowerShell para auditoria
⚠️ Importante: O script não altera nada no ambiente — ele apenas lê e gera um relatório informativo.
===========================================================
# CONFIGURAÇÕES
$organizations = @("contoso", "outraOrg") # substitua pelos nomes de suas organizações
$pat = "SEU_PAT_AQUI" # Token com acesso necessário
$authHeader = @{
Authorization = ("Basic {0}" -f [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$pat")))
}
# Define a partir de quando considerar atividade (últimos 30 dias)
$cutoff = (Get-Date).AddDays(-30)
function Invoke-AzDo {
param($Uri)
try {
return Invoke-RestMethod -Uri $Uri -Headers $authHeader -ErrorAction Stop
} catch {
return $null
}
}
$report = @()
foreach ($org in $organizations) {
Write-Host "n Checando organização: $org" -ForegroundColor Cyan
# Obtem usuários Basic
$uriUsers = "https://vsaex.dev.azure.com/$org/_apis/userentitlements?top=1000&api-version=7.1-preview.1"
$resUsers = Invoke-AzDo $uriUsers
if (-not $resUsers) { Write-Warning "Erro ao obter usuários de $org"; continue }
$basicUsers = $resUsers.value | Where-Object { $_.accessLevel.licenseDisplayName -eq "Basic" }
foreach ($u in $basicUsers) {
$email = $u.user.mailAddress
$name = $u.user.displayName
Write-Host "→ Usuário: $name"
$active = $false
# 1. Verifica commits
$uriRepos = "https://dev.azure.com/$org/_apis/projects?api-version=7.1"
$projects = Invoke-AzDo $uriRepos
foreach ($p in $projects.value) {
$uriGit = "https://dev.azure.com/$org/$($p.name)/_apis/git/repositories?api-version=7.1"
$repos = Invoke-AzDo $uriGit
foreach ($r in $repos.value) {
$uriCommits = "https://dev.azure.com/$org/$($p.name)/_apis/git/repositories/$($r.id)/commits?searchCriteria.author=$email&$top=1&api-version=7.1"
$comm = Invoke-AzDo $uriCommits
if ($comm.value -and [datetime]$comm.value[0].author.date -ge $cutoff) { $active = $true }
if ($active) { break }
}
if ($active) { break }
}
# 2. Verifica builds
if (-not $active) {
foreach ($p in $projects.value) {
$uriBuilds = "https://dev.azure.com/$org/$($p.name)/_apis/build/builds?requestedFor=$email&$top=1&api-version=7.1"
$b = Invoke-AzDo $uriBuilds
if ($b.value -and [datetime]$b.value[0].queueTime -ge $cutoff) {
$active = $true; break
}
}
}
# 3. Verifica boards (work items modifications)
if (-not $active) {
foreach ($p in $projects.value) {
$uriWork = "https://dev.azure.com/$org/$($p.name)/_apis/wit/activitylogs?user=$email&$top=1&startDateTime=$($cutoff.ToString("o"))&api-version=7.1"
$w = Invoke-AzDo $uriWork
if ($w.value) {
$active = $true; break
}
}
}
# Sugestão de ação
$action = if ($active) { "Manter Basic" } else { "Rebaixar para Stakeholder" }
$report += [PSCustomObject]@{
Organizacao = $org
Nome = $name
Email = $email
Licenca = "Basic"
AtivoUltimos30d = $active
AcaoSugerida = $action
}
}
}
# Exporta resultado
$out = Join-Path (Get-Location).Path "Auditoria_Basic_Atividade.csv"
$report | Sort-Object Organizacao, Nome | Export-Csv -Path $out -NoTypeInformation -Encoding UTF8
Write-Host "n Relatório gerado: $out" -ForegroundColor Green
Exemplo de Resultado
Abaixo, um exemplo real do relatório gerado pelo script:
Neste cenário, enquanto João segue ativo e justifica sua licença Basic, a usuária Ana não apresentou nenhuma atividade relevante nos últimos 30 dias — o que sugere a possibilidade de rebaixamento para Stakeholder, liberando a licença para outro colaborador ou reduzindo custos.
Por que isso importa?
Em grandes organizações, é comum que licenças fiquem esquecidas — principalmente quando há rotatividade, mudanças de time ou paralisação de projetos.
Licenças Basic são pagas. Já a Stakeholder é 100% gratuita. Ou seja, identificar esses usuários inativos permite economizar de forma inteligente, sem prejudicar a produtividade.
Ao repetir essa auditoria periodicamente, você terá controle total e proativo sobre o uso real das licenças no Azure DevOps.
Comentários
Postar um comentário