<?xml version="1.0"?>
<rss version="2.0"><channel><title/><link>https://www.edugeek.net/blogs/blog/887-duke5as-blog/</link><description/><language>en</language><item><title>Move Cisco Wireless Control Server (WCS) to a New Windows Install</title><link>https://www.edugeek.net/blogs/entry/1154-move-cisco-wireless-control-server-wcs-to-a-new-windows-install/</link><description><![CDATA[<p>Just documenting this in case some other poor soul is stuck running this garbage and needs to move it from one Windows Server install to another...</p><p> </p><p>
I have the bad luck of still having to support this and it was originally loaded on a 2003 x86 server that was upgraded in place to 2008 x86.  It's now 2019 and server 2008 will no longer be getting security updates past January 2020.  There is no migration path from 2008 x86 to something newer.  So, WCS needs to be either moved or reloaded onto something newer.  The original plan was to spin up a fresh load and import the configuration backups.  Herein lies the problem...even if you have the original installer for WCS it does two OS checks and won't proceed unless it detects server 2003.   The first check can be bypassed simply by using compatibility settings on the self extracting exe, but the second check is performed by an installer written in Java.  So this was a dead end, or so I thought.</p><p> </p><p>
Turns out the entire thing is written in Java and served out via Apache.  For the most part it is very self contained with everything residing in its own install folder.</p><p> </p><p>
Do this...</p><p> </p><p>
</p><ol style="list-style-type:decimal;"><li>Copy the WCS install folder off of the old server<br />
</li><li>Copy Start Menu program group<br />
</li><li>Export the registry keys for its two services (Apache and WCS)<br />
</li><li>Load new server (I used 2019) and give it the same hostname as the old server<br />
</li><li>Copy the install folder back to the new server<br />
</li><li>Copy the Start Menu group over<br />
</li><li>Import the registry keys<br />
</li><li>Open 443 and 80 on the local firewall if it's running<br />
</li><li>Start the services<br />
</li><li>Give it a couple minutes to catch its tail and open it up in a web browser<br />
</li><li>Done!<br />
</li></ol><p></p><p> </p><p>
Only four things of note:</p><p>
</p><ul><li>I kept the original file pathing the same between both servers<br />
</li><li>It's imperative you use the same hostname on the new server as the license will fail<br />
</li><li>The service controls in the Start Menu program group need to be run as an administrator or they won't work.  Probably a UAC thing...<br />
</li><li>I'm running WCS 7.0.  Your mileage may vary with other versions.<br />
</li></ul><p></p><p> </p><p>
If you look around the net everyone will basically tell you not to try and Cisco had their reasons for only supporting 2003.  Well, it's crap...  The entire thing is written in Java.  The whole point of that is it'll run on a toaster if you wanted it to.  The whole thing stinks of planned obsolescence.  Basically the same thing as the expiring built-in certs in the access points.</p><p> </p><p>
I hope this helps someone out there....</p>]]></description><guid isPermaLink="false">1154</guid><pubDate>Fri, 02 Aug 2019 14:49:42 +0000</pubDate></item><item><title>Remote Control - TightVNC Wrapper</title><link>https://www.edugeek.net/blogs/entry/1147-remote-control-tightvnc-wrapper/</link><description><![CDATA[<p><a class="ipsAttachLink ipsAttachLink_image" href="https://www.edugeek.net/uploads/monthly_2019_04/ZenControl.JPG.8e199b5b43dccc32fc9e82e3322c642a.JPG" data-fileid="377" data-fileext="jpg" rel=""><img class="ipsImage ipsImage_thumbnailed" alt="ZenControl.JPG.8e199b5b43dccc32fc9e82e3322c642a.JPG" data-fileid="377" data-src="https://www.edugeek.net/uploads/monthly_2019_04/ZenControl.JPG.8e199b5b43dccc32fc9e82e3322c642a.JPG" src="https://www.edugeek.net/applications/core/interface/js/spacer.png" /></a></p><p> </p><p>
This is a utility that I wrote in VB .Net 2010 a number of years ago.  It's basically a wrapper application for TightVNC that allows me to remotely control client computers in the domain (think Team Viewer).  The inspiration for this came from an older application called 'Gen Control' that basically does the exact same thing, but only worked for Windows XP and earlier.  As of this writing I can verify that it works on all version of Windows Vista through 10 1809.  </p><p> </p><p> </p><p>
This is what it does in a nutshell:</p><p> </p><p>
</p><ol style="list-style-type:decimal;"><li>Writes configuration registry keys to client's registry<br />
</li><li>Copies TightVNC server binary and DLL to client via admin share<br />
</li><li>Registers TightVNC as service on the client<br />
</li><li>Calls the TightVNC viewer on the support technician's computer and connects to client<br />
</li><li>Monitors for closing of TightVNC viewer<br />
</li><li>Unregisters TightVNC server service on client computer<br />
</li><li>Removes registry keys from client<br />
</li><li>Deletes files that were copied over<br />
</li><li><br />
</li></ol><p></p><p> </p><p>
Some security concerns I took into account when writing this:</p><p> </p><p>
</p><ul><li>Passwords for the TightVNC session are randomly generated for every connection<br />
</li><li>When the server service is registered on the client the startup mode is set to manual<br />
</li><li>In the event of a loss of connection before the cleanup can begin a script is placed in startup that will perform the cleanup the next time the client is restarted<br />
</li></ul><p></p><p> </p><p>
Requirements:</p><p> </p><p>
</p><ul><li>Remote Computer Management is enable and the port open via GPO<br />
<p></p><div style="margin-left:25px;">Computer Configuration / Admin Templates / Windows Components / Windows Remote Management (WinRM)/WinRM Service: Allow remote server management through WinRM</div><p><br /> <br />
</p></li><li>Remote Registry Service is started and the port open via GPO<br />
<p></p><div style="margin-left:25px;">Computer Configuration / Policies / Windows Settings / Security Settings / System Services: Remote Registry</div><p><br />
</p><p></p><div style="margin-left:25px;">Enabled and set to automatic startup</div><p><br /> <br />
</p></li><li>Port 5900 is open to incoming connections on client firewall<br />
</li><li>Secure Attention Sequence needs to be enable via GPO to send CTRL-ALT-DEL to clients via TightVNC<br />
<p></p><div style="margin-left:25px;">Computer Configuration / Admin Templates / Windows Components / Windows Logon Options: Disable or enable software Secure Attention </div><p><br />
</p><p></p><div style="margin-left:25px;">Enabled and limited to services</div><p><br /> <br />
</p></li><li>The account being used to run this application from needs to have local admin credentials on clients you're attempting to connect to<br />
</li></ul><p></p><p> </p><p>
There are options to to change ports, run ping tests before attempting connections, block user input from the client end, and other things.</p><p> </p><p>
When opening ports on the client computers through GPO be sure to do it on the domain profile on the firewall config.  This way if you're working with laptops then when the device goes offsite those firewall exceptions are closed.</p><p> </p><p>
Attached to this blog post is the source code and compiled binary.  The compiled binary can be found here: .\ZenControl v2\ZenControl\bin\Debug</p><p> </p><p>
Any questions or comments can be dropped here.  I haven't updated this code in quite some time and there might be some bugs, but it is actively used by my department.  This handy utility has saved me from having to make plenty of trips to remote buildings in the dead of Winter.  I hope this helps someone out there....</p><p> </p><p>
<a href="https://www.edugeek.net/applications/core/interface/file/attachment.php?id=378" data-fileid="378" data-fileext="7z" rel="">ZenControl v2.7z</a></p>]]></description><guid isPermaLink="false">1147</guid><pubDate>Mon, 15 Apr 2019 15:34:43 +0000</pubDate></item><item><title>Power Shell Script for Automatic User Creation in AD</title><link>https://www.edugeek.net/blogs/entry/1118-power-shell-script-for-automatic-user-creation-in-ad/</link><description><![CDATA[<p>#Updated 2018/08/30: </p><p>
</p><ul><li>Fixed a bug where it didn't count the number of newly created users correctly.<br />
</li><li>Fixed another bug with the continue statement not working as intended when finding an invalid grade<br />
</li></ul><p></p><p> </p><p> </p><p>
I wrote this Power Shell script to automate user creation in Active Directory.  It's run off of a scheduled task and maintains a log file of everything it does. It handles pretty much everything:</p><p> </p><p>
</p><ol style="list-style-type:decimal;"><li>Copies the export and names by date, also names log by date for easy sorting and retention for debugging if need be<br />
</li><li>Rudimentary checking of the export file for date and columns<br />
</li><li>Username formatting<br />
</li><li>User creation and OU placement<br />
</li><li>Group membership<br />
</li><li>Home folder creation<br />
</li><li>Home folder persmissions<br />
</li></ol><p></p><p> </p><p> </p><p>
The Export looks like this:</p><p> </p><p>
</p><pre class="ipsCode">
PSNumber	Lastname	Firstname	Grade
1000001000	O'Neill	Jack	12
1000001001	Carter	Samantha	9
1000001002	Jackson	Daniel	11
1000001003	Mal Doran	Vala	11
1000001004	McKay	Rodney	12
1000001005	Hammond	George	12
1000001006	Quinn	Jonas	10
1000001007	Mitchell	Cameron	12
</pre><div></div><p></p><p> </p><p>
You'll have to change a few things to fit your environment, but this makes for a good starting point.  The script is designed with an OU structure that breaks students down into graduation years.</p><p> </p><p>
-Students</p><p>
  -2018</p><p>
    +Student A</p><p>
    +Student B</p><p>
  -2017</p><p>
  -2016</p><p>
  -2015</p><p>
    +Student C</p><p> </p><p>
