Avoid The Panic: Company Start Menu Folder Changed and I have Hundreds Of Packages to Update Print E-mail
PowerShell
Written by Darwin Sanoy   
Wednesday, June 17, 2015 5:49pm

I was teaching a great team of Packaging Automation Engineers our course "Windows Application Internals for Packaging, Virtualization, Automation and Troubleshooting" and their company had recently gone through a re-branding which included a company name change.  They were looking at a lot of work to comply with the request to change the start menu folder their company uses from the old company name to the new one.  Hundreds of packages to rebuild and reinstall ?  There has to be a better way!  There is...

 

There was a real mix of people on the team - a couple had just joined the team and a couple have been automating Windows software installation for nearly 10 years!  I love working teaching experienced teams because I get to learn more than I usually learn when teaching!

 

In a module on the file system tricks that Windows uses, we covered how Microsoft uses hidden symbolic links to create compatibility for applications that use old file system references such as "c:\Documents and Settings\userid\My Document\My Music"  Fred asked if this trick might be able to help them fix up their start menu without fixing old packages and just modifying new ones.  We setup and after class working session to find out.

It struck me that this problem may hit a lot of packagers where the company undergoes a merger and a new company name is inherited - or in the case of law firms and pharmaceuticals the old names are mashed up together to create a renaming challenge for both companies in the merger!

Fred and worked out the details together and this is what we came up with.  First we discovered some of our requirements and necessary tests as we went.

Optimally the solution should strive for the following goals:

  1. Allow for immediate moving of all existing shortcuts to the new location so the user sees only the new location.
  2. Allow packages still coded for the old location to place their shortcuts in the new location - but without modifying the packages.
  3. Allow new or updated packages to use the new location directly.
  4. Not break self-healing or uninstall of any packages already installed *BEFORE* the change.
  5. Fix up any possible accidental occurrences of the old folder name in user start menus - although these should be rare in their non-admin, SCCM managed environment, working it into the script would be better than explaining to managers why some people retained the old references.

Modeling after the symbolic links in Windows that are used for fixing up legacy file system location references, we figured out the automation should do the following:

  1. Move any user profile start menus to the new location.
  2. Move the old machine start menu folder to the new location (overwrite any user entries of the same name)
  3. Create a symbolic link to the new location that matches the old name.
  4. Hide the symbolic link so it does not show on the start menu.
  5. Restart explorer (if immediate start menu update is required - otherwise wait for natural restarts / logoffs by users)

If this approach works as well as Microsoft's, then this automation specification should meet the design requirements.

Below I have included the commented, tested and working PowerShell code.

Here are a couple things to keep in mind when using the code:

  • Have this run during your machine builds to ensure the junctions are installed for when those new machines run the old packages you may not have time to get to for a while.  If you don't do this, the old start menu folder will mysteriously start appearing again on machines that don't have the junctions.
  • Use the attached download rather than copying the code from the browser.
  • The user start menu folder should be renamed in place so that it's permissions do not need to be altered. (an alternative is robocopy with permissions preservation switches)
  • If you try to browse another user's profile you'll receive a message asking if you want to gain permissions to that profile.  Logon as the actual user to verify the results.
  • The below code does not create the symbolic link for each user.  If for some reason you cannot eliminate the cause of getting a per-user shortcut, drop me a line and I can enhance the below to handle that requirement.
  • Attempting to use PowerShell CMDLets for the file operations was painful.  Between Copy-Item, Move-Item and Rename-Item - some do not recurse, some create a subfolder when one is not desired, re-running when the target exists can be challenging.
  • PowerShell for setting file attributes can't set attributes on the junction - they always flow through to the target.  Attrib /L puts the attributes on the junction.
$oldstartmenufolderlist = @("Old Folder Name","Another old folder")
$newstartmenufolder = "New Folder Name"
Foreach ($oldstartmenufolder in $oldstartmenufolderlist)
  {
  $fullfolderoldpath = "$env:programdata\Microsoft\Windows\Start Menu\Programs\$oldstartmenufolder"
  $fullfoldernewstartmenufolder = "$env:programdata\Microsoft\Windows\Start Menu\Programs\$newstartmenufolder"

  #If we already did this particular folder on this machine - skip it
  If ((Test-path $fullfolderoldpath) -AND -NOT ((get-item "$fullfolderoldpath" -force).attributes -ilike "*ReparsePoint*"))
    {
    $counter++ #Rename the old folder to the new (one line)

    #Robocopy is much less fuss than PowerShell file CMDLets (many funky behaviors) and gives logging for free
    Start-Process "Robocopy.exe" -ArgumentList "`"$fullfolderoldpath`" `"$fullfoldernewstartmenufolder`" /MOVE /IS /E /LOG+:`"$env:public\StartMenuFixUp_$newstartmenufolder_$counter_$(get-date -f yyyyMMdd_HHMMss).log`"" -Wait

    #Create a directory junction from the old location to the new, directory symlinks
    #do not appear to work correctly (next line is one line)
    cmd.exe /c "mklink `"$fullfolderoldpath`" `"$fullfoldernewstartmenufolder`" /J"
   
    #Hide the junction - "/L" ensures attrib works applies to the junction and not the target of the junction. (next line is one line)
    attrib +h "$fullfolderoldpath" /L

    # The following line handles all user profiles that may have the same folder by renaming it. (next line is one line)
    Get-ChildItem -force -Path "C:\Users\*\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\$oldstartmenufolder" | ForEach-Object {Rename-item -force -Path $_.FullName -NewName ($_.fullname -replace "$oldstartmenufolder","$newstartmenufolder")}
    }
  }
next line is one line
Attachments:
Download this file (CSI_StartMenuFixUp.zip)CSI_StartMenuFixUp.zip[ ]0.9 Kb