Jump to content

Recommended Posts

Posted

A very good response!

 

Just recently finished shifting over an 80 machine CC network to vanilla. One of the most stressful tasks in my life so far. I tried my very best to plan everything down to a T which involved spending hours talking to people and the mirror (It works for me :lol: ), waking up at night to write down notes. All to make sure I hadn't missed anything. In the end I filled two A4 books with notes.

I also left it for a week or two and realised I had still missed some things off :D

Still, it all paid off and they are all as happy as larry

 

In my opinion, its best to start with a new forest with a two way trust (Bearing in mind you may come across difficulty with cross forest roaming profiles and offline files probably along with other things too). At least you know where you stand and there won't be any possibility of odd RM Schemas or specific forest related nonsense anywhere. Its also got a psycholgical reason, starting on a clean slate and all!

Posted

I would replicate the scenario in to a Virtual environment as both a training and research excercise.

Just my $0.02 worth :)

Posted

ok.. I thought it time for an update!

 

A lot has happened since I last posted and in brief we have decided to go ahead with part of this, starting THIS SUMMER :mad:

We were originally planning to spend around a year planning and prepping for this and making the whole network vanilla in one summer break (2012). The more I have thought about this I dont think realistically that time would allow myself and my colleague to carry this out all in one go. As such we have planned, tested and are more or less ready to go on rolling out vanilla Windows XP to all workstations over the 6 week summer break (and have had approval from SMT). Here is the software we are planning on using:

 

AD User Management: At present we are going to carry on using the RM Management console to create our users. Quite simply it does the job, it has never let us done and once it's worked its magic we can simply drop the new accounts into the correct OU.

 

Workstation Imaging: We are using WDS for our desktop OS deployment which will be sending out an image of XP SP3 with the only addition being .net framework (we are sending other Windows updates out via WSUS but wanted this to be installed - for SIMs - as soon as the workstation is built). Following this Group Policy will install Display Changer (to set screen res to maximum - great little command-line program if your not already using it) and WPKG Client...

 

Software Rollout: Serious thanks to whoever suggested WPKG above!!! Quite simply it is one of the best open-source programs I have come across in a long time. So simple really in what it does and how it does it but provides a great deal of flexibility in how software is distributed. Not come up against any scenarios it cant fulfil for us yet.

 

Software Updates: WSUS...'nuff said

 

