Pular para o conteúdo principal

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:

  • Identifica apenas contas Cloud-Only no Azure AD;

  • Coleta dados como nome, UPN, cargo, departamento e último login;

  • Verifica no Exchange Online se a conta é uma Shared Mailbox;

  • Exporta todas essas informações para um arquivo .CSV completo.

Com isso, conseguimos diferenciar claramente:

  • Contas de usuário reais e inativas, que podem ser removidas com segurança;

  • Contas associadas a caixas compartilhadas, que devem ser mantidas.

O Script

Aqui está o script criado para essa auditoria (com uso dos módulos Microsoft.Graph.Users e ExchangeOnlineManagement):

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

#Requires -Modules Microsoft.Graph.Users, ExchangeOnlineManagement


<#

.SYNOPSIS

    Audits Azure AD users, focusing on Cloud-Only accounts, retrieves specific properties including last sign-in time,

    identifies shared mailboxes via Exchange Online, and exports the results to a CSV file.


.DESCRIPTION

    This script connects to Microsoft Graph and Exchange Online to gather user information.

    It filters for users created directly in Azure AD (Cloud-Only) by checking the 'onPremisesSyncEnabled' property.

    For each Cloud-Only user, it retrieves DisplayName, UserPrincipalName, Department, JobTitle, and LastSignInDateTime.

    It then checks Exchange Online to determine if the UserPrincipalName corresponds to a Shared Mailbox.

    Finally, it compiles this information and exports it to a CSV file named 'AzureAD_Cloud_User_Audit.csv' in the script's execution directory.


.NOTES

    Author: Lucas Silva

    Date: 2025-05-27


    Prerequisites:

    - PowerShell 5.1 or later.

    - Microsoft Graph PowerShell SDK (Modules: Microsoft.Graph.Users).

      Install using: Install-Module Microsoft.Graph.Users -Scope CurrentUser

    - Exchange Online Management module (V3 or later recommended).

      Install using: Install-Module ExchangeOnlineManagement -Scope CurrentUser


    Permissions Required:

    - Microsoft Graph: User.Read.All (at least) for reading user properties and sign-in activity.

    - Exchange Online: View-Only Recipients or higher role for Get-EXORecipient.


    Execution:

    1. Save the script as a .ps1 file (e.g., Audit-CloudUsers.ps1).

    2. Open PowerShell as an administrator (recommended for first-time module installation if needed).

    3. Navigate to the directory where you saved the script.

    4. Run the script: .\Audit-CloudUsers.ps1

    5. You will be prompted to authenticate to Microsoft Graph and Exchange Online.


.EXAMPLE

    .\Audit-CloudUsers.ps1

    Runs the script, prompts for authentication, performs the audit, and creates 'AzureAD_Cloud_User_Audit.csv'.

#>


param (

    [Parameter(Mandatory=$false)]

    [string]$OutputPath = ".\AzureAD_Cloud_User_Audit.csv"

)


# --- Script Start --- 


Write-Host "Iniciando auditoria de usuários Cloud-Only no Azure AD..." -ForegroundColor Cyan


#region Connect to Services


Write-Host "Verificando e conectando aos serviços necessários (Microsoft Graph e Exchange Online)..." -ForegroundColor Yellow


# Check and Import Modules

try {

    Import-Module Microsoft.Graph.Users -ErrorAction Stop

    Import-Module ExchangeOnlineManagement -ErrorAction Stop

    Write-Host "Módulos necessários carregados com sucesso." -ForegroundColor Green

} catch {

    Write-Error "Erro ao carregar módulos necessários (Microsoft.Graph.Users, ExchangeOnlineManagement). Certifique-se de que estão instalados (Install-Module Microsoft.Graph.Users; Install-Module ExchangeOnlineManagement). Detalhes: $($_.Exception.Message)"

    # Exit if modules can't be loaded

    return 

}


# Connect to Microsoft Graph

try {

    # Check existing connection

    $graphConnection = Get-MgContext -ErrorAction SilentlyContinue

    if (-not $graphConnection) {

        Write-Host "Conectando ao Microsoft Graph..." 

        # Define required scopes

        $scopes = @("User.Read.All", "AuditLog.Read.All") # AuditLog.Read.All needed for signInActivity

        Connect-MgGraph -Scopes $scopes

        Write-Host "Conectado ao Microsoft Graph com sucesso." -ForegroundColor Green

    } else {

        Write-Host "Já conectado ao Microsoft Graph." -ForegroundColor Green

    }

} catch {

    Write-Error "Falha ao conectar ao Microsoft Graph. Verifique permissões e conectividade. Detalhes: $($_.Exception.Message)"

    return

}


