2020-05-21

自動關閉咬住 User Profile Disk (UPD) 的 Pooled VM 並還原檢查點

有時候會遇到 RDCB Database 與實際連線 (Pooled VM 配發) 不同的情況
比如 User 連上 PooledVM-001,User Profile Disk 也 mount 上了 PooledVM-001
但 RDCB DB 的 rds.session Table 卻因故沒有這筆 Record (目前不確定為什麼會這樣)
則 User 在下一次連線的時候 RDCB 可能會分配給他另一台 Pooled VM 比如 PooledVM-002
而因為 User 的 PRofile Disk 已經 mount 在 PooledVM-001 (Open File)
所以這次 User 進入 PooledVM-002 的時候就無法再掛上他的 Profile Disk

這個時候 User 已經登入的 PooledVM-002 會跟他說【無法登入你的帳號】
事實上只是 Profile Disk 無法 mount 而已
但 User 只會跟你說不能用、連不上這種令你無法理解問題是什麼的說詞
真心認為微軟應該要寫說: 請告訴你的系統管理員找不到你的 Profile
而不是只叫 User 去找系統管理員而已



為了自動解決這個問題,又要寫個自動檢查的程式來處理了
以下 PowerShell Script 要在存放 User Profile Disk 的 Server 上跑

  • 程式先檢查目前被 Open 的 Profile Disk .vhdx 檔案,根據檔名取出 User SID
  • 再與目前連線中的 RDSession 比對
  • 有連線、User Profile Disk File 也有被 Open 的就略過
  • 沒連線,但 User Profile Disk File 卻被 Open 的就進行處理
  • 找出目標 VM Host、找出 User Profile Disk 是被 mount 在哪一台 VM 上
  • 針對那台 VM 進行關機、還原的指令 (需要用到 Invoke-Command 遠端命令)

  • Updated: 2020.05.26
    • 找到 Locked User Profile Disk 後等待一分鐘再查一次兩次都查到相同記錄才進行 Unlock
      避免第一次查的時候遇到 User 正在連線中的問題

$DomainName = "Contoso.com"
$DefaultActivedRDCBMaster = "RDCB.$DomainName"

