2013 PowerShell Scripting Games Advanced Event 1 – Parameters Don’t Always Work As Expected
The scenario for this event states the following which has been paraphrased:
Someone allowed log files to build up for around two years and they're starting to causing disk
space issues on the server. You need to build a tool to archive the log files because this has
happened more than once. Applications write log files to a sub-folder in C:\Application\Log
.
Examples are C:\Application\Log\App1
, C:\Application\Log\OtherApp
, and
C:\Application\Log\ThisAppAlso
. The log files have random file names and a .log
extension. After the
log files are written to disk, they're no longer used by the applications.
Your tool should archive log files older than 90 days and move them to \\NASServer\Archives
,
maintaining the source sub-folder stucture such as \\NASServer\Archives\App1
.
The source location, destination location, and file age to archive should each be a parameter. Any errors that happen during the move should be clearly displayed. Output should not be displayed if the command completes successfully.
Using Get-ChildItem
with the Filter
parameter:
I started out working with Get-ChildItem
and was almost set on using the Filter
parameter to
filter the results down to only log files, the problem is that using Filter
in that manner would
also return any file extension that begins with log such as .log1 as shown in the following results:
So unfortunately if you used the Filter
parameter with Get-ChildItem
in your solution, it
doesn't meets the requirements of this scenario since it will archive more than just .log files.
Using Get-ChildItem
with the Include
parameter:
The other parameter that I thought would accomplish the task was Get-ChildItem
with the Include
parameter. The issue I ran into with it is that it only works when using the Recurse
parameter.
At first, using the Recurse
parameter didn't seem to be an issue:
After re-reading the scenario, I decided that I only wanted to move log files in the first level
sub-folders and not in the actual C:\Application\Log
folder and not in sub-folders deeper than the
first level sub-folders so as you can see in the previous image, the Include
parameter doesn't
accomplish that task.
I ended up using something similar to the following which retrieves only the files in the first
level sub-folders that are older than 90 days without using either the Filter
or Include
parameter:
I'll continue this blog post tomorrow to discuss how I dynamically retrieved this information since the scenario states the folder names could change. I'll discuss how I determined the folder names without making an additional call to the file system. I'll also show you how I used the list of folder names instead of iterating through each file individually to move the files to the correct sub-folder on the destination end.
Update 02/09/14:
The link to my solution is no longer valid so I’m posting it here since I’ve received some requests for it:
1function Move-LogFile {
2
3<#
4.SYNOPSIS
5Move files with a .log extension from the SourcePath to the DestinationPath that are older than Days.
6.DESCRIPTION
7Move-LogFile is a function that moves files with a .log extension from the first level subfolders that are specified via
8the SourcePath parameter to the same subfolder name in the DestinationPath parameter that are older than the number of days
9specified in the Days parameter. If a subfolder does not exist in the destination with the same name as the source, it will
10be created. This function requires PowerShell version 3.
11.PARAMETER SourcePath
12The parent path of the subfolders where the log files reside. Log files in the actual SourcePath folder will not be archived,
13only first level subfolders of the specified SourcePath location.
14.PARAMETER DestinationPath
15Parent Path of the Destination folder to archive the log files. The name of the original subfolder where the log files reside
16will be created if it doesn't already exist in the Destination folder. Destination subfolders are only created if one or more
17files need to be archived based on the days parameter. Empty subfolders are not created until needed.
18.PARAMETER Days
19Log files not written to in more than the number of days specified in this parameter are moved to the destination folder location.
20.PARAMETER Force
21Switch parameter that when specified overwrites destination files if they already exist.
22.EXAMPLE
23Move-LogFile -SourcePath 'C:\Application\Log' -DestinationPath '\\NASServer\Archives' -Days 90
24.EXAMPLE
25Move-LogFile -SourcePath 'C:\Application\Log' -DestinationPath '\\NASServer\Archives' -Days 90 -Force
26#>
27
28 [CmdletBinding()]
29 param (
30 [string]$SourcePath = 'C:\Application\Log',
31 [string]$DestinationPath = '\\NASServer\Archives',
32 [int]$Days = 90,
33 [switch]$Force
34 )
35
36 BEGIN {
37 Write-Verbose "Retrieving a list of files to be archived that are older than $($Days) days"
38 try {
39 $files = Get-ChildItem -Path (Join-Path -Path $SourcePath -ChildPath '*\*.log') -ErrorAction Stop |
40 Where-Object LastWriteTime -lt (Get-Date).AddDays(-$days)
41 }
42 catch {
43 Write-Warning $_.Exception.Message
44 }
45
46 $folders = $files.directory.name | Select-Object -Unique
47 Write-Verbose "A total of $($files.Count) files have been found in $($folders.Count) folders that require archival"
48 }
49
50 PROCESS {
51 foreach ($folder in $folders) {
52
53 $problem = $false
54 $ArchiveDestination = Join-Path -Path $DestinationPath -ChildPath $folder
55 $ArchiveSource = Join-Path -Path $SourcePath -ChildPath $folder
56 $ArchiveFiles = $files | Where-Object directoryname -eq $ArchiveSource
57
58 if (-not (Test-Path $ArchiveDestination)) {
59 Write-Verbose "Creating a directory named $($folder) in $($DestinationPath)"
60 try {
61 New-Item -ItemType directory -Path $ArchiveDestination -ErrorAction Stop | Out-Null
62 }
63 catch {
64 $problem = $true
65 Write-Warning $_.Exception.Message
66 }
67 }
68
69 if (-not $problem) {
70 Write-Verbose "Archiving $($ArchiveFiles.Count) files from $($ArchiveSource) to $($ArchiveDestination)"
71 try {
72 If ($Force) {
73 $ArchiveFiles | Move-Item -Destination $ArchiveDestination -Force -ErrorAction Stop
74 }
75 Else {
76 $ArchiveFiles | Move-Item -Destination $ArchiveDestination -ErrorAction Stop
77 }
78 }
79 catch {
80 Write-Warning $_.Exception.Message
81 }
82 }
83
84 }
85 }
86
87 END {
88 Remove-Variable -Name SourcePath, DestinationPath, Days, Force, files, folders, folder,
89 problem, ArchiveDestination, ArchiveSource, ArchiveFiles -ErrorAction SilentlyContinue
90 }
91
92}
µ