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
    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 = "Output\Groups",
        [string]$Encoding = "UTF8",
        [ValidateSet('None', 'Minimal', 'Standard')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)
    $requiredScopes = @("Group.Read.All", "AuditLog.Read.All")
    $graphAuth = Get-GraphAuthType -RequiredScopes $requiredScopes
    Write-LogFile -Message "=== Starting Groups Collection ===" -Color "Cyan" -Level Minimal

    if (!(Test-Path $OutputDir)) {
        New-Item -ItemType Directory -Force -Path $OutputDir > $null
    } 
    else {
        if (!(Test-Path -Path $OutputDir)) {
            Write-LogFile -Message "[Error] Custom directory invalid: $OutputDir exiting script" -Level Minimal
        }
    }

    try {
        Write-LogFile -Message "[INFO] Fetching all groups..." -Level Standard
        $allGroups = Get-MgGroup -All
        Write-LogFile -Message "[INFO] Found $($allGroups.Count) groups" -Level Standard -Color "Green"

        $results = $allGroups | ForEach-Object {
            [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 ';'
            }
        }

        $date = Get-Date -Format "yyyyMMddHHmm"
        $filePath = Join-Path $OutputDir "$($date)-Groups.csv"
        $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding

        Write-LogFile -Message "`nGroup Analysis Results:" -Color "Cyan" -Level Standard
        Write-LogFile -Message "Total Groups: $($results.Count)" -Level Standard
        Write-LogFile -Message " - Security Enabled: $(($results | Where-Object { $_.SecurityEnabled -eq $true }).Count)" -Level Standard
        Write-LogFile -Message " - Mail Enabled: $(($results | Where-Object { $_.MailEnabled -eq $true }).Count)" -Level Standard
        Write-LogFile -Message " - On-Premises Synced: $(($results | Where-Object { $_.OnPremisesSyncEnabled -eq $true }).Count)" -Level Standard

        Write-LogFile -Message "`nExported File:" -Color "Cyan" -Level Standard
        Write-LogFile -Message " - File: $filePath" -Level Standard

    }
    catch {
        Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" -Level Minimal
        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 = "Output\Groups",
        [string]$Encoding = "UTF8",
        [ValidateSet('None', 'Minimal', 'Standard')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)
    $requiredScopes = @("Group.Read.All", "Directory.Read.All")
    $graphAuth = Get-GraphAuthType -RequiredScopes $RequiredScopes

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

    if (!(Test-Path $OutputDir)) {
        New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
    }

    try {
        Write-LogFile -Message "[INFO] Fetching all groups..." -Level Standard
        $allGroups = Get-MgGroup -All
        Write-LogFile -Message "[INFO] Found $($allGroups.Count) groups" -Level Standard -Color "Green"

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

            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
            }
        }
        $date = Get-Date -Format "yyyyMMddHHmm"
        $filePath = Join-Path $OutputDir "$($date)-GroupMembers.csv"
        $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding

        Write-LogFile -Message "`nExported File:" -Color "Cyan" -Level Standard
        Write-LogFile -Message " - File: $filePath" -Level Standard
    }
    catch {
        Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" -Level Minimal
        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 = "Output\Groups",
        [string]$Encoding = "UTF8",
        [ValidateSet('None', 'Minimal', 'Standard')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)
    $requiredScopes = @("Group.Read.All", "Directory.Read.All")
    $graphAuth = Get-GraphAuthType -RequiredScopes $RequiredScopes
        
    Write-LogFile -Message "=== Starting Dynamic Groups Collection ===" -Color "Cyan" -Level Minimal

    if (!(Test-Path $OutputDir)) {
        New-Item -ItemType Directory -Force -Path $OutputDir > $null
    } 
    else {
        if (!(Test-Path -Path $OutputDir)) {
            Write-LogFile -Message "[Error] Custom directory invalid: $OutputDir exiting script" -Level Minimal
        }
    }  

    try {
        Write-LogFile -Message "[INFO] Fetching all groups from Microsoft Graph..." -Level Standard
        $allGroups = Get-MgGroup -All
        Write-LogFile -Message "[INFO] Found $($allGroups.Count) total groups" -Level Standard

        $dynamicGroups = $allGroups | Where-Object { $_.MembershipRule -ne $null }
        Write-LogFile -Message "[INFO] Found $($dynamicGroups.Count) dynamic groups" -Level Standard

        $results = $dynamicGroups | ForEach-Object {
            [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
            }
        }

        $date = Get-Date -Format "yyyyMMddHHmm"
        $filePath = Join-Path $OutputDir "$($date)-DynamicGroups.csv"
        $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding

        Write-LogFile -Message "`nDynamic Group Analysis Results:" -Color "Cyan" -Level Standard
        Write-LogFile -Message "Total Dynamic Groups: $($results.Count)" -Level Standard
        Write-LogFile -Message " - Security Enabled: $(($results | Where-Object { $_.SecurityEnabled -eq $true }).Count)" -Level Standard
        Write-LogFile -Message " - Mail Enabled: $(($results | Where-Object { $_.MailEnabled -eq $true }).Count)" -Level Standard
        
        $processingStates = $results | Group-Object -Property MembershipRuleProcessingState
        Write-LogFile -Message "`nMembership Rule Processing States:" -Color "Cyan" -Level Standard
        foreach ($state in $processingStates) {
            Write-LogFile -Message " - $($state.Name): $($state.Count)" -Level Standard
        }

        Write-LogFile -Message "`nExported File:" -Color "Cyan" -Level Standard
        Write-LogFile -Message " - File: $filePath" -Level Standard
    }
    catch {
        Write-LogFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" -Level Minimal
        throw
    }
}