The Function Make-ItemRotate allow to rotate items in specific location that name start like specific prefix. It have a simple versioning system for hold old copies based on the last write date, or simply numbering the next version.

It will be very helpful in scripting backup system to hold old copies and do not overwrite it. Script using only native PowerShell cmdlets then may be use without having to install additional components.

Current version: 1.1

PowerShell
Edit|Remove
function Make-ItemRotate 
{ 
    <# 
    .SYNOPSIS 
        Funtion that allow rotate items in location. 
 
    .DESCRIPTION 
        The Make-ItemRotate cmdlet allow rotate specific items in location. It usefull in backup to not overwrite but save a copy in different location. 
         
    .PARAMETER RotateCount 
        Specifies how many copies you need. Rest of copies will be remove or move to archive location. WARNING! Real copy is RotateCount-1. Why ? If RotateCount is 3 then two copies are old and it hold one place for new backup. 
 
    .PARAMETER Path 
        Specifies a local path.  
 
    .PARAMETER Prefix 
        Specifies prefix of items that will be rotate         
             
    .PARAMETER Sufix 
        Specifies sufix of items. It may be %d (add date of last write to item  name) or %n (add numer to item name). Default is %d. 
 
    .PARAMETER ArchivePath 
        Specifies a path where will be move old copies. If not, old copies will be removed. When item is moving to archive location his name is always rename with %d sufix. 
 
    .PARAMETER MoveAsJob 
        Try move or remove old item as job -asynchronously.         
             
    .PARAMETER GetRaport 
        Get report about current items and archive location. Also change Write-Verbose to Write-Output for save this to a logfile. 
         
    .EXAMPLE 
        PS C:\> Make-ItemRotate -Path P:\test -Prefix "Nowy" -Verbose -Sufix "_(%n)" -ArchivePath P:\test2 -RotateCount 3 -GetRaport 
 
        VERBOSE: Performing operation "Rotate object Nowy*" on Target "P:\test". 
        Rotate history: 3 
        Items patch: P:\test 
        Archive patch: P:\test2 
        Items prefix: Nowy 
        Items sufix: _(%n) 
        Move As Job: False 
 
        Item P:\test\Nowy folder is too old. 
        Rename  P:\test\Nowy folder, to: Nowy_(20111005T105615367562) and move to: P:\test2. 
        Item P:\test\Nowy folder (2) is too old. 
        Rename  P:\test\Nowy folder (2), to: Nowy_(20111005T105616433668) and move to: P:\test2. 
        Rename  P:\test\Nowy folder (3), to: Nowy_(2). 
        Rename  P:\test\Nowy folder (4), to: Nowy_(1). 
        Get original location (P:\test) status. 
 
            Directory: P:\test 
 
        Mode                LastWriteTime     Length Name 
        ----                -------------     ------ ---- 
        d----        2011-10-05     10:56            Nowy_(2) 
        d----        2011-10-05     10:56            Nowy_(1) 
        VERBOSE:  Get archive location (P:\test2) status. 
 
            Directory: P:\test2 
 
        Mode                LastWriteTime     Length Name 
        ----                -------------     ------ ---- 
        d----        2011-10-05     10:56            Nowy_(20111005T105615367562) 
        d----        2011-10-05     10:56            Nowy_(20111005T105616433668) 
 
    .NOTES 
        Author: Michal Gajda 
        Blog  : http://commandlinegeeks.com/ 
    #> 
     
    [CmdletBinding( 
        SupportsShouldProcess=$True, 
        ConfirmImpact="low"  
    )] 
    param( 
        [parameter()] 
        [ValidateScript({$_ -ge 2})] 
        [Int]$RotateCount = 3, 
        [parameter(Mandatory=$true)] 
        [ValidateNotNullOrEmpty()] 
        [String]$Path, 
        [parameter(Mandatory=$true)] 
        [ValidateNotNullOrEmpty()] 
        [String]$Prefix, 
        [String]$Sufix = "_%d", 
        [String]$ArchivePath = "", 
        [Switch]$MoveAsJob = $false, 
        [Switch]$GetRaport = $false 
    ) 
 
    if ($pscmdlet.ShouldProcess($Path,"Rotate object $Prefix*"))  
    { 
         
        Switch($Sufix) 
        { 
            {$_ -match "%d"} {$RotateType = "Date"} 
            {$_ -match "%n"} {$RotateType = "Number"} 
            default {$RotateType = "Date"} 
        } 
 
        if($GetRaport)  
        {  
            Write-Output "Rotate Report:" 
            Write-Output "Rotate history: $RotateCount"  
            Write-Output "Items patch: $Path" 
            Write-Output "Archive patch: $ArchivePath" 
            Write-Output "Items prefix: $Prefix" 
            Write-Output "Items sufix: $Sufix" 
            Write-Output "Move As Job: $MoveAsJob`n" 
        } 
        else  
        {  
            Write-Verbose "Rotate Report:" 
            Write-Verbose "Rotate history: $RotateCount"  
            Write-Verbose "Items patch: $Path" 
            Write-Verbose "Archive patch: $ArchivePath" 
            Write-Verbose "Items prefix: $Prefix" 
            Write-Verbose "Items sufix: $Sufix" 
            Write-Verbose "Move As Job: $MoveAsJob`n" 
        }     
                 
        $ChildItem = Get-ChildItem -Path $Path "$Prefix*"  | Sort-Object LastWriteTime  
        $ChildItemCount = ($ChildItem | Measure-Object).Count 
        $rotateFlag = $false 
         
        ForEach($item in $ChildItem) 
        { 
            if($item.PSIsContainer)  
            {  
                $itemType = "folder" 
                $itemExtension = "" 
            } 
            else  
            {  
                $itemType = "file"  
                $itemExtension = $item.Extension 
            }         
 
            if($RotateCount -le $ChildItemCount) 
            { 
                if($GetRaport) { Write-Output "Item $($item.FullName) is too old." } 
                else { Write-Verbose "Item $($item.FullName) is too old." }     
                 
                if($ArchivePath -ne "" ) 
                { 
                    if(Test-Path($ArchivePath)) 
                    { 
                        $CurrentSufix = $Sufix -replace "%d|%n",($item.LastWriteTime.ToString("yyyyMMddTHmmssffffff")) 
                        if($item.Name -eq ($Prefix+$CurrentSufix+$itemExtension)) 
                        { 
                            if($GetRaport) { Write-Output "Ignore rename $itemType: $($item.FullName), just move to $ArchivePath." } 
                            else { Write-Verbose "Ignore rename $itemType: $($item.FullName), just move to $ArchivePath." }                             
                             
                            if($MoveAsJob) 
                            { 
                                Start-Job -ScriptBlock {Move-Item -Path $item.FullName -Destination $ArchivePath}  
                            } 
                            else 
                            { 
                                Move-Item -Path $item.FullName -Destination $ArchivePath 
                            } 
                        } 
                        else 
                        { 
                            if($GetRaport) { Write-Output "Rename $itemType: $($item.FullName), to: $($Prefix+$CurrentSufix+$itemExtension) and move to: $ArchivePath." } 
                            else { Write-Verbose "Rename $itemType: $($item.FullName), to: $($Prefix+$CurrentSufix+$itemExtension) and move to: $ArchivePath." }                             
                             
                            if($MoveAsJob) 
                            {                         
                                Start-Job -ScriptBlock { 
                                    Rename-Item -Path $item.FullName -NewName ($Prefix+$CurrentSufix+$itemExtension) 
                                    Move-Item -Path (Join-path -Path $item.PSParentPath -ChildPath ($Prefix+$CurrentSufix+$itemExtension)) -Destination $ArchivePath  
                                } 
                            } 
                            else 
                            { 
                                Rename-Item -Path $item.FullName -NewName ($Prefix+$CurrentSufix+$itemExtension) 
                                Move-Item -Path (Join-path -Path $item.PSParentPath -ChildPath ($Prefix+$CurrentSufix+$itemExtension)) -Destination $ArchivePath  
                            }     
                        }     
                    } 
                    else 
                    { 
                        Write-Warning "Path don't exist: $ArchivePath" 
 
                        if(!$rotateFlag) 
                        { 
                            $numberOfDigits = ([string]$ChildItemCount).Length  
                        }     
                        $rotateFlag = $true 
                    } 
                } 
                else 
                { 
                    if($GetRaport) { Write-Output "Remove Item: $($item.FullName)." } 
                    else { Write-Verbose "Remove Item: $($item.FullName)." } 
                         
                    Remove-Item -Path  $item.FullName -Recurse -Force 
                } 
            } 
            else 
            { 
                if(!$rotateFlag) 
                { 
                    $numberOfDigits = ([string]$RotateCount).Length  
                } 
                $rotateFlag = $true 
            } 
             
            if($rotateFlag) 
            { 
                Switch($RotateType) 
                { 
                    "Date" {$CurrentSufix = $Sufix -replace "%d",($item.LastWriteTime.ToString("yyyyMMddTHmmssffffff"))} 
                    "Number" {$CurrentSufix = $Sufix -replace "%n",("{0:D$numberOfDigits}" -$ChildItemCount)} 
                } 
                     
                if($item.Name -eq ($Prefix+$CurrentSufix+$itemExtension)) 
                { 
                    if($GetRaport) { Write-Output "Ignore rename $itemType: $($item.FullName)." } 
                    else { Write-Verbose "Ignore rename $itemType: $($item.FullName)." }                     
                } 
                else 
                {                 
                    $tryAgain = 0 
                    do 
                    { 
                        if($tryAgain -eq 0)  
                        { 
                            $newName = $Prefix+$CurrentSufix+$itemExtension 
                        } 
                        else 
                        { 
                            $newName = $Prefix+$CurrentSufix+("-"+$tryAgain)+$itemExtension 
                        } 
                        $tryAgain++ 
                    } 
                    while(Test-Path -Path (Join-path -Path $item.PSParentPath -ChildPath $newName)) 
                     
                    if($GetRaport) { Write-Output "Rename $itemType: $($item.FullName), to: $newName." } 
                    else { Write-Verbose "Rename $itemType: $($item.FullName), to: $newName." }     
                     
                    Rename-Item -Path $item.FullName -NewName $newName 
                }     
                     
            } 
            $ChildItemCount-- 
        } 
         
        if($GetRaport) 
        { 
            if($GetRaport) { Write-Output "Get original location ($path) status." } 
            else { Write-Verbose "Get original location ($path) status." }     
             
            Get-ChildItem -Path $Path "$Prefix*"  | Sort-Object LastWriteTime  
             
            if($ArchivePath -ne "" ) 
            { 
                if($GetRaport) { Write-Output "Get archive location ($ArchivePath) status." } 
                else { Write-Verbose "Get archive location ($ArchivePath) status." }     
 
                Get-ChildItem -Path $ArchivePath "$Prefix*"  | Sort-Object LastWriteTime  
            } 
         
        } 
    }     
}