Pular para o conteúdo principal

Como Fazer Auditoria de Uso dos Projetos no Azure DevOps com PowerShell

Se você gerencia várias organizações e projetos no Azure DevOps, pode ser um desafio saber quais deles estão realmente ativos — seja com Boards (work items), commits em repositórios ou pipelines de CI/CD em uso.

Neste artigo, vou mostrar um método simples usando PowerShell para gerar um relatório CSV que ajuda a entender o uso real dos seus projetos, sem alterar nada no ambiente — só leitura.

O que vamos auditar?

  • Projetos existentes em várias organizações (organizações = assinaturas dentro do Azure DevOps).

  • Se os projetos têm Boards (work items) ativos.

  • Data do último commit nos repositórios.

  • Se existem pipelines com execuções recentes.

  • Uma aproximação da data de criação do projeto, baseada no primeiro commit encontrado.

Pré-requisitos

  • Permissão de acesso às organizações do Azure DevOps.

  • Um Personal Access Token (PAT) com permissões de leitura para:

    • Projetos e times

    • Work Items

    • Repositórios Git

    • Pipelines

  • PowerShell 7+ instalado na sua máquina.

Como gerar seu PAT

  1. Entre no Azure DevOps.

  2. Clique no seu avatar > Security > Personal Access Tokens > New Token.

  3. Configure com as permissões de leitura e validade desejadas.

  4. Copie o token gerado — ele aparecerá só uma vez.

Script PowerShell para auditoria

O script abaixo conecta nas suas organizações, lista os projetos e coleta as informações de uso, exportando para um arquivo CSV:


# Configurar suas organizações aqui

$organizations = @("sua-org-1", "sua-org-2")


# Solicita o PAT para autenticação segura

$pat = Read-Host -Prompt "Informe seu PAT do Azure DevOps" -AsSecureString

$ptraw = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($pat))

$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$ptraw"))

$headers = @{Authorization = "Basic $base64AuthInfo"}


$resultados = @()


foreach ($org in $organizations) {

    Write-Host "Organização: $org"

    $projectsUrl = "https://dev.azure.com/$org/_apis/projects?api-version=7.0"

    $projects = (Invoke-RestMethod -Uri $projectsUrl -Headers $headers).value


    foreach ($project in $projects) {

        $projectName = $project.name

        Write-Host "Projeto: $projectName"


        # Verifica uso de Boards

        $wiql = @{ query = "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = '$projectName'" } | ConvertTo-Json -Depth 3

        $workItemsUrl = "https://dev.azure.com/$org/$projectName/_apis/wit/wiql?api-version=7.0"

        $workItems = Invoke-RestMethod -Method Post -Uri $workItemsUrl -Headers $headers -Body $wiql -ContentType "application/json"

        $temBoards = if ($workItems.workItems.Count -gt 0) { "Sim" } else { "Não" }


        # Último commit (mais recente) em todos os repositórios do projeto

        $reposUrl = "https://dev.azure.com/$org/$projectName/_apis/git/repositories?api-version=7.0"

        $repos = (Invoke-RestMethod -Uri $reposUrl -Headers $headers).value

        $ultimoCommit = $null

        $primeiroCommit = $null


        foreach ($repo in $repos) {

            # Commit mais recente

            $commitsRecentesUrl = "https://dev.azure.com/$org/$projectName/_apis/git/repositories/$($repo.id)/commits?`$top=1"

            $commitsRecentes = (Invoke-RestMethod -Uri $commitsRecentesUrl -Headers $headers).value

            if ($commitsRecentes.Count -gt 0) {

                $dataRecente = $commitsRecentes[0].committer.date

                if (-not $ultimoCommit -or [datetime]$dataRecente -gt [datetime]$ultimoCommit) {

                    $ultimoCommit = $dataRecente

                }

            }


            # Commit mais antigo (para data aproximada da criação)

            $commitsAntigosUrl = "https://dev.azure.com/$org/$projectName/_apis/git/repositories/$($repo.id)/commits?`$top=1&`$orderby=authorDate asc"

            $commitsAntigos = (Invoke-RestMethod -Uri $commitsAntigosUrl -Headers $headers).value

            if ($commitsAntigos.Count -gt 0) {

                $dataAntiga = $commitsAntigos[0].committer.date

                if (-not $primeiroCommit -or [datetime]$dataAntiga -lt [datetime]$primeiroCommit) {

                    $primeiroCommit = $dataAntiga

                }

            }

        }


        if (-not $ultimoCommit) { $ultimoCommit = "Sem commit" }

        if (-not $primeiroCommit) { $primeiroCommit = "Indisponível" }


        # Verifica uso de pipelines

        $pipelinesUrl = "https://dev.azure.com/$org/$projectName/_apis/pipelines?api-version=7.0"

        $pipelines = (Invoke-RestMethod -Uri $pipelinesUrl -Headers $headers).value

        $usouPipeline = "Não"

        foreach ($pipeline in $pipelines) {

            $runsUrl = "https://dev.azure.com/$org/$projectName/_apis/pipelines/$($pipeline.id)/runs?`$top=1"

            $runs = (Invoke-RestMethod -Uri $runsUrl -Headers $headers).value

            if ($runs.Count -gt 0) {

                $usouPipeline = "Sim"

                break

            }

        }


        # Monta o objeto resultado

        $resultados += [PSCustomObject]@{

            Organizacao     = $org

            Projeto         = $projectName

            CriadoEm        = $primeiroCommit

            BoardsUsado     = $temBoards

            UltimoCommit    = $ultimoCommit

            PipelinesUsado  = $usouPipeline

        }

    }

}