Every student is a member of their respective graduation year group: Students_2018, Students_2017, etc.</p><p> </p><p>
Account name is the first six letters of the last name, followed by the last four digits of their student number.  The script will also remove some special characters from the names that AD could gripe about.</p><p> </p><p>
I hope this helps...</p><p> </p><p>
-Mark</p><p> </p><p>
</p><pre class="ipsCode">
#Define editable strings
$WorkingDir = "script path goes here"
$ADDomain = "Domain Name goes here"
$Password = "student"
$BaseOU = "ou=Grad Year,ou=Students,ou=Your User OU,dc=domain name,dc=com"
$BaseHome = "\\yourfileserver\students$\"
$HomeDrive = "H:"
$UserCount = 0

#Define non-editable strings
$LogPath = $WorkingDir + "Log $($LogDate).txt"
$LogDate = Get-Date -UFormat "%Y-%m-%d.%H.%M.%S"
$PSExportPath = $WorkingDir + "student.export.text"
$UserExportPath = $WorkingDir + "User $($LogDate).txt"
$TimeStart = Get-Date

#Create the log file and write the header - file named with date
"Processing started on $($TimeStart)" | Out-File $LogPath -append 
"--------------------------------------------" | Out-File $LogPath -append 
"" | Out-File $LogPath -append 

#Load the Active Directory module
Try{Import-Module ActiveDirectory -ErrorAction Stop}
Catch{
   "[CRITICAL] Active Directory module not loaded! Aborting." | Out-File $LogPath -append 
   "`t $($_.Exception.ItemName)" | Out-File $LogPath -Append
   "`t $($_.Exception.Message)" | Out-File $LogPath -Append
   Exit
}

#Check for existance of user export file
$FileExists = Test-Path $PSExportPath 
If ($FileExists -eq $False){
   "[CRITICAL] User export not found! Aborting." | Out-File $LogPath -append 
   Exit
}

#Check age of user export file and stop script if more than 12 hours old
$PSExportFile = Get-Item $PSExportPath
$Limit = (Get-Date).AddHours(-12)
If ($PSExportFile.LastWriteTime -lt $Limit){
   "[CRITICAL] User export is more than 12 hours old! Aborting." | Out-File $LogPath -append 
   Exit
}

#Copy automated user export file to another name that includes the date
#this preserves the file along with the log, both named by date for debugging purposes
Copy-Item $PSExportPath $UserExportPath | Out-Null

#Import CSV and bust down using a tab delimiter - script stops if this fails
Try{$Users = Import-Csv -Delimiter "`t" -Path $UserExportPath -ErrorAction Stop}
Catch{
   "[CRITICAL] Export not formatted properly! Aborting." | Out-File $LogPath -append
   "`t $($_.Exception.ItemName)" | Out-File $LogPath -Append
   "`t $($_.Exception.Message)" | Out-File $LogPath -Append 
   Exit
}

#Used to determine fiscal year - most US schools operate on a 9 month calendar year
#because of our SIS system the graduation year is derived from the current year and the student's grade level
#anything after the month of June is considered to be the next calendar year
If ($TimeStart.Month -gt 7) {$FiscalYear = $TimeStart.Year + 1}
Else {$FiscalYear = $TimeStart.Year}