Start Menu: A fairly long, manually written batch script but it does the job, runs in only a couple of seconds (even on older spec PC's) and provides users with a clean start-menu, only showing software installed on the machine.

 

Print Management: Gone (or should I say going) for Papercut. This is virtually the last thing we have to do and configure now and shouldnt be too tricky (I think the only slight consideration is the print release terminal we need to configure for our staff room printer).

 

All in all we have managed to configure a network which very much replicates RM's offering (at least the bits we use) but should (long-term) be easier to management without expensive support contracts, is much quicker to boot (particularly on older hardware) and is just as secure with appropriate software restrictions in place.

 

Thanks for all the advice on here... definately has helped me decide on some of the software we have selected and I will keep this post updated (in case anyone is interested) over the summer for those who are interested enough to read.

Posted
with the only addition being .net framework

 

That's reasonable, but if it's XP SP3 then I'd add: a) GPP update so they immediately understand 2008+ GPPs, b) IE8 because that's the end of the IE line for XP i.e. you won't be uninstalling it and putting IE9 on, c) Anything else global that satisfies the same kind of reasoning.

 

Quite simply it (WPKG - Ed.) is one of the best open-source programs I have come across in a long time. So simple really in what it does

 

You obviously haven't looked at the code then ;b It has been a couple of years, but I looked enough to submit a few bugfixes back to the project (was adapting it slightly) and it's actually jolly complex. The winning WPKG concept is simple though: Is-it-installed/has-it-installed checks.

Posted
Start Menu: A fairly long, manually written batch script but it does the job, runs in only a couple of seconds (even on older spec PC's) and provides users with a clean start-menu, only showing software installed on the machine.

 

Hi Cyber - I'm interested in how you did this. WOuld you be willing to post your script?

 

Many thanks

 

Gareth

Posted
Hi Cyber - I'm interested in how you did this. WOuld you be willing to post your script?

 

Many thanks

 

Gareth

 

yeah, not a problem at all. We are absolutely swamped at the minute (got this to finish in the next 3 weeks, along with having 2 work experience students, school show to help out with etc.) but as soon as I get five minutes I will post it and try and explain the thinking behind it and how it works.

Posted

if flattening is not an option right now (although if you planned to do the work during summer and its not an option now, its probably not an option at all!) then there are ways of removing RM stuff.

Unfortunately I can only talk from CC4 experience, but since a couple of months ago we have had to keep running with CC4 on the server, but all clients had the services disabled. We also have a batch file which run on a client machine will uninstall all RM MSI's. its not foolproof but gets rid of 99% of RM components installed. If you know all your machines have the same RM components installed, get a txt file with each line listing the GUID of the RM software (usually found in the uninstall string of registry) and have a .bat file do this:

 

if not exist wiperm.txt GOTO NOTFOUND

 

FOR /F %%I IN (wiperm.txt) do msiexec /X %%I /Q /NORESTART

goto OK

 

:OK

echo All Done..

GOTO END

 

as i said, it's a dirty way of doing things, and we were partially forced by RM to do this so its not perfect in any way, but if you dont want to rebuild, there are still options but as Hightower said a while back, still plan. Even if you do this method, plan for something to go wrong, plan for worst case and work from there

Posted
Hi Cyber - I'm interested in how you did this. WOuld you be willing to post your script?

Many thanks

Gareth

 

We use a vbs script to create a start menu based on the same structure as our users were used to from CC3 (in fact using the exact same shortcut repository!) We copy the available shortcuts to a local folder where the start menu is redirected to. Seems to be working ok from all the feedback from staff that have used our W7 stations since the Easter holidays

 

Set objShell = CreateObject("WScript.Shell")

Set WshShell = WScript.CreateObject("WScript.Shell")

Set objFSO = CreateObject("Scripting.FileSystemObject")

'Define location for master start menu structure with ntfs security to control access

objStartFolder = "X:\Group Resources\Programs"

'Define local path for start menu files to be copied to

objStartMenuFolder = "C:\ProgramData\Bradon\Windows\Start Menu\Programs"

 

'Clear out the current Start Menu

If (objFSO.FileExists(objStartMenuFolder & "\*.lnk")) Then

objFSO.DeleteFile(objStartMenuFolder & "\*.lnk"), DeleteReadOnly

end if

'Delete all subfolders within local start menu

DelSubFolders objFSO.GetFolder(objStartMenuFolder)

 

ShowSubfolders objFSO.GetFolder(objStartFolder)

 

Sub ShowSubFolders(Folder)

On Error Resume Next

'Loop though folders in master location

For Each Subfolder in Folder.SubFolders

Set objFolder = objFSO.GetFolder(Subfolder.Path)

Set colFiles = objFolder.Files

For Each objFile in colFiles

'Extract location from each lnk file

Set shortcut = objShell.CreateShortcut(Subfolder.Path & "\" & objFile.Name)

shortTarget = shortcut.TargetPath

 

'Check to see if the target path exists on local machine. If it does, create the folder if not already there, then copy the lnk file

If objFSO.FileExists(shortTarget) Then

If Not ObjFSO.FolderExists(ObjStartMenuFolder & "\" & SubFolder.Name) Then objFSO.CreateFolder(ObjStartMenuFolder & "\" & SubFolder.Name)

objFSO.CopyFile objFile.Path, ObjStartMenuFolder & "\" & SubFolder.Name & "\" & ObjFile.Name

end if

Next

'Check to see if folder was actually empty,if so remove it

set Temp = objFSO.GetFolder(ObjStartMenuFolder & "\" & SubFolder.Name)

Set TFile = Temp.Files

If TFile.count = 0 then objFSO.DeleteFolder(ObjStartMenuFolder & "\" & SubFolder.Name)

'Check recursive folders.

ShowSubFolders Subfolder

Next

On Error Goto 0

End Sub

 

 

Sub DelSubFolders(Folder)

On Error Resume Next

For Each Subfolder in Folder.SubFolders

if (SubFolder.Name = "Accessories") Or(SubFolder.Name = "Startup") then

Set objFolder = objFSO.GetFolder(Subfolder.Path)

If (objFSO.FileExists(objFolder & "\*.lnk")) Then

objFSO.DeleteFile(objFolder & "\*.lnk"), DeleteReadOnly

End if

else

Set objFolder = objFSO.GetFolder(Subfolder.Path)

objFSO.DeleteFolder(ObjFolder)

end if

Next

On Error Goto 0

End Sub

  • Thanks 4
Posted

Ive got a similar one which copies a start menu and desktop from the server, and its got error handling and update sequences in it. I think Ive posted it elsewhere too, but will post it again.

 

On the note of CC x > Vanilla, I did it a few years ago - and went about it as has been described here. New OU and develop in there with inheritance disabled. IMHO, its only the same as any system transition. Ive recently taken over somewhere new and am in the process of renovating the setup there. Im doing exactly the same thing again... new OU and develop all the GPOs there. Lots of people know im an SCCM fan... so if you want any help with that PM me or look on my blog. It is running side by side with FOG at the moment - which is being retired. No performance issues here, and its still only running single server while i wait for my new servers ;-|)

 

Good luck

Posted
I did a migrate away from CC in 2003 or 2006, can't exactly remember which year it was but I did the fresh install method. Basically setup our new servers vanilla style and copied user areas and public documents onto the new servers. Seem to remember using a microsoft export/importer for user accounts, but anyway took no longer than four weeks for the network to be up and running and machines imaged. The longest part was transferring user docs and public docs, it could also be a good time to test a restore of your backup system. We also had to create a lot of MSI's but thats becoming less of an issue these days.
  • 1 year later...
Posted
We use a vbs script to create a start menu based on the same structure as our users were used to from CC3 (in fact using the exact same shortcut repository!) We copy the available shortcuts to a local folder where the start menu is redirected to. Seems to be working ok from all the feedback from staff that have used our W7 stations since the Easter holidays

 

Nice script @Boredguy. I'd like to ...ahem... borrow this if I may. Only trouble is it doesn't handle nested folders within the Start Menu source directory. It copies each nested folder to the root of the target Start Menu dir.

 

Any of you scripting geniuses able to offer a solution?

Posted

We were running it at logon, but have since moved to an AutoIt compiled EXE that we run at station bootup as it was much faster.

 

It checks the local shortcut repository against the network one, and hides any shortcuts that are not available. If the computer is rebooted during the day, the script checks a holding file to see if it should perform the scan again which cuts down on the number of times it updates the shortcuts (especially on our student laptops which could be turned on 5 or 6 times a day)

 

I'll check with my Technican tomorrow and see if he's happy for it to be circulated with you all. Most of the config is controlled via a ini file so it should be easy to redistrubute.

  • Thanks 1
Posted
We were running it at logon, but have since moved to an AutoIt compiled EXE that we run at station bootup as it was much faster.

 

It checks the local shortcut repository against the network one, and hides any shortcuts that are not available. If the computer is rebooted during the day, the script checks a holding file to see if it should perform the scan again which cuts down on the number of times it updates the shortcuts (especially on our student laptops which could be turned on 5 or 6 times a day)

 

I'll check with my Technican tomorrow and see if he's happy for it to be circulated with you all. Most of the config is controlled via a ini file so it should be easy to redistrubute.

 

That would be great... thanks.

Posted (edited)
We were running it at logon, but have since moved to an AutoIt compiled EXE that we run at station bootup as it was much faster.

 

It checks the local shortcut repository against the network one, and hides any shortcuts that are not available. If the computer is rebooted during the day, the script checks a holding file to see if it should perform the scan again which cuts down on the number of times it updates the shortcuts (especially on our student laptops which could be turned on 5 or 6 times a day)

 

I'll check with my Technican tomorrow and see if he's happy for it to be circulated with you all. Most of the config is controlled via a ini file so it should be easy to redistrubute.

 

Thanks, I had been trying something similar using just group policy, bit of a pain in the rear so this is quite cool, I will still need to add some shortcuts using group policy though for programs that aren't installed on every machine.

 

EDIT: Just actually read the script to see it does this itself, even better

Edited by british_government
Posted

Yep as long as it's via a UNC path and not a mapped drive it will adjust the hidden flag.

Software that is only on certain machines will only be viewable when the lnk source can be found (very handy for SIMS, etc)

We have 3 main folders, Staff, Student and Exams, and then just redirect the startmenu to the relevant folder at logon.

 

We use WPKG to deploy software, so we have this application running every time, but you can easily add it into the GPO to run at startup.

Posted

You will need to complile this in AutoIT 3.3.8.1 or higher.

 

File - CopyStartMenu

#NoTrayIcon
; Shortcut Scanner 0.1
#include 

; Script Settings
$DirectoryVariableString = IniRead (@ScriptDir & "\Settings.ini","Settings","Source","") ; Where to scan, use | with no spaces for additional paths.
$FileDestination = IniRead (@ScriptDir & "\Settings.ini","Settings","Destination","") ; Where to copy the files to.
$ComputerRecord = IniRead (@ScriptDir & "\Settings.ini","Settings","ComputerRecord","") ; Record file of last shortcut date detected on previous scan.
$NetworkRecordDir = IniRead (@ScriptDir & "\Settings.ini","Settings","NetworkRecordDir","") ; Record file of last shortcut date detected on previous scan.
$NetworkRecordFile = $NetworkRecordDir & "\" & @COMputerName

; Multi fuctional use variables
$DirectoryArray = "" ; Declared to be used between functions.
$ProcessingDir = 0 ; Declared to be used between functions.
$ScannedItem = "" ; Declared to be used between functions.
$NetworkShortcutTimeStamp = 0 ; Last shortcut timestamp on server.
$ComputerShortcutTimeStamp = 0 ; Last shortcut timestamp on computer.
$DateLastRun = 0; Date last run on computer.

;
Func DownloadUpdatedFiles()
DirRemove($FileDestination,1)
DirCopy($DirectoryVariableString,$FileDestination,1)
EndFunc

; Update shortcuts and files.
Func UpdateFiles()

; Update shortcuts if need be.
If $ComputerShortcutTimeStamp <> $NetworkShortcutTimeStamp Then
	DownloadUpdatedFiles()
ElseIf FileExists($NetworkRecordFile) = 0 Then
	DownloadUpdatedFiles()
EndIf

; Delete the last scan record and replace with lastest scan record.
FileDelete($ComputerRecord)
$LastScanFile = FileOpen($ComputerRecord, 1)
If $LastScanFile = -1 Then Exit
FileWriteLine($LastScanFile, $NetworkShortcutTimeStamp)
FileWrite($LastScanFile, @YEAR & @mon & @MDAY)
FileClose($LastScanFile)

FileDelete($NetworkRecordFile)
$NetworkRecordHandle = FileOpen($NetworkRecordFile, 1)
If $NetworkRecordHandle = -1 Then Exit
FileWrite($NetworkRecordHandle, @YEAR & @mon & @MDAY)
FileClose($NetworkRecordHandle)

EndFunc

; Process last time stamp on server.
Func FileToProcess()
$FullFilePath = $DirectoryArray[$ProcessingDir] & "\" & $ScannedItem
$TimeStamp = FileGetTime($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem, 0, 1)
If Number($TimeStamp) > $NetworkShortcutTimeStamp Then $NetworkShortcutTimeStamp = Number($TimeStamp)
EndFunc

; Find All Directories
Func ProcessAllDirectories()
$ProcessingDir = 0
$DirCounter = UBound($DirectoryArray)
Do
	$SkippedOnError = 0
	$ScanHandle = FileFindFirstFile($DirectoryArray[$ProcessingDir] & "\*")
	; Check if the search was successful
	If $ScanHandle <> -1 Then
		While 1
			$IsADirectory = 0
			$ScannedItem = FileFindNextFile($ScanHandle)
			If @error Then ExitLoop
			$attrib = FileGetAttrib($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem)
			If @error Then
				$SkippedOnError = 1
;					MsgBox(4096,"Error", "Could not obtain attributes on: " & $DirectoryArray[$ProcessingDir] & "\" & $ScannedItem)
;					Exit
			Else
				If StringInStr($attrib, "D") Then
					$DirCounter = $DirCounter + 1
					_ArrayAdd($DirectoryArray, $DirectoryArray[$ProcessingDir] & "\" & $ScannedItem)
					$IsADirectory = 1
				Else
					FileToProcess()
				EndIf
			EndIf
		WEnd
	EndIf
	; Close the search handle
	FileClose($ScanHandle)
	$ProcessingDir = $ProcessingDir + 1
Until $ProcessingDir = UBound($DirectoryArray)
_ArraySort($DirectoryArray)
EndFunc

; Directory Variable Input
Func DirectoryVariableInput()
$DirectoryArray = StringSplit($DirectoryVariableString, '|', 2)
EndFunc

; Find last modified local file
Func LastModifiedLocalFile()

; If information was not picked up from the Settings.ini file, then end program.
If $DirectoryVariableString = "" OR $FileDestination = "" OR $ComputerRecord = "" OR $NetworkRecordDir = "" Then Exit

; Open the last scan log, if it doesn't exist set the set the last scan to 0 forcing all shortcuts to be downloaded.
$LastScanFile = FileOpen($ComputerRecord, 0)
If $LastScanFile = -1 Then
	$ComputerShortcutTimeStamp = 0
	$DateLastRun = 0
Else
	$ComputerShortcutTimeStamp = FileReadLine($LastScanFile, 1)
	$DateLastRun = FileReadLine($LastScanFile, 2)
EndIf
FileClose($LastScanFile)
If FileExists($NetworkRecordFile) Then
	If $DateLastRun = @YEAR & @mon & @MDAY Then
		Exit
	EndIf
EndIf
EndFunc

; Main Script Here

LastModifiedLocalFile()
DirectoryVariableInput()
ProcessAllDirectories()
UpdateFiles()

 

File - ShortcutChecker

#NoTrayIcon
; FileScan 0.1
#include 

; Script Settings
$DirectoryVariableString = IniRead (@ScriptDir & "\Settings.ini","Settings","Destination","") ; Where to scan, use | with no spaces for additional paths.

; Multi fuctional use variables
$DirectoryArray = "" ; Declared to be used between functions.
$ShortcutArray = "" ; Declared to be used between functions.
$ProcessingDir = 0 ; Declared to be used between functions.
$ScannedItem = "" ; Declared to be used between functions.

$CurrentDirectory = ""
$HiddenFiles=0
$UnhiddenFiles=0

; Process File Info Into Array
Func ProcessFileStatus()
$FullFilePath = $DirectoryArray[$ProcessingDir] & "\" & $ScannedItem
$ShortcutDetails = FileGetShortcut($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem)
$attrib = FileGetAttrib($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem)
If StringInStr($attrib, "H") Then
	$HiddenFiles=1
Else
	$UnhiddenFiles=1
EndIf
EndFunc

; Process Directory Hidden Status
Func ProcessDirectoryStatus()
$ProcessingDir=0
Do
	$SkippedOnError = 0
	$ScanHandle = FileFindFirstFile($DirectoryArray[$ProcessingDir] & "\*")
	; Check if the search was successful
	If $ScanHandle <> -1 Then
		While 1
			$ScannedItem = FileFindNextFile($ScanHandle)
			If @error Then ExitLoop
			$attrib = FileGetAttrib($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem)
			If @error Then
				$SkippedOnError = 1
			Else
				ProcessFileStatus()
			EndIf
		WEnd
		If $UnhiddenFiles = 0 Then
			FileSetAttrib($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem, "+H")
		Else
			FileSetAttrib($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem, "-H")
		EndIf
		$HiddenFiles=0
		$UnhiddenFiles=0
	EndIf
	; Close the search handle
	FileClose($ScanHandle)
	$ProcessingDir = $ProcessingDir + 1
Until $ProcessingDir = UBound($DirectoryArray)
EndFunc

; Process File Info Into Array
Func FileToProcess()
$FullFilePath = $DirectoryArray[$ProcessingDir] & "\" & $ScannedItem
$ShortcutDetails = FileGetShortcut($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem)
If StringRight($ScannedItem,4) = ".lnk" Then
	If StringLeft($ShortcutDetails[0],3) = "C:\" Then
		If FileExists($ShortcutDetails[0]) Then
			FileSetAttrib($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem, "-H")
			_ArrayAdd($ShortcutArray, $DirectoryArray[$ProcessingDir] & "|U")
		Else
			FileSetAttrib($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem, "+H")
			_ArrayAdd($ShortcutArray, $DirectoryArray[$ProcessingDir] & "|H")
		EndIf
	EndIf
EndIf
EndFunc

; Find All Directories
Func ProcessAllDirectories()
$ProcessingDir = 0
Do
	$SkippedOnError = 0
	$ScanHandle = FileFindFirstFile($DirectoryArray[$ProcessingDir] & "\*")
	; Check if the search was successful
	If $ScanHandle <> -1 Then
		While 1
			$ScannedItem = FileFindNextFile($ScanHandle)
			If @error Then ExitLoop
			$attrib = FileGetAttrib($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem)
			If @error Then
				$SkippedOnError = 1
			Else
				If StringInStr($attrib, "D") Then
					$CurrentDirectory = $DirectoryArray[$ProcessingDir] & "\" & $ScannedItem
					_ArrayAdd($DirectoryArray, $CurrentDirectory)
				Else
					FileToProcess()
				EndIf
			EndIf
		WEnd
	EndIf
	; Close the search handle
	FileClose($ScanHandle)
	$ProcessingDir = $ProcessingDir + 1
Until $ProcessingDir = UBound($DirectoryArray)
_ArraySort($DirectoryArray, 1)
;_ArrayDisplay($DirectoryArray, "Test List")
ProcessDirectoryStatus()
EndFunc

; Directory Variable Input
Func DirectoryVariableInput()
If $DirectoryVariableString = "" Then Exit
$DirectoryArray = StringSplit($DirectoryVariableString, '|', 2)
EndFunc

; Main Script Here

DirectoryVariableInput()
ProcessAllDirectories()

 

File - Settings.ini

[settings]

Source="\\ABC-SVR-01\Applications$\StartMenu\UserType"
Destination="C:\ProgramData\FolderName\Windows\Start Menu"
NetworkRecordDir="\\ABC-SVR-01\Applications$\StartMenu\ShortcutDeploy"
ComputerRecord="C:\ProgramData\FolderName\Windows\LastScan"

 

The ini file contains the location to where the start menu structure is to be copied down by the first program, and where it gets copied to (based on your redirection settings).

So first you run the CopyStartMenu program, then run the ShortcutChecker to see which files are able to be hidden.

You can merge the 2 programs into 1, but we found it easier to leave it seperate for debugging purpose.

  • Thanks 1
Posted
You sir are a legend. This works perfectly and has saved me many hours reinventing the wheel. Thanks very much. :)
  • 3 years later...
Posted
You will need to complile this in AutoIT 3.3.8.1 or higher.

 

File - CopyStartMenu

#NoTrayIcon
; Shortcut Scanner 0.1
#include 

; Script Settings
$DirectoryVariableString = IniRead (@ScriptDir & "\Settings.ini","Settings","Source","") ; Where to scan, use | with no spaces for additional paths.
$FileDestination = IniRead (@ScriptDir & "\Settings.ini","Settings","Destination","") ; Where to copy the files to.
$ComputerRecord = IniRead (@ScriptDir & "\Settings.ini","Settings","ComputerRecord","") ; Record file of last shortcut date detected on previous scan.
$NetworkRecordDir = IniRead (@ScriptDir & "\Settings.ini","Settings","NetworkRecordDir","") ; Record file of last shortcut date detected on previous scan.
$NetworkRecordFile = $NetworkRecordDir & "\" & @COMputerName

; Multi fuctional use variables
$DirectoryArray = "" ; Declared to be used between functions.
$ProcessingDir = 0 ; Declared to be used between functions.
$ScannedItem = "" ; Declared to be used between functions.
$NetworkShortcutTimeStamp = 0 ; Last shortcut timestamp on server.
$ComputerShortcutTimeStamp = 0 ; Last shortcut timestamp on computer.
$DateLastRun = 0; Date last run on computer.

;
Func DownloadUpdatedFiles()
DirRemove($FileDestination,1)
DirCopy($DirectoryVariableString,$FileDestination,1)
EndFunc

; Update shortcuts and files.
Func UpdateFiles()

; Update shortcuts if need be.
If $ComputerShortcutTimeStamp <> $NetworkShortcutTimeStamp Then
	DownloadUpdatedFiles()
ElseIf FileExists($NetworkRecordFile) = 0 Then
	DownloadUpdatedFiles()
EndIf

; Delete the last scan record and replace with lastest scan record.
FileDelete($ComputerRecord)
$LastScanFile = FileOpen($ComputerRecord, 1)
If $LastScanFile = -1 Then Exit
FileWriteLine($LastScanFile, $NetworkShortcutTimeStamp)
FileWrite($LastScanFile, @YEAR & @mon & @MDAY)
FileClose($LastScanFile)

FileDelete($NetworkRecordFile)
$NetworkRecordHandle = FileOpen($NetworkRecordFile, 1)
If $NetworkRecordHandle = -1 Then Exit
FileWrite($NetworkRecordHandle, @YEAR & @mon & @MDAY)
FileClose($NetworkRecordHandle)

EndFunc

; Process last time stamp on server.
Func FileToProcess()
$FullFilePath = $DirectoryArray[$ProcessingDir] & "" & $ScannedItem
$TimeStamp = FileGetTime($DirectoryArray[$ProcessingDir] & "" & $ScannedItem, 0, 1)
If Number($TimeStamp) > $NetworkShortcutTimeStamp Then $NetworkShortcutTimeStamp = Number($TimeStamp)
EndFunc

; Find All Directories
Func ProcessAllDirectories()
$ProcessingDir = 0
$DirCounter = UBound($DirectoryArray)
Do
	$SkippedOnError = 0
	$ScanHandle = FileFindFirstFile($DirectoryArray[$ProcessingDir] & "\*")
	; Check if the search was successful
	If $ScanHandle <> -1 Then
		While 1
			$IsADirectory = 0
			$ScannedItem = FileFindNextFile($ScanHandle)
			If @error Then ExitLoop
			$attrib = FileGetAttrib($DirectoryArray[$ProcessingDir] & "" & $ScannedItem)
			If @error Then
				$SkippedOnError = 1
;					MsgBox(4096,"Error", "Could not obtain attributes on: " & $DirectoryArray[$ProcessingDir] & "" & $ScannedItem)
;					Exit
			Else
				If StringInStr($attrib, "D") Then
					$DirCounter = $DirCounter + 1
					_ArrayAdd($DirectoryArray, $DirectoryArray[$ProcessingDir] & "" & $ScannedItem)
					$IsADirectory = 1
				Else
					FileToProcess()
				EndIf
			EndIf
		WEnd
	EndIf
	; Close the search handle
	FileClose($ScanHandle)
	$ProcessingDir = $ProcessingDir + 1
Until $ProcessingDir = UBound($DirectoryArray)
_ArraySort($DirectoryArray)
EndFunc

; Directory Variable Input
Func DirectoryVariableInput()
$DirectoryArray = StringSplit($DirectoryVariableString, '|', 2)
EndFunc

; Find last modified local file
Func LastModifiedLocalFile()

; If information was not picked up from the Settings.ini file, then end program.
If $DirectoryVariableString = "" OR $FileDestination = "" OR $ComputerRecord = "" OR $NetworkRecordDir = "" Then Exit

; Open the last scan log, if it doesn't exist set the set the last scan to 0 forcing all shortcuts to be downloaded.
$LastScanFile = FileOpen($ComputerRecord, 0)
If $LastScanFile = -1 Then
	$ComputerShortcutTimeStamp = 0
	$DateLastRun = 0
Else
	$ComputerShortcutTimeStamp = FileReadLine($LastScanFile, 1)
	$DateLastRun = FileReadLine($LastScanFile, 2)
EndIf
FileClose($LastScanFile)
If FileExists($NetworkRecordFile) Then
	If $DateLastRun = @YEAR & @mon & @MDAY Then
		Exit
	EndIf
EndIf
EndFunc

; Main Script Here

LastModifiedLocalFile()
DirectoryVariableInput()
ProcessAllDirectories()
UpdateFiles()

 

File - ShortcutChecker

#NoTrayIcon
; FileScan 0.1
#include 

; Script Settings
$DirectoryVariableString = IniRead (@ScriptDir & "\Settings.ini","Settings","Destination","") ; Where to scan, use | with no spaces for additional paths.

; Multi fuctional use variables
$DirectoryArray = "" ; Declared to be used between functions.
$ShortcutArray = "" ; Declared to be used between functions.
$ProcessingDir = 0 ; Declared to be used between functions.
$ScannedItem = "" ; Declared to be used between functions.

$CurrentDirectory = ""
$HiddenFiles=0
$UnhiddenFiles=0

; Process File Info Into Array
Func ProcessFileStatus()
$FullFilePath = $DirectoryArray[$ProcessingDir] & "\" & $ScannedItem
$ShortcutDetails = FileGetShortcut($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem)
$attrib = FileGetAttrib($DirectoryArray[$ProcessingDir] & "\" & $ScannedItem)
If StringInStr($attrib, "H") Then
	$HiddenFiles=1
Else
	$UnhiddenFiles=1
EndIf
EndFunc

; Process Directory Hidden Status
Func ProcessDirectoryStatus()
$ProcessingDir=0
Do
	$SkippedOnError = 0
	$ScanHandle = FileFindFirstFile($DirectoryArray[$ProcessingDir] & "\*")
	; Check if the search was successful
	If $ScanHandle <> -1 Then
		While 1
			$ScannedItem = FileFindNextFile($ScanHandle)
			If @error Then ExitLoop
			$attrib = FileGetAttrib($DirectoryArray[$ProcessingDir] & "" & $ScannedItem)
			If @error Then
				$SkippedOnError = 1
			Else
				ProcessFileStatus()
			EndIf
		WEnd
		If $UnhiddenFiles = 0 Then
			FileSetAttrib($DirectoryArray[$ProcessingDir] & "" & $ScannedItem, "+H")
		Else
			FileSetAttrib($DirectoryArray[$ProcessingDir] & "" & $ScannedItem, "-H")
		EndIf
		$HiddenFiles=0
		$UnhiddenFiles=0
	EndIf
	; Close the search handle
	FileClose($ScanHandle)
	$ProcessingDir = $ProcessingDir + 1
Until $ProcessingDir = UBound($DirectoryArray)
EndFunc

; Process File Info Into Array
Func FileToProcess()
$FullFilePath = $DirectoryArray[$ProcessingDir] & "" & $ScannedItem
$ShortcutDetails = FileGetShortcut($DirectoryArray[$ProcessingDir] & "" & $ScannedItem)
If StringRight($ScannedItem,4) = ".lnk" Then
	If StringLeft($ShortcutDetails[0],3) = "C:" Then
		If FileExists($ShortcutDetails[0]) Then
			FileSetAttrib($DirectoryArray[$ProcessingDir] & "" & $ScannedItem, "-H")
			_ArrayAdd($ShortcutArray, $DirectoryArray[$ProcessingDir] & "|U")
		Else
			FileSetAttrib($DirectoryArray[$ProcessingDir] & "" & $ScannedItem, "+H")
			_ArrayAdd($ShortcutArray, $DirectoryArray[$ProcessingDir] & "|H")
		EndIf
	EndIf
EndIf
EndFunc

; Find All Directories
Func ProcessAllDirectories()
$ProcessingDir = 0
Do
	$SkippedOnError = 0
	$ScanHandle = FileFindFirstFile($DirectoryArray[$ProcessingDir] & "\*")
	; Check if the search was successful
	If $ScanHandle <> -1 Then
		While 1
			$ScannedItem = FileFindNextFile($ScanHandle)
			If @error Then ExitLoop
			$attrib = FileGetAttrib($DirectoryArray[$ProcessingDir] & "" & $ScannedItem)
			If @error Then
				$SkippedOnError = 1
			Else
				If StringInStr($attrib, "D") Then
					$CurrentDirectory = $DirectoryArray[$ProcessingDir] & "" & $ScannedItem
					_ArrayAdd($DirectoryArray, $CurrentDirectory)
				Else
					FileToProcess()
				EndIf
			EndIf
		WEnd
	EndIf
	; Close the search handle
	FileClose($ScanHandle)
	$ProcessingDir = $ProcessingDir + 1
Until $ProcessingDir = UBound($DirectoryArray)
_ArraySort($DirectoryArray, 1)
;_ArrayDisplay($DirectoryArray, "Test List")
ProcessDirectoryStatus()
EndFunc

; Directory Variable Input
Func DirectoryVariableInput()
If $DirectoryVariableString = "" Then Exit
$DirectoryArray = StringSplit($DirectoryVariableString, '|', 2)
EndFunc

; Main Script Here

DirectoryVariableInput()
ProcessAllDirectories()

 

File - Settings.ini

[settings]

Source="\\ABC-SVR-01\Applications$\StartMenu\UserType"
Destination="C:\ProgramData\FolderName\Windows\Start Menu"
NetworkRecordDir="\\ABC-SVR-01\Applications$\StartMenu\ShortcutDeploy"
ComputerRecord="C:\ProgramData\FolderName\Windows\LastScan"

 

The ini file contains the location to where the start menu structure is to be copied down by the first program, and where it gets copied to (based on your redirection settings).

So first you run the CopyStartMenu program, then run the ShortcutChecker to see which files are able to be hidden.

You can merge the 2 programs into 1, but we found it easier to leave it seperate for debugging purpose.

 

Hey @Boredguy. I've been successfully using your autoit scripts for start menu management for the past few years with great success but have just stumbled across a problem. I wonder if you've also experienced the same problem and found a solution?

 

Basically the issue is this - when a shortcut target includes a parameter (for example "C:\Program Files\Oracle\VirtualBox\VBoxManage.exe" startvm "testvm") the shortcut gets hidden even though the target executable exists. If I remove the parameter after the path to the exe then the shortcut doesn't get hidden.

 

Any ideas?

 

Many thanks

Posted

I must admit, we moved to using a PowerShell script to generate the start menu as it processed faster than the AutoIt.

 

Will poke @DJ-1701 to see if he can spot a quick fix for the AutoIt script.

Posted
I must admit, we moved to using a PowerShell script to generate the start menu as it processed faster than the AutoIt.

 

Will poke @DJ-1701 to see if he can spot a quick fix for the AutoIt script.

 

Thanks for the quick response @Boredguy. Fancy sharing your powershell script? :)

Posted

The PowerShell version :-)... so much easier to edit than recompiling that exe each time! Also I merged it all into one script.

 

Apart from only checking the target path (and not the additional arguments), two other main differences in the PowerShell script:

 

1) I removed the 'LastScan' creation and check files that I put in the AutoIt version as these are now surplus to requirement.

 

2) I changed the root of the $Source Start Menu folder to require a 32-Bit and a 64-Bit folder, and then have the Staff and Students Start Menu folders below. This is due to some shortcut target locations differing depending on the OS architecture.

 

Note: If a 32-Bit OS is detected the 32-Bit folders are copied down, if a 64-Bit OS is detected the 64-Bit folders are copied down.

 

Image of the new $Source Start Menu structure:

[ATTACH=CONFIG]34090[/ATTACH]

 

# Specify source and destination of shortcut folders below.
# ****************************************************************************
# * Note: In the source location, create two folders named 32-Bit and 64-Bit *
# *       to host your Start Menu shortcuts (as shortcut paths may vary).    *
# ****************************************************************************
$Source = "\\ABC-SVR-01\Applications$\StartMenu"
$Destination="C:\ProgramData\FolderName\Windows\Start Menu"

# Inform user if no Source or Destination has been provided.
If (($Source -eq "") -or ($Destination -eq ""))
{
   Write-Host "Missing Source and/or Destination, please check and try again."
   Exit
}

# If there is a trailing backslash for $Source and/or $Destination, remove the backslash.
If ($Source.Substring($Source.Length-1) -eq "\")
{
   $Source = $Source.Substring(0,$Source.Length-1)
}
If ($Destination.Substring($Destination.Length-1) -eq "\")
{
   $Destination = $Destination.Substring(0,$Destination.Length-1)
}

# Update the Source path to include the OS Architecture type to the end of the path.
# If no OS Architecture is returned, then use the 32-Bit shortcut folder.
$Bit = ((Get-WMIObject Win32_OperatingSystem).OSArchitecture)
If ($Bit -eq "") {$Bit = "32-Bit"}
$Source = $Source + "\" + $Bit

# Inform user if Source does not exist.
If (!(Test-Path($Source)))
{
   Write-Host "The Source location does not exist, please check and try again."
   Exit
}

# Get the latest last modified date on all shortcuts in Source and Destination paths.
$NetworkStamp = Get-ChildItem "$Source\*" -Recurse -Force -include *.lnk `
               | Where {!$_.PsIsContainer} | select Name,DirctoryName, LastWriteTime `
               | Sort LastWriteTime -descending | select -first 1
$LocalStamp = Get-ChildItem "$Destination\*" -Recurse -Force -include *.lnk -ErrorAction SilentlyContinue `
             | Where {!$_.PsIsContainer} | select Name,DirctoryName, LastWriteTime `
             | Sort LastWriteTime -descending | select -first 1

# If there are any differences in the last modified date:
# 1) Delete files in Destination (if it exists).
# 2) Copy files from Source to Destination.
If ($NetworkStamp.LastWriteTime -ne $LocalStamp.LastWriteTime)
{
   If (Test-Path($Destination))
   {
       Remove-Item "$Destination" -Recurse -Force
   }
   New-Item -ItemType directory -Path "$Destination"
   Copy-Item "$Source\*" -Destination "$Destination" -Recurse
}

# Create a list of folders copied to the destination folder.
$ListOfFolders = Get-ChildItem $Destination -Recurse -Force | Where-Object {$_.PSIsContainer} `
                | Sort-Object FullName -Descending | % { $_.FullName }

# Create a shell object, so we can check if the shortcuts are valid.
$sh = New-Object -COM WScript.Shell

# Check if Array is Null (Prevents ForEach null bug in PowerShell 2).
If ($ListOfFolders -ne $null)
{
   
   # Do the following for every folder we scanned.
   FOREACH ($Folder in $ListOfFolders)
   {
       
       # Set the hidden flag to True for the folder.
       # Note: This will hide the folder later on if applications do not exist.
       $AllItemsHidden = $true
       
       # Get a list of all shortcuts in the current folder.
       $ListOfShortcuts = Get-ChildItem "$Folder\*" -Force -include *.lnk `
                          | % { $_.FullName }
       
       # Check if Array is Null (Prevents ForEach null bug in PowerShell 2).
       If ($ListOfShortcuts -ne $null)
       {
           
           # Do the following for every shortcut we scanned.
           FOREACH ($Shortcut in $ListOfShortcuts)
           {
               
               # Find out if the shortcut is hidden.
               $Item = Get-Item $Shortcut -Force
               $CheckHidden = (Get-ItemProperty $Item).Attributes.ToString() -match "Hidden"
               
               # Get the target path of the shortcut (the path of the application).
               # If it refers to C drive (C:), check if it exists, and ensure the shortcut
               # is visible to the users, otherwise hide the shortcut.
               $PathOfApplication = $sh.CreateShortcut($Shortcut).TargetPath
               If ($PathOfApplication.Substring(0,2) -eq "C:")
               {
                   If (Test-Path($PathOfApplication))
                   {
                       $AllItemsHidden = $false
                       If ($CheckHidden)
                       {
                           $Item.Attributes = $Item.Attributes -bxor [system.IO.FileAttributes]::Hidden
                       }
                   }
                   ElseIf ((!(Test-Path($PathOfApplication))) -and (!($CheckHidden)))
                   {
                       $Item.Attributes = $Item.Attributes -bxor [system.IO.FileAttributes]::Hidden
                   }
               }
           }
   
           # List all files in the folder (excluding desktop.ini and thumbs.db, which aren't required).
           $ListOfSubFilesAndFolders = Get-ChildItem "$Folder\*" -Force -exclude "desktop.ini","thumbs.db" `
                                       | % { $_.FullName }
           
           # Check if Array is Null (Prevents ForEach null bug in PowerShell 2).
           If ($ListOfSubFilesAndFolders -ne $null)
           {
               
               # Do the following for every file or subfolder we scanned.
               FOREACH ($SubFilesOrFolder in $ListOfSubFilesAndFolders)
               {
                   
                   # Find out if the file is hidden. If it is not, then it is a required resource.
                   # inform the program not to hide the folder.
                   $Item = Get-Item $SubFilesOrFolder -Force
                   $CheckHidden = $Item.Attributes.ToString() -match "Hidden"
                   If ($CheckHidden -eq $false)
                   {
                       $AllItemsHidden = $false
                   }
               }
           }
           
           # If all of the files are hidden in the folder, then hide the unrequired folder.
           # If any file is unhidden in the folder, then unhide the unrequired folder.
           $Item = Get-Item $Folder -Force
           $CheckHidden = (Get-ItemProperty $Item).Attributes.ToString() -match "Hidden"
           If (($AllItemsHidden -and !($CheckHidden)) -or (!($AllItemsHidden) -and $CheckHidden))
           {
               $Item.Attributes = $Item.Attributes -bxor [system.IO.FileAttributes]::Hidden
           }
       }
   }
}

  • Thanks 1
Posted
The PowerShell version :-)... so much easier to edit than recompiling that exe each time! Also I merged it all into one script.

 

Knew I kept him around for something :)

  • Thanks 2

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now



×
×
  • Create New...