Powershell to delete files from SharePoint Document libraries - powershell

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.

Related

Powershell.I want to add a object to my array $computers but it said that the index is out of bounds

I want to add a object to my array $computers
but it said that the index is out of bounds
function ajouterperipherique ($id, $emplacement, $type) {
$computers = import-csv -path "C:\Temp\Peripherique.csv"
echo $computers
$newObject = [pscustomobject]#{
idObject = $id
EmplacementObject = $emplacement
TypeObject=$type
}
for ($i = 0; $i -lt $computers.Count; $i++) {
if ($i +1 -eq $computers.count) {
$computers[$computers.count+1]=$newObject
}
}
Write-Host ($newObject | Format-List | Out-String)
}
ajouterperipherique "GLADIATOR" "ordinateur" "Statique"
Here is the solution proposed by Lee_Dailey:
$csvPath='C:\Temp\Peripherique.csv'
function ajouterperipherique {
param(
[string]$ID,
[string]$Emplacement,
[string]$Type,
[string]$Path
)
if(-not(Test-Path $Path) -or -not [IO.Path]::GetExtension($Path) -eq '.csv')
{
throw 'File doest not exist or is not a Csv...'
}
[pscustomobject]#{
Identifiant = $id
Type = $type
Emplacement = $emplacement
}|Export-Csv $Path -NoTypeInformation -Append
}
ajouterperipherique -ID "GLADIATOR" -Emplacement "ordinateur" -Type "Statique" -Path $csvPath
A few tips, as pointed out in comments, you shouldn't really use or you should try to avoid Write-Host whenever possible.
You shouldn't really hardcode paths inside your functions, since they're meant to be re-used, hardcoding information you know can change in the future is never a good idea.
You might also wanna consider setting your parameters as Mandatory, parameters are somewhat important in Powershell and can make your life easier. I recommend reading this article if you're thinking of creating more functions in the future: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_functions_advanced_parameters?view=powershell-7.1

Get local group members: version agnostic

