Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
408 views
in Technique[技术] by (71.8m points)

powershell - ForEach-Object changing result of SearchUnifiedAuditLog

I have a powershell script to retrieve the most recent login for guest users. It works fine. (Based on https://gallery.technet.microsoft.com/office/Get-Guest-User-Last-Login-39f8237e)

$startDate = "{0:yyyy-MM-dd}" -f (get-date).AddDays(-365)   #look 1 year back (not sure what the maximum is, but 1 year seems to work)
$endDate = "{0:yyyy-MM-dd}" -f (get-date)   #current date.
$externalUserExtention = "*#EXT#*"
$filePath="c:empguest_user_last_logins.txt"

function logtoText($filePath, $msg) {
    $msg >> $filepath;
}

function find_last_login_date($user) {
    $lastLoginDate = Search-UnifiedAuditLog -UserIds $user.UserPrincipalName -StartDate $startDate -EndDate $endDate| Foreach-Object {$_.CreationDate = [DateTime]$_.CreationDate; $_} | Group-Object UserIds | Foreach-Object {$_.Group | Sort-Object CreationDate | Select-Object -Last 1} | Select CreationDate
    Write-Host "User " $user.UserPrincipalName "| Last Login Date -" $lastLoginDate.CreationDate
    logtoText $filePath ($user.UserPrincipalName + "," + $lastLoginDate.CreationDate)
    Write-Output $user.UserPrincipalName
}

Clear-Content $filePath
logtoText $filePath ('Username', 'Last Login DateTime')

#Get All External Users
$allExternalUsers = Get-MsolUser -All | Where-Object -FilterScript { $_.UserPrincipalName -Like $externalUserExtention }
ForEach($externalUser in $allExternalUsers) {
    find_last_login_date($externalUser)
}

The output is as expected:

User [email protected]#EXT#@tenant.onmicrosoft.com  | Last Login Date - 
[email protected]#EXT#@tenant.onmicrosoft.com
User [email protected]#EXT#@tenant.onmicrosoft.com  | Last Login Date - 11/04/2020 12:02:12 
[email protected]#EXT#@tenant.onmicrosoft.com
...
User [email protected]#EXT#@tenant.onmicrosoft.com  | Last Login Date - 12/04/2020 14:22:25
[email protected]#EXT#@tenant.onmicrosoft.com
...

The extra Write-Output inside find_last_login_date is just their for debugging this.

Now when I change it to ForEach-Object it does something weird. Probably it makes sense to the experts, but not to me :-)

Replace

ForEach($externalUser in $allExternalUsers) {
    find_last_login_date($externalUser)
}

With

ForEach-Object -InputObject $allExternalUsers {
    find_last_login_date($_)
}

And the output becomes

User [email protected]#EXT#@tenant.onmicrosoft.com | Last Login Date - 10/01/2021 14:05:36 05/01/2021 20:48:45 05/01/2021 16:09:25 04/01/2021 07:36:26 22/12/2020 08:01:07 19/12/2020 10:24:08 18/12/2020 14:20:51 18/12/2020 08:05:55 16/12/2020 17:32:28 14/12/2
020 08:20:56 13/12/2020 07:58:13 11/12/2020 10:58:58 10/12/2020 08:08:28
[email protected]#EXT#@tenant.onmicrosoft.com
[email protected]#EXT#@tenant.onmicrosoft.com
...
[email protected]#EXT#@tenant.onmicrosoft.com
...

So it seems to trigger the script to only look for the last login date once and also the select top 1 fails, etc.

Any idea what I am doing wrong? At first I thought it was about the "nested" ForEach-Object but even with the code to do the search inside a function it keeps doing this. Is it by default doing things in parallel and do I need to wait for completion of all tasks? Something telse?

I'm looking at ForEach-Object to parallelize the above script as it takes a very long time to run on a large tenant.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

From the docs :

When you use the InputObject parameter with ForEach-Object, instead of piping command results to ForEach-Object, the InputObject value is treated as a single object. This is true even if the value is a collection that is the result of a command, such as -InputObject (Get-Process). Because InputObject cannot return individual properties from an array or collection of objects, we recommend that if you use ForEach-Object to perform operations on a collection of objects for those objects that have specific values in defined properties, you use ForEach-Object in the pipeline.

Change

ForEach-Object -InputObject $allExternalUsers {
    find_last_login_date($_)
}

into

$allExternalUsers | ForEach-Object {
    find_last_login_date($_)
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...