So because of how the Windows Task Scheduler works, I always use two batchfiles for scheduled tasks. My naming convention in this case is:
- checkStudentsRecentusers.cmd
- checkStudentsRecentusers-scheduledTask.cmd
The problem with the Windows Task scheduler is that once you've created a task that invokes a batchfile, you can't change that batchfile any more. So I create a batchfile for the scheduler that invokes my normal batchfile with a single line (and any required parameters):
c:\scripts\scheduled\checkStudentsRecentusers.cmd diff nopause
My checkStudentsRecentusers.cmd batchfile has the folowing content:
@ECHO OFF
CLS
ECHO ********************************
ECHO ** Check Students Recentusers **
ECHO ********************************
pwsh -ExecutionPolicy Bypass -File c:\scripts\scheduled\ps\checkStudentsRecentusers.ps1 %1
if /I "%2" NEQ "NoPause" @Pause
The reason why I need a batchfile is because of the ExecutionPolicy.
And here's the powershell script I use to check the students:
$action = $args[0]
$file = "c:\csv-output\blockedStudents.csv"
$GAM = "c:\scripts\gam\gam.exe"
write "File: $file"
write "Action: $action"
$action = "diff"
function writeCSV {
$timestamp = get-date -format 'dd/MM/yyyy hh:mm tt'
#write "$index $($crosSerialNumber),$($crosStatus),$($crosLocation),$($crosAssetID),$($crosUser),$($crosRecentUser),$($crosNotes)"
add-content $file "$($timestamp),$($crosSerialNumber),$($crosStatus),$($crosLocation),$($crosAssetID),$($crosUser),$($crosRecentUser),$($crosNotes)"
}
function disableUser {
if ( $crosRecentUser ) {
$groups = &$GAM print groups member $crosRecentUser
$User = Get-ADUser -Filter {Emailaddress -eq $crosRecentUser} | select-object samaccountname -expandproperty samaccountname
if ( $groups -like "*allstudents@tmc*" ) {
write "Disabling $($crosRecentUser)"
writeCSV
Disable-ADAccount -Identity $User
&$GAM update user $crosRecentUser suspended on
&$GAM update cros $crosDeviceID notes "$($crosRecentUser):$today"
}
}
}
function clearNotes {
if ( $crosNotes ) {
write "Clearing notes on $($crosAssetID) as assigned user and recent user are the same."
&$GAM update cros $crosDeviceID notes ""
}
}
function checkRecentuser {
write "Checking $($crosAssetID)"
if ( $crosUser -ne $crosRecentUser ) {
write "Checking $($crosRecentUser) on $($crosAssetID)"
if ( -not $crosNotes ) {
write "No existing notes"
disableUser
} else {
write "Existing notes, checking"
$notes = $crosNotes -split ':'
if ( $notes[1] -lt $today ) {
if ( $notes[0] -ne $crosRecentUser ) {
write "Notes was for a previous day"
disableUser
}
}
}
} else { clearNotes }
}
function checkChromebooks {
$chromebooksY7 = &$GAM print cros fields serialnumber,status,location,assetid,user,recentusers,notes listlimit 1 limit_to_ou "/Devices/Secondary/Y07"
$chromebooksY8 = &$GAM print cros fields serialnumber,status,location,assetid,user,recentusers,notes listlimit 1 limit_to_ou "/Devices/Secondary/Y08"
$chromebooksY9 = &$GAM print cros fields serialnumber,status,location,assetid,user,recentusers,notes listlimit 1 limit_to_ou "/Devices/Secondary/Y09"
$chromebooksY10 = &$GAM print cros fields serialnumber,status,location,assetid,user,recentusers,notes listlimit 1 limit_to_ou "/Devices/Secondary/Y10"
$chromebooksY11 = &$GAM print cros fields serialnumber,status,location,assetid,user,recentusers,notes listlimit 1 limit_to_ou "/Devices/Secondary/Y11"
$chromebooksY12 = &$GAM print cros fields serialnumber,status,location,assetid,user,recentusers,notes listlimit 1 limit_to_ou "/Devices/Secondary/Y12"
$chromebooks = $chromebooksY7 + $chromebooksY8 + $chromebooksY9 + $chromebooksY10 + $chromebooksY11 + $chromebooksY12
$today = get-date -format yyyyMMdd
write $chromebooks
foreach ($chromebook in $chromebooks) {
$index = $chromebooks.indexof($chromebook)
$data = $chromebook -split ','
if ( $data[0] -eq "deviceId" ) {
$indexDeviceID = [array]::indexof($data,"deviceId")
$indexStatus = [array]::indexof($data,"status")
$indexSerialNumber = [array]::indexof($data,"serialNumber")
$indexUser = [array]::indexof($data,"annotatedUser")
$indexLocation = [array]::indexof($data,"annotatedLocation")
$indexAssetID = [array]::indexof($data,"annotatedAssetId")
$indexRecentuser = [array]::indexof($data,"recentUsers.email")
$indexNotes = [array]::indexof($data,"notes")
continue
}
$crosDeviceID = $data[$indexDeviceID]
$crosStatus = $data[$indexStatus]
$crosSerialNumber = $data[$indexSerialNumber]
$crosUser = $data[$indexUser]
$crosLocation = $data[$indexLocation]
$crosAssetID = $data[$indexAssetID]
$crosRecentUser = $data[$indexRecentuser]
$crosNotes = $data[$indexNotes]
if ( $index -ne 0 ) {
if ( $crosStatus -eq 'ACTIVE' ) {
if ( $action -eq "diff" ) {
checkRecentuser }
else { writeCSV }
}
}
}
}
checkChromebooks
We have our students and chromebooks separated in OU's per year. Devices live in the /Devices/Primary/Y* or /Devices/Secondary/Y* OU's, students live in the /Students/Primary/Y* or /Students/Secondary/Y* OU's. We only check our Secondary (highschool) student chromebooks for mismatching recentusers.
checkChromebooks is the main function, it basically retrieves all chromebooks for each year in arrays and then builds one large array. Doing this helps with debugging.
Then the function parses each individual chromebook record in the array and checks the recentuser, and if the recentuser is different from the assigned user, it suspends the user account and logs this in a csv file (set in $file, you'll have to change that accordingly).
The same goes for the $GAM variable which you'll need to set to the location of gam.exe.
Once a user is disabled, the notefield is set which ensures the script doesn't check this user again on the same day. That way we don't have to manually clear notes and we keep a little record in the user account.
I can't remember what other actions I wanted to do, the script is currently hardcoded to "diff", which checks if the recentuser is different. We've been running this script for a few years now.
Because students know they'll get locked out, very few try to use someone elses laptop.