I found this thread that offers two basic approaches to getting local group members.
This works for me in all versions of powershell, but depends on using the old NET command line utility.
function Get-LocalGroupMembers() {
param ([string]$groupName = $(throw "Need a name") )
$lines = net localgroup $groupName
$found = $false
for ($i = 0; $i -lt $lines.Length; $i++ ) {
if ( $found ) {
if ( -not $lines[$i].StartsWith("The command completed")) {
$lines[$i]
}
} elseif ( $lines[$i] -match "^----" ) {
$found = $true;
}
}
}
This works for me in PowerShell 2.0, but barfs in PS5.0 with Error while invoking GetType. Could not find member.
It only barfs on some groups, including Administrators, which has me thinking it's some sort of security feature, like requiring elevated privileges to REALLY have admin rights in a script.
Function Get-LocalGroupMembers
{
Param(
[string]
$server = "."
)
Try
{
$computer = [ADSI]"WinNT://$( $Server ),computer"
$computer.psbase.children |
where {
$_.psbase.schemaClassName -eq 'group'
} |
ForEach {
$GroupName = $_.Name.ToString()
$group =[ADSI]$_.psbase.Path
$group.psbase.Invoke("Members") |
foreach {
$memberName = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) -replace "WinNT:", ""
$props = #{
"LocalGroup" = $GroupName
"MemberName" = $memberName
}
$obj = New-Object -TypeName psobject -Property $props
Write-Output $obj
} # foreach members
} # foreach group
}
Catch
{
Throw
}
}
I think I read somewhere that PS5.1 has a native CMDlet finally. But I can't depend on a particular version of PS, I need to support everything from PS2.0 in Win7 up. That said, is there a single version agnostic solution that doesn't depend on a command line utility kludge? Or do I need to have code that uses the old hack or the new CMDlet depending on PS version I am running on?
So, I am having good luck with this solution.
$hostname = (Get-WmiObject -computerName:'.' -class:Win32_ComputerSystem).name
$wmiQuery = Get-WmiObject -computerName:'.' -query:"SELECT * FROM Win32_GroupUser WHERE GroupComponent=`"Win32_Group.Domain='$Hostname',Name='$group'`""
if ($wmiQuery -ne $null) {
:searchLoop foreach ($item in $wmiQuery) {
if (((($item.PartComponent -split "\,")[1] -split "=")[1]).trim('"') -eq $user) {
$localGroup = $true
break :searchLoop
}
}
}
I'm not sure yet if I like that overly complex IF vs some variables, but the functionality is there and working across all versions of PS without resorting to command line kludges, which was the goal.
Note that this just returns true if the user is a member of the group, which is all I need. The other code I posted would provide a list of members, which is the basis of doing a check, and I just hadn't modified it to show the real end goal, since the problem manifested without that.
Instead this :
$memberName = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null) -replace "WinNT:", ""
You can try this :
$memberName = ([ADSI]$_).InvokeGet("Name")

Why Isn't This Counting Correctly | PowerShell

Right now, I have a CSV file which contains 3,800+ records. This file contains a list of server names, followed by an abbreviation stating if the server is a Windows server, Linux server, etc. The file also contains comments or documentation, where each line starts with "#", stating it is a comment. What I have so far is as follows.
$file = Get-Content .\allsystems.csv
$arraysplit = #()
$arrayfinal = #()
[int]$windows = 0
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing.Split(":")
$arrayfinal = #($arraysplit[0], $arraysplit[1])
}
}
foreach ($item in $arrayfinal){
if ($item[1] -contains 'NT'){
$windows++
}
else {
continue
}
}
$windows
The goal of this script is to count the total number of Windows servers. My issue is that the first "foreach" block works fine, but the second one results in "$Windows" being 0. I'm honestly not sure why this isn't working. Two example lines of data are as follows:
example:LNX
example2:NT
if the goal is to count the windows servers, why do you need the array?
can't you just say something like
foreach ($thing in $file)
{
if ($thing -notmatch "^#" -and $thing -match "NT") { $windows++ }
}
$arrayfinal = #($arraysplit[0], $arraysplit[1])
This replaces the array for every run.
Changing it to += gave another issue. It simply appended each individual element. I used this post's info to fix it, sort of forcing a 2d array: How to create array of arrays in powershell?.
$file = Get-Content .\allsystems.csv
$arraysplit = #()
$arrayfinal = #()
[int]$windows = 0
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing.Split(":")
$arrayfinal += ,$arraysplit
}
}
foreach ($item in $arrayfinal){
if ($item[1] -contains 'NT'){
$windows++
}
else {
continue
}
}
$windows
1
I also changed the file around and added more instances of both NT and other random garbage. Seems it works fine.
I'd avoid making another ForEach loop for bumping count occurrences. Your $arrayfinal also rewrites everytime, so I used ArrayList.
$file = Get-Content "E:\Code\PS\myPS\2018\Jun\12\allSystems.csv"
$arrayFinal = New-Object System.Collections.ArrayList($null)
foreach ($thing in $file){
if ($thing.StartsWith("#")) {
continue
}
else {
$arraysplit = $thing -split ":"
if($arraysplit[1] -match "NT" -or $arraysplit[1] -match "Windows")
{
$arrayfinal.Add($arraysplit[1]) | Out-Null
}
}
}
Write-Host "Entries with 'NT' or 'Windows' $($arrayFinal.Count)"
I'm not sure if you want to keep 'Example', 'example2'... so I have skipped adding them to arrayfinal, assuming the goal is to count "NT" or "Windows" occurrances
The goal of this script is to count the total number of Windows servers.
I'd suggest the easy way: using cmdlets built for this.
$csv = Get-Content -Path .\file.csv |
Where-Object { -not $_.StartsWith('#') } |
ConvertFrom-Csv
#($csv.servertype).Where({ $_.Equals('NT') }).Count
# Compatibility mode:
# ($csv.servertype | Where-Object { $_.Equals('NT') }).Count
Replace servertype and 'NT' with whatever that header/value is called.

Delete particular items in SharePoint document library

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()
}
}

Using "starts with" in powershell script instead of contains?

In my current powershell script I have hash table with values. Am using
this syntax
$x = $f.contains("$k")
but I figured recently that am having problems with this approach I was wondering if powershell has something that says "starts with," or related, that would search thru the hash table with "starts with" instead of contains
Example of the hash table:
"bio.txt" = "server1\datafiles\bio";
etc.......
EDIT Sample from comments
foreach ($key in $filehash.keys) {
$path = $filehash.get_Item($key)
$filecount = 0
foreach ($file in $FileArray) {
if ($file.LastWriteTime -lt($(GetDate).adddays(-1))) {
[string] $k = $key.ToLower()
[string] $f = $file.name.ToLower()
if ($x = $f.contains("$k")) { }
}
}
}
Try using -like to check if a string starts with yourvalue. I rewrote your sample in the comments to use it:
$filehash.GetEnumerator() | foreach {
#$_ is now current object from hashtable(like foreach)
#$_.key is key and $_.value is path
$filecount = 0
foreach ($file in $FileArray) {
if ( ($file.LastWriteTime -lt $((Get-Date).AddDays(-1))) -and ($file.name -like "$($_.Key)*") ) {
#process file
}
}
}