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" } # Determine the sign-in status based on the error code $signInStatus = if ($Item.Status.ErrorCode -eq 0) { "Success" } else { "Failure" } # Create the result object directly $result = [PSCustomObject]@{ 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 SignInStatus = $signInStatus # New property for Sign-In Status City = $Item.Location.City # New property for City State = $Item.Location.State # New property for State CountryOrRegion = $Item.Location.CountryOrRegion # New property for Country/Region } # Add the result to the context $Context.Results.Add($result) Write-EnhancedLog -Message "Successfully added result for device: $deviceName for user: $($Item.UserDisplayName) with status: $signInStatus" -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' 68 #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" -Level 'INFO' $graphApiUrl = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?`$filter=azureADDeviceId eq '$EntraDeviceId'" Write-EnhancedLog -Message "Constructed Graph API URL: $graphApiUrl" -Level 'INFO' $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." -Level 'INFO' return "Present" } else { Write-EnhancedLog -Message "Device is absent in Intune." -Level 'WARNING' return "Absent" } } else { Write-EnhancedLog -Message "Received empty response from Intune API." -Level 'WARNING' 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 "INFO" $latestJsonFile = Find-LatestJsonFile -Directory $subFolderPath if ($latestJsonFile) { Write-EnhancedLog -Message "Latest JSON file found: $latestJsonFile" -Level "INFO" $signInLogs = Load-SignInLogs -JsonFilePath $latestJsonFile # Log the count of sign-in logs found Write-EnhancedLog -Message "Loaded $(@($signInLogs).Count) sign-in logs from file: $latestJsonFile" -Level "INFO" # Debugger can be enabled here if needed for further inspection # Wait-Debugger if ($null -eq $signInLogs -or @($signInLogs).Count -eq 0) { Write-EnhancedLog -Message "No sign-in logs found in $latestJsonFile." -Level "WARNING" return @() } else { Write-EnhancedLog -Message "Sign-in logs found in $latestJsonFile. Starting to process them." -Level "NOTICE" # Process-AllDevices -Json $signInLogs -Headers $Headers return $signInLogs } # Wait-Debugger } 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' 71 #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 'INFO' # $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() # } # $status = [PSCustomObject]@{ # ErrorCode = $element.GetProperty("status").GetProperty("errorCode").GetInt32() # FailureReason = $element.GetProperty("status").GetProperty("failureReason").GetString() # AdditionalDetails = $element.GetProperty("status").GetProperty("additionalDetails").GetString() #returns a get string error possibly due to some being null and some having special characters like like continuation # } # $location = [PSCustomObject]@{ # City = $element.GetProperty("location").GetProperty("city").GetString() # State = $element.GetProperty("location").GetProperty("state").GetString() # CountryOrRegion = $element.GetProperty("location").GetProperty("countryOrRegion").GetString() # } # $signInLog = [PSCustomObject]@{ # UserDisplayName = $element.GetProperty("userDisplayName").GetString() # UserId = $element.GetProperty("userId").GetString() # DeviceDetail = $deviceDetail # Status = $status # Include the status object in the sign-in log # Location = $location # Include the location object in the sign-in log # } # $signInLogs.Add($signInLog) # } # Write-EnhancedLog -Message "Sign-in logs loaded successfully from $JsonFilePath." -Level "INFO" # } catch { # Handle-Error -ErrorRecord $_ # } finally { # $fileStream.Dispose() # } # return $signInLogs # } function Load-SignInLogs { param ( [string]$JsonFilePath ) $signInLogs = [System.Collections.Generic.List[PSCustomObject]]::new() try { $jsonContent = Get-Content -Path $JsonFilePath -Raw | ConvertFrom-Json foreach ($entry in $jsonContent) { $signInLogs.Add([PSCustomObject]@{ UserDisplayName = $entry.userDisplayName UserId = $entry.userId DeviceDetail = $entry.deviceDetail Status = $entry.status Location = $entry.location }) } Write-Host "Sign-in logs loaded successfully from $JsonFilePath." } catch { Write-Host "An error occurred: $_" } return $signInLogs.ToArray() } #EndRegion '.\Public\Load-SignInLogs.ps1' 84 #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 ErrorCode is properly accessed $errorCode = $Item.Status.ErrorCode $failureReason = $Item.Status.FailureReason # Wait-Debugger if ($null -eq $errorCode) { Write-EnhancedLog -Message "Missing ErrorCode in the sign-in log for user $($Item.userDisplayName). Skipping this item." -Level "WARNING" return } # Check if sign-in was successful if ($errorCode -ne 0) { Write-EnhancedLog -Message "Sign-in attempt failed for user $($Item.userDisplayName) with ErrorCode: $errorCode - $failureReason" -Level "WARNING" return } # 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 } else { Write-EnhancedLog -Message "Device ID $uniqueId for user $($Item.userDisplayName) has already been processed and will be skipped." -Level "WARNING" } } 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" } } #v2 with parallel processing # function Process-DeviceItem { # [CmdletBinding()] # param ( # [Parameter(Mandatory = $true)] # $Item, # [Parameter(Mandatory = $true)] # $Headers # ) # Begin { # Write-EnhancedLog -Message "Starting Process-DeviceItem function" -Level "INFO" # Log-Params -Params @{ Item = $Item } # # Local context-like structure # $result = $null # } # Process { # $errorCode = $Item.Status.ErrorCode # $failureReason = $Item.Status.FailureReason # if ($null -eq $errorCode) { # Write-EnhancedLog -Message "Missing ErrorCode in the sign-in log for user $($Item.userDisplayName). Skipping this item." -Level "WARNING" # return # } # if ($errorCode -ne 0) { # Write-EnhancedLog -Message "Sign-in attempt failed for user $($Item.userDisplayName) with ErrorCode: $errorCode - $failureReason" -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 { # if ([string]::IsNullOrWhiteSpace($deviceId)) { # $uniqueId = "$userId-$os".ToLowerInvariant() # } # else { # $uniqueId = $deviceId.ToLowerInvariant() # } # Write-EnhancedLog -Message "Processing device item for user: $($Item.userDisplayName) with unique ID: $uniqueId" -Level "INFO" # # 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" # $deviceState = Fetch-DeviceStateWithRetry -DeviceId $deviceId -Username $Item.userDisplayName -Headers $Headers # $osVersion = Fetch-OSVersionWithRetry -DeviceId $deviceId -Headers $Headers # # Create a local result object # $result = [PSCustomObject]@{ # DeviceName = $Item.DeviceDetail.DisplayName # UserName = $Item.UserDisplayName # DeviceEntraID = $deviceId # UserEntraID = $userId # DeviceOS = $Item.DeviceDetail.OperatingSystem # OSVersion = $osVersion # DeviceComplianceStatus = if ($Item.DeviceDetail.IsCompliant) { "Compliant" } else { "Non-Compliant" } # DeviceStateInIntune = $deviceState # TrustType = $Item.DeviceDetail.TrustType # UserLicense = if ($hasPremiumLicense) { "Microsoft 365 Business Premium" } else { "Other" } # SignInStatus = "Success" # } # Write-EnhancedLog -Message "Successfully processed device item for user: $($Item.userDisplayName)" -Level "INFO" # } # 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" # return $result # } # } # # Global log queue # $global:LogQueue = [System.Collections.Concurrent.ConcurrentQueue[System.String]]::new() # # Start background job for async logging # $global:LogJob = Start-Job -ScriptBlock { # param ($logQueue) # while ($true) { # if ($logQueue.TryDequeue([ref]$logMessage)) { # # Example of writing to a file, you can modify this to fit your logging method # Add-Content -Path "logfile.txt" -Value $logMessage # } else { # Start-Sleep -Milliseconds 100 # } # } # } -ArgumentList $global:LogQueue # function Write-AsyncLog { # param ( # [string]$Message, # [string]$Level = "INFO", # [switch]$WriteToConsole = $true # Add a parameter to control console output # ) # $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" # $logMessage = "[$timestamp][$Level] $Message" # # Enqueue the log message for asynchronous file logging # $global:LogQueue.Enqueue($logMessage) # # Optionally write the log message to the console # if ($WriteToConsole) { # Write-Host $logMessage # } # } # function Process-DeviceItem { # [CmdletBinding()] # param ( # [Parameter(Mandatory = $true)] # $Item, # [Parameter(Mandatory = $true)] # $Context, # [Parameter(Mandatory = $true)] # $Headers # ) # Begin { # Write-AsyncLog -Message "Starting Process-DeviceItem function" -Level "INFO" # Log-Params -Params @{ Item = $Item; Context = $Context } # Initialize-Context -Context $Context # } # Process { # $errorCode = $Item.Status.ErrorCode # $failureReason = $Item.Status.FailureReason # if ($null -eq $errorCode) { # Write-AsyncLog -Message "Missing ErrorCode in the sign-in log for user $($Item.userDisplayName). Skipping this item." -Level "WARNING" # return # } # if ($errorCode -ne 0) { # Write-AsyncLog -Message "Sign-in attempt failed for user $($Item.userDisplayName) with ErrorCode: $errorCode - $failureReason" -Level "WARNING" # return # } # if (-not $Item.deviceDetail) { # Write-AsyncLog -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-AsyncLog -Message "Missing userId for device item" -Level "WARNING" # return # } # try { # $uniqueId = if ([string]::IsNullOrWhiteSpace($deviceId)) { # "$userId-$os".ToLowerInvariant() # } else { # $deviceId.ToLowerInvariant() # } # Write-AsyncLog -Message "Processing device item for user: $($Item.userDisplayName) with unique ID: $uniqueId" -Level "INFO" # if (Handle-ExternalAADTenant -Item $Item -Context $Context -UniqueId $uniqueId) { # return # } # if ($Context.UniqueDeviceIds.Add($uniqueId)) { # if ([string]::IsNullOrWhiteSpace($deviceId)) { # $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-AsyncLog -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 # } # $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-AsyncLog -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 # } # else { # Write-AsyncLog -Message "Device ID $uniqueId for user $($Item.userDisplayName) has already been processed and will be skipped." -Level "WARNING" # } # } # catch { # Write-AsyncLog -Message "An error occurred while processing the device item for user: $($Item.userDisplayName) - $_" -Level "ERROR" # Handle-Error -ErrorRecord $_ # } # } # End { # Write-AsyncLog -Message "Exiting Process-DeviceItem function" -Level "INFO" # } # } #EndRegion '.\Public\Process-DeviceItem.ps1' 335 #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 } else { Write-Host "Loaded $(@($signInLogs).Count) sign-in logs." } # Debugging: Print the first sign-in log entry if ($signInLogs.Count -gt 0) { $firstSignInLog = $signInLogs[0] # Determine the sign-in status based on the ErrorCode $signInStatus = if ($firstSignInLog.Status.ErrorCode -eq 0) { "Success" } else { "Failure" } Write-Host "First sign-in log entry:" Write-Host "UserDisplayName: $($firstSignInLog.UserDisplayName)" Write-Host "UserId: $($firstSignInLog.UserId)" Write-Host "SignInStatus: $signInStatus" # Print the sign-in status 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 # Process-DeviceItem -Item $log -Headers $Headers } catch { Write-Error "Error processing item: $($_.Exception.Message)" Handle-Error -ErrorRecord $_ } } } # Stop the logging job when done Stop-Job -Job $global:LogJob Remove-Job -Job $global:LogJob # $jobs = @() # 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) { # $jobs += Start-Job -ScriptBlock { # param ($log, $context, $headers) # try { # Process-DeviceItem -Item $log -Context $context -Headers $headers # } # catch { # Write-Error "Error processing item: $($_.Exception.Message)" # Handle-Error -ErrorRecord $_ # } # } -ArgumentList $log, $context, $Headers # } # } # # Wait for all jobs to complete # $jobs | ForEach-Object { # $_ | Wait-Job # } # # Retrieve job results # $jobs | ForEach-Object { # Receive-Job -Job $_ # Remove-Job -Job $_ # } # $signInLogs | ForEach-Object -Parallel { # param ( # $log, # $context, # $headers # ) # # 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 $_ # } # } # } -ArgumentList $_, $using:context, $using:headers -ThrottleLimit 10 # 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' 125 |