Scripts/Get-Groups.ps1

Function Get-Groups {
<#
    .SYNOPSIS
    Retrieves all groups in the organization.
 
    .DESCRIPTION
    Retrieves all groups, including details such as group ID and display name.
 
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\Groups
 
    .PARAMETER Encoding
    Encoding is the parameter specifying the encoding of the CSV output file.
    Default: UTF8
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Debug: Verbose logging for debugging purposes
    Default: Standard
     
    .EXAMPLE
    Get-Groups
    Retrieves all groups and exports the output to a CSV file.
     
    .EXAMPLE
    Get-Groups -Encoding utf32
    Retrieves all groups and exports the output to a CSV file with UTF-32 encoding.
         
    .EXAMPLE
    Get-Groups -OutputDir C:\Windows\Temp
    Retrieves all groups and saves the output to the C:\Windows\Temp folder.
#>
    

    [CmdletBinding()]
    param(
        [string]$OutputDir,
        [string]$Encoding = "UTF8",
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Init-Logging
    Init-OutputDir -Component "Groups" -FilePostfix "Groups" -CustomOutputDir $OutputDir

    $requiredScopes = @("Group.Read.All", "AuditLog.Read.All")
    $graphAuth = Get-GraphAuthType -RequiredScopes $requiredScopes

    Write-LogFile -Message "=== Starting Groups Collection ===" -Color "Cyan" -Level Standard

    try {
        Write-LogFile -Message "[INFO] Fetching all groups..." -Level Standard

        if ($isDebugEnabled) {
            $performance = Measure-Command {
                $allGroups = Get-MgGroup -All
            }
            Write-LogFile -Message "[DEBUG] Groups retrieval completed in $([math]::round($performance.TotalSeconds, 2)) seconds" -Level Debug
        } else {
            $allGroups = Get-MgGroup -All
        }

        Write-LogFile -Message "[INFO] Found $($allGroups.Count) groups" -Level Standard -Color "Green"

        $results = $allGroups | ForEach-Object {
            if ($isDebugEnabled) {
                Write-LogFile -Message "[DEBUG] Processing group: $($_.DisplayName)" -Level Debug
                if ($_.MembershipRule) {
                    Write-LogFile -Message "[DEBUG] Rule length: $($_.MembershipRule.Length) characters" -Level Debug
                    Write-LogFile -Message "[DEBUG] Processing state: $($_.MembershipRuleProcessingState)" -Level Debug
                }
                Write-LogFile -Message "[DEBUG] Security enabled: $($_.SecurityEnabled)" -Level Debug
                Write-LogFile -Message "[DEBUG] Mail enabled: $($_.MailEnabled)" -Level Debug
            }

            [PSCustomObject]@{
                GroupId = $_.Id
                DisplayName = $_.DisplayName
                Description = $_.Description
                Mail = $_.Mail
                MailEnabled = $_.MailEnabled
                MailNickname = $_.MailNickname
                SecurityEnabled = $_.SecurityEnabled
                GroupTypes = $_.GroupTypes -join ','
                CreatedDateTime = $_.CreatedDateTime
                RenewedDateTime = $_.RenewedDateTime
                ExpirationDateTime = $_.ExpirationDateTime
                Visibility = $_.Visibility
                OnPremisesSyncEnabled = $_.OnPremisesSyncEnabled
                OnPremisesLastSyncDateTime = $_.OnPremisesLastSyncDateTime
                SecurityIdentifier = $_.SecurityIdentifier
                IsManagementRestricted = $_.IsManagementRestricted
                MembershipRule = $_.MembershipRule
                MembershipRuleProcessingState = $_.MembershipRuleProcessingState
                Classification = $_.Classification
                HideFromAddressLists = $_.HideFromAddressLists
                HideFromOutlookClients = $_.HideFromOutlookClients
                IsAssignableToRole = $_.IsAssignableToRole
                PreferredDataLocation = $_.PreferredDataLocation
                ProxyAddresses = $_.ProxyAddresses -join ';'
            }
        }

        $results | Export-Csv -Path $script:outputFile -NoTypeInformation -Encoding $Encoding
        $summaryData = [ordered]@{
            "Group Summary" = [ordered]@{
                "Total Groups" = $results.Count
                "Security Enabled" = ($results | Where-Object { $_.SecurityEnabled -eq $true }).Count
                "Mail Enabled" = ($results | Where-Object { $_.MailEnabled -eq $true }).Count
                "On-Premises Synced" = ($results | Where-Object { $_.OnPremisesSyncEnabled -eq $true }).Count
            }
        }
        Write-Summary -Summary $summaryData -Title "Group Analysis Summary"
    }
    catch {
        Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" -Level Minimal
        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Error details:" -Level Debug
            Write-LogFile -Message "[DEBUG] Exception type: $($_.Exception.GetType().Name)" -Level Debug
            Write-LogFile -Message "[DEBUG] Full error: $($_.Exception.ToString())" -Level Debug
            Write-LogFile -Message "[DEBUG] Stack trace: $($_.ScriptStackTrace)" -Level Debug
        }
        throw
    }
}

