Reducing Profile Size with a Profile Clean Up Script – PowerShell Edition

Delete by Cari McGee

I recently posted a script for removing unnecessary files and pruning files based on their age, which can be used at logoff to keep profile sizes manageable - Reducing Profile Size with a Profile Clean Up Script.

Andrew Morgan (@andyjmorgan) has kindly translated my very basic VBscript to PowerShell. This can be used as a standalone script or the function (remove-itembyage) could be integrated into your own scripts and has the added benefit of in-built help and the ability to run silently.

Just like the original script, this could be executed at logoff, before the profile is saved back to the network, to perform two actions:

  1. Delete all files of a specific file type in a specified folder, including sub-folders
  2. Delete all files older than X days in a specified folder, including sub-folders

For example, you could use the script to delete all .log or temporary files below %APPDATA% that aren’t required to be roamed, or delete all Cookies older than 90 days to keep the Cookies folder to a manageable size.

Note: the script listing below has the -whatif parameter applied when calling the function, so no deletes will occur unless the parameter is removed.

function remove-itembyage{
    <#
        .SYNOPSIS
            remove items from folders recursively.

        .DESCRIPTION
            this function removes items older than a specified age from the target folder

        .PARAMETER Days
            Specifies the ammount of days since the file was last written to you wish to filter on.

        .PARAMETER Path
            Specifies the path to the folder you wish to search recursively.

        .PARAMETER Silent
            Instructs the function not to return any output.

         .EXAMPLE
            PS C:\> remove-itembyage -days 0 -path $recent

            This command searches the $recent directory, for any files, then deletes them.

        .EXAMPLE
            PS C:\> remove-itembyage -days 5 -path $recent

            This command searches the $recent directory, for files older than 5 days, then deletes them.

        .EXAMPLE
            PS C:\> remove-itembyage -days 10 -path $appdata -typefilter "txt,log"

            This command searches the $cookies directory, for files older than 10 days and end with txt or log extensions, then deletes them.

        .EXAMPLE
            PS C:\> remove-itembyage -days 10 -path $cookies -typefilter "txt,log" -silent

            This command searches the $cookies directory, for files older than 10 days and end with txt or log extensions, then deletes them without a report.

        .NOTES
            http://blog.stealthpuppy.com/user-virtualization/profile-clean-up-script-powershell-edition/ for support information.

        .LINK

http://blog.stealthpuppy.com/user-virtualization/profile-clean-up-script-powershell-edition/

    #>

    [cmdletbinding(SupportsShouldProcess=$True)]
    param(
        [Parameter(Mandatory=$true, Position=0,HelpMessage="Number of days to filter by, E.G. ""14""")]
        [int]$days,
        [Parameter(Mandatory=$true, Position=1,HelpMessage="Path to files you wish to delete")]
        [string]$path,
        [string]$typefilter,
        [switch]$silent)

    #check for silent switch
    if ($silent){$ea="Silentlycontinue"}
    Else {$ea="Continue"}

    #check for typefilter, creates an array if specified.
    if (!($typefilter)){$filter="*"}
    Else{$filter=foreach ($item in $typefilter.split(",")){$item.insert(0,"*.")}}

    if (test-path $path){
        $now=get-date
        $datefilter=$now.adddays(-$days)
        foreach ($file in get-childitem "$path\*" -recurse -force -include $filter | where {$_.PSIsContainer -eq $false -and $_.lastwritetime -le $datefilter -and $_.name -ne "desktop.ini"}){
            if (!($silent)){write-host "Deleting: $($file.fullname)"}
            remove-item -literalPath $file.fullname -force -ea $ea
        }#end for
    }#end if

    Else{
        if (!($silent)){write-warning "the path specified does not exist! ($path)"}
    }#end else
}#end function

#Get KnownFolder Paths
$appdata=$env:appdata
$Cookies=(new-object -com shell.application).namespace(289).Self.Path
$History=(new-object -com shell.application).namespace(34).Self.Path
$recent=(new-object -com shell.application).namespace(8).Self.Path
$profile=$env:userprofile

