FileSystem.psm1
Set-StrictMode -Version Latest function Compare-FilePath { <# .SYNOPSIS This function checks the hash of 2 files see if they are the same .EXAMPLE PS> Compare-FilePath -ReferencePath 'C:\Windows\file.txt' -DifferencePath '\\COMPUTER\c$\Windows\file.txt' This example checks to see if the file C:\Windows\file.txt is exactly the same as the file \\COMPUTER\c$\Windows\file.txt .PARAMETER ReferencePath The first file path to compare .PARAMETER DifferencePath The second file path to compare #> [OutputType([bool])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] [string]$ReferenceFilePath, [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path -Path $_ -PathType Leaf })] [string]$DifferenceFilePath ) process { try { $ReferenceHash = Get-MyFileHash -Path $ReferenceFilePath $DifferenceHash = Get-MyFileHash -Path $DifferenceFilePath $ReferenceHash.SHA256 -ne $DifferenceHash.SHA256 } catch { Write-Log -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" -LogLevel '3' $PSCmdlet.ThrowTerminatingError($_) } } } function Compare-FolderPath { <# .SYNOPSIS This function checks all files inside of a folder against another folder to see if they are the same .EXAMPLE PS> Compare-FilePath -ReferencePath 'C:\Windows' -DifferencePath '\\COMPUTER\c$\Windows' This example checks to see if the contents in C:\Windows is exactly the same as the contents in \\COMPUTER\c$\Windows .PARAMETER ReferencePath The first folder path to compare .PARAMETER DifferencePath The second folder path to compare #> [OutputType([bool])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path -Path $_ -PathType Container })] [string]$ReferenceFolderPath, [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path -Path $_ -PathType Container })] [string]$DifferenceFolderPath ) process { try { $ReferenceFiles = Get-ChildItem -Path $ReferenceFolderPath -Recurse | Where-Object { -not $_.PsIsContainer } $DifferenceFiles = Get-ChildItem -Path $DifferenceFolderPath -Recurse | Where-Object { -not $_.PsIsContainer } if ($ReferenceFiles.Count -ne $DifferenceFiles.Count) { Write-Log -Message "Folder path '$ReferenceFolderPath' and '$DifferenceFolderPath' file counts are different" -LogLevel '2' $false } elseif (Compare-Object -ReferenceObject ($ReferenceFiles | Get-MyFileHash) -DifferenceObject ($DifferenceFiles | Get-MyFileHash)) { Write-Log -Message "Folder path '$ReferenceFolderPath' and '$DifferenceFolderPath' file hashes are different" -LogLevel '2' $false } else { Write-Log -Message "Folder path '$ReferenceFolderPath' and '$DifferenceFolderPath' have equal contents" $true } } catch { Write-Log -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" -LogLevel '3' $PSCmdlet.ThrowTerminatingError($_) } } } function Copy-FileWithHashCheck { <# .SYNOPSIS This function copies a file and then verifies the copy was successful by comparing the source and destination file hash values. .EXAMPLE PS> Copy-FileWithHashCheck -SourceFilePath 'C:\Windows\file1.txt' -DestinationFolderPath '\\COMPUTER\c$\Windows\file2.txt' This example copied the file from C:\Windows\file1.txt to \\COMPUTER\c$\Windows and then checks the hash for the source file and destination file to ensure the copy was successful. .PARAMETER SourceFilePath The source file path .PARAMETER DestinationFolderPath The destination folder path .PARAMETER Force Overwrite the destination file if one exists #> [OutputType()] [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $True)] [Alias('Fullname')] [string]$SourceFilePath, [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path -Path $_ -PathType Container })] [string]$DestinationFolderPath, [Parameter()] [switch]$Force ) process { try { $CopyParams = @{ 'Path' = $SourceFilePath; 'Destination' = $DestinationFolderPath } ## If the file is already there, check to see if it's the one we're copying in the first place $DestFilePath = "$DestinationFolderPath\$($SourceFilePath | Split-Path -Leaf)" if (Test-Path -Path $DestFilePath -PathType 'Leaf') { if (Compare-FilePath -ReferenceFilePath $SourceFilePath -DifferenceFilePath $DestFilePath) { Write-Log -Message "The file $SourceFilePath is already in $DestinationFolderPath and is the same. No need to copy" } elseif (-not $Force.IsPresent) { throw "The file $SourceFilePath is already in $DestinationFolderPath but is not the same file being copied and -Force was not used." } else { $CopyParams.Force = $true } } Write-Log -Message "Copying [$($CopyParams.Path)] to [[$($CopyParams.Destination)]...." Copy-Item @CopyParams if (Compare-FilePath -ReferenceFilePath $SourceFilePath -DifferenceFilePath $DestFilePath) { Write-Log -Message "The file $SourceFilePath was successfully copied to $DestinationFolderPath." } throw "Attempted to copy the file $SourceFilePath to $DestinationFolderPath but failed the hash check" } catch { Write-Log -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" -LogLevel '3' $PSCmdlet.ThrowTerminatingError($_) } } } function Find-InTextFile { <# .SYNOPSIS Performs a find (or replace) on a string in a text file or files. .EXAMPLE PS> Find-InTextFile -FilePath 'C:\MyFile.txt' -Find 'water' -Replace 'wine' Replaces all instances of the string 'water' into the string 'wine' in 'C:\MyFile.txt'. .EXAMPLE PS> Find-InTextFile -FilePath 'C:\MyFile.txt' -Find 'water' Finds all instances of the string 'water' in the file 'C:\MyFile.txt'. .PARAMETER FilePath The file path of the text file you'd like to perform a find/replace on. .PARAMETER Find The string you'd like to replace. .PARAMETER Replace The string you'd like to replace your 'Find' string with. .PARAMETER UseRegex Use this switch parameter if you're finding strings using regex else the Find string will be escaped from regex characters .PARAMETER NewFilePath If a new file with the replaced the string needs to be created instead of replacing the contents of the existing file use this param to create a new file. .PARAMETER Force If the NewFilePath param is used using this param will overwrite any file that exists in NewFilePath. #> [OutputType([Microsoft.PowerShell.Commands.MatchInfo])] [CmdletBinding(DefaultParameterSetName = 'NewFile')] param ( [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path -Path $_ -PathType 'Leaf' })] [string[]]$FilePath, [Parameter(Mandatory = $true)] [string]$Find, [Parameter()] [string]$Replace, [Parameter()] [switch]$UseRegex, [Parameter(ParameterSetName = 'NewFile')] [ValidateScript({ Test-Path -Path ($_ | Split-Path -Parent) -PathType 'Container' })] [string]$NewFilePath, [Parameter(ParameterSetName = 'NewFile')] [switch]$Force ) begin { if (-not $UseRegex.IsPresent) { $Find = [regex]::Escape($Find) } } process { try { foreach ($File in $FilePath) { if ($Replace) { if ($NewFilePath) { if ((Test-Path -Path $NewFilePath -PathType 'Leaf') -and $Force.IsPresent) { Remove-Item -Path $NewFilePath -Force (Get-Content $File) -replace $Find, $Replace | Add-Content -Path $NewFilePath -Force } elseif ((Test-Path -Path $NewFilePath -PathType 'Leaf') -and (-not $Force.IsPresent)) { Write-Log -Message "The file at '$NewFilePath' already exists and the -Force param was not used" -LogLevel 2 } else { (Get-Content $File) -replace $Find, $Replace | Add-Content -Path $NewFilePath -Force } } else { (Get-Content $File) -replace $Find, $Replace | Add-Content -Path "$File.tmp" -Force Remove-Item -Path $File Rename-Item -Path "$File.tmp" -NewName $File } } else { Select-String -Path $File -Pattern $Find } } } catch { Write-Log -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" -LogLevel '3' $PSCmdlet.ThrowTerminatingError($_) } } } function Register-File { <# .SYNOPSIS A function that uses the utility regsvr32.exe utility to register a file .PARAMETER FilePath The file path #> [OutputType()] [CmdletBinding()] param ( [Parameter()] [ValidateScript({ Test-Path -Path $_ -PathType 'Leaf' })] [string]$FilePath ) process { try { $Result = Start-Process -FilePath 'regsvr32.exe' -ArgumentList "/s `"$FilePath`"" -Wait -NoNewWindow -PassThru Wait-MyProcess -ProcessId $Result.Id if ($Result.ExitCode -ne '0') { throw "Process ID [$($Result.Id)] failed. Exit code was [$($Result.ExitCode)]" } } catch { Write-Log -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" -LogLevel '3' $PSCmdlet.ThrowTerminatingError($_) } } } function Set-MyFileSystemAcl { <# .SYNOPSIS This function allows an easy method to set a file system access ACE .PARAMETER Path The file path of a file .PARAMETER Identity The security principal you'd like to set the ACE to. This should be specified like DOMAIN\user or LOCALMACHINE\User. .PARAMETER Right One of many file system rights. For a list http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.filesystemrights(v=vs.110).aspx .PARAMETER InheritanceFlags The flags to set on how you'd like the object inheritance to be set. Possible values are ContainerInherit, None or ObjectInherit. http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.inheritanceflags(v=vs.110).aspx .PARAMETER PropagationFlags The flag that specifies on how you'd permission propagation to behave. Possible values are InheritOnly, None or NoPropagateInherit. http://msdn.microsoft.com/en-us/library/system.security.accesscontrol.propagationflags(v=vs.110).aspx .PARAMETER Type The type (Allow or Deny) of permissions to add. http://msdn.microsoft.com/en-us/library/w4ds5h86(v=vs.110).aspx #> [OutputType()] [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path -Path $_ })] [string]$Path, [Parameter(Mandatory = $true)] [string]$Identity, [Parameter(Mandatory = $true)] [string]$Right, [Parameter(Mandatory = $true)] [string]$InheritanceFlags, [Parameter(Mandatory = $true)] [string]$PropagationFlags, [Parameter(Mandatory = $true)] [string]$Type ) process { try { $Acl = (Get-Item $Path).GetAccessControl('Access') $Ar = New-Object System.Security.AccessControl.FileSystemAccessRule($Identity, $Right, $InheritanceFlags, $PropagationFlags, $Type) $Acl.SetAccessRule($Ar) if ($PSCmdlet.ShouldProcess($Path, 'ACL Change')) { Set-Acl $Path $Acl } } catch { Write-Log -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" -LogLevel '3' $PSCmdlet.ThrowTerminatingError($_) } } } function Get-FileVersion { <# .SYNOPSIS This function finds the file version of a file. This is useful for applications that don't register themselves properly with Windows Installer .PARAMETER FilePath A valid file path #> [OutputType([string])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateScript({ Test-Path -Path $_ -PathType 'Leaf' })] [string]$FilePath ) process { try { (Get-ItemProperty -Path $FilePath).VersionInfo.FileVersion } catch { Write-Log -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" -LogLevel '3' $PSCmdlet.ThrowTerminatingError($_) } } } function Get-MyFileHash { <# .SYNOPSIS Calculates the hash on a given file based on the seleced hash algorithm. .DESCRIPTION Calculates the hash on a given file based on the seleced hash algorithm. Multiple hashing algorithms can be used with this command. .PARAMETER Path File or files that will be scanned for hashes. .PARAMETER Algorithm The type of algorithm that will be used to determine the hash of a file or files. Default hash algorithm used is SHA256. More then 1 algorithm type can be used. Available hash algorithms: MD5 SHA1 SHA256 (Default) SHA384 SHA512 RIPEM160 .NOTES Name: Get-FileHash Author: Boe Prox Created: 18 March 2013 Modified: 28 Jan 2014 1.1 - Fixed bug with incorrect hash when using multiple algorithms .OUTPUTS System.IO.FileInfo.Hash .EXAMPLE Get-FileHash -Path Test2.txt Path SHA256 ---- ------ C:\users\prox\desktop\TEST2.txt 5f8c58306e46b23ef45889494e991d6fc9244e5d78bc093f1712b0ce671acc15 Description ----------- Displays the SHA256 hash for the text file. .EXAMPLE Get-FileHash -Path .\TEST2.txt -Algorithm MD5,SHA256,RIPEMD160 | Format-List Path : C:\users\prox\desktop\TEST2.txt MD5 : cb8e60205f5e8cae268af2b47a8e5a13 SHA256 : 5f8c58306e46b23ef45889494e991d6fc9244e5d78bc093f1712b0ce671acc15 RIPEMD160 : e64d1fa7b058e607319133b2aa4f69352a3fcbc3 Description ----------- Displays MD5,SHA256 and RIPEMD160 hashes for the text file. .EXAMPLE Get-ChildItem -Filter *.exe | Get-FileHash -Algorithm MD5 Path MD5 ---- --- C:\users\prox\desktop\handle.exe 50c128c5b28237b3a01afbdf0e546245 C:\users\prox\desktop\PortQry.exe c6ac67f4076ca431acc575912c194245 C:\users\prox\desktop\procexp.exe b4caa7f3d726120e1b835d52fe358d3f C:\users\prox\desktop\Procmon.exe 9c85f494132cc6027762d8ddf1dd5a12 C:\users\prox\desktop\PsExec.exe aeee996fd3484f28e5cd85fe26b6bdcd C:\users\prox\desktop\pskill.exe b5891462c9ca5bddfe63d3bae3c14e0b C:\users\prox\desktop\Tcpview.exe 485bc6763729511dcfd52ccb008f5c59 Description ----------- Uses pipeline input from Get-ChildItem to get MD5 hashes of executables. #> [OutputType([PSObject])] [CmdletBinding()] Param ( [Parameter(Position = 0, Mandatory = $true, ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $True)] [Alias("PSPath", "FullName")] [string[]]$Path, [Parameter(Position = 1)] [ValidateSet("MD5", "SHA1", "SHA256", "SHA384", "SHA512", "RIPEMD160")] [string[]]$Algorithm = "SHA256" ) Process { ForEach ($item in $Path) { try { $item = (Resolve-Path $item).ProviderPath If (-Not ([uri]$item).IsAbsoluteUri) { Write-Log -Message ("{0} is not a full path, using current directory: {1}" -f $item, $pwd) $item = (Join-Path $pwd ($item -replace "\.\\", "")) } If (Test-Path $item -PathType Container) { Write-Log -Message ("Cannot calculate hash for directory: {0}" -f $item) -LogLevel 2 Return } $object = New-Object PSObject -Property @{ Path = $item } #Open the Stream $stream = ([IO.StreamReader]$item).BaseStream foreach ($Type in $Algorithm) { [string]$hash = -join ([Security.Cryptography.HashAlgorithm]::Create($Type).ComputeHash($stream) | ForEach-Object { "{0:x2}" -f $_ }) $null = $stream.Seek(0, 0) #If multiple algorithms are used, then they will be added to existing object $object = Add-Member -InputObject $Object -MemberType NoteProperty -Name $Type -Value $Hash -PassThru } $object.pstypenames.insert(0, 'System.IO.FileInfo.Hash') #Output an object with the hash, algorithm and path Write-Output $object #Close the stream $stream.Close() } catch { Write-Log -Message "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)" -LogLevel '3' $PSCmdlet.ThrowTerminatingError($_) } } } } |