EnhancedAO.Graph.SignInLogs.psm1
#Region '.\Public\Add-Result.ps1' -1 # Function to add results to the context function Add-Result { param ( [Parameter(Mandatory = $true)] $Context, [Parameter(Mandatory = $true)] $Item, [Parameter(Mandatory = $true)] [string] $DeviceId, [Parameter(Mandatory = $true)] [string] $DeviceState, [Parameter(Mandatory = $true)] [bool] $HasPremiumLicense, [Parameter(Mandatory = $false)] [string]$OSVersion ) try { $deviceName = $Item.DeviceDetail.DisplayName if ([string]::IsNullOrWhiteSpace($deviceName)) { $deviceName = "BYOD" } # Determine the compliance status $complianceStatus = if ($Item.DeviceDetail.IsCompliant) { "Compliant" } else { "Non-Compliant" } # Determine the user license $userLicense = if ($HasPremiumLicense) { "Microsoft 365 Business Premium" } else { "Other" } # Create a new Result object $splatNewResult = @{ DeviceName = $deviceName UserName = $Item.UserDisplayName DeviceEntraID = $DeviceId UserEntraID = $Item.UserId DeviceOS = $Item.DeviceDetail.OperatingSystem OSVersion = $osVersion DeviceComplianceStatus = $complianceStatus DeviceStateInIntune = $DeviceState TrustType = $Item.DeviceDetail.TrustType UserLicense = $userLicense } $result = New-Result @splatNewResult # Add the result to the context $Context.Results.Add($result) Write-EnhancedLog -Message "Successfully added result for device: $deviceName for user: $($Item.UserDisplayName)" -Level "INFO" } catch { Handle-Error -ErrorRecord $_ Write-EnhancedLog -Message "Failed to add result for device: $($Item.DeviceDetail.DisplayName)" -Level "ERROR" } } # # Example of how to use the functions # $context = New-ProcessingContext # $deviceDetail = New-DeviceDetail -DeviceId "device1" -DisplayName "Device One" -OperatingSystem "Windows 10" -IsCompliant $true -TrustType "AzureAD" # $item = New-SignInLog -UserDisplayName "John Doe" -UserId "user1" -DeviceDetail $deviceDetail # Add-Result -Context $context -Item $item -DeviceId "device1" -DeviceState "Active" -HasPremiumLicense $true #EndRegion '.\Public\Add-Result.ps1' 63 #Region '.\Public\Check-DeviceStateInIntune.ps1' -1 function Initialize-HttpClient { param ( [hashtable]$Headers ) $httpClient = [System.Net.Http.HttpClient]::new() $httpClient.DefaultRequestHeaders.Add("Authorization", $Headers["Authorization"]) return $httpClient } function Check-DeviceStateInIntune { param ( [Parameter(Mandatory = $true)] [string]$EntraDeviceId, [Parameter(Mandatory = $true)] [string]$Username, [Parameter(Mandatory = $true)] [hashtable]$Headers ) if ([string]::IsNullOrWhiteSpace($EntraDeviceId)) { return "Absent" } Write-EnhancedLog -Message "Checking device state in Intune for Entra Device ID: $EntraDeviceId for username: $Username" -ForegroundColor Cyan $graphApiUrl = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?`$filter=azureADDeviceId eq '$EntraDeviceId'" Write-EnhancedLog -Message "Constructed Graph API URL: $graphApiUrl" $httpClient = Initialize-HttpClient -Headers $Headers try { $response = $httpClient.GetStringAsync($graphApiUrl).Result if (-not [string]::IsNullOrEmpty($response)) { $responseJson = [System.Text.Json.JsonDocument]::Parse($response) $valueProperty = $responseJson.RootElement.GetProperty("value") if ($valueProperty.GetArrayLength() -gt 0) { Write-EnhancedLog -Message "Device is present in Intune." -ForegroundColor Green return "Present" } else { Write-EnhancedLog -Message "Device is absent in Intune." -ForegroundColor Yellow return "Absent" } } else { Write-EnhancedLog -Message "Received empty response from Intune API." -ForegroundColor Yellow return "NoData" } } catch { Handle-Error -ErrorRecord $_ return "Error" } finally { if ($null -ne $responseJson) { $responseJson.Dispose() } $httpClient.Dispose() } } # # Example usage # $entraDeviceId = "your_device_id" # $username = "your_username" # $headers = @{ "Authorization" = "Bearer your_token" } # $deviceState = Check-DeviceStateInIntune -EntraDeviceId $entraDeviceId -Username $username -Headers $headers # Write-Output "Device State: $deviceState" #EndRegion '.\Public\Check-DeviceStateInIntune.ps1' 68 #Region '.\Public\Export-SignInLogs.ps1' -1 function Export-SignInLogs { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$ScriptRoot, [Parameter(Mandatory = $true)] [string]$ExportsFolderName, [Parameter(Mandatory = $true)] [string]$ExportSubFolderName, [Parameter(Mandatory = $true)] [hashtable]$headers, [Parameter(Mandatory = $true)] [string]$url ) # Ensure the exports folder is clean before exporting $exportFolder = Ensure-ExportsFolder -BasePath $ScriptRoot -ExportsFolderName $ExportsFolderName -ExportSubFolderName $ExportSubFolderName # Get the sign-in logs (assuming you have a way to fetch these logs) # $signInLogs = Get-SignInLogs # Replace with the actual command to get sign-in logs $signInLogs = Get-SignInLogs -url $url -Headers $headers # Check if there are no sign-in logs if ($signInLogs.Count -eq 0) { Write-EnhancedLog -Message "NO sign-in logs found." -Level "WARNING" return } # Generate a timestamp for the export $timestamp = Get-Date -Format "yyyyMMddHHmmss" $baseOutputPath = Join-Path -Path $exportFolder -ChildPath "SignInLogs_$timestamp" # Setup parameters for Export-Data using splatting $exportParams = @{ Data = $signInLogs BaseOutputPath = $baseOutputPath # IncludeCSV = $true IncludeJSON = $true # IncludeXML = $true # IncludePlainText = $true # IncludeExcel = $true # IncludeYAML = $true } # Call the Export-Data function with splatted parameters Export-Data @exportParams Write-EnhancedLog -Message "Data export completed successfully." -Level "INFO" -ForegroundColor ([ConsoleColor]::Green) } # # Define the root path where the scripts and exports are located # $scriptRoot = "C:\MyScripts" # # Optionally, specify the names for the exports folder and subfolder # $exportsFolderName = "CustomExports" # $exportSubFolderName = "CustomSignInLogs" # # Call the function to export sign-in logs to XML (and other formats) # Export-SignInLogsToXML -ScriptRoot $scriptRoot -ExportsFolderName $exportsFolderName -ExportSubFolderName $exportSubFolderName #EndRegion '.\Public\Export-SignInLogs.ps1' 62 #Region '.\Public\ExportAndProcessSignInLogs.ps1' -1 function ExportAndProcessSignInLogs { param ( [Parameter(Mandatory = $true)] [string]$ScriptRoot, [Parameter(Mandatory = $true)] [string]$ExportsFolderName, [Parameter(Mandatory = $true)] [string]$ExportSubFolderName, [Parameter(Mandatory = $true)] [string]$url, [Parameter(Mandatory = $true)] [hashtable]$Headers ) try { $ExportSignInLogsparams = @{ ScriptRoot = $ScriptRoot ExportsFolderName = $ExportsFolderName ExportSubFolderName= $ExportSubFolderName url = $url Headers = $Headers } # Ask user if they want to export fresh sign-in logs $exportFreshLogs = Read-Host "Would you like to export fresh logs? (yes/no)" if ($exportFreshLogs -eq 'yes') { Export-SignInLogs @ExportSignInLogsparams } $subFolderPath = Join-Path -Path $ScriptRoot -ChildPath $ExportsFolderName $subFolderPath = Join-Path -Path $subFolderPath -ChildPath $ExportSubFolderName Write-EnhancedLog -Message "Looking for JSON files in $subFolderPath" -Level "DEBUG" $latestJsonFile = Find-LatestJsonFile -Directory $subFolderPath if ($latestJsonFile) { Write-EnhancedLog -Message "Latest JSON file found: $latestJsonFile" -Level "DEBUG" $signInLogs = Load-SignInLogs -JsonFilePath $latestJsonFile if ($signInLogs.Count -gt 0) { Write-EnhancedLog -Message "Sign-in logs found in $latestJsonFile. Starting to process it" -Level "INFO" # Process-AllDevices -Json $signInLogs -Headers $Headers return $signInLogs } else { Write-EnhancedLog -Message "No sign-in logs found in $latestJsonFile." -Level "WARNING" return @() } } else { Write-EnhancedLog -Message "No JSON file found to load sign-in logs." -Level "WARNING" return @() } } catch { Handle-Error -ErrorRecord $_ return @() } } #EndRegion '.\Public\ExportAndProcessSignInLogs.ps1' 58 #Region '.\Public\Fetch-DeviceStateWithRetry.ps1' -1 function Fetch-DeviceStateWithRetry { param ( [Parameter(Mandatory = $true)] [string]$DeviceId, [Parameter(Mandatory = $true)] [string]$Username, [Parameter(Mandatory = $true)] [hashtable]$Headers ) $retryCount = 0 $maxRetries = 3 $fetchSuccess = $false $deviceState = "Unknown" do { try { $deviceState = Check-DeviceStateInIntune -entraDeviceId $DeviceId -username $Username -Headers $Headers $fetchSuccess = $true } catch { Write-EnhancedLog -Message "Failed to check device state for device ID: $DeviceId. Attempt $($retryCount + 1) of $maxRetries" -Level "ERROR" $retryCount++ Start-Sleep -Seconds 2 } } while (-not $fetchSuccess -and $retryCount -lt $maxRetries) if (-not $fetchSuccess) { Write-EnhancedLog -Message "Failed to check device state for device ID: $DeviceId after $maxRetries attempts." -Level "ERROR" } return $deviceState } #EndRegion '.\Public\Fetch-DeviceStateWithRetry.ps1' 33 #Region '.\Public\Fetch-OSVersion.ps1' -1 function Fetch-OSVersion { param ( [Parameter(Mandatory = $true)] [string]$DeviceId, [Parameter(Mandatory = $true)] [hashtable]$Headers ) Begin { Write-EnhancedLog -Message "Starting Fetch-OSVersion function for Device ID: $DeviceId" -Level "INFO" Log-Params -Params @{ DeviceId = $DeviceId } } Process { $uri = "https://graph.microsoft.com/v1.0/devices?$filter=deviceId eq '$DeviceId'" $httpClient = Initialize-HttpClient -Headers $Headers try { Write-EnhancedLog -Message "Fetching OS version from URL: $uri" -Level "INFO" $response = $httpClient.GetStringAsync($uri).Result if (-not [string]::IsNullOrEmpty($response)) { $responseJson = [System.Text.Json.JsonDocument]::Parse($response) $deviceDetails = $responseJson.RootElement.GetProperty("value").EnumerateArray() | Where-Object { $_.GetProperty("deviceId").GetString() -eq $DeviceId } if ($deviceDetails) { $osVersion = $deviceDetails.GetProperty("operatingSystemVersion").GetString() Write-EnhancedLog -Message "OS Version for Device ID $DeviceId $osVersion" -Level "INFO" $responseJson.Dispose() return $osVersion } else { Write-EnhancedLog -Message "No matching device found for Device ID $DeviceId" -Level "WARNING" -ForegroundColor Yellow return $null } } else { Write-EnhancedLog -Message "Received empty response from OS version API for Device ID: $DeviceId." -Level "WARNING" -ForegroundColor Yellow return $null } } catch { Write-EnhancedLog -Message "An error occurred while fetching OS version: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor Red Handle-Error -ErrorRecord $_ return $null } finally { $httpClient.Dispose() } } End { Write-EnhancedLog -Message "Exiting Fetch-OSVersion function for Device ID: $DeviceId" -Level "INFO" } } #EndRegion '.\Public\Fetch-OSVersion.ps1' 52 #Region '.\Public\Fetch-OSVersionWithRetry.ps1' -1 function Fetch-OSVersionWithRetry { param ( [Parameter(Mandatory = $true)] [string]$DeviceId, [Parameter(Mandatory = $true)] [hashtable]$Headers ) $retryCount = 0 $maxRetries = 3 $fetchSuccess = $false $osVersion = "Unknown" do { try { $osVersion = Fetch-OSVersion -DeviceId $DeviceId -Headers $Headers $fetchSuccess = $true } catch { Write-EnhancedLog -Message "Failed to fetch OS version for device ID: $DeviceId. Attempt $($retryCount + 1) of $maxRetries" -Level "ERROR" $retryCount++ Start-Sleep -Seconds 2 } } while (-not $fetchSuccess -and $retryCount -lt $maxRetries) if (-not $fetchSuccess) { Write-EnhancedLog -Message "Failed to fetch OS version for device ID: $DeviceId after $maxRetries attempts." -Level "ERROR" } return $osVersion } #EndRegion '.\Public\Fetch-OSVersionWithRetry.ps1' 32 #Region '.\Public\Fetch-UserLicense.ps1' -1 function Fetch-UserLicense { param ( [Parameter(Mandatory = $true)] [string]$UserId, [Parameter(Mandatory = $true)] [string]$Username, [Parameter(Mandatory = $true)] [hashtable]$Headers ) try { Write-EnhancedLog -Message "Fetching licenses for user: $Username with ID: $UserId" -ForegroundColor Cyan $userLicenses = Get-UserLicenses -userId $UserId -username $Item.userDisplayName -Headers $Headers return $userLicenses } catch { Handle-Error -ErrorRecord $_ # return $null } } #EndRegion '.\Public\Fetch-UserLicense.ps1' 20 #Region '.\Public\Fetch-UserLicensesWithRetry.ps1' -1 function Fetch-UserLicensesWithRetry { param ( [Parameter(Mandatory = $true)] [string]$UserId, [Parameter(Mandatory = $true)] [string]$Username, [Parameter(Mandatory = $true)] [hashtable]$Headers ) $retryCount = 0 $maxRetries = 3 $fetchSuccess = $false $userLicenses = @() do { try { $userLicenses = Fetch-UserLicense -UserId $UserId -Username $Username -Headers $Headers $fetchSuccess = $true } catch { Write-EnhancedLog -Message "Failed to fetch licenses for user: $Username. Attempt $($retryCount + 1) of $maxRetries" -Level "ERROR" $retryCount++ Start-Sleep -Seconds 2 } } while (-not $fetchSuccess -and $retryCount -lt $maxRetries) if (-not $fetchSuccess) { Write-EnhancedLog -Message "Failed to fetch licenses for user: $Username after $maxRetries attempts." -Level "ERROR" } return $userLicenses } #EndRegion '.\Public\Fetch-UserLicensesWithRetry.ps1' 34 #Region '.\Public\Find-LatestJsonFile.ps1' -1 # Function to find the latest JSON file in the specified directory function Find-LatestJsonFile { param ( [Parameter(Mandatory = $true)] [string]$Directory ) $jsonFiles = Get-ChildItem -Path $Directory -Filter *.json | Sort-Object LastWriteTime -Descending if ($jsonFiles.Count -gt 0) { return $jsonFiles[0].FullName } else { Write-EnhancedLog -Message "No JSON files found in $Directory." -Level "ERROR" # return $null } } #EndRegion '.\Public\Find-LatestJsonFile.ps1' 19 #Region '.\Public\Generate-LicenseReports.ps1' -1 # Function to generate reports based on user licenses function Generate-LicenseReports { param ( [Parameter(Mandatory = $true)] [System.Collections.Generic.List[PSCustomObject]]$Results, [Parameter(Mandatory = $true)] [string]$PSScriptRoot, [Parameter(Mandatory = $true)] [string]$ExportsFolderName ) # Remove duplicates based on UserEntraID $uniqueResults = $Results | Sort-Object -Property UserEntraID -Unique # Generate reports for users with and without Business Premium licenses $premiumLicenses = $uniqueResults | Where-Object { $_.UserLicense -eq 'Microsoft 365 Business Premium' } $nonPremiumLicenses = $uniqueResults | Where-Object { $_.UserLicense -ne 'Microsoft 365 Business Premium' } $premiumLicenses | Export-Csv "$PSScriptRoot/$ExportsFolderName/Report_PremiumLicenses.csv" -NoTypeInformation $nonPremiumLicenses | Export-Csv "$PSScriptRoot/$ExportsFolderName/Report_NonPremiumLicenses.csv" -NoTypeInformation # Output totals to console Write-EnhancedLog -Message "Total users with Business Premium licenses: $($premiumLicenses.Count)" -Level "INFO" Write-EnhancedLog -Message "Total users without Business Premium licenses: $($nonPremiumLicenses.Count)" -Level "INFO" Write-EnhancedLog -Message "Generated reports for users with and without Business Premium licenses." -Level "INFO" } # # Example usage # $Json = @() # Your JSON data here # $Headers = @{} # Your actual headers # $PSScriptRoot = "C:\Path\To\ScriptRoot" # Update to your script root path # $ExportsFolderName = "CustomExports" # $results = Process-AllDevices -Json $Json -Headers $Headers # # Generate and export the reports # Generate-LicenseReports -Results $results -PSScriptRoot $PSScriptRoot -ExportsFolderName $ExportsFolderName #EndRegion '.\Public\Generate-LicenseReports.ps1' 39 #Region '.\Public\Generate-PII-RemovedReport.ps1' -1 # Function to generate a report for PII Removed cases function Generate-PII-RemovedReport { param ( [Parameter(Mandatory = $true)] [System.Collections.Generic.List[PSCustomObject]]$Results, [Parameter(Mandatory = $true)] [string]$PSScriptRoot, [Parameter(Mandatory = $true)] [string]$ExportsFolderName ) # Filter results for PII Removed (external) cases $piiRemovedResults = $Results | Where-Object { $_.DeviceStateInIntune -eq 'External' } # Export the results to a CSV file $piiRemovedResults | Export-Csv "$PSScriptRoot/$ExportsFolderName/Report_PIIRemoved.csv" -NoTypeInformation # Output totals to console Write-EnhancedLog -Message "Total users with PII Removed (external Azure AD/Entra ID tenants): $($piiRemovedResults.Count)" -Level "Warning" Write-EnhancedLog -Message "Generated report for users with PII Removed (external Azure AD/Entra ID tenants." -Level "INFO" } # # Example usage # $Json = @() # Your JSON data here # $Headers = @{} # Your actual headers # $PSScriptRoot = "C:\Path\To\ScriptRoot" # Update to your script root path # $ExportsFolderName = "CustomExports" # $results = Process-AllDevices -Json $Json -Headers $Headers # # Generate and export the PII Removed report # Generate-PII-RemovedReport -Results $results -PSScriptRoot $PSScriptRoot -ExportsFolderName $ExportsFolderName #EndRegion '.\Public\Generate-PII-RemovedReport.ps1' 33 #Region '.\Public\Get-UserLicenses.ps1' -1 function Initialize-HttpClient { param ( [hashtable]$Headers ) $httpClient = [System.Net.Http.HttpClient]::new() $httpClient.DefaultRequestHeaders.Add("Authorization", $Headers["Authorization"]) return $httpClient } function Get-UserLicenses { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [string]$UserId, [Parameter(Mandatory = $true)] [string]$Username, [Parameter(Mandatory = $true)] [hashtable]$Headers ) Begin { Write-EnhancedLog -Message "Starting Get-UserLicenses function" -Level "INFO" Log-Params -Params @{ UserId = $UserId; Username = $Username } } Process { $licenses = [System.Collections.Generic.List[string]]::new() $uri = "https://graph.microsoft.com/v1.0/users/$UserId/licenseDetails" $httpClient = Initialize-HttpClient -Headers $Headers try { Write-EnhancedLog -Message "Fetching licenses for user ID: $UserId with username: $Username" -Level "INFO" -ForegroundColor ([ConsoleColor]::Cyan) $response = $httpClient.GetStringAsync($uri).Result if (-not [string]::IsNullOrEmpty($response)) { $responseJson = [System.Text.Json.JsonDocument]::Parse($response) $valueProperty = $responseJson.RootElement.GetProperty("value") foreach ($license in $valueProperty.EnumerateArray()) { $skuId = $license.GetProperty("skuId").GetString() $licenses.Add($skuId) Write-EnhancedLog -Message "Found license for user: $Username with SKU ID: $skuId" -Level "INFO" -ForegroundColor ([ConsoleColor]::Green) } $responseJson.Dispose() } else { Write-EnhancedLog -Message "Received empty response from license API." -Level "WARNING" -ForegroundColor ([ConsoleColor]::Yellow) } } catch { Write-EnhancedLog -Message "An error occurred while fetching licenses: $($_.Exception.Message)" -Level "ERROR" -ForegroundColor ([ConsoleColor]::Red) Handle-Error -ErrorRecord $_ throw } finally { $httpClient.Dispose() } return $licenses } End { Write-EnhancedLog -Message "Exiting Get-UserLicenses function" -Level "INFO" } } # # Example usage # $userId = "your_user_id" # $username = "your_username" # $headers = @{ "Authorization" = "Bearer your_token" } # $licenses = Get-UserLicenses -UserId $userId -Username $username -Headers $headers # Write-Output "Licenses: $($licenses -join ', ')" #EndRegion '.\Public\Get-UserLicenses.ps1' 79 #Region '.\Public\Handle-ExternalAADTenant.ps1' -1 function Handle-ExternalAADTenant { param ( [Parameter(Mandatory = $true)] [PSCustomObject]$Item, [Parameter(Mandatory = $true)] [PSCustomObject]$Context, [string]$UniqueId ) if ([string]::Equals($Item.deviceDetail.deviceId, "{PII Removed}", [System.StringComparison]::OrdinalIgnoreCase)) { if ($Context.UniqueDeviceIds.Add($UniqueId)) { Write-EnhancedLog -Message "External Azure AD tenant detected for user: $($Item.userDisplayName)" -Level "INFO" Add-Result -Context $Context -Item $Item -DeviceId "N/A" -DeviceState "External" -HasPremiumLicense $false -OSVersion $null } return $true } return $false } #EndRegion '.\Public\Handle-ExternalAADTenant.ps1' 19 #Region '.\Public\Initialize-Context.ps1' -1 function Initialize-Context { param ( [Parameter(Mandatory = $true)] [PSCustomObject]$Context ) if (-not $Context.UniqueDeviceIds) { $Context.UniqueDeviceIds = [System.Collections.Generic.HashSet[string]]::new() } } #EndRegion '.\Public\Initialize-Context.ps1' 11 #Region '.\Public\Load-SignInLogs.ps1' -1 function Load-SignInLogs { param ( [Parameter(Mandatory = $true)] [string]$JsonFilePath ) $signInLogs = [System.Collections.Generic.List[PSCustomObject]]::new() Write-EnhancedLog -Message "Opening file: $JsonFilePath" -Level 'Debug' $fileStream = [System.IO.FileStream]::new($JsonFilePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read, 4096, [System.IO.FileOptions]::SequentialScan) try { $jsonDoc = [System.Text.Json.JsonDocument]::Parse($fileStream) foreach ($element in $jsonDoc.RootElement.EnumerateArray()) { $deviceDetail = [PSCustomObject]@{ DeviceId = $element.GetProperty("deviceDetail").GetProperty("deviceId").GetString() DisplayName = $element.GetProperty("deviceDetail").GetProperty("displayName").GetString() OperatingSystem = $element.GetProperty("deviceDetail").GetProperty("operatingSystem").GetString() IsCompliant = $element.GetProperty("deviceDetail").GetProperty("isCompliant").GetBoolean() TrustType = $element.GetProperty("deviceDetail").GetProperty("trustType").GetString() } $signInLog = [PSCustomObject]@{ UserDisplayName = $element.GetProperty("userDisplayName").GetString() UserId = $element.GetProperty("userId").GetString() DeviceDetail = $deviceDetail } $signInLogs.Add($signInLog) } Write-EnhancedLog -Message "Sign-in logs loaded successfully from $JsonFilePath." -Level "INFO" -ForegroundColor ([ConsoleColor]::Green) } catch { Handle-Error -ErrorRecord $_ } finally { $fileStream.Dispose() } return $signInLogs } # # Example usage # $jsonFilePath = "path_to_your_json_file.json" # $signInLogs = Load-SignInLogs -JsonFilePath $jsonFilePath # Write-Output "Sign-In Logs: $($signInLogs | Format-Table -AutoSize)" #EndRegion '.\Public\Load-SignInLogs.ps1' 47 #Region '.\Public\New-DeviceDetail.ps1' -1 # Function to create a DeviceDetail object function New-DeviceDetail { param ( [string] $DeviceId, [string] $DisplayName, [string] $OperatingSystem, [bool] $IsCompliant, [string] $TrustType ) [PSCustomObject]@{ DeviceId = $DeviceId DisplayName = $DisplayName OperatingSystem = $OperatingSystem IsCompliant = $IsCompliant TrustType = $TrustType } } #EndRegion '.\Public\New-DeviceDetail.ps1' 18 #Region '.\Public\New-DeviceItem.ps1' -1 # Function to create a DeviceItem object function New-DeviceItem { param ( [string] $DeviceId, [string] $UserId, [string] $UserDisplayName ) [PSCustomObject]@{ DeviceId = $DeviceId UserId = $UserId UserDisplayName = $UserDisplayName } } #EndRegion '.\Public\New-DeviceItem.ps1' 14 #Region '.\Public\New-ProcessingContext.ps1' -1 # Function to create a ProcessingContext object function New-ProcessingContext { [PSCustomObject]@{ UniqueDeviceIds = [System.Collections.Generic.HashSet[string]]::new() Results = [System.Collections.Generic.List[PSCustomObject]]::new() # ProcessedUserDeviceIds = [System.Collections.Generic.Dictionary[string, System.Collections.Generic.HashSet[string]]]::new() } } #EndRegion '.\Public\New-ProcessingContext.ps1' 11 #Region '.\Public\New-Result.ps1' -1 # Function to create a Result object function New-Result { param ( [string] $DeviceName, [string] $UserName, [string] $DeviceEntraID, [string] $UserEntraID, [string] $DeviceOS, [string] $DeviceComplianceStatus, [string] $DeviceStateInIntune, [string] $TrustType, [string] $UserLicense, [string] $OSVersion ) [PSCustomObject]@{ DeviceName = $DeviceName UserName = $UserName DeviceEntraID = $DeviceEntraID UserEntraID = $UserEntraID DeviceOS = $DeviceOS DeviceComplianceStatus = $DeviceComplianceStatus DeviceStateInIntune = $DeviceStateInIntune TrustType = $TrustType UserLicense = $UserLicense OSVersion = $OSVersion } } #EndRegion '.\Public\New-Result.ps1' 28 #Region '.\Public\New-SignInLog.ps1' -1 # Function to create a SignInLog object function New-SignInLog { param ( [string] $UserDisplayName, [string] $UserId, $DeviceDetail ) [PSCustomObject]@{ UserDisplayName = $UserDisplayName UserId = $UserId DeviceDetail = $DeviceDetail } } #EndRegion '.\Public\New-SignInLog.ps1' 14 #Region '.\Public\Process-DeviceItem-old.ps1' -1 # function Process-DeviceItem { # [CmdletBinding()] # param ( # [Parameter(Mandatory = $true)] # $Item, # [Parameter(Mandatory = $true)] # $Context, # [Parameter(Mandatory = $true)] # $Headers # ) # Begin { # Write-EnhancedLog -Message "Starting Process-DeviceItem function" -Level "INFO" # Log-Params -Params @{ Item = $Item; Context = $Context } # if (-not $Context.UniqueDeviceIds) { # $Context.UniqueDeviceIds = [System.Collections.Generic.HashSet[string]]::new() # } # } # Process { # # Ensure deviceDetail object and properties exist # if (-not $Item.deviceDetail) { # Write-EnhancedLog -Message "Missing deviceDetail for user: $($Item.userDisplayName)" -Level "WARNING" # return # } # $deviceId = $Item.deviceDetail.deviceId # $userId = $Item.userId # $os = $Item.deviceDetail.operatingSystem # if (-not $userId) { # Write-EnhancedLog -Message "Missing userId for device item" -Level "WARNING" # return # } # # Construct uniqueId based on availability of deviceId and OS for BYOD # $uniqueId = if ([string]::IsNullOrWhiteSpace($deviceId)) { # "$userId-$os".ToLowerInvariant() # } else { # $deviceId.ToLowerInvariant() # } # try { # # Log the device and user information # Write-EnhancedLog -Message "Processing device item for user: $($Item.userDisplayName) with unique ID: $uniqueId" -Level "INFO" # # Handle external Azure AD tenant case # if ([string]::Equals($deviceId, "{PII Removed}", [System.StringComparison]::OrdinalIgnoreCase)) { # if ($Context.UniqueDeviceIds.Add($uniqueId)) { # Write-EnhancedLog -Message "External Azure AD tenant detected for user: $($Item.userDisplayName)" -Level "INFO" # Add-Result -Context $Context -Item $Item -DeviceId "N/A" -DeviceState "External" -HasPremiumLicense $false -OSVersion $null # } # return # } # # Process only if the unique ID is not already processed # if ($Context.UniqueDeviceIds.Add($uniqueId)) { # # Handle BYOD case # if ([string]::IsNullOrWhiteSpace($deviceId)) { # # Check if there are already devices with the same userId and OS # # $existingBYODs = $Context.UniqueDeviceIds | Where-Object { $_ -like "$userId-*" } # # if ($existingBYODs.Count -gt 0) { # # Write-EnhancedLog -Message "User $($Item.userDisplayName) has multiple BYOD devices with the same OS: $os" -Level "WARNING" # # } # # Fetch user licenses with retry logic # $retryCount = 0 # $maxRetries = 3 # do { # try { # $userLicenses = Fetch-UserLicense -UserId $userId -Username $Item.userDisplayName -Headers $Headers # $fetchSuccess = $true # } catch { # Write-EnhancedLog -Message "Failed to fetch licenses for user: $($Item.userDisplayName). Attempt $($retryCount + 1) of $maxRetries" -Level "ERROR" # $fetchSuccess = $false # $retryCount++ # Start-Sleep -Seconds 2 # } # } while (-not $fetchSuccess -and $retryCount -lt $maxRetries) # if (-not $fetchSuccess) { # Write-EnhancedLog -Message "Failed to fetch licenses for user: $($Item.userDisplayName) after $maxRetries attempts." -Level "ERROR" # $userLicenses = @() # } # $hasPremiumLicense = $userLicenses -and $userLicenses.Count -gt 0 -and $userLicenses.Contains("cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46") # Write-EnhancedLog -Message "User $($Item.userDisplayName) has the following licenses: $($userLicenses -join ', ')" -Level "INFO" # Add-Result -Context $Context -Item $Item -DeviceId "N/A" -DeviceState "BYOD" -HasPremiumLicense $hasPremiumLicense -OSVersion $null # return # } # # Handle managed device case with retry logic # $retryCount = 0 # $maxRetries = 3 # $deviceState = "Unknown" # do { # try { # # Call the method to check device state # $deviceState = Check-DeviceStateInIntune -entraDeviceId $deviceId -username $Item.userDisplayName -Headers $Headers # $fetchSuccess = $true # } catch { # Write-EnhancedLog -Message "Failed to check device state for device ID: $deviceId. Attempt $($retryCount + 1) of $maxRetries" -Level "ERROR" # $fetchSuccess = $false # $retryCount++ # Start-Sleep -Seconds 2 # } # } while (-not $fetchSuccess -and $retryCount -lt $maxRetries) # if (-not $fetchSuccess) { # Write-EnhancedLog -Message "Failed to check device state for device ID: $deviceId after $maxRetries attempts." -Level "ERROR" # } # $retryCount = 0 # $osVersion = "Unknown" # do { # try { # # Fetch OS version # $osVersion = Fetch-OSVersion -DeviceId $deviceId -Headers $Headers # $fetchSuccess = $true # } catch { # Write-EnhancedLog -Message "Failed to fetch OS version for device ID: $deviceId. Attempt $($retryCount + 1) of $maxRetries" -Level "ERROR" # $fetchSuccess = $false # $retryCount++ # Start-Sleep -Seconds 2 # } # } while (-not $fetchSuccess -and $retryCount -lt $maxRetries) # if (-not $fetchSuccess) { # Write-EnhancedLog -Message "Failed to fetch OS version for device ID: $deviceId after $maxRetries attempts." -Level "ERROR" # } # $retryCount = 0 # $userLicenses = @() # do { # try { # # Fetch user licenses # $userLicenses = Fetch-UserLicense -UserId $userId -Username $Item.userDisplayName -Headers $Headers # $fetchSuccess = $true # } catch { # Write-EnhancedLog -Message "Failed to fetch licenses for user: $($Item.userDisplayName). Attempt $($retryCount + 1) of $maxRetries" -Level "ERROR" # $fetchSuccess = $false # $retryCount++ # Start-Sleep -Seconds 2 # } # } while (-not $fetchSuccess -and $retryCount -lt $maxRetries) # if (-not $fetchSuccess) { # Write-EnhancedLog -Message "Failed to fetch licenses for user: $($Item.userDisplayName) after $maxRetries attempts." -Level "ERROR" # } # $hasPremiumLicense = $userLicenses -and $userLicenses.Count -gt 0 -and $userLicenses.Contains("cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46") # Write-EnhancedLog -Message "User $($Item.userDisplayName) has the following licenses: $($userLicenses -join ', ')" -Level "INFO" # Add-Result -Context $Context -Item $Item -DeviceId $deviceId -DeviceState $deviceState -HasPremiumLicense $hasPremiumLicense -OSVersion $osVersion # } # } catch { # Write-EnhancedLog -Message "An error occurred while processing the device item for user: $($Item.userDisplayName) - $_" -Level "ERROR" # Handle-Error -ErrorRecord $_ # } # } # End { # Write-EnhancedLog -Message "Exiting Process-DeviceItem function" -Level "INFO" # } # } #EndRegion '.\Public\Process-DeviceItem-old.ps1' 168 #Region '.\Public\Process-DeviceItem.ps1' -1 function Process-DeviceItem { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] $Item, [Parameter(Mandatory = $true)] $Context, [Parameter(Mandatory = $true)] $Headers ) Begin { Write-EnhancedLog -Message "Starting Process-DeviceItem function" -Level "INFO" Log-Params -Params @{ Item = $Item; Context = $Context } Initialize-Context -Context $Context } Process { # Ensure deviceDetail object and properties exist if (-not $Item.deviceDetail) { Write-EnhancedLog -Message "Missing deviceDetail for user: $($Item.userDisplayName)" -Level "WARNING" return } $deviceId = $Item.deviceDetail.deviceId $userId = $Item.userId $os = $Item.deviceDetail.operatingSystem if (-not $userId) { Write-EnhancedLog -Message "Missing userId for device item" -Level "WARNING" return } try { # Construct uniqueId based on availability of deviceId and OS for BYOD if ([string]::IsNullOrWhiteSpace($deviceId)) { $uniqueId = "$userId-$os".ToLowerInvariant() } else { $uniqueId = $deviceId.ToLowerInvariant() } # Log the device and user information Write-EnhancedLog -Message "Processing device item for user: $($Item.userDisplayName) with unique ID: $uniqueId" -Level "INFO" # Handle external Azure AD tenant case if (Handle-ExternalAADTenant -Item $Item -Context $Context -UniqueId $uniqueId) { return } # Process only if the unique ID is not already processed if ($Context.UniqueDeviceIds.Add($uniqueId)) { # Handle BYOD case if ([string]::IsNullOrWhiteSpace($deviceId)) { # Fetch user licenses with retry logic $userLicenses = Fetch-UserLicensesWithRetry -UserId $userId -Username $Item.userDisplayName -Headers $Headers $hasPremiumLicense = $userLicenses -and $userLicenses.Count -gt 0 -and $userLicenses.Contains("cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46") Write-EnhancedLog -Message "User $($Item.userDisplayName) has the following licenses: $($userLicenses -join ', ')" -Level "INFO" Add-Result -Context $Context -Item $Item -DeviceId "N/A" -DeviceState "BYOD" -HasPremiumLicense $hasPremiumLicense -OSVersion $null return } # Handle managed device case with retry logic $deviceState = Fetch-DeviceStateWithRetry -DeviceId $deviceId -Username $Item.userDisplayName -Headers $Headers $osVersion = Fetch-OSVersionWithRetry -DeviceId $deviceId -Headers $Headers $userLicenses = Fetch-UserLicensesWithRetry -UserId $userId -Username $Item.userDisplayName -Headers $Headers $hasPremiumLicense = $userLicenses -and $userLicenses.Count -gt 0 -and $userLicenses.Contains("cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46") Write-EnhancedLog -Message "User $($Item.userDisplayName) has the following licenses: $($userLicenses -join ', ')" -Level "INFO" Add-Result -Context $Context -Item $Item -DeviceId $deviceId -DeviceState $deviceState -HasPremiumLicense $hasPremiumLicense -OSVersion $osVersion } } catch { Write-EnhancedLog -Message "An error occurred while processing the device item for user: $($Item.userDisplayName) - $_" -Level "ERROR" Handle-Error -ErrorRecord $_ } } End { Write-EnhancedLog -Message "Exiting Process-DeviceItem function" -Level "INFO" } } #EndRegion '.\Public\Process-DeviceItem.ps1' 83 #Region '.\Public\Process-SignInLogs.ps1' -1 function Process-SignInLogs { param ( [Parameter(Mandatory = $true)] [System.Collections.Generic.List[PSCustomObject]]$signInLogs, [Parameter(Mandatory = $true)] [hashtable]$Headers ) # Ensure the signInLogs variable is not null before using it if ($null -eq $signInLogs -or $signInLogs.Count -eq 0) { Write-Warning "No sign-in logs were loaded." exit 1 } # Display the count of loaded sign-in logs Write-Host "Loaded $($signInLogs.Count) sign-in logs." # Debugging: Print the first sign-in log entry if ($signInLogs.Count -gt 0) { $firstSignInLog = $signInLogs[0] Write-Host "First sign-in log entry:" Write-Host "UserDisplayName: $($firstSignInLog.UserDisplayName)" Write-Host "UserId: $($firstSignInLog.UserId)" Write-Host "DeviceDetail:" Write-Host " DeviceId: $($firstSignInLog.DeviceDetail.DeviceId)" Write-Host " DisplayName: $($firstSignInLog.DeviceDetail.DisplayName)" Write-Host " OperatingSystem: $($firstSignInLog.DeviceDetail.OperatingSystem)" Write-Host " IsCompliant: $($firstSignInLog.DeviceDetail.IsCompliant)" Write-Host " TrustType: $($firstSignInLog.DeviceDetail.TrustType)" } $context = New-ProcessingContext # Process each log item directly foreach ($log in $signInLogs) { # Exclude "On-Premises Directory Synchronization Service Account" user if ($log.UserDisplayName -ne "On-Premises Directory Synchronization Service Account" -and $null -ne $log) { try { Process-DeviceItem -Item $log -Context $context -Headers $Headers } catch { Write-Error "Error processing item: $($_.Exception.Message)" Handle-Error -ErrorRecord $_ } } } # Remove null entries from the results list $context.Results = $context.Results | Where-Object { $_ -ne $null } # Return the results return $context.Results } #EndRegion '.\Public\Process-SignInLogs.ps1' 53 |