Configuring Hyper-V Virtual Networks with PowerShell

I’ve been configuring a Windows Server 2008 R2 Hyper-V deployment in the lab via MDT to a couple of ProLiant DL380 G5’s. I’ve been keeping the deployment as simple as possible, so there’s no SCVMM integrated at this point and as such I’ve need to configure the Hyper-V networking once the OS is deployed to the machine. Naturally, I don’t want to do that manually.

Mailbag - Deploying multiple editions of Office 2010 with App-V

I’m not sure why I didn’t think of this earlier – I get emails from readers fairly regularly and many of them make great topics for blog posts. So here’s the first in a series of posts where I’ll cover interesting questions I get via email and where I think other readers will benefit from a public response.

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

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:\&gt; remove-itembyage -days 0 -path $recent

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

        .EXAMPLE
        PS C:\&gt; remove-itembyage -days 5 -path $recent

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

        .EXAMPLE
        PS C:\&gt; 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:\&gt; 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
        /user-virtualization/profile-clean-up-script-powershell-edition/ for support information.

        .LINK
        /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

Reducing Profile Size with a Profile Clean Up Script

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) &gt; intDays Then
                If UCase(objFile.Name) &lt;&gt; "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

Pagination