假设已将“删除系统重启时早于指定天数的用户配置文件”组策略设置为在系统启动时删除过期的配置文件。 本文提供了一个示例脚本,可帮助确定何时考虑删除配置文件,因为应用组策略。
注意
Windows Management Instrumentation (WMI) 提供程序还可用于配置配置文件处理,并且可以替代组策略设置。 Microsoft System Center Configuration Manager(SCCM)是一个可以执行此操作的软件示例。
适用于: Windows 10 版本 1809、Windows 11、Windows Server 2019 和更高版本的 Windows
详细信息
Windows 10 版本 1809、Windows Server 2019(版本 1809)和更高版本更改配置文件期限的确定方式,并删除对ntuser.dat文件的修改时间戳的依赖。 Windows 现在使用带时间戳的注册表值来表示加载时间、卸载时间和可能的清理时间。 ntuser.dat时间戳现在用作回退决策。
以下注册表值跟踪配置文件服务上次加载配置文件,并用于验证 Unload 和 Cleanup 值:
- LocalProfileLoadTimeLow
- LocalProfileLoadTimeHigh
以下注册表值跟踪配置文件服务上次卸载配置文件:
- LocalProfileUnLoadTimeLow
- LocalProfileUnLoadTimeHigh
以下注册表值跟踪清理时间。 如果其他任一值无效或不存在,并且 ntuser.dat 时间戳在启动期间没有足够的时间删除,并且配置了策略,则从此时间戳计算年龄。 如果用户在设置注册表值时登录,则会删除注册表值,并跟踪将还原到其他值。
- LocalProfileCleanupCheckTimeLow
- LocalProfileCleanupCheckTimeHigh
重要
此示例脚本在任何Microsoft标准支持计划或服务下都不受支持。
示例脚本按原样提供,没有任何保证。 Microsoft进一步否认所有默示担保,包括不限于适销性或针对特定用途的适用性的任何默示担保。
示例脚本和文档的使用或性能导致的整个风险依然存在。 在任何情况下,不得Microsoft、其作者或参与创建、制作或交付脚本的任何其他人对任何损害负责(包括但不限于业务利润损失、业务中断、业务中断、业务信息损失或其他经济损失)因使用或无法使用示例脚本或文档而导致的损害。 即使Microsoft被告知这种损害的可能性。
脚本
Function TranslateSID2Name
{
param(
[string]$sid
)
try
{
$osid = New-Object System.Security.Principal.SecurityIdentifier( $sid )
$osid.Translate([System.Security.Principal.NTAccount])
}
catch
{
"<Unresolved>"
}
}
Function ConvertToDate
{
param(
[uint32]$lowpart,
[uint32]$highpart
)
$ft64 = ( [UInt64]$highpart -shl 32) -bor $lowpart
[datetime]::FromFileTime( $ft64 )
}
Function Get-ProfileLoadUnloadInfo
{
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true)][String] $Name,
[int] $CleanUpDays
)
BEGIN {}
PROCESS{
$SaveErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = "SilentlyContinue"
$date0 = [datetime]::FromFileTime(0)
$profilelistsidkey = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$Name"
$localprofileloadtimelow = 0
$localprofileloadtimelow = Get-ItemPropertyValue -Path $profilelistsidkey -Name LocalProfileLoadTimeLow
$localprofileloadtimehigh = 0
$localprofileloadtimehigh = Get-ItemPropertyValue -Path $profilelistsidkey -Name LocalProfileLoadTimeHigh
$localprofileloadtime = ConvertToDate $localprofileloadtimelow $localprofileloadtimehigh
$localprofileunloadtimelow = 0
$localprofileunloadtimelow = Get-ItemPropertyValue -Path $profilelistsidkey -Name LocalProfileUnLoadTimeLow
$localprofileunloadtimehigh = 0
$localprofileunloadtimehigh = Get-ItemPropertyValue -Path $profilelistsidkey -Name LocalProfileUnLoadTimeHigh
$localprofileunloadtime = ConvertToDate $localprofileunloadtimelow $localprofileunloadtimehigh
$localprofilecleanupchecktimelow = 0
$localprofilecleanupchecktimelow = Get-ItemPropertyValue -Path $profilelistsidkey -Name LocalProfileCleanupCheckTimeLow
$localprofilecleanupchecktimehigh = 0
$localprofilecleanupchecktimehigh = Get-ItemPropertyValue -Path $profilelistsidkey -Name LocalProfileCleanupCheckTimeHigh
$localprofilecleanupchecktime = ConvertToDate $localprofilecleanupchecktimelow $localprofilecleanupchecktimehigh
$profilepath = "-"
$profilepath = Get-ItemPropertyValue -Path $profilelistsidkey -Name ProfileImagePath
$manprofilepath = $profilepath + "\NTUSER.MAN"
$datprofilepath = $profilepath + "\NTUSER.DAT"
$ntusermodified = $date0
if( Test-Path $manprofilepath ){
$ntusermodified = Get-ItemPropertyValue $manprofilepath -Name LastWriteTime
}
elseif( Test-Path $datprofilepath ){
$ntusermodified = Get-ItemPropertyValue $datprofilepath -Name LastWriteTime
}
$ErrorActionPreference = $SaveErrorActionPreference
$determinedfrom = "NtUserDate"
$basedate = $ntusermodified
switch( ($localprofileloadtime, $localprofileunloadtime, $localprofilecleanupchecktime | Measure-Object -Maximum).Maximum )
{
$date0
{
break
}
#$localprofileloadtime
#{
# $determinedfrom = "-" # shouldn't happen since would most likely indicate profile currently loaded or machine crashed
# $basedate = $date0
#}
$localprofileunloadtime
{
$determinedfrom = "UnloadDate"
$basedate = $localprofileunloadtime
}
$localprofilecleanupchecktime
{
$determinedfrom = "CleanupCheckDate"
$basedate = $localprofilecleanupchecktime
}
}
$profileagedays = "?"
if( ($basedate -gt $date0) )
{
$profileagedays = [Math]::Truncate( (New-TimeSpan -Start $basedate -End (Get-Date)).TotalDays )
}
if( ($cleanupdays -gt 0) -and ($basedate -gt $date0) )
{
$cleanupdate = $basedate.AddDays($cleanupdays)
}
else
{
$cleanupdate = $date0
}
[PSCustomObject] @{
Sid = $Name
ProfilePath = $profilepath
LoadDate = $localprofileloadtime
UnLoadDate = $localprofileunloadtime
CleanupCheckDate = $localprofilecleanupchecktime
NTUserDate = $ntusermodified
DeterminedFrom = $determinedfrom
ProfileAgeDays = $profileagedays
CleanupDate = $cleanupdate
}
}
END{}
}
$SaveErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = "SilentlyContinue"
# Begin: Check GPO or WMI controlled and get CleanupProfiles value
$WMIcontrolled = $false
$cleanupvalue = 0
$UserStateRoaming = 0
$UserStateRoaming = Get-ItemPropertyValue -Path "HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion\UserState\UserStateTechnologies\ConfigurationControls" -Name RoamingUserProfile
if( ($UserStateRoaming -eq $null) -or ($UserStateRoaming -eq 0) )
{
# Profiles controlled by GPO, get GPO configured setting if available
$cleanupvalue = Get-ItemPropertyValue -Path "HKLM:Software\Policies\Microsoft\Windows\System" -Name CleanupProfiles
}
else
{
# Profiles controlled by WMI, get setting if configured
$cleanupvalue = Get-ItemPropertyValue -Path "HKLM:Software\Microsoft\Windows\CurrentVersion\UserState\RoamingUserProfile" -Name CleanupProfiles
$WMIcontrolled = $true
}
if( $cleanupvalue -eq $null )
{
$cleanupvalue = 0
}
$cleanupwarning = ""
if( $cleanupvalue -eq 0)
{
$cleanupwarning = " - Value needs to be 1 or larger for profiles to age and be deleted. Check setting and GP application."
}
# End: Check GPO or WMI controlled and get CleanupProfiles value
$ErrorActionPreference = $SaveErrorActionPreference
# Get unloaded profiles information on machine
$profinfo = (gwmi win32_userprofile -Filter "Loaded = 'False' and Special = 'False'").sid | Get-ProfileLoadUnloadInfo -CleanUpDays $cleanupvalue | Sort-Object -Property @{E="CleanupDate"; Descending = $false}, @{E="ProfileAgeDays"; Descending = $true}
$Date0 = [datetime]::FromFileTime(0)
"Current Date........: $((Get-Date).ToString())"
"Run By..............: $($env:USERDOMAIN)\$($env:USERNAME)"
$OSInfo = Get-ComputerInfo CsName,OsName,WindowsVersion,OsVersion,OsLastBootUpTime,OsUptime
"Machine Name........: " + $OSInfo.CsName
"Operating System....: " + $OSInfo.OsName
"Windows Version.....: " + $OSInfo.WindowsVersion + " (" + $OSInfo.OsVersion + ")"
$ProfsvcdllVersionInfo = (Get-Item $env:windir\system32\profsvc.dll).VersionInfo
"Profsvc.dll Version.: $($ProfsvcdllVersionInfo.FileMajorPart).$($ProfsvcdllVersionInfo.FileMinorPart).$($ProfsvcdllVersionInfo.FileBuildPart).$($ProfsvcdllVersionInfo.FilePrivatePart)"
"Last boot time......: " + $OSInfo.OsLastBootUpTime.ToString() + " (Up time: " + $OSInfo.OsUptime + ")"
"-" * 21
"Profile Cleanup Days: $cleanupvalue$cleanupwarning"
"Controlled by WMI...: $WMIcontrolled"
"Profile count.......: $($profinfo.count)"
$profoutput = $profinfo | Select-Object `
Sid, `
@{L="User"; E={TranslateSID2Name $_.Sid }}, `
ProfilePath, `
@{L="LoadDate"; E={if($_.LoadDate -gt $Date0){ $_.LoadDate } else {"-"}}}, `
@{L="UnLoadDate"; E={if( $_.UnLoadDate -gt $Date0 ){ $_.UnLoadDate } else {"-"}}}, `
@{L="CleanupCheckDate"; E={if( $_.CleanupCheckDate -gt $Date0 ){ $_.CleanupCheckDate } else {"-"}}}, `
@{L="NtUserDate"; E={if( $_.NtUserDate -gt $Date0 ) { $_.NtUserDate } else { "-" }}}, `
DeterminedFrom, `
ProfileAgeDays, `
@{L="CleanupDate"; E={if( $_.CleanupDate -gt $Date0 ) { $_.CleanupDate } else { "-" }}}
# Different output options
#$profoutput | fl *
$profoutput | ft * # NOTE: may not give all columns depending on window width.
#$profoutput | Out-GridView
#$profoutput | Export-Csv -Path ProfileAge.csv -NoTypeInformation
示例输出
下面是脚本的示例输出。 可以根据窗口的宽度截断表格输出,因此信息的格式可以根据需要更改为其他可用格式:
Current Date........: 8/21/2024
Run As..............: DOMAIN\username
Machine Name........: COMPUTERNAME
Operating System....: Microsoft Windows 10
Windows Version.....: <version>
Profsvc.dll Version.: <version of profsvc.dll>
Last boot time......: <uptime of the machine>
---------------------
Profile Cleanup Days: 30
Controlled by WMI...: False
Profile count.......:
Sid User ProfilePath LoadDate UnLoadDate CleanupCheckDate NtUserDate DeterminedFrom ProfileAgeDays
--- ---- ----------- -------- ---------- ---------------- ---------- -------------- -------------
S-1-5-21-xxxxxxxxxx-xxxxxxxxxx-xxxxxxxxx-xxx DOMAIN\username C:\Users\username 11.07.2024 13:19:07 11.07.2024 13:20:11 - 11.07.2024 13:20:11 UnloadDate 61