#Function where the magic happens
Function AddUsers{

   #Step through each line of the export loaded into $Users
   :nextUser foreach ($User in $Users) {

       $i++

       #Assign each column it's own variable
       $UserFirstname = $User.First_Name
       $UserLastname = $User.Last_Name
       $PSNumber = $User.Student_Number
       $Grade = $User.Grade_Level

       #Check for blank entries in any of the columns - if any are found then log it and go back to the top of the loop, skipping this user line
       If (($UserFirstname -eq "") -Or ($UserLastname -eq "") -Or ($PSNumber -eq "") -Or ($Grade -eq "")){

           "[ERROR] Information missing.  Check source line $($i)." | Out-File $LogPath -Append
           Continue nextUser
       }

       #Remove special characters that don't play nice in AD from user names
       $UserFirstname = $UserFirstname.Replace(".","")
       $UserFirstname = $UserFirstname.Replace(" ","")
       $UserFirstname = $UserFirstname.Replace("'","")
       $UserFirstname = $UserFirstname.Replace("-","")

       $UserLastname = $UserLastname.Replace(".","")
       $UserLastname = $UserLastname.Replace(" ","")
       $UserLastname = $UserLastname.Replace("'","")
       $UserLastname = $UserLastname.Replace("-","")

       #Generate user's login name - syntax for our environment is first six characters of last name followed by last four digits of student number
       If ($UserLastname.Length -lt 6){
           $SAM = $UserLastname + $PSNumber.Substring($PSNumber.Length -4,4)
       }
       ElseIf($UserLastname.Length -ige 6){
           $SAM = $UserLastname.Substring(0,6) + $PSNumber.Substring($PSNumber.Length -4,4)
       }

       #Generate strings for other pieces of user information
       $Displayname = $UserFirstname + " " + $UserLastname
       $UPN = $SAM + "@" + $ADDomain
      
       #Make sure that grade is within proper bounds - if not, write to the log and continue back to the top of the loop skipping this user from further processing
       Switch ($Grade) {

           12 {$GradYear = $FiscalYear + 0}
           11 {$GradYear = $FiscalYear + 1}
           10 {$GradYear = $FiscalYear + 2}
           9 {$GradYear = $FiscalYear + 3}
           8 {$GradYear = $FiscalYear + 4}
           7 {$GradYear = $FiscalYear + 5}
           6 {$GradYear = $FiscalYear + 6}
           5 {$GradYear = $FiscalYear + 7}
           4 {$GradYear = $FiscalYear + 8}
           3 {$GradYear = $FiscalYear + 9}
           2 {$GradYear = $FiscalYear + 10}
           1 {$GradYear = $FiscalYear + 11}
           0 {$GradYear = $FiscalYear + 12}
           Default {"[ERROR] Grade not valid.  Check source line  $($i)." | Out-File $LogPath -Append
           Continue nextUser}

           }

       #Generate some more stuff and populate variables
       $HomeFolder =  $BaseHome + $GradYear + "\" + $SAM
       $OU = "ou=" + $GradYear + "," + $BaseOU
       $GroupName = "Students_" + $GradYear

       #Try block to check for existance of user - if this fails then log it and continue back to the top of the loop, skipping this user from further processing
       Try   {$UserExists = Get-ADUser -LDAPFilter "(sAMAccountName=$SAM)"}
       Catch {
               "[ERROR] Unabled to check for duplicate user $(SAM)  Source line  $($i)." | Out-File $LogPath -Append
               "`t $($_.Exception.ItemName)" | Out-File $LogPath -Append
               "`t $($_.Exception.Message)" | Out-File $LogPath -Append
               Continue nextUser
       }

       #If user doesn't exist, create said user
       If(!$UserExists){
           
           #Try block to create user - if this fails then log it and continue back to the top of the loop, skipping this user from further processing
           Try{

               New-ADUser -Name $SAM -DisplayName $Displayname -SamAccountName $SAM -UserPrincipalName $UPN -GivenName $UserFirstname -Surname $UserLastname -HomePhone $PSNumber -Description $GradYear -AccountPassword (ConvertTo-SecureString $Password -AsPlainText -Force) -Enabled $true -ChangePasswordAtLogon $true -PasswordNeverExpires $false -Path $OU -HomeDirectory $HomeFolder -HomeDrive $HomeDrive -ErrorAction Stop

           }
           Catch{
               "[ERROR] User $($SAM) not created.  Source line  $($i)." | Out-File $LogPath -Append
               "`t $($_.Exception.ItemName)" | Out-File $LogPath -Append
               "`t $($_.Exception.Message)" | Out-File $LogPath -Append
               Continue nextUser
           }

           #Count the number of users added for later use in the log
           $Global:UserCount = $Global:UserCount + 1
           
           "[sUCCESS] User $($SAM) created.  Source line  $($i)." | Out-File $LogPath -Append

           #Try block to add newly created user to the appropriate student graduation group - if this fails then log it and continue back to the top of the loop, skipping this user from further processing
           Try{Add-ADGroupMember -Identity $GroupName -Members $SAM -ErrorAction Stop}
           Catch{
               "[ERROR] User $($SAM) not added to group $($GroupName).  Source line  $($i)." | Out-File $LogPath -Append
               "`t $($_.Exception.ItemName)" | Out-File $LogPath -Append
               "`t $($_.Exception.Message)" | Out-File $LogPath -Append
               Continue nextUser
           }

           "[sUCCESS] User $($SAM) added to group $($GroupName).  Source line  $($i)." | Out-File $LogPath -Append

           #Try block for creating user's home folder and setting permissions - if this fails then log it and continue back to the top of the loop, skipping this user from further processing
           Try{
               New-Item -ItemType "Directory"  -Path $HomeFolder -ErrorAction Stop
               $Acl = (Get-Item $HomeFolder).GetAccessControl('Access')
               $Ar = New-Object System.Security.AccessControl.FileSystemAccessRule($SAM, "Modify",'ContainerInherit,ObjectInherit', 'None', 'Allow')
               $Acl.SetAccessRule($Ar)
               Set-Acl -path $HomeFolder -AclObject $Acl
               }
           Catch{
           "[ERROR] User $($SAM) home folder messed up.  Source line  $($i)." | Out-File $LogPath -Append
           "`t $($_.Exception.ItemName)" | Out-File $LogPath -Append
           "`t $($_.Exception.Message)" | Out-File $LogPath -Append
           Continue nextUser
           }
           
           "[sUCCESS] User $($SAM) home folder created.  Source line  $($i)." | Out-File $LogPath -Append
       }
       Else{
           "[iNFO] User $($SAM) already exists.  Source line  $($i)." | Out-File $LogPath -Append
           Continue nextUser
       }
   }
   #Give a half second between creating users to let AD catch its tail
   Start-Sleep -Milliseconds 500
}

Addusers

#Write statistics to the end of the log
$TimeEnd = Get-Date
$ElapsedTime = $TimeEnd - $TimeStart
"" | Out-File $LogPath -Append
"Processing Finished on: $($TimeEnd)" | Out-File $LogPath -Append
"Total processing time:  $($ElapsedTime)"  | Out-File $LogPath -Append
"Users created:          $($UserCount)"  | Out-File $LogPath -Append

