FileSystem.psm1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
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($_)
            }
        }
    }
}