ForEach-Object Search UnifiedAuditLog 变化结果

ForEach-Object changing result of SearchUnifiedAuditLog

我有一个 powershell 脚本来检索来宾用户的最新登录信息。它工作正常。 (基于 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:\temp\guest_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)
}

输出符合预期:

User user1@external.com#EXT#@tenant.onmicrosoft.com  | Last Login Date - 
user1@external.com#EXT#@tenant.onmicrosoft.com
User user2@external.com#EXT#@tenant.onmicrosoft.com  | Last Login Date - 11/04/2020 12:02:12 
user2@external.com#EXT#@tenant.onmicrosoft.com
...
User userNNNN@external.com#EXT#@tenant.onmicrosoft.com  | Last Login Date - 12/04/2020 14:22:25
userNNNN@external.com#EXT#@tenant.onmicrosoft.com
...

find_last_login_date 里面额外的 Write-Output 只是用来调试的。

现在,当我将其更改为 ForEach-Object 时,它会做一些奇怪的事情。可能对专家来说很有意义,但对我来说不是:-)

替换

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

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

输出变为

User userX@external.com#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
user1@external.com#EXT#@tenant.onmicrosoft.com
user2@external.com#EXT#@tenant.onmicrosoft.com
...
userNNNN@external.com#EXT#@tenant.onmicrosoft.com
...

所以它似乎触发脚本只查找最后一次登录日期并且 select top 1 失败,等等

知道我做错了什么吗?起初我以为它是关于“嵌套的”ForEach-Object,但即使使用代码在函数内部进行搜索,它也会继续这样做。 默认情况下是并行做事吗,我需要等待所有任务完成吗?还有别的吗?

我正在查看 ForEach-Object 以并行化上述脚本,因为在大型租户上 运行 需要很长时间。

来自 the docs :

当您将 InputObject 参数与 ForEach-Object 一起使用时,InputObject 值将被视为单个对象,而不是通过管道将命令结果传递给 ForEach-Object。即使该值是命令结果的集合也是如此,例如 -InputObject (Get-Process)。因为 InputObject 不能 return 数组或对象集合中的单个属性,所以我们建议,如果您使用 ForEach-Object 对对象集合执行操作,对于那些在定义的属性中具有特定值的对象,您可以使用 ForEach-管道中的对象。

改变

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

进入

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