סדר בפרופילים
דרך מעניינת לעשות סדר בפרופילים, להמיר את ה Roaming Profiles למאין שילוב של Roaming Profile ו- Redirection של תיקיות מסוימות. המטרה של כל העניין היא להפעיל quota על הנתונים שנשמרים ע"י המשתמשים בעבודת על גבי חוות ה Citrix\TS.
מצב קיים
כל המשתמשים עובדים עם Roaming Profile היושב על גבי ה storage המרכזי של הארגון (netapp), אין מחיקת פרופילים ביציאה ואין הגבלה על גודל הפרופיל.
הבעיה
הפרופילים גודלים בלי שליטה. נעשה נסיון להפעיל את ההגדרה של Limit Profile Size ב GPO, אבל ההגדרה דרך ה GPO היא בעיתית, ואני אסביר.
ניקח לדוגמא שהגבלתי את הפרופילים ל 10mb, כל עוד הוא ב session, המשתמש יוכל להוסיף לפרופיל שלו כל כמות מידע שהוא רק רוצה, הוא רק ייקבל הודעה כאשר הוא יחרוג מההגבלה הנ"ל, בנוסף לכך, הוא לא יהיה מסוגל לעשות logoff מה session כל עוד הוא עובר את ההגבלה. אבל, יש כאן דרך להתחכמות (וכידוע לכולנו, המשתמשים רכשו לעצמם שם לא רע בתחום הזה עם השנים), נגיד ובאמת המשתמש רושם לפרופיל שלו מעבר להגבלה שמוגדרת לו ב GPO - למשל מעתיק קובץ של 40mb ל Desktop - הוא באמת יקבל הודעה שהוא חרג מההגבלה שלו וכמובן שלא יהיה מסוגל לבצע logoff, אבל הוא כן יוכל לעשות disconnect מה session שלו, לחכות את פרק הזמן שבו ה session ינותק מהשרת ולהכנס בחזרה. כמובן שהפרופיל שלו יכיל את כל המידע (כולל המידע מעבר להגבלה) והוא יוכל לכתוב עוד חומר נוסף, ולחזור כמובן על הקומבינה בפעם הבאה.
אבל אם נעזוב לרגע את זה שאפשר לאכוף את ההגבלה, מה שיותר מפריע זה שכל עוד המשתמש נמצא ב session הוא יכול לכתוב כמה שהוא רק רוצה לתיקיית הפרופיל שלו. כאמור, זה רק מתריע ומונע logoff.
הגדרת הפיתרון
לאחר מחשבה מעמיקה (אני רוצה refill לוויינשטפן בבקשה) החלטנו לפצל את הפרופיל לשני חלקים מרכזיים. Desktop, My Documents, Favorites, Cookies, אותן תיקיות שלמשתמש יש אינטרקציה ישירה איתן, כלומר, כל הכתיבות לתיקיות הנ"ל נעשות ע"י המשתמש ולא ע"י תהליכים השקופים לו (טוב נו, חוץ מה Cookies). אותן ארבעת התיקיות ישבו ב share משלהם על גבי ה storage בו יוגדר quota (ב netapp) ויהיו מוגדרות ב redirection לפרופיל, ישבו ב netapp\ctx_redirect\\. תצורה זו לא תאפשר למשתמש לחרוג מהגדרת ה quota שלו גם אם הוא ב session.
כל שאר חלקי הפרופיל (application data, ntuser.dat וכו') ימשיכו לעבוד כ roaming profile (אשר קטן בצורה דרסטית עקב ה redirection של ארבעת התיקיות הנ"ל) במקום חדש, netapp\ctx_profiles\\.
מה תכלס עשינו
במצב ההתחלתי כל ה Roaming profiles ישבו ב Share הבא: netapp\ctx_profiles\\ ומתחת ישבו התיקיות של כל משתמש. כמובן, שהיסטורית סרר שם בלגן לא קטן (פרופילים ישנים, גדולים מדי, דפוקים ועוד).
כדי להגדיר את חלוקת הפרופיל לתצורה החדשה, הגדרנו את ההגדרות הבאות ב GPO (יצרנו חדש, אשר יכיל רק את ההגדרות האלה):
User Configuration-->Windows Settings-->Folder Redirection
כאמור, מיפוי של Desktop ו- My Documents ל- netapp\ctx_redirect\username\\.
מכיוון שתיקיית Favorites ו- Cookies לא כלולות בתיקיות הניתנות ל redirection ב default הינו צריכים להוסיף Policy Template בו הוספנו את התיקיות Cookies ו- Favorites:
CLASS USER
CATEGORY "Citrix Redirection"
CATEGORY "System"
POLICY "+ User Shell Folders"
KEYNAME "Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders"
PART "--------------------------------------------------------------------------" TEXT
END PART
PART "Cookies" EDITTEXT
VALUENAME "Cookies"
DEFAULT "%USERPROFILE%\Cookies"
REQUIRED
#if VERSION >= 2
EXPANDABLETEXT
#endif
END PART
PART "Favorites" EDITTEXT
VALUENAME "Favorites"
DEFAULT "%USERPROFILE%\Favorites"
REQUIRED
#if VERSION >= 2
EXPANDABLETEXT
#endif
END PART
END POLICY ; + User Shell Folders
END CATEGORY ; System
END CATEGORY ; Citrix Extension
כמובן, שגם התי התיקיות האלה הופנו ל netapp\ctx_redirect\username\\.
את הקישור לתיקיית ה Roaming Profiles החדשה עשינו ע"י הפוליסי הבא:
Computer Configuration-->Administrative Templates-->Windows Components-->Terminal Services-->Set path for TS Roaming Profiles: \\netapp\ctx_profiles\username
מכיוון שרצינו מעבר חלק של המשתמשים בין התצורה הישנה (Roaming profile פשוט) לתצורה החדשה (שילוב של Redirection ו- Roaming profile) הינו צריכים להעתיק את התיקיות הרלוונטיות מה Roaming Profile הקיים לתיקיות החדשות, כאמור, Desktop, Favorites, Cookies, My Documents ל- netapp\ctx_redirect\username\\ וכל שאר הפרופיל ל- netapp\ctx_profiles\username\\.
כל זה נעשה ע"י הסקריפט הבא:
<package>
<job id="Sessions">
<comment>
</comment>
<runtime>
<description>
</description>
<example>
</example>
</runtime>
<reference object="MetaFrameCOM.MetaFrameFarm"/>
<script language="VBScript">
Dim theFarm, aSession, SessionState, users
SessionState = Array("Unknown", _
"Active", _
"Connected", _
"Connecting", _
"Shadowing", _
"Disconnected", _
"Idle", _
"Listening", _
"Resetting", _
"Down", _
"Init")
'
' Create MetaFrameFarm object
'
Set fs = WScript.CreateObject("Scripting.filesystemobject")
Set users = fs.opentextfile("c:\users.txt",1)
Set theFarm = CreateObject("MetaFrameCOM.MetaFrameFarm")
if Err.Number <> 0 Then
WScript.Echo "Can't create MetaFrameFarm object"
WScript.Echo "(" & Err.Number & ") " & Err.Description
WScript.Echo ""
WScript.Quit Err.Number
End if
'
' Initialize the farm object.
'
theFarm.Initialize(MetaFrameWinFarmObject)
if Err.Number <> 0 Then
WScript.Echo "Can't Initialize MetaFrameFarm object"
WScript.Echo "(" & Err.Number & ") " & Err.Description
WScript.Echo ""
WScript.Quit Err.Number
End if
'
' Are you Citrix Administrator?
'
if theFarm.WinFarmObject.IsCitrixAdministrator = 0 then
WScript.Echo "You must be a Citrix admin to run this script"
WScript.Echo ""
WScript.Quit 0
End If
'
' Print out the farm name.
'
WScript.Echo "MetaFrame Farm Name: " & theFarm.FarmName
WScript.Echo ""
'
' Display all sessions in the farm.
'
WScript.Echo "All sessions in the farm (" & Now & ")"
WScript.Echo "------------------------------------------------"
Do
b_temp = false
u_temp = users.ReadLine
For Each aSession In theFarm.Sessions
if Err.Number <> 0 Then
WScript.Echo "Can't enumerate sessions"
WScript.Echo "(" & Err.Number & ") " & Err.Description
WScript.Echo ""
WScript.Quit Err.Number
End if
If u_temp = aSession.username Then
b_temp = True
End if
Next
If not b_temp Then
wscript.echo u_temp & " are not logged in"
Call Find_folder(u_temp)
'Return = WshShell.Run(ROBO, 0, true)
End If
loop While users.AtEndOfStream <> true
Function Find_folder (user)
Set fs = WScript.CreateObject("Scripting.filesystemobject")
Set objFolder = fs.GetFolder("\\netapp\ctx_profiles\")
Set colSubfolders = objFolder.Subfolders
Set WshShell = WScript.CreateObject("WScript.Shell")
For Each objSubfolder In colSubfolders
If InStr(objSubfolder.name, user)And NOT InStr(objSubfolder.name, (user & ".TESTDOMAIN")) Then
date1 = objSubfolder.DateLastModified
folder1 = objSubfolder.name
'WScript.echo objSubfolder.name & " " & objSubfolder.DateLastModified
'WScript.Echo InStr(objSubfolder.name, user)
End If
If InStr(objSubfolder.name, (user & ".TESTDOMAIN")) = 1 Then
date2 = objSubfolder.DateLastModified
folder2 = objSubfolder.name
'WScript.echo " blabla " & objSubfolder.name & " " & objSubfolder.DateLastModified
'WScript.Echo InStr(objSubfolder.name, (user & ".TESTDOMAIN"))
End If
Next
If date1>date2 Then
WScript.Echo user & " " & date1 & "without TESTDOMAIN"
rob = "c:\robocopy.exe \\netapp\ctx_profiles\" & folder1 & " \\netapp\ctx_profiles\" & user & ".TESTDOMAIN /E /SEC /XD dirs desktop Favorites ""My Documents"" Cookies"
return = WshShell.run(rob , 0, True)
return = WshShell.run("c:\robocopy.exe \\netapp\ctx_profiles\" & folder1 & "\Desktop \\netapp\ctx_riderect\" & user & ".TESTDOMAIN\Desktop /E /COPYALL" , 0, True)
return = WshShell.run("c:\robocopy.exe \\netapp\ctx_profiles\" & folder1 & "\Favorites \\netapp\ctx_riderect\" & user & ".TESTDOMAIN\Favorites /E /COPYALL" , 0, True)
return = WshShell.run("c:\robocopy.exe \\netapp\ctx_profiles\" & folder1 & "\Cookies \\netapp\ctx_riderect\" & user & ".TESTDOMAIN\Cookies /E /COPYALL" , 0, True)
return = WshShell.run("c:\robocopy.exe ""\\netapp\ctx_profiles\" & folder1 & "\My Documents"" ""\\netapp\ctx_riderect\" & user & ".TESTDOMAIN\My Documents"" /E /COPYALL" , 0, True)
End If
If date2>date1 Then
WScript.Echo user & " " & date2 & " TESTDOMAIN"
rob = "c:\robocopy.exe \\netapp\ctx_profiles\" & folder2 & " \\netapp\ctx_profiles\" & user & ".TESTDOMAIN /E /COPYALL /XD dirs desktop Favorites ""My Documents"" Cookies"
return = WshShell.run(rob , 0, True)
return = WshShell.run("c:\robocopy.exe \\netapp\ctx_profiles\" & folder2 & "\Desktop \\netapp\ctx_riderect\" & user & ".TESTDOMAIN\Desktop /E /COPYALL" , 0, True)
return = WshShell.run("c:\robocopy.exe \\netapp\ctx_profiles\" & folder2 & "\Favorites \\netapp\ctx_riderect\" & user & ".TESTDOMAIN\Favorites /E /COPYALL" , 0, True)
return = WshShell.run("c:\robocopy.exe \\netapp\ctx_profiles\" & folder2 & "\Cookies \\netapp\ctx_riderect\" & user & ".TESTDOMAIN\Cookies /E /COPYALL" , 0, True)
return = WshShell.run("c:\robocopy.exe ""\\netapp\ctx_profiles\" & folder2 & "\My Documents"" ""\\netapp\ctx_riderect\" & user & ".TESTDOMAIN\My Documents"" /E /COPYALL" , 0, True)
End If
End Function
</script>
</job>
</package>
הסבר קצר על מה קורה כאן בכלל, הסקריפט עובר על כל השורות בקובץ users.txt המכיל את המשתמשים אשר עשו logoff מהחווה.
כל משתמש הרשום בקובץ נבדק האם הוא כרגע נמצא ב login בחווה, אם לא, נעשת העתקה של התיקיות המדוברות למיקומים החדשים. לא מבוצעת שום העתקה אם המשתמש ב login.
מכיוון שחלק מהמשתמשים מחזיקים ב Roaming Profile עם סיומת שם הדומיין (כל אלה שקיבלו את ההגדרה מה GPO) וחלק בלי סיומת (כאלה שמקבלים את ההגדרה מה AD) הינו צריכים למצוא את הפרופיל המעודכן ביותר (בגלל זה ההשוואה בין date1 לבין date2).
אחרי פעולת הסקריפט, הנתיבים החדשים יהיו מעודכנים עם הנתונים הרלוונטיים של המשתמשים, מה שיאפשר החלפה חלקה של ה GPO לתצורה החדשה.
*תודה למושי על ההשקעה.