PowerShell File Archive

Administration with PowerShell put simply, is brilliant! I generally use PowerShell at least once or twice a day for various tasks and any opportunity to do a bit of coding I take gladly. My most recent endeavour with this has been an archive job, in which we were using volumes on a virtual machine then backing up SQL databases to them and removing files based on their age and name – 5 days ish and “.BAK”. The old world method of doing this was a little annoying in my opinion as the version written back in the day had to have a line configured for each folder but since when I first encountered the script I was fairly new in the company I let it be.

One of the best things I find about using PowerShell is you can start simple and build, so without any further detail – “Get-ChildItem” (posh DIR)

Get-ChildItem -Path "C:\"

Very handy little bit of code as this forms the base of what we’re after doing, we need to identify all files within a given location and evaluate their age and type so using GCI with the following switches allows us to extent the use.

Get-ChildItem -Path C:\* -Include *BAK -Recurse

I’ll be honest I was a little surprised at how many results came back from this, I obviously don’t keep an eye on my local backups! I had a little muck around with checking the properties at this point as you can dig into various aspects of each object by simply specifying it within the pipe via a select or pipe the object to a format-list so that all properties are exposed, be warned though it can be information overload! Additionally my own personal preference when making scripts like this is to make them as flexible as possible so I started to change it up at this point by adding in variables to make the script more portable, I’m a bit of a nerd when it comes to formatting, specifically tabulation.

The next step was to get the GCI working with these variables, simple enough, declare variable, set variable and GO – no need for a code snippet here. After that I wanted to expose information about each item which met the criteria we were after so I used a foreach loop.

New-Variable -Name strLocation -Value 'C:\' -scope script
New-Variable -Name strFileType -Value "BAK" -scope script

foreach ($file in Get-ChildItem -Path $strLocation* -Include *$strFileType -Recurse)
    {
    write-host $file
    }

In the snippet above I’m just proving to myself that it’s working, if it enters the loop write out the name of the object you have context on.

To define the age of the file be expose one of the backups properties, I’m able to use that information by creating a variable which I then use to form a New-TimeSpan between the current objects CreationTime and the current date (get-date), what I want to do once I’ve got this context is do something with the file, in this case delete it however I like to test things first so I’ll start by writing this out to the current PS window.

New-Variable -Name strLocation -Value 'C:\' -scope script
New-Variable -Name strFileType -Value "BAK" -scope script
New-Variable -Name intAge -Value 7 -scope script

foreach ($file in Get-ChildItem -Path $strLocation* -Include *$strFileType -Recurse)
    {
    $DaysOld = (New-TimeSpan -Start $file.CreationTime -End (get-date -format g)).Days
    if (($DaysOld -ge $intAge) -eq $true)
        {
            write-host $file.FullName
        }
    }

Once happy with the results you can then replace the write-host file out with the actual code required which is shown below.

Remove-Item -LiteralPath $file.FullName 

And that’s it for a basic archive, you can tinker with that as a base but basically that’ll give you a simple delete with limited constraints, personally I might use it for my own PC at home for removing any video files which other users create – handy. Further to that below is my final script which I ended up producing which I introduced a few extra features (Logging and Debug), enjoy.

################################################################################################################################################
## 	Name		: FileArchive.ps1
## 	Function	: Written to remove files from a location
##	Version		: 1
##	Author 		: RT
##	Usage		: Admin/Advanced only
################################################################################################################################################
clear 

$error.clear()

New-Variable -Name strLocation -Value 'C:\' -scope script
New-Variable -Name strFileType -Value "BAK" -scope script
New-Variable -Name strLogFile -Value "" -scope script
New-Variable -Name strLogFileLocation -Value "C:\" -scope script
New-Variable -Name intAge -Value 7 -scope script
New-Variable -Name debug -Value 1 -scope script

$strLogFile = Get-Date -uformat "%d%m"
$strLogFile = "Log_"+$strLogFile + (Get-Date -UFormat "%Y") +".txt"

if (($debug -eq 0)-eq $true)
    {
        new-item -Path $strLogFileLocation -Name $strLogFile -ItemType file
    }