#commands
remove-itembyage -days 0 -path $appdata -typefilter "txt,log" -silent -whatif
remove-itembyage -days 90 -path $cookies -silent -whatif
remove-itembyage -days 14 -path $recent -silent -whatif
remove-itembyage -days 21 -path $history -silent -whatif
remove-itembyage -days 14 -path "$appdata\Microsoft\office\Recent" -silent -whatif
Posted in User Virtualization | Tagged , | 2 Comments

App-V MVP renewed for 2012

MVP header image

I was quite relieved and grateful to receive the Microsoft MVP award again for 2012:

Congratulations! We are pleased to present you with the 2012 Microsoft® MVP Award! This award is given to exceptional technical community leaders who actively share their high quality, real world expertise with others. We appreciate your outstanding contributions in App-V technical communities during the past year.

A big thank-you to the community, other App-V MVPs and the App-V product team. Here’s to another full year.

Posted in Community | Leave a comment

Reducing Profile Size with a Profile Clean Up Script

Delete by Cari McGee

Windows profiles become larger over time – it’s an inescapable fact. This means that if you are using roaming profiles, logons (and logoff) will be longer and longer. It’s not just individual file sizes, but also the number of files stored in a profile that will make the synchronisation process slower.

One approach to reducing profile sizes is to exclude certain folders. A better solution is to ditch roaming profiles and use a third-party solution to manage roaming of the user environment.

However, there will still be folders that need to be roamed to maintain the experience that users expect when moving between devices (i.e. consistency). For those folders we can implement some maintenance to keep them at a manageable size – that is remove files that are not needed in a roaming profile (e.g. log files) or delete files older than a specific number of days.

Warning: there’s a reason that Windows doesn’t do this maintenance itself – only each application vendor will have an understanding of whether specific files are required or can be discarded (hence the roaming and local portions of AppData). However, as any experienced Windows admin knows – many vendors either don’t test for or don’t care about roaming scenarios, therefore I strongly recommend testing this approach before production deployment.

As a part of an upcoming version of this configuration, I’ve created a script that will execute at logoff, before the profile is saved back to the network, that will perform two actions:

  1. Delete all files of a specific file type in a specified folder, including sub-folders
  2. Delete all files older than X days in a specified folder, including sub-folders

So for example, you could use the script to delete all .log files below %APPDATA% or delete all Cookies older than 90 days.

The script is extremely simple on purpose and I recommend testing thoroughly before implementing – use at your own risk; however feedback is welcome.

' Profile clean up - remove unneeded or old files before logoff
' --------------------------------------------------------------
' Original scripts:
'	http://www.wisesoft.co.uk/scripts/vbscript_recursive_file_delete_by_extension.aspx
'	http://ss64.com/vb/syntax-profile.html
'	http://csi-windows.com/toolkit/csigetspecialfolder

'	Version 2.0; 27/12/2011

Option Explicit
On Error Resume Next 'Avoid file in use issues

Dim strExtensionsToDelete, strAppData, strUserProfile, objFSO, strCookies, strHistory, strRecent, objShellApp
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objShellApp = CreateObject("Shell.Application")
Const CSIDL_COOKIES = "&H21"
Const CSIDL_HISTORY = "&H22"
Const CSIDL_RECENT = "&H08"
Const CSIDL_NETHOOD = "&H13"
Const CSIDL_APPDATA = "&H1A"
Const CSIDL_PROFILE = "&H28"

' Folder to delete files from (files will also be deleted from Subfolders)
strUserProfile = objShellApp.NameSpace(cint(CSIDL_PROFILE)).Self.Path
strAppData = objShellApp.NameSpace(cint(CSIDL_APPDATA)).Self.Path
strCookies = objShellApp.NameSpace(cint(CSIDL_COOKIES)).Self.Path
strHistory = objShellApp.NameSpace(cint(CSIDL_HISTORY)).Self.Path
strRecent = objShellApp.NameSpace(cint(CSIDL_RECENT)).Self.Path
strNetHood = objShellApp.NameSpace(cint(CSIDL_NETHOOD)).Self.Path

