Scripts/Get-UsersInfo.ps1

function Get-Users {
<#
    .SYNOPSIS
    Retrieves the creation time and date of the last password change for all users.
    Script inspired by: https://github.com/tomwechsler/Microsoft_Graph/blob/main/Entra_ID/Create_time_last_password.ps1
 
    .DESCRIPTION
    Retrieves the creation time and date of the last password change for all users.
 
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\Users
 
    .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-Users
    Retrieves the creation time and date of the last password change for all users.
 
    .EXAMPLE
    Get-Users -Encoding utf32
    Retrieves the creation time and date of the last password change for all users and exports the output to a CSV file with UTF-32 encoding.
         
    .EXAMPLE
    Get-Users -OutputDir C:\Windows\Temp
    Retrieves the creation time and date of the last password change for all users and saves the output to the C:\Windows\Temp folder.
#>

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

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


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

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

    try {
        $selectobjects = "UserPrincipalName","DisplayName","Id","CompanyName","Department","JobTitle","City","Country","Identities","UserType","LastPasswordChangeDateTime","AccountEnabled","CreatedDateTime","CreationType","ExternalUserState","ExternalUserStateChangeDateTime","SignInActivity","OnPremisesSyncEnabled"
        $mgUsers = Get-MgUser -All -Select $selectobjects 

        $formattedUsers = $mgUsers | ForEach-Object {
            [PSCustomObject]@{
                UserPrincipalName = $_.UserPrincipalName
                DisplayName = $_.DisplayName
                Id = $_.Id
                Department = $_.Department
                JobTitle = $_.JobTitle
                AccountEnabled = $_.AccountEnabled
                CreatedDateTime = $_.CreatedDateTime
                LastPasswordChangeDateTime = $_.LastPasswordChangeDateTime
                UserType = $_.UserType
                OnPremisesSyncEnabled = $_.OnPremisesSyncEnabled
                Mail = $_.Mail
                LastSignInDateTime = $_.SignInActivity.LastSignInDateTime
                LastNonInteractiveSignInDateTime = $_.SignInActivity.LastNonInteractiveSignInDateTime
                IdentityProvider = ($_.Identities | Where-Object { $_.SignInType -eq "federated" }).Issuer
                City = $_.City
                Country = $_.Country
                UsageLocation = $_.UsageLocation
            }
        }

        $date = (Get-Date).AddDays(-7)
        $oneweekold = $mgUsers | Where-Object {
            $_.CreatedDateTime -gt $date
        }

        $date = (Get-Date).AddDays(-30)
        $onemonthold = $mgUsers | Where-Object {
            $_.CreatedDateTime -gt $date
        }

        $date = (Get-Date).AddDays(-90)
        $threemonthold = $mgUsers | Where-Object {
            $_.CreatedDateTime -gt $date
        }

        $date = (Get-Date).AddDays(-180)
        $sixmonthold = $mgUsers | Where-Object {
            $_.CreatedDateTime -gt $date
        }

        $date = (Get-Date).AddDays(-360)
        $OneYear = $mgUsers | Where-Object {
            $_.CreatedDateTime -gt $date
        }

        Get-MgUser | Get-Member > $null
        $date = Get-Date -Format "yyyyMMddHHmm"
        $filePath = "$OutputDir\$($date)-Users.csv"
        $formattedUsers  | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding

        $userSummary = [PSCustomObject]@{
            TotalUsers = $mgUsers.Count
            EnabledUsers = ($mgUsers | Where-Object { $_.AccountEnabled }).Count
            DisabledUsers = ($mgUsers | Where-Object { -not $_.AccountEnabled }).Count
            SyncedUsers = ($mgUsers | Where-Object { $_.OnPremisesSyncEnabled }).Count
            GuestUsers = ($mgUsers | Where-Object { $_.UserType -eq "Guest" }).Count
            LastSevenDays = $oneweekold.Count
            LastThirtyDays = $onemonthold.Count
            LastNinetyDays = $threemonthold.Count
            Onehundredeighty = $sixmonthold.Count
            OneYear = $OneYear.Count
        }

        Write-LogFile -Message "User Analysis Results:" -Color "Cyan" -Level Standard
        Write-LogFile -Message "Total Users: $($userSummary.TotalUsers)" -Level Standard
        Write-LogFile -Message " - Enabled: $($userSummary.EnabledUsers)" -Level Standard
        Write-LogFile -Message " - Disabled: $($userSummary.DisabledUsers)" -Level Standard
        Write-LogFile -Message " - Synced from On-Premises: $($userSummary.SyncedUsers)" -Level Standard
        Write-LogFile -Message " - Guest Users: $($userSummary.GuestUsers)" -Level Standard

        Write-LogFile -Message "`nRecent Account Creation:" -Color "Cyan" -Level Standard
        Write-LogFile -Message " - Last 7 days: $($userSummary.LastSevenDays)" -Level Standard
        Write-LogFile -Message " - Last 30 days: $($userSummary.LastThirtyDays)" -Level Standard
        Write-LogFile -Message " - Last 90 days: $($userSummary.LastNinetyDays)" -Level Standard
        Write-LogFile -Message " - Last 6 months: $($userSummary.Onehundredeighty)" -Level Standard
        Write-LogFile -Message " - Last 1 year $($userSummary.OneYear)" -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-AdminUsers {
<#
    .SYNOPSIS
    Retrieves all Administrator directory roles.
 
    .DESCRIPTION
    Retrieves Administrator directory roles, including the identification of users associated with each specific role.
 
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\Admins
 
    .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-AdminUsers
    Retrieves Administrator directory roles, including the identification of users associated with each specific role.
     
    .EXAMPLE
    Get-AdminUsers -Encoding utf32
    Retrieves Administrator directory roles, including the identification of users associated with each specific role and exports the output to a CSV file with UTF-32 encoding.
         
    .EXAMPLE
    Get-AdminUsers -OutputDir C:\Windows\Temp
    Retrieves Administrator directory roles, including the identification of users associated with each specific role and saves the output to the C:\Windows\Temp folder.
#>
    

    [CmdletBinding()]
    param(
        [string]$outputDir = "Output\Admins",
        [string]$Encoding = "UTF8",
        [ValidateSet('None', 'Minimal', 'Standard')]
        [string]$LogLevel = 'Standard'
    )

    Set-LogLevel -Level ([LogLevel]::$LogLevel)
    $date = Get-Date -Format "yyyyMMddHHmm"

    Write-LogFile -Message "=== Starting Admin Users Collection ===" -Color "Cyan" -Level Minimal

    $requiredScopes = @("User.Read.All", "Directory.Read.All")
    $graphAuth = Get-GraphAuthType -RequiredScopes $RequiredScopes
        
    if (!(Test-Path $OutputDir)) {
        New-Item -ItemType Directory -Force -Path $OutputDir > $null
    } 
    else {
        if (!(Test-Path -Path $OutputDir)) {
            Write-Error "[Error] Custom directory invalid: $OutputDir exiting script" -ErrorAction Stop
            Write-LogFile -Message "[Error] Custom directory invalid: $OutputDir exiting script" -Level Minimal
        }
    }    

    Write-LogFile -Message "[INFO] Analyzing administrator roles..." -Level Standard

    $rolesWithUsers = @()
    $rolesWithoutUsers = @()
    $exportedFiles = @()
    $totalAdminCount = 0

    try {
        $getRoles = Get-MgDirectoryRole -all
        foreach ($role in $getRoles) {
            $roleId = $role.Id
            $roleName = $role.DisplayName

            if ($roleName -like "*Admin*") {
                $areThereUsers = Get-MgDirectoryRoleMember -DirectoryRoleId $roleId

                if ($null -eq $areThereUsers) {
                    $rolesWithoutUsers += $roleName
                    continue
                }

                $results = @()
                $count = 0
                foreach ($user in $areThereUsers) {
                    $userid = $user.Id
                    if ($userid -eq ".") {
                        continue
                    }

                    $count++
                    try {
                        try {
                            $getUserName = Get-MgUser -Filter ("Id eq '$userid'") -ErrorAction Stop
                        } catch {
                            if ($_.Exception.Response.StatusCode -eq 429) {
                                Start-Sleep -Seconds 5
                                $getUserName = Get-MgUser -Filter ("Id eq '$userid'") -ErrorAction Stop
                            } else {
                                throw
                            }
                        }
                    
                        $userName = $getUserName.UserPrincipalName
                        $results += [PSCustomObject]@{
                            UserName = $userName
                            UserId = $userid
                            Role = $roleName
                            DisplayName = $getUserName.DisplayName
                            Department = $getUserName.Department
                            JobTitle = $getUserName.JobTitle
                            AccountEnabled = $getUserName.AccountEnabled
                            CreatedDateTime = $getUserName.CreatedDateTime
                        }
                    }
                    catch {}
                }

                if ($results.Count -gt 0) {
                    $totalAdminCount += $results.Count
                    $rolesWithUsers += "$roleName ($($results.Count) users)"
                    
                    $filePath = "$OutputDir\$($date)-$($roleName.Replace(' ','_')).csv"
                    $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding
                    $exportedFiles += $filePath
                }
                else {
                    $rolesWithoutUsers += $roleName
                }
            }
        }

        $outputDirMerged = "$OutputDir\Merged\"
        If (!(test-path $outputDirMerged)) {
            New-Item -ItemType Directory -Force -Path $outputDirMerged > $null
        }

        $mergedFile = "$outputDirMerged$($date)-All-Administrators.csv"
        Get-ChildItem $OutputDir -Filter "*Administrator.csv" | 
            Select-Object -ExpandProperty FullName | 
            Import-Csv | 
            Export-Csv $mergedFile -NoTypeInformation -Append

        Write-LogFile -Message "`nRoles with users:" -Color "Green" -Level Standard
        foreach ($role in $rolesWithUsers) {
            Write-LogFile -Message " + $role" -Level Standard
        }

        Write-LogFile -Message "`nEmpty roles:" -Color "Yellow" -Level Standard
        foreach ($role in $rolesWithoutUsers) {
            Write-LogFile -Message " - $role" -Level Standard
        }

        Write-LogFile -Message "`nSummary:" -Level Standard -Color "Cyan"
        Write-LogFile -Message " Total admin roles: $($rolesWithUsers.Count + $rolesWithoutUsers.Count)" -Level Standard
        Write-LogFile -Message " Roles with users: $($rolesWithUsers.Count)" -Level Standard
        Write-LogFile -Message " Empty roles: $($rolesWithoutUsers.Count)" -Level Standard
        Write-LogFile -Message " Total administrators: $totalAdminCount" -Level Standard

        Write-LogFile -Message "`nExported files:" -Level Standard -Color "Cyan"
        Write-LogFile -Message " Individual role files: $OutputDir" -Level Standard
        Write-LogFile -Message " Merged file: $mergedFile" -Level Standard
    }
    catch {
        Write-logFile -Message "[ERROR] An error occurred: $($_.Exception.Message)" -Color "Red" -Level Minimal
        throw
    }
}