Delete particular items in SharePoint document library - powershell

I want to create PowerShell script that will be run daily by time scheduler.
Documents that should be deleted will have Yes value in column1.
Bellow is my code, but I am not sure why it doesn't work
$web = get-spweb "URL"
$Libraries = $web.Lists | where {$_.BaseType -eq "DocumentLibrary"}
foreach ($library in $Libraries) {
$items = $library.items
foreach ($item in $items)
{
If($item["Column1"] -eq "Yes")
{
$item.delete()
}
}
}
Any suggestion what I did wrong?
EDIT:
After I ran script it didn't give me any error message, but instead documents weren't deleted.
EDIT2: After code edit I got following message:
Collection was modified; enumeration operation may not execute. At
C:\Users\user\Desktop\DeleteItems.ps1:5 char:14
+ foreach ($item in $items)
+ ~~~~~
+ CategoryInfo : OperationStopped: (:) [], InvalidOperationException
+ FullyQualifiedErrorId : System.InvalidOperationException

The reason your code is not working is because you are attempting to delete items from an enumeration inside of a foreach loop. Most collections in .NET with throw an exception if an item is added or deleted during a foreach loop.
Try a regular for loop and see what happens:
$web = get-spweb "URL"
$Libraries = $web.Lists | where {$_.BaseType -eq "DocumentLibrary"}
foreach ($library in $Libraries) {
$items = $library.items
for ($i=0; $i -lt $items.Count; $i++)
{
If($items[$i]["Column1"] -eq "Yes")
{
$items[$i].delete()
}
}
}
That may be enough, but powershell might throw an exception for the outer foreach loop too since technically you're modifying the document libraries inside the loop. If that is the case, you'll need to use a for loop on the outside too.
Also, one way to speed up your powershell is to use a caml query on the document libraries so that it only returns the items where Column1 = yes (I'm assuming Column1 is a Yes/No field). That would look like this:
$web = get-spweb "URL"
$Libraries = $web.Lists | where {$_.BaseType -eq "DocumentLibrary"}
foreach ($library in $Libraries) {
$query = New-Object Microsoft.SharePoint.SPQuery
$query.Query = "<Where><Eq><FieldRef Name='Column1'/><Value Type='Boolean'>1</Value></Eq></Where>"
$items = $library.GetItems($query)
for ($i=0; $i -lt $items.Count; $i++)
{
$items[$i].delete()
}
}

Related

Speeding up Test-Path and GCI on large data set

I have a large list of Customer IDs (40,000). Files for each Customer have been saved in many different locations. I first run a Test-Path to see if the directory exists, if it does then I run Get-ChildItem and filter down the results to find the file I need (Any file matching string 'Contract'). I am hoping to educate myself on how to speed this up, I have attempted to introduce another variable 'Trigger' to try and prevent some of the excess code from running if the matching file is found. It is taking a very long time to loop this through 40,000 times so if there is a better way any help greatly appreciated, many thanks.
Here is the code I'm currently using
ForEach ($Customer in $CustomerList)
{
$Trigger = 0
$Result1 = Test-Path "$Location1\$Customer"
$Result2 = Test-Path "$Location2\$Customer"
$Result3 = Test-Path "$Location3\$Customer"
IF ($Result1 -eq $True)
{
$1Files = GCI "$Location1\$Customer" -Recurse
ForEach ($File IN $1Files)
{
IF ($Trigger -eq 0)
{
$FileName = $File.Name
$FileLocation = $File.FullName
IF ($FileName -match 'Contract')
{
$Report += "$FileName $FileLocation"
$Trigger = 1
}
}
}
}
ELSEIF ($Result2 -eq True)
{
Same as result 1 codeblock but using $Location2
}
ELSEIF ($Result3 -eq True)
{
Same as result 1 codeblock but using $Location3
}
}

Powershell to delete files from SharePoint Document libraries

I am trying to multiple files from multiple SharePoint libraries in SharePoint 2010 on Application server in the farm with the below script:
$list = Get-Content $libpath
$web = Get-Spweb $url
$lib = $web.Lists | where { $_.title -eq $libname }
foreach ($libr in $lib) {
$file = $libr.Items
foreach ( $fil in $file) {
If ($fil.Name -eq $item) {
$fil.Delete()
}
}
}
The problem is that $libr.Items is coming up empty even though the library is not empty.
$libr.Files
$libr.Files.Name
All showing up empty as well.
Please help in fixing it.
Thank you
Try the below code.
Add-PSSnapin Microsoft.SharePoint.PowerShell
$SPWeb = Get-SPWeb "Provide your web here"
$docfiles = $SPWeb.GetFolder("Provide your Document Library name here").Files
foreach ($docfile in $docfiles) {
Write-host "File Name: " $docfile.Name
$docfile.Delete();
}
Not sure what this does:
$lib = $web.Lists | where { $_.title -eq $libname }
foreach ($libr in $lib) {
$lib in this case should be a single library, why the foreach?
Can I suggest you get the library explicitly then verify that it exists:
$lib = $web.lists.TryGetList($libname)
if ($lib) {
$items = $lib.items
foreach ($item in $items) {
#Your logic here
#Do you really want to delete? Maybe use $item.Recycle()
}
}
Also if you know what you want to delete up front, look into batching as you can delete items much faster.

Powershell Error: missing '=' operator after key in hash literal

I needed to create a script that checks to see if an IIS App Pool for a specific site and its child App Pools are started. (I have a separate script to start "stopped" App Pools as I just want to check if they are stopped) I was able to create the script however when I modified it to format the output better I keep getting this error,
At E:\iis\scripts\svc_pl_fm_app_pool_status.ps1:12 char:6
+ App Pool = $item.Name;
+ ~
Missing '=' operator after key in hash literal.
At E:\iis\scripts\svc_pl_fm_app_pool_status.ps1:7 char:29
+ foreach ($item in $results) {
+ ~
Missing closing '}' in statement block.
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : MissingEqualsInHashLiteral
I checked here and Microsoft but I left more confused about the error then when I first saw it. I saw this Missing closing '}' in statement block. so i assumed I was in fact missing one but I checked and I am not. I am not sure if it is a spacing/ indentation issue but I do not know enough about this error message. Here is my script below.
$results = $item = $appPool = $status = $NULL
$status = "1"
import-module WebAdministration
$AppPoolList = #()
$results = Get-ChildItem IIS:\AppPools\* | Where-Object { ($_.Name -like "someAppPool*" -and $_.Name -like "someChildAppPool*" )}
foreach ($item in $results) {
if ($item.State -ne "Started") {$status = "0"}
$AppPoolList += [PSCustomObject]#{
App Pool = $item.Name;
Status = $item.State;
}
}
$AppPoolList | Format-List

putting foreach loop into a csv file

im stuck on how to output my foreach loop into a csv or excel file. this script just get all computers off a local network and then test to see if that computer has a certain KBPatch. The script works just like I said I need help trying to make it output to a csv file. any tips/help is appreciated
Code Below
$strCategory = "computer"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$ObjSearcher.SearchRoot = $objDomain
$objSearcher.filter = ("(objectCategory=$strCategory)")
$colProplist = "name"
foreach($i in $colProplist)
{
$objSearcher.PropertiesToLoad.Add($i)
}
#Finds all operating systems and computer names
$colResults = $objSearcher.FindAll()
foreach($objResult in $colResults)
{
$objComputer = $objResult.Properties;
$names = $objComputer.name
# Define Hotfix to check
$CheckKBS = #(“patch#" , "patch#")
#Query the computers for the HotFix
foreach($name in $names)
{
foreach ($CheckKB in $CheckKBS) {
$HotFixQuery = Get-HotFix -ComputerName $name | Where-Object {$_.HotFixId -eq $CheckKB} | Select-Object -First 1;
if($HotFixQuery -eq $null)
{
Write-Host “Hotfix $CheckKB is not installed on $name”;
}
else
{
Write-Host “Hotfix $CheckKB was installed on $name by ” $($HotFixQuery.InstalledBy);
}
}}
}
To do this cleanly I usually use a custom object.
Before your foreach, instantiate an empty array:
$records = #()
and make an object template:
$tmpRecord = [PSCustomObject]#{
serverName = ''
missingKB = ''
}
Inside the foreach, clone the record obj:
$record = $tmpRecord.psobject.copy()
put your data into the record:
$record.serverName = $name
$record.missingKB = $CheckKb
Put the record into the array:
$records += $record
Then after the foreach, export to csv:
$records | export-csv yourcsv.csv
The need for an object template was confusing to me when I first learned this pattern. You need this because of the combination of scope and how objects are added to arrays (by reference).
If you try to get away with declaring an object inside the loop then that object will be scoped to the lifetime of the foreach loop. You'll then add a reference to that object to your $records array. After the foreach loop completes you will have an array full of references to objects that do not exist.

What is the cleanest way to join in one array the result of two or more calls to Get-ChildItem?

I'm facing the problem of moving and copying some items on the file system with PowerShell.
I know by experiments the fact that, even with PowerShell v3, the cmdlet Copy-Item, Move-Item and Delete-Item cannot handle correctly reparse point like junction and symbolic link, and can lead to disasters if used with switch -Recurse.
I want to prevent this evenience. I have to handle two or more folder each run, so I was thinking to something like this.
$Strings = #{ ... }
$ori = Get-ChildItem $OriginPath -Recurse
$dri = Get-ChildItem $DestinationPath -Recurse
$items = ($ori + $dri) | where { $_.Attributes -match 'ReparsePoint' }
if ($items.Length -gt 0)
{
Write-Verbose ($Strings.LogExistingReparsePoint -f $items.Length)
$items | foreach { Write-Verbose " $($_.FullName)" }
throw ($Strings.ErrorExistingReparsePoint -f $items.Length)
}
This doen't work because $ori and $dri can be also single items and not arrays: the op-Addition will fail. Changing to
$items = #(#($ori) + #($dri)) | where { $_.Attributes -match 'ReparsePoint' }
poses another problem because $ori and $dri can also be $null and I can end with an array containing $null. When piping the join resutl to Where-Object, again, I can end with a $null, a single item, or an array.
The only apparently working solution is the more complex code following
$items = $()
if ($ori -ne $null) { $items += #($ori) }
if ($dri -ne $null) { $items += #($dri) }
$items = $items | where { $_.Attributes -match 'ReparsePoint' }
if ($items -ne $null)
{
Write-Verbose ($Strings.LogExistingReparsePoint -f #($items).Length)
$items | foreach { Write-Verbose " $($_.FullName)" }
throw ($Strings.ErrorExistingReparsePoint -f #($items).Length)
}
There is some better approch?
I'm interested for sure if there is a way to handle reparse point with PowerShell cmdlets in the correct way, but I'm much more interested to know how to join and filters two or more "PowerShell collections".
I conclude observing that, at present, this feature of PowerShell, the "polymorphic array", doen't appear such a benefit to me.
Thanks for reading.
Just add a filter to throw out nulls. You're on the right track.
$items = #(#($ori) + #($dri)) | ? { $_ -ne $null }
I've been on Powershell 3 for a while now but from what I can tell this should work in 2.0 as well:
$items = #($ori, $dri) | %{ $_ } | ? { $_.Attributes -match 'ReparsePoint' }
Basically %{ $_ } is a foreach loop that unrolls the inner arrays by iterating over them and passing each inner element ($_) down the pipeline. Nulls will automatically be excluded from the pipeline.