#Debug function
Function Debug {
   Write-Host "Username:    "  $SAM
   Write-Host "OU:          "  $OU
   Write-Host "Grad Year:   "  $GradYear
   Write-Host "Home Folder: "  $HomeFolder
   #Write-Host $UPN
   #Write-Host $GApps
   Write-Host
}
</pre><div></div><p></p>]]></description><guid isPermaLink="false">1118</guid><pubDate>Mon, 16 Apr 2018 19:58:54 +0000</pubDate></item><item><title>'The Dude' Network Monitor</title><link>https://www.edugeek.net/blogs/entry/1075-the-dude-network-monitor/</link><description><![CDATA[<p>The Dude is free SNMP poller from Mikrotik.  The Windows version was recently discontinued and replaced with a newer one (still free) that only runs on their proprietary Router OS.  Even though it was free, it is easily the best network monitor that I have used.  Here is the version that has the Windows installable server piece.</p><p> </p><p>
<a href="https://www.edugeek.net/applications/core/interface/file/attachment.php?id=369" data-fileid="369" data-fileext="zip" rel="">dude-install-4.0beta3.zip</a></p>]]></description><guid isPermaLink="false">1075</guid><pubDate>Mon, 17 Oct 2016 20:59:03 +0000</pubDate></item><item><title>Making WSUS Public Facing for Remote Users</title><link>https://www.edugeek.net/blogs/entry/1064-making-wsus-public-facing-for-remote-users/</link><description><![CDATA[<p>So how does one get security and software updates out to remote users?  My school has three criteria that set this problem into motion: our teachers all have laptops, they're allowed to take said laptops home, and we're on a 9 month calendar year.  So, for the staff that elect take their laptops home that means they're offsite for almost three months.  That is a long time to go with no security updates...</p><p> </p><p>
Beginning to look at ways of solving this problem I saw that some admins had come up with scripts that detect whether or not the system is on the domain network, and if not it would revert Windows Update to look to Microsoft rather than the internal WSUS server.  This would work for most, but I just don't use WSUS for delivering Windows security updates - I also use it to update Chrome, Flash, Adobe Reader and a few others using <a href="https://wsuspackagepublisher.codeplex.com/" rel="external nofollow">WSUS Package Publisher/</a> (auto update for said software is disabled in base image).  The ability for remote users then to reach the internal WSUS server is pretty much a necessity then at this point unless I wanted to change my process (which I didn't).</p><p> </p><p>
Before setting this there needs to be some prerequisites:</p><p> </p><p>
</p><ul><li>Your internal domain name doesn't have a made up TLD like .local or .corp<br />
</li><li>You own your domain name on the outside<br />
</li><li>You have a certificate authority<br />
</li><li>A functioning WSUS install<br />
</li></ul><p></p><p> </p><p>
The above isn't absolute and there are ways around them, but this is how I set it up on my network.  And since this is going to be exposed on the outside, you need to do move forward with security in mind.  We will be enabling SSL and locking down some sections of WSUS.</p>]]></description><guid isPermaLink="false">1064</guid><pubDate>Wed, 01 Jun 2016 18:08:05 +0000</pubDate></item><item><title>Setting Higher Than Supported Resolutions on Netbooks</title><link>https://www.edugeek.net/blogs/entry/1035-setting-higher-than-supported-resolutions-on-netbooks/</link><description><![CDATA[<p>It's rather common knowledge now that you can enable down scaling (if the hardware supports it) in Windows to increase resolution beyond the max supported by a display.  It basically involves searching for every REG_DWORD <em>Display1_DownScalingSupported</em> and changing the value to one.  The problem I ran into was I needed to do this to a multitude of different model netbooks and the key name changes between models since it is named by GUID.  So a simple registry export/import wouldn't work on dissimilar models.  Here is a simple batch file solution:</p><p> </p><p>
</p><pre class="ipsCode">
 <a contenteditable="false" rel="" href="https://www.edugeek.net/profile/15239-echo/" data-mentionid="15239" data-ipshover-target="https://www.edugeek.net/profile/15239-echo/?do=hovercard" data-ipshover="">@echo</a> on

set KEY=HKLM
Set VALUE=Display1_DownScalingSupported


for /f  %%A in ('reg query %KEY% /v %VALUE% /s ^| findstr HKEY') do (
reg add %%A /v "Display1_DownScalingSupported" /t REG_DWORD /d 1 /f
</pre><div></div><p></p><p> </p><p>
The above will enable you to select 1024x768 on a system that only supports 1024x600.  The aspect ratio will be off, but if you have applications that check for resolution requirements then this will get you past that.  After the keys are changed the system needs to be rebooted.</p><p> </p><p>
This was successfully used on Dell Latitude 2100 and 2120 netbooks to get past the 1024x768 resolution requirement for the NWEA Locked Down Browser.</p>]]></description><guid isPermaLink="false">1035</guid><pubDate>Tue, 21 Jul 2015 13:01:35 +0000</pubDate></item><item><title>VB Script to set Java security level to medium</title><link>https://www.edugeek.net/blogs/entry/916-vb-script-to-set-java-security-level-to-medium/</link><description><![CDATA[<p>Long story short, Java started including an expiration timer on JRE releases.  So many days after a release, regardless of update settings, a dialog will start popping up asking the user to update.  This really isn't an issue, but with it JRE will stop running unsigned code.  Many sites that my teaching staff have come to rely on have unsigned Java apps, and keeping over 500 frozen wireless laptops up to date every time a new version of JRE is released isn't really feasible.  So enters VB script to get around the issue.</p><p> </p><p>
The code below is a sub from my logon script that deals with this issue.  The setting the keeps Java from running said unsigned code is the security level and is stored in the <em>deployment.propterties</em> text file in each user's local profile.  The default level is high and the line is not present in the config.  Adding the line will force JRE into medium the next time it is launched.  Since this is plucked out of my logon script you'll need a source for the username, but other than this it'll work as is for Vista/7.  For 2k/XP you'll need to modify the path.</p><p> </p><p>
</p><pre class="ipsCode">
Sub TempFixes5

	Set objFSO = CreateObject("Scripting.FileSystemObject")

	Const ForReading = 1
	Const ForWriting = 2

	FoundIt = False

	strUserFilePath = "c:\Users\" &amp; strUserName &amp; "\AppData\LocalLow\Sun\Java\Deployment\deployment.properties"
	strSecurityMedium = "deployment.security.level=MEDIUM"

	'Check to see if Java is installed, if not, then exit sub
	If objFSO.FileExists(strUserFilePath) Then
		'Do Nothing
	Else
		Exit Sub
	End If

	'Check to see if Java security is set to medium, if so, then exit sub
	FoundIt = (InStr(1,objFSO.OpenTextFile(strUserFilePath,1,true,-2).ReadAll,strSecurityMedium,1) &lt;&gt; 0)
	If FoundIt = True Then
		Exit Sub
	Else
		'Do Nothing
	End If

	'Open Java configuration files for reading
	Set objUserFile = objFSO.OpenTextFile(strUserFilePath, ForReading)

	'Copy file contents into string
	strUserFile = objUserFile.ReadAll

	'Add medium security line to config
	strNewUserFile = strUserFile &amp; Chr(13) &amp; strSecurityMedium
	
	'Open Java configuration files for writing
	Set objUserFile = objFSO.OpenTextFile(strUserFilePath, ForWriting)

	'Write the files
	objUserFile.WriteLine strNewUserFile

	'Removes Java configuration file from memory
	objUserFile.Close

	'House cleaning
	Set objFSO = Nothing