foreach ($file in Get-ChildItem -Path $strLocation* -Include *$strFileType -Recurse)
    {
        $DaysOld = (New-TimeSpan -Start $file.CreationTime -End (get-date -format g)).Days
        if (($DaysOld -ge $intAge) -eq $true)
            {
                $info = @{}
                $info.DateNow=(get-date -Format g)
                $info.DaysOld=$DaysOld
                $info.Created=$file.CreationTime
                $info.DIRLoc=$file.FullName
                $object = New-Object -TypeName PSObject –Prop $info

            if (($debug -eq 0)-eq $true)
                {
                    $object | format-list | out-file -FilePath $strLogFileLocation$strLogFile -append
                    Remove-Item -LiteralPath $file.FullName
                }
            if (($debug -eq 1)-eq $true)
                {
                    $object | format-list
                }
            }
    }

write-host $error

Remove-Variable strLocation
Remove-Variable strFileType
Remove-Variable intAge
Remove-Variable debug
Remove-Variable strLogFile
Remove-Variable strLogFileLocation

Get going with PowerShell

I was scanning through my scripts for PowerShell the other day and I came across a script I was quite fond of back in the day but I can’t remember for the life of me why I wrote it however, I remember using it recently for some mischief! What I wrote was basically a tool for restarting a service against any given host which as you can imagine was used a few times for restarting/stopping MSSQLSERVER on my colleagues PC!

So, without further a-do, the code!!

##################
## FUNCTIONS
##################
function FuncMail
	{#param ($strTo, $strFrom, $strSubject, $strBody, $smtpServer)
	param($To, $From, $Subject, $Body, $smtpServer)
	$msg = new-object Net.Mail.MailMessage
	$smtp = new-object Net.Mail.SmtpClient($smtpServer)
	$msg.From = $From
	$msg.To.Add($To)
	$msg.Subject = $Subject
	$msg.IsBodyHtml = 1
	$msg.Body = $Body
	$smtp.Send($msg)
	}

function FuncRestartService ($server, $service)
{
	$intPingError = 0
	$intError = 0

	$ping = new-object System.Net.NetworkInformation.Ping
	try
		{
			$rslt = $ping.send($server)
		}
	catch
		{
			$intPingError = 1
			$strError = $Error
		}
	if ($intPingError –eq 0) #success: ping
		{
	        write-host “...ping returned, running service restart”
			try
				{
					Restart-Service -InputObject $(Get-Service -Computer $server -Name $service ) -force
				}
			catch
				{
					$intError = 1
					$strError = $Error
				}
			if ($intError -eq 1) #failure: restart - fully exit program
				{
					Write-Host "...an error occurred, notifying by email"
					FuncMail -To $emTo -From $emFrom  -Subject "Server: $server - Error" -Body "$server\$service restart was attempted but failed. Details: $strError" -smtpServer $emSMTP
					break
				}
			else #success: restart
				{
					write-host “...finshed restarting service email sent”
					FuncMail -To $emTo -From $emFrom  -Subject "Server: $server - Restart" -Body "$server\$service has been restarted" -smtpServer $emSMTP
				}
		}
	else #failure: ping - fully exit program
		{
			Write-Host "...ping failed, notifying via email"
			FuncMail -To $emTo -From $emFrom  -Subject "Server: $server - Status" -Body "$server is not responding to ping, please investigate. Details: $strError" -smtpServer $emSMTP
			break
		}
}
##################
## EMAIL Variables
##################
$emTo 		= 'Richard.Thwaites@SomePlace.com'
$emFrom		= 'TMServiceRestart@SomePlace.com'
$emSMTP		= 'AnExchangeServer.com'

##################
## RUN Program
##################
Write-Host "Starting Program..."
Write-Host "...To execute run ""CheckService ServerName"""

FuncRestartService "APCNameHere" "MSSQLSERVER"

Write-Host "...program complete"

If you wanted to you can call two sets of the function if required, suppose you wanted to restart a set of services against an array of servers, a few posts I’ve seen tend to read these in to loops via text files but I prefer the all-in-one way.

Cheers,
Rik