Skip to content
Menu
The Lonely Administrator
  • PowerShell Tips & Tricks
  • Books & Training
  • Essential PowerShell Learning Resources
  • Privacy Policy
  • About Me
The Lonely Administrator

Get File Hash

Posted on March 31, 2011

The other day I had the need to calculate MD5 file hashes in order to compare files. The PowerShell Community Extensions has a nifty cmdlet, Get-Hash, that does exactly that. Unfortunately, sometimes even free tools like this aren't an option, as in the case of my client. Or they don't go far enough. So with a little work I wrote my own function to compute a file hash using either MD5, SHA1, or SHA256.

Manage and Report Active Directory, Exchange and Microsoft 365 with
ManageEngine ADManager Plus - Download Free Trial

Exclusive offer on ADManager Plus for US and UK regions. Claim now!

My function, Get-FileHash, takes file names as input and writes a custom object to the pipeline.

[cc lang="PowerShell"]
PS E:\temp> get-filehash winlogon.xml

Date : 3/31/2011 10:04:46 AM
FullName : E:\temp\winlogon.xml
FileHash : 817511618E6ECCF41DC3CA93B5677EDE
HashType : MD5
Filename : winlogon.xml
Size : 17700
[/cc]

The Date property is not the file date but the date of the hash. I can even audit entire folders and use the data later.
[cc lang="PowerShell"]
C:\work> dir c:\scripts\*.ps* | Get-FileHash | Export-CLIXml PSScriptHashes.xml
[/cc]

Here's the function minus the comment based help, which I generated by the way with New-CommentHelp.