End Sub
</pre><div></div><p></p>]]></description><guid isPermaLink="false">916</guid><pubDate>Mon, 28 Oct 2013 14:45:43 +0000</pubDate></item><item><title>VB Script to disable the Java IE plugin</title><link>https://www.edugeek.net/blogs/entry/820-vb-script-to-disable-the-java-ie-plugin/</link><description><![CDATA[<p>Java has been an extreme pain in the neck as of late with the constant updates.  Many sources are calling to uninstall it, but unfortunately many of us don't have the luxury of doing that.  So the next best thing besides pushing out a stream of updates (that are already exploitable out of the box) you can do is to disable the Internet Explorer plugin.  The ability to run Java within a browser is something that isn't needed in my district; all that is needed is for Java Web Start to handle the JNLP file association.</p><p> </p><p>
Now, I just figured I could write an ADM template and do this through GPO, but the values you need to changed are stored under a key named according to the version of Java installed.  So writing a template would only be good for the version you wrote it for.  Scripting was next viable alternative and this one will handle any version of Java installed on the system.</p><p> </p><p>
</p><pre class="ipsCode">
Const HKEY_CLASSES_ROOT = &amp;H80000000
Const HKEY_CURRENT_USER = &amp;H80000001
Const HKEY_LOCAL_MACHINE = &amp;H80000002
Const HKEY_USERS = &amp;H80000003
Const HKEY_CURRENT_CONFIG = &amp;H80000005

strComputer = "."

Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" &amp; strComputer &amp; "\root\cimv2")
Set colOS = objWMIService.ExecQuery("Select * from Win32_OperatingSystem")

For Each OS in colOS
strOSArch = OS.OSArchitecture
Next

If InStr(strOSArch, "64") Then
WriteKeys HKEY_LOCAL_MACHINE,"Software\Wow6432Node\JavaSoft\Java Plug-in\",strComputer
WriteKeys HKEY_LOCAL_MACHINE,"Software\JavaSoft\Java Plug-in\",strComputer
Else
WriteKeys HKEY_LOCAL_MACHINE,"Software\JavaSoft\Java Plug-in\",strComputer
End if



Function WriteKeys(HKEY_LOCAL_MACHINE, strKeyPath, strComputer)

Set objReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" &amp; strComputer &amp; "\root\default:StdRegProv")
objReg.EnumKey HKEY_LOCAL_MACHINE, strKeyPath, arrSubKeys

For Each SubKey in arrSubKeys
	objReg.SetDWORDValue HKEY_LOCAL_MACHINE,strKeyPath &amp; SubKey, "UseJava2IExplorer", 0
	objReg.SetDWORDValue HKEY_LOCAL_MACHINE,strKeyPath &amp; SubKey, "UseNewJavaPlugin", 0
Next

End Function
</pre><div></div><p></p><p> </p><p>
Set it up as a startup script in GPO and you should be all set.  I hope this helps...</p>]]></description><guid isPermaLink="false">820</guid><pubDate>Mon, 04 Mar 2013 19:50:10 +0000</pubDate></item><item><title>VB Script to snoop DHCP logs</title><link>https://www.edugeek.net/blogs/entry/808-vb-script-to-snoop-dhcp-logs/</link><description><![CDATA[<p>A forum post got me to thinking about a script to parse DHCP logs to notify someone when a particular MAC checks in with the server.  You can run this script as a reoccurring task on the DHCP server and when it finds a match for the desired MAC it will send an email regarding the activity.  This script will also keep a log of its own to prevent multiple email messages for the same log entry it finds.  I've run this in a production environment with an internally hosted Exchange server and it works.  You may need to debug a little depending on your environment.</p><p> </p><p>
The <em>On Error Resume Next</em> line in the email function is to prevent the script from hanging if there is an error with the mail server.  If that were to happen without it there instances of wscript.exe would keep spawning and never exit each time the scheduled task would fire.</p><p> </p><p>
</p><pre class="ipsCode">
Call Main

Sub Main
'MAC to find
strFindMAC = "xxxxxxxxxxxx"

Set objFSO = CreateObject("Scripting.FileSystemObject")

Const ForReading = 1
Const ForWriting = 1

'Determine log name based on day of week
strLogPath = "c:\windows\system32\dhcp\"
strWeekDay = weekdayname(weekday(date))
strFullLogPath = strLogPath &amp; "DhcpSrvLog-" &amp; Left(strWeekday,3) &amp; ".log"

'Check to see if log exists
If objFSO.FileExists(strFullLogPath) Then
	'Do Nothing
Else
	WScript.Quit
End If

'Open DHCP log file for reading
Set objDHCPLogFile = objFSO.OpenTextFile(strFullLogPath, ForReading)
	
'Copy file contents into string
strDHCPLogFile = objDHCPLogFile.ReadAll
objDHCPLogFile.Close

'Break into individual lines
arrLines = Split(strDHCPLogFile,vbCrLf)

'Loop through the array and build another array of lines that contain the MAC
For Each strLine in arrLines
	If InStr(strLine, strFindMAC) Then
		intCount = intCount + 1
		ReDim Preserve arrFound(intCount)
		arrFound(intCount) = strLine
	End If
Next

'If no matches are found then abort the script
If intCount = 0 Then
	WScript.Quit
End If

'Get the last line of matching MAC log entries
strLastLine = arrFound(intCount)

'Split the log entry on commas and dump to array
arrLastLine = Split(strLastLine, Chr(44))

'Format message
strDate = arrLastLine(1)
strTime = arrLastLine(2)
strAction = arrLastLine(3)
strIPAddress = arrLastLine(4)
strHostName = arrLastLine(5)

strMessage = "MAC address " &amp; strFindMAC &amp; " has checked into the DHCP server on " &amp; strDate &amp; " at " &amp; strTime &amp; "." &amp; Chr(13) _
			 &amp; strIPAddress &amp; " was " &amp; strAction &amp; "ed" &amp; " to hostname " &amp; strHostName &amp; "."


'This next block of code will read/write a log for the script itself.  This will prevent the script from sending multiple
'emails for the same DHCP log entry.

'Check to see if script log exists
If objFSO.FileExists("DHCPScriptLog.txt") Then
		'Read log and get the time
		Set objScriptLogFile = objFSO.OpenTextFile("DHCPScriptLog.txt", ForReading)
		strLastTime = objScriptLogFile.ReadAll
		objScriptLogFile.Close
		
		'If the last time matches the time pulled from the DHCP log then quit the script
		If strLastTime = strTime Then
			WScript.Quit
		End If
End If
	
'Write the log file
Set objScriptLogFile = objFSO.CreateTextFile("DHCPScriptLog.txt", ForWriting)
objScriptLogFile.Write strTime
objScriptLogFile.Close

'Call the email function to send the message
Email(strMessage)

End Sub

Function Email(strMessage)

On Error Resume Next

Set objMessage = CreateObject("CDO.Message")
objMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2 
objMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "mail.domain.com"
objMessage.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25 
objMessage.Configuration.Fields.Update