# Connect to Exchange Online

try {

    # Check existing connection

    $exoConnection = Get-ConnectionInformation -ErrorAction SilentlyContinue

    if (-not ($exoConnection | Where-Object { $_.AppName -eq 'ExchangeOnline' -and $_.ConnectionState -eq 'Connected' })) {

        Write-Host "Conectando ao Exchange Online..."

        Connect-ExchangeOnline -ShowBanner:$false

        Write-Host "Conectado ao Exchange Online com sucesso." -ForegroundColor Green

    } else {

        Write-Host "Já conectado ao Exchange Online." -ForegroundColor Green

    }

} catch {

    Write-Error "Falha ao conectar ao Exchange Online. Verifique permissões e conectividade. Detalhes: $($_.Exception.Message)"

    return

}


#endregion


#region Fetch and Process Users


Write-Host "Buscando usuários Cloud-Only no Azure AD..." -ForegroundColor Yellow


$allUsers = @()

try {

    # Get users - Filter for members, exclude guests, and check onPremisesSyncEnabled

    # Select necessary properties including signInActivity for LastSignInDateTime

    # Note: Filtering directly on onPremisesSyncEnabled can be inconsistent. We filter later.

    # Note: signInActivity requires Azure AD Premium P1 or P2 license.

    $users = Get-MgUser -All -Property Id, DisplayName, UserPrincipalName, Department, JobTitle, UserType, onPremisesSyncEnabled, signInActivity 

        -Filter "UserType eq 'Member'"


    Write-Host "Encontrados $($users.Count) usuários do tipo 'Member'. Filtrando por Cloud-Only e processando..."


    $ProgressPreference = 'SilentlyContinue' # Suppress progress bar for Get-EXORecipient inside loop

    $i = 0

    foreach ($user in $users) {

        $i++

        Write-Progress -Activity "Processando Usuários" -Status "Verificando Usuário $i de $($users.Count): $($user.UserPrincipalName)" -PercentComplete (($i / $users.Count) * 100)


        # Filter for Cloud-Only: onPremisesSyncEnabled is null or explicitly false

        if ($null -eq $user.onPremisesSyncEnabled -or $user.onPremisesSyncEnabled -eq $false) {

            

            # Check if it's a Shared Mailbox in Exchange Online

            $isSharedMailbox = "Não"

            try {

                $recipient = Get-EXORecipient -Identity $user.UserPrincipalName -ErrorAction SilentlyContinue

                if ($recipient -ne $null -and $recipient.RecipientTypeDetails -eq "SharedMailbox") {

                    $isSharedMailbox = "Sim"

                }

            } catch {

                # Handle potential errors if user not found in EXO or other issues

                Write-Warning "Não foi possível verificar o status da caixa de correio para $($user.UserPrincipalName) no Exchange Online. Detalhes: $($_.Exception.Message)"

            }


            # Get Last Sign-In DateTime (Handle potential null value)

            $lastSignIn = if ($user.SignInActivity -ne $null) { $user.SignInActivity.LastSignInDateTime } else { $null }


            # Create custom object with desired properties

            $userData = [PSCustomObject]@{ 

                Nome                = $user.DisplayName

                EnderecoDeEmail     = $user.UserPrincipalName

                Departamento        = $user.Department

                Cargo               = $user.JobTitle

                UltimoLogin         = $lastSignIn

                EhCaixaCompartilhada = $isSharedMailbox

            }

            $allUsers += $userData

        }

    }

    $ProgressPreference = 'Continue' # Restore progress bar preference


} catch {

    Write-Error "Erro ao buscar ou processar usuários do Azure AD. Detalhes: $($_.Exception.Message)"

    # Attempt to disconnect before exiting

    Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue

    Disconnect-MgGraph -ErrorAction SilentlyContinue

    return

}


Write-Host "Processamento de usuários concluído. Encontrados $($allUsers.Count) usuários Cloud-Only." -ForegroundColor Green


#endregion


#region Export to CSV


if ($allUsers.Count -gt 0) {

    Write-Host "Exportando resultados para CSV: $OutputPath" -ForegroundColor Yellow

    try {

        $allUsers | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8 -Delimiter ';'

        Write-Host "Exportação para CSV concluída com sucesso: $OutputPath" -ForegroundColor Green

    } catch {

        Write-Error "Falha ao exportar dados para CSV. Verifique o caminho e permissões. Detalhes: $($_.Exception.Message)"

    }

} else {

    Write-Host "Nenhum usuário Cloud-Only encontrado para exportar." -ForegroundColor Yellow

}


