Xuất tất cả danh sách người dùng với thời gian login gần nhất trên office 365
Mẫu Script PowerShell sử dụng Microsoft Graph
# Cài module nếu chưa có
Install-Module Microsoft.Graph -Scope CurrentUser -Force
# Kết nối tới Graph với quyền cần thiết
Connect-MgGraph -Scopes "AuditLog.Read.All","Directory.Read.All","User.Read.All"
# Đặt profile beta nếu cần (SignInActivity có thể trong beta)
Select-MgProfile -Name "beta"
# Lấy tất cả user kèm SignInActivity
$users = Get-MgUser -All -Property "displayName","userPrincipalName","signInActivity"
# Tạo danh sách kết quả
$report = $users | Select-Object @{Name="DisplayName";Expression={$_.DisplayName}},
@{Name="UPN";Expression={$_.UserPrincipalName}},
@{Name="LastSignIn";Expression={
if ($_.SignInActivity.LastSignInDateTime) {
$_.SignInActivity.LastSignInDateTime
} else {
"Never or > retention"
}
}}
# Xuất ra CSV
$report | Export-Csv -Path "Office365_Users_LastSignIn.csv" -NoTypeInformation
# Hiển thị danh sách
$report | Format-Table -AutoSize
I. Cài đặt và kết nối Microsoft Graph
Mở PowerShell (Run as Administrator) và chạy:
Sau đó đăng nhập bằng tài khoản có quyền Global Reader / User Administrator / Global Admin:
🔸 Dùng
betaprofile để có thể truy cập trường SignInActivity, vì trường này không có trong bản stable.
Tuyệt! Đây là script hoàn chỉnh đã FIX lỗi license null, giữ đủ các cột như bạn muốn và bổ sung LastLogin (định dạng dd/MM/yyyy HH:mm:ss). Chạy được trên PowerShell 5.1/7.x, không cần Entra P1/P2.
# ============================
# FULL Office 365 User Report + LastLogin (no Entra P1/P2)
# Kết quả: C:\Reports\O365_FullColumns_WithLastLogin.csv
# ============================
# (Khuyến nghị nếu bị chặn script)
# Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Force
# 1) Kết nối Microsoft Graph
Connect-MgGraph -Scopes "Reports.Read.All","User.Read.All","Directory.Read.All"
# 2) Tải báo cáo Active User Detail (D180) -> CSV
$dir = "C:\Reports"
New-Item -ItemType Directory -Path $dir -Force | Out-Null
$actCsv = Join-Path $dir "O365_ActiveUserDetail_D180.csv"
Get-MgReportOffice365ActiveUserDetail -Period D180 -OutFile $actCsv
$act = Import-Csv $actCsv
# 3) Lấy Directory users (để có đủ thông tin người dùng)
$uProps = @(
"id","displayName","userPrincipalName","givenName","surname","createdDateTime",
"jobTitle","department","companyName",
"city","country","officeLocation","state","usageLocation",
"onPremisesSyncEnabled","accountEnabled","preferredLanguage",
"mobilePhone","businessPhones","postalCode","streetAddress","proxyAddresses",
"assignedLicenses"
)
$users = Get-MgUser -All -Property ($uProps -join ",")
# Bảng tra UPN -> user object
$uLookup = @{}
foreach ($u in $users) { if ($u.UserPrincipalName) { $uLookup[$u.UserPrincipalName.ToLower()] = $u } }
# 4) Map SkuId -> SkuPartNumber (tên gói license) — an toàn khi null
$skuMap = @{}
try {
$skus = Get-MgSubscribedSku
if ($skus) {
foreach ($s in $skus) {
if ($s.SkuId -and $s.SkuPartNumber) {
$skuMap[$s.SkuId.Guid] = $s.SkuPartNumber
}
}
}
} catch { }
# 5) Helper: lấy ngày mới nhất trong các cột hoạt động (xấp xỉ last login)
function Get-LatestDate {
param([string[]]$values)
$dates = @()
foreach ($v in $values) {
if (-not $v) { continue }
$v = $v.Trim(); if ($v -eq "") { continue }
try { $dates += [datetime]::Parse($v) } catch { }
}
if ($dates.Count -gt 0) { ($dates | Sort-Object -Descending | Select-Object -First 1) } else { $null }
}
$activityCols = @(
'Last Activity Date',
'Exchange Last Activity Date',
'OneDrive Last Activity Date',
'SharePoint Last Activity Date',
'Teams Last Activity Date',
'Yammer Last Activity Date',
'Skype For Business Last Activity Date'
)
# 6) Hợp nhất dữ liệu, xuất đúng các cột mong muốn + LastLogin
$result = foreach ($row in $act) {
# Lấy UPN từ report
$upn = $row.'User Principal Name'; if (-not $upn) { $upn = $row.UserPrincipalName }
if (-not $upn) { continue }
$u = $uLookup[$upn.ToLower()]
# License readable (lọc null, fallback SkuId)
$licNames = ""; $licIds = ""
if ($u -and $u.AssignedLicenses -and $u.AssignedLicenses.Count -gt 0) {
$ids = @($u.AssignedLicenses | ForEach-Object { $_.SkuId.Guid } | Where-Object { $_ -and $_ -ne [guid]::Empty })
if ($ids.Count -gt 0) {
$licIds = [string]::Join(';', $ids)
$names = @()
foreach ($id in $ids) {
if ($skuMap.Count -gt 0 -and $skuMap.ContainsKey($id)) { $names += $skuMap[$id] } else { $names += $id }
}
if ($names.Count -gt 0) { $licNames = [string]::Join(';', $names) }
}
}
# LastLogin = max ngày hoạt động trong report
$vals = foreach ($c in $activityCols) { if ($row.PSObject.Properties.Name -contains $c) { $row.$c } }
$last = Get-LatestDate $vals
$lastFmt= if ($last) { ([datetime]$last).ToString("dd/MM/yyyy HH:mm:ss") } else { "" }
# Tạo record theo đúng các cột bạn cần + LastLogin
[pscustomobject]@{
'Display name' = $(if ($u) { $u.DisplayName } else { $row.'Display Name' })
'DirSyncEnabled' = $(if ($u) { $u.OnPremisesSyncEnabled } else { "" })
'User principal name' = $upn
'Object Id' = $(if ($u) { $u.Id } else { "" })
'First name' = $(if ($u) { $u.GivenName } else { "" })
'Last name' = $(if ($u) { $u.Surname } else { "" })
'When created' = $(if ($u) { $u.CreatedDateTime } else { "" })
'Soft deletion time stamp' = "" # chỉ có ở Recycle bin report
'Title' = $(if ($u) { $u.JobTitle } else { "" })
'Department' = $(if ($u) { $u.Department } else { "" })
'Preferred data location' = "" # không có trực tiếp trong user object
'City' = $(if ($u) { $u.City } else { "" })
'CountryOrRegion' = $(if ($u) { $u.Country } else { "" })
'Office' = $(if ($u) { $u.OfficeLocation } else { "" })
'StateOrProvince' = $(if ($u) { $u.State } else { "" })
'Usage location' = $(if ($u) { $u.UsageLocation } else { "" })
'Last dirsync time' = "" # có trong AAD Connect report riêng
'Block credential' = $(if ($u -and $u.AccountEnabled -eq $false) {'TRUE'} else {'FALSE'})
'Licenses' = $licNames
'Password never expires' = "" # cần policy riêng nếu muốn lấy
'Last password change time stamp' = "" # yêu cầu P1/P2 nếu lấy từ SignInActivity
'Mobile Phone' = $(if ($u) { $u.MobilePhone } else { "" })
'Phone number' = $(if ($u -and $u.BusinessPhones) { $u.BusinessPhones -join ';' } else { "" })
'Postal code' = $(if ($u) { $u.PostalCode } else { "" })
'Preferred language' = $(if ($u) { $u.PreferredLanguage } else { "" })
'Street address' = $(if ($u) { $u.StreetAddress } else { "" })
'Fax' = "" # không có trường fax
'Proxy addresses' = $(if ($u -and $u.ProxyAddresses) { $u.ProxyAddresses -join ';' } else { "" })
'LastLogin' = $lastFmt
}
}
# 7) Xuất CSV
$out = Join-Path $dir "O365_FullColumns_WithLastLogin.csv"
$result | Export-Csv -Path $out -NoTypeInformation -Encoding UTF8
Write-Host "`n✅ Đã xuất báo cáo đầy đủ: $out"
LastLogin = ngày mới nhất phát hiện hoạt động của user trong 180 ngày (Exchange/OneDrive/SharePoint/Teams/Yammer/Skype…).
Các cột như Soft deletion time stamp, Last dirsync time, Password never expires, Last password change time stamp… không có nguồn trực tiếp khi không dùng P1/P2 hoặc AAD Connect report → mình để trống để giữ đúng layout. Nếu bạn có nguồn các báo cáo đó, mình sẽ merge thêm thành một file duy nhất.
Post a Comment