objMessage.Subject = "DHCP Log Script"
objMessage.From = "DHCPScript@domain.com"
objMessage.To = "you@domain.com"
objMessage.TextBody = strMessage

objMessage.Send

End Function
</pre><div></div><p></p>]]></description><guid isPermaLink="false">808</guid><pubDate>Fri, 01 Feb 2013 16:39:13 +0000</pubDate></item><item><title>Squid Proxy and Youtube for Schools</title><link>https://www.edugeek.net/blogs/entry/782-squid-proxy-and-youtube-for-schools/</link><description><![CDATA[<p>Getting Youtube for Schools to work is something that is either easy, or an extreme PITA; which is entirely dependent upon what you use for a proxy.  If you're reading this, then you know Squid falls in the ladder of those two scenarios.</p><p> </p><p>
There are two ways to go about doing it: adding a custom HTTP header, or rewriting the URL on the fly.  The cleanest way is using the custom HTTP header, but as of Squid 3.1 it doesn't natively support adding custom headers.  You can only change existing ones.  To do this you need to setup an ICAP server and it seemed a bit overkill for this simple task, so I elected to go for the URL rewriting.  The basic gist of this is that your school's filter ID needs to be appended onto the end of every URL.  This is accomplished by means of either a perl or PHP script that Squid calls out to do the rewrite.  Seeing as how I'm a VB monkey and couldn't wrap my head around Perl in the time allotted for this task so I cheated. </p><p> </p><p>
Following Youtube's guidelines I wrote the script in VB to give me the desired output and used a converter to go to Perl.  It needed some tweaks, but it seems to work.  Just replace the filter ID with your own in the script and you should be set.</p><p> </p><p>
</p><pre class="ipsCode">
#!/usr/bin/perl
$|=1;

$strSchoolID = 'edufilter=xxxxxxxxxxxxxxxxxx';

while (&lt;&gt;) {
@X = split;
$strURL = $X[0];

if ((index($strURL, 'youtube.com') + 1)) {
   if ((index($strURL, 'edufilter') + 1)) {
       print $strURL, "\n";
   }
   elsif ((index($strURL, '.css') + 1)) {
       print $strURL, "\n";
   }
   elsif ((index($strURL, '.gif') + 1)) {
       print $strURL, "\n";
   }
   elsif ((index($strURL, '.png') + 1)) {
       print $strURL, "\n";
   }
   elsif ((index($strURL, 'gif') + 1)) {
       print $strURL, "\n";
   }
   elsif ((index($strURL, '.js') + 1)) {
       print $strURL, "\n";
   }
   elsif ((index($strURL, '.xml') + 1)) {
       print $strURL, "\n";
   }
   elsif ((index($strURL, '?') + 1)) {
       $strURL = $strURL . '&amp;' . $strSchoolID;
       print "$strURL\n";
   }
   else {
       $strURL = $strURL . '?' . $strSchoolID;
       print "$strURL\n";
   }
}
else {
   print "$strURL\n";
}
}
</pre><div></div><p></p><p> </p><p>
The Squid configuration should look something like this:</p><p> </p><p>
</p><pre class="ipsCode">
url_rewrite_program /etc/squid3/helper/urlrewrite3.pl
redirect_rewrites_host_header on
redirect_children 30
acl RedirectYoutube dstdomain .youtube.com
redirector_access allow RedirectYoutube
redirector_access deny all
</pre><div></div><p></p><p> </p><p>
By default Squid will pass every outgoing connection through the script, so I set an ACL to only pass connections going to Youtube.  And don't forget to set execute permission on the script file.</p><p> </p><p>
I haven't pushed this out to an entire building yet, but it works with my test group so far.  If you find any bugs with this drop me a line.  I hope this helps!</p>]]></description><guid isPermaLink="false">782</guid><pubDate>Fri, 14 Dec 2012 16:24:00 +0000</pubDate></item><item><title>Getting the UUID from a running Windows machine, or a computer's AD object</title><link>https://www.edugeek.net/blogs/entry/753-getting-the-uuid-from-a-running-windows-machine-or-a-computers-ad-object/</link><description><![CDATA[<blockquote data-ipsquote="" class="ipsQuote" data-ipsquote-username="Duke5A" data-cite="Duke5A" data-ipsquote-contentapp="blog" data-ipsquote-contenttype="blogs" data-ipsquote-contentid="753" data-ipsquote-contentclass="blog_Entry"><div>I was having issues with this too using WDS.  The UUID is stored in the <em>netbootguid</em> property in the machine's computer object.  If you were ever curious enough to want to find out what it is you can use this script:<p> </p><p>
</p><pre class="ipsCode">
strLDAPComp = "LDAP://CN=machinename,OU=example computers,DC=example,DC=com"

Set objComp = GetObject(strLDAPComp)

Msgbox "AD UUID:" &amp; ConvertObjectGuidToString(objComp.netbootguid)

Function ConvertObjectGuidToString(ByVal arrRawObjectGUID)
     Dim i, strByte
     Dim arrObjectGUID(15)
     For i = 1 To LenB(arrRawObjectGUID)
           strByte = Hex(AscB(MidB(arrRawObjectGUID, i, 1)))
           If Len(strByte) = 1 Then strByte = "0" &amp; strByte
           arrObjectGUID(i - 1) = strByte
     Next
     ConvertObjectGuidToString = Join(arrObjectGUID, "")
End Function
</pre><div></div><p></p><p> </p><p>
To get the UUID from a Windows box (replace the . in the computer name string with a netbios name to get it from a networked Windows box) that is running, you can use this script:</p><p> </p><p>
</p><pre class="ipsCode">
'http://www.tech-archive.net/Archive/Scripting/microsoft.public.scripting.vbscript/2006-07/msg00078.html
'http://araihan.wordpress.com/2010/02/19/how-to-set-computer-naming-policy-in-windows-deployment-services-wds/
'http://www.experts-exchange.com/OS/Microsoft_Operating_Systems/Server/2003_Server/Q_21909382.html

'On Error Resume Next

Const wbemFlagReturnImmediately = &amp;h10
Const wbemFlagForwardOnly = &amp;h20

strComputer = "."

Set objWMIService = GetObject("winmgmts:\\" &amp; strComputer &amp; "\root\CIMV2")
Set colItems = objWMIService.ExecQuery("SELECT * FROM Win32_ComputerSystemProduct", "WQL", wbemFlagReturnImmediately + wbemFlagForwardOnly)