$DateTimeString = Get-Date -format ("yyyyMMdd HHmmss")
$TranscriptLog = (Get-Item -Path ".\" -Verbose).FullName + "\Logs\" + ("AutoUnlockProfileDisk " + $DateTimeString + ".log")
start-transcript -path $TranscriptLog

# --------------------------
# Function Prepare Start
# --------------------------


Function GetLockedUser([string]$DomainName,[string]$DefaultActivedRDCBMaster) {

$OpenProFiles = @();
foreach ($File in $(Get-SmbOpenFile | select Path,ClientUserName)) {
$File.Path = $File.Path.ToUpper()
if ($File.Path -like '*.VHDX') {
$OpenProFiles  += ((((Split-Path $File.Path -leaf) -replace 'UVHD-','') -replace '.VHDX','') + ';' + ((($File.ClientUserName.ToUpper()) -replace 'CONTOSO\\') -replace '\$',''));
};
};
$SIDs = ($OpenProFiles | Sort-Object | Get-Unique);

$OpenProFiles = @(
foreach ($Item in $SIDs) {
$Item = $Item -split ';';
$SID = $Item[0];
$Server = $Item[1];
[PSCustomObject]@{Server = $Server; Name = (((New-Object System.Security.Principal.SecurityIdentifier($SID)).Translate([System.Security.Principal.NTAccount])).Value).ToUpper(); SID = $SID;};
};
);

$ActiveRDCBMaster = $(Get-RDConnectionBrokerHighAvailability -ConnectionBroker $DefaultActivedRDCBMaster).ActiveManagementServer
$RDSessions = Get-RDUserSession -ConnectionBroker $ActiveRDCBMaster | Select-Object –Property DomainName,UserName,ServerName,HostServer;

# Profile Disk Opened But no RDSession
$LockedProfileDisks = @();
$LockedProfileDisksReport = @();
foreach ($OpenProFile in $OpenProFiles) {
$Hit = 0;
foreach ($RDSession in $RDSessions) {
if ( (($OpenProFile.Name.ToUpper()) -eq ($RDSession.DomainName.ToUpper() + '\' + $RDSession.UserName.ToUpper())) -and ($OpenProFile.Server -eq (($RDSession.HostServer.ToUpper()) -replace ('.' + $DomainName),'')) ) {
$Hit = 1;
break;
};
};
if ($Hit -eq 0) {
$LockedProfileDisks += ($OpenProFile.Server + ';' + $OpenProFile.SID);
};
};
return $OpenProFiles,$RDSessions,$LockedProfileDisks;
};


# --------------------------
# Function Prepare End
# --------------------------

$GetLockedUserReport = GetLockedUser $DomainName $DefaultActivedRDCBMaster
$OpenProFilesReport = $GetLockedUserReport[0];
$RDSessionsReport = $GetLockedUserReport[1];
$LockedProfileDisks = $GetLockedUserReport[2];

$KeepLog = 0;

if ($LockedProfileDisks.Count -gt 0) {
write-host
write-host ("$($LockedProfileDisks.Count) Locked User Profile Disk(s) Found")
write-host
$LockedProfileDisks
write-host
write-host "Now wait 1 minute for second check"
write-host
Start-Sleep 60
$SecondGetLockedUserReport = GetLockedUser $DomainName $DefaultActivedRDCBMaster
$SecondOpenProFilesReport = $SecondGetLockedUserReport[0];
$SecondRDSessionsReport = $SecondGetLockedUserReport[1];
$SecondLockedProfileDisks = $SecondGetLockedUserReport[2];

write-host "Opened User Profile Second Check"
$SecondOpenProFilesReport | Format-Table -AutoSize

write-host "RD Sessions Second Check"
$SecondRDSessionsReport | Format-Table -AutoSize

if ($SecondLockedProfileDisks.Count -gt 0) {
write-host
write-host ("$($SecondLockedProfileDisks.Count) Locked User Profile Disk(s) Found")
write-host
$SecondLockedProfileDisks
write-host
foreach ($Item in $LockedProfileDisks) {
$Item = $Item -split ';';
$LockedOnServer = $Item[0];
$LockedSID = $Item[1];
$Hit = 0;
foreach ($SecondItem in $SecondLockedProfileDisks) {
$SecondItem = $SecondItem -split ';';
$SecondLockedOnServer = $SecondItem[0];
$SecondLockedSID = $SecondItem[1];
if ( ($LockedOnServer -eq $SecondLockedOnServer) -and ($LockedSID -eq $SecondLockedSID) ) {
write-host ("Hit - " + $SecondLockedOnServer + " / " + $SecondLockedSID);
$Hit = 1;
break;
};
};
if ($Hit -eq 1) {
$KeepLog = 1;

$Command = {
param($LockedOnServer,$LockedSID)
$TargetVM = Get-VM | Where {$_.State -eq 'Running'} | Get-VMHardDiskDrive -ControllerType SCSI | Where {$_.Path | Select-String -Pattern $LockedSID -CaseSensitive -SimpleMatch}
$ReturnMessage = "";
$ReturnMessage += ($LockedOnServer + "`t`t" + $TargetVM.VMName + "`n")
$ReturnMessage += ((((New-Object System.Security.Principal.SecurityIdentifier($LockedSID)).Translate([System.Security.Principal.NTAccount])).Value) + "`t`t" + $LockedSID + "`n")
$ReturnMessage += ("Stop VM`t`t`t" + $TargetVM.VMName + "`n")
$ReturnMessage += ($TargetVM.VMName | Stop-VM -Force)
$ReturnMessage += ("Revert VM`t`t" + $TargetVM.VMName + "`n")
$ReturnMessage += ($TargetVM.VMName | Get-VM | Foreach-Object { $_ | Get-VMSnapshot | Sort CreationTime | Select -Last 1 | Restore-VMSnapshot -Confirm:$false })
$ReturnMessage += (write-host "-------------------------------------`n")
$ReturnMessage += ("Start VM`t`t`t" + $TargetVM.VMName + "`n")
$ReturnMessage += ($TargetVM.VMName | Start-VM)
return $ReturnMessage;
};

Invoke-Command -ComputerName $LockedOnServer -ArgumentList $LockedOnServer,$LockedSID -ScriptBlock $Command;

};
};
};

} else {
write-host
write-host "No Locked User Profile Disk"
write-host
};

write-host
write-host "-------------------------------------"
write-host
Stop-Transcript

if ($KeepLog -eq 0) {
remove-item $TranscriptLog -Confirm:$False
};
exit;





以下還不確定用不用得到
是用來檢查沒有 mount Profile Disk 的 Session

# RDSession with no Profile Disk
foreach ($RDSession in $RDSessions) {

$Hit = 0;
foreach ($OpenProFile in $OpenProFiles) {
if ( ($OpenProFile.Server -eq (($RDSession.HostServer.ToUpper()) -replace ('.' + $DomainName),'')) -and (($OpenProFile.Name.ToUpper()) -eq ($RDSession.DomainName.ToUpper() + '\' + $RDSession.UserName.ToUpper())) ) {
$Hit = 1;
break;
};
};
if ($Hit -eq 0) {
write-host ($RDSession.DomainName.ToUpper() + '\' + $RDSession.UserName.ToUpper())
};
};



沒有留言:

張貼留言