[cc lang="PowerShell"]
Function Get-FileHash {

[cmdletbinding(SupportsShouldProcess=$True,ConfirmImpact="Low")]

Param (
[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter file name and path.",
ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[ValidateNotNullorEmpty()]
[Alias("name","file","path")]
[string[]]$PSPath,
[Parameter(Position=1,Mandatory=$False)]
[Alias("hash")]
[ValidateSet("MD5","SHA1","SHA256")]
[string]$Type = "MD5",
[switch]$Force
)

Begin
{
#what time are we starting?
$cmdStart=Get-Date
Write-Verbose "$(Get-Date) Starting $($myinvocation.mycommand)"

if ($force)
{
Write-Verbose "$(Get-Date) Using -Force to find hidden files"
}
#create the hash provider
Switch ($Type) {
"sha1" {
$provider = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider
}
"sha256" {
$provider = New-Object System.Security.Cryptography.SHA256CryptoServiceProvider
}
"md5" {
$provider = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
}
}
Write-Verbose "$(Get-Date) Calculating $type hash"
} #begin

Process
{
Foreach ($name in $PSPath) {
Write-Verbose "$(Get-Date) Verifying $name"

#verify file exists
if (Test-Path -Path $name)
{
Write-Verbose "$(Get-Date) Path verified"

Try
{
#get the file item
if ($force)
{
$file=Get-Item -Path $name -force -ErrorAction "Stop"
}
else
{
$file=Get-Item -Path $name -ErrorAction "Stop"
}
}

Catch
{
Write-Warning "Cannot get item $name. Verify it is not a hidden file (did you use -Force?) and that you have correct permissions."
}

#only process if we were able to get the item
if ($file)
{
#only process if file size is greater than 0
#this will also fail if the object is not from the
#filesystem provider
if ($file.length -gt 0)
{
Write-Verbose "$(Get-Date) Opening file stream"
if ($pscmdlet.ShouldProcess($file.fullname))
{
Try
{
$inStream = $file.OpenRead()
Write-Verbose "$(Get-Date) Computing hash"
$start=Get-Date
$hashBytes = $provider.ComputeHash($inStream)
$end=Get-Date
Write-Verbose "$(Get-Date) hash computed in $(($end-$start).ToString())"
Write-Verbose "$(Get-Date) Closing file stream"
$inStream.Close() | Out-Null

#define a hash string variable
$hashString=""
Write-Verbose "$(Get-Date) hashing file bytes"

foreach ($byte in $hashBytes)
{
#calculate the hash
$hashString+=$byte.ToString("X2")
}

#write the hash object to the pipeline
New-Object -TypeName PSObject -Property @{
Filename=$file.name
FullName=$file.Fullname
FileHash=$hashString
HashType=$Type
Size=$file.length
Date=Get-Date
}
} #try
Catch
{
Write-Warning "Failed to get file contents for $($file.name)."
Write-Warning $_.Exception.Message
}
} #should process
} #if $file.length
else
{
Write-Warning "$(Get-Date) File size for $name is 0 or is not from the filesystem provider."
}
}#if $file
} #if Test-Path
else
{
Write-Warning "$(Get-Date) Failed to find $name."
}

} #foreach $file

} #process

End
{
Write-Verbose "$(Get-Date) Ending $($myinvocation.mycommand)"
#what time did we finish?
$cmdEnd=Get-Date
Write-Verbose "$(Get-Date) Total processing time $(($cmdEnd-$cmdStart).ToString())"
}

} #end function
[/cc]

The function takes file names as a parameter. You can also specify the hash algorithm. The default is MD5. If you will be calculating hash signatures for hidden items you will need to use -Force. Although I'm not changing the file, I included -WhatIf support because calculating a hash for a 30GB VHD takes some time and I often just wanted to test the rest of the function.

Much of the function is comprised of error handling and testing. For example if the item is not in the filesystem or has a 0 size, there is nothing to calculate so I check for those things. Same thing goes for permissions. You must have READ permission otherwise there's no way to calculate a hash. If you don't, then an exception is caught.

The core functionality is derived from creating an appropriate crypto provider.

[cc lang="PowerShell"]
Switch ($Type) {
"sha1" {
$provider = New-Object System.Security.Cryptography.SHA1CryptoServiceProvider
}
"sha256" {
$provider = New-Object System.Security.Cryptography.SHA256CryptoServiceProvider
}
"md5" {
$provider = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
}
}
[/cc]

Once I've confirmed the file, it's contents are read as a stream and the provider computes a hash.

[cc lang="PowerShell"]
$inStream = $file.OpenRead()
Write-Verbose "$(Get-Date) Computing hash"
$start=Get-Date
$hashBytes = $provider.ComputeHash($inStream)
$end=Get-Date
Write-Verbose "$(Get-Date) hash computed in $(($end-$start).ToString())"
Write-Verbose "$(Get-Date) Closing file stream"
$inStream.Close() | Out-Null
[/cc]

This is where disk speed really comes into play. It took over a minute to calculate a hash for a 3GB file on an external USB drive and 17 seconds on an SSD. In any event, the hash is collection of bytes which is converted into a friendly string format.

[cc lang="PowerShell"]
foreach ($byte in $hashBytes) {
#calculate the hash
$hashString+=$byte.ToString("X2")
}
[/cc]

At this point all that remains is to create a custom object with the information I want.
[cc lang="PowerShell"]
#write the hash object to the pipeline
New-Object -TypeName PSObject -Property @{
Filename=$file.name
FullName=$file.Fullname
FileHash=$hashString
HashType=$Type
Size=$file.length
Date=Get-Date
}
[/cc]

I hope you'll let me know what works, and doesn't. I'm also open to enhancement suggestions.

Download Get-FileHash.


Behind the PowerShell Pipeline

Share this:

  • Click to share on X (Opens in new window) X
  • Click to share on Facebook (Opens in new window) Facebook
  • Click to share on Mastodon (Opens in new window) Mastodon
  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on Pocket (Opens in new window) Pocket
  • Click to share on Reddit (Opens in new window) Reddit
  • Click to print (Opens in new window) Print
  • Click to email a link to a friend (Opens in new window) Email

Like this:

Like Loading...

Related

4 thoughts on “Get File Hash”

  1. Pritesh Patel says:
    April 3, 2011 at 11:48 am

    I was working on a similar script, mainly to practise scripting and give myself something new to engage the mind with through PowerShell.

    A script out on the web called Get-MD5 which I was re-writing, and your’s use a stream – is this needed? I ask because I shortened by:

    $x = get-item file.ps1
    $provider = New-Object System.Security.Cryptography.MD5CryptoServiceProvider
    $hashBytes = $provider.ComputeHash($x.openread())
    [string]$hashBytes

    Thanks for any help.
    Kind regards,
    Pritesh Patel

    1. Jeffery Hicks says:
      April 3, 2011 at 11:57 am

      The way I understand hashing you have to read the contents to verify they haven’t changed. Now, whether you read as a stream or some other technique is up for debate and testing I suppose. Can you give me a link to the original Get-MD5 script you found?

      1. Pritesh Patel says:
        April 3, 2011 at 12:05 pm

        Hi Jeff,

        Thanks for the reply, here is a link to the script I found many moons ago:

        http://blogs.msdn.com/powershell/archive/2006/04/25/583225.aspx

        Kind regards,
        Pritesh Patel

      2. Jeffery Hicks says:
        April 3, 2011 at 12:30 pm

        I see now. We’re doing the same thing in terms of reading the file. The old example is combining steps. Mine are separate more for clarity. But there’s no real difference other than the way the hashbytes are formatted.

Comments are closed.

reports

Powered by Buttondown.

Join me on Mastodon

The PowerShell Practice Primer
Learn PowerShell in a Month of Lunches Fourth edition


Get More PowerShell Books

Other Online Content

github



PluralSightAuthor

Active Directory ADSI Automation Backup Books CIM CLI conferences console Friday Fun FridayFun Function functions Get-WMIObject GitHub hashtable HTML Hyper-V Iron Scripter ISE Measure-Object module modules MrRoboto new-object objects Out-Gridview Pipeline PowerShell PowerShell ISE Profile prompt Registry Regular Expressions remoting SAPIEN ScriptBlock Scripting Techmentor Training VBScript WMI WPF Write-Host xml

©2025 The Lonely Administrator | Powered by SuperbThemes!
%d