Function Get-GroupMembers {
<#
    .SYNOPSIS
    Retrieves all members of each group and their relevant details.
 
    .DESCRIPTION
    Enumerates all members of every group in the organization, including when they were added, their permissions, and roles.
 
    .PARAMETER OutputDir
    The output directory for saving group member details.
    Default: Output\Groups
 
    .PARAMETER Encoding
    The encoding for CSV files.
    Default: UTF8
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Default: Standard
 
    .EXAMPLE
    Get-GroupMembers
    Retrieves all group members and their details.
 
    .EXAMPLE
    Get-GroupMembers -OutputDir C:\Temp -Encoding utf32
    Retrieves all group members and saves details to C:\Temp with UTF-32 encoding.
#>


    [CmdletBinding()]
    param(
        [string]$OutputDir,
        [string]$Encoding = "UTF8",
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Init-Logging
    Init-OutputDir -Component "Groups" -FilePostfix "GroupMembers" -CustomOutputDir $OutputDir

    $requiredScopes = @("Group.Read.All", "Directory.Read.All")
    $graphAuth = Get-GraphAuthType -RequiredScopes $RequiredScopes

    Write-LogFile -Message "=== Starting Group Members Collection ===" -Color "Cyan" -Level Standard

    try {
        Write-LogFile -Message "[INFO] Fetching all groups..." -Level Standard
        if ($isDebugEnabled) {
            $groupsPerformance = Measure-Command {
                $allGroups = Get-MgGroup -All
            }
            Write-LogFile -Message "[DEBUG] Groups retrieval completed in $([math]::round($groupsPerformance.TotalSeconds, 2)) seconds" -Level Debug
        } else {
            $allGroups = Get-MgGroup -All
        }
        Write-LogFile -Message "[INFO] Found $($allGroups.Count) groups" -Level Standard -Color "Green"

        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Starting member enumeration for $($allGroups.Count) groups..." -Level Debug
        }

        $results = @()
        foreach ($group in $allGroups) {
            Write-LogFile -Message "[INFO] Processing group: $($group.DisplayName)" -Level Standard

            if ($isDebugEnabled) {
                Write-LogFile -Message "[DEBUG] Processing group details:" -Level Debug
                Write-LogFile -Message "[DEBUG] Group ID: $($group.Id)" -Level Debug
                Write-LogFile -Message "[DEBUG] Display Name: $($group.DisplayName)" -Level Debug
                Write-LogFile -Message "[DEBUG] Group Types: $($group.GroupTypes -join ', ')" -Level Debug
                Write-LogFile -Message "[DEBUG] Mail Enabled: $($group.MailEnabled)" -Level Debug
                Write-LogFile -Message "[DEBUG] Security Enabled: $($group.SecurityEnabled)" -Level Debug
            }

            try {
                $members = Get-MgGroupMember -GroupId $group.Id -All | ForEach-Object {
                    [PSCustomObject]@{
                        GroupName = $group.DisplayName
                        GroupId = $group.Id
                        MemberId = $_.Id
                        DisplayName = $_.AdditionalProperties.displayName
                        Email = $_.AdditionalProperties.mail
                        UserPrincipalName = $_.AdditionalProperties.userPrincipalName
                        GroupCreated = $_.CreatedDateTime
                    }
                }

                $results += $members
            }
            catch {
                Write-LogFile -Message "[ERROR] Failed to retrieve members for group: $($group.DisplayName) Error: $($_.Exception.Message)" -Color "Red" -Level Minimal
            }
        }

        $results | Export-Csv -Path $script:outputFile -NoTypeInformation -Encoding $Encoding
        $summaryData = [ordered]@{
            "Collection Results" = [ordered]@{
                "Total Groups Processed" = $allGroups.Count
                "Total Members Found" = $results.Count
            }
        }

        Write-Summary -Summary $summaryData -Title "Group Members Summary"
    }
    catch {
        Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" -Level Minimal
        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Error details:" -Level Debug
            Write-LogFile -Message "[DEBUG] Exception type: $($_.Exception.GetType().Name)" -Level Debug
            Write-LogFile -Message "[DEBUG] Full error: $($_.Exception.ToString())" -Level Debug
            Write-LogFile -Message "[DEBUG] Stack trace: $($_.ScriptStackTrace)" -Level Debug
        }
        throw
    }
}
Function Get-DynamicGroups {
<#
    .SYNOPSIS
    Retrieves all dynamic groups and their membership rules.
 
    .DESCRIPTION
    Retrieves dynamic groups and includes details about their membership rules, which determine automatic user inclusion.
 
    .PARAMETER OutputDir
    The output directory for saving dynamic group details.
    Default: Output\Groups
 
    .PARAMETER Encoding
    The encoding for CSV files.
    Default: UTF8
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Default: Standard
 
    .EXAMPLE
    Get-DynamicGroups
    Retrieves dynamic groups and their membership rules, outputting the details to a CSV file.
 
    .EXAMPLE
    Get-DynamicGroups -OutputDir C:\Temp -Encoding utf32
    Retrieves dynamic groups and saves details to C:\Temp with UTF-32 encoding.
#>


    [CmdletBinding()]
    param(
        [string]$OutputDir,
        [string]$Encoding = "UTF8",
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Init-Logging
    Init-OutputDir -Component "Groups" -FilePostfix "DynamicGroups" -CustomOutputDir $OutputDir

    $requiredScopes = @("Group.Read.All", "Directory.Read.All")
    $graphAuth = Get-GraphAuthType -RequiredScopes $RequiredScopes

    Write-LogFile -Message "=== Starting Dynamic Groups Collection ===" -Color "Cyan" -Level Standard
    try {
        Write-LogFile -Message "[INFO] Fetching all groups from Microsoft Graph..." -Level Standard

        if ($isDebugEnabled) {
            $groupsPerformance = Measure-Command {
                $allGroups = Get-MgGroup -All
            }
            Write-LogFile -Message "[DEBUG] Groups retrieval completed in $([math]::round($groupsPerformance.TotalSeconds, 2)) seconds" -Level Debug
        } else {
            $allGroups = Get-MgGroup -All
        }

        Write-LogFile -Message "[INFO] Found $($allGroups.Count) total groups" -Level Standard

        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Analyzing groups for dynamic membership rules..." -Level Debug
            $filterPerformance = Measure-Command {
                $dynamicGroups = $allGroups | Where-Object { $_.MembershipRule -ne $null }
            }
            Write-LogFile -Message "[DEBUG] Dynamic groups filtering completed in $([math]::round($filterPerformance.TotalSeconds, 2)) seconds" -Level Debug
        } else {
            $dynamicGroups = $allGroups | Where-Object { $_.MembershipRule -ne $null }
        }

        Write-LogFile -Message "[INFO] Found $($dynamicGroups.Count) dynamic groups" -Level Standard

        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Dynamic groups breakdown:" -Level Debug
            Write-LogFile -Message "[DEBUG] Dynamic groups: $($dynamicGroups.Count)" -Level Debug
            Write-LogFile -Message "[DEBUG] Static groups: $($allGroups.Count - $dynamicGroups.Count)" -Level Debug
            Write-LogFile -Message "[DEBUG] Dynamic percentage: $([math]::Round(($dynamicGroups.Count / [math]::Max($allGroups.Count, 1)) * 100, 2))%" -Level Debug
        }

        $results = $dynamicGroups | ForEach-Object {
            if ($isDebugEnabled) {
                Write-LogFile -Message "[DEBUG] Processing dynamic group: $($_.DisplayName)" -Level Debug
                Write-LogFile -Message "[DEBUG] Rule length: $($_.MembershipRule.Length) characters" -Level Debug
                Write-LogFile -Message "[DEBUG] Processing state: $($_.MembershipRuleProcessingState)" -Level Debug
                Write-LogFile -Message "[DEBUG] Security enabled: $($_.SecurityEnabled)" -Level Debug
                Write-LogFile -Message "[DEBUG] Mail enabled: $($_.MailEnabled)" -Level Debug
            }
            
            [PSCustomObject]@{
                GroupId = $_.Id
                DisplayName = $_.DisplayName
                Description = $_.Description
                Mail = $_.Mail
                MailEnabled = $_.MailEnabled
                MailNickname = $_.MailNickname
                SecurityEnabled = $_.SecurityEnabled
                GroupTypes = $_.GroupTypes -join ','
                CreatedDateTime = $_.CreatedDateTime
                RenewedDateTime = $_.RenewedDateTime
                MembershipRule = $_.MembershipRule
                MembershipRuleProcessingState = $_.MembershipRuleProcessingState
                OnPremisesSyncEnabled = $_.OnPremisesSyncEnabled
                SecurityIdentifier = $_.SecurityIdentifier
                Classification = $_.Classification
                Visibility = $_.Visibility
            }
        }

        $results | Export-Csv -Path $script:outputFile -NoTypeInformation -Encoding $Encoding
        $totalCount = if ($results) { @($results).Count } else { 0 }
        $securityEnabledCount = @($results | Where-Object { $_.SecurityEnabled -eq $true }).Count
        $mailEnabledCount = @($results | Where-Object { $_.MailEnabled -eq $true }).Count

        $processingStates = $results | Group-Object -Property MembershipRuleProcessingState
        $statesHashtable = [ordered]@{}
        foreach ($state in $processingStates) {
            $statesHashtable[$state.Name] = $state.Count
        }

        $summaryData = [ordered]@{
            "Dynamic Group Summary" = [ordered]@{
                "Total Dynamic Groups" = $totalCount
                "Security Enabled" = $securityEnabledCount
                "Mail Enabled" = $mailEnabledCount
            }
            "Membership Rule Processing States" = $statesHashtable
        }

        Write-Summary -Summary $summaryData -Title "Dynamic Group Analysis Summary"
    }
    catch {
        Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" -Level Minimal
        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Error details:" -Level Debug
            Write-LogFile -Message "[DEBUG] Exception type: $($_.Exception.GetType().Name)" -Level Debug
            Write-LogFile -Message "[DEBUG] Full error: $($_.Exception.ToString())" -Level Debug
            Write-LogFile -Message "[DEBUG] Stack trace: $($_.ScriptStackTrace)" -Level Debug
        }
        throw
    }
}