PowerShell script takes days to finish - powershell

The following script runs great except for the fact that it takes days to finish. Does anyone have any tweaks or tips to cut down the execution time?
The shared directory, Images\Equipment, contains over 5500 folders is grows daily.
# If the Admin folder for Equipment Images does not exist, make a new one and set the correct permissions.
$Location = "E:\Images\Equipment\*\"
$file = "E:\Images\Equipment\*\Admin"
foreach ($_ in (Get-ChildItem E:\Images\Equipment\*\)){if(($_.PSIsContainer -AND $_.name -eq "Admin")-eq $false)
{
New-Item -Path $location -Name "Admin" -ItemType directory
$errorActionPreference = "continue"}}
$folder = "E:\Images\Equipment\*\Admin"
Get-ChildItem E:\Images\Equipment\ -Directory -Filter "admin" -Recurse | ForEach-Object {
$acl = Get-Acl $_.FullName
if ($acl.AreAccessRulesProtected) { $acl.Access | % {$acl.purgeaccessrules($_.IdentityReference)} }
else {
$isProtected = $true
$preserveInheritance = $false
$acl.SetAccessRuleProtection($isProtected, $preserveInheritance)
}
$account="recoequip\folder sales group"
$rights=[System.Security.AccessControl.FileSystemRights]::FullControl
$inheritance=[System.Security.AccessControl.InheritanceFlags]"ContainerInherit,ObjectInherit"
$propagation=[System.Security.AccessControl.PropagationFlags]::None
$allowdeny=[System.Security.AccessControl.AccessControlType]::Allow
$dirACE=New-Object System.Security.AccessControl.FileSystemAccessRule ($account,$rights,$inheritance,$propagation,$allowdeny)
$ACL.AddAccessRule($dirACE)
Set-Acl -aclobject $ACL -Path $folder
Write-Host $folder Permissions added}
Thank you for any assistance.
Sara

Ok, to try and speed up your script I would create the ACE once at the beginning as suggested by mjolinor in his comment above. I would not bother looking for folders that are missing an Admin subfolder, and just create the Admin sub-folder in each folder regardless, and use the -force argument. That won't delete and re-create, but what it will do is return a folder object for each folder even if it already existed. Collect all those folders in a variable. Then iterate through those folders and apply the correct permissions for them.
# If the Admin folder for Equipment Images does not exist, make a new one and set the correct permissions.
#Define ACE to apply
$account="recoequip\folder sales group"
$rights=[System.Security.AccessControl.FileSystemRights]::FullControl
$inheritance=[System.Security.AccessControl.InheritanceFlags]"ContainerInherit,ObjectInherit"
$propagation=[System.Security.AccessControl.PropagationFlags]::None
$allowdeny=[System.Security.AccessControl.AccessControlType]::Allow
$dirACE=New-Object System.Security.AccessControl.FileSystemAccessRule ($account,$rights,$inheritance,$propagation,$allowdeny)
#Make sure all Equipment Images folders have an 'Admin' subfolder, and store the folder objects for later
$Folders = Get-ChildItem E:\Images\Equipment\* -Directory | %{New-Item -Path ($_.FullName + "\Admin") -ItemType directory -ErrorAction Continue -Force}
ForEach($Folder in $Folders){
$acl = Get-Acl $Folder.FullName
if ($acl.AreAccessRulesProtected) { $acl.Access | % {$acl.purgeaccessrules($_.IdentityReference)} }
else {
$isProtected = $true
$preserveInheritance = $false
$acl.SetAccessRuleProtection($isProtected, $preserveInheritance)
}
$ACL.AddAccessRule($dirACE)
Set-Acl -aclobject $ACL -Path $folder
Write-Host $folder Permissions added
}
This does assume that you are using PSv3 or better and have access to the -Directory switch for the Get-ChildItem cmdlet. If you do not have v3 installed I would suggest upgrading your version of PowerShell. I realize that's not always an option so alternatively you can change line 12 to this:
$Folders = Get-ChildItem E:\Images\Equipment\* -Attributes Directory | %{New-Item -Path ($_.FullName + "\Admin") -ItemType directory -ErrorAction Continue -Force}
or
$Folders = Get-ChildItem E:\Images\Equipment\* | ?{$_.PSIsContainer} | %{New-Item -Path ($_.FullName + "\Admin") -ItemType directory -ErrorAction Continue -Force}
If you have to use one of these I would suggest the first one since that filters for only directories at the FileSystem Provider level, instead of returning all folders and files, and then making PowerShell filter out the files.

