PowerShell to Copy folders with read only attributes

Hi,

I’m new to PowerShell and need guidance on a script I’m currently building.

Requirements - the script needs to look through a bunch of files to search for a specific date. If the file is older than this date it needs to be moved to another location with read only attributes.  With regards to the read only attributes this means the share permissions are not changed just the NTFS permissions are altered. This is so the file is unable to be altered. If the file needs updating, then it is moved to a new location and saved.

This is what I have so far. It sort of works but when a file is copied no folder structure is added.  
$StrSource ="D:\deptkp\*.*"  #path to source files
$StrTarget= "D:\archive    #path to where source files are going
$Date = "3"

Get-ChildItem -Recurse -Path $StrSource | Where-Object {$_.LastWriteTime -lt (get-date).AddDays($Date)} | Copy-Item -Destination $StrTarget -Force | Set-ItemProperty -Name IsReadOnly -Value $true 

Open in new window


Also is there a way to output the copy progress to the screen

Currently being run a server 2012r2

Thanks Nick
Nicholas RefaloAsked:
Who is Participating?
 
Jose Gabriel Ortega CConnect With a Mentor CEO J0rt3g4 Consulting ServicesCommented:
This will be my answer:

[CmdletBinding()]
param(
    [Parameter(Mandatory=$true,Position=0)]$source,
    [Parameter(Mandatory=$true,Position=1)]$target,
    [Parameter(Mandatory=$false,Position=2)]$Days=3
)

[int]$GLOBAL:FilesCopied=0 #variable to determine the files that have been copied
[int]$GLOBAL:FilesExisting=0   #variable to determine the existing files
[int]$GLOBAL:TotalDirectories=0  #variable to determine the number of directories created


function Copy-Info{
    [CmdletBinding()]
    param(
        [Parameter(Position=0,mandatory=$true)] [string]$sourceDir,
        [Parameter(Position=1,mandatory=$true)] [string]$targetDir,
        [Parameter(Position=2,mandatory=$true)] [string]$DaysBehind

    )
    BEGIN{
        if(! [System.IO.Directory]::Exists($targetDir) ){
           [System.IO.Directory]::CreateDirectory($targetDir) | Out-Null
            Set-DateAttributes -OriginalFilePath $sourceDir -TargetFilePath $targetDir -folder
            $GLOBAL:TotalDirectories++
            Write-Host -ForegroundColor Cyan "Folder ""$targetDir"" created..."
        }
    }
    PROCESS{
        foreach($file in [System.IO.Directory]::GetFiles($sourceDir) ){
            #generalcounter

            $FilePath=[System.IO.Path]::Combine($targetDir, [System.IO.Path]::GetFileName($file))
            $FileInfoNew = new-object System.IO.FileInfo($file)


            if(![System.IO.File]::Exists($FilePath) ){ #IF doesn't exists, check the lastwritedate attribute to move it or not.
                if($FileInfoNew.LastWriteTime -lt [Datetime]::Now.AddDays(-$DaysBehind)){
                    #copy it
                    [System.IO.File]::Copy($file, $FilePath)
                    #set attributes of date
                    Set-DateAttributes -OriginalFilePath $file -TargetFilePath $FilePath
                    #Set ALC.
                    Set-FileALC -OFile $file -TFile $FilePath
                    #SetReadOnly
                    Set-ItemProperty -Path $FilePath -Name IsReadOnly -Value $true
                }
                $GLOBAL:FilesCopied++
            }
            else{
                #The file already exist but set the Date properties and set ALC
			    $FileInfoExisting = new-object System.IO.FileInfo($FilePath)
			    $FileInfoNew      = new-object System.IO.FileInfo($file)
                $GLOBAL:FilesExisting++
                #setattributes
                Set-DateAttributes -OriginalFilePath $file -TargetFilePath $FilePath
                #Set ALC.
                Set-FileALC -OFile $file -TFile $FilePath
                #SetReadOnly
                Set-ItemProperty -Path $FilePath -Name IsReadOnly -Value $true
            }
        }
        
        Set-FolderALC -OFolder $sourceDir -TFolder $targetDir

        foreach($dir in [System.IO.Directory]::GetDirectories($sourceDir) ){
            $test = [System.IO.Path]::Combine($targetDir, (New-Object -TypeName System.IO.DirectoryInfo -ArgumentList $dir).Name)
            Copy-Info -sourceDir $dir -targetDir $test -DaysBehind $DaysBehind
            Set-DateAttributes -OriginalFilePath $dir -TargetFilePath $test -folder
        }
    }
    END{}
    

    #get source files
    
 }