For Each objItem In colItems
Msgbox "UUID: " &amp; objItem.UUID
Next
</pre><div></div><p></p><p> </p><p>
I can't take a whole lot of credit for these as they were pieced together from sources around the net.  Only the second script had comments in it with sources (it's been a while since I've had to use these).  The UUID stored in AD actually has a part of it reversed and that is what the function in the first script fixes to make it human readable.</p></div></blockquote>]]></description><guid isPermaLink="false">753</guid><pubDate>Tue, 25 Sep 2012 15:27:39 +0000</pubDate></item><item><title>VB Script for network printer deployment</title><link>https://www.edugeek.net/blogs/entry/744-vb-script-for-network-printer-deployment/</link><description><![CDATA[<p>VB script may be showing its age, but it's still incredibly useful.  Printer deployment at my district is handled by means of a VB script that collects various bits of information that is then used to determine who gets what printers; said bits of information include: username, user group membership, user LDAP path, AD site name, computer name, computer group membership, and computer LDAP path.  Obviously you don't need to use all of this, but this script will still pull it out for you and you can customize to your needs.</p><p> </p><p>
The sample script below I left in debug mode.  You can run as is and it'll bring up the collected information in message boxes.  In addition, the sub that pulls group membership calls out to a function that will in turn call back on itself for every nested group it finds.  This will add nested group membership to the final results.  I hope this helps...</p><p> </p><p>
Edit: The strExpectedPrtName string is derived from the computer name.  Most machines in the district are named by building, room, and then number.  For instance, a teacher computer in room 12 in building A would be: BDA-RM12-TW01.  Classroom printers are named the same way, just minus the workstation designation, so it would be: BDA-RM12.  So the at the end of the printer sub it'll attempt to add a printer with the name it derived from the classroom workstation name.  If it doesn't exist, it'll simply skip over it an finish normally due to the error handling.</p><p> </p><p>
</p><pre class="ipsCode">
Dim strUserName
Dim strUserPath
Dim strADSite
Dim strCompName
Dim strCompPath
Dim strGroups
Dim strUserGroups
Dim strCompGroups

Call InitializeScript()
Call GetGroupMemberships
Call Debug
'Call Printers


Sub InitializeScript()
Set objNetwork = CreateObject("WScript.Network")
Set objADSysInfo = CreateObject("ADSystemInfo")

strUserName = objNetwork.UserName
strUserPath = "LDAP://" &amp; objADSysInfo.UserName
strADSite =  objADSysInfo.SiteName
strCompName = objNetwork.ComputerName
strCompPath = "LDAP://" &amp; objADSysInfo.ComputerName	    
End Sub

Sub Debug
Msgbox "Username: " &amp; strUserName
Msgbox "User LDAP Path: " &amp; strUserPath
Msgbox "AD Site: " &amp; strADSite
Msgbox "Computer Name: " &amp; strCompName
Msgbox "LDAP Computer Path: " &amp; strCompPath
Msgbox "User is a member of the following groups: " &amp; strUserGroups
MsgBox "Computer is a member of the following groups:" &amp; strCompGroups
End Sub

Sub GetGroupMemberships
Set objUser = GetObject(strUserPath)
   Set colUserGroups = objUser.Groups

Set objComp = GetObject(strCompPath)
   Set colCompGroups = objComp.Groups

   For Each objUserGroup in colUserGroups
       strGroups = strGroups &amp; objUserGroup.CN &amp; "|"
       GetNested(objUserGroup)
   Next
	
	strUserGroups = strGroups
	strGroups = ""
	
For Each objCompGroup in colCompGroups
       strGroups = strGroups &amp; objCompGroup.CN &amp; "|"
	GetNested(objCompGroup)
   Next

	strCompGroups = strGroups
	strGroups = ""
	
End Sub

Function GetNested(objGroup)
   On Error Resume Next

   colMembers = objGroup.GetEx("memberOf")

   For Each strMember in colMembers
       strPath = "LDAP://" &amp; strMember
       Set objNestedGroup = GetObject(strPath)
       strGroups = strGroups &amp; objNestedGroup.CN &amp; "|"
       GetNested(objNestedGroup)
   Next	
End Function	

Sub Printers
Set objNetwork = CreateObject("WScript.Network")

If Len(strCompName) &lt; 5 Then
	strExpectedPrtName = ""
Else
	Position = InStr(5, StrCompName, "-") - 1
	If Position &lt; 0 Then Position = 5
	strExpectedPrtName = LCase(Left(strCompName, Position))
	intCompNumber = Right(strCompName,2)
End If

If InStr(LCase(strCompPath), "BuildingA") Then

	strPrintServer = "\\BldA-PrtSrv01\"
	objNetwork.AddWindowsPrinterConnection strPrintServer &amp; "BldA-Copier"
	objNetwork.SetDefaultPrinter strPrintServer &amp; "BldA-Copier"
	
ElseIf InStr(LCase(strCompPath), "BuildingB") Then

	strPrintServer = "\\BldB-PrtSrv01\"

	objNetwork.AddWindowsPrinterConnection strPrintServer &amp; "BldB-Copier"

	If InStr(LCase(strUserGroups), "staff") Then
		objNetwork.AddWindowsPrinterConnection strPrintServer &amp; "BldB-OfficeCopier"
		objNetwork.AddWindowsPrinterConnection strPrintServer &amp; "BldB-MediaCopy"
	End If

	If InStr(LCase(strUserGroups), "student") Then
		objNetwork.AddWindowsPrinterConnection strPrintServer &amp; "BldB-LibPrt01"
		objNetwork.AddWindowsPrinterConnection strPrintServer &amp; "BldB-LibPrt02"
	End If
End If

On Error Resume Next

objNetwork.AddWindowsPrinterConnection strPrintServer &amp; strExpectedPrtName

If Err.Number &lt;&gt; 0 Then
	'Nothing to see here folks, move along.
	Exit Sub
Else
	objNetwork.SetDefaultPrinter strPrintServer &amp; strExpectedPrtName
End If

End Sub
</pre><div></div><p></p>]]></description><guid isPermaLink="false">744</guid><pubDate>Mon, 20 Aug 2012 12:48:49 +0000</pubDate></item><item><title>VB script for creating student drop boxes</title><link>https://www.edugeek.net/blogs/entry/740-vb-script-for-creating-student-drop-boxes/</link><description><![CDATA[<p>A lot of our staff use drop boxes pretty heavily for student assignments.  A drop box is pretty much a share that looks somewhat like this:</p><p> </p><p>
</p><pre class="ipsCode">
-Building 01
  -Staff Member A
    +Drop Off
    +Pickup
 +Staff Member B
 +Staff Member C
 +Staff Member D