# Exporta para CSV

$csvPath = ".\\auditoria-devops-multiorgs.csv"

$resultados | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8


Write-Host "Auditoria concluída! Arquivo salvo em $csvPath"

=====================================================================

Como usar

  1. Copie o script acima para um arquivo .ps1 (exemplo: auditoria-devops.ps1).

  2. Altere a variável $organizations para as suas organizações do Azure DevOps.

  3. Abra o PowerShell e execute o script.

  4. Informe seu PAT quando solicitado.

  5. Aguarde o término da execução e confira o arquivo .csv gerado.

Resultado esperado

O arquivo CSV terá as seguintes informações:


Por que vale a pena usar esse script de auditoria no Azure DevOps?

  1. Você ganha visibilidade real do ambiente
    Saber exatamente quais projetos estão ativos, abandonados ou apenas parcialmente utilizados evita desperdício de recursos e ajuda na organização.

  2. Ajuda no planejamento de governança
    Você pode identificar padrões: quais equipes usam mais Boards? Quais usam só o repositório? Isso ajuda a definir diretrizes e boas práticas internas.

  3. É ideal para ambientes com muitas organizações e projetos
    Em vez de fazer tudo manualmente, o script percorre todas as organizações de forma automatizada.

  4. Garante segurança e tranquilidade
    Como o script é somente leitura, ele não modifica nada — ou seja, você pode rodar sem medo em produção.

  5. Facilita relatórios e auditorias oficiais
    O CSV gerado pode ser facilmente enviado para times de governança, compliance, segurança ou gestão.

  6. É base para automações futuras
    Você pode adaptar esse script para criar painéis no Power BI, gerar alertas, ou até sugerir arquivamento automático no futuro (com cuidado, claro).

Se você trabalha com DevOps em uma organização que tem múltiplos times, projetos legados e ambientes complexos, esse script é uma mão na roda para iniciar uma auditoria eficiente, sem riscos e com dados valiosos.



Comentários

Postagens mais visitadas deste blog

Otimize Custos e Segurança: Auditoria Fácil de Usuários e Licenças no Azure DevOps

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...

Auditoria de Usuários Ativos no Azure AD com Último Login e Tipo de Conta

Durante a gestão de ambientes em nuvem, é comum nos depararmos com contas de usuários que permanecem ativas, mesmo sem uso recente. Isso impacta diretamente no consumo de licenças e na postura de segurança da organização. Pensando nisso, desenvolvi um script em PowerShell utilizando o Microsoft Graph para facilitar auditorias de contas ativas no Azure AD , trazendo informações importantes como: Nome do usuário E-mail (UPN) Departamento e cargo Data de criação da conta Último login registrado Tipo de conta (nuvem ou sincronizada com AD local) O que o script faz? - Conecta-se ao Microsoft Graph com as permissões necessárias - Busca todos os usuários internos ativos (tipo "Member") - Verifica a última data de login registrada - Informa se a conta é nuvem (Cloud Only) ou sincronizada (On-Premises) - Exporta os dados para um relatório CSV ================================================================ # Script para auditoria de usuários ativos no Azure...

Auditoria no Azure AD: o risco oculto das caixas compartilhadas

Durante o planejamento de uma auditoria de contas no Azure AD, me deparei com um desafio importante: ao listar usuários Cloud-Only, percebi que caixas compartilhadas do Exchange Online também são exibidas como contas de usuário comuns no diretório . O problema: Shared Mailboxes que "parecem" usuários Quando criamos uma caixa compartilhada (Shared Mailbox) no Microsoft 365, o sistema cria automaticamente uma conta de usuário associada, com UPN e tudo. Mesmo após a conversão da caixa para o tipo "compartilhada", essa conta permanece visível no Azure AD e continua se comportando como uma conta Cloud-Only. Ou seja, se você estiver rodando um script ou fazendo uma auditoria manual para identificar contas inativas ou que devem ser excluídas, corre o risco de excluir acidentalmente uma caixa de e-mail ativa e compartilhada com outros times . A solução: auditoria com checagem de tipo de caixa Para evitar esse tipo de erro, desenvolvi um script em PowerShell que: Identi...