Forum Discussion

StuartK73's avatar
StuartK73
Iron Contributor
Mar 25, 2025

Entra Shared Device Mode Sign-ins

Hi All

 

I hope you are well.

 

Anyway, I understand that Entra Shared Devices are "userless" but is there any way to see a log of who has signed into an Entra Shared Device? Maybe even a log of the last person to sign in?

 

Info appreciated.

 

Stuart

 

  • ppel123's avatar
    ppel123
    Copper Contributor

    Hi Stuart, just a quick question for clarification, are the users logging in at the lock screen using their Entra ID credentials? In that case could a script like the one mentioned here provide any useful insights (it provides the last logged in user)? I also have another approach in my mind, but let's first see if the above covers what you want to investigate here.

    • StuartK73's avatar
      StuartK73
      Iron Contributor

      Hi Buddy

       

      Many thanks for the quick and informative reply.

       

      Yes, these are Entra Shared Device Mode Android Enterprise devices where the user initially logs in using their M365 creds and then they set a Session PIN.

       

      Regards

      • ppel123's avatar
        ppel123
        Copper Contributor

        I was thinking of a script to check the Sign in Audit Logs in Entra and try to get the information that you want. 

        I have written one that does something similar. Please take a look at it below in case it provides any useful insights (and of course if you have any questions, let me know).

        The below if for one device.

         

        # Ensure the directory exists
        $logDirectory = "C:\Temp"
        if (!(Test-Path $logDirectory)) {
            New-Item -ItemType Directory -Path $logDirectory | Out-Null
        }
        
        # Start transcript to capture console output to a file
        Start-Transcript -Path "C:\Temp\AuditLogsDevices.log" -Append
        
        try {
            ###############################################################################
            # Connect to Microsoft Graph
            ###############################################################################
            Connect-MgGraph -Scopes "AuditLog.Read.All"
        
            ###############################################################################
            # Prepare the DataTable
            ###############################################################################
            $table = New-Object System.Data.DataTable
            $table.Columns.Add("DeviceName")        | Out-Null
            $table.Columns.Add("DeviceID")          | Out-Null
            $table.Columns.Add("IsManaged")         | Out-Null
            $table.Columns.Add("SignInTime")        | Out-Null
            $table.Columns.Add("UserPrincipalName") | Out-Null
            $table.Columns.Add("AppDisplayName")    | Out-Null
            $table.Columns.Add("TokenIssuerType")   | Out-Null
            $table.Columns.Add("IsInteractive")     | Out-Null
        
            ###############################################################################
            # Define the single device name (adjust as needed)
            ###############################################################################
            $deviceName = "YourSharedDeviceName"
        
            ###############################################################################
            # Retrieve sign-in logs for the last 30 days for this one device
            # Using server-side filter to be more efficient
            ###############################################################################
            $since = (Get-Date).AddDays(-30).ToString("yyyy-MM-ddTHH:mm:ssZ")
            Write-Host "`nFetching sign-ins from the last 30 days for device: $deviceName"
        
            # Note: 'contains(...)' + 'tolower(...)' helps match partial/case-insensitive device names.
            $signInLogs = Get-MgAuditLogSignIn -All `
                -Filter "createdDateTime ge $since and contains(tolower(deviceDetail/displayName), '$($deviceName.ToLower())')" `
                | Select-Object `
                    CreatedDateTime,
                    UserPrincipalName,
                    AppDisplayName,
                    IsInteractive,
                    TokenIssuerType,
                    @{
                        Name       = 'DeviceId'
                        Expression = { $_.DeviceDetail.DeviceId }
                    },
                    @{
                        Name       = 'DeviceName'
                        Expression = { $_.DeviceDetail.DisplayName }
                    },
                    @{
                        Name       = 'IsManaged'
                        Expression = { $_.DeviceDetail.IsManaged }
                    }
        
            Write-Host "Total sign-ins retrieved for device '$deviceName': $($signInLogs.Count)"
        
            ###############################################################################
            # Populate the DataTable with the sign-in details
            ###############################################################################
            if ($signInLogs -and $signInLogs.Count -gt 0) {
                foreach ($log in $signInLogs) {
                    $row = $table.NewRow()
                    $row["DeviceName"]        = $log.DeviceName
                    $row["DeviceID"]          = $log.DeviceId
                    $row["IsManaged"]         = $log.IsManaged
                    $row["SignInTime"]        = $log.CreatedDateTime
                    $row["UserPrincipalName"] = $log.UserPrincipalName
                    $row["AppDisplayName"]    = $log.AppDisplayName
                    $row["TokenIssuerType"]   = $log.TokenIssuerType
                    $row["IsInteractive"]     = $log.IsInteractive
                    $table.Rows.Add($row)
                }
            }
            else {
                Write-Host "No sign-in logs found for device: $deviceName"
            }
        
            ###############################################################################
            # Display the results
            ###############################################################################
            Write-Host "`n=== Final Table of Device Sign-Ins ==="
            $table  # Shows the DataTable on screen in the console
        
            # Display in a separate GridView window (useful for interactive inspection)
            $table | Out-GridView -Title "Device Sign-Ins"
        
            # Export to CSV if desired:
            $table | Export-Csv -Path "C:\Temp\DeviceSignIns.csv" -NoTypeInformation
            Write-Host "Exported results to C:\Temp\DeviceSignIns.csv"
        
        } finally {
            # Stop transcript to end console logging
            Stop-Transcript
        }

         

        And this one is for many devices.

        # Ensure the directory exists
        $logDirectory = "C:\Temp"
        if (!(Test-Path $logDirectory)) {
            New-Item -ItemType Directory -Path $logDirectory | Out-Null
        }
        
        # Start transcript to capture console output to a file
        Start-Transcript -Path "C:\Temp\AuditLogsDevices.log" -Append
        
        try {
            ###############################################################################
            # Connect to Microsoft Graph
            ###############################################################################
            Connect-MgGraph -Scopes "AuditLog.Read.All"
        
            ###############################################################################
            # Prepare the DataTable
            ###############################################################################
            $table = New-Object System.Data.DataTable
            $table.Columns.Add("DeviceName")        | Out-Null
            $table.Columns.Add("DeviceID")          | Out-Null
            $table.Columns.Add("IsManaged")         | Out-Null
            $table.Columns.Add("SignInTime")        | Out-Null
            $table.Columns.Add("UserPrincipalName") | Out-Null
            $table.Columns.Add("AppDisplayName")    | Out-Null
            $table.Columns.Add("TokenIssuerType")   | Out-Null
            $table.Columns.Add("IsInteractive")     | Out-Null
        
            ###############################################################################
            # Read the list of device names from a text file
            # (one device name per line in C:\Temp\devices.txt)
            ###############################################################################
            $devices = Get-Content -Path "C:\Temp\devices.txt"
        
            ###############################################################################
            # Retrieve ALL sign-in logs for a specified time window
            # Adjust the date range as needed, e.g. last 30 days
            ###############################################################################
            $since = (Get-Date).AddDays(-30).ToString("yyyy-MM-ddTHH:mm:ssZ")
            Write-Host "`nFetching all sign-ins from the last 30 days... (This might take a while.)"
        
            $allSignInLogs = Get-MgAuditLogSignIn -All `
                -Filter "createdDateTime ge $since" `
                | Select-Object `
                    CreatedDateTime,
                    UserPrincipalName,
                    AppDisplayName,
                    IsInteractive,
                    TokenIssuerType,
                    @{
                        Name       = 'DeviceId'
                        Expression = { $_.DeviceDetail.DeviceId }
                    },
                    @{
                        Name       = 'DeviceName'
                        Expression = { $_.DeviceDetail.DisplayName }
                    },
                    @{
                        Name       = 'IsManaged'
                        Expression = { $_.DeviceDetail.IsManaged }
                    }
        
            Write-Host "Total sign-ins retrieved: $($allSignInLogs.Count)"
        
            ###############################################################################
            # For each device in the text file, filter the sign-ins locally
            ###############################################################################
            foreach ($dev in $devices) {
                Write-Host "`n=== Searching sign-in logs for device: $dev ==="
        
                # Filter locally for sign-ins that match this device name
                $signInLogs = $allSignInLogs | Where-Object {
                    $_.DeviceName -and ($_.DeviceName -like "*$dev*")
                }
        
                if ($signInLogs -and $signInLogs.Count -gt 0) {
                    Write-Host "Found $($signInLogs.Count) sign-ins for device: $dev"
                    foreach ($log in $signInLogs) {
                        $row = $table.NewRow()
                        $row["DeviceName"]        = $log.DeviceName
                        $row["DeviceID"]          = $log.DeviceId
                        $row["IsManaged"]         = $log.IsManaged
                        $row["SignInTime"]        = $log.CreatedDateTime
                        $row["UserPrincipalName"] = $log.UserPrincipalName
                        $row["AppDisplayName"]    = $log.AppDisplayName
                        $row["TokenIssuerType"]   = $log.TokenIssuerType
                        $row["IsInteractive"]     = $log.IsInteractive
                        $table.Rows.Add($row)
                    }
                }
                else {
                    Write-Host "No sign-in logs found for device: $dev"
                }
            }
        
            ###############################################################################
            # Display the results
            ###############################################################################
            Write-Host "`n=== Final Table of Device Sign-Ins ==="
            $table  # Shows the DataTable on screen in the console
        
            # Display in a separate GridView window (useful for interactive inspection)
            $table | Out-GridView -Title "Device Sign-Ins"
        
            # Export to CSV if desired:
            $table | Export-Csv -Path "C:\Temp\DeviceSignIns.csv" -NoTypeInformation
            Write-Host "Exported results to C:\Temp\DeviceSignIns.csv"
        
        } finally {
            # Stop transcript to end console logging
            Stop-Transcript
        }

         

        Just to mention here that there are some considerations when trying to run scripts using the Get-MgAuditLogSignIn cmdlet. Specifically:

        • Large Data Sets: Retrieving all sign-ins from the last 30 days can be resource-intensive, especially in larger tenants. You might consider narrowing the time range (e.g., last 7 days) or adding server-side filters to limit results.
        • Graph Throttling: Microsoft Graph enforces rate limits. If you’re processing large amounts of data, you may hit throttling. Implement retry logic or break down queries into smaller chunks if you notice performance degradation.

Resources