function Set-DateAttributes{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]$OriginalFilePath,
        [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$true)]$TargetFilePath,
        [Parameter(Mandatory=$false,Position=2,ValueFromPipeline=$true)][switch]$folder
    )
    BEGIN{
        [int]$logcounter=0
    }
    PROCESS{
        if(!($folder)){
            [System.IO.FileInfo] $fi = New-Object System.IO.FileInfo -ArgumentList $originalFilePAth
            [System.IO.File]::SetCreationTime($targetFilePath,$fi.CreationTime)
            [System.IO.File]::SetLastWriteTime($TargetFilePath,$fi.LastWriteTime)
            [System.IO.File]::SetLastAccessTime( $TargetFilePath,$fi.LastAccessTime)
        }
        else{
            [System.IO.DirectoryInfo]$di = New-Object System.IO.DirectoryInfo -ArgumentList $OriginalFilePath
            [System.IO.Directory]::SetCreationTime($TargetFilePath,$di.CreationTime)
            [System.IO.Directory]::SetLastWriteTime($TargetFilePath,$di.LastWriteTime)
            [System.IO.Directory]::SetLastAccessTime($TargetFilePath,$di.LastAccessTime)
        }
    }
    END{
        
    }
}
function Set-FolderALC{            
	[cmdletbinding()]            
	param(
		[Parameter(Mandatory=$true,position=0)][string]$OFolder,
        [Parameter(Mandatory=$true,position=1)][string]$TFolder
	)
	BEGIN{
        $OAcl = (Get-Item $OFolder).GetAccessControl('Access')
        $Tacl = (Get-Item $TFolder).GetAccessControl('Access')
	}
	PROCESS{
            #Set Privilegies from the Source $OACL to the Target $TFolder.
		    Set-Acl -path $TFolder -AclObject $OAcl 
	}
	END{}
} 
function Set-FileALC{            
	[cmdletbinding()]            
	param(
		[Parameter(Mandatory=$true,position=0)][string]$OFile,
        [Parameter(Mandatory=$true,position=1)][string]$TFile
	)
	BEGIN{
        $OAcl = (Get-Item $OFile).GetAccessControl('Access')
        $Tacl = (Get-Item $TFile).GetAccessControl('Access')
	}
	PROCESS{
            #Set Privilegies from the Source $OACL to the Target $TFolder.
		    Set-Acl -path $TFile -AclObject $OAcl 
	}
	END{}
} 



#STARTSCRIPT
Copy-Info -sourceDir $source -targetDir $target -DaysBehind $Days

Write-Output "CreatedDirectories:$GLOBAL:TotalDirectories`tCopied:$GLOBAL:FilesCopied`tExisting:$GLOBAL:FilesExisting"

Open in new window


Here's how to use the code if you save it as FileALC.ps1

http://recordit.co/C98fBXf7vY
0
 
Jose Gabriel Ortega CCEO J0rt3g4 Consulting ServicesCommented:
Since the moment you "move" the file from location, you will get changed the Shared permissions will be removed by default. Are you aware of this?.

Your days are in the future it should be (-$Date). I don't usually like oneliners but I'd propose an answer shortly.
0
 