+Building 02
</pre><div></div><p></p><p> </p><p>
Every staff member of a particular building (or group) will have a folder on a share named by their last name, and under said staff name will be two addition folders.  One for students to pickup assignments, and the other for them to drop off, or turn finished assignments in.  To keep the folder structure intact and to keep students from sharing their assignments with others, file level permissions are set.  This is where this gets a bit goofy...</p><p> </p><p>
For this to work correctly, you're going to need two security groups: one for building level staff, and a second for the students (ours are done by grade level).  At the root of the building folder both security groups have read and execute to allow them to traverse the folder structure.  At the pickup level, the specific teacher will have Read/Write on sub folders and files only so they can manage files without being able to delete the pickup folder itself.  The drop off folder though is a bit more intricate.  We can't allow students read and list to this or they'll be able open other student's completed assignments, and since the students group has read/execute at the building level, we'll need to block inheritance on this folder.  Once inheritance is blocked, the students group is given create files/write data only.  Teachers are given the same permissions as on the pickup folder: RW on sub folders and files only.  Now all the students can do is blindly save files to this folder.</p><p> </p><p>
That's pretty much the gist of drop off and pickup folders, but creating these by hand would be an enormous time sink, so I wrote a VB script that will handle bulk creation.  This script will pull teacher names from an OU in Active Directly, and will pass off permission settings to icacls.  All you have to do is create a fresh building folder on a share somewhere, edit the strings at the top of the script and let it go.</p><p> </p><p>
Cheers!</p><p> </p><p>
</p><pre class="ipsCode">
strLDAPPath = "LDAP://OU=BuildingA,OU=Staff,OU=Organization,DC=sampledomain,DC=com"
strUNCPath = "\\server\Dropbox\BuildingA\"
strTeacherGroup = "BuildingA_Staff"
strStudentGroup = "BuildingA_Students"

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objShell = WScript.CreateObject("WScript.Shell")    
Set objContainer = GetObject(strLDAPPath)
objContainer.Filter = Array("user")

objRun = objShell.Run("icacls" &amp; Chr(32) &amp; strUNCPath &amp; Chr(32) &amp; "/grant" &amp; Chr(32) &amp; strTeacherGroup &amp; ":(OI)(CI)RX")
WScript.Sleep 100
objRun = objShell.Run("icacls" &amp; Chr(32) &amp; strUNCPath &amp; Chr(32) &amp; "/grant" &amp; Chr(32) &amp; strStudentGroup &amp; ":(OI)(CI)RX")

For Each User in objContainer

strUser = User.SAMAccountName
strFullPath = strUNCPath &amp; strUser

On Error Resume Next
Set objFolder = objFSO.CreateFolder(strFullPath)

If Err.Number &lt;&gt; 0 Then
	strLog = strLog &amp; strUser &amp; Chr(58) &amp; Chr(32) &amp; Err.Description &amp; Chr(13)
Else
	Set objFolder = objFSO.CreateFolder(strFullPath &amp; "\Pickup")
	Set objFolder = objFSO.CreateFolder(strFullPath &amp; "\DropOff")
		
	objRun = objShell.Run("icacls" &amp; Chr(32) &amp; strFullPath &amp; "\DropOff" &amp; Chr(32) &amp; "/inheritance:d")
	wscript.sleep 400
	objRun = objShell.Run("icacls" &amp; Chr(32) &amp; strFullPath &amp; "\DropOff" &amp; Chr(32) &amp; "/remove:g" &amp; Chr(32) &amp; strStudentGroup)
	wscript.sleep 400
	objRun = objShell.Run("icacls" &amp; Chr(32) &amp; strFullPath &amp; "\DropOff" &amp; Chr(32) &amp; "/grant" &amp; Chr(32) &amp; strStudentGroup &amp; ":(OI)(CI)(WD)")
	wscript.sleep 100
	objRun = objShell.Run("icacls" &amp; Chr(32) &amp; strFullPath &amp; "\Pickup" &amp; Chr(32) &amp; "/grant" &amp; Chr(32) &amp; strUser &amp; ":(OI)(CI)(WD,AD,DC,X,RA,RD,REA)")
	wscript.sleep 400
	objRun = objShell.Run("icacls" &amp; Chr(32) &amp; strFullPath &amp; "\DropOff" &amp; Chr(32) &amp; "/grant" &amp; Chr(32) &amp; strUser &amp; ":(OI)(CI)(WD,AD,DC,X,RA,RD,REA)")
	
End If

WScript.Sleep 100

Next

'msgbox strLog
</pre><div></div><p></p>]]></description><guid isPermaLink="false">740</guid><pubDate>Tue, 07 Aug 2012 18:24:31 +0000</pubDate></item><item><title>Card Readers and Mapped Network Drives</title><link>https://www.edugeek.net/blogs/entry/642-card-readers-and-mapped-network-drives/</link><description><![CDATA[<p>Just this past Easter break I was tasked with installing about 60 or so new machines in the media centers in our district.  It wasn't until the end of the deployment that I discovered that mapped drives assigned via the login script weren't working properly as the card readers were taking up the letters.  Moving the mapped drives higher up in the alphabet wasn't going to fly as it would affect every user on the network, and there wasn't any group policy setting that would allow you to set the first available drive for removable media, so I was looking at having to revisit every machine and disable the readers.  Enter Microsoft's command line utility version of the device manager, <strong>Devcon.</strong></p><p> </p><p>
You'll need the deviceID for the card reader in question and this can be obtained through device manager.  If all the readers you're tying to disable are the same make/model, then this will be same for all of them.  Copy the entire string.</p><p> </p><p>
<a class="ipsAttachLink ipsAttachLink_image" href="https://www.edugeek.net/uploads/monthly_2012_04/CardReaderDM.png.1592e2d395bed98d5ef8621f40e385c2.png" data-fileid="152" data-fileext="png" rel=""><img class="ipsImage ipsImage_thumbnailed" alt="CardReaderDM.png.1592e2d395bed98d5ef8621f40e385c2.png" data-fileid="152" data-src="https://www.edugeek.net/uploads/monthly_2012_04/CardReaderDM.png.1592e2d395bed98d5ef8621f40e385c2.png" src="https://www.edugeek.net/applications/core/interface/js/spacer.png" /></a></p><p> </p><p>
Now the syntax to disable said card reader will look like this.  Just replace the deviceID shown with your own.  If the need arises, this utility also supports wild cards.</p><p> </p><p>
</p><pre class="ipsCode">devconx64 -disable "USB\VID_0644&amp;PID_0200&amp;REV_0408"</pre><div></div><p></p><p> </p><p>
This can be pushed out a number of ways; I wound up going ahead and creating a GPO and setting it up as a startup script.  Startup scripts run with machine credentials and will have the permissions necessary to disable hardware.  If you choose to incorporate this solution into a login script, then this will run with the user's credentials.  So be sure the user logging in has local admin privileges.</p><p> </p><p>
One last note, the download for Devcon from Microsoft includes x32 and an ia64 version.  Obviously ia64 is for the Itanium architecture and won't do any good for the majority of us out there.  There is an x64 build, but it is only available through a resource CD or something of the like and not as a standalone download.  So I've attached a zip to this post containing both the x32 and x64 versions for convenience.</p><p> </p><p>
I hope this helps.</p><p> </p><p>
<a href="https://www.edugeek.net/applications/core/interface/file/attachment.php?id=153" data-fileid="153" data-fileext="zip" rel="">DevCon.zip</a></p>]]></description><guid isPermaLink="false">642</guid><pubDate>Mon, 16 Apr 2012 18:32:41 +0000</pubDate></item></channel></rss>