This was to long to add in a comment.
This is the error it gives repeatedly in between assigning permissions to certain Admin folders.
Get-Acl : Cannot validate argument on parameter 'Path'. The argument is null or empty. Provide an argument that is not null or
empty, and then try the command again.
At line:15 char:20
+ $acl = Get-Acl $_.FullName
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Get-Acl], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetAclCommand
You cannot call a method on a null-valued expression.
At line:21 char:9
+ $acl.SetAccessRuleProtection($isProtected, $preserveInheritance)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At line:24 char:5
+ $ACL.AddAccessRule($dirACE)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Set-Acl : Cannot bind argument to parameter 'AclObject' because it is null.
At line:26 char:24
+ Set-Acl -aclobject $ACL -Path $folder
+ ~~~~
+ CategoryInfo : InvalidData: (:) [Set-Acl], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SetAclCommand

Related

Remove-item varibale for path from csv

i have to delete folders based on a excel list, so i have tried to import the execl with import csv but it dosent work.
The import doesnt fill the variable in the correct format.
This is the code i tried to use:
$folders = import-csv G:\Book1.csv foreach ($folder in $folders) {Remove-Item -Path $folder -Recurse -Force}
The error is this:
Remove-Item : Cannot find drive. A drive with the name '#{Foldername=G' does not exist.
At line:4 char:5
+ Remove-Item -Path $folder -Recurse -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (#{Foldername=G:String) [Remove-Item], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot find drive. A drive with the name '#{Foldername=G' does not exist.
At line:4 char:5
+ Remove-Item -Path $folder -Recurse -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (#{Foldername=G:String) [Remove-Item], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand
Remove-Item : Cannot find drive. A drive with the name '#{Foldername=G' does not exist.
At line:4 char:5
+ Remove-Item -Path $folder -Recurse -Force
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (#{Foldername=G:String) [Remove-Item], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand
In the CSV in the 1.Line heading stands foldername in line 2-4 are the name of the folders (a, b, c).
This is the CSV:
Foldername
G:\Test\a
G:\Test\b
G:\Test\c
The directory is G:\Test\a , b, c
I have found a Solution, the CSV file was a Problem and the import so i had to modify the CSV File:
"Foldername"
"G:\Test\a"
"G:\Test\b"
"G:\Test\c"
the powershell code is this now:
$folders = import-csv .csv | ForEach-Object {
write-host $($_.Foldername)
Remove-Item -Path $($_.Foldername) -Recurse -Force
}

Get-ChildItem : A parameter cannot be found that matches parameter name 'Directory' [duplicate]

This question already has answers here:
A parameter cannot be found that matches parameter name 'directory'
(2 answers)
Closed last year.
I am getting Get-ChildItem : *A parameter cannot be found that matches parameter name 'Directory'* error message , below is the script used, kindly help me in finding the issue?
Script:
Function Clear-ReadOnlyFiles([string]$path)
{
"Clearing readonly attribute from files in $path"
New-Variable -Name read_only -Value 1 -Option readonly
Get-ChildItem -Path $path |
Where-Object { $_.attributes -match 'readonly' } |
ForEach-Object {
$_.attributes = $_.attributes -Bxor $read_only }
}#end Clear-ReadonlyFiles
$ZipCmd = "{0}\7za.exe" -f $BackupDir
$ZipCmdArgs = "-sdel"
# Grab Directories in Location
$Directories = Get-ChildItem -Path $BackupDir -Directory
# Cycle Through and Launch 7za to Compress
# Check if Archive Exists - If yes, delete source folder
ForEach ($Directory in $Directories)
{
$Source = "{0}\{1}" -f $BackupDir,$Directory.Name
$Archive = "{0}.7z" -f $Source
# Clear Read-Only Attribute on Folder
$SubFiles = Get-ChildItem -Path $Directory -Recurse -ReadOnly
ForEach ($SubFile in $SubFiles) {
$SubFile.IsReadOnly = $False
}
#If ($Directory.IsReadOnly -eq $True) { $Directory.set_IsReadOnly($False) }
$AllArgs = #('a', $ZipCmdArgs, $Archive, $Source);
& $ZipCmd $AllArgs
}
Error message:
Get-ChildItem : A parameter cannot be found that matches parameter name 'Directory'.
At C:\Play\BackupCompress.ps1:35 char:57
+ $Directories = Get-ChildItem -Path $BackupDir -Directory <<<<
+ CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
Get-ChildItem : A parameter cannot be found that matches parameter name 'ReadOnly'.
At C:\Play\BackupCompress.ps1:45 char:66
+ $SubFiles = Get-ChildItem -Path $Directory -Recurse -ReadOnly <<<<
+ CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
Property 'IsReadOnly' cannot be found on this object; make sure it exists and is settable.
At C:\Play\BackupCompress.ps1:47 char:18
+ $SubFile. <<<< IsReadOnly = $False
+ CategoryInfo : InvalidOperation: (IsReadOnly:String) [], RuntimeException
+ FullyQualifiedErrorId : PropertyNotFound
I believe that the Directory switch was added in PowerShell 3.0. In older versions you will need to check the PSIsContainer property of each child item, which will be true if the item is a directory:
$Directories = Get-ChildItem -Path $BackupDir | Where-Object { $_.PSIsContainer }
-Directory is a conditional parameter and only works on paths whose provider is "FileSystem". It's present on this page: Get-ChildItem for FileSystem, but not on the generic one.
Make sure that the correct provider appears when you type the following:
Get-Item $BackupDir | Select-Object -Property PSProvider

Open zip files in nested directories fails with "unexpected token" error

I have a script that I am trying to get to sort through a directory and open all the zip files and store the text files all to one directory. Here is the code:
#Script to open zip files in tree
New-Item E:\Files -type directory
Get-ChildItem -Path E:\SNL_Insurance\* -Recurse -Exclude "*.md5"|
ForEach-Object {
$file = $_
write-host $file;
$destination = "E:\Files"
$shell = New-Object -com shell.application
$zip = $shell.NameSpace($file) |
foreach($item in $_.items()){
$shell.Namespace($destination).copyhere($item)
}
}
I think I almost have it, but keep getting this error (any elaboration on piping would be helpful):
Unexpected token 'in' in expression or statement.
At E:\Expand-ZIPFile.ps1:14 char:19
+ foreach($item in <<<< $_.items()){
+ CategoryInfo : ParserError: (in:String) [], ParseException
+ FullyQualifiedErrorId : UnexpectedToken
EDIT:
Ahh... thanks for that distinction. I made your edits but after each of my "write-host" checks to see the file name I get the following error:
`You cannot call a method on a null-valued expression. At E:\Expand-ZIPFile.ps1:14 char:30 + foreach($item in $zip.items <<<< ()){ + CategoryInfo : InvalidOperation: (items:String) [], RuntimeException + FullyQualifiedErrorId : InvokeMethodOnNull
EDIT2: So the original code does copy files to a new directory but also copies the whole zip file over. I tried to add an if statement to only copy files that are .txt but the code just steps through each directory without copying anything. If you have any idea that would be appreciated as I have exhausted all my ideas. Here is the code:
new-Item E:\Files -type directory
Get-Childitem -path E:\SNL_Insurance\* -recurse -exclude "*.md5" |
Foreach-object {
$file = $_
write-host $file;
$destination = "E:\Files"
$shell = new-object -com shell.application
$zip = $shell.NameSpace($file.Fullname)
foreach($item in $zip.items()){
if ($item.Extension -eq ".txt") {
$shell.Namespace($destination).copyhere($item)
}
}
}
You're confusing foreach with ForEach-Object loops. The former neither read from nor do they write to pipelines. Also, $file is a FileInfo object, not a path. The NameSpace method expects a path string, so you need to use $file.FullName.
Replace
$zip = $shell.NameSpace($file) |
foreach($item in $_.items()){
$shell.Namespace($destination).copyhere($item)
}
with
$zip = $shell.NameSpace($file.FullName)
foreach($item in $zip.items()){
$shell.Namespace($destination).copyhere($item)
}

PowerShell - Get-ChildItem does not exclude directory

When recursively searching through a specific directory, Get-ChildItem will search starting at the root. I do not want to search through the C:\Windows directory. I want to restrict the search to the C:\Docs directory.
Here is what I am running:
PS> Get-ChildItem -path “C:\docs” -Filter "*crypt*" -recurse -ErrorAction Stop
Get-ChildItem : Access to the path 'C:\Windows\CSC' is denied.
At line:1 char:1
+ Get-ChildItem -path “C:\docs” -Filter "*crypt*" -recurse -ErrorAction ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\Windows\CSC:String) [Get-ChildItem], UnauthorizedAccessException
+ FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
PS> Get-ChildItem -path “C:\docs” -Filter "*crypt*" -exclude "C:\windows" -recurse -ErrorAction Stop
Get-ChildItem : Access to the path 'C:\Windows\CSC' is denied.
At line:1 char:1
+ Get-ChildItem -path “C:\docs” -Filter "*crypt*" -exclude "C:\windows" ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (C:\Windows\CSC:String) [Get-ChildItem], UnauthorizedAccessException
+ FullyQualifiedErrorId : DirUnauthorizedAccessError,Microsoft.PowerShell.Commands.GetChildItemCommand
EDIT: TL;DR: User error. I do not have the C:\Docs directory.
I am editing this script, which is running on several servers. I am testing it on my laptop. I still don't understand why it would look through the rest of the file system, once it could not find the starting path.
It looks like (I haven't searched enough) this may be a bug in Get-ChildItem; if you pass a non-existent path to the -path parameter, it searches from the root of that drive (at least for local drives).
Before calling Get-ChildItem, test for the existence of the path and you can avoid this.
$mypath = "c:\docs";
if (test-path -path $mypath) {
Get-childitem –path $mypath –filter “*crypt*" -recurse -ErrorAction stop;
} else {
Write-Warning "$mypath not found";
}

Get data from CSV to delete users folders from multiple servers

I am very new to powershell and situation is that I have some unneeded users folder that must be deleted on different servers. Path on all servers is f.e. "\server1\hiddenshare$\username" My CSV looks like:
ServerName,FolderName
SERVER1,AAAA
SERVER2,AAA1
SERVER3,AAA2
And I am trying to run this code:
$path = "C:\temp\servers_folders.csv"
$ServerName = Import-Csv -Path $path
$FolderName = Import-CSV -Path $path
Import-Csv -Path $path | % {
Remove-Item -Path \\$ServerName\hiddenshare$\$FolderName -Recurse
}
After all I get this error:
Remove-Item : Cannot find path '\\ \$\ ' because it does not exist.
At C:\temp\servers_folders.csv.ps1:5 char:3
+ Remove-Item -Path \\$ServerName\hiddenshare$\$FolderName -Recurse
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (\\ \usr$\ :String) [Remove-Item], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.RemoveItemCommand
you dont need to import your csv more than once.
Inside the foreach block you have to reference the current item by $_
try this, remove -whatif if it's ok
$servers=import-csv .\servers.csv
$servers | %{
remove-item -path \\$($_.servername)\users$\$($_.FolderName) -whatif
}