David Johnson, CD, MVPOwnerCommented:
function Get-Something
{
  <#
    .SYNOPSIS
    Short Description
    .DESCRIPTION
    Detailed Description
    .EXAMPLE
    Get-Something
    explains how to use the command
    can be multiple lines
    .EXAMPLE
    Get-Something
    another example
    can have as many examples as you like
  #>
  param
  (
    [Parameter(Position=0)]
    [String] $StrSource = 'D:\deptkp\*.*',
    
    [Parameter(Position=1)]
    [string] $StrTarget = 'D:\archive',
    
    [Parameter(Position=2)]
    [String] $Date = '3'
  )
  $OLDDate = (get-date).AddDays(-$date)
  $length  = ($StrTarget.Length) -1
  if($StrTarget[$length]  -ne "\")
    {
    $strTarget = $StrTarget + "\"
    }
  $files   = Get-ChildItem -Recurse -Path $StrSource | Where-Object {$_.LastWriteTime -lt $OLDDate} 
  foreach($file in $files){
        $destFile = $StrTarget + $file.name
        Write-verbose -Message ("Copying {0} to {1}" -f $file.fullname,$destFile)
        Copy-Item -Path $file.fullname   -Destination $destFile
        Set-ItemProperty -Path $destFile -Name IsReadOnly -Value $true -ErrorAction SilentlyContinue
        }
}

Open in new window


Get-Something -StrSource G:\My -StrTarget G:\output -Verbose
0
 
Nicholas RefaloAuthor Commented:
Thank you Jose for your script, I'm currently running tests but receiving the following error any ideas?

Also once the file is copied it is not setting it to read only.  

Error when running script
Folder "D:\archive\Collaboration\CDS -SAN\1. Final SAN Revision A files (including WB) + Pricing Models\Archive - Do Not
 Use\SPT PQs and Component Lists Post Sampling and Review 24August2016 CGarcia\Do Not Use - Archived PQs - with Incorrec
t PQ Cost" created...
new-object : Exception calling ".ctor" with "1" argument(s): "The specified path, file name, or both are too long. The
fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters."
At D:\Scripts\FileALC.ps1:35 char:28
+             $FileInfoNew = new-object System.IO.FileInfo($file)
+                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

Exception calling "Copy" with "2" argument(s): "The specified path, file name, or both are too long. The fully
qualified file name must be less than 260 characters, and the directory name must be less than 248 characters."
At D:\Scripts\FileALC.ps1:41 char:21
+                     [System.IO.File]::Copy($file, $FilePath)
+                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : PathTooLongException

New-Object : Exception calling ".ctor" with "1" argument(s): "The specified path, file name, or both are too long. The
fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters."
At D:\Scripts\FileALC.ps1:91 char:40
+             [System.IO.FileInfo] $fi = New-Object System.IO.FileInfo -ArgumentLi ...
+                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

Cannot convert argument "creationTime", with value: "", for "SetCreationTime" to type "System.DateTime": "Cannot
convert null to type "System.DateTime"."
At D:\Scripts\FileALC.ps1:92 char:13
+             [System.IO.File]::SetCreationTime($targetFilePath,$fi.CreationTime)
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Cannot convert argument "lastWriteTime", with value: "", for "SetLastWriteTime" to type "System.DateTime": "Cannot
convert null to type "System.DateTime"."
At D:\Scripts\FileALC.ps1:93 char:13
+             [System.IO.File]::SetLastWriteTime($TargetFilePath,$fi.LastWriteTime ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Cannot convert argument "lastAccessTime", with value: "", for "SetLastAccessTime" to type "System.DateTime": "Cannot
convert null to type "System.DateTime"."
At D:\Scripts\FileALC.ps1:94 char:13
+             [System.IO.File]::SetLastAccessTime( $TargetFilePath,$fi.LastAccessT ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Get-Item : Cannot find path 'D:\deptkp\Collaboration\CDS -SAN\1. Final SAN Revision A files (including WB) + Pricing
Models\Archive - Do Not Use\SPT PQs and Component Lists Post Sampling and Review 24August2016 CGarcia\Do Not Use -
Archived PQs - with Incorrect PQ Cost\1. ACL Pack - PQ00018959.pdf' because it does not exist.
At D:\Scripts\FileALC.ps1:130 char:18
+         $OAcl = (Get-Item $OFile).GetAccessControl('Access')
+                  ~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (D:\deptkp\Colla... PQ00018959.pdf:String) [Get-Item], ItemNotFoundExcep
   tion
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand

You cannot call a method on a null-valued expression.
At D:\Scripts\FileALC.ps1:130 char:9
+         $OAcl = (Get-Item $OFile).GetAccessControl('Access')
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

Get-Item : Cannot find path 'D:\archive\Collaboration\CDS -SAN\1. Final SAN Revision A files (including WB) + Pricing
Models\Archive - Do Not Use\SPT PQs and Component Lists Post Sampling and Review 24August2016 CGarcia\Do Not Use -
Archived PQs - with Incorrect PQ Cost\1. ACL Pack - PQ00018959.pdf' because it does not exist.
At D:\Scripts\FileALC.ps1:131 char:18
+         $Tacl = (Get-Item $TFile).GetAccessControl('Access')
+                  ~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (D:\archive\Coll... PQ00018959.pdf:String) [Get-Item], ItemNotFoundExcep
   tion
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand

You cannot call a method on a null-valued expression.
At D:\Scripts\FileALC.ps1:131 char:9
+         $Tacl = (Get-Item $TFile).GetAccessControl('Access')
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

Set-Acl : Cannot bind argument to parameter 'AclObject' because it is null.
At D:\Scripts\FileALC.ps1:135 char:39
+             Set-Acl -path $TFile -AclObject $OAcl
+                                             ~~~~~
    + CategoryInfo          : InvalidData: (:) [Set-Acl], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SetAclComma
   nd

Set-ItemProperty : Cannot find path 'D:\archive\Collaboration\CDS -SAN\1. Final SAN Revision A files (including WB) +
Pricing Models\Archive - Do Not Use\SPT PQs and Component Lists Post Sampling and Review 24August2016 CGarcia\Do Not
Use - Archived PQs - with Incorrect PQ Cost\1. ACL Pack - PQ00018959.pdf' because it does not exist.
At D:\Scripts\FileALC.ps1:47 char:21
+                     Set-ItemProperty -Path $FilePath -Name IsReadOnly -Value $tr ...
+                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (D:\archive\Coll... PQ00018959.pdf:String) [Set-ItemProperty], ItemNotFo
   undException
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.SetItemPropertyCommand

new-object : Exception calling ".ctor" with "1" argument(s): "The specified path, file name, or both are too long. The
fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters."
At D:\Scripts\FileALC.ps1:35 char:28
+             $FileInfoNew = new-object System.IO.FileInfo($file)
+                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

Exception calling "Copy" with "2" argument(s): "The specified path, file name, or both are too long. The fully
qualified file name must be less than 260 characters, and the directory name must be less than 248 characters."
At D:\Scripts\FileALC.ps1:41 char:21
+                     [System.IO.File]::Copy($file, $FilePath)
+                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : PathTooLongException

New-Object : Exception calling ".ctor" with "1" argument(s): "The specified path, file name, or both are too long. The
fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters."
At D:\Scripts\FileALC.ps1:91 char:40
+             [System.IO.FileInfo] $fi = New-Object System.IO.FileInfo -ArgumentLi ...
+                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

Cannot convert argument "creationTime", with value: "", for "SetCreationTime" to type "System.DateTime": "Cannot
convert null to type "System.DateTime"."
At D:\Scripts\FileALC.ps1:92 char:13
+             [System.IO.File]::SetCreationTime($targetFilePath,$fi.CreationTime)
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

Open in new window

0
 
Nicholas RefaloAuthor Commented:
thank you for your script, it needed some adjustment to fit our environment but all good now thanks for your assistance
1
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.