' Main
RecursiveDeleteByExtension strAppData, "tmp,log"
RecursiveDeleteOlder 90, strCookies
RecursiveDeleteOlder 14, strRecent
RecursiveDeleteOlder 21, strHistory
RecursiveDeleteOlder 21, strNetHood
RecursiveDeleteOlder 14, strAppData & "\Microsoft\Office\Recent"
'RecursiveDeleteOlder 5, strAppData & "\Sun\Java\Deployment\cache"
'RecursiveDeleteOlder 3, strAppData & "\Macromedia\Flash Player"
'RecursiveDeleteOlder 14, strUserProfile & "\Oracle Jar Cache"

Sub RecursiveDeleteByExtension(ByVal strPath,strExtensionsToDelete)
	' Walk through strPath and sub-folders and delete files of type strExtensionsToDelete
	Dim objFolder, objSubFolder, objFile, strExt

	If objFSO.FolderExists(strPath) = True Then
		Set objFolder = objFSO.GetFolder(strPath)
		For Each objFile in objFolder.Files
			For each strExt in Split(UCase(strExtensionsToDelete),",")
				If Right(UCase(objFile.Path),Len(strExt)+1) = "." & strExt then
					WScript.Echo "Deleting: " & objFile.Path
					objFile.Delete(True)
					Exit For
				End If
			Next
		Next
		For Each objSubFolder in objFolder.SubFolders
			RecursiveDeleteByExtension objSubFolder.Path,strExtensionsToDelete
		Next
	End If
End Sub

Sub RecursiveDeleteOlder(ByVal intDays,strPath)
	' Delete files from strPath that are more than intDays old
	Dim objFolder, objFile, objSubFolder

	If objFSO.FolderExists(strPath) = True Then
		Set objFolder = objFSO.GetFolder(strPath)
		For each objFile in objFolder.files
			If DateDiff("d", objFile.DateLastModified,Now) > intDays Then
				If UCase(objFile.Name) <> "DESKTOP.INI" Then  ' Ensure we don't delete desktop.ini
					WScript.Echo "Deleting: " & objFile.Path
					objFile.Delete(True)
				End If
			End If
		Next
		For Each objSubFolder in objFolder.SubFolders
			RecursiveDeleteOlder intDays,objSubFolder.Path
		Next
	End If
End Sub
Posted in User Virtualization | Tagged | 1 Comment

Don’t put yourself at risk by virtualizing Adobe Reader X

Adobe released a new security advisory for Reader and Acrobat 9 and X this week to address details of an upcoming fix to these versions for a 0 day vulnerability. Exploits for this vulnerability exist for Reader and Acrobat 9 and are currently active:

A critical vulnerability has been identified in Adobe Reader X (10.1.1) and earlier versions for Windows and Macintosh, Adobe Reader 9.4.6 and earlier 9.x versions for UNIX, and Adobe Acrobat X (10.1.1) and earlier versions for Windows and Macintosh. This vulnerability (CVE-2011-2462) could cause a crash and potentially allow an attacker to take control of the affected system. There are reports that the vulnerability is being actively exploited in limited, targeted attacks in the wild against Adobe Reader 9.x on Windows.

Since the release of Reader and Acrobat X, there have been no malware that has been effective against the Protected Mode (sandbox) feature of version X. From Adobe’s blog post on this issue:

I’d like to take this moment to encourage any remaining users still running Adobe Reader or Acrobat 9.x (or worse, older unsupported versions) to PLEASE upgrade to Adobe Reader or Acrobat X. We put a tremendous amount of work into securing Adobe Reader and Acrobat X, and, to date, there has not been a single piece of malware identified that is effective against a version X install. Help us help you by running the latest version of the software!

If you have any version of Adobe Reader other than X deployed, you should seriously consider migrating to the new version as a matter of priority. That’s not “lets consider doing this in the next month” – you should stop reading this post and get started deploying Reader X now.

Furthermore if are deploying or have deployed Reader X, I can’t recommend virtualizing it with application virtualization. The reason for this is that Protected Mode is not compatible and is not supported with application virtualization. It doesn’t work with Citrix App Streaming, Microsoft App-V or VMware ThinApp (it may be possible with the current version of ThinApp, but I haven’t confirmed).

