Public/Get-TntOrganizationReport.ps1

function Get-TntOrganizationReport {
    <#
    .SYNOPSIS
        Retrieves and displays information about the Microsoft 365 tenant organization.

    .DESCRIPTION
        This function connects to Microsoft Graph using an app registration and retrieves essential
        information about the tenant organization. It provides a clear overview of the organization's
        configuration, including company details, domains and directory statistics.

    .PARAMETER TenantId
        The Azure AD Tenant ID (GUID) to connect to.

    .PARAMETER ClientId
        The Application (Client) ID of the app registration created for security reporting.

    .PARAMETER ClientSecret
        The client secret for the app registration. Use this for automated scenarios.

    .PARAMETER CertificateThumbprint
        The thumbprint of the certificate to use for authentication instead of client secret.

    .PARAMETER IncludeDirectoryStats
        Switch to include detailed directory object statistics in the report.

    .EXAMPLE
        Get-TntOrganizationReport -TenantId $tenantId -ClientId $clientId -ClientSecret $secret

        Retrieves and displays basic tenant information.

    .EXAMPLE
        Get-TntOrganizationReport -TenantId $tenantId -ClientId $clientId -ClientSecret $secret -IncludeDirectoryStats

        Retrieves comprehensive tenant information including directory statistics.

    .INPUTS
        None. This function does not accept pipeline input.

    .OUTPUTS
        System.Management.Automation.PSCustomObject
        Returns a structured object containing:
        - Summary: Core tenant details (Name, ID, Location, Sync Status)
        - DirectoryStatistics: Counts of users, groups, devices (if requested)
        - AllDomains: List of verified and federated domains

    .NOTES
        Author: Tom de Leeuw
        Website: https://systom.dev
        Module: TenantReports

        Required Permissions:
        - Organization.Read.All (Application)
        - Directory.Read.All (Application)
        - Domain.Read.All (Application)
        - SubscribedSku.Read.All (Application)
        - User.Read.All (Application)

    .LINK
        https://systom.dev
    #>


    [CmdletBinding(DefaultParameterSetName = 'ClientSecret')]
    [OutputType([System.Management.Automation.PSCustomObject])]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ClientSecret')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Certificate')]
        [Parameter(ParameterSetName = 'Interactive')]
        [ValidateNotNullOrEmpty()]
        [Alias('Tenant')]
        [string]$TenantId,

        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'ClientSecret')]
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Certificate')]
        [Parameter(ParameterSetName = 'Interactive')]
        [ValidatePattern('^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$')]
        [Alias('ApplicationId')]
        [string]$ClientId,

        [Parameter(Mandatory = $true, ParameterSetName = 'ClientSecret', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('Secret', 'ApplicationSecret')]
        [SecureString]$ClientSecret,

        [Parameter(Mandatory = $true, ParameterSetName = 'Certificate', ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('Thumbprint')]
        [string]$CertificateThumbprint,

        # Use interactive authentication (no app registration required).
        [Parameter(Mandatory = $true, ParameterSetName = 'Interactive')]
        [switch]$Interactive
    )

    begin {
        Write-Information 'Starting tenant information retrieval...' -InformationAction Continue
    }

    process {
        try {
            $ConnectionParams = Get-ConnectionParameters -BoundParameters $PSBoundParameters
            $ConnectionInfo = Connect-TntGraphSession @ConnectionParams

            Write-Verbose 'Retrieving organization details...'
            $Organization = Get-MgOrganization -ErrorAction Stop

            # Handle multiple organizations (rare but possible)
            if ($Organization -is [array]) {
                $Organization = $Organization[0]
                Write-Warning "Multiple organizations found, using primary: $($Organization.DisplayName)"
            }

            # Retrieve verified domains
            Write-Verbose 'Retrieving domain information...'
            $Domains = Get-MgDomain -All -ErrorAction Stop
            $PrimaryDomain = $Domains | Where-Object { $_.IsDefault -eq $true }
            $InitialDomain = $Domains | Where-Object { $_.IsInitial -eq $true }
            $VerifiedDomains = $Domains | Where-Object { $_.IsVerified -eq $true }
            $FederatedDomains = $Domains | Where-Object { $_.AuthenticationType -eq 'Federated' }

            Write-Verbose 'Retrieving directory statistics...'
            try {
                # Get user count
                $UserCount = (Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/users?`$count=true&`$top=1" -Headers @{'ConsistencyLevel' = 'eventual' } -Method GET).'@odata.count'

                # Get group count
                $GroupCount = (Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/groups?`$count=true&`$top=1" -Headers @{'ConsistencyLevel' = 'eventual' } -Method GET).'@odata.count'

                # Get device count
                $DeviceCount = (Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/devices?`$count=true&`$top=1" -Headers @{'ConsistencyLevel' = 'eventual' } -Method GET).'@odata.count'

                # Get application count
                $AppCount = (Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/applications?`$count=true&`$top=1" -Headers @{'ConsistencyLevel' = 'eventual' } -Method GET).'@odata.count'

                $DirectoryStats = [PSCustomObject]@{
                    TotalUsers        = $UserCount
                    TotalGroups       = $GroupCount
                    TotalDevices      = $DeviceCount
                    TotalApplications = $AppCount
                    LastUpdated       = Get-Date
                }
            } catch {
                Write-Warning "Unable to retrieve complete directory statistics: $($_.Exception.Message)"
            }

            # Parse technical contact information
            $TechnicalContact = if ($Organization.TechnicalNotificationMails) {
                $Organization.TechnicalNotificationMails -join '; '
            } else {
                'Not specified'
            }

            # Parse privacy profile
            $PrivacyProfile = if ($Organization.PrivacyProfile) {
                [PSCustomObject]@{
                    ContactEmail = $Organization.PrivacyProfile.ContactEmail ?? 'Not specified'
                    StatementUrl = $Organization.PrivacyProfile.StatementUrl ?? 'Not specified'
                }
            } else {
                [PSCustomObject]@{
                    ContactEmail = 'Not specified'
                    StatementUrl = 'Not specified'
                }
            }

            # Build tenant information
            $TenantInfo = [PSCustomObject]@{
                OrganizationName           = $Organization.DisplayName
                TenantId                   = $Organization.Id
                CreatedDateTime            = $Organization.CreatedDateTime
                OrganizationType           = $Organization.OrganizationType -join ', '

                # Contact Information
                TechnicalNotificationEmail = $TechnicalContact
                MarketingNotificationEmail = if ($Organization.MarketingNotificationEmails) {
                    $Organization.MarketingNotificationEmails -join '; '
                } else { 'Not specified' }

                # Location Information
                Country                    = $Organization.Country ?? 'Not specified'
                CountryLetterCode          = $Organization.CountryLetterCode ?? 'Not specified'
                State                      = $Organization.State ?? 'Not specified'
                City                       = $Organization.City ?? 'Not specified'
                PostalCode                 = $Organization.PostalCode ?? 'Not specified'
                PreferredLanguage          = $Organization.PreferredLanguage ?? 'Not specified'

                # Domain Information
                PrimaryDomain              = $PrimaryDomain.Id
                InitialDomain              = $InitialDomain.Id
                TotalDomains               = $Domains.Count
                VerifiedDomains            = $VerifiedDomains.Count
                FederatedDomains           = $FederatedDomains.Count

                # Feature Settings
                OnPremisesSyncEnabled      = $Organization.OnPremisesSyncEnabled ?? $false
                OnPremisesLastSyncDateTime = $Organization.OnPremisesLastSyncDateTime
                DirSyncServiceAccount      = $Organization.DirSyncServiceAccount ?? 'Not configured'
                PasswordSyncEnabled        = $Organization.OnPremisesPasswordSyncEnabled ?? $false
                AssignedPlans              = $Organization.AssignedPlans.Count

                # Privacy Information
                PrivacyProfile             = $PrivacyProfile

                # Metadata
                ReportGeneratedDate        = Get-Date
                ReportGeneratedBy          = $ClientId
            }

            Write-Information "Tenant information retrieval completed - $($Organization.DisplayName)" -InformationAction Continue

            [PSCustomObject][Ordered] @{
                Summary             = $TenantInfo
                DirectoryStatistics = $DirectoryStats
                AllDomains          = ($Domains | Select-Object Id, IsDefault, IsInitial, IsVerified, AuthenticationType | Sort-Object Id)
            }
        } catch {
            $errorRecord = [System.Management.Automation.ErrorRecord]::new(
                [System.Exception]::new("Get-TntOrganizationReport failed: $($_.Exception.Message)", $_.Exception),
                'GetTntOrganizationReportError',
                [System.Management.Automation.ErrorCategory]::OperationStopped,
                $TenantId
            )
            $PSCmdlet.ThrowTerminatingError($errorRecord)
        } finally {
            if ($ConnectionInfo.ShouldDisconnect) {
                Disconnect-TntGraphSession -ConnectionState $ConnectionInfo
            }
        }
    }
}