PSFSTools.psm1
New-Alias -Name "mkprj" -Value New-ProjectFolder -Option ReadOnly New-Alias -Name "rdold" -Value Remove-OlderThan -Option ReadOnly New-Alias -Name "bckar" -Value Backup-ArchiveFiles -Option ReadOnly New-Alias -Name "ntemp" -Value New-TemplateFileServer -Option ReadOnly New-Alias -Name "wfs" -Value Write-FileServerFromTemplate -Option ReadOnly New-Alias -Name "gdf" -Value Get-DedupFiles -Option ReadOnly New-Alias -Name "slcf" -Value Show-LatestCreatedFile -Option ReadOnly New-Alias -Name "slwf" -Value Show-LatestWritedFile -Option ReadOnly New-Alias -Name "slaf" -Value Show-LatestAccessedFile -Option ReadOnly function New-ProjectFolder () { <# .SYNOPSIS New project folder .DESCRIPTION Create a project folder and assign ACL with three Active Directory groups: Owner Writer Reader .EXAMPLE New-ProjectFolder -Name Test -LitheralPath C:\Project -Permission Owner,Writer,Reader -OU "OU=Test,DC=Test,DC=local" .EXAMPLE New-ProjectFolder -Name "Test 2" -LitheralPath C:\Project -Permission Writer,Reader -OU "OU=Test,DC=Test,DC=local" -DomainController srv1.dc.local #> [CmdletBinding()] param ( [parameter(mandatory = $true)][ValidateNotNull()][string]$Name, [parameter(mandatory = $true)][ValidateNotNull()][string]$LitheralPath, [parameter(mandatory = $true)][ValidateSet("Reader", "Writer", "Owner")][array]$Permission, [parameter(mandatory = $true)][ValidateNotNull()][string]$OU, [parameter(mandatory = $false)][string]$DomainController, [parameter(mandatory = $false)][switch]$Log ) Set-Variable -Name $OU -Option AllScope if ($DomainController) { Set-Variable -Name $DomainController -Option AllScope } # Verify if Log file exists if ($Log.IsPresent) { if (!([System.Diagnostics.EventLog]::SourceExists("ProjectFolder"))) { New-EventLog -LogName "Application" -Source "ProjectFolder" } } function Get-Error () { if ($Error) { return $false } else { return $true } } function Get-DomainClass ($OrganizationalUnit) { $Domain = ($OrganizationalUnit -split "," | Select-String "DC=" | ForEach-Object { $_ -replace "DC=", "" }) -join "." return $Domain } function Connect-DomainController () { if ($DomainController) { $IPAddress = ([System.Net.Dns]::GetHostAddresses($DomainController)).IPAddressToString if (Test-Connection -ComputerName $IPAddress -Quiet -Count 1) { $ActiveDirectorySession = New-PSSession -ConfigurationName "Microsoft.PowerShell" -ComputerName $DomainController -Authentication Kerberos Import-PSSession $ActiveDirectorySession -AllowClobber -DisableNameChecking -Module ActiveDirectory | Out-Null } Write-Verbose -Message "Connected to domain controller: $DomainController" } else { $DomainClass = Get-DomainClass -OrganizationalUnit $OU $IPAddress = ([System.Net.Dns]::GetHostAddresses($DomainClass)).IPAddressToString -split " " foreach ($IP in $IPAddress) { $DomainController = ([System.Net.Dns]::GetHostbyAddress($IP)).HostName if (Test-Connection -ComputerName $DomainController -Quiet -Count 1) { $ActiveDirectorySession = New-PSSession -ConfigurationName "Microsoft.PowerShell" -ComputerName $DomainController -Authentication Kerberos Import-PSSession $ActiveDirectorySession -AllowClobber -DisableNameChecking -Module ActiveDirectory | Out-Null Write-Verbose -Message "Connected to domain controller: $DomainController" break } } } Get-Error $Global:ActiveDirectorySession = $ActiveDirectorySession } $Error.clear() $FullPath = "$LitheralPath\$Name" # Check if folder already exists Write-Verbose -Message "Check if folder project $FullPath exists" if (Test-Path $FullPath) { $ErrorActionPreference = "Stop" Write-Error -Message "Project already exists." } # Connect to domain controller if (-not(Connect-DomainController)) { $ErrorActionPreference = "Stop" Write-Error -Message "Not connect to domain controller." } # Create folder Write-Verbose -Message "Create folder $FullPath" New-Item -Path $FullPath -ItemType Directory | Out-Null # Remove inheritance and add Administrators group to folder Write-Verbose -Message "Remove inheritance and add Administrators group to folder $FullPath" $Domain = Get-DomainClass -OrganizationalUnit $OU $ACL = Get-Acl -Path $FullPath $ACL.SetAccessRuleProtection($True, $False) $InheritanceFlag = @() $InheritanceFlag += [System.Security.AccessControl.InheritanceFlags]::ContainerInherit $InheritanceFlag += [System.Security.AccessControl.InheritanceFlags]::ObjectInherit $PropagationFlag = [System.Security.AccessControl.PropagationFlags]::None $objType = [System.Security.AccessControl.AccessControlType]::Allow $colRights = [System.Security.AccessControl.FileSystemRights]"FullControl" $objGroup = New-Object System.Security.Principal.NTAccount("BUILTIN", "Administrators") $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule($objGroup, $colRights, $InheritanceFlag, $PropagationFlag, $objType) $ACL.AddAccessRule($objACE) $ACL.SetOwner($objGroup) Set-ACL -Path $FullPath -AclObject $ACL $ACL = Get-Acl -Path $FullPath # Create a permission groups and set permission to folder Write-Verbose -Message "Create a permission groups and set permission to folder $FullPath" if ($Permission -contains "Owner") { # Create Active Directory group $OwnerGroupName = (("$Name" + "_Owners").Replace(" ", "_")).Trim() Write-Verbose -Message "Create a permission for Owner group: $OwnerGroupName" ## Name max 64 char $OwnerGroupName = if ($OwnerGroupName.Length -gt 64) { $OwnerGroupName.Substring(0, 64) } else { $OwnerGroupName } $OwnerGroupSAM = ((("$Name" + ".owners").Replace(" ", "_")).Trim()).ToLower() ## Samaccountname max 256 $OwnerGroupSAM = if ($OwnerGroupSAM.Length -gt 256) { $OwnerGroupSAM.Substring(0, 256) } else { $OwnerGroupSAM } New-ADGroup -Name $OwnerGroupName -SamAccountName $OwnerGroupSAM -GroupCategory Security -GroupScope Global -DisplayName "$Name Owners" -Path $OU -Description "Owners of project folder $FullPath on $($env:computername)" do { Start-Sleep -Seconds 2 } until (Get-ADGroup -Identity $OwnerGroupSAM -ErrorAction SilentlyContinue) Invoke-Command -Session $ActiveDirectorySession -ScriptBlock { repadmin /syncall /AdeP } | Out-Null # Assign permission to folder with Active Directory group $colRights = [System.Security.AccessControl.FileSystemRights]"FullControl" $objGroup = New-Object System.Security.Principal.NTAccount($Domain, $OwnerGroupSAM) $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule($objGroup, $colRights, $InheritanceFlag, $PropagationFlag, $objType) $ACL.AddAccessRule($objACE) Write-Verbose -Message "Assign permission FullControl to $OwnerGroupName on $FullPath" Set-ACL -Path $FullPath -AclObject $ACL } if ($Permission -contains "Writer") { # Create Active Directory group $WriterGroupName = (("$Name" + "_Writers").Replace(" ", "_")).Trim() Write-Verbose -Message "Create a permission for Owner group: $WriterGroupName" ## Name max 64 char $WriterGroupName = if ($WriterGroupName.Length -gt 64) { $WriterGroupName.Substring(0, 64) } else { $WriterGroupName } $WriterGroupSAM = ((("$Name" + ".writers").Replace(" ", "_")).Trim()).ToLower() ## Samaccountname max 256 $WriterGroupSAM = if ($WriterGroupSAM.Length -gt 256) { $WriterGroupSAM.Substring(0, 256) } else { $WriterGroupSAM } New-ADGroup -Name $WriterGroupName -SamAccountName $WriterGroupSAM -GroupCategory Security -GroupScope Global -DisplayName "$Name Writers" -Path $OU -Description "Writers of project folder $FullPath on $($env:computername)" do { Start-Sleep -Seconds 2 } until (Get-ADGroup -Identity $WriterGroupSAM -ErrorAction SilentlyContinue) Invoke-Command -Session $ActiveDirectorySession -ScriptBlock { repadmin /syncall /AdeP } | Out-Null # Assign permission to folder with Active Directory group $colRights = [System.Security.AccessControl.FileSystemRights]"Read,Write,Modify" $objGroup = New-Object System.Security.Principal.NTAccount($Domain, $WriterGroupSAM) $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule($objGroup, $colRights, $InheritanceFlag, $PropagationFlag, $objType) $ACL.AddAccessRule($objACE) Write-Verbose -Message "Assign permissions Read,Write,Modify to $WriterGroupName on $FullPath" Set-ACL -Path $FullPath -AclObject $ACL } if ($Permission -contains "Reader") { # Create Active Directory group $ReaderGroupName = (("$Name" + "_Readers").Replace(" ", "_")).Trim() Write-Verbose -Message "Create a permission for Owner group: $ReaderGroupName" ## Name max 64 char $ReaderGroupName = if ($ReaderGroupName.Length -gt 64) { $ReaderGroupName.Substring(0, 64) } else { $ReaderGroupName } $ReaderGroupSAM = ((("$Name" + ".readers").Replace(" ", "_")).Trim()).ToLower() ## Samaccountname max 256 $ReaderGroupSAM = if ($ReaderGroupSAM.Length -gt 256) { $ReaderGroupSAM.Substring(0, 256) } else { $ReaderGroupSAM } New-ADGroup -Name $ReaderGroupName -SamAccountName $ReaderGroupSAM -GroupCategory Security -GroupScope Global -DisplayName "$Name Readers" -Path $OU -Description "Readers of project folder $FullPath on $($env:computername)" do { Start-Sleep -Seconds 2 } until (Get-ADGroup -Identity $ReaderGroupSAM -ErrorAction SilentlyContinue) Invoke-Command -Session $ActiveDirectorySession -ScriptBlock { repadmin /syncall /AdeP } | Out-Null # Assign permission to folder with Active Directory group $colRights = [System.Security.AccessControl.FileSystemRights]"Read,ReadAndExecute,ListDirectory" $objGroup = New-Object System.Security.Principal.NTAccount($Domain, $ReaderGroupSAM) $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule($objGroup, $colRights, $InheritanceFlag, $PropagationFlag, $objType) $ACL.AddAccessRule($objACE) Write-Verbose -Message "Assign permissions Read,ReadAndExecute,ListDirectory to $ReaderGroupName on $FullPath" Set-ACL -Path $FullPath -AclObject $ACL } # Log the event if ($Log.IsPresent) { if ($?) { Write-EventLog -LogName "Application" -Source "ProjectFolder" -EntryType "Information" -EventID 1 -Category 0 -Message "Project folder $FullPath has been created with permissions "@Permission } else { Write-EventLog -LogName "Application" -Source "ProjectFolder" -EntryType "Error" -EventID 1 -Category 0 -Message "Check the project folder $FullPath; an error are occurred" } } # Remove all session Get-PSSession | Remove-PSSession Write-Host "Created project folder $FullPath with permission: "@Permission } function Remove-OlderThan () { <# .SYNOPSIS Remove files and folders older than days .DESCRIPTION Remove files and folders older than days .EXAMPLE Remove-OlderThan -Path C:\Temp -Days 15 -Recurse #> [CmdletBinding()] param ( [parameter(mandatory = $true)][ValidateNotNull()][string]$Path, $Days = 7, [parameter(mandatory = $false)][switch]$Recurse ) $Days = (Get-Date).AddDays(-$Days) if ($Recurse.IsPresent) { Write-Verbose -Message "Delete files older than $Days, recursively in all the folders" # Delete files older than the $Days. Get-ChildItem -Path $Path -Recurse -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $Days } | Remove-Item -Force -Recurse -Confirm:$false } else { # Delete files older than the $Days. Write-Verbose -Message "Delete files older than $Days" Get-ChildItem -Path $Path -Force | Where-Object { !$_.PSIsContainer -and $_.CreationTime -lt $Days } | Remove-Item -Force -Confirm:$false } # Delete any empty directories left behind after deleting the old files. Write-Verbose -Message "Delete any empty directories left behind after deleting the old files recursively in all the folders" Get-ChildItem -Path $Path -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }).Length -eq 0 } | Remove-Item -Force -Recurse -Confirm:$false } function Backup-ArchiveFiles () { <# .SYNOPSIS Archive files .DESCRIPTION Archive files older than a number of years .EXAMPLE Backup-ArchivedFiles -Path C:\Temp -Years 2 -ArchivePath D:\Temp .EXAMPLE Backup-ArchivedFiles -Path C:\Temp -Years 2 -ArchivePath D:\Temp -Exclude path1,"folder with space" -AllFiles #> [CmdletBinding()] param ( [parameter(mandatory = $true)][ValidateNotNull()][string]$Path, $Years = 1, [parameter(mandatory = $true)][ValidateNotNull()][string]$ArchivePath, [parameter(mandatory = $false)][array]$Exclude, [parameter(mandatory = $false)][switch]$AllFiles, [parameter(mandatory = $false)][switch]$DeleteEmptyFolders, [parameter(mandatory = $false)][switch]$Log ) # Verify if Log file exists if ($Log.IsPresent) { if (!([System.Diagnostics.EventLog]::SourceExists("Archive"))) { New-EventLog -LogName "Application" -Source "Archive" } } # Calculate time span $OlderThan = (Get-Date).AddYears(-$Years) $Year = $OlderThan.Year Write-Verbose -Message "Calculate time span: $OlderThan" # Prepare exclusion patterns if ($Exclude) { foreach ($Exclusion in $Exclude) { $Pattern += "|" + $Exclusion -replace "\\", "\\" -replace ":", "\:" -replace "\.", "\." } $Pattern = $Pattern.TrimStart("|") } else { $Pattern = [void] } Write-Verbose -Message "Prepare exclusion patterns: $Pattern" # Consider all files or not if ($AllFiles.IsPresent) { # Loop the path: only folders foreach ($folder in (Get-ChildItem -Path $Path -Directory -Recurse | Where-Object { $_.FullName -notmatch $Pattern } )) { $Destination = ("$ArchivePath\$Year\" + ($folder.FullName -replace "(^[A-Za-z]\:\\)", "")) -replace "\\$", "" Write-Verbose -Message "Set destination: $Destination" # Check if all files in a folder are older than $Year $all = Get-ChildItem -Path $folder.FullName -File | Where-Object { $_.FullName -notmatch $Pattern } | Measure-Object $older = Get-ChildItem -Path $folder.FullName -File | Where-Object { $_.FullName -notmatch $Pattern -and $_.LastAccessTime -lt $OlderThan } | Measure-Object Write-Verbose -Message "Check if all files in folder $($folder.FullName) are older than $OlderThan; all is $($all.Count) - older is $($older.Count)" if (($all.Count -eq $older.Count) -and ($all.Count -ne 0)) { Write-Verbose -Message "Move files into $($folder.FullName) in $Destination" if (-not (Test-Path -Path $Destination)) { New-Item -Path $Destination -ItemType Directory | Out-Null } Get-ChildItem -Path $folder.FullName -File | Where-Object { $_.FullName -notmatch $Pattern } | Move-Item -Destination "$Destination\" -Force -Confirm:$false if ($Log.IsPresent) { if (Test-Path $Destination) { Write-EventLog -LogName "Application" -Source "Archive" -EntryType "Information" -EventID 1 -Category 0 -Message "Archived file $($folder.FullName) to $Destination successfully" } else { Write-EventLog -LogName "Application" -Source "Archive" -EntryType "Error" -EventID 1 -Category 0 -Message "Archived file $($folder.FullName) failed to $Destination" } } } } } else { # Loop the path: only files foreach ($file in (Get-ChildItem -Path $Path -File -Recurse | Where-Object { $_.FullName -notmatch $Pattern -and $_.LastAccessTime -lt $OlderThan } )) { $Destination = ("$ArchivePath\$Year\" + ($file.FullName -replace "(^[A-Za-z]\:\\)", "")) -replace "\\$", "" Write-Verbose -Message "Move file $($file.FullName) in $Destination" if (-not (Test-Path -Path $(Split-Path -Path $Destination))) { New-Item -Path $(Split-Path -Path $Destination) -ItemType Directory | Out-Null } Move-Item -Path $file.FullName -Destination $Destination -Force -Confirm:$false if ($Log.IsPresent) { if (Test-Path $Destination) { Write-EventLog -LogName "Application" -Source "Archive" -EntryType "Information" -EventID 1 -Category 0 -Message "Archived file $($file.FullName) to $Destination" } else { Write-EventLog -LogName "Application" -Source "Archive" -EntryType "Error" -EventID 1 -Category 0 -Message "Archived file $($file.FullName) failed to $Destination" } } } } # Delete empty folders...or not if ($DeleteEmptyFolders.IsPresent) { Write-Verbose -Message "Delete any empty directories left behind after deleting the old files." Get-ChildItem -Path $Path -Recurse -Force | Where-Object { $_.PSIsContainer -and (Get-ChildItem -Path $_.FullName -Recurse -Force | Where-Object { !$_.PSIsContainer }).Length -eq 0 } | Remove-Item -Force -Recurse -Confirm:$false } } function New-TemplateFileServer () { <# .SYNOPSIS Create file server structure template. .DESCRIPTION Create a xml default template for a file server structure. .EXAMPLE New-TemplateFileServer -Path C:\Temp\fs1.xml #> [CmdletBinding()] param ( [parameter(mandatory = $true)][string] $Path ) $template = @" <?xml version="1.0" encoding="UTF-8" standalone="no" ?> <!-- Root folder --> <folder name="root"> <!-- Permission local group g1 --> <permission inheritance="true|false" type="allow|deny" full="true|false" write="true|false" read="true|false">WORKGROUP\g1</permission> <!-- d1 folder --> <folder name="d1"> <!-- Permission local group g1 --> <permission inheritance="true|false" type="allow|deny" full="true|false" write="true|false" read="true|false">WORKGROUP\g1</permission> <!-- Permission ldap group g2 --> <permission inheritance="true|false" type="allow|deny" full="true|false" write="true|false" read="true|false">DOMAIN\g2</permission> <!-- s2 subfolder --> <folder name="s1"> <!-- Permission ldap group g2 --> <permission inheritance="true|false" type="allow|deny" full="true|false" write="true|false" read="true|false">DOMAIN\g2</permission> </folder> </folder> <!-- d2 folder --> <folder name="d2"> <!-- Permission local group g1 --> <permission inheritance="true|false" type="allow|deny" full="true|false" write="true|false" read="true|false">WORKGROUP\g1</permission> <!-- Permission ldap group g3 --> <permission inheritance="true|false" type="allow|deny" full="true|false" write="true|false" read="true|false">DOMAIN\g3</permission> </folder> </folder> "@ Out-File -FilePath $Path -Encoding utf8 -InputObject $template if (Test-Path -Path $Path -ErrorAction SilentlyContinue) { Write-Host "New template $Path" } else { Write-Error -Message "Unable to write a template $Path" } } function Write-FileServerFromTemplate () { <# .SYNOPSIS Create or modify structure of file server based on template file. .DESCRIPTION Create or modify structure of file server based on template file. The file is a xml file create with New-TemplateFileServer. .EXAMPLE Write-FileServerFromTemplate -Template C:\Temp\fs1.xml .EXAMPLE Write-FileServerFromTemplate -Template C:\Temp\fs1.xml -RootPath D:\FS .EXAMPLE Write-FileServerFromTemplate -Template C:\Temp\fs1.xml -RootPath D:\FS -DeleteDiff #> [CmdletBinding(SupportsShouldProcess = $true)] param ( [parameter(mandatory = $true)][string] $Template, [string] $RootPath = $($PWD.Path), [switch] $DeleteDiff, [switch] $ForceDiff, [switch] $ForceACL ) # Read Template try { [xml] $Template = Get-Content -Path $Template } catch [System.Management.Automation.ArgumentTransformationMetadataException] { throw "$Template is not a xml format." } # Set root $root = Join-Path -Path $RootPath -ChildPath $Template.folder.name Write-Verbose "root $root" # Create a function than walk to xml child function createFSTree ($xml, $root) { $fc = 0 foreach ($e in $xml) { # Create folder structure if ($e.ParentNode.folder -as [array]) { $parent = Join-Path -Path $root -ChildPath $e.ParentNode.folder[$fc].name $tempDirs = $e.ParentNode.folder.name } else { $parent = Join-Path -Path $root -ChildPath $e.ParentNode.folder.name $tempDirs = @($e.ParentNode.folder.name) } # Create folder if not exists if (-not(Test-Path -Path $parent -ErrorAction SilentlyContinue)) { New-Item -Path $parent -ItemType Directory | Out-Null Write-Host "$parent folder created" -ForegroundColor Green } else { Write-Host "$parent folder exists" -ForegroundColor Yellow } # Check DeleteOld if ($DeleteDiff.IsPresent -and $e.ParentNode.folder) { $folders = Get-ChildItem -Path (Get-Item -Path $parent).parent.FullName -Directory foreach ($folder in $folders) { if ($tempdirs -and $tempDirs -notcontains $folder.Name) { if ($ForceDiff -or $PSCmdlet.ShouldContinue("Are you sure delete folder $($folder.FullName) ?", "Delete folder $($folder.FullName).")) { Remove-Item -Path $folder.FullName -Recurse -Force Write-Host "Deleted folder $($folder.FullName)" -ForegroundColor Red } } } } # Apply permissions $ACL = Get-Acl -Path $RootPath [bool]$changed = $false foreach ($p in $e.permission) { $acl_map = [pscustomobject]@{ full = "FullControl" write = "Read,Write,Modify" read = "Read,ReadAndExecute,ListDirectory" type = $p.type inheritance = "InheritOnly" group = $p."#text" } Write-Verbose "Group: $($acl_map.group)" $ACL.SetAccessRuleProtection($true, $true) $InheritanceFlag = @() $InheritanceFlag += [System.Security.AccessControl.InheritanceFlags]::ContainerInherit $InheritanceFlag += [System.Security.AccessControl.InheritanceFlags]::ObjectInherit $PropagationFlag = $( if ($p.inheritance -eq "true") { $acl_map.inheritance } else { "None" } ) $objType = $( if ($acl_map.type -eq "allow") { 0 } else { 1 } ) $colRights = $( if ($p.full -eq "true") { $acl_map.full } elseif ($p.write -eq "true") { $acl_map.write } elseif ($p.read -eq "true") { $acl_map.read } ) $objGroup = New-Object System.Security.Principal.NTAccount($acl_map.group -split '\', -1, 'SimpleMatch') $objACE = New-Object System.Security.AccessControl.FileSystemAccessRule($objGroup, $colRights, $InheritanceFlag, $PropagationFlag, $objType) $currentAcl = Get-Acl -Path $parent $ACL.SetAccessRule($objACE) # Check if changed permission foreach ($permission in $objACE) { if ($currentAcl.Access.IdentityReference -notcontains $permission.IdentityReference) { [bool]$changed = $true } else { foreach ($c in $currentAcl.Access) { # Check IdentityReference if ($c.IdentityReference -eq $permission.IdentityReference) { # Check AccessControlType if ($c.AccessControlType -ne $permission.AccessControlType) { [bool]$changed = $true continue } # Check FileSystemRights if ($c.FileSystemRights -ne $permission.FileSystemRights) { [bool]$changed = $true continue } } } } } } # Check if removed permission foreach ($permission in $currentAcl.Access) { if ($ACL.Access.IdentityReference -notcontains $permission.IdentityReference) { [bool]$changed = $true } } if ($changed) { (Get-Item -Path $parent).SetAccessControl($ACL) Write-Host -ForegroundColor DarkGreen $ACL.AccessToString } elseif ($ForceACL.IsPresent) { (Get-Item -Path $parent).SetAccessControl($ACL) Write-Host -ForegroundColor DarkGreen $ACL.AccessToString } # Check if child has a folder if ($e.folder.folder) { createFSTree -xml $e.folder -root $parent } $fc++ } } # Walk to xml createFSTree -xml $Template.folder -root $root } function Get-DedupFiles () { <# .SYNOPSIS Retrieve all duplicate files from a path. .DESCRIPTION Retrieve all duplicate files from a path. Duplicate files return with additional information. .EXAMPLE Get-DedupFiles -Path C:\Temp .EXAMPLE Get-DedupFiles -Path C:\Temp -Recurse .EXAMPLE Get-DedupFiles -Path C:\Temp -Recurse -Depth 5 #> [CmdletBinding()] param ( [parameter(mandatory = $true)][string] $Path, [int] $Depth, [switch] $Recurse ) # Init result variables $files = @() $duplicates = @() # Splatting parameter $HashArguments = @{ Path = $Path File = $true Recurse = $Recurse.IsPresent } if ($Depth) { $HashArguments.Add("Depth", $Depth) } # Iterate each files traverse root path foreach ($file in (Get-ChildItem @HashArguments)) { # Prepare PSCustomObject $FileHash = [PSCustomObject]@{ Name = $file.Name Path = $file.FullName DedupFile = $null DedupHash = $null Created = $file.CreationTime Modified = $file.LastWriteTime Accessed = $file.LastAccessTime Hash = (Get-FileHash $file.FullName -Algorithm MD5).Hash } # Does it have a duplicate? foreach ($file in $files) { if ($FileHash.Hash -eq $file.Hash) { $FileHash.DedupFile = $file.Path $FileHash.DedupHash = $file.Hash $duplicates += $FileHash break } } $files += $FileHash } # Return duplicates return $duplicates } function Show-LatestCreatedFile () { <# .SYNOPSIS Show the latest created files based on a date or size. .DESCRIPTION Show the latest created files based on a date or size. The size of the files can be specified in bytes 1, 1MB, 1GB, 1TB, 1PB. .EXAMPLE Show-LatestCreatedFile -Path C:\Temp .EXAMPLE Show-LatestCreatedFile -Path C:\Temp -Recurse -Size 50MB .EXAMPLE Show-LatestCreatedFile -Path C:\Temp -Recurse -CreationTime '05/29/2016' #> [CmdletBinding()] param ( [parameter(mandatory = $true)][string] $Path, [int] $Depth, [int] $Size = 1, [datetime] $CreationTime, [switch] $Recurse ) # Splatting parameter $HashArguments = @{ Path = $Path File = $true Recurse = $Recurse.IsPresent } # Get files $files = Get-ChildItem @HashArguments | Where-Object { if ($Size -and $CreationTime) { $_.Length -gt $Size -and $_.CreationTime -gt $CreationTime } elseif ($Size) { $_.Length -gt $Size } elseif ($CreationTime) { $_.CreationTime -gt $CreationTime } } # Order files and print switch -Regex ([math]::truncate([math]::log($Size, 1024))) { '^0' { $files | Sort-Object CreationTime -Descending | Select-Object FullName, CreationTime, @{Name="Size bytes"; Expression={ "{0:N0}" -f ($_.Length) } } } '^1' { $files | Sort-Object CreationTime -Descending | Select-Object FullName, CreationTime, @{Name="Size KB"; Expression={ "{0:n2} KB" -f ($_.Length / 1KB) } } } '^2' { $files | Sort-Object CreationTime -Descending | Select-Object FullName, CreationTime, @{Name="Size MB"; Expression={ "{0:n2} MB" -f ($_.Length / 1MB) } } } '^3' { $files | Sort-Object CreationTime -Descending | Select-Object FullName, CreationTime, @{Name="Size GB"; Expression={ "{0:n2} GB" -f ($_.Length / 1GB) } } } '^4' { $files | Sort-Object CreationTime -Descending | Select-Object FullName, CreationTime, @{Name="Size TB"; Expression={ "{0:n2} TB" -f ($_.Length / 1TB) } } } '^5' { $files | Sort-Object CreationTime -Descending | Select-Object FullName, CreationTime, @{Name="Size PB"; Expression={ "{0:n2} PB" -f ($_.Length / 1PB) } } } default { $files | Sort-Object CreationTime -Descending | Select-Object FullName, CreationTime, @{Name="Size KB"; Expression={ "{0:n2} KB" -f ($_.Length / 1KB) } } } } } function Show-LatestWritedFile () { <# .SYNOPSIS Show the latest modified files based on a date or size. .DESCRIPTION Show the latest modified files based on a date or size. The size of the files can be specified in bytes 1, 1MB, 1GB, 1TB, 1PB. .EXAMPLE Show-LatestWritedFile -Path C:\Temp .EXAMPLE Show-LatestWritedFile -Path C:\Temp -Recurse -Size 50MB .EXAMPLE Show-LatestWritedFile -Path C:\Temp -Recurse -LastWriteTime '05/29/2016' #> [CmdletBinding()] param ( [parameter(mandatory = $true)][string] $Path, [int] $Depth, [int] $Size = 1, [datetime] $LastWriteTime, [switch] $Recurse ) # Splatting parameter $HashArguments = @{ Path = $Path File = $true Recurse = $Recurse.IsPresent } # Get files $files = Get-ChildItem @HashArguments | Where-Object { if ($Size -and $LastWriteTime) { $_.Length -gt $Size -and $_.LastWriteTime -gt $LastWriteTime } elseif ($Size) { $_.Length -gt $Size } elseif ($LastWriteTime) { $_.LastWriteTime -gt $LastWriteTime } } # Order files and print switch -Regex ([math]::truncate([math]::log($Size, 1024))) { '^0' { $files | Sort-Object LastWriteTime -Descending | Select-Object FullName, LastWriteTime, @{Name="Size bytes"; Expression={ "{0:N0}" -f ($_.Length) } } } '^1' { $files | Sort-Object LastWriteTime -Descending | Select-Object FullName, LastWriteTime, @{Name="Size KB"; Expression={ "{0:n2} KB" -f ($_.Length / 1KB) } } } '^2' { $files | Sort-Object LastWriteTime -Descending | Select-Object FullName, LastWriteTime, @{Name="Size MB"; Expression={ "{0:n2} MB" -f ($_.Length / 1MB) } } } '^3' { $files | Sort-Object LastWriteTime -Descending | Select-Object FullName, LastWriteTime, @{Name="Size GB"; Expression={ "{0:n2} GB" -f ($_.Length / 1GB) } } } '^4' { $files | Sort-Object LastWriteTime -Descending | Select-Object FullName, LastWriteTime, @{Name="Size TB"; Expression={ "{0:n2} TB" -f ($_.Length / 1TB) } } } '^5' { $files | Sort-Object LastWriteTime -Descending | Select-Object FullName, LastWriteTime, @{Name="Size PB"; Expression={ "{0:n2} PB" -f ($_.Length / 1PB) } } } default { $files | Sort-Object LastWriteTime -Descending | Select-Object FullName, LastWriteTime, @{Name="Size KB"; Expression={ "{0:n2} KB" -f ($_.Length / 1KB) } } } } } function Show-LatestAccessedFile () { <# .SYNOPSIS Show the latest accessed files based on a date or size. .DESCRIPTION Show the latest accessed files based on a date or size. The size of the files can be specified in bytes 1, 1MB, 1GB, 1TB, 1PB. .EXAMPLE Show-LatestAccessedFile -Path C:\Temp .EXAMPLE Show-LatestAccessedFile -Path C:\Temp -Recurse -Size 50MB .EXAMPLE Show-LatestAccessedFile -Path C:\Temp -Recurse -LastWriteTime '05/29/2016' #> [CmdletBinding()] param ( [parameter(mandatory = $true)][string] $Path, [int] $Depth, [int] $Size = 1, [datetime] $LastAccessTime, [switch] $Recurse ) # Splatting parameter $HashArguments = @{ Path = $Path File = $true Recurse = $Recurse.IsPresent } # Get files $files = Get-ChildItem @HashArguments | Where-Object { if ($Size -and $LastAccessTime) { $_.Length -gt $Size -and $_.LastAccessTime -gt $LastAccessTime } elseif ($Size) { $_.Length -gt $Size } elseif ($LastAccessTime) { $_.LastAccessTime -gt $LastAccessTime } } # Order files and print switch -Regex ([math]::truncate([math]::log($Size, 1024))) { '^0' { $files | Sort-Object LastAccessTime -Descending | Select-Object FullName, LastAccessTime, @{Name="Size bytes"; Expression={ "{0:N0}" -f ($_.Length) } } } '^1' { $files | Sort-Object LastAccessTime -Descending | Select-Object FullName, LastAccessTime, @{Name="Size KB"; Expression={ "{0:n2} KB" -f ($_.Length / 1KB) } } } '^2' { $files | Sort-Object LastAccessTime -Descending | Select-Object FullName, LastAccessTime, @{Name="Size MB"; Expression={ "{0:n2} MB" -f ($_.Length / 1MB) } } } '^3' { $files | Sort-Object LastAccessTime -Descending | Select-Object FullName, LastAccessTime, @{Name="Size GB"; Expression={ "{0:n2} GB" -f ($_.Length / 1GB) } } } '^4' { $files | Sort-Object LastAccessTime -Descending | Select-Object FullName, LastAccessTime, @{Name="Size TB"; Expression={ "{0:n2} TB" -f ($_.Length / 1TB) } } } '^5' { $files | Sort-Object LastAccessTime -Descending | Select-Object FullName, LastAccessTime, @{Name="Size PB"; Expression={ "{0:n2} PB" -f ($_.Length / 1PB) } } } default { $files | Sort-Object LastAccessTime -Descending | Select-Object FullName, LastAccessTime, @{Name="Size KB"; Expression={ "{0:n2} KB" -f ($_.Length / 1KB) } } } } } |