[Update: thanks to prompting from Dan Gough, I've confirmed that Protected Mode in Reader X (10.1.1), works under App-V 4.6.1.30091 (Hotfix 4)]

[Update 2: Protected Mode in Reader X is confirmed to work under ThinApp 4.6.2 and 4.7. You'll have to update your virtual applications and re-enable Protected Mode with the latest releases]

In short – leaving Protected Mode enabled will protect your users and devices and because Protected Mode has been incompatible with the isolation that application virtualisation introduces, I recommend that you do not deploy Reader X with application virtualization solutions unless you are using the very latest versions.

But.. what about those scenarios when a virtualized application needs to call a locally installed Reader X? Until the app virt vendors fully support Protected Mode, the best you can do is ensure that Protected Mode is only disabled when Reader runs within the virtualization environment (using a tool like PolicyPak) and is not completely disabled. Until then, the best we can do is cross our fingers and hope it doesn’t happen to us.

Posted in Virtualisation | Tagged | Leave a comment

Delivering Office with App-V – The Need for Profile Management

Because Office is a core application of most desktop deployments, user interaction with Office and the user experience are important factors in the deployment of Office. From an administration perspective, providing a seamless user experience requires managing the user preferences of an application, independent of the application delivery method.

Multiple App-V packages are common

Microsoft recommends sequencing applications on the same operating system as the target clients are running. This means that if your target clients are running Windows XP and Windows 7, then you should create two App-V packages for each application – one for each operating system.

However, in practice it is often advisable to sequence on the lowest common denominator. In the example with Windows XP and Windows 7 clients, sequencing should be performed on Windows XP. In the event that a package does not then execute correctly on Windows 7, then the application should be re-sequenced on Windows 7.

The same applies to x86 and x64 processor architectures – if you are deliverying 32-bit applications to both x86 and x64 Windows, you should sequence in a 32-bit Windows environment. If you find that a 32-bit virtual application package executes OK on x86 Windows but not on x64 Windows, you will have to create two packages, one for each processor architecture.

There are several reasons for this, but they’re out of scope of a discussion on profile management; however what this highlights is that if you have multiple packages for the same application due to different operating systems and/or processor architectures, again the only way to improve the user experience is to rely on a third party profile management solution that works independently of the App-V package.

App-V and User Profiles

The default behaviour of App-V is to not only virtualize the application, but also the user profile locations for that application (HKEY_CURRENT_USER and %APPDATA%). This means that that profile information for the Microsoft Office packages will be stored, in its entirety, in the PKG.

The implication of this is that the settings for a virtualized Office package will be specific to that package – that is, a user’s Office settings will not only be specific to a version of Office but also specific to an individual Office package.

Consider the following scenarios:

  • A user moves between desktops where Office Standard has been deployed to the first desktop, but Office Professional has been deployed to the second. These will be different App-V packages, so by default, no user preferences will be shared
  • You create an Office package which has been released to production and later find issues with the package that requires re-creating it from scratch – user preferences from the old package will not be shared with the new package
  • You find that you need to create multiple Office packages for different platforms – for example a package for desktops and a package for Remote Desktop Session Host servers. These are separate App-V packages and user preferences will not be consistent across those packages

Each scenario will result in separate App-V packages for the same applications.

If you need to upgrade a package or migrate between Office versions, you now have a further challenge that you would not have if Office were installed instead of virtualized.

By implementing a 3rd party profile management solution, you gain the ability to manage user’s Office preferences independent of the Office version (App-V package version or Office version) and remove the reliance on a specific Office package. A profile management solution will allow you to create, update and re-create Office packages without affecting the end-user experience.

What solution should I use?

The user profile management or user state virtualization tools built into Windows aren’t able to see into the App-V virtual environment and therefore aren’t able to manage an application user preferences independent of the App-V package. If you would like to manage user preferences more granularly, a 3rd party solution will be required.

A profile management solution that is capable of managing user preferences inside and across App-V packages will provide you with the flexibility and consistency of user experience required to support a core application like Microsoft Office. Without providing users with a consistent user experience or one that matches their existing Office deployments, user acceptance will be low.

For an objective comparison of the 3rd party solutions available, see the following white paper: UEM Smackdown: Head-to-head analysis of Appsense, Citrix, Immidio, Liquidware Labs, Microsoft, Quest, RES, Scense, Tricerat and others

Posted in Deployment | Tagged , | 3 Comments