#endregion


#region Disconnect Services


Write-Host "Desconectando dos serviços..." -ForegroundColor Yellow

try {

    Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue

    # Disconnect-MgGraph # Optional: Keep Graph connection if running multiple scripts

    Write-Host "Desconexão concluída (Exchange Online)." -ForegroundColor Green

} catch {

    Write-Warning "Ocorreu um problema ao desconectar dos serviços. Detalhes: $($_.Exception.Message)"

}


#endregion


Write-Host "Auditoria concluída." -ForegroundColor Cyan


# --- Script End ---

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

Resultado

Ao final da execução, obtemos um relatório claro e seguro com os seguintes campos:


Esse nível de detalhe garantiu uma auditoria segura, sem riscos de exclusão de caixas essenciais para a operação da empresa.

Conclusão

Automatizar a auditoria de contas no Azure AD é fundamental — mas é preciso fazer isso com inteligência. O uso combinado de Microsoft Graph e Exchange Online PowerShell permite análises mais seguras, eficazes e alinhadas com a realidade do ambiente Microsoft 365.

Fica aqui o alerta: caixas compartilhadas criadas a partir de contas Cloud-Only não devem ser tratadas como contas inativas comuns. Identifique, classifique e só então decida se deve excluir ou manter.


Comentários

Postagens mais visitadas deste blog

Microsoft Authenticator agora suporta Passkeys: Como ativar e testar

 A autenticação de dois fatores (MFA) sempre foi uma das melhores práticas para proteger contas online. Agora, a Microsoft deu um passo além ao adicionar suporte nativo para Passkeys no Microsoft Authenticator . Essa atualização, lançada em janeiro de 2025, permite que os usuários façam login sem precisar de senhas tradicionais, utilizando métodos resistentes a phishing e mais convenientes, como biometria ou PIN. Neste artigo, vamos explorar o que são Passkeys, os benefícios desse novo recurso e como ativá-lo e testá-lo no Microsoft Authenticator . O que são Passkeys? As Passkeys são um método moderno de autenticação baseado no padrão FIDO2/WebAuthn , que permite que os usuários façam login de maneira segura sem precisar inserir senhas. Em vez disso, eles usam biometria (impressão digital ou reconhecimento facial), um PIN ou outro fator local para validar a identidade. Principais benefícios das Passkeys - Resistência a phishing: Como não há senhas para serem roubadas, ataques d...

Mudança no Processo de Exclusão de Usuário no Microsoft 365

 A Microsoft fez uma atualização importante no processo de exclusão de usuários no Microsoft 365 . Agora, ao excluir um usuário, os delegados (pessoas que têm acesso à caixa de correio e aos arquivos) terão apenas 30 dias para acessar o OneDrive do usuário excluído. O que muda? Prazo reduzido : O tempo de acesso aos arquivos foi limitado a 30 dias após a exclusão do usuário, o que significa que os dados ficam acessíveis por um período mais curto. Impacto nos Delegados : Antes, os delegados podiam acessar os dados por mais tempo. Agora, a Microsoft reduziu esse prazo para evitar o armazenamento prolongado de dados de usuários excluídos. Ação Rápida Necessária : Caso seja necessário manter os arquivos por mais tempo, a organização deve tomar providências para transferir ou arquivar esses dados antes que o acesso expire. Por que isso é importante? Essa mudança reflete um esforço da Microsoft para alinhar a gestão de dados com práticas de segurança mais rígidas, incentivando as empre...

Microsoft está removendo as permissões "Todos, exceto usuários externos" no OneDrive

 Já encontrou arquivos no OneDrive acessíveis para toda a sua organização, mesmo sem ter compartilhado intencionalmente? Isso acontece por conta da permissão "Todos, exceto usuários externos" (EEEU) , que permite o acesso interno sem restrições. Mas isso está prestes a mudar. - A partir de 10 de abril de 2025 , a Microsoft removerá a permissão EEEU dos sites raiz do OneDrive e das bibliotecas de documentos padrão, reduzindo o risco de compartilhamento acidental de dados. - Se seus aplicativos, processos ou usuários dependem dessa permissão, eles perderão o acesso assim que a alteração for aplicada. - No entanto, permissões diretas de arquivos e pastas não serão afetadas – quem já tem acesso explícito continuará com permissão. Muitas organizações deixam conteúdos abertos para todos os usuários internos sem perceber. Apesar de parecer inofensivo, isso aumenta riscos de segurança e pode levar à exposição involuntária de dados. O que você deve fazer? ✅ Revisar as permissões ...