I structure this post by breaking down the code according to the requirements. I hope this will allow everyone to better understand how its done.

Ingredients we need

There are 2 arrays that we will require to house the below information:
-filename: array to house all filenames to do multi file transfer
-object: array to house all the info for output in a tabular format later

The code could be something like the below:

$filename = @()
$objects=@()

1st Requirement: User Input

We will need some code to read for inputs to determine what needs to be copied. 1st thing will be the filename, which will be getting via the below code:

$input = Read-host “Input the file name”

This will trigger a user input that will write into a string called input

Remember one of the ingredients we required is an array to house all the filenames to do multi file transfer. This is where it will come in handy. The way we are going to prompt for additional files to added is by looping the question “Input the filename” until the user hit enter without any other inputs. Oh yeah, we also change the question in order to make it more intuitive “Input the filename [Hit Enter for last value]”.

1
2
3
4
$input = Read-host "Input the filename [Hit Enter for last value]"
  if ([string]::IsNullOrEmpty($input)){}
  else{$filename += $input}
} until ([string]::IsNullOrEmpty($input))
Click here for explanation of code above 1st line and the last line is to indicate to perform whatever that is in 2nd to 4th line until it detected a last entry (ENTER key without any input).
2nd line is the question and the request for user input.
3rd line is to detect for the last entry (ENTER key without any input).
4th line will write the input into the array filename until the last entry.


Next thing to do is to request for user input of the source/destination path and write them to string variables for usage later. Command will be like the below:

$inputsource = Read-host "Input the source path "
$inputDestination = Read-host "Input the destination path "


2nd Requirement: File Copy (obviously)

File Copy is done using Powershell command copy-item with parameters Path, Force, Destination. -Path: Full filename (source path plus the filename). -Force: to indicate that copying is mandatory with no questions asked (e.g. sometimes copying a read-only file will result in a prompt to acknowledge the copying of the read-only file). -Destination: for the destination path to copy to. Command will be like the below:

Copy-Item -Path $filename -Force -Destination $inputDestination

Now is the time to copy all the files(their name) stored in the filename array. To do this we will need to read each individual entry in the filename array.

1
2
3
ForEach ($item in $filename) {
        Copy-Item -Path $inputsource\$item -force -Destination $inputDestination
}
Click here for explanation of code above 1st and last line of the code is to read each individual entry in the filename array and perform whatever is in line 2.
Line 2 is the command to do the filecopy.


3rd Requirement: Showing the hashcode of files

Showing of hashcode in Powershell 4+ is a simple one line cmdlet called Get-FileHash. But the powershell version that comes default with my Windows 7 machine is 2.x. Trying to be as vanilla as possible, so I decided against upgrading to Powershell 4+. Fortunately, Boe Prox has solved this in his blog post. Below is the extract I used from his script.

1
2
3
4
$stream = ([IO.StreamReader]"fullfilename").BaseStream
[string]$hash = -join ([Security.Cryptography.HashAlgorithm]::Create("MD5").ComputeHash( $stream ) | ForEach { "{0:x2}" -f $_ })
$null = $stream.Seek(0,0)
$stream.Close()
Click here for explanation of code above 1st line is to read the file as a file stream. The "fullfilename" is the location of the file and its filename combined.
2nd line is the actual command to generate the hash code.("MD5" can be changed to whatever algorithm you want like SHA256).
The last two line is to clean up and close the file stream in order for it to be reused for the next hash code generation.


Optional Requirement: Showing timestamp and size of the files, start and end date time of the deployment

Trying to make this as a more professional looking deployment tool, I have decided to add in the timestamp and size of file as part of the output. It should also be able to show the start and end date time of the deployment Code is straightforward for getting the file details using cmdlets Get-Item to seek for the length(size of file) and LastWriteTime(timestamp or last modified date)

$itemsize = (Get-Item "fullfilename").length
$itemdate = (Get-Item "fullfilename").LastWriteTime


As for the part of deployment start and end date time, modify(hint: change the “Start of Deployment” to “End of Deployment”) as needed and place the below code at the beginning of the program and the end of the program.

Start of Deployment: '' +(get-date -format F).ToString() + [string][TimeZoneInfo]::Local.DisplayName


Output in table format

Remember the final output you see in the introduction? We will need an array to hold all the information and finally output all the results. One of the first ingredient at the start of this post called objects which is an array will be host to that.

Next we will need to add records to the objects array. Records will be the name of the file, one record for the one at the source and another record for the one at the destination. Each entry should be able hold some information of its own. For example a record for file named abc.txt should also have information in the same record its hashcode, timestamp and filesize.

In order to achieve this, we will need to declare a PSObject for each record and store it to the objects array. PSObject is a custom object which allows you to add custom properties to the object itself. For example in this case, I can create a PSObject to house the name of the file and add custom properties to PSObject itself like for hashcode, timestamp.

Example code will be as below:

1
2
3
4
5
6
7
$object = New-Object PSObject -Property @{ 
   Name = $item
}
$object = Add-Member -InputObject $Object -MemberType NoteProperty -Name "Location" -Value $SourceOrDestination -PassThru
$object = Add-Member -InputObject $Object -MemberType NoteProperty -Name "Size" -Value $itemsize -PassThru
$object = Add-Member -InputObject $Object -MemberType NoteProperty -Name "Date Modified" -Value $itemDate -PassThru
$object = Add-Member -InputObject $Object -MemberType NoteProperty -Name "Hash" -Value $Hash -PassThru
Click here for explanation of code above 1st line of the code is to create a new PSObject by the name of object (I used object as we are going to store it into the objects array which seems common sense to me, but if that confuses you, change it into something else)
2nd line is to create the identifier of that object which is the name. ($item is the string variable which will house the filename).
3rd line is to close the object creation.
4,5,6,7 are for adding of custom properties to the object itself which we have already identified as filesize(string variable $itemsize), date modified(string variable $itemDate) and hashcode (string variable $Hash). One more property will be SourceOrDestination(string variable $SourceOrDestination). This is used to differentiate whether we are referring to the file at the Source Location or the file at the Destination location.


Once we have all the information required for the object, we will just add them to the array like below:

$objects += $object

Once we have completed adding all the object to the objects array. We can print the output with the command below:

write-output $objects | format-table -autosize

This command above will print out in a table and autosize to align it for each entry.

Now that we have gotten all the core understanding of everything. It is time to link them all